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);
}
}
};