最近在学习Android8.0的Launcher源码遇到APP安装过程中监听APK安装过程,顺便就看了一遍APK源码安装的过程。相比较之前的版本APK安装源码有一点出处,由于才疏学浅,什么地方讲不到请多多谅解。文章末尾附上函数调用时序图
在开发过程中有时候需要进行APP更新,或者下载其他APP到本地让后使用以下代码进行安装,我这里单单讲解在执行startActivity之后的运行过程。
Intent intent = new Intent(Intent.ACTION_VIEW); //设置intent的数据类型是应用程序application intent.setDataAndType(Uri.parse("file://" + apkfile.toString()), "application/vnd.android.package-archive"); //为这个新apk开启一个新的activity栈 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); //开始安装 startActivity(intent);
在执行完这几行代码后会启动系统的一个APP进行APK的安装,我们可以通过源码查看在系统中有一个packageinstaller的系统应用。然后首先查看看清单文件
<activity android:name=".InstallStart" android:exported="true" android:excludeFromRecents="true"> <intent-filter android:priority="1"> <action android:name="android.intent.action.VIEW" /> <action android:name="android.intent.action.INSTALL_PACKAGE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file" /> <data android:scheme="content" /> <data android:mimeType="application/vnd.android.package-archive" /> <intent-filter android:priority="1"> <action android:name="android.intent.action.INSTALL_PACKAGE" /> <category android:name="android.intent.category.DEFAULT" /> <data android:scheme="file" /> <data android:scheme="package" /> <data android:scheme="content" /> <intent-filter android:priority="1"> <action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" /> <category android:name="android.intent.category.DEFAULT" />
从中可以发现首先启动的是名为InstallStart的Activity,由于整个源码有点多,因此这里只是贴出部分重要方法。在这里只需要看onCreate的方法
import android.Manifest; import android.app.Activity; import android.content.Intent; import android.content.pm.ApplicationInfo; import android.content.pm.PackageInstaller; import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.util.Log; public class InstallStart extends Activity { private static final String LOG_TAG = InstallStart.class.getSimpleName(); private static final String SCHEME_CONTENT = "content"; private static final String DOWNLOADS_AUTHORITY = "downloads"; private IActivityManager mIActivityManager; private IPackageManager mIPackageManager; private boolean mAbortInstall = false; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); mIPackageManager = AppGlobals.getPackageManager(); Intent intent = getIntent(); String callingPackage = getCallingPackage(); // If the activity was started via a PackageInstaller session, we retrieve the calling // package from that session int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1); if (callingPackage == null && sessionId != -1) { //我们第三发安装sessionId为-1 PackageInstaller packageInstaller = getPackageManager().getPackageInstaller(); PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId); callingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null; } final ApplicationInfo sourceInfo = getSourceInfo(callingPackage); final int originatingUid = getOriginatingUid(sourceInfo); boolean isTrustedSource = false; if (sourceInfo != null && (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) { isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false); } if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) { final int targetSdkVersion = getMaxTargetSdkVersionForUid(originatingUid); if (targetSdkVersion < 0) { Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid); // Invalid originating uid supplied. Abort install. mAbortInstall = true; } else if (targetSdkVersion >= Build.VERSION_CODES.O && !declaresAppOpPermission( originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) { //在现版本第三方安装APK需要申明REQUEST_INSTALL_PACKAGES权限,declaresAppOpPermission检测 //是否申请改权限 Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission " + Manifest.permission.REQUEST_INSTALL_PACKAGES); mAbortInstall = true; } } if (mAbortInstall) { //如果经过上述一系列操作没问题,当然是false setResult(RESULT_CANCELED); finish(); return; } Intent nextActivity = new Intent(intent); nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); // 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())) { //在这里我们第三方app是不会跳转到这里的 nextActivity.setClass(this, PackageInstallerActivity.class); } else { Uri packageUri = intent.getData(); if (packageUri == null) { // if there's nothing to do, quietly slip into the ether Intent result = new Intent(); result.putExtra(Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_FAILED_INVALID_URI); setResult(RESULT_FIRST_USER, result); nextActivity = null; } else { if (packageUri.getScheme().equals(SCHEME_CONTENT)) { nextActivity.setClass(this, InstallStaging.class); } else { nextActivity.setClass(this, PackageInstallerActivity.class); } } } if (nextActivity != null) { //跳转到下一个Activity startActivity(nextActivity); } finish(); } ...... }
经过一系列的解析操作之后会跳转到新的界面
public class PackageInstallerActivity extends OverlayTouchActivity implements OnClickListener { private static final String TAG = "PackageInstaller"; ..... @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); if (icicle != null) { mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY); } mPm = getPackageManager(); //获取PackageManager mIpm = AppGlobals.getPackageManager(); mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE); mInstaller = mPm.getPackageInstaller();//获取PackageInstaller mUserManager = (UserManager) getSystemService(Context.USER_SERVICE); final Intent intent = getIntent(); mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE); mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO); mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID, PackageInstaller.SessionParams.UID_UNKNOWN); mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) ? getPackageNameForUid(mOriginatingUid) : null; final Uri packageUri; if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.equals(intent.getAction())) {//非Android的GooglePaly上的app可以直接忽略了 final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1); final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId); //在PackageInstaller中创建一个Session if (info == null || !info.sealed || info.resolvedBaseCodePath == null) { Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring"); finish(); return; } mSessionId = sessionId;// packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath)); mOriginatingURI = null; mReferrerURI = null; } else { mSessionId = -1; packageUri = intent.getData(); //获取安装APK安装包的路径 mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI); mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER); } // if there's nothing to do, quietly slip into the ether if (packageUri == null) { Log.w(TAG, "Unspecified source"); setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI); finish(); return; } if (DeviceUtils.isWear(this)) { showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR); return; } boolean wasSetUp = processPackageUri(packageUri); if (!wasSetUp) { return; } // load dummy layout with OK button disabled until we override this layout in // startInstallConfirm bindUi(R.layout.install_confirm, false); //安装界面绑定 checkIfAllowedAndInitiateInstall(); //检测此APK是否允许,并且初始化安装前操作界面 } ..... public void onClick(View v) { if (v == mOk) {//点击确定安装按钮 if (mOk.isEnabled()) { if (mOkCanInstall || mScrollView == null) { if (mSessionId != -1) { mInstaller.setPermissionsResult(mSessionId, true); finish(); } else { startInstall(); } } else { mScrollView.pageScroll(View.FOCUS_DOWN); } } } else if (v == mCancel) { // Cancel and finish setResult(RESULT_CANCELED); if (mSessionId != -1) { mInstaller.setPermissionsResult(mSessionId, false); } finish(); } } ..... private void startInstall() { // Start subactivity to actually install the application Intent newIntent = new Intent(); newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo); newIntent.setData(mPackageURI); newIntent.setClass(this, InstallInstalling.class); String installerPackageName = getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME); if (mOriginatingURI != null) { newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI); } if (mReferrerURI != null) { newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI); } if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) { newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid); } if (installerPackageName != null) { newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, installerPackageName); } if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) { newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true); newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT); } if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI); startActivity(newIntent); //跳转到安装进行中的界面 finish(); } ..... }
在上述界面中将APK解析,并分析其中权限信息等内容。此时,点击安装就会进入到下一个界面
public class InstallInstalling extends Activity { private static final String LOG_TAG = InstallInstalling.class.getSimpleName(); private static final String SESSION_ID = "com.android.packageinstaller.SESSION_ID"; private static final String INSTALL_ID = "com.android.packageinstaller.INSTALL_ID"; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.install_installing); ApplicationInfo appInfo = getIntent() .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO); mPackageURI = getIntent().getData(); if ("package".equals(mPackageURI.getScheme())) {//查看是否是package路径Uri,发现明显不是 try { getPackageManager().installExistingPackage(appInfo.packageName); launchSuccess(); } catch (PackageManager.NameNotFoundException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } else { final File sourceFile = new File(mPackageURI.getPath()); PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo, sourceFile), R.id.app_snippet); if (savedInstanceState != null) { //为null mSessionId = savedInstanceState.getInt(SESSION_ID); mInstallId = savedInstanceState.getInt(INSTALL_ID); // Reregister for result; might instantly call back if result was delivered while // activity was destroyed try { InstallEventReceiver.addObserver(this, mInstallId, this::launchFinishBasedOnResult); } catch (EventResultPersister.OutOfIdsException e) { // Does not happen } } else { PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL); params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER); params.originatingUri = getIntent() .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI); params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID, UID_UNKNOWN); File file = new File(mPackageURI.getPath()); try { PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0); params.setAppPackageName(pkg.packageName); params.setInstallLocation(pkg.installLocation); params.setSize( PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride)); } catch (PackageParser.PackageParserException e) { Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults."); Log.e(LOG_TAG, "Cannot calculate installed size " + file + ". Try only apk size."); params.setSize(file.length()); } catch (IOException e) { Log.e(LOG_TAG, "Cannot calculate installed size " + file + ". Try only apk size."); params.setSize(file.length()); } try { mInstallId = InstallEventReceiver .addObserver(this, EventResultPersister.GENERATE_NEW_ID, this::launchFinishBasedOnResult); } catch (EventResultPersister.OutOfIdsException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } try {//在PackageInstaller创建用于通信的session,关于创建Seeion过程这里不再做叙述 mSessionId = getPackageManager().getPackageInstaller().createSession(params); } catch (IOException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } mCancelButton = (Button) findViewById(R.id.cancel_button); mCancelButton.setOnClickListener(view -> { if (mInstallingTask != null) { mInstallingTask.cancel(true); } if (mSessionId > 0) { getPackageManager().getPackageInstaller().abandonSession(mSessionId); mSessionId = 0; } setResult(RESULT_CANCELED); finish(); }); mSessionCallback = new InstallSessionCallback(); } } @Override protected void onResume() { super.onResume(); // This is the first onResume in a single life of the activity if (mInstallingTask == null) { PackageInstaller installer = getPackageManager().getPackageInstaller(); //获取PackageInstaller PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);//根据在onCreate中创建的SeesionID获取相应session信息 if (sessionInfo != null && !sessionInfo.isActive()) { mInstallingTask = new InstallingAsyncTask(); //创建异步任务 mInstallingTask.execute(); //执行异步任务 } else { // we will receive a broadcast when the install is finished mCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } } } private final class InstallingAsyncTask extends AsyncTask, Void, PackageInstaller.Session> { volatile boolean isDone; @Override protected PackageInstaller.Session doInBackground(Void... params) { PackageInstaller.Session session; try {//根据SessionId打开PackageInstaller端Session session = getPackageManager().getPackageInstaller().openSession(mSessionId); } catch (IOException e) { return null; } session.setStagingProgress(0); try { File file = new File(mPackageURI.getPath());//获取APK安装包 try (InputStream in = new FileInputStream(file)) { long sizeBytes = file.length(); try (OutputStream out = session .openWrite("PackageInstaller", 0, sizeBytes)) { byte[] buffer = new byte[1024 * 1024]; while (true) {//读取APK,并通过Session传递APK安装包 int numRead = in.read(buffer); if (numRead == -1) { session.fsync(out); break; } if (isCancelled()) { session.close(); break; } out.write(buffer, 0, numRead); if (sizeBytes > 0) { float fraction = ((float) numRead / (float) sizeBytes); session.addProgress(fraction); } } } } return session; } catch (IOException | SecurityException e) { Log.e(LOG_TAG, "Could not write package", e); session.close(); return null; } finally { synchronized (this) { isDone = true; notifyAll(); } } } @Override protected void onPostExecute(PackageInstaller.Session session) { if (session != null) { Intent broadcastIntent = new Intent(BROADCAST_ACTION); broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND); broadcastIntent.setPackage( getPackageManager().getPermissionControllerPackageName()); broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId); PendingIntent pendingIntent = PendingIntent.getBroadcast( InstallInstalling.this, mInstallId, broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT); session.commit(pendingIntent.getIntentSender()); mCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } else { getPackageManager().getPackageInstaller().abandonSession(mSessionId); if (!isCancelled()) { launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null); } } } } }
在上述操作中,主要是完成以下步骤
在这一步操作中用到PackageInstaller,下面看一下这部分代码。由于代码太多,这里只做部分方法讲解。
public PackageInstaller(IPackageInstaller installer, String installerPackageName, int userId) { mInstaller = installer; mInstallerPackageName = installerPackageName; mUserId = userId; }
此方法中主要是成员变量的赋值,在这里需要注意的主要是mInstaller成员变量。这里是远程PackageInstallerServices对象,在后面多处用到。
public int createSession(@NonNull SessionParams params) throws IOException { try { return mInstaller.createSession(params, mInstallerPackageName, mUserId); } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
看到此方法,发现也是通过远程对象创建的
public @NonNull Session openSession(int sessionId) throws IOException { try { return new Session(mInstaller.openSession(sessionId)); } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
此方法中根据远程对象打开Session的返回值创建本地Session。
由上面最后安装界面可知,在最后创建异步任务中打开Session,打开之后就是调用openWrite方法开始进行APK安装包传输
public @NonNull OutputStream openWrite(@NonNull String name, long offsetBytes, long lengthBytes) throws IOException { try { if (ENABLE_REVOCABLE_FD) { return new ParcelFileDescriptor.AutoCloseOutputStream( mSession.openWrite(name, offsetBytes, lengthBytes)); } else { final ParcelFileDescriptor clientSocket = mSession.openWrite(name, offsetBytes, lengthBytes); return new FileBridge.FileBridgeOutputStream(clientSocket); } } catch (RuntimeException e) { ExceptionUtils.maybeUnwrapIOException(e); throw e; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
由此方法可以看出最后还是调用远程Session的openWrite方法。
在安装最后一个界面的最后的异步任务中执行了session.fsync(out)方法,下面看看这个方法
public void fsync(@NonNull OutputStream out) throws IOException { if (ENABLE_REVOCABLE_FD) { if (out instanceof ParcelFileDescriptor.AutoCloseOutputStream) { try { Os.fsync(((ParcelFileDescriptor.AutoCloseOutputStream) out).getFD()); } catch (ErrnoException e) { throw e.rethrowAsIOException(); } } else { throw new IllegalArgumentException("Unrecognized stream"); } } else { if (out instanceof FileBridge.FileBridgeOutputStream) { ((FileBridge.FileBridgeOutputStream) out).fsync(); } else { throw new IllegalArgumentException("Unrecognized stream"); } } }这个方法就是将读取到内存中的文件保存到本地。
public void commit(@NonNull IntentSender statusReceiver) { try { mSession.commit(statusReceiver, false); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
最后调用的还是远程的commit方法,下面看一看远程的PackageInstallerSession
@Override public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) { Preconditions.checkNotNull(statusReceiver); final boolean wasSealed; synchronized (mLock) { assertCallerIsOwnerOrRootLocked(); assertPreparedAndNotDestroyedLocked("commit"); final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter( mContext, statusReceiver, sessionId, isInstallerDeviceOwnerLocked(), userId); mRemoteObserver = adapter.getBinder(); if (forTransfer) { mContext.enforceCallingOrSelfPermission(Manifest.permission.INSTALL_PACKAGES, null); if (mInstallerUid == mOriginalInstallerUid) { throw new IllegalArgumentException("Session has not been transferred"); } } else { if (mInstallerUid != mOriginalInstallerUid) { throw new IllegalArgumentException("Session has been transferred"); } } wasSealed = mSealed; if (!mSealed) { try { sealAndValidateLocked(); } catch (IOException e) { throw new IllegalArgumentException(e); } catch (PackageManagerException e) { destroyInternal(); // Cannot call dispatchFinal synchronous as this might be called from inside the // system server on the main thread. Hence the call back scheduled in // dispachFinal has to be scheduled on a different thread. mHandler.obtainMessage(MSG_SESSION_FINISHED_WITH_EXCEPTION, e).sendToTarget(); return; } } // Client staging is fully done at this point mClientProgress = 1f; computeProgressLocked(true); // This ongoing commit should keep session active, even though client // will probably close their end. mActiveCount.incrementAndGet(); mCommitted = true; mHandler.obtainMessage(MSG_COMMIT).sendToTarget(); } 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); } }
由此方法可以,最后发送了一个MSG_COMMIT方法。下面看一下定义的Handler。
private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { switch (msg.what) { case MSG_COMMIT: synchronized (mLock) { try { commitLocked(); } catch (PackageManagerException e) { final String completeMsg = ExceptionUtils.getCompleteMessage(e); Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); destroyInternal(); dispatchSessionFinished(e.error, completeMsg, null); } } break; case MSG_SESSION_FINISHED_WITH_EXCEPTION: PackageManagerException e = (PackageManagerException) msg.obj; dispatchSessionFinished(e.error, ExceptionUtils.getCompleteMessage(e), null); break; } return true; } };
此方法也是相当简单,只需要看commitLocker()方法。
private void commitLocked() throws PackageManagerException { if (mDestroyed) { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed"); } if (!mSealed) { throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed"); } Preconditions.checkNotNull(mPackageName); Preconditions.checkNotNull(mSignatures); Preconditions.checkNotNull(mResolvedBaseFile); ..... mRelinquished = true; mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params, mInstallerPackageName, mInstallerUid, user, mCertificates); }至此,PackageInstallerSession的工作就完成了。最后,将接力棒给了PackageManagerServices去执行。
void installStage(String packageName, File stagedDir, String stagedCid, IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams, String installerPackageName, int installerUid, UserHandle user, Certificate[][] certificates) { ..... final Message msg = mHandler.obtainMessage(INIT_COPY); final int installReason = fixUpInstallReason(installerPackageName, installerUid, sessionParams.installReason); final InstallParams params = new InstallParams(origin, null, observer, sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid, verificationInfo, user, sessionParams.abiOverride, sessionParams.grantedRuntimePermissions, certificates, installReason); params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params)); msg.obj = params; ..... mHandler.sendMessage(msg); }
从此方法中可以看出发送了一个INIT_COPY消息
void doHandleMessage(Message msg) { switch (msg.what) { case INIT_COPY: { HandlerParams params = (HandlerParams) msg.obj; int idx = 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; } '' '' ' }'再次方法中可以看出首先需要执行连接服务操作,让后将参数添加到mPendingInstalls变量中。首先我们可以看连接服务的方法
private boolean connectToService() { if (DEBUG_SD_INSTALL) Log.i(TAG, "Trying to bind to" + " DefaultContainerService"); Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT); Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); if (mContext.bindServiceAsUser(service, mDefContainerConn, Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) { Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); mBound = true; return true; } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); return false; }
使用mDefaultContainerConn连接DefaultContainerServer服务,然后看一下这个变量定义处
final private DefaultContainerConnection mDefContainerConn = new DefaultContainerConnection(); class DefaultContainerConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected"); final IMediaContainerService imcs = IMediaContainerService.Stub .asInterface(Binder.allowBlocking(service)); mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); } public void onServiceDisconnected(ComponentName name) { if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected"); } }
可以看出连接成功之后会发送一个MCS_BOUND消息,后边看这个消息的处理过程
void doHandleMessage(Message msg) { switch (msg.what) { ..... case MCS_BOUND: { if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound"); if (msg.obj != null) { mContainerService = (IMediaContainerService) msg.obj; Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "bindingMCS", System.identityHashCode(mHandler)); } if (mContainerService == null) { if (!mBound) { // Something seriously wrong since we are not bound and we are not // waiting for connection. Bail out. Slog.e(TAG, "Cannot bind to media container service"); for (HandlerParams params : mPendingInstalls) { // Indicate service bind error params.serviceError(); Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "queueInstall", System.identityHashCode(params)); if (params.traceMethod != null) { Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, params.traceMethod, params.traceCookie); } return; } mPendingInstalls.clear(); } else { Slog.w(TAG, "Waiting to connect to media container service"); } } else if (mPendingInstalls.size() > 0) { HandlerParams params = 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()) { //进行APK文件的拷贝 // 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); Message ubmsg = 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); } } else { // Should never happen ideally. Slog.w(TAG, "Empty queue"); } break; } ..... }
在这里面params.startCopy()这行代码就是要开始APK安装包的拷贝过程,可以查看InstallParams类中的startcopy方法。可以看到InstallParams继承HandlerParams,startCopy()方法的实现在HandlerParams类中。接下来看看HandlerParams中startCopy()方法
final boolean startCopy() { boolean res; ..... handleStartCopy();//进行拷贝文件 ..... handleReturnCode();//处理拷贝操作返回的操作 return res; }
这个方法中主要有两个方法,一个是进行APK拷贝文件的操作,一个是处理拷贝文件后的操作。首先看拷贝文件的操作
从源码中可以看出在HandleParams类中并没有实现,是一个抽象方法。再进入PackageManageService开始创建的是InstallParams对象,可以看InstallParams中handleStartCopy()方法实现
public void handleStartCopy() throws RemoteException { int ret = PackageManager.INSTALL_SUCCEEDED; ...... if (ret == PackageManager.INSTALL_SUCCEEDED) { ..... } final InstallArgs args = createInstallArgs(this); //创建用于实际拷贝APK安装包的对象 mArgs = args; if (ret == PackageManager.INSTALL_SUCCEEDED) { ...... if (!origin.existing && requiredUid != -1 && isVerificationEnabled( verifierUser.getIdentifier(), installFlags, installerUid)) { //判断是否有权限 ...... } } else { ret = args.copyApk(mContainerService, true); //拷贝APK安装包 } } mRet = ret; }从此方法可以看出APK的拷贝工作又交给另外的对象进行处理,看一下InstallArgs的创建时根据变量来创建不同的对象。这里不用看其他的,直接看FileInstallArgs中的copyApk()方法。
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk"); try { return doCopyApk(imcs, temp); } finally { Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); } } private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { if (origin.staged) { //在创建的时候设置为ture,因为在一开始已经通过Session拷贝APK if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy"); codeFile = origin.file; resourceFile = origin.file; return PackageManager.INSTALL_SUCCEEDED; } ..... }在copyApk()中又调用doCopyApk()方法进行拷贝工作。在新版本中主要是通过Session拷贝APK,因此在doCopyApk()方法中首先进行判断,而此状态变量在一开始创建的时候已经初始化为true。我感觉有必要看一下次变量初始化的地方
static OriginInfo fromStagedFile(File file) { return new OriginInfo(file, null, true, false); } static OriginInfo fromStagedContainer(String cid) { return new OriginInfo(null, cid, true, false); } private OriginInfo(File file, String cid, boolean staged, boolean existing) { this.file = file; this.cid = cid; this.staged = staged; this.existing = existing; '''''' }
由此可见,拷贝APK的工作在doCopyApk()开始之初已经返回。接下来看拷贝APK操作之后操作。
void handleReturnCode() { // If mArgs is null, then MCS couldn't be reached. When it // reconnects, it will try again to install. At that point, this // will succeed. if (mArgs != null) { processPendingInstall(mArgs, mRet); } }
擦,直接调用了另外一个方法
private void processPendingInstall(final InstallArgs args, final int currentStatus) { // Queue up an async operation since the package installation may take a little while. mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); // Result object to be returned PackageInstalledInfo res = new PackageInstalledInfo(); res.setReturnCode(currentStatus); res.uid = -1; res.pkg = null; res.removedInfo = null; if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { args.doPreInstall(res.returnCode);//1、apk安装前操作,删除安装残留文件 synchronized (mInstallLock) { installPackageTracedLI(args, res); //2、apk安装 } args.doPostInstall(res.returnCode, res.uid);//3、 } // A restore should be performed at this point if (a) the install // succeeded, (b) the operation is not an update, and (c) the new // package has not opted out of backup participation. final boolean update = res.removedInfo != null && res.removedInfo.removedPackage != null; final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags; boolean doRestore = !update && ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0); ...... if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {//从前面可知update和doRestore都为false ...... } if (!doRestore) { ...... Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);//发送消息 mHandler.sendMessage(msg); } } }); }
此方法中又四步操作
从上述操作还是到handler中,查看doHandlerMessage消息
void doHandleMessage(Message msg) { switch (msg.what) { ..... case POST_INSTALL: { if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1); PostInstallData data = mRunningInstalls.get(msg.arg1); final boolean didRestore = (msg.arg2 != 0); mRunningInstalls.delete(msg.arg1); if (data != null) { ...... handlePackagePostInstall(parentRes, grantPermissions, killApp, virtualPreload, grantedPermissions, didRestore, args.installerPackageName, args.observer); // Handle the child packages final int childCount = (parentRes.addedChildPackages != null) ? parentRes.addedChildPackages.size() : 0; for (int i = 0; i < childCount; i++) { PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i); handlePackagePostInstall(childRes, grantPermissions, killApp, virtualPreload, grantedPermissions, false /*didRestore*/, args.installerPackageName, args.observer); } ...... } break; ...... }
在此处可以看到执行handlePackagePosetInstall()方法
private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions, boolean killApp, boolean virtualPreload, String[] grantedPermissions, boolean launchedForRestore, String installerPackage, IPackageInstallObserver2 installObserver) { if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { // Send the removed broadcasts if (res.removedInfo != null) { res.removedInfo.sendPackageRemovedBroadcasts(killApp); } // Now that we successfully installed the package, grant runtime // permissions if requested before broadcasting the install. Also // for legacy apps in permission review mode we clear the permission // review flag which is used to emulate runtime permissions for // legacy apps. if (grantPermissions) {//此处表示已经安装成功,现在在发送广播之前赋予运行权限 grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions); } ...... // Send installed broadcasts if the package is not a static shared lib. if (res.pkg.staticSharedLibName == null) { mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath); // Send added for users that see the package for the first time // sendPackageAddedForNewUsers also deals with system apps int appId = UserHandle.getAppId(res.uid); boolean isSystem = res.pkg.applicationInfo.isSystemApp(); sendPackageAddedForNewUsers(packageName, isSystem || virtualPreload, virtualPreload /*startReceiver*/, appId, firstUsers); //发送APK安装成功的消息 // Send added for users that don't see the package for the first time Bundle extras = new Bundle(1); extras.putInt(Intent.EXTRA_UID, res.uid); if (update) { extras.putBoolean(Intent.EXTRA_REPLACING, true); } sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName, extras, 0 /*flags*/, null /*targetPackage*/, null /*finishedReceiver*/, updateUsers);//发送APK安装成功的消息 ...... for (int userId : firstUsers) { PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId); // There's a race currently where some install events may interleave with an uninstall. // This can lead to package info being null (b/36642664). if (info != null) { mDexManager.notifyPackageInstalled(info, userId); } } } // If someone is watching installs - notify them if (installObserver != null) { try { Bundle extras = extrasForInstallResult(res); installObserver.onPackageInstalled(res.name, res.returnCode, res.returnMsg, extras); } catch (RemoteException e) { Slog.i(TAG, "Observer no longer exists."); } } }
此函数主要是受运行权限,并且发送已经安装成功广播。至此,此安装过程完成。附上函数调用时序图