1 安裝入口PackageInstallerActivity,這個類只是在安裝前做准備。通過各種校驗,然后彈出被安裝應用的權限框,等待用戶安裝。具體的流程如下
1.1 求mSessionId 如果是已經存在的則判斷對應的SessinInfo是否存在,否則默認一個-1
if (PackageInstaller.ACTION_CONFIRM_PERMISSIONS.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;
}
//如果有SessInfo則證明傳過來的sessionId是有效的,並且獲取packageUri
mSessionId = sessionId;
packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
mOriginatingURI = null;
mReferrerURI = null;
} else {
//如果是用戶自己拉起來的安裝,則默認sessionId為-1 病且獲取 packageUri
mSessionId = -1;
packageUri = intent.getData();
mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
}
1.2 開始做校驗
// 返回URI解析錯誤 -3
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. 3 如果是系統應用拉起安裝則直接進入包分析階段
final boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(intent);
if (!requestFromUnknownSource) {
//進入packageUri處理階段
processPackageUri(packageUri);
return;
}
//安裝請求是否來自於一個未知的源。
private boolean isInstallRequestFromUnknownSource(Intent intent) {
String callerPackage = getCallingPackage();
if (callerPackage != null && intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)) {
try {
mSourceInfo = mPm.getApplicationInfo(callerPackage, 0);
if (mSourceInfo != null) {
if ((mSourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
// Privileged apps are not considered an unknown source.
//如果安裝請求是來自於一個系統應用,則可以明確源是已知的
return false;
}
}
} catch (NameNotFoundException e) { }
}
return true;//否則源是未知
}
1. 4 針對管理員賬戶做不同的處理
// If the admin prohibits it, or we're running in a managed profile, just show error
// and exit. Otherwise show an option to take the user to Settings to change the setting.
// 是否是管理員用戶
final boolean isManagedProfile = mUserManager.isManagedProfile();
if (isUnknownSourcesDisallowed()) {
//如果有用戶限制了未知來源應用的安裝
if ((mUserManager.getUserRestrictionSource(UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES,
Process.myUserHandle()) & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
//如果不是system用戶限制當前用戶安裝未知來源app,啟動設置,使用戶在設置里面修改
showDialogInner(DLG_UNKNOWN_SOURCES);
} else {
//如果是system用戶限制的,則直接退出
startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
clearCachedApkIfNeededAndFinish();
}
} else if (!isUnknownSourcesEnabled() && isManagedProfile) {
//如果不允許安裝未知市場的應用,並且當前是管理員用戶,則彈出"您的管理員不允許安裝來源不明的應用"的對話框
showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
} else if (!isUnknownSourcesEnabled()) {
// Ask user to enable setting first
//如果不允許安裝未知市場的應用,則彈出這個對話框修改設置
showDialogInner(DLG_UNKNOWN_SOURCES);
} else {
//進入packageUri處理階段
processPackageUri(packageUri);
}
1. 5 處理文件的uri
//處理包的uri
private void processPackageUri(final Uri packageUri) {
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
final PackageUtil.AppSnippet as;
switch (scheme) {
case SCHEME_PACKAGE:
try {
mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(), PackageManager.GET_PERMISSIONS | PackageManager.GET_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;
}
as = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo), mPm.getApplicationIcon(mPkgInfo.applicationInfo));
break;
case SCHEME_FILE:
File sourceFile = new File(packageUri.getPath());
PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);
// Check for parse errors
if (parsed == null) {
Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
showDialogInner(DLG_PACKAGE_ERROR);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
return;
}
mPkgInfo = PackageParser.generatePackageInfo(parsed, null, PackageManager.GET_PERMISSIONS, 0, 0, null, new PackageUserState());
as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
break;
case SCHEME_CONTENT:
//重新復制一個安裝包在解析,返回一個SCHEME_FILE類型的uri重新解析包uri
mStagingAsynTask = new StagingAsyncTask();
mStagingAsynTask.execute(packageUri);
return;
default:
Log.w(TAG, "Unsupported scheme " + scheme);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
clearCachedApkIfNeededAndFinish();
return;
}
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
//啟動安裝
initiateInstall();
}
1.6 啟動安裝
//啟動安裝
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. 是否有同名應用已經安裝上去了。在此安裝則被認為是替換安裝
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".
// 獲取設備上有殘存數據,並且標記為“installed”的,實際上已經被卸載的應用。
mAppInfo = mPm.getApplicationInfo(pkgName, PackageManager.GET_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
////如果應用是被卸載的,但是又是被標識成安裝過的,則認為是新安裝
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
//列出權限列表,等待用戶確認安裝
startInstallConfirm();
}
1.7 確認安裝權限
private void startInstallConfirm() {
TabHost tabHost = (TabHost)findViewById(android.R.id.tabhost);
tabHost.setup();
tabHost.setVisibility(View.VISIBLE);
ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
TabsAdapter adapter = new TabsAdapter(this, tabHost, viewPager);
// If the app supports runtime permissions the new permissions will
// be requested at runtime, hence we do not show them at install.
// 如果app支持運行時權限,這里會顯示新的運行時權限
// 根據版本判斷app是否有可能有運行時權限
boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
boolean permVisible = false;
mScrollView = null;
mOkCanInstall = false;
int msg = 0;
//perms這個對象包括了該應用的用戶的uid以及相應的一些權限,以及權限組信息。
AppSecurityPermissions perms = new AppSecurityPermissions(this, mPkgInfo);
//所有的權限數量
final int N = perms.getPermissionCount(AppSecurityPermissions.WHICH_ALL);
if (mAppInfo != null) {
//如果是替換應用
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? R.string.install_confirm_question_update_system : R.string.install_confirm_question_update;
mScrollView = new CaffeinatedScrollView(this);
mScrollView.setFillViewport(true);
boolean newPermissionsFound = false;
if (!supportsRuntimePermissions) {
newPermissionsFound = (perms.getPermissionCount(AppSecurityPermissions.WHICH_NEW) > 0);
if (newPermissionsFound) {
permVisible = true;
//顯示新添加的權限項(這個view竟然是系統的view)
mScrollView.addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_NEW));
}
}
if (!supportsRuntimePermissions && !newPermissionsFound) {
//如果既不支持可運行權限項也沒有新權限發現,則提示沒有新權限
LayoutInflater inflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE);
TextView label = (TextView)inflater.inflate(R.layout.label, null);
label.setText(R.string.no_new_perms);
mScrollView.addView(label);
}
adapter.addTab(tabHost.newTabSpec(TAB_ID_NEW).setIndicator(getText(R.string.newPerms)), mScrollView);
} else {
findViewById(R.id.tabscontainer).setVisibility(View.GONE);
findViewById(R.id.spacer).setVisibility(View.VISIBLE);
}
if (!supportsRuntimePermissions && N > 0) {
permVisible = true;
LayoutInflater inflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View root = inflater.inflate(R.layout.permissions_list, null);
if (mScrollView == null) {
mScrollView = (CaffeinatedScrollView)root.findViewById(R.id.scrollview);
}
//如果沒有運行時權限並且有權限,則列出所有權限
((ViewGroup)root.findViewById(R.id.permission_list)).addView(perms.getPermissionsView(AppSecurityPermissions.WHICH_ALL));
adapter.addTab(tabHost.newTabSpec(TAB_ID_ALL).setIndicator(getText(R.string.allPerms)), root);
}
if (!permVisible) {
//如果不需要任何權限。更新的不需要新的權限以及運行時權限
if (mAppInfo != null) {
// This is an update to an application, but there are no
// permissions at all.
msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0 ? R.string.install_confirm_question_update_system_no_perms: R.string.install_confirm_question_update_no_perms;
findViewById(R.id.spacer).setVisibility(View.VISIBLE);
} else {
// This is a new application with no permissions.
msg = R.string.install_confirm_question_no_perms;
}
tabHost.setVisibility(View.INVISIBLE);
mScrollView = null;
}
if (msg != 0) {
((TextView)findViewById(R.id.install_confirm_question)).setText(msg);
}
mInstallConfirm.setVisibility(View.VISIBLE);
mOk.setEnabled(true);
if (mScrollView == null) {
// There is nothing to scroll view, so the ok button is immediately
// set to install.
mOk.setText(R.string.install);
mOkCanInstall = true;
} else {
mScrollView.setFullScrollAction(new Runnable() {
@Override
public void run() {
mOk.setText(R.string.install);
mOkCanInstall = true;
}
});
}
}
1. 8 點擊安裝
public void onClick(View v) {
if (v == mOk) {
if (mOkCanInstall || mScrollView == null) {
if (mSessionId != -1) {
//如果原來是確認權限請求則賦予安裝權限退出
mInstaller.setPermissionsResult(mSessionId, true);
clearCachedApkIfNeededAndFinish();
} else {
//開始安裝
startInstall();
}
} else {mScrollView.pageScroll(View.FOCUS_DOWN);}
} else if (v == mCancel) {
// Cancel and finish 取消安裝
setResult(RESULT_CANCELED);
if (mSessionId != -1) {
mInstaller.setPermissionsResult(mSessionId, false);
}
clearCachedApkIfNeededAndFinish();
}
}
1.9 進入安裝
private void startInstall() {
// Start subactivity to actually install the application
Intent newIntent = new Intent();
//帶走安裝包的applicationInfo
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mPkgInfo.applicationInfo);
newIntent.setData(mPackageURI); //帶走安裝包的applicationInfo
newIntent.setClass(this, InstallAppProgress.class);
String installerPackageName = getIntent().getStringExtra( Intent.EXTRA_INSTALLER_PACKAGE_NAME);
if (mOriginatingURI != null) { //帶走安裝包的mOriginatingURI
newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
}
if (mReferrerURI != null) {//帶走安裝包的mReferrerURI
newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
}
if (mOriginatingUid != VerificationParams.NO_UID) {//帶走安裝包的mOriginatingUid,這個uid如果不是拉安裝的應用的uid
newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
}
if (installerPackageName != null) {//帶走安裝包的installerPackageName
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();
}
2 安裝過程InstallAppProgress
2.1 注冊安裝監聽
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION);
registerReceiver(mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null /*scheduler*/);
2.2 正式安裝
替換現存的包標示
final int installFlags = getInstallFlags(mAppInfo.packageName);
if ("package".equals(mPackageURI.getScheme())) {
try {
//安裝與該應用同名的應用,應該比較快,否則會拋出異常
pm.installExistingPackage(mAppInfo.packageName);
onPackageInstalled(PackageInstaller.STATUS_SUCCESS);
} catch (PackageManager.NameNotFoundException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE_INVALID);
}
} else {
final PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
PackageInstaller.SessionParams.MODE_FULL_INSTALL);
params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
params.originatingUri = getIntent().getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID, UID_UNKNOWN);
File file = new File(mPackageURI.getPath());
try {
//解析安裝包,設置安裝位置。這個安裝位置是從AndroidManifest文件獲取的,至於怎么獲取,最后指向native 層。沒有繼續跟蹤
params.setInstallLocation(PackageParser.parsePackageLite(file, 0).installLocation);
} catch (PackageParser.PackageParserException e) {
Log.e(TAG, "Cannot parse package " + file + ". Assuming defaults.");
}
mInstallHandler.post(new Runnable() {
@Override
public void run() {
doPackageStage(pm, params);
}
});
}
2.3 后台安裝
private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
//初始化安裝器
final PackageInstaller packageInstaller = pm.getPackageInstaller();
PackageInstaller.Session session = null;
try {
final String packageLocation = mPackageURI.getPath();
final File file = new File(packageLocation);
//獲取sessionId
final int sessionId = packageInstaller.createSession(params);
final byte[] buffer = new byte[65536];
//獲取session
session = packageInstaller.openSession(sessionId);
final InputStream in = new FileInputStream(file);
final long sizeBytes = file.length();
final OutputStream out = session.openWrite("PackageInstaller", 0, sizeBytes);
try {
int c;
//安裝中..............
while ((c = in.read(buffer)) != -1) {
out.write(buffer, 0, c);
if (sizeBytes > 0) {
final float fraction = ((float) c / (float) sizeBytes);
session.addProgress(fraction);
}
}
session.fsync(out);
} finally {
IoUtils.closeQuietly(in);
IoUtils.closeQuietly(out);
}
// Create a PendingIntent and use it to generate the IntentSender
//發起安裝完成提交通知
Intent broadcastIntent = new Intent(BROADCAST_ACTION);
PendingIntent pendingIntent = PendingIntent.getBroadcast(
InstallAppProgress.this /*context*/,
sessionId,
broadcastIntent,
PendingIntent.FLAG_UPDATE_CURRENT);
session.commit(pendingIntent.getIntentSender());
} catch (IOException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE);
} finally {
IoUtils.closeQuietly(session);
}
}
2.4 接受安裝結果
private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final int statusCode = intent.getIntExtra(
PackageInstaller.EXTRA_STATUS, PackageInstaller.STATUS_FAILURE);
//等待安裝
if (statusCode == PackageInstaller.STATUS_PENDING_USER_ACTION) {
context.startActivity((Intent)intent.getParcelableExtra(Intent.EXTRA_INTENT));
} else {
//返回安裝結果
onPackageInstalled(statusCode);
}
}
};
