Android 12 新功能分析
https://developer.android.com/about/versions/12/features
https://blog.csdn.net/weixin_40611659/article/details/119645712
關(guān)于存儲(chǔ):
/**
* Allow apps to create the requests to manage the media files without user confirmation.
*
* @see android.Manifest.permission#MANAGE_MEDIA
* @see android.provider.MediaStore#createDeleteRequest(ContentResolver, Collection)
* @see android.provider.MediaStore#createTrashRequest(ContentResolver, Collection, boolean)
* @see android.provider.MediaStore#createWriteRequest(ContentResolver, Collection)
*
* @hide
*/
https://juejin.cn/post/6932363591689437192
關(guān)于項(xiàng)目中的適配
android 12 適配 (kdocs.cn)
apk的大體流程如下:
· 第一步:拷貝文件到指定的目錄: 在Android系統(tǒng)中,apk安裝文件是會(huì)被保存起來(lái)的拓巧,默認(rèn)情況下斯碌,用戶安裝的apk首先會(huì)被拷貝到/data/app目錄下,/data/app目錄是用戶有權(quán)限訪問(wèn)的目錄肛度,在安裝apk的時(shí)候會(huì)自動(dòng)選擇該目錄存放用戶安裝的文件傻唾,而系統(tǒng)出場(chǎng)的apk文件則被放到了/system分區(qū)下,包括/system/app承耿,/system/vendor/app冠骄,以及/system/priv-app等等,該分區(qū)只有ROOT權(quán)限的用戶才能訪問(wèn)瘩绒,這也就是為什么在沒(méi)有Root手機(jī)之前猴抹,我們沒(méi)法刪除系統(tǒng)出場(chǎng)的app的原因了。
· 第二步:解壓縮apk锁荔,寶貝文件蟀给,創(chuàng)建應(yīng)用的數(shù)據(jù)目錄 為了加快app的啟動(dòng)速度,apk在安裝的時(shí)候阳堕,會(huì)首先將app的可執(zhí)行文件dex拷貝到/data/dalvik-cache目錄跋理,緩存起來(lái)。然后恬总,在/data/data/目錄下創(chuàng)建應(yīng)用程序的數(shù)據(jù)目錄(以應(yīng)用的包名命名)前普,存放在應(yīng)用的相關(guān)數(shù)據(jù),如數(shù)據(jù)庫(kù)壹堰、xml文件拭卿、cache、二進(jìn)制的so動(dòng)態(tài)庫(kù)等贱纠。
· 第三步:解析apk的AndroidManifest.xml文件
Android系統(tǒng)中峻厚,也有一個(gè)類似注冊(cè)表的東西,用來(lái)記錄當(dāng)前所有安裝的應(yīng)用的基本信息谆焊,每次系統(tǒng)安裝或者卸載了任何apk文件惠桃,都會(huì)更新這個(gè)文件。這個(gè)文件位于如下目錄:/data/system/packages.xml辖试。系統(tǒng)在安裝這個(gè)apk的過(guò)程中辜王,會(huì)解析apk的AndroidManifest.xml文件,提取出這個(gè)apk的重要信息寫入到packages.xml文件中罐孝,這些信息包括:權(quán)限呐馆、應(yīng)用包名、APK的安裝位置莲兢、版本摹恰、userID等等辫继。由此,我們就知道了為什么一些應(yīng)用市場(chǎng)和軟件管理類的app能夠很清楚地知道當(dāng)前手機(jī)所安裝的所有app俗慈,以及這些app的詳細(xì)信息了。另外一件事就是Linux的用戶Id和用戶組Id遣耍,以便他們可以獲得合適的運(yùn)行權(quán)限闺阱。以上都是由PackageServcieManager完成的,后面我們會(huì)重點(diǎn)介紹PackageServiceManager舵变。
第四步:顯示快捷方式 如果這些應(yīng)用程序在PackageManagerService服務(wù)注冊(cè)好了酣溃,如果我們想要在Android桌米上看到這些應(yīng)用程序,還需要有一個(gè)Home應(yīng)用程序纪隙,負(fù)責(zé)從PackageManagerService服務(wù)中把這些安裝好的應(yīng)用程序取出來(lái)赊豌,并以友好的方式在桌面上展現(xiàn)出來(lái),例如以快捷圖標(biāo)的形式绵咱。在Android系統(tǒng)中碘饼,負(fù)責(zé)把系統(tǒng)中已經(jīng)安裝的應(yīng)用程序在桌面中展現(xiàn)出來(lái)的Home應(yīng)用就是Launcher了。
安裝 APK 主要分為以下三種場(chǎng)景
- 安裝系統(tǒng)應(yīng)用:系統(tǒng)啟動(dòng)后調(diào)用 PackageManagerService.main() 初始化注冊(cè)解析安裝工作
public static PackageManagerService main(Context context, Installer installer,
boolean factoryTest, boolean onlyCore) {
// Self-check for initial settings.
PackageManagerServiceCompilerMapping.checkProperties();
PackageManagerService m = new PackageManagerService(context, installer,
factoryTest, onlyCore);
m.enableSystemUserPackages();
ServiceManager.addService("package", m);
final PackageManagerNative pmn = m.new PackageManagerNative();
ServiceManager.addService("package_native", pmn);
return m;
}
- 通過(guò) adb 安裝:通過(guò) pm 參數(shù)悲伶,調(diào)用 PM 的 runInstall 方法艾恼,進(jìn)入 PackageManagerService 安裝安裝工作
- 通過(guò)系統(tǒng)安裝器 PackageInstaller 進(jìn)行安裝:先調(diào)用 InstallStart 進(jìn)行權(quán)限檢查之后啟動(dòng) PackageInstallActivity,調(diào)用 PackageInstallActivity 的 startInstall 方法麸锉,點(diǎn)擊 OK 按鈕后進(jìn)入 PackageManagerService 完成拷貝解析安裝工作
所有安裝方式大致相同钠绍,最終就是回到 PackageManagerService 中,安裝一個(gè) APK 的大致流程如下:
- 拷貝到 APK 文件到指定目錄
- 解壓縮 APK花沉,拷貝文件柳爽,創(chuàng)建應(yīng)用的數(shù)據(jù)目錄
- 解析 APK 的 AndroidManifest.xml 文件
- 向 Launcher 應(yīng)用申請(qǐng)?zhí)砑觿?chuàng)建快捷方式
本文主要來(lái)分析通過(guò)安裝器 PackageInstaller 安裝 APK,這是用戶最常用的一種方式
7.0以前安裝的入口是PackageInstallerActivity 7.0以后是InstallStart
3.1 InstallStart
主要工作:
判斷是否勾選“未知來(lái)源”選項(xiàng)碱屁,若未勾選跳轉(zhuǎn)到設(shè)置安裝未知來(lái)源界面
對(duì)于大于等于 Android 8.0 版本磷脯,會(huì)先檢查是否申請(qǐng)安裝權(quán)限,若沒(méi)有則中斷安裝
判斷 Uri 的 Scheme 協(xié)議忽媒,若是 content 則調(diào)用 InstallStaging, 若是 package 則調(diào)用 PackageInstallerActivity,但是實(shí)際上 installStaging中的 StagingAsyncTask 會(huì)將content協(xié)議的Uri轉(zhuǎn)換為File協(xié)議争拐,然后跳轉(zhuǎn)到PackageInstallerActivity,所以最終安裝的開始還是PackageInstallerActivity
3.2 PackageInstallerActivity
主要工作:
- 顯示安裝界面
- 初始化安裝需要用的各種對(duì)象晦雨,比如 PackageManager架曹、IPackageManager、AppOpsManager闹瞧、UserManager绑雄、PackageInstaller 等等
- 根據(jù)傳遞過(guò)來(lái)的 Scheme 協(xié)議做不同的處理
- 檢查是否允許、初始化安裝
- 在準(zhǔn)備安裝的之前奥邮,檢查應(yīng)用列表判斷該應(yīng)用是否已安裝万牺,若已安裝則提示該應(yīng)用已安裝罗珍,由用戶決定是否替換
- 在安裝界面,提取出 APK 中權(quán)限信息并展示出來(lái)
- 點(diǎn)擊 OK 按鈕確認(rèn)安裝后脚粟,會(huì)調(diào)用 startInstall 開始安裝工作
protected void onCreate(Bundle icicle) {
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
super.onCreate(null);
// 初始化安裝需要用到的對(duì)象
mPm = getPackageManager();
mIpm = AppGlobals.getPackageManager();
mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
mInstaller = mPm.getPackageInstaller();
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
// 根據(jù)Uri的Scheme做不同的處理
boolean wasSetUp = processPackageUri(packageUri);
if (!wasSetUp) {
return;
}
// 顯示安裝界面
bindUi();
// 檢查是否允許安裝包覆旱,如果允許則啟動(dòng)安裝。如果不允許顯示適當(dāng)?shù)膶?duì)話框
checkIfAllowedAndInitiateInstall();
}
主要做了對(duì)象的初始化核无,解析 Uri 的 Scheme扣唱,初始化界面,安裝包檢查等等工作团南,接著查看一下
processPackageUri 方法
private boolean processPackageUri(final Uri packageUri) {
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
// 根據(jù)這個(gè)Scheme協(xié)議分別對(duì)package協(xié)議和file協(xié)議進(jìn)行處理
switch (scheme) {
case SCHEME_PACKAGE: {
try {
// 通過(guò)PackageManager對(duì)象獲取指定包名的包信息
mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),
PackageManager.GET_PERMISSIONS
| PackageManager.MATCH_UNINSTALLED_PACKAGES);
} catch (NameNotFoundException e) {
}
if (mPkgInfo == null) {
Log.w(TAG, "Requested package " + packageUri.getScheme()
+ " not available. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return false;
}
mAppSnippet = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
mPm.getApplicationIcon(mPkgInfo.applicationInfo));
} break;
case ContentResolver.SCHEME_FILE: {
// 根據(jù)packageUri創(chuàng)建一個(gè)新的File
File sourceFile = new File(packageUri.getPath());
// 解析APK得到APK的信息噪沙,PackageParser.Package存儲(chǔ)了APK的所有信息
PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);
if (parsed == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return false;
}
// 根據(jù)PackageParser.Package得到的APK信息,生成PackageInfo
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
} break;
default: {
throw new IllegalArgumentException("Unexpected URI scheme " + packageUri);
}
}
return true;
}
主要對(duì) Scheme 協(xié)議分別對(duì) package 協(xié)議和 file 協(xié)議進(jìn)行處理
SCHEME_PACKAGE:
在 package 協(xié)議中調(diào)用了 PackageManager.getPackageInfo 方法生成 PackageInfo吐根,PackageInfo 是跨進(jìn)程傳遞的包數(shù)據(jù)(activities正歼、receivers、services拷橘、providers局义、permissions等等)包含 APK 的所有信息
SCHEME_FILE:
在 file 協(xié)議的處理中調(diào)用了 PackageUtil.getPackageInfo 方法,方法內(nèi)部調(diào)用了 PackageParser.parsePackage() 把 APK 文件的 manifest 和簽名信息都解析完成并保存在了 Package膜楷,Package 包含了該 APK 的所有信息
調(diào)用 PackageParser.generatePackageInfo 生成 PackageInfo
接著往下走旭咽,都解析完成之后,回到 onCreate 方法赌厅,繼續(xù)調(diào)用 checkIfAllowedAndInitiateInstall 方法
private void checkIfAllowedAndInitiateInstall() {
## 首先檢查安裝應(yīng)用程序的用戶限制穷绵,如果有限制并彈出彈出提示Dialog或者跳轉(zhuǎn)到設(shè)置界面
final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
return;
} else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
finish();
return;
}
// 判斷如果允許安裝未知來(lái)源或者根據(jù)Intent判斷得出該APK不是未知來(lái)源
if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
initiateInstall();
} else {
// 檢查未知安裝源限制,如果有限制彈出Dialog,顯示相應(yīng)的信息
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) {
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 {
// 處理未知來(lái)源的APK
handleUnknownSources();
}
}
}
主要檢查安裝應(yīng)用程序的用戶限制,當(dāng) APK 文件不對(duì)或者安裝有限制則調(diào)用 showDialogInner 方法特愿,彈出 dialog 提示用戶仲墨,顯示相應(yīng)的錯(cuò)誤信息,來(lái)看一下都有那些錯(cuò)誤信息
// Dialog identifiers used in showDialog
private static final int DLG_BASE = 0;
// package信息錯(cuò)誤
private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
// 存儲(chǔ)空間不夠
private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
// 安裝錯(cuò)誤
private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
// 用戶限制的未知來(lái)源
private static final int DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = DLG_BASE + 5;
private static final int DLG_ANONYMOUS_SOURCE = DLG_BASE + 6;
// 在wear上不支持
private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7;
private static final int DLG_EXTERNAL_SOURCE_BLOCKED = DLG_BASE + 8;
// 安裝限制用戶使用的應(yīng)用程序
private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = DLG_BASE + 9;
如果用戶允許安裝未知來(lái)源揍障,會(huì)調(diào)用 initiateInstall 方法
private void initiateInstall() {
String pkgName = mPkgInfo.packageName;
// 檢查設(shè)備上是否存在相同包名的APK
String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
if (oldName != null && oldName.length > 0 && oldName[0] != null) {
pkgName = oldName[0];
mPkgInfo.packageName = pkgName;
mPkgInfo.applicationInfo.packageName = pkgName;
}
// 檢查package是否已安裝, 如果已經(jīng)安裝則顯示對(duì)話框提示用戶是否替換目养。
try {
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.MATCH_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
// 初始化確認(rèn)安裝界面
startInstallConfirm();
}
根據(jù)包名獲取應(yīng)用程序的信息,調(diào)用 startInstallConfirm 方法初始化安裝確認(rèn)界面后毒嫡,當(dāng)用戶點(diǎn)擊確認(rèn)按鈕之后發(fā)生了什么岩齿,接著查看確認(rèn)按鈕點(diǎn)擊事件
private void bindUi() {
...
// 點(diǎn)擊確認(rèn)按鈕舞吭,安裝APK
mAlert.setButton(DialogInterface.BUTTON_POSITIVE, getString(R.string.install),
(ignored, ignored2) -> {
if (mOk.isEnabled()) {
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, true);
finish();
} else {
// 啟動(dòng)Activity來(lái)完成應(yīng)用的安裝
startInstall();
}
}
}, null);
// 點(diǎn)擊取消按鈕刨仑,取消此次安裝
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();
mOk = mAlert.getButton(DialogInterface.BUTTON_POSITIVE);
mOk.setEnabled(false);
}
當(dāng)用戶點(diǎn)擊確認(rèn)按鈕調(diào)用了 startInstall 方法拷泽,啟動(dòng)子 Activity 完成 APK 的安裝
private void startInstall() {
// 啟動(dòng)子Activity來(lái)完成應(yīng)用的安
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallInstalling.class);
...
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
startActivity(newIntent);
finish();
}
startInstall 方法用來(lái)跳轉(zhuǎn)到 InstallInstalling,并關(guān)閉掉當(dāng)前的 PackageInstallerActivity
3.3 InstallInstalling
主要工作:
向包管理器發(fā)送包的信息咬摇,然后等待包管理器處理結(jié)果
注冊(cè)一個(gè)觀察者 InstallEventReceiver伐蒂,并接受安裝成功和失敗的回調(diào)
在方法 onResume 中創(chuàng)建同步棧,打開安裝 session肛鹏,設(shè)置安裝進(jìn)度條
InstallInstalling 首先向包管理器發(fā)送包的信息逸邦,然后等待包管理器處理結(jié)果恩沛,并在方法 InstallSuccess 和方法 InstallFailed 進(jìn)行成功和失敗的處理,查看 InstallInstalling 的 onCreate 方法:
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
// 判斷安裝的應(yīng)用是否已經(jīng)存在
if ("package".equals(mPackageURI.getScheme())) {
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.AppSnippet as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
...
if (savedInstanceState != null) {
// 如果savedInstanceState 不為空缕减,獲取已經(jīng)存在mSessionId 和mInstallId 重新注冊(cè)
mSessionId = savedInstanceState.getInt(SESSION_ID);
mInstallId = savedInstanceState.getInt(INSTALL_ID);
try {
// 根據(jù)mInstallId向InstallEventReceiver注冊(cè)一個(gè)觀察者雷客,launchFinishBasedOnResult會(huì)接收到安裝事件的回調(diào)
InstallEventReceiver.addObserver(this, mInstallId,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
}
} else {
// 如果為空創(chuàng)建SessionParams,代表安裝會(huì)話的參數(shù)
// 解析APK, 并將解析的參數(shù)賦值給SessionParams
PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
...
try {
// 注冊(cè)InstallEventReceiver烛卧,并在launchFinishBasedOnResult會(huì)接收到安裝事件的回調(diào)
mInstallId = InstallEventReceiver
.addObserver(this, EventResultPersister.GENERATE_NEW_ID,
this::launchFinishBasedOnResult);
} catch (EventResultPersister.OutOfIdsException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
try {
// createSession 內(nèi)部通過(guò)IPackageInstaller與PackageInstallerService進(jìn)行進(jìn)程間通信佛纫,
// 最終調(diào)用的是PackageInstallerService的createSession方法來(lái)創(chuàng)建并返回mSessionId
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
} catch (IOException e) {
launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
}
}
...
}
}
最終都會(huì)注冊(cè)一個(gè)觀察者 InstallEventReceiver,并在 launchFinishBasedOnResult 會(huì)接收到安裝事件的回調(diào)总放,其中 InstallEventReceiver 繼承自 BroadcastReceiver,用于接收安裝事件并回調(diào)給 EventResultPersister
createSession 內(nèi)部通過(guò) IPackageInstaller 與 PackageInstallerService 進(jìn)行進(jìn)程間通信好爬,最終調(diào)用的是 PackageInstallerService的createSession 方法來(lái)創(chuàng)建并返回 mSessionId
接下來(lái)在 onResume 方法創(chuàng)建 InstallingAsyncTask 用來(lái)執(zhí)行 APK 的安裝局雄,接著查看 onResume 方法
protected void onResume() {
super.onResume();
if (mInstallingTask == null) {
PackageInstaller installer = getPackageManager().getPackageInstaller();
// 根據(jù)mSessionId 獲取SessionInfo, 代表安裝會(huì)話的詳細(xì)信息
PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
if (sessionInfo != null && !sessionInfo.isActive()) {
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
} else {
// 安裝完成后會(huì)收到廣播
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}
得到 SessionInfo 創(chuàng)建并創(chuàng)建 InstallingAsyncTask,InstallingAsyncTask 的 doInBackground 方法設(shè)置安裝進(jìn)度條存炮,并將 APK 信息寫入 PackageInstaller.Session炬搭,寫入完成之后,在 InstallingAsyncTask 的 onPostExecute 進(jìn)行成功與失敗的處理穆桂,接著查看 onPostExecute 方法
protected void onPostExecute(PackageInstaller.Session 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.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);
}
}
}
創(chuàng)建了 broadcastIntent宫盔,并通過(guò) PackageInstaller.Session 的 commit 方法發(fā)送出去,通過(guò) broadcastIntent 構(gòu)造方法指定的 Intent 的 Action 為 BROADCAST_ACTION享完,而 BROADCAST_ACTION 是一個(gè)常量值
private static final String BROADCAST_ACTION =
"com.android.packageinstaller.ACTION_INSTALL_COMMIT";
回到 InstallInstalling.OnCreate 方法灼芭,在 OnCreate 方法注冊(cè) InstallEventReceiver,而 InstallEventReceiver 繼承自 BroadcastReceiver般又,而使用 BroadcastReceiver 需要在 AndroidManifest.xml注冊(cè)彼绷,接著查看 AndroidManifest.xml:
/frameworks/base/packages/PackageInstaller/AndroidManifest.xml
<receiver android:name=".InstallEventReceiver"
android:permission="android.permission.INSTALL_PACKAGES"
android:exported="true">
<intent-filter android:priority="1">
<action android:name="com.android.packageinstaller.ACTION_INSTALL_COMMIT" />
</intent-filter>
</receiver>
安裝結(jié)束之后,會(huì)在觀察者 InstallEventReceiver 注冊(cè)的回調(diào)方法 launchFinishBasedOnResult 處理安裝事件的結(jié)果茴迁,接著查看 launchFinishBasedOnResult
private void launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage) {
if (statusCode == PackageInstaller.STATUS_SUCCESS) {
launchSuccess();
} else {
launchFailure(legacyStatus, statusMessage);
}
}
private void launchSuccess() {
Intent successIntent = new Intent(getIntent());
successIntent.setClass(this, InstallSuccess.class);
successIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
startActivity(successIntent);
finish();
}
private void launchFailure(int legacyStatus, String statusMessage) {
Intent failureIntent = new Intent(getIntent());
failureIntent.setClass(this, InstallFailed.class);
failureIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
failureIntent.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, legacyStatus);
failureIntent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, statusMessage);
startActivity(failureIntent);
finish();
}
安裝成功和失敗寄悯,都會(huì)啟動(dòng)一個(gè)新的 Activity(InstallSuccess、InstallFailed)將結(jié)果展示給用戶堕义,然后 finish 掉 InstallInstalling
總結(jié)一下 PackageInstaller 安裝APK的過(guò)程:
現(xiàn)在來(lái)總結(jié)下PackageInstaller初始化的過(guò)程:
根據(jù)Uri的Scheme協(xié)議不同猜旬,跳轉(zhuǎn)到不同的界面,content協(xié)議跳轉(zhuǎn)到InstallStart倦卖,其他的跳轉(zhuǎn)到PackageInstallerActivity洒擦。本文應(yīng)用場(chǎng)景中,如果是Android7.0以及更高版本會(huì)跳轉(zhuǎn)到InstallStart糖耸。
InstallStart將content協(xié)議的Uri轉(zhuǎn)換為File協(xié)議秘遏,然后跳轉(zhuǎn)到PackageInstallerActivity。
PackageInstallerActivity會(huì)分別對(duì)package協(xié)議和file協(xié)議的Uri進(jìn)行處理嘉竟,如果是file協(xié)議會(huì)解析APK文件得到包信息PackageInfo邦危。
點(diǎn)擊 OK 按鈕確認(rèn)安裝后洋侨,會(huì)調(diào)用 startInstall 開始安裝工作
如果用戶允許安裝,然后跳轉(zhuǎn)到 InstallInstalling倦蚪,進(jìn)行 APK 的安裝工作
在 InstallInstalling 中希坚,向包管理器發(fā)送包的信息,然后注冊(cè)一個(gè)觀察者 InstallEventReceiver陵且,并接受安裝成功和失敗的回調(diào)
PackageInstallSession
Android通過(guò)PackageInstallerSession來(lái)表示一次安裝過(guò)程裁僧,一個(gè)PackageInstallerSession包含一個(gè)系統(tǒng)中唯一的一個(gè)SessionId,如果一個(gè)應(yīng)用的安裝必須分幾個(gè)階段來(lái)完成慕购,即使設(shè)備重啟了聊疲,也可以通過(guò)這個(gè)ID來(lái)繼續(xù)安裝過(guò)程
PackageInstallerService
安裝的遠(yuǎn)程服務(wù)類,用于返回sessionid
@Override
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
if (hasParentSessionId()) {
throw new IllegalStateException(
"Session " + sessionId + " is a child of multi-package session "
+ getParentSessionId() + " and may not be committed directly.");
}
if (!markAsSealed(statusReceiver, forTransfer)) {
return;
}
if (isMultiPackage()) {
synchronized (mLock) {
final IntentSender childIntentSender =
new ChildStatusIntentReceiver(mChildSessions.clone(), statusReceiver)
.getIntentSender();
boolean sealFailed = false;
for (int i = mChildSessions.size() - 1; i >= 0; --i) {
// seal all children, regardless if any of them fail; we'll throw/return
// as appropriate once all children have been processed
if (!mChildSessions.valueAt(i)
.markAsSealed(childIntentSender, forTransfer)) {
sealFailed = true;
}
}
if (sealFailed) {
return;
}
}
}
dispatchSessionSealed();
}
/**
* Kicks off the install flow. The first step is to persist 'sealed' flags
* to prevent mutations of hard links created later.
*/
private void dispatchSessionSealed() {
mHandler.obtainMessage(MSG_ON_SESSION_SEALED).sendToTarget();
}
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
case MSG_ON_SESSION_SEALED:
handleSessionSealed();
break;
case MSG_STREAM_VALIDATE_AND_COMMIT:
handleStreamValidateAndCommit();
break;
case MSG_INSTALL:
handleInstall();
break;
case MSG_ON_PACKAGE_INSTALLED:
final SomeArgs args = (SomeArgs) msg.obj;
final String packageName = (String) args.arg1;
final String message = (String) args.arg2;
final Bundle extras = (Bundle) args.arg3;
final IntentSender statusReceiver = (IntentSender) args.arg4;
final int returnCode = args.argi1;
args.recycle();
sendOnPackageInstalled(mContext, statusReceiver, sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId,
packageName, returnCode, message, extras);
break;
case MSG_SESSION_VALIDATION_FAILURE:
final int error = msg.arg1;
final String detailMessage = (String) msg.obj;
onSessionValidationFailure(error, detailMessage);
break;
}
return true;
}
};
經(jīng)過(guò)一系列處理最終是調(diào)用的還是Pms中的安裝方法
private void installNonStaged()
throws PackageManagerException {
final PackageManagerService.InstallParams installingSession = makeInstallParams();
if (installingSession == null) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Session should contain at least one apk session for installation");
}
if (isMultiPackage()) {
final List<PackageInstallerSession> childSessions;
synchronized (mLock) {
childSessions = getChildSessionsLocked();
}
List<PackageManagerService.InstallParams> installingChildSessions =
new ArrayList<>(childSessions.size());
boolean success = true;
PackageManagerException failure = null;
for (int i = 0; i < childSessions.size(); ++i) {
final PackageInstallerSession session = childSessions.get(i);
try {
final PackageManagerService.InstallParams installingChildSession =
session.makeInstallParams();
if (installingChildSession != null) {
installingChildSessions.add(installingChildSession);
}
} catch (PackageManagerException e) {
failure = e;
success = false;
}
}
if (!success) {
final IntentSender statusReceiver;
synchronized (mLock) {
statusReceiver = mRemoteStatusReceiver;
}
sendOnPackageInstalled(mContext, statusReceiver, sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwner(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
}
mPm.installStage(installingSession, installingChildSessions);
} else {
mPm.installStage(installingSession);
}
}
packageManagerService
final void startCopy() {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
handleStartCopy();
handleReturnCode();
}
@Override
void handleReturnCode() {
processPendingInstall();
}
private void processPendingInstall() {
InstallArgs args = createInstallArgs(this);
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
mRet = args.copyApk();
}
if (mRet == PackageManager.INSTALL_SUCCEEDED) {
F2fsUtils.releaseCompressedBlocks(
mContext.getContentResolver(), new File(args.getCodePath()));
}
if (mParentInstallParams != null) {
mParentInstallParams.tryProcessInstallRequest(args, mRet);
} else {
PackageInstalledInfo res = createPackageInstalledInfo(mRet);
processInstallRequestsAsync(
res.returnCode == PackageManager.INSTALL_SUCCEEDED,
Collections.singletonList(new InstallRequest(args, res)));
}
}
// Queue up an async operation since the package installation may take a little while.
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);
}
}
// Note: supporting multi package install of both APEXes and APKs might requir some
// thinking to ensure atomicity of the install.
if (!apexInstallRequests.isEmpty() && !apkInstallRequests.isEmpty()) {
// This should've been caught at the validation step, but for some reason wasn't.
throw new IllegalStateException(
"Attempted to do a multi package install of both APEXes and APKs");
}
if (!apexInstallRequests.isEmpty()) {
if (success) {
// Since installApexPackages requires talking to external service (apexd), we
// schedule to run it async. Once it finishes, it will resume the install.
Thread t = new Thread(() -> installApexPackagesTraced(apexInstallRequests),
"installApexPackages");
t.start();
} else {
// Non-staged APEX installation failed somewhere before
// processInstallRequestAsync. In that case just notify the observer about the
// failure.
InstallRequest request = apexInstallRequests.get(0);
notifyInstallObserver(request.installResult, request.args.observer);
}
return;
}
if (success) {
for (InstallRequest request : apkInstallRequests) {
request.args.doPreInstall(request.installResult.returnCode);
}
synchronized (mInstallLock) {
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));
}
});
}
if (!doRestore) {
// No restore possible, or the Backup Manager was mysteriously not
// available -- just fire the post-install work request directly.
if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
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 && data.res.freezer != null) {
data.res.freezer.close();
}
if (data != null && data.mPostInstallRunnable != null) {
data.mPostInstallRunnable.run();
} else if (data != null && data.args != null) {
InstallArgs args = data.args;
PackageInstalledInfo parentRes = data.res;
final boolean killApp = (args.installFlags
& PackageManager.INSTALL_DONT_KILL_APP) == 0;
final boolean virtualPreload = ((args.installFlags
& PackageManager.INSTALL_VIRTUAL_PRELOAD) != 0);
handlePackagePostInstall(parentRes, killApp, virtualPreload,
didRestore, args.installSource.installerPackageName, args.observer,
args.mDataLoaderType);
https://www.edrawmax.cn/online/share.html?code=98e50f7242b611ec802fefb61da1a18d
Android 12 針對(duì)使用 PackageInstaller
API 的應(yīng)用引入了 setRequireUserAction()
方法沪悲。此方法可讓安裝程序應(yīng)用執(zhí)行應(yīng)用更新而無(wú)需用戶確認(rèn)操作获洲。
@UserActionRequirement
private int computeUserActionRequirement() {
final String packageName;
synchronized (mLock) {
if (mPermissionsManuallyAccepted) {
return USER_ACTION_NOT_NEEDED;
}
packageName = mPackageName;
}
final boolean forcePermissionPrompt =
(params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0
|| params.requireUserAction == SessionParams.USER_ACTION_REQUIRED;
if (forcePermissionPrompt) {
return USER_ACTION_REQUIRED;
}
// It is safe to access mInstallerUid and mInstallSource without lock
// because they are immutable after sealing.
final boolean isInstallPermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
final boolean isSelfUpdatePermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_SELF_UPDATES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
final boolean isUpdatePermissionGranted =
(mPm.checkUidPermission(android.Manifest.permission.INSTALL_PACKAGE_UPDATES,
mInstallerUid) == PackageManager.PERMISSION_GRANTED);
final boolean isUpdateWithoutUserActionPermissionGranted = (mPm.checkUidPermission(
android.Manifest.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION, mInstallerUid)
== PackageManager.PERMISSION_GRANTED);
final int targetPackageUid = mPm.getPackageUid(packageName, 0, userId);
final boolean isUpdate = targetPackageUid != -1 || isApexSession();
final InstallSourceInfo existingInstallSourceInfo = isUpdate
? mPm.getInstallSourceInfo(packageName)
: null;
final String existingInstallerPackageName = existingInstallSourceInfo != null
? existingInstallSourceInfo.getInstallingPackageName()
: null;
final boolean isInstallerOfRecord = isUpdate
&& Objects.equals(existingInstallerPackageName, getInstallerPackageName());
final boolean isSelfUpdate = targetPackageUid == mInstallerUid;
final boolean isPermissionGranted = isInstallPermissionGranted
|| (isUpdatePermissionGranted && isUpdate)
|| (isSelfUpdatePermissionGranted && isSelfUpdate);
final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
// Device owners and affiliated profile owners are allowed to silently install packages, so
// the permission check is waived if the installer is the device owner.
final boolean noUserActionNecessary = isPermissionGranted || isInstallerRoot
|| isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwner();
if (noUserActionNecessary) {
return USER_ACTION_NOT_NEEDED;
}
if (mPm.isInstallDisabledForPackage(getInstallerPackageName(), mInstallerUid, userId)) {
// show the installer to account for device poslicy or unknown sources use cases
return USER_ACTION_REQUIRED;
}
if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED
&& isUpdateWithoutUserActionPermissionGranted
&& (isInstallerOfRecord || isSelfUpdate)) {
return USER_ACTION_PENDING_APK_PARSING;
}
return USER_ACTION_REQUIRED;
}
private PackageManagerService.VerificationParams prepareForVerification()
throws PackageManagerException {
assertNotLocked("makeSessionActive");
@UserActionRequirement
int userActionRequirement = USER_ACTION_NOT_NEEDED;
// TODO(b/159331446): Move this to makeSessionActiveForInstall and update javadoc
if (!params.isMultiPackage) {
userActionRequirement = computeUserActionRequirement();
if (userActionRequirement == USER_ACTION_REQUIRED) {
sendPendingUserActionIntent();
return null;
} // else, we'll wait until we parse to determine if we need to
}
boolean silentUpdatePolicyEnforceable = false;
synchronized (mLock) {
if (mRelinquished) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Session relinquished");
}
if (mDestroyed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Session destroyed");
}
if (!mSealed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR,
"Session not sealed");
}
PackageLite result = parseApkLite();
if (result != null) {
mPackageLite = result;
synchronized (mProgressLock) {
mInternalProgress = 0.5f;
computeProgressLocked(true);
}
extractNativeLibraries(
mPackageLite, stageDir, params.abiOverride, mayInheritNativeLibs());
1 安裝程序聲明了 UPDATE_PACKAGES_WITHOUT_USER_ACTION 權(quán)限。
2 正在安裝的應(yīng)用程序的目標(biāo)是 API 級(jí)別 29(Android 10)或更高
if (userActionRequirement == USER_ACTION_PENDING_APK_PARSING) {
if (result.getTargetSdk() < Build.VERSION_CODES.Q) {
sendPendingUserActionIntent();
return null;
}
3 安裝器選擇了新的行為 不需要操作的行為
if (params.requireUserAction == SessionParams.USER_ACTION_NOT_REQUIRED) {
silentUpdatePolicyEnforceable = true;
}
}
}
}
if (silentUpdatePolicyEnforceable) {
if (!mSilentUpdatePolicy.isSilentUpdateAllowed(
getInstallerPackageName(), getPackageName())) {
// Fall back to the non-silent update if a repeated installation is invoked within
// the throttle time.
sendPendingUserActionIntent();
return null;
}
記錄靜默更新程序
mSilentUpdatePolicy.track(getInstallerPackageName(), getPackageName());
}
synchronized (mLock) {
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 onUserActionRequired(Intent intent) {
throw new IllegalStateException();
}
@Override
public void onPackageInstalled(String basePackageName, int returnCode, String msg,
Bundle extras) {
if (returnCode == INSTALL_SUCCEEDED) {
onVerificationComplete();
} else {
onSessionVerificationFailure(returnCode, msg);
}
}
};
} else {
localObserver = null;
}
private void onVerificationComplete() {
// Staged sessions will be installed later during boot
if (isStaged()) {
// TODO(b/136257624): Remove this once all verification logic has been transferred out
// of StagingManager.
mStagingManager.notifyPreRebootVerification_Apk_Complete(mStagedSession);
// TODO(b/136257624): We also need to destroy internals for verified staged session,
// otherwise file descriptors are never closed for verified staged session until reboot
return;
}
install();
}
直接執(zhí)行安裝