PackageInstaller代碼參考:android 11。
aosp的PackageInstaller所在位置:frameworks/base/packages/PackageInstaller
一、InstallStart
從AndroidManifest.xml了解到InstallStart為入口Activity
<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>
這里啟動(dòng)InstallStart主要分三種:
-
android.intent.action.VIEW
常規(guī)的三方應(yīng)用安裝設(shè)置的action -
android.intent.action.INSTALL_PACKAGE
系統(tǒng)應(yīng)用才擁有的權(quán)限 -
android.content.pm.action.CONFIRM_INSTALL
session install
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
protected void onCreate(@Nullable Bundle savedInstanceState) {
...
//Activity中的getCallingPackage需要呼起源按startActivityForResult方式啟動(dòng)才能獲取到callingpakcage猜谚,否則返回null
String callingPackage = getCallingPackage();
...
//通過(guò)callingpackage獲取ApplicationInfo
final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
final int originatingUid = getOriginatingUid(sourceInfo);
...
if (isSessionInstall) {
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
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)) {
nextActivity.setClass(this, PackageInstallerActivity.class);
} else {
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) {
startActivity(nextActivity);
}
finish();
}
跳轉(zhuǎn)頁(yè)面規(guī)則:
action 是 sessioninstall:直接跳轉(zhuǎn)PackageInstallerActivity麻惶;
action 不是 sessioninstall:
- uri為content scheme跳轉(zhuǎn) InstallStaging
- uri為pacakge scheme跳轉(zhuǎn) PackageInstallerActivity
- uri為其他scheme 這里應(yīng)該是指file溶推,則結(jié)束安裝
莫非7.0之后除了通過(guò)FileUriExposedException來(lái)強(qiáng)制使用content方式之外碍脏,Installer也有邏輯限制么?掃下7.0-11.0的Installer:發(fā)現(xiàn)從8.1開(kāi)始卖擅,原生Installer對(duì)file uri做了如上限制鸣奔,直接結(jié)束安裝。當(dāng)然Installer各廠商都會(huì)做定制惩阶,而且廠商直接的差異也是非常大的挎狸。
另外,從前面InstallStart的intent-filter了解到断楷,PackageInstallerActivity.SCHEME_PACKAGE 對(duì)應(yīng)的是android.intent.action.INSTALL_PACKAGE action锨匆,非三方使用的
<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>
那么如果正常的是走action:android.intent.action.VIEW + uri conent,那么會(huì)進(jìn)入到InstallStaging頁(yè)面冬筒。
二恐锣、InstallStaging
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
protected void onResume() {
super.onResume();
...
mStagingTask = new StagingAsyncTask();
mStagingTask.execute(getIntent().getData());
}
跳轉(zhuǎn)InstallStaging 核心功能在這個(gè)異步任務(wù)。接下來(lái)看看這個(gè)任務(wù)是做了什么:
private final class StagingAsyncTask extends AsyncTask<Uri, Void, Boolean> {
@Override
protected Boolean doInBackground(Uri... params) {
if (params == null || params.length <= 0) {
return false;
}
Uri packageUri = params[0];
try (InputStream in = getContentResolver().openInputStream(packageUri)) {
// Despite the comments in ContentResolver#openInputStream the returned stream can
// be null.
if (in == null) {
return false;
}
//將待安裝的apk copy一份到當(dāng)前installer data/data對(duì)應(yīng)的目錄下
try (OutputStream out = new FileOutputStream(mStagedFile)) {
byte[] buffer = new byte[1024 * 1024];
int bytesRead;
while ((bytesRead = in.read(buffer)) >= 0) {
// Be nice and respond to a cancellation
if (isCancelled()) {
return false;
}
out.write(buffer, 0, bytesRead);
}
}
} catch (IOException | SecurityException | IllegalStateException e) {
Log.w(LOG_TAG, "Error staging apk from content URI", e);
return false;
}
return true;
}
@Override
protected void onPostExecute(Boolean success) {
if (success) {
// Now start the installation again from a file
Intent installIntent = new Intent(getIntent());
//基于copy后的apk路徑舞痰,將uri從content切為file來(lái)進(jìn)行后續(xù)的安裝操作
installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
installIntent.setData(Uri.fromFile(mStagedFile));
if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
}
installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivity(installIntent);
InstallStaging.this.finish();
} else {
showError();
}
}
}
InstallStaging通過(guò)三方app提供的content路徑土榴,將待安裝的apk copy一份到當(dāng)前installer data/data對(duì)應(yīng)的目錄下,然后將uri從content轉(zhuǎn)為file响牛,跳轉(zhuǎn)到DeleteStagedFileOnResult頁(yè)面玷禽。
三、DeleteStagedFileOnResult
public class DeleteStagedFileOnResult extends Activity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//跳轉(zhuǎn)到PackageInstallerActivity
if (savedInstanceState == null) {
Intent installIntent = new Intent(getIntent());
installIntent.setClass(this, PackageInstallerActivity.class);
installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
startActivityForResult(installIntent, 0);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
//刪除copy的apk文件
File sourceFile = new File(getIntent().getData().getPath());
sourceFile.delete();
setResult(resultCode, data);
finish();
}
}
DeleteStagedFileOnResult做的事情很簡(jiǎn)單呀打,就是跳轉(zhuǎn)PackageInstallerActivity矢赁,然后startActivity回調(diào)之后刪除copy的apk文件。
四贬丛、PackageInstallerActivity
在前面InstallStart跳轉(zhuǎn)邏輯坯台,如果是sessionInstall 或者匹配uri scheme為package,則直接跳轉(zhuǎn)PackageInstallerActivity瘫寝,如果是uri scheme為content,則要通過(guò)InstallStaging->DeleteStagedFileOnResult然后再跳到PackageInstallerActivity稠炬。繞了遠(yuǎn)路焕阿,但也只是做了apk的copy,最終殊途同歸首启,下面來(lái)詳細(xì)看一下PackageInstallerActivity:
frameworks/base/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
protected void onCreate(Bundle icicle) {
getWindow().addSystemFlags(SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
super.onCreate(null);
if (icicle != null) {
mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
}
mPm = getPackageManager();
mIpm = AppGlobals.getPackageManager();
mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
mInstaller = mPm.getPackageInstaller();
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
final Intent intent = getIntent();
mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);
mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
PackageInstaller.SessionParams.UID_UNKNOWN);
mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)
? getPackageNameForUid(mOriginatingUid) : null;
final Uri packageUri;
//確認(rèn)sessionInstall方式的sessionInfo
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;
packageUri = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
// if there's nothing to do, quietly slip into the ether
if (packageUri == null) {
Log.w(TAG, "Unspecified source");
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
finish();
return;
}
if (DeviceUtils.isWear(this)) {
showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);
return;
}
//1 解析uri
boolean wasSetUp = processPackageUri(packageUri);
if (!wasSetUp) {
return;
}
// load dummy layout with OK button disabled until we override this layout in
// startInstallConfirm
bindUi();
//2 安裝前檢查
checkIfAllowedAndInitiateInstall();
}
先看1暮屡,解析uri
private boolean processPackageUri(final Uri packageUri) {
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
//因?yàn)閏opy apk后,content會(huì)轉(zhuǎn)為file毅桃,因此到PackageInstallerActivity中就沒(méi)有content的scheme類型了
switch (scheme) {
case SCHEME_PACKAGE: {
try {
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: {
File sourceFile = new File(packageUri.getPath());
mPkgInfo = PackageUtil.getPackageInfo(this, sourceFile,
PackageManager.GET_PERMISSIONS);
// Check for parse errors
if (mPkgInfo == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return false;
}
//通過(guò)application info獲取應(yīng)用的icon和label褒纲,并包裝為PackageUtil.AppSnippet
mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
} break;
default: {
throw new IllegalArgumentException("Unexpected URI scheme " + packageUri);
}
}
return true;
}
再看2安裝前檢查
/**
* Check if it is allowed to install the package and initiate install if allowed. If not allowed
* show the appropriate dialog.
*/
private void checkIfAllowedAndInitiateInstall() {
// Check for install apps user restriction first.
//系統(tǒng)對(duì)多用戶管理會(huì)設(shè)置不同的權(quán)限准夷,這里是做用戶權(quán)限校驗(yàn)
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;
}
//用戶手動(dòng)設(shè)置同意未知來(lái)源安裝權(quán)限
if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
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) {
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 {
handleUnknownSources();
}
}
}
這里主要是安裝一系列權(quán)限限制檢查,在允許未知來(lái)源安裝的條件下莺掠,接著看initiateInstall
private void initiateInstall() {
String pkgName = mPkgInfo.packageName;
// Check if there is already a package on the device with this name
// but it has been renamed to something else.
//檢查設(shè)備上是否已經(jīng)有此名稱的軟件包, 但是它已經(jīng)被重命名為其他東西衫嵌。
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;
}
// Check if package is already installed. display confirmation dialog if replacing pkg
try {
// This is a little convoluted because we want to get all uninstalled
// apps, but this may include apps with just data, and if it is just
// data we still want to count it as "installed”.
//檢查當(dāng)前包是否已經(jīng)被安裝
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.MATCH_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
startInstallConfirm();
}
private void startInstallConfirm() {
View viewToEnable;
//已經(jīng)安裝的app升級(jí)
if (mAppInfo != null) {
viewToEnable = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
? requireViewById(R.id.install_confirm_question_update_system)
: requireViewById(R.id.install_confirm_question_update);
} else {
//新安裝app
// This is a new application with no permissions.
viewToEnable = requireViewById(R.id.install_confirm_question);
}
viewToEnable.setVisibility(View.VISIBLE);
mEnableOk = true;
mOk.setEnabled(true);
mOk.setFilterTouchesWhenObscured(true);
}
這里設(shè)置mOk,對(duì)應(yīng)的安裝按鈕彻秆,設(shè)置可點(diǎn)擊安裝楔绞。按鈕初始化是在前面bindUi的時(shí)候:
private void bindUi() {
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();
}
}
}, null);
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);
if (!mOk.isInTouchMode()) {
mAlert.getButton(DialogInterface.BUTTON_NEGATIVE).requestFocus();
}
}
如果是正常用戶安裝這里直接就走startInstall()
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI);
newIntent.setClass(this, InstallInstalling.class);
String installerPackageName = getIntent().getStringExtra(
Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (mOriginatingURI != null) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
}
if (mReferrerURI != null) {
newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
}
if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
}
if (installerPackageName != null) {
newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
installerPackageName);
}
if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
}
newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
startActivity(newIntent);
finish();
}
跳轉(zhuǎn)到InstallInstalling來(lái)處理
五、InstallInstalling
protected void onCreate(@Nullable Bundle savedInstanceState) {
…
//通過(guò)PackageInstallerService來(lái)創(chuàng)建session
mSessionId = getPackageManager().getPackageInstaller().createSession(params);
...
}
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerService.java
private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
throws IOException {
...
session = new PackageInstallerSession(mInternalCallback, mContext, mPm, this,
mInstallThread.getLooper(), mStagingManager, sessionId, userId, callingUid,
installSource, params, createdMillis,
stageDir, stageCid, null, false, false, false, false, null, SessionInfo.INVALID_ID,
false, false, false, SessionInfo.STAGED_SESSION_NO_ERROR, "");
synchronized (mSessions) {
mSessions.put(sessionId, session);
}
...
return sessionId;
}
創(chuàng)建了一個(gè)PackageInstallerSession唇兑,這里通過(guò)PackageInstallerSession做進(jìn)程間通信酒朵,最終將安裝apk任務(wù)交給PackageManagerService
再接著往下看InstallInstalling的onResume
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);
if (sessionInfo != null && !sessionInfo.isActive()) {
mInstallingTask = new InstallingAsyncTask();
mInstallingTask.execute();
} else {
// we will receive a broadcast when the install is finished
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
}
}
}
啟動(dòng)InstallingAsyncTask異步任務(wù)
/**
* Send the package to the package installer and then register a event result observer that
* will call {@link #launchFinishBasedOnResult(int, int, String)}
*/
private final class InstallingAsyncTask extends AsyncTask<Void, Void,
PackageInstaller.Session> {
volatile boolean isDone;
@Override
protected PackageInstaller.Session doInBackground(Void... params) {
PackageInstaller.Session session;
try {
//openSession就是獲取onCreate創(chuàng)建的PackageInstaller.Session,并將PackageInstallerSession賦給當(dāng)前session
session = getPackageManager().getPackageInstaller().openSession(mSessionId);
} catch (IOException e) {
return null;
}
session.setStagingProgress(0);
//將apk文件寫到session中
try {
File file = new File(mPackageURI.getPath());
try (InputStream in = new FileInputStream(file)) {
long sizeBytes = file.length();
try (OutputStream out = session
.openWrite("PackageInstaller", 0, sizeBytes)) {
byte[] buffer = new byte[1024 * 1024];
while (true) {
int numRead = in.read(buffer);
if (numRead == -1) {
session.fsync(out);
break;
}
if (isCancelled()) {
session.close();
break;
}
out.write(buffer, 0, numRead);
if (sizeBytes > 0) {
float fraction = ((float) numRead / (float) sizeBytes);
session.addProgress(fraction);
}
}
}
}
return session;
} catch (IOException | SecurityException e) {
Log.e(LOG_TAG, "Could not write package", e);
session.close();
return null;
} finally {
synchronized (this) {
isDone = true;
notifyAll();
}
}
}
@Override
protected void onPostExecute(PackageInstaller.Session session) {
if (session != null) {
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
broadcastIntent.setPackage(getPackageName());
broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallInstalling.this,
mInstallId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
//session commit
session.commit(pendingIntent.getIntentSender());
mCancelButton.setEnabled(false);
setFinishOnTouchOutside(false);
} else {
getPackageManager().getPackageInstaller().abandonSession(mSessionId);
if (!isCancelled()) {
launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
}
}
}
}
InstallInstalling主要干三件事情:
- 創(chuàng)建與PackageManagerService進(jìn)程間通信的PackageInstallerSession扎附;
- 將apk通過(guò)io流寫入到PackageInstallerSession中蔫耽;
- 調(diào)用PackageInstallerSession的commit方法,將apk交給PackageManagerService來(lái)執(zhí)行安裝留夜。
六匙铡、PackageInstallerSession
session.commit最終是PackageInstallerSession執(zhí)行commit
frameworks/base/services/core/java/com/android/server/pm/PackageInstallerSession.java
public void commit(@NonNull IntentSender statusReceiver, boolean forTransfer) {
...
dispatchStreamValidateAndCommit();
}
private void dispatchStreamValidateAndCommit() {
mHandler.obtainMessage(MSG_STREAM_VALIDATE_AND_COMMIT).sendToTarget();
}
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
switch (msg.what) {
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,
isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId,
packageName, returnCode, message, extras);
break;
case MSG_SESSION_VERIFICATION_FAILURE:
final int error = msg.arg1;
final String detailMessage = (String) msg.obj;
onSessionVerificationFailure(error, detailMessage);
break;
}
return true;
}
};
private void handleStreamValidateAndCommit() {
...
mHandler.obtainMessage(MSG_INSTALL).sendToTarget();
}
private void handleInstall() {
...
synchronized (mLock) {
installNonStagedLocked(childSessions);
}
...
}
private void installNonStagedLocked(List<PackageInstallerSession> childSessions)
throws PackageManagerException {
final PackageManagerService.ActiveInstallSession installingSession =
makeSessionActiveLocked();
if (installingSession == null) {
return;
}
if (isMultiPackage()) {
List<PackageManagerService.ActiveInstallSession> 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.ActiveInstallSession installingChildSession =
session.makeSessionActiveLocked();
if (installingChildSession != null) {
installingChildSessions.add(installingChildSession);
}
} catch (PackageManagerException e) {
failure = e;
success = false;
}
}
if (!success) {
sendOnPackageInstalled(mContext, mRemoteStatusReceiver, sessionId,
isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked(), userId, null,
failure.error, failure.getLocalizedMessage(), null);
return;
}
mPm.installStage(installingChildSessions);
} else {
mPm.installStage(installingSession);
}
}
這里mPm是PackageManagerService,通過(guò)installStage來(lái)執(zhí)行安裝香伴。
這里補(bǔ)充一個(gè)分支:
private PackageManagerService.ActiveInstallSession makeSessionActiveLocked()
throws PackageManagerException {
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");
}
final IPackageInstallObserver2 localObserver;
if ((params.installFlags & PackageManager.INSTALL_APEX) != 0) {
localObserver = null;
} else {
if (!params.isMultiPackage) {
Preconditions.checkNotNull(mPackageName);
Preconditions.checkNotNull(mSigningDetails);
Preconditions.checkNotNull(mResolvedBaseFile);
if (needToAskForPermissionsLocked()) {
// User needs to confirm installation;
// give installer an intent they can use to involve
// user.
// 如果權(quán)限校驗(yàn)不滿足慰枕,則走PackageInstallObserverAdapter回調(diào)
final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
intent.setPackage(mPm.getPackageInstallerPackageName());
intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
try {
mRemoteObserver.onUserActionRequired(intent);
} catch (RemoteException ignored) {
}
// Commit was keeping session marked as active until now; release
// that extra refcount so session appears idle.
closeInternal(false);
return null;
}
...
return new PackageManagerService.ActiveInstallSession(mPackageName, stageDir,
localObserver, params, mInstallerPackageName, mInstallerUid, user,
mSigningDetails);
}
這里needToAskForPermissionsLocked()會(huì)校驗(yàn)系統(tǒng)權(quán)限:
private boolean needToAskForPermissionsLocked() {
if (mPermissionsManuallyAccepted) {
return false;
}
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 int targetPackageUid = mPm.getPackageUid(mPackageName, 0, userId);
final boolean isPermissionGranted = isInstallPermissionGranted
|| (isUpdatePermissionGranted && targetPackageUid != -1)
|| (isSelfUpdatePermissionGranted && targetPackageUid == mInstallerUid);
final boolean isInstallerRoot = (mInstallerUid == Process.ROOT_UID);
final boolean isInstallerSystem = (mInstallerUid == Process.SYSTEM_UID);
final boolean forcePermissionPrompt =
(params.installFlags & PackageManager.INSTALL_FORCE_PERMISSION_PROMPT) != 0;
// 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.
return forcePermissionPrompt || !(isPermissionGranted || isInstallerRoot
|| isInstallerSystem || isInstallerDeviceOwnerOrAffiliatedProfileOwnerLocked());
}
如果權(quán)限校驗(yàn)不滿足,則走PackageInstallObserverAdapter回調(diào)
final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_INSTALL);
intent.setPackage(mPm.getPackageInstallerPackageName());
intent.putExtra(PackageInstaller.EXTRA_SESSION_ID, sessionId);
mRemoteObserver.onUserActionRequired(intent);
這里會(huì)拉起session.commit(pendingIntent.getIntentSender());中對(duì)應(yīng)的組件的回調(diào)
static class PackageInstallObserverAdapter extends PackageInstallObserver {
999 private final Context mContext;
1000 private final IntentSender mTarget;
1001 private final int mSessionId;
1002 private final boolean mShowNotification;
1003 private final int mUserId;
1004
1005 public PackageInstallObserverAdapter(Context context, IntentSender target, int sessionId,
1006 boolean showNotification, int userId) {
1007 mContext = context;
1008 mTarget = target;
1009 mSessionId = sessionId;
1010 mShowNotification = showNotification;
1011 mUserId = userId;
1012 }
1013
1014 @Override
1015 public void onUserActionRequired(Intent intent) {
1016 final Intent fillIn = new Intent();
1017 fillIn.putExtra(PackageInstaller.EXTRA_SESSION_ID, mSessionId);
1018 fillIn.putExtra(PackageInstaller.EXTRA_STATUS,
1019 PackageInstaller.STATUS_PENDING_USER_ACTION);
1020 fillIn.putExtra(Intent.EXTRA_INTENT, intent);
1021 try {
1022 mTarget.sendIntent(mContext, 0, fillIn, null, null);
1023 } catch (SendIntentException ignored) {
1024 }
1025 }
三方應(yīng)用可以通過(guò)session install這種方式拉起PackageInstaller安裝即纲。
七具帮、總結(jié)
整體流程時(shí)序:
不同階段的任務(wù)梳理:
1)InstallStart
主要是根據(jù)不同的uri scheme執(zhí)行不同頁(yè)面的跳轉(zhuǎn):
跳轉(zhuǎn)頁(yè)面規(guī)則:
action 是 sessioninstall:直接跳轉(zhuǎn)PackageInstallerActivity;
action 不是 sessioninstall:
- uri為content scheme跳轉(zhuǎn) InstallStaging
- uri為pacakge scheme跳轉(zhuǎn) PackageInstallerActivity
- uri為其他scheme 這里應(yīng)該是指file低斋,則結(jié)束安裝
2)InstallStaging
通過(guò)三方app提供的content路徑蜂厅,將待安裝的apk copy一份到當(dāng)前installer data/data對(duì)應(yīng)的目錄下,然后將uri scheme從content轉(zhuǎn)為file膊畴。
3)DeleteStagedFileOnResult
跳轉(zhuǎn)PackageInstallerActivity掘猿,然后startActivity回調(diào)之后刪除copy的apk文件。
4)PackageInstallerActivity
Uri解析和安裝前權(quán)限檢查(用戶權(quán)限檢查唇跨、安裝來(lái)源權(quán)限檢查)稠通。
5)InstallInstalling
建立與PackageManagerService進(jìn)程間通信的通道,將apk傳遞過(guò)去
- 創(chuàng)建與PackageManagerService進(jìn)程間通信的PackageInstallerSession买猖;
- 將apk通過(guò)io流寫入到PackageInstallerSession中改橘;
- 調(diào)用PackageInstallerSession的commit方法,將apk交給PackageManagerService來(lái)執(zhí)行安裝玉控。
6)PackageInstallerSession
觸發(fā)PackageManagerService對(duì)apk進(jìn)行安裝飞主。
本篇文章簡(jiǎn)單梳理了原生的PackageInstaller的中轉(zhuǎn)安裝流程,目前國(guó)內(nèi)廠商基于原生PackageInstaller都進(jìn)行了深度定制,且策略各不相同碌识,與原生差異還是相當(dāng)大的碾篡。
八、廠商定制研究
- huawei: 基于原生PackageInstaller改的筏餐,整體頁(yè)面轉(zhuǎn)換與原生高度一致开泽,對(duì)不同頁(yè)面的邏輯有自己的定制;
- vivo:在9.0及其以下的低版本中胖烛,新加了一個(gè)VivoPackageInstallerActivity眼姐,定制邏輯在這個(gè)activity中,主流程也基本與原生保持一致;
- xiaomi:不是基于原生PackageInstaller改的佩番,邏輯集中在PackageInstallerAcitivty中众旗;
- oppo:不是基于原生PackageInstaller改的,邏輯集中在OppoPackageInstallerActivity中趟畏;