前言 在本系列上一篇文章Android包管理機制(一)PackageInstaller的初始化中我們學習了PackageInstaller是如何初始化的,這一篇文章我們接著學習PackageInstaller是如何安裝APK的。本系列文章的源碼基於Android8.0。 1.PackageInsta ...
前言
在本系列上一篇文章Android包管理機制(一)PackageInstaller的初始化中我們學習了PackageInstaller是如何初始化的,這一篇文章我們接著學習PackageInstaller是如何安裝APK的。本系列文章的源碼基於Android8.0。
1.PackageInstaller中的處理
緊接著上一篇的內容,在PackageInstallerActivity調用startInstallConfirm方法初始化安裝確認界面後,這個安裝確認界面就會呈現給用戶,用戶如果想要安裝這個應用程式就會點擊確定按鈕,就會調用PackageInstallerActivity的onClick方法,如下所示。
packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
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();//1 } } else { mScrollView.pageScroll(View.FOCUS_DOWN); } } } else if (v == mCancel) { ... finish(); } }View Code
onClick方法中分別對確定和取消按鈕做處理,主要查看對確定按鈕的處理,註釋1處調用了startInstall方法:
packages/apps/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
private void startInstall() { Intent newIntent = new Intent(); newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo); newIntent.setData(mPackageURI);//1 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(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI); startActivity(newIntent); finish(); }View Code
startInstall方法用於跳轉到InstallInstalling這個Activity,並關閉掉當前的PackageInstallerActivity。InstallInstalling主要用於向包管理器發送包的信息並處理包管理的回調。 InstallInstalling的onCreate方法如下所示。
packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@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())) { try { getPackageManager().installExistingPackage(appInfo.packageName); launchSuccess(); } catch (PackageManager.NameNotFoundException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } else { //根據mPackageURI創建一個對應的File final File sourceFile = new File(mPackageURI.getPath()); PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo, sourceFile), R.id.app_snippet); //如果savedInstanceState不為null,獲取此前保存的mSessionId和mInstallId if (savedInstanceState != null) {//1 mSessionId = savedInstanceState.getInt(SESSION_ID); mInstallId = savedInstanceState.getInt(INSTALL_ID); //向InstallEventReceiver註冊一個觀察者 try { InstallEventReceiver.addObserver(this, mInstallId, this::launchFinishBasedOnResult);//2 } catch (EventResultPersister.OutOfIdsException e) { } } else { PackageInstaller.SessionParams params = new PackageInstaller.SessionParams( PackageInstaller.SessionParams.MODE_FULL_INSTALL);//3 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());//4 try { PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);//5 params.setAppPackageName(pkg.packageName); params.setInstallLocation(pkg.installLocation); params.setSize( PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride)); } catch (PackageParser.PackageParserException e) { ... } try { mInstallId = InstallEventReceiver .addObserver(this, EventResultPersister.GENERATE_NEW_ID, this::launchFinishBasedOnResult);//6 } catch (EventResultPersister.OutOfIdsException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } try { mSessionId = getPackageManager().getPackageInstaller().createSession(params);//7 } catch (IOException e) { launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null); } } ... mSessionCallback = new InstallSessionCallback(); } }View Code
onCreate方法中會分別對package和content協議的Uri進行處理,我們來看content協議的Uri處理部分。註釋1處如果savedInstanceState不為null,獲取此前保存的mSessionId和mInstallId,其中mSessionId是安裝包的會話id,mInstallId是等待的安裝事件id。註釋2處根據mInstallId向InstallEventReceiver註冊一個觀察者,launchFinishBasedOnResult會接收到安裝事件的回調,無論安裝成功或者失敗都會關閉當前的Activity(InstallInstalling)。如果savedInstanceState為null,代碼的邏輯也是類似的,註釋3處創建SessionParams,它用來代表安裝會話的參數,註釋4、5處根據mPackageUri對包(APK)進行輕量級的解析,並將解析的參數賦值給SessionParams。註釋6處和註釋2處類似向InstallEventReceiver註冊一個觀察者返回一個新的mInstallId,其中InstallEventReceiver繼承自BroadcastReceiver,用於接收安裝事件並回調給EventResultPersister。 註釋7處PackageInstaller的createSession方法內部會通過IPackageInstaller與PackageInstallerService進行進程間通信,最終調用的是PackageInstallerService的createSession方法來創建並返回mSessionId。
InstallInstalling的onCreate方法就分析到這,接著查看InstallInstalling的onResume方法:
packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@Override protected void onResume() { super.onResume(); if (mInstallingTask == null) { PackageInstaller installer = getPackageManager().getPackageInstaller(); PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);//1 if (sessionInfo != null && !sessionInfo.isActive()) {//2 mInstallingTask = new InstallingAsyncTask(); mInstallingTask.execute(); } else { mCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } } }View Code
註釋1處根據mSessionId得到SessionInfo,SessionInfo代表安裝會話的詳細信息。註釋2處如果sessionInfo不為Null並且不是活動的,就創建並執行InstallingAsyncTask。InstallingAsyncTask的doInBackground方法中會根據包(APK)的Uri,將APK的信息通過IO流的形式寫入到PackageInstaller.Session中。InstallingAsyncTask的onPostExecute方法如下所示。
packages/apps/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@Override protected void onPostExecute(PackageInstaller.Session session) { if (session != null) { Intent broadcastIntent = new Intent(BROADCAST_ACTION); 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());//1 mCancelButton.setEnabled(false); setFinishOnTouchOutside(false); } else { getPackageManager().getPackageInstaller().abandonSession(mSessionId); if (!isCancelled()) { launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null); } } }View Code
創建了一個PendingIntent,並將該PendingIntent的IntentSender通過註釋1處的PackageInstaller.Session的commit方法發送出去,發送去哪了呢?接著查看PackageInstaller.Session的commit方法。
frameworks/base/core/java/android/content/pm/PackageInstaller.java
public void commit(@NonNull IntentSender statusReceiver) { try { mSession.commit(statusReceiver); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }View Code
mSession的類型為IPackageInstallerSession,這說明要通過IPackageInstallerSession來進行進程間的通信,最終會調用PackageInstallerSession的commit方法,這樣代碼邏輯就到了Java框架層的。
2.Java框架層的處理
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
@Override public void commit(IntentSender statusReceiver) { Preconditions.checkNotNull(statusReceiver); ... mActiveCount.incrementAndGet(); final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext, statusReceiver, sessionId, mIsInstallerDeviceOwner, userId); mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();//1 }View Code
commit方法中會將包的信息封裝為PackageInstallObserverAdapter ,它在PMS中被定義。在註釋1處會向Handler發送一個類型為MSG_COMMIT的消息,其中adapter.getBinder()
會得到IPackageInstallObserver2.Stub類型的觀察者,從類型就知道這個觀察者是可以跨進程進行回調的。處理該消息的代碼如下所示。
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
private final Handler.Callback mHandlerCallback = new Handler.Callback() { @Override public boolean handleMessage(Message msg) { final PackageInfo pkgInfo = mPm.getPackageInfo( params.appPackageName, PackageManager.GET_SIGNATURES | PackageManager.MATCH_STATIC_SHARED_LIBRARIES /*flags*/, userId); final ApplicationInfo appInfo = mPm.getApplicationInfo( params.appPackageName, 0, userId); synchronized (mLock) { if (msg.obj != null) { mRemoteObserver = (IPackageInstallObserver2) msg.obj;//1 } try { commitLocked(pkgInfo, appInfo);//2 } catch (PackageManagerException e) { final String completeMsg = ExceptionUtils.getCompleteMessage(e); Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg); destroyInternal(); dispatchSessionFinished(e.error, completeMsg, null);//3 } return true; } } };View Code
註釋1處獲取IPackageInstallObserver2類型的觀察者mRemoteObserver,註釋2處的commitLocked方法如下所示。
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo) throws PackageManagerException { ... mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params, installerPackageName, installerUid, user, mCertificates); }View Code
commitLocked方法比較長,這裡截取最主要的信息,會調用PMS的installStage方法,這樣代碼邏輯就進入了PMS中。
回到mHandlerCallback的handleMessage方法,如果commitLocked方法出現PackageManagerException異常,就會調用註釋3處的dispatchSessionFinished方法,它的實現如下所示:
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
private void dispatchSessionFinished(int returnCode, String msg, Bundle extras) { mFinalStatus = returnCode; mFinalMessage = msg; if (mRemoteObserver != null) { try { mRemoteObserver.onPackageInstalled(mPackageName, returnCode, msg, extras);//1 } catch (RemoteException ignored) { } } ... }View Code
註釋1處會調用IPackageInstallObserver2的onPackageInstalled方法,具體是實現在PackageInstallObserver類中:
frameworks/base/core/java/android/app/PackageInstallObserver.java
public class PackageInstallObserver { private final IPackageInstallObserver2.Stub mBinder = new IPackageInstallObserver2.Stub() { ... @Override public void onPackageInstalled(String basePackageName, int returnCode, String msg, Bundle extras) { PackageInstallObserver.this.onPackageInstalled(basePackageName, returnCode, msg, extras);//1 } };View Code
註釋1處調用了PackageInstallObserver的onPackageInstalled方法,實現這個方法的類為PackageInstallObserver的子類、前面提到的PackageInstallObserverAdapter。總結一下就是dispatchSessionFinished方法會通過mRemoteObserver的onPackageInstalled方法,將Complete方法出現的PackageManagerException的異常信息回調給PackageInstallObserverAdapter。
3.總結
本篇文章講解了PackageInstaller安裝APK的過程,簡單來說就兩步:
- 將APK的信息通過IO流的形式寫入到PackageInstaller.Session中。
- 調用PackageInstaller.Session的commit方法,將APK的信息交由PMS處理。
由於PMS中對APK安裝的處理比較複雜,因此關於PMS的處理部分會在本系列的下一篇文章進行講解。