一、安装方式
1、和用户交互式安装,这个方式可以通过点击一个apk,还有通过代码的方式发起安装,这个方式会弹出安装界面,权限选择以及确定,这个方式也是用户最常用的方式,图形界面安装
2、通过adb install -r xxx 这个命令行的方式发起安装
3、系统核心服务PackageManagerService启动系统的时候,会扫描系统内的app,如果没有安装过 就发起安装
这三种方式最后都会走到PMS来发起安装,今天我们就用最常见的第一种方式来讲解
二、流程分析
代码环境:android7.1.2,其他的版本大同小异
1、发起安装请求的Intent
public void startInstall(Context context, String path) {
Intent install = new Intent(Intent.ACTION_VIEW);
install.setDataAndType(Uri.parse("file://" + path), "application/vnd.android.package-archive");
install.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(install);
}
一般来将我们通过这个可以发起对一个apk的安装,这个就是发送一个inetnt,
我们来看这个Intent包括一个action 还有Data和对应的Type
action:Intent.ACTION_VIEW
data:Uri.parse(“file://” + path)
type:“application/vnd.android.package-archive”
这个Intent是在PackageInstaller 这个系统内置的app中接收的,我们来看下他的Manifest文件
2、PackageInstaller# AndroidManifest.xml
我们从这可以看到第一个filter了,里面有action.VIEW 下面还有对应的mimeType,说明这个activity就是接收那个Intent的,接下来我们看下这个Activity:PackageInstallerActivity
3、PackageInstaller#PackageInstallerActivity
1、onCreate
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
mPm = getPackageManager();
mInstaller = mPm.getPackageInstaller();
mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
final Intent intent = getIntent();
mOriginatingUid = getOriginatingUid(intent);
final Uri packageUri;
//这个是需要权限确认的时候要走的
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;
}
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;
}
//set view
setContentView(R.layout.install_start);
mInstallConfirm = findViewById(R.id.install_confirm_panel);
mInstallConfirm.setVisibility(View.INVISIBLE);
mOk = (Button)findViewById(R.id.ok_button);
mCancel = (Button)findViewById(R.id.cancel_button);
mOk.setOnClickListener(this);
mCancel.setOnClickListener(this);
//这个是从packageUri 中解析出要按照这个apk的packageinfo 等相关性信息
//是根据scheme来分来的
boolean wasSetUp = processPackageUri(packageUri);
if (!wasSetUp) {
return;
}
//这里里面主要是做了安装app前的各项检查,确定这个app是否可以安装
//下面列举了一些错误可能,要弹出的警告框
//private static final int DLG_BASE = 0;
//private static final int DLG_UNKNOWN_SOURCES = DLG_BASE + 1;
//private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
//private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
//private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
//private static final int DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES = DLG_BASE + 6;
//private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7;
checkIfAllowedAndInitiateInstall(false);
//上面这个检查完了之后会调用initiateInstall() 方法
}
//根据不同的scheme 解析uri
//分成三种,file,content package
//我们常见的就是file 通过uri传过来 进行安装
//content是穿过来个Conprovider的地址 然后通过一个AsyncTask进行读取,保存为file 然后走入file流程
//package 这个是就是安装哪些已经卸载了的包 重新安装
private boolean processPackageUri(final Uri packageUri) {
mPackageURI = packageUri;
final String scheme = packageUri.getScheme();
final PackageUtil.AppSnippet as;
switch (scheme) {
case SCHEME_PACKAGE: {
try {
//packageUri.getSchemeSpecificPart() 获取uri的SchemeSpecificPart 部分,实际是报名
//这个方法实际获取已经卸载了的包的信息
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 false;
}
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 false;
}
mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
PackageManager.GET_PERMISSIONS, 0, 0, null,
new PackageUserState());
as = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
} break;
//传入ContentPorvider,然后读取保存到一个package.apk的数据,然后走file的流程 安装这个apk
case SCHEME_CONTENT: {
mStagingAsynTask = new StagingAsyncTask();
mStagingAsynTask.execute(packageUri);
return false;
}
default: {
Log.w(TAG, "Unsupported scheme " + scheme);
setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
clearCachedApkIfNeededAndFinish();
return false;
}
}
//这里就是生成一个要安装的app的图标还有名字 做一个展示
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
return true;
}
4、PackageInstallerActivity#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.
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".
mAppInfo = mPm.getApplicationInfo(pkgName,
PackageManager.GET_UNINSTALLED_PACKAGES);
if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
mAppInfo = null;
}
} catch (NameNotFoundException e) {
mAppInfo = null;
}
//这个方法最后会启动安装的确认View
startInstallConfirm();
}
5、PackageInstallerActivity#startInstallConfirm
private void startInstallConfirm() {
((TextView) findViewById(R.id.install_confirm_question))
.setText(R.string.install_confirm_question);
findViewById(R.id.spacer).setVisibility(View.GONE);
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.
boolean supportsRuntimePermissions = mPkgInfo.applicationInfo.targetSdkVersion
>= Build.VERSION_CODES.M;
boolean permVisible = false;
mScrollView = null;
mOkCanInstall = false;
int msg = 0;
//这个是权限展示的View封装,里面会通过mPkgInfo 获取需要展示的权限
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;
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;
}
});
}
}
上面这个方法就是构建一个确认安装的界面,里面有权限列表,还有确认和取消,这个相信大家应该熟悉这个界面
最终我们需要点击确认按钮 继续走
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();
}
}
6、PackageInstallerActivity#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);
//真正的安装apk页面
newIntent.setClass(this, InstallAppProgress.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 != VerificationParams.NO_UID) {
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();
}
总结PackageInstallerActivity这个页面做的事情:
1、获取传入的uri数据,然后根据scheme类型解析不同的安装类型,就是数据来源不同,不同处理
2、解析这个apk,分析是否有错误,空间够不够,包是不是完整,然后给出提示警告框dialog
3、展示要安装的app信息包括名字 图标,权限,确认和取消键
然后继续往下走
上面这个就是 往Intent添加数据,然后跳转到一个新的页面,这个页面是真正的安装apk的页面
这个页面是InstallAppProgress
7、InstallAppProgress#onCreate
我们直接进入onCreate方法看下
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Intent intent = getIntent();
mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
mPackageURI = intent.getData();
final String scheme = mPackageURI.getScheme();
if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
throw new IllegalArgumentException("unexpected scheme " + scheme);
}
mInstallThread = new HandlerThread("InstallThread");
mInstallThread.start();
mInstallHandler = new Handler(mInstallThread.getLooper());
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION);
registerReceiver(
mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null /*scheduler*/);
initView();
}
上面就是做了初始化通过,实例化了一个HandlerThread,这个是带有Looper的线程,可以让线程队列中依次排队执行
继续看initView()
void initView() {
setContentView(R.layout.op_progress);
final PackageUtil.AppSnippet as;
final PackageManager pm = getPackageManager();
final int installFlags = getInstallFlags(mAppInfo.packageName);
if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {
Log.w(TAG, "Replacing package:" + mAppInfo.packageName);
}
if ("package".equals(mPackageURI.getScheme())) {
as = new PackageUtil.AppSnippet(pm.getApplicationLabel(mAppInfo),
pm.getApplicationIcon(mAppInfo));
} else {
final File sourceFile = new File(mPackageURI.getPath());
as = PackageUtil.getAppSnippet(this, mAppInfo, sourceFile);
}
mLabel = as.label;
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
mStatusTextView = (TextView)findViewById(R.id.center_text);
mExplanationTextView = (TextView) findViewById(R.id.explanation);
mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
mProgressBar.setIndeterminate(true);
// Hide button till progress is being displayed
mOkPanel = findViewById(R.id.buttons_panel);
mDoneButton = (Button)findViewById(R.id.done_button);
mLaunchButton = (Button)findViewById(R.id.launch_button);
mOkPanel.setVisibility(View.INVISIBLE);
//这个安装类型少见 我们先不考虑
if ("package".equals(mPackageURI.getScheme())) {
try {
pm.installExistingPackage(mAppInfo.packageName);
onPackageInstalled(PackageInstaller.STATUS_SUCCESS);
} catch (PackageManager.NameNotFoundException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE_INVALID);
}
} else {
//最终进入这里,下面就是构造一个要安装的SessionParams,设置各种参数
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 {
PackageLite pkg = PackageParser.parsePackageLite(file, 0);
params.setAppPackageName(pkg.packageName);
params.setInstallLocation(pkg.installLocation);
params.setSize(
PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
} catch (PackageParser.PackageParserException e) {
Log.e(TAG, "Cannot parse package " + file + ". Assuming defaults.");
Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
} catch (IOException e) {
Log.e(TAG, "Cannot calculate installed size " + file + ". Try only apk size.");
params.setSize(file.length());
}
mInstallHandler.post(new Runnable() {
@Override
public void run() {
//进入关键代码 这个是在线程中执行的了
doPackageStage(pm, params);
}
});
}
}
8、InstallAppProgress#doPackageStage
private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
//这个PackageInstaller 其实是这个PackageInstallerService的客户端的代理
//这个PackageInstaller 是在ApplicationPackageManager中创建的
//ApplicationPackageManager是PMS的在客户端的实际代理者
final PackageInstaller packageInstaller = pm.getPackageInstaller();
//这个Session 其实是远程服务端PackageInstallerSesion的客户端代理类
//之间通过Binder通信的
PackageInstaller.Session session = null;
try {
final String packageLocation = mPackageURI.getPath();
final File file = new File(packageLocation);
//调用远端的PackageInstallerService 创建一个sessionId,这个id是随机产生的
final int sessionId = packageInstaller.createSession(params);
final byte[] buffer = new byte[65536];
调用远端的PackageInstallerService 创建了一个PackageInstallerSession
//然后返回一个客户端的一个代理,这个代理是通过IPackageInstallerSession 通信的
//IPackageInstallerSession 其实是通过IPackageInstallerSession.aidl生成的java
//相关的具体aidl的东西 这里不多说了,自行相关学习
session = packageInstaller.openSession(sessionId);
final InputStream in = new FileInputStream(file);
final long sizeBytes = file.length();
//这里通过远端构造一个FileDescriptor 然后实例化个FileBridge,文件描述符,最终来对外抛出 一个OutputStream
//通过FileBridge 桥接多进程文件访问,
//这个的写入数据,远程端也对应有的相应的文件也在写入数据,远程端写入的数据文件文件名字是“PackageInstaller”,这个文件是记录在session中的
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);
}
}
//刷入
//这样就把这个apk文件 复制到了session中的某个文件变量中保存起来,
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);
//提交这个安装请求,aidl调用,最终会调用PackageInstallerSession的commit方法
session.commit(pendingIntent.getIntentSender());
} catch (IOException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE);
} finally {
IoUtils.closeQuietly(session);
}
}
上面我们提到了 final int sessionId = packageInstaller.createSession(params); 这个最终是在远程的PackageInstallService 实现的
我们来看下PackageInstallService 是如何创建这个Session的
@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
try {
return createSessionInternal(params, installerPackageName, userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
private int createSessionInternal(SessionParams params, String installerPackageName, int userId)
throws IOException {
//这个是获取调用者端的uid,这个uid就是PackageInstaller 这个安装器app了
final int callingUid = Binder.getCallingUid();
//通过PMS 执行权限申请,如果过程有权限不满足 就抛出异常了
mPm.enforceCrossUserPermission(callingUid, userId, true, true, "createSession");
//检查这个安装app的uid是否具有安装权限,这里说的就是PackageInstaller
if (mPm.isUserRestricted(userId, UserManager.DISALLOW_INSTALL_APPS)) {
throw new SecurityException("User restriction prevents installing");
}
//如果调用的uid是shell的uid或者Process.ROOT_UID 那么添加adb的flag,就是告诉后面这个是adb执行的,
if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) {
params.installFlags |= PackageManager.INSTALL_FROM_ADB;
} else {
//不然就是用户正常用户的安装
mAppOps.checkPackage(callingUid, installerPackageName);
//去掉INSTALL_FROM_ADB
params.installFlags &= ~PackageManager.INSTALL_FROM_ADB;
//去掉INSTALL_ALL_USERS
params.installFlags &= ~PackageManager.INSTALL_ALL_USERS;
params.installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
}
// Only system components can circumvent runtime permissions when installing.
if ((params.installFlags & PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0
&& mContext.checkCallingOrSelfPermission(Manifest.permission
.INSTALL_GRANT_RUNTIME_PERMISSIONS) == PackageManager.PERMISSION_DENIED) {
throw new SecurityException("You need the "
+ "android.permission.INSTALL_GRANT_RUNTIME_PERMISSIONS permission "
+ "to use the PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS flag");
}
// Defensively resize giant app icons
if (params.appIcon != null) {
final ActivityManager am = (ActivityManager) mContext.getSystemService(
Context.ACTIVITY_SERVICE);
final int iconSize = am.getLauncherLargeIconSize();
if ((params.appIcon.getWidth() > iconSize * 2)
|| (params.appIcon.getHeight() > iconSize * 2)) {
params.appIcon = Bitmap.createScaledBitmap(params.appIcon, iconSize, iconSize,
true);
}
}
switch (params.mode) {
//一般都是这个模式 全部替换安装
case SessionParams.MODE_FULL_INSTALL:
//这个是继承安装,不是全量的
case SessionParams.MODE_INHERIT_EXISTING:
break;
default:
throw new IllegalArgumentException("Invalid install mode: " + params.mode);
}
// If caller requested explicit location, sanity check it, otherwise
// resolve the best internal or adopted location.
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
//检测安装需要的空间够不够
if (!PackageHelper.fitsOnInternal(mContext, params.sizeBytes)) {
throw new IOException("No suitable internal storage available");
}
} else if ((params.installFlags & PackageManager.INSTALL_EXTERNAL) != 0) {
if (!PackageHelper.fitsOnExternal(mContext, params.sizeBytes)) {
throw new IOException("No suitable external storage available");
}
} else if ((params.installFlags & PackageManager.INSTALL_FORCE_VOLUME_UUID) != 0) {
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
params.setInstallFlagsInternal();
} else {
// For now, installs to adopted media are treated as internal from
// an install flag point-of-view.
params.setInstallFlagsInternal();
// Resolve best location for install, based on combination of
// requested install flags, delta size, and manifest settings.
final long ident = Binder.clearCallingIdentity();
try {
params.volumeUuid = PackageHelper.resolveInstallVolume(mContext,
params.appPackageName, params.installLocation, params.sizeBytes);
} finally {
Binder.restoreCallingIdentity(ident);
}
}
final int sessionId;
final PackageInstallerSession session;
synchronized (mSessions) {
// Sanity check that installer isn't going crazy
//这里就是检测要安装的这个app,是否已经在待安装列表里有了 而且数量很大,如果
//数量超过了MAX_ACTIVE_SESSIONS==1024 就抛出异常
final int activeCount = getSessionCount(mSessions, callingUid);
if (activeCount >= MAX_ACTIVE_SESSIONS) {
throw new IllegalStateException(
"Too many active sessions for UID " + callingUid);
}
//检测历史记录
final int historicalCount = getSessionCount(mHistoricalSessions, callingUid);
if (historicalCount >= MAX_HISTORICAL_SESSIONS) {
throw new IllegalStateException(
"Too many historical sessions for UID " + callingUid);
}
//创建一个sessionid,这个是通过ScureRandom创建的,然后存入本地SparseBooleanArray
sessionId = allocateSessionIdLocked();
}
final long createdMillis = System.currentTimeMillis();
// We're staging to exactly one location
File stageDir = null;
String stageCid = null;
if ((params.installFlags & PackageManager.INSTALL_INTERNAL) != 0) {
final boolean isEphemeral =
(params.installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
//创建要保存的apk file的目录,要按照的file 会被拷贝到这里来,然后保存session
//这个是内部的sdcard走的
stageDir = buildStageDir(params.volumeUuid, sessionId, isEphemeral);
} else {
//安装到外部sdcard,获取stageCid="smdl" + sessionId + ".tmp";
stageCid = buildExternalStageCid(sessionId);
}
//真正的创建了一个PackageInstallerSession
session = new PackageInstallerSession(mInternalCallback, mContext, mPm,
mInstallThread.getLooper(), sessionId, userId, installerPackageName, callingUid,
params, createdMillis, stageDir, stageCid, false, false);
synchronized (mSessions) {
//添加到内部的mSessions中
mSessions.put(sessionId, session);
}
mCallbacks.notifySessionCreated(session.sessionId, session.userId);
//写入到xml中,防止安装终止,还可以从xml里恢复,设计很周全
writeSessionsAsync();
return sessionId;
}
PackageInstallService的作用:
1、维护着安装apk的安装会话PackageInstallSession,比如有一个新的安装请求来了,PackageInstallService不着急安装,先放到里面的SparseArray中存起来,然后等待合适的时候在来安装,对apk的安装起到安装缓冲和调度作用
2、创建PackageInstallSession 创建sessionid 并管理
3、中间还会把安装会话保存到xml本地文件中,比如因为某个原因终端了,等下次启动这个服务了,还能从这个xml中读取会话,然后安装
4、每次安装操作都会去这里查询,然后安装
9、PackageInstallerSession#commit
前面说到了InstallAppProgress 这个页面是最终的用户可见的安装页面了,最后这个调用了session.commit(pendingIntent.getIntentSender());
最终是调用了PackageInstallerSession中的commit方法,过程是aidl,参数传入的是一个IntentSender,是PendingIntent的广播类型的Intent
/*
整个方法我们直接看下面的重点
*/
@Override
public void commit(IntentSender statusReceiver) {
Preconditions.checkNotNull(statusReceiver);
final boolean wasSealed;
synchronized (mLock) {
wasSealed = mSealed;
if (!mSealed) {
// Verify that all writers are hands-off
for (FileBridge bridge : mBridges) {
if (!bridge.isClosed()) {
throw new SecurityException("Files still open");
}
}
mSealed = true;
}
// Client staging is fully done at this point
mClientProgress = 1f;
computeProgressLocked(true);
}
if (!wasSealed) {
// Persist the fact that we've sealed ourselves to prevent
// mutations of any hard links we create. We do this without holding
// the session lock, since otherwise it's a lock inversion.
mCallback.onSessionSealedBlocking(this);
}
// This ongoing commit should keep session active, even though client
// will probably close their end.
mActiveCount.incrementAndGet();
//实例化一个观察者,这个观察者主要处理权限不被允许,和安装完成的动作监听
final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
//发送MSG_COMMIT到mHandler中处理
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
继续看mHandler中是如何处理的
mHandler = new Handler(looper, mHandlerCallback);
//继续看mHandlerCallback
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// Cache package manager data without the lock held
//mPm 这个是PMS,这个是构造方法中传入的
//获取PackageInfo
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES /*flags*/, userId);
//获取ApplicationInfo
final ApplicationInfo appInfo = mPm.getApplicationInfo(
params.appPackageName, 0, userId);
synchronized (mLock) {
if (msg.obj != null) {
//获取一个观察者,这个是一个binder IPackageInstallObserver2这个是一个aidl文件,mRemoteObserver实际上是IPackageInstallObserver2.Stub 是一个继承Binder的类
//我个人觉得这里不需要binder 也可以,因为这个Session是和ams是一个进程中的,这里可以直接引用mPm 这个是AMS的直接引用
mRemoteObserver = (IPackageInstallObserver2) msg.obj;
}
try {
//我们继续看这个
commitLocked(pkgInfo, appInfo);
} catch (PackageManagerException e) {
final String completeMsg = ExceptionUtils.getCompleteMessage(e);
Slog.e(TAG, "Commit of session " + sessionId + " failed: " + completeMsg);
//销毁内部的各种文件
destroyInternal();
// 发送回调信息 比如通知PackageInstallService 安装失败
dispatchSessionFinished(e.error, completeMsg, null);
}
return true;
}
}
};
commitLocked
private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
throws PackageManagerException {
if (mDestroyed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session destroyed");
}
if (!mSealed) {
throw new PackageManagerException(INSTALL_FAILED_INTERNAL_ERROR, "Session not sealed");
}
try {
//解析和保存apk copy后的目录
resolveStageDir();
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_CONTAINER_ERROR,
"Failed to resolve stage location", e);
}
// Verify that stage looks sane with respect to existing application.
// This currently only ensures packageName, versionCode, and certificate
// consistency.
//这个就是安装前的最后检查了,检查各项是否正确,比如版本号,签名,如果是覆盖安装app,前后的信息是否一致等
validateInstallLocked(pkgInfo, appInfo);
Preconditions.checkNotNull(mPackageName);
Preconditions.checkNotNull(mSignatures);
Preconditions.checkNotNull(mResolvedBaseFile);
//检查安装器是否有权限安装,没有权限安装就需要回调之前说的那个观察者的onUserActionRequired
//这个mPermissionsAccepted 这个信息是这个类的构造函数里就初始化的了,
if (!mPermissionsAccepted) {
// User needs to accept permissions; give installer an intent they
// can use to involve user.
final Intent intent = new Intent(PackageInstaller.ACTION_CONFIRM_PERMISSIONS);
intent.setPackage(mContext.getPackageManager().getPermissionControllerPackageName());
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.
close();
return;
}
if (stageCid != null) {
// Figure out the final installed size and resize the container once
// and for all. Internally the parser handles straddling between two
// locations when inheriting.
final long finalSize = calculateInstalledSize();
//最后在调整安装尺寸
resizeContainer(stageCid, finalSize);
}
// Inherit any packages and native libraries from existing install that
// haven't been overridden.
//从安装过的app中提取 可用的库文件
if (params.mode == SessionParams.MODE_INHERIT_EXISTING) {
try {
final List<File> fromFiles = mResolvedInheritedFiles;
final File toDir = resolveStageDir();
if (LOGD) Slog.d(TAG, "Inherited files: " + mResolvedInheritedFiles);
if (!mResolvedInheritedFiles.isEmpty() && mInheritedFilesBase == null) {
throw new IllegalStateException("mInheritedFilesBase == null");
}
if (isLinkPossible(fromFiles, toDir)) {
if (!mResolvedInstructionSets.isEmpty()) {
final File oatDir = new File(toDir, "oat");
createOatDirs(mResolvedInstructionSets, oatDir);
}
linkFiles(fromFiles, toDir, mInheritedFilesBase);
} else {
// TODO: this should delegate to DCS so the system process
// avoids holding open FDs into containers.
copyFiles(fromFiles, toDir);
}
} catch (IOException e) {
throw new PackageManagerException(INSTALL_FAILED_INSUFFICIENT_STORAGE,
"Failed to inherit existing install", e);
}
}
// TODO: surface more granular state from dexopt
mInternalProgress = 0.5f;
computeProgressLocked(true);
// Unpack native libraries
//提取native文件 就是那些so
extractNativeLibraries(mResolvedStageDir, params.abiOverride);
// Container is ready to go, let's seal it up!
if (stageCid != null) {
finalizeAndFixContainer(stageCid);
}
// We've reached point of no return; call into PMS to install the stage.
// Regardless of success or failure we always destroy session.
//构造一个本地观察者,这个到时候要传入到pms中,pms中 安装完成后,会回调这个
final IPackageInstallObserver2 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) {
destroyInternal();
dispatchSessionFinished(returnCode, msg, extras);
}
};
final UserHandle user;
//这个是设备上的用户,
if ((params.installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
//所有用户
user = UserHandle.ALL;
} else {
//特定用户
user = new UserHandle(userId);
}
mRelinquished = true;
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
installerPackageName, installerUid, user, mCertificates);
}
总结下这个方法做了什么事情
检查安装前的各项设置是否正常(版本号,包名,签名等),权限是否具备,提取其他已经安装了的apk的库(这个库是以来关系),实例化一个观察者来通知上层(安装完成),提取so文件,设置用用属性,最后调用PMS的
installStage来安装