---
title: 'PackageInstallerActivity 安裝'
disqus: kyleAlien
---
PackageInstallerActivity 安裝
===
## OverView of Content
[TOC]
## 安裝 APK
安裝 APK 有以下幾種方式
1. 透過 Adb 指令進行安裝
2. 手動下載 APK 檔案,通過系統安裝器 PackageInstaller 安裝 APK
3. 手機開機時,安裝應用
4. 通過電腦 or 應用商店安裝
以下說明第二點,**手動下載 APK 檔案,通過系統安裝器 PackageInstaller 安裝 APK**
### 安裝重點步驟
* 重點步驟如下
1. 把 APK 的信息通過 IO 寫入 PackageInstaller#Session 中
2. 調用 Session#commit 後,透過 PKMS 安裝 APK
3. 將 APK 複製到 `/data` 目錄下
### 隱式呼叫 InstallStart - 入口
* 查看在 PackageInstaller 目錄下的 [**AndroidManifest.xml 文件**](https://android.googlesource.com/platform/frameworks/base/+/master/packages/PackageInstaller/AndroidManifest.xml),可以看到 Activity 的入口
```xml=
// AndroidManifest.xml
<activity android:name=".InstallStart"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
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="content" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.intent.action.INSTALL_PACKAGE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="package" />
<data android:scheme="content" />
</intent-filter>
<intent-filter android:priority="1">
<action android:name="android.content.pm.action.CONFIRM_INSTALL" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
```
:::warning
* 在 Android 7.0 以後,如果 Intent 對外暴露 file://Uri 則會引發 FileUriExposedException 異常,7.0 以後必須透過 FileProvider 獲取 Uri
```java=
// 版本差異的安裝
public void installApp(Object data) {
Intent intent = new Intent(Intent.ACTION_VIEW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_ACTIVITY_NEW_TASK);
// 透過 FileProvider 獲取 Uri
Uri contentUri = FileProvider.getUriForFile(activity, BuildConfig.APPLICATION_ID + ".fileProvider", new File((String) data));
intent.setDataAndType(contentUri, "application/vnd.android.package-archive");
} else {
// 7.0 以前可以透過 File uri 傳遞
intent.setDataAndType(Uri.fromFile(new File((String) data)), "application/vnd.android.package-archive");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
}
activity.startActivity(intent);
}
```
:::
> 
### [PackageInstallerActivity](https://android.googlesource.com/platform/frameworks/base/+/master/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java) - 安裝 APK 視窗
* 系統安裝方式是透過 PackageInstallerActivity (在 [**packages 目錄**](https://android.googlesource.com/platform/frameworks/base/+/master/packages/PackageInstaller/src/com/android/packageinstaller)下),其中有幾個重要成員我們需要先知道他們的功能(如下表)
| 使用類 | 功能 |
| -------- | -------- |
| PackageManager | 給應用 APP 提供功能的抽象,最終會透過 PKMS 來實現 |
| IPackageManager | AIDL 接口,用來與 PKMS 通訊 |
| AppOpsManager | 動態檢測權限(Android 4.3 後) |
| PackageInstaller | 安裝、升級、卸載 APP 的服務 |
| UserManager | 用來多用戶管理 |
* 安裝 APK 步驟如下
1. **onCreate**:取得 intent,並取得要安裝的 Package 的 Uri,該 Uri 可以直接透過 intent#action 傳入,也可以透過 intent#data 帶入
> 主要差異是 SessionId
```java=
// PackageInstallerActivity.java
private int mSessionId = -1;
private String mOriginatingPackage; // The package name corresponding to #mOriginatingUid
ApplicationInfo mSourceInfo;
PackageInstaller mInstaller;
@Override
protected void onCreate(Bundle icicle) {
// 該 Activity 視窗,將該 Window 設定在最上層
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
// 取得 PKMS 的通訊代理
mPm = getPackageManager();
... 省略
// 取得安裝進程的代理
mInstaller = mPm.getPackageInstaller();
... 省略
final Intent intent = getIntent();
// 呼叫者
mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
// 呼叫者屬性
mCallingAttributionTag = intent.getStringExtra(EXTRA_CALLING_ATTRIBUTION_TAG);
// 呼叫者的訊息
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;
// Package Uri
final Uri packageUri;
if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
// 已經準備好安裝
final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
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;
// 透過 intent#getData 取得 Uri
packageUri = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
// 如果沒辦法取得 Uri 則直接 關閉 Activity
if (packageUri == null) {
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
finish();
return;
}
... 不支援穿戴裝置
// @ processPackageUri 分析
boolean wasSetUp = processPackageUri(packageUri);
if (mLocalLOGV) Log.i(TAG, "wasSetUp: " + wasSetUp);
if (!wasSetUp) {
return;
}
}
```
* **processPackageUri 分析 Uri 協議:取得 PackageInfo、創建 [PackageUtil](https://android.googlesource.com/platform/frameworks/base/+/master/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java)#AppSnippet**:支持兩種 scheme,^1.^ package (可以從 PKMS 取得)、^2.^ file (透過分析 Uri 的 Path,其實也是透過 PKMS 取得)
:::info
* AppSnippet 是 APK 的基本資訊,像是 APP Icon、name... 等等訊息
:::
```java=
// ContentResolver.java
public static final String SCHEME_FILE = "file";
// -------------------------------------------------------------------
// PackageInstallerActivity.java
private PackageUtil.AppSnippet mAppSnippet;
PackageInfo mPkgInfo;
PackageManager mPm;
static final String SCHEME_PACKAGE = "package";
private boolean processPackageUri(final Uri packageUri) {
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
...log 訊息
switch (scheme) {
case SCHEME_PACKAGE: { // "package"
try {
mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS
| PackageManager.MATCH_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
}
if (mPkgInfo == null) {
... 獲取失敗
return false;
}
CharSequence label = mPm.getApplicationLabel(mPkgInfo.applicationInfo);
... log
mAppSnippet = new PackageUtil.AppSnippet(label,
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} break;
case ContentResolver.SCHEME_FILE: { // "file"
// 透過 path 獲取 PackageInfo
File sourceFile = new File(packageUri.getPath());
// 對 parsed 進行進一步的處理,並獲得 PackageInfo
mPkgInfo = PackageUtil.getPackageInfo(this, sourceFile,
PackageManager.GET_PERMISSIONS);
// Check for parse errors
if (mPkgInfo == null) {
... 獲取失敗
return false;
}
... log
mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
} break;
default: {
throw new IllegalArgumentException("Unexpected URI scheme " + packageUri);
}
}
return true;
}
```
> 
2. **onResume**:透過 bindUi 方法,顯示安裝提示 Dialog
```java=
// PackageInstallerActivity.java
@Override
protected void onResume() {
super.onResume();
... log
if (mAppSnippet != null) {
// @ 分析 build ui
bindUi();
// @ checkIfAllowedAndInitiateInstall
// 判斷是否是未知來源的 應用,
// 如果開啟允須安裝未知來源的選項,則直接進行初始化安裝
checkIfAllowedAndInitiateInstall();
}
if (mOk != null) {
mOk.setEnabled(mEnableOk);
}
}
private void checkIfAllowedAndInitiateInstall() {
// 首先檢查用戶對應用安裝的限制
final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
// 不允許安裝
if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
... log
showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
return;
} else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
... log
startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
finish();
return;
}
// 是否是未知來源、是否允許未知來源安裝 ?
if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
if (mLocalLOGV) Log.i(TAG, "install allowed");
// @ initiateInstall 直接進行初始安裝
initiateInstall();
} else {
// Check for unknown sources restrictions.
final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
final int unknownSourcesGlobalRestrictionSource = mUserManager.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY, Process.myUserHandle());
final int systemRestriction = UserManager.RESTRICTION_SOURCE_SYSTEM
& (unknownSourcesRestrictionSource | unknownSourcesGlobalRestrictionSource);
// 跳出未知來源警告視窗
if (systemRestriction != 0) {
if (mLocalLOGV) Log.i(TAG, "Showing DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER");
showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
} else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
startAdminSupportDetailsActivity(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
} else if (unknownSourcesGlobalRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
startAdminSupportDetailsActivity(
UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES_GLOBALLY);
} else {
// 處理未知來源 APK
handleUnknownSources();
}
}
}
private void bindUi() {
// mAlert 是父類 member、類型是 AlertController
mAlert.setIcon(mAppSnippet.icon);
mAlert.setTitle(mAppSnippet.label);
mAlert.setView(R.layout.install_content_view);
// 按下確定
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
(ignored, ignored2) -> {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
finish();
} else {
// @ 分析 startInstall
startInstall();
}
}
}, null);
// 取消 Dialog
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
// Cancel and finish
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
finish();
}, null);
setupAlert();
// 確定 Button
mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mOk.setEnabled(false);
if (!mOk.isInTouchMode()) {
mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
}
}
```
> 
### [PackageInstallerActivity](https://android.googlesource.com/platform/frameworks/base/+/master/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java) - 啟動 APK 過程
* 在上個小節我們有介紹 SDK 如何建構安裝 Dialog & 檢查機制,如果都通過後,使用者點擊安裝就會觸發 PackageInstallerActivity#startInstall 方法
1. 帶入 PackageInstallerActivity 分析好的 ApplicationInfo,下一個 Activity 會使用到
2. 將使用者帶入的 Uri 再次帶入 InstallInstallingActivity
3. 關閉當前 Actiivty,透過 intent **呼叫 InstallInstalling Activity**
```java=
// PackageInstallerActivity.java
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
// 1. 必須放入 ApplicationInfo,在下一個 Activity 會取出來用
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
// 2. 將使用者帶入的 Uri 再次帶入 InstallInstallingActivity
newIntent.setData(mPackageURI);
// 3. 目標跳轉 InstallInstalling Activity
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 (mLocalLOGV) Log.i(TAG, "downloaded app uri=" + mPackageURI);
startActivity(newIntent);
finish();
}
```
> 
## [InstallInstalling](https://android.googlesource.com/platform/frameworks/base/+/master/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java) 概述
InstallInstalling 主要功能是在透過 **IPackageInstaller** ^1.^ 向 PackageInstallerService 發送 APK 訊息、^2.^ 處理 PackageInstallerService 回調訊息 (這也是一個 AIDL)
### [InstallInstalling](https://android.googlesource.com/platform/frameworks/base/+/master/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java) 註冊、監聽廣播
* InstallInstalling 註冊、監聽廣播
1. 透過 intent 取得 ApplicationInfo、mPackageURI
```java=
// InstallInstalling.java
private Uri mPackageURI;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ApplicationInfo appInfo = getIntent()
.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
// 使用者帶入的 Uri
mPackageURI = getIntent().getData();
...
}
```
* 判斷 Scheme 做不同行為,根據上一個 Activity 我們知道這裡只接受 2 種 Uri Scheme
1. package:則就是已經安裝過的 APK
2. file:^1.^ **跳出 Dialog 視窗**(以下為第一次安裝,並且沒有被回收) 、^2.^**輕量級分析 APK**、^3.^註冊廣播監聽 GENERATE_NEW_ID、^4.^ 透過代理創建 Session 後取得 session id
:::success
createSession 方法是透過創建 IPackageInstaller 代理與 PackageInstallerService 通訊
:::
```java=
// InstallInstalling.java
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
// package 代表是已存在 APK
if ("package".equals(mPackageURI.getScheme())) {
try {
getPackageManager().installExistingPackage(appInfo.packageName);
launchSuccess();
} catch (PackageManager.NameNotFoundException e) {
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
} else {
// 1. 根據 URI 創建對應的 File 檔案
final File sourceFile = new File(mPackageURI.getPath());
// 取得 Apk 快照
PackageUtil.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
mAlert.setIcon(as.icon);
mAlert.setTitle(as.label);
mAlert.setView(R.layout.install_content_view);
// 取消按鈕
mAlert.setButton(DialogInterface.BUTTON_NEGATIVE, getString(R.string.cancel),
(ignored, ignored2) -> {
if (mInstallingTask != null) {
mInstallingTask.cancel(true);
}
if (mSessionId > 0) {
// 取消 Package installer 的 session
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
mSessionId = 0;
}
setResult(RESULT_CANCELED);
finish();
}, null);
setupAlert();
requireViewById(R.id.installing).setVisibility(View.VISIBLE);
// 判斷 Activity 是否是有被回收
if (savedInstanceState != null) {
// 取出被回收前的 SessionID、InstallID
// mSessionId 是 Apk 的 Session id
mSessionId = savedInstanceState.getInt(SESSION_ID);
// mInstallId 是安裝事件的 id
mInstallId = savedInstanceState.getInt(INSTALL_ID);
// 2. 向 InstallEventReceiver 添加安裝成功的觀察者
try {
// 回調會觸發 launchFinishBasedOnResult 方法
InstallEventReceiver.addObserver(this, mInstallId,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
// Does not happen
}
} else {
// 創建 Session 參數
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.setInstallAsInstantApp(false);
params.setReferrerUri(getIntent().getParcelableExtra(Intent.EXTRA_REFERRER));
params.setOriginatingUri(getIntent()
.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI));
params.setOriginatingUid(getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
UID_UNKNOWN));
params.setInstallerPackageName(getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME));
params.setInstallReason(PackageManager.INSTALL_REASON_USER);
// 3-1. 根據 Uri 創建一個對應的 File,準備進行輕量級解析
File file = new File(mPackageURI.getPath());
try {
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
// 輕量解析 Package
final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
input.reset(), file, /* flags */ 0);
if (result.isError()) {
// parse 失敗
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());
} else {
final PackageLite pkg = result.getResult();
params.setAppPackageName(pkg.getPackageName());
params.setInstallLocation(pkg.getInstallLocation());
params.setSize(
PackageHelper.calculateInstalledSize(pkg, params.abiOverride));
}
} catch (IOException e) {
Log.e(LOG_TAG,
"Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
}
try {
// 向 InstallEventReceiver 添加安裝成功的觀察者
// 3-2. 添加廣播監聽
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
try {
// 3-3. 透過代理創建 Session 後取得 session id
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
} catch (IOException e) {
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
}
// 取消按鈕
mCancelButton = mAlert.getButton(DialogInterface.BUTTON_NEGATIVE);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
// 回收 Activity 前儲存資料
outState.putInt(SESSION_ID, mSessionId);
outState.putInt(INSTALL_ID, mInstallId);
}
```
安裝流程圖,以下 **有相同的地方就是添加 InstallEventReceiver 的監聽**,最終不論成功還是失敗都會關閉該 Actiivty
> 
2. onResume 方法會創建一個異步任務,並且取得 PackageInstaller.SessionInfo,並判斷該 **^1.^ Session 不為 null、^2.^ 不在活動中**
```java=
// InstallInstalling.java
@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.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
// 判斷該 Session 可用的兩個條件
if (sessionInfo != null && !sessionInfo.isActive()) {
// @ InstallingAsyncTask 是內部類
mInstallingTask = new InstallingAsyncTask();
// 執行異步任務
mInstallingTask.execute();
} else {
// we will receive a broadcast when the install is finished
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}
```
> 
### InstallingAsyncTask 異步任務
* InstallInstalling#InstallingAsyncTask 創建一個異步任務
1. 在 doInBackground 方法 **讀取 Uri Path 的 APK 內容**,**對 ==PackageInstaller#Session 寫入==**
```java=
// InstallInstalling.java
// 內部類,可取得外部資訊
private final class InstallingAsyncTask extends AsyncTask<Void,
Void,
PackageInstaller.Session> {
volatile boolean isDone;
@Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
try {
// 取得 Session
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
synchronized (this) {
isDone = true;
notifyAll();
}
return null;
}
// Session 進度設定為 0
session.setStagingProgress(0);
try {
// 透過路徑 取得 APK 檔案
File file = new File(mPackageURI.getPath());
// 讀取 APK 檔案
try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
// 寫入 Session
try (OutputStream out = session
.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[1024 * 1024];
while (true) {
int numRead = in.read(buffer);
// 讀取完畢 numRead 就是 -1
if (numRead == -1) {
session.fsync(out);
break;
}
if (isCancelled()) {
session.close();
break;
}
// 通過 IO 流,寫入到 PackageInstaller#Session 中
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();
}
}
}
...
}
```
> 
2. 如果成功在 onPostExecute 方法呼叫 Session#**commit** (進入系統 PackageInstaller.Session 服務)
```java=
// InstallInstalling.java
// 內部類,可取得外部資訊
private final class InstallingAsyncTask extends AsyncTask<Void,
Void,
PackageInstaller.Session> {
volatile boolean isDone;
...
@Override
protected void onPostExecute(PackageInstaller.Session session) {
// 判斷 doInBackground 傳入的 session 參數
if (session != null) {
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.setPackage(getPackageName());
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
// 創建 PendingIntent
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_MUTABLE);
// 透過 Session#commit 發送 PendingIntent
// @ Session#commit 分析
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
// 拋棄使用的 Session
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
if (!isCancelled()) {
launchFailure(PackageInstaller.STATUS_FAILURE,
PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
}
```
> 
* PackageInstaller.Session 內是透過 IPackageInstallerSession 進行進程通訊
```java=
// PackageInstaller.java
public static class Session implements Closeable {
protected final IPackageInstallerSession mSession;
public void commit(@NonNull IntentSender statusReceiver) {
try {
// @ PackageInstallerSession#commit
mSession.commit(statusReceiver, false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
```
## 安裝 APK
簡單步驟如下
1. 把 APK 的信息通過 IO 寫入 PackageInstaller#Session 中
2. 調用 Session#commit 後,透過 PKMS 安裝 APK
3. 將 APK 複製到 `/data` 目錄下
### [PackageInstallerActivity](https://android.googlesource.com/platform/frameworks/base/+/master/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java) - 系統安裝 APK
* PackageInstallerActivity 安裝請看另外一篇文章 [**PackageInstallerActivity 安裝**](https://hackmd.io/b_xKfbCISS-58VSvq9szEw?view),最終會呼叫到系統的 PackageInstaller 進程的 Session#commit 方法
### PackageInstaller - commit 呼叫 [Session](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/PackageInstallerSession.java)
* 到這一步就在系統進程 (SystemServer) 中,而 IPackageInstallerSession 的實作類是 [**PackageInstallerSession**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/PackageInstallerSession.java),之後透過 PackageInstallerSession#commit 方法發送 MSG_ON_SESSION_SEALED 訊息
```java=
// PackageInstaller.java
public static class Session implements Closeable {
protected final IPackageInstallerSession mSession;
public void commit(@NonNull IntentSender statusReceiver) {
try {
// @ PackageInstallerSession#commit
mSession.commit(statusReceiver, false);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
}
// --------------------------------------------------------------
// PackageInstallerSession.java
private final Handler mHandler;
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
...
// 避免重複提交
if (!markAsSealed(statusReceiver, forTransfer)) {
return;
}
...
// @ dispatchSessionSealed 方法
dispatchSessionSealed();
}
private void dispatchSessionSealed() {
// @ MSG_ON_SESSION_SEALED
mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
}
```
> 
### [PackageInstallerSession](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/PackageInstallerSession.java) - 驗證處理
* 該 Handler 是在 PackageInstallerSession 建構函數中被初始化並設定 callback 對象,所以可以在 callback 中接到以下訊息
1. MSG_ON_SESSION_SEALED:密封處理
2. MSG_STREAM_VALIDATE_AND_COMMIT:檢查所有 Session 是否都準備好
3. **MSG_INSTALL**:進行安裝 APK 前的 **驗證行為**
```java=
// PackageInstallerSession.java
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_ON_SESSION_SEALED:
// @ 1. handleSessionSealed 方法
handleSessionSealed();
break;
case MSG_STREAM_VALIDATE_AND_COMMIT:
// @ 2. handleStreamValidateAndCommit 方法
handleStreamValidateAndCommit();
break;
case MSG_INSTALL:
// @ 3. handleInstall 方法
handleInstall();
... 省略其他 case
}
return true;
}
}
public PackageInstallerSession(PackageInstallerService.InternalCallback callback,
/* 省略部分參數*/ ) {
...
// @ mHandlerCallback member
mHandler = new Handler(looper, mHandlerCallback);
...
}
private void handleSessionSealed() {
// 密封處理,防止我們創建的 Linked 被改變
mCallback.onSessionSealedBlocking(this);
// @ dispatchStreamValidateAndCommit 分析
dispatchStreamValidateAndCommit();
}
private void dispatchStreamValidateAndCommit() {
// 第二步 @MSG_STREAM_VALIDATE_AND_COMMIT
mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}
private void handleStreamValidateAndCommit() {
PackageManagerException unrecoverableFailure = null;
// 檢查 Session 是否都準備好
boolean allSessionsReady = false;
try {
allSessionsReady = streamValidateAndCommit();
} catch (PackageManagerException e) {
unrecoverableFailure = e;
}
... 省略部分
if (!allSessionsReady) {
// 尚未準備好則會直接返回
return;
}
// 第三步 @MSG_INSTALL
mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}
```
* handleInstall 方法後的過程較為反鎖,**主要是在做 ++==驗證==++ 的動作**,這裡只列出幾個重點方法,但是最終安裝還是會透過 **PKMS**#**==installStage==**
```java=
// PackageInstallerSession.java
private final PackageManagerService mPm;
private void handleInstall() {
... 省略部分
verify();
}
private void verify() {
try {
verifyNonStaged();
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
onSessionVerificationFailure(e.error, completeMsg);
}
}
private void verifyNonStaged()
throws PackageManagerException {
// @ prepareForVerification 方法
final PackageManagerService.VerificationParams verifyingSession =
prepareForVerification();
if (verifyingSession == null) {
return;
}
... 省略部分
if (isMultiPackage()) {
... 省略部分
} else {
mPm.verifyStage(verifyingSession);
}
}
@Nullable
private PackageManagerService.VerificationParams prepareForVerification()
throws PackageManagerException {
... 省略部分
synchronized (mLock) {
// @ makeVerificationParamsLocked
return makeVerificationParamsLocked();
}
}
private PackageManagerService.VerificationParams makeVerificationParamsLocked() {
final IPackageInstallObserver2 localObserver;
if (!hasParentSessionId()) {
// Avoid attaching this observer to child session since they won't use it.
localObserver = new IPackageInstallObserver2.Stub() {
...
@Override
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
if (returnCode == INSTALL_SUCCEEDED) {
// @ onVerificationComplete
onVerificationComplete();
} else {
onSessionVerificationFailure(returnCode, msg);
}
}
};
} else {
localObserver = null;
}
... 省略部分
}
private void onVerificationComplete() {
// Staged sessions will be installed later during boot
if (isStaged()) {
...
return;
}
// @ 分析 install 方法
install();
}
private void install() {
try {
// @ 分析 installNonStaged
installNonStaged();
} /* 省略 catch */
}
private void installNonStaged()
throws PackageManagerException {
// 創建安裝參數類
final PackageManagerService.InstallParams installingSession = makeInstallParams();
if (installingSession == null) {
... 創建失敗就拋出
}
if (isMultiPackage()) { // 安裝多個 APK
... 省略部分
} else {
// @ PKMS#installStage 分析
mPm.installStage(installingSession);
}
}
```
* 接著看 PKMS#installStage 方法
### [PKMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/PackageManagerService.java) - 複製 APK 檔案
* 在 PackageInstallerSession 驗證完 APK 後,就會讓 PKMS 對自己的 Handler 發送 **INIT_COPY** 訊息 (並攜帶 InstallParams 參數),進行複製 APK 的行為 (從 PKMS#**installStage** 分析)
:::info
InstallParams 是 APK 數據
:::
```java=
// PackageManagerService.java
void installStage(InstallParams params) {
final Message msg = mHandler.obtainMessage(INIT_COPY);
msg.obj = params;
... 省略 trace
mHandler.sendMessage(msg);
}
class PackageHandler extends Handler {
public void handleMessage(Message msg) {
try {
doHandleMessage(msg);
} finally {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
// HandlerParams 是抽象類
HandlerParams params = (HandlerParams) msg.obj;
if (params != null) {
... 省略 trace & log
// @ startCopy 方法
params.startCopy();
}
}
}
}
}
```
> 
* HandlerParams 是抽象類,會依照是 多個安裝 or 單個安裝來進行不同的行為 (這裡來看 單個安裝 InstallParams 類),主要做三件事情
:::success
* HandlerParams 就是一種模板設計
```java=
// PackageManagerService.java
private abstract class HandlerParams {
final void startCopy() {
... log 訊息
handleStartCopy();
handleReturnCode();
}
abstract void handleStartCopy();
abstract void handleReturnCode();
}
```
:::
1. handleStartCopy:`getMinimalPackageInfo` 輕量分析 apk 檔案
2. handleReturnCode 又分為兩個部份
- 前半段 copyApk:複製 base.apk 檔案
- handleReturnCode 後半段 processInstallRequestsAsync:異步安裝 Apk
```java=
// PackageManagerService.java
// 實作類
class InstallParams extends HandlerParams {
public void handleStartCopy() {
if ((installFlags & PackageManager.INSTALL_APEX) != 0) {
mRet = INSTALL_SUCCEEDED;
return;
}
// Package 進行輕量分析
PackageInfoLite pkgLite = PackageManagerServiceUtils.getMinimalPackageInfo(mContext,
mPackageLite, origin.resolvedPath, installFlags, packageAbiOverride);
... 省略部分
// overrideInstallLocation 會依照策略改變 (這邊暫不分析)
mRet = overrideInstallLocation(pkgLite);
}
@Override
void handleReturnCode() {
// @ 查看 processPendingInstall
processPendingInstall();
}
private void processPendingInstall() {
// 前半段
InstallArgs args = createInstallArgs(this);
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
// @ InstallArgs#copyApk 複製 Apk
mRet = args.copyApk();
}
... 省略部分
// 後半段
if (mParentInstallParams != null) {
mParentInstallParams.tryProcessInstallRequest(args, mRet);
} else {
PackageInstalledInfo res = createPackageInstalledInfo(mRet);
// @ processInstallRequestsAsync
processInstallRequestsAsync(
res.returnCode == PackageManager.INSTALL_SUCCEEDED,
Collections.singletonList(new InstallRequest(args, res)));
}
}
}
```
> 
* InstallParams#processPendingInstall 分析:
1. 前半段 複製 Apk:InstallArgs 同樣是個**抽象類**,目前的實做有兩種,這裡分析 **FileInstallArgs 類**
| InstallArgs 子類 | 說明 |
| - | - |
| MoveInstallArgs | 用於處理已安裝 APK 在儲存中移動 |
| FileInstallArgs | 非安裝到 `mnt/saec` 的 APK(也就是 **安裝到 `/data` 目錄下的 APK**) |
```java=
// PackageManagerService.java
private InstallArgs createInstallArgs(InstallParams params) {
if (params.move != null) {
return new MoveInstallArgs(params);
} else {
return new FileInstallArgs(params);
}
}
static abstract class InstallArgs {
... 省略部分
abstract int copyApk();
}
// ---------------------------------------------------------------
// PackageManagerService.java
final PackageInstallerService mInstallerService;
// 實作類
class FileInstallArgs extends InstallArgs {
private File codeFile;
private File resourceFile;
int copyApk() {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
try {
// @ doCopyApk
return doCopyApk();
} finally {
... Trace
}
}
private int doCopyApk() {
...
try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
// 創建臨時文件儲存目錄
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
// 暫時目錄賦予 codeFile
codeFile = tempDir;
} /* 省略部分 */
// @ copyPackage 追蹤
int ret = PackageManagerServiceUtils.copyPackage(
origin.file.getAbsolutePath(), codeFile);
if (ret != PackageManager.INSTALL_SUCCEEDED) { // 複製 Apk 失敗
Slog.e(TAG, "Failed to copy package");
return ret;
}
}
}
```
:::success
* 透過 PackageManagerServiceUtils#**copyPackage** 進行複製,**對目標資料夾 (一般應用是 `/data/app` 下臨時資料夾) 寫入一個權限為 644 的 base.apk 的文件**
> 下面那一串就是臨時資料夾
> 
:::
```java=
// PackageManagerServiceUtils.java
public static int copyPackage(String packagePath, File targetDir) {
if (packagePath == null) {
return PackageManager.INSTALL_FAILED_INVALID_URI;
}
try {
final File packageFile = new File(packagePath);
final ParseTypeImpl input = ParseTypeImpl.forDefaultParsing();
// Parse 輕量級分析
final ParseResult<PackageLite> result = ApkLiteParseUtils.parsePackageLite(
input.reset(), packageFile, /* flags */ 0);
if (result.isError()) {
Slog.w(TAG, "Failed to parse package at " + packagePath);
return result.getErrorCode();
}
// 結果也包括 apk 的 source 路徑
final PackageLite pkg = result.getResult();
// @ copyFile 方法,複製一個 base.apk
copyFile(pkg.getBaseApkPath(), targetDir, "base.apk");
...
return PackageManager.INSTALL_SUCCEEDED;
} catch (IOException | ErrnoException e) {
Slog.w(TAG, "Failed to copy package at " + packagePath + ": " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
}
private static void copyFile(String sourcePath, File targetDir, String targetName)
throws ErrnoException, IOException {
...
// 用目標資料夾 + 目標檔案組成 File
final File targetFile = new File(targetDir, targetName);
// 644 的檔案
final FileDescriptor targetFd = Os.open(targetFile.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
// 改變權限
Os.chmod(targetFile.getAbsolutePath(), 0644);
FileInputStream source = null;
try {
// 開啟 input IO 流,準備寫入 <目標資料夾>/base.apk
source = new FileInputStream(sourcePath);
FileUtils.copy(source.getFD(), targetFd);
} finally {
IoUtils.closeQuietly(source);
}
}
```
2. 後半段 異步安裝 APK,在這裡會 **確保安裝環境**,主要分析 processInstallRequestsAsync 方法
> 透過 Handler#post 來達成異步
```java=
// PackageManagerService.java
private void processInstallRequestsAsync(boolean success,
List<InstallRequest> installRequests) {
mHandler.post(() -> {
List<InstallRequest> apexInstallRequests = new ArrayList<>();
List<InstallRequest> apkInstallRequests = new ArrayList<>();
for (InstallRequest request : installRequests) {
if ((request.args.installFlags & PackageManager.INSTALL_APEX) != 0) {
apexInstallRequests.add(request);
} else {
apkInstallRequests.add(request);
}
}
... 省略部分
if (success) {
for (InstallRequest request : apkInstallRequests) {
// 預安裝:在安裝前確保安裝環境可靠
// 如果安裝環境不可靠則會清除複製的 apk
request.args.doPreInstall(request.installResult.returnCode);
}
synchronized (mInstallLock) {
// 重點
// @ 分析 installPackagesTracedLI 方法
installPackagesTracedLI(apkInstallRequests);
}
for (InstallRequest request : apkInstallRequests) {
// 如果安裝失敗,清除無用訊息
request.args.doPostInstall(
request.installResult.returnCode, request.installResult.uid);
}
}
for (InstallRequest request : apkInstallRequests) {
restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
new PostInstallData(request.args, request.installResult, null));
}
});
}
private void installPackagesTracedLI(List<InstallRequest> requests) {
try {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installPackages");
// @installPackagesLI
installPackagesLI(requests);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
```
> 
### [PKMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/PackageManagerService.java) - installPackagesLI 安裝、優化 Package
* 回顧一下,FileInstallArgs 在複製要安裝的 apk 到臨時目錄後 (複製名稱為 base.apk) 會呼叫 **PKMS#installPackagesLI 方法**
* PackageManagerService#installPackagesLI 方法有以下幾個重點方法步驟,我們先概略知道一下他們的功能
| 方法名 | 功能 |
| -------- | -------- |
| installPackagesLI | FileInstallArgs 呼叫該方法,開始進行安裝的入口 |
| preparePackageLI | 分析 Package 並對其進行初始驗證 |
| scanPackageTracedLI | 透過先前分析的 prepareResult 結果進行掃描 |
| ReconcileRequest | Reconcile 調和,檢查裝置中驗證的 Package,確保安裝成功 |
| commitPackagesLocked | 準備 commit 提交物件(CommitRequest),提交所有掃描的 Package 狀態,這是安裝過程中唯一可以修改系統狀態的地方,必須在此階段之前確定所有錯誤 |
| executePostCommitSteps | 執行 commit,**為新的程式路徑準備應用程式配置文件,並 ++檢查 dex 優化++**,完成 APK 安裝 |
```java=
// PackageManagerService.java
private void installPackagesLI(List<InstallRequest> requests) {
...
// <包名、掃描結果>
final Map<String, PrepareResult> prepareResults = new ArrayMap<>(requests.size());
// <包名、安裝資訊>
final Map<String, PackageInstalledInfo> installResults = new ArrayMap<>(requests.size());
...
boolean success = false;
try {
...
// 遍歷安裝需求
for (InstallRequest request : requests) {
final PrepareResult prepareResult;
try {
...
// 1. 分析 Package 並對其進行初始驗證
prepareResult =
preparePackageLI(request.args, request.installResult);
} /* 省略 catch(會 return)、finally (Trace) */
// 設定返回為成功
request.installResult.setReturnCode(PackageManager.INSTALL_SUCCEEDED);
// 安裝來源包名
request.installResult.installerPackageName =
request.args.installSource.installerPackageName;
// APK 包名
final String packageName = prepareResult.packageToScan.getPackageName();
prepareResults.put(packageName, prepareResult);
installResults.put(packageName, request.installResult);
installArgs.put(packageName, request.args);
try {
// 2. 透過先前分析的 prepareResult 結果進行掃描
final ScanResult result = scanPackageTracedLI(
prepareResult.packageToScan, prepareResult.parseFlags,
prepareResult.scanFlags, System.currentTimeMillis(),
request.args.user, request.args.abiOverride);
... 省略部分
} catch (PackageManagerException e) {
request.installResult.setError("Scanning Failed.", e);
return;
}
}
// 3. Reconcile 調和,檢查裝置中驗證的 Package,確保安裝成功
ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
installResults,
prepareResults,
mSharedLibraries,
Collections.unmodifiableMap(mPackages), versionInfos,
lastStaticSharedLibSettings);
CommitRequest commitRequest = null;
synchronized (mLock) {
Map<String, ReconciledPackage> reconciledPackages;
... 省略部分
try {
... Trace 訊息
// 4. 準備 commit 提交物件(CommitRequest),提交所有掃描的 Package 狀態,這是安裝過程中唯一可以修改系統狀態的地方
// 必須在此階段之前確定所有錯誤
commitRequest = new CommitRequest(reconciledPackages,
mUserManager.getUserIds());
// 提交掃描的 Package
commitPackagesLocked(commitRequest);
success = true;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
// 5. 執行 commit,完成 APK 安裝
// @ 分析 executePostCommitSteps
executePostCommitSteps(commitRequest);
} /* 省略 finally */
} // Function
```
> 
* **PKMS#executePostCommitSteps** 安裝 APK && 為新 APP 配置文件 && 檢查是否需要進行 dex 優化,這有分為兩種情況
1. 裝置沒有這個應用 (直接安裝):準備 APP 路徑、配置文件
2. 裝置已經有這個應用 (替換更新):清除原來 APP 數據 (看是否有設置)、重新生成 APP 數據目錄等步驟
```java=
// PackageManagerService.java
private void executePostCommitSteps(CommitRequest commitRequest) {
final ArraySet<IncrementalStorage> incrementalStorages = new ArraySet<>();
for (ReconciledPackage reconciledPkg : commitRequest.reconciledPackages.values()) {
... 省略部分
// 1. 進行 package 安裝
// @ prepareAppDataAfterInstallLIF 方法
prepareAppDataAfterInstallLIF(pkg);
// 2. 清除資料 (如果有需要的話)
if (reconciledPkg.prepareResult.clearCodeCache) {
clearAppDataLIF(pkg, UserHandle.USER_ALL, FLAG_STORAGE_DE | FLAG_STORAGE_CE
| FLAG_STORAGE_EXTERNAL | Installer.FLAG_CLEAR_CODE_CACHE_ONLY);
}
// 查看是否需要替換 App
if (reconciledPkg.prepareResult.replace) {
// 喚醒 Package 更新
mDexManager.notifyPackageUpdated(pkg.getPackageName(),
pkg.getBaseApkPath(), pkg.getSplitCodePaths());
}
// 3. 為新 APP 的路徑準備 ++應用程序配置文件++,
// 這需要在調用 dexopt 之前完成
// 以便任何安裝時配置文件都可以用於優化
mArtManagerService.prepareAppProfiles(
pkg,
resolveUserIds(reconciledPkg.installArgs.user.getIdentifier()),
/* updateReferenceProfileContent= */ true);
... 省略部分
final boolean performDexopt =
(!instantApp || Global.getInt(mContext.getContentResolver(),
Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& !pkg.isDebuggable()
&& (!onIncremental)
&& dexoptOptions.isCompilationEnabled();
if (performDexopt) {
... 省略部分
// 4. 進行 dexopt 優化
mPackageDexOptimizer.performDexOpt(pkg, realPkgSetting,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
mDexManager.getPackageUseInfoOrDefault(packageName),
dexoptOptions);
}
...
}
...
}
```
> 
* 接下來準備分析 `prepareAppDataAfterInstallLIF` 方法
### [PKMS](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/PackageManagerService.java) - 準備 AppData & 呼叫 Installer 安裝
* 從上一部的 prepareAppDataAfterInstallLIF 方法開始分析,該方法 **最終會調用到 Installer 進程為 APK 進行安裝**
:::info
這中間有一個 Installer 算是一個橋接設計,它會連接真正的 Installer 服務
:::
```java=
// PackageManagerService.java
final ArtManagerService mArtManagerService;
// 創建 or 調整 or 更新
private void prepareAppDataAfterInstallLIF(AndroidPackage pkg) {
final PackageSetting ps;
synchronized (mLock) {
ps = mSettings.getPackageLPr(pkg.getPackageName());
mSettings.writeKernelMappingLPr(ps);
}
...
Installer.Batch batch = new Installer.Batch();
for (UserInfo user : mUserManager.getUsers(false /*excludeDying*/)) {
... 省略部分
if (ps.getInstalled(user.id)) { // 已安裝
// @ prepareAppData 分析
prepareAppData(batch, pkg, user.id, flags).thenRun(() -> {
... 省略部分
});
}
}
...
}
private @NonNull CompletableFuture<?> prepareAppData(@NonNull Installer.Batch batch,
@Nullable AndroidPackage pkg, int userId, int flags) {
...
// @ prepareAppDataLeaf 方法
return prepareAppDataLeaf(batch, pkg, userId, flags);
}
private @NonNull CompletableFuture<?> prepareAppDataLeaf(@NonNull Installer.Batch batch,
@NonNull AndroidPackage pkg, int userId, int flags) {
...
// createAppData 安裝完成後,更新裝置
return batch.createAppData(volumeUuid, packageName, userId, flags, appId, seInfo,
targetSdkVersion).whenComplete((ceDataInode, e) -> {
if (e != null) { // 有錯誤就刪除重建 AppData
...
// 刪除 AppData
destroyAppDataLeafLIF(pkg, userId, flags);
try {
// 創建 AppData
ceDataInode = mInstaller.createAppData(volumeUuid, packageName, userId,
flags, appId, seInfo, pkg.getTargetSdkVersion());
logCriticalInfo(Log.DEBUG, "Recovery succeeded!");
} /* 省略 catch */
}
...
if (mIsUpgrade || mFirstBoot || (userId != UserHandle.USER_SYSTEM)) {
// @ prepareAppProfiles 分析
mArtManagerService.prepareAppProfiles(pkg, userId,
/* updateReferenceProfileContent= */ false);
}
...
prepareAppDataContentsLeafLIF(pkg, ps, userId, flags);
}
}
}
```
* [**ArtManagerService**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/dex/ArtManagerService.java)#prepareAppProfiles 準備創建 AppData
```java=
// ArtManagerService.java
public void prepareAppProfiles(
AndroidPackage pkg, @UserIdInt int user,
boolean updateReferenceProfileContent) {
... 省略部分
try {
ArrayMap<String, String> codePathsProfileNames = getPackageProfileNames(pkg);
for (int i = codePathsProfileNames.size() - 1; i >= 0; i--) {
... 省略部分
synchronized (mInstaller) {
// @ prepareAppProfile (該 Installer 類並非進程 Installer 服務)
boolean result = mInstaller.prepareAppProfile(pkg.getPackageName(), user, appId,
profileName, codePath, dexMetadataPath);
...
}
}
} /* 省略 catch */
}
```
* [**Installer**](https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/pm/Installer.java)#Batch#createAppData 創建 AppData 參數 (這個 Installer 並非服務進程)
```java=
// Installer.java
public class Installer extends SystemService {
private volatile IInstalld mInstalld;
public boolean prepareAppProfile(String pkg, @UserIdInt int userId, @AppIdInt int appId,
String profileName, String codePath, String dexMetadataPath) throws InstallerException {
...
try {
// 呼叫 Installer 服務進程的 prepareAppProfile
return mInstalld.prepareAppProfile(pkg, userId, appId, profileName, codePath,
dexMetadataPath);
} /* 省略 catch */
}
public static class Batch {
public synchronized @NonNull CompletableFuture<Long> createAppData(String uuid,
String packageName, int userId, int flags, int appId, String seInfo,
int targetSdkVersion) {
if (mExecuted) throw new IllegalStateException();
final CreateAppDataArgs args = buildCreateAppDataArgs(uuid, packageName, userId, flags,
appId, seInfo, targetSdkVersion);
final CompletableFuture<Long> future = new CompletableFuture<>();
mArgs.add(args);
mFutures.add(future);
return future;
}
}
}
```
> 
## Appendix & FAQ
:::info
:::
###### tags: `Android Framework`