// The the installation source as the nextActivity thinks this activity is the source, hence // set the originating UID and sourceInfo explicitly nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage); nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo); nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) { nextActivity.setClass(this, PackageInstallerActivity.class); } else { UripackageUri= intent.getData();
if (packageUri != null && (packageUri.getScheme().equals(ContentResolver.SCHEME_FILE) || packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))) { // Copy file to prevent it from being changed underneath this process nextActivity.setClass(this, InstallStaging.class); } elseif (packageUri != null && packageUri.getScheme().equals( PackageInstallerActivity.SCHEME_PACKAGE)) { nextActivity.setClass(this, PackageInstallerActivity.class); } else { Intentresult=newIntent(); result.putExtra(Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_FAILED_INVALID_URI); setResult(RESULT_FIRST_USER, result);
nextActivity = null; } }
if (nextActivity != null) { startActivity(nextActivity); } finish();
/** * If a package gets installed from an content URI this step loads the package and turns itinto * and installation from a file. Then it re-starts the installation as usual. */
// This is the first onResume in a single life of the activity if (mStagingTask == null) { // File does not exist, or became invalid if (mStagedFile == null) { // Create file delayed to be able to show error try { mStagedFile = TemporaryFileManager.getStagedFile(this); } catch (IOException e) { showError(); return; } }
privatefinalclassStagingAsyncTaskextendsAsyncTask<Uri, Void, Boolean> { @Override protected Boolean doInBackground(Uri... params) { if (params == null || params.length <= 0) { returnfalse; } UripackageUri= params[0]; try (InputStreamin= getContentResolver().openInputStream(packageUri)) { // Despite the comments in ContentResolver#openInputStream the returned stream can // be null. if (in == null) { returnfalse; }
try (OutputStreamout=newFileOutputStream(mStagedFile)) { byte[] buffer = newbyte[1024 * 1024]; int bytesRead; while ((bytesRead = in.read(buffer)) >= 0) { // Be nice and respond to a cancellation if (isCancelled()) { returnfalse; } out.write(buffer, 0, bytesRead); } } } catch (IOException | SecurityException | IllegalStateException e) { Log.w(LOG_TAG, "Error staging apk from content URI", e); returnfalse; } returntrue; }
@Override protectedvoidonPostExecute(Boolean success) { if (success) { // Now start the installation again from a file IntentinstallIntent=newIntent(getIntent()); installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class); installIntent.setData(Uri.fromFile(mStagedFile));
if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); }
/** * This activity is launched when a new application is installed via side loading * The package is first parsed and the user is notified of parse errors via a dialog. * If the package is successfully parsed, the user is notified to turn on the install unknown * applications setting. A memory check is made at this point and the user is notified of out * of memory conditions if any. If the package is already existing on the device, * a confirmation dialog (to replace the existing package) is presented to the user. * Based on the user response the package is then installed by launching InstallAppConfirm * sub activity. All state transitions are handled in this activity */
// load dummy layout with OK button disabled until we override this layout in // startInstallConfirm bindUi(R.layout.install_confirm, false); checkIfAllowedAndInitiateInstall(); }
/** * Send package to the package manager and handle results from package manager. Once the * installation succeeds, start {@link InstallSuccess} or {@link InstallFailed}. * <p>This has two phases: First send the data to the package manager, then waituntil the package * manager processed the result.</p> */
这里的InstallEventReceiver是继承自BroadcastReceiver的广播接收器,可以看作是一个中间层,真正保存观察者的类是EventResultPersister,对于EventResultPersister注释是这样的:Persists results of events and calls back observers when a matching result arrives.
// This is the first onResume in a single life of the activity if (mInstallingTask == null) { PackageInstallerinstaller= getPackageManager().getPackageInstaller(); PackageInstaller.SessionInfosessionInfo= installer.getSessionInfo(mSessionId);
if (sessionInfo != null && !sessionInfo.isActive()) { mInstallingTask = newInstallingAsyncTask(); mInstallingTask.execute(); } else { // we will receive a broadcast when the install is finished mCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } } } /** * Send the package to the package installer and then register a event result observer that * will call {@link #launchFinishBasedOnResult(int, int, String)} */ privatefinalclassInstallingAsyncTaskextendsAsyncTask<Void, Void, PackageInstaller.Session> { volatileboolean isDone;
if (!wasSealed) { // Persist the fact that we've sealed ourselves to prevent // mutations of any hard links we create. We do this without holding // the session lock, since otherwise it's a lock inversion. mCallback.onSessionSealedBlocking(this); } }
voiddoHandleMessage(Message msg) { switch (msg.what) { case INIT_COPY: { HandlerParamsparams= (HandlerParams) msg.obj; intidx= mPendingInstalls.size(); if (DEBUG_INSTALL) Slog.i(TAG, "init_copy idx=" + idx + ": " + params); // If a bind was already initiated we dont really // need to do anything. The pending install // will be processed later on. if (!mBound) { Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS", System.identityHashCode(mHandler)); // If this is the only one pending we might // have to bind to the service again. if (!connectToService()) { Slog.e(TAG, "Failed to bind to media container service"); params.serviceError(); Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS", System.identityHashCode(mHandler)); if (params.traceMethod != null) { Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod, params.traceCookie); } return; } else { // Once we bind to the service, the first // pending request will be processed. mPendingInstalls.add(idx, params); } } else { mPendingInstalls.add(idx, params); // Already bound to the service. Just make // sure we trigger off processing the first request. if (idx == 0) { mHandler.sendEmptyMessage(MCS_BOUND); } } break; } }
elseif (mPendingInstalls.size() > 0) { HandlerParamsparams= mPendingInstalls.get(0); if (params != null) { Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", System.identityHashCode(params)); Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "startCopy"); if (params.startCopy()) { // We are done... look for more work or to // go idle. if (DEBUG_SD_INSTALL) Log.i(TAG, "Checking for more work or unbind..."); // Delete pending install if (mPendingInstalls.size() > 0) { mPendingInstalls.remove(0); } if (mPendingInstalls.size() == 0) { if (mBound) { if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting delayed MCS_UNBIND"); removeMessages(MCS_UNBIND); Messageubmsg= obtainMessage(MCS_UNBIND); // Unbind after a little delay, to avoid // continual thrashing. sendMessageDelayed(ubmsg, 10000); } } else { // There are more pending requests in queue. // Just post MCS_BOUND message to trigger processing // of next pending install. if (DEBUG_SD_INSTALL) Log.i(TAG, "Posting MCS_BOUND for next work"); mHandler.sendEmptyMessage(MCS_BOUND); } } Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } }
/* * Invoke remote method to get package information and install * location values. Override install location based on default * policy if needed and then create install arguments based * on the install location. */