1 概述
installStart 本質(zhì)一個Activity策幼,屬于系統(tǒng)應(yīng)用程序PackageInstaller
android8.0后系統(tǒng)禁止跨進(jìn)程傳遞文件url巡通,所以文件的url都以content做scheme來進(jìn)行傳遞
8.0后安裝應(yīng)用請求
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(xxxxx, "application/vnd.android.package-archive");
這里通過封裝該intent啟動InstallStartActivity
<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>
通過PackageInstaller應(yīng)用程序的manifest.xml文件我們可以知道為什么會啟動InstallStart
<intent-filter android:priority="1">
<action android:name="android.intent.action.VIEW" />
<data android:scheme="content" />
<data android:mimeType="application/vnd.android.package-archive" />
</intent-filter>
2 installStart都做了什么
首先作為Activity内边,在啟動后一定遵循生命周期
2.1 onCreate
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//獲取packageManager
mPackageManager = getPackageManager();
//獲取UserManager類對象
mUserManager = getSystemService(UserManager.class);
//獲取調(diào)用方intent
Intent intent = getIntent();
// 獲取調(diào)用者包名用于驗(yàn)證調(diào)用者信息
String callingPackage = getCallingPackage();
String callingAttributionTag = null;
//這里ACTION_CONFIRM_INSTALL主要是用戶確認(rèn)是否安裝應(yīng)用的操作
//一般場景為安裝程序需要執(zhí)行敏感操作,需用戶確認(rèn)才能繼續(xù)安裝
final boolean isSessionInstall =
PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction());
// If the activity was started via a PackageInstaller session, we retrieve the calling
// package from that session
//這里獲取安裝sessionid,普通安裝 sessionid -1
final int sessionId = (isSessionInstall
? intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1)
: -1);
/**
* 這里主要針對這種異常case進(jìn)行判斷
* 調(diào)用者包信息闰渔,找不到暮现,但是存在安裝會話
*/
if (callingPackage == null && sessionId != -1) {
PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
callingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null;
callingAttributionTag =
(sessionInfo != null) ? sessionInfo.getInstallerAttributionTag() : null;
}
//根據(jù)包獲取應(yīng)用相關(guān)信息
final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
//獲取元進(jìn)程用戶id
final int originatingUid = getOriginatingUid(sourceInfo);
//定義了一個flag还绘,作用 檢查安裝資源是否值得信任
boolean isTrustedSource = false;
if (sourceInfo != null
&& (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
/**
* case
* 條件 元進(jìn)程不為空,并且元進(jìn)程為系統(tǒng)應(yīng)用進(jìn)程
*
* 從intent獲取EXTRA_NOT_UNKNOWN_SOURCE的值確認(rèn)是否為值得信任的資源
*/
isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
}
if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
/**
* case 如果不是可信任資源且元進(jìn)程的uid不是未知的
*
* 過程
* 獲取sdk版本
* 獲取失敗直接拒絕安裝
*
* 獲取成功
* 檢查sdk版本大于 Build.VERSION_CODES.O并且該UID所對應(yīng)應(yīng)用未請求安裝包權(quán)限
*
*拒絕安裝
*/
final int targetSdkVersion = getMaxTargetSdkVersionForUid(this, 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 && !isUidRequestingPermission(
originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
+ Manifest.permission.REQUEST_INSTALL_PACKAGES);
mAbortInstall = true;
}
}
if (mAbortInstall) {
//設(shè)置啟動結(jié)果RESULT_CANCELED 通知啟動activity
setResult(RESULT_CANCELED);
//關(guān)閉當(dāng)前activity
finish();
return;
}
//這里構(gòu)建新的intent封裝 啟動Activity的intent
Intent nextActivity = new Intent(intent);
//定義新intent的flag
//Intent.FLAG_ACTIVITY_FORWARD_RESULT 通知啟動Activity可以接受結(jié)果
// Intent.FLAG_GRANT_READ_URI_PERMISSION 設(shè)置目標(biāo)activity可以讀取url
nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT
| Intent.FLAG_GRANT_READ_URI_PERMISSION);
// The the installation source as the nextActivity thinks this activity is the source, hence
// set the originating UID and sourceInfo explicitly
//元進(jìn)程包信息
nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_ATTRIBUTION_TAG,
callingAttributionTag);
//元進(jìn)程 進(jìn)程信息
nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
//元進(jìn)程 用戶id
nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
if (isSessionInstall) {
/**
* case1 這里判斷當(dāng)前是否為用戶確認(rèn)安裝操作
*
* 結(jié)果 跳轉(zhuǎn)到PackageInstallerActivity
*
*/
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
/**
* case2 普通安裝栖袋,這里檢查packageUri的Scheme是否為content
*
* 結(jié)果跳轉(zhuǎn)到InstallStaging
*/
Uri packageUri = intent.getData();
if (packageUri != null && packageUri.getScheme().equals(
ContentResolver.SCHEME_CONTENT)) {
// [IMPORTANT] This path is deprecated, but should still work. Only necessary
// features should be added.
// Copy file to prevent it from being changed underneath this process
nextActivity.setClass(this, InstallStaging.class);
} else if (packageUri != null && packageUri.getScheme().equals(
PackageInstallerActivity.SCHEME_PACKAGE)) {
//case3 這里判斷packageUri的scheme是否為package
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
//case4 如果上述幾種情況都不滿足拍顷,則通知源端,應(yīng)用安裝失敗
Intent result = new Intent();
result.putExtra(Intent.EXTRA_INSTALL_RESULT,
PackageManager.INSTALL_FAILED_INVALID_URI);
setResult(RESULT_FIRST_USER, result);
nextActivity = null;
}
}
if (nextActivity != null) {
//啟動下一個Activity
startActivity(nextActivity);
}
//退出當(dāng)前activity
finish();
}
總結(jié):InstallStart類作為系統(tǒng)應(yīng)用程序安裝啟動的第一個Activity塘幅,它的主要職責(zé)為
- 1 獲取元進(jìn)程的包信息昔案,應(yīng)用信息 用戶id
- 2 確認(rèn)當(dāng)次操作是普通安裝還是用戶確認(rèn)安裝
- 3 確認(rèn)安裝資源是否可信任
- 4 最后根據(jù)安裝方式和intent的data的scheme來確認(rèn)跳轉(zhuǎn)哪一個Acitivty,進(jìn)行接下來的處理
普通安裝這里首先看InstallStaging