简述下APK的安装过程,在文章的正式开始之前,我们需要做一些知识的补充
1:如何调用起安装界面,以从sd卡安装为例子
Intent intent = new Intent(Intent.ACTION_INSTALL_PACKAGE);
intent.setData(Uri.parse("file:/sdcard/qq.apk"));
startActivity(intent);
2:在setData中,Uri的构成
Uri的格式是 “file://< absolute path>”,例如 : xl://goods:8888/goodsDetail?goodsId=10011002
xl代表该Scheme 协议名称
goods代表Scheme作用于哪个地址域
goodsDetail代表Scheme指定的页面
goodsId代表传递的参数
8888代表该路径的端口号
有了Uri我们就可以在其他应用程序中访问这个资源
正式开始介绍Apk的安装
当我们在SD卡点击一个apk的时候,文件系统会为我们发起一次类似于隐式Intent的动作,此时将会把framework中的安装apk的应用程序唤起,界面如下
该界面的代码位置在:packages.apps.PackageInstaller目录下,其入口类为PackageInstallerActivity.java
@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);
boolean wasSetUp = processPackageUri(packageUri);
if (!wasSetUp) {
return;
}
checkIfAllowedAndInitiateInstall(false);
}
第6行,获得PackageManager对象,该类主要用于在当前设备安装的软件包检索与应用程序相关的各种信息的类,检索来源主要因考Apk的Manifest.xml文件;
第7行,通过getPackageInstaller方法,获得PackageInstaller对象;
该类的主要作用是提供安装,升级和删除应用程序的能力,包括支持作为单一打包的应用程序或打包成多个“分裂”APK的应用程序。
疑问一:
PackageManager类为抽象类,他是怎么通过getPackageInstaller方法获得PackageInstaller对象?
获得PackageManager对象的方法一般是在Activity中调用getPackageManager方法,或者通过Context#getPackageManager,此时这个Context有可能来自Application,Activity或者Service
我们先看下这几个组件的继承关系
Context.java作为抽象类主要提供有关应用程序环境的全局信息的接口。其实现由 Android系统提供
,它允许访问特定于应用程序的资源和类,以及向上调用应用程序级别的操作,例如启动活动,广播和接收意图等;
由此可见通过Context#getPackageManager获取PackageManager是由ContextWrapper类实现的
Context mBase;
@Override
public PackageManager getPackageManager() {
return mBase.getPackageManager();
}
疑问二:
这个context实现者是谁?
回答这个问题,我们要回到ActivityThread.java类中,该类的作用是在应用程序进程中管理主线程,管理activity,广播等,其作为应用首个执行的类,我们看下main函数做了哪些工作
public static void main(String[] args) {
//...省略代码...
ActivityThread thread = new ActivityThread();
thread.attach(false);
if (sMainThreadHandler == null) {
sMainThreadHandler = thread.getHandler();
}
if (false) {
Looper.myLooper().setMessageLogging(new
LogPrinter(Log.DEBUG, "ActivityThread"));
}
// End of event ActivityThreadMain.
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
Looper.loop();
throw new RuntimeException("Main thread loop unexpectedly exited");
}
第6行绑定应用程序,创建上下文,Application等,
第9行创建主线程的handler
在主线程中运行一个消息队列
抽象类Context的实现就是在thread.attach(false)方法中实现的
private void attach(boolean system) {
// 省略代码
try {
mInstrumentation = new Instrumentation();
ContextImpl context = ContextImpl.createAppContext(
this, getSystemContext().mPackageInfo);
mInitialApplication = context.mPackageInfo.makeApplication(true, null);
mInitialApplication.onCreate();
} catch (Exception e) {
throw new RuntimeException(
"Unable to instantiate Application():" + e.toString(), e);
}
// 省略代码
}
第5行,我们可以看到context的实现为ContextImpl .java
第6行,创建Application对象
第7行,完成Application对象的生命周期之一的onCreate
我们在回到PackageInstallerActivity.java的onCreate方法当中,继续分析Apk安装的过程
第15行~28行,一般不会执行
第29行~32行,从intent当中获取Uri类型的packageUri,mOriginatingURI,mReferrerURI,其中packageUri的值即我们通过intent.setData(Uri.parse(“file:/sdcard/qq.apk”));
第49行~55行,完成UI布局
第57行完成Apk的解析,我们详细看下代码
/**
* Parse the Uri and set up the installer for this package.
* 解析Uri并设置这个包的安装程序
*
* @param packageUri The URI to parse
*
* @return {@code true} iff the installer could be set up
*/
private boolean processPackageUri(final Uri packageUri) {
mPackageURI = packageUri;
//根据intent.setData中的值,获取Scheme
final String scheme = packageUri.getScheme();
//在我们这次举例中,Scheme为file,即会运行case SCHEME_FILE分支
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 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;
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;
}
}
//initSnippetForNewApp主要是对布局文件进行图片和文字的显示
PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
return true;
}
第36行,通过Url获取文件路劲,创建File对象
第37行,将会调用PackageParser.java的parsePackage方法,根据生成的目标文件路径,获取包的相关信息;PackageParser.java类的主要作用是解析在硬盘上的apk,该类支持解析单一的apk,也支持被分割成多快的apk
//PackageParser.java
public static PackageLite parsePackageLite(File packageFile, int flags)
throws PackageParserException {
if (packageFile.isDirectory()) {
return parseClusterPackageLite(packageFile, flags);
} else {
return parseMonolithicPackageLite(packageFile, flags);
}
}
该方法第一个参数为待解析apk的文件,第二个参数为0;因为packageFile里面只有一个apk文件,顾直接跳转到第7行parseMonolithicPackageLite方法
//Monolithic 单片
private static PackageLite parseMonolithicPackageLite(File packageFile, int flags)
throws PackageParserException {
final ApkLite baseApk = parseApkLite(packageFile, flags);
final String packagePath = packageFile.getAbsolutePath();
return new PackageLite(packagePath, baseApk, null, null, null);
}
第4行调用parseApkLite方法得到ApkLite的对象,ApkLite是一个轻量级的描述apk的对象,其中有apk的包名,版本号,证书,签名,安装位置等信息;
我们来看下parseApkLite方法,这个方法里面我做了详细的解释
public static ApkLite parseApkLite(File apkFile, int flags)
throws PackageParserException {
final String apkPath = apkFile.getAbsolutePath();
AssetManager assets = null;
XmlResourceParser parser = null;
try {
assets = new AssetManager();
assets.setConfiguration(0, 0, null, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
Build.VERSION.RESOURCES_SDK_INT);
int cookie = assets.addAssetPath(apkPath);
if (cookie == 0) {
throw new PackageParserException(INSTALL_PARSE_FAILED_NOT_APK,
"Failed to parse " + apkPath);
}
final DisplayMetrics metrics = new DisplayMetrics();
metrics.setToDefaults();
final Resources res = new Resources(assets, metrics, null);
//通过assets返回XmlResourceParser对象,该对象主要用于解析AndroidManifest.xml文件
parser = assets.openXmlResourceParser(cookie, ANDROID_MANIFEST_FILENAME);
//Signature主要用于存储签名
final Signature[] signatures;
//Certificate主要用于存储证书
final Certificate[][] certificates;
if ((flags & PARSE_COLLECT_CERTIFICATES) != 0) {
// TODO: factor signature related items out of Package object
//构建一个空的Package对象,传给collectCertificates方法,
//collectCertificates方法完成之后,会对tempPkg对象成员进行赋值
//Package代表一个完整的apk信息,也可以表示被分割成多快的apk信息
final Package tempPkg = new Package(null);
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "collectCertificates");
try {
//collectCertificates方法首先确认apk是否使用v2签名,如果是V2签名,则构建StrictJarFile对象
//改对象是解析jar的一个基类,通过ZipEntry的方式完成对apk的解析,apk本身其实就是一个zip文件,所以可以
//使用ZipEntry类;完成解析之后,对tempPkg成员进行赋值,如证书,签名
collectCertificates(tempPkg, apkFile, 0 /*parseFlags*/);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
signatures = tempPkg.mSignatures;
certificates = tempPkg.mCertificates;
} else {
signatures = null;
certificates = null;
}
final AttributeSet attrs = parser;
//调用parseApkLite方法返回ApkLite对象,ApkLite对象表示关于一个apk轻量级的描述
//如包名,安装位置等
return parseApkLite(apkPath, res, parser, attrs, flags, signatures, certificates);
} catch (XmlPullParserException | IOException | RuntimeException e) {
throw new PackageParserException(INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION,
"Failed to parse " + apkPath, e);
} finally {
IoUtils.closeQuietly(parser);
IoUtils.closeQuietly(assets);
}
}
parseMonolithicPackageLite方法完成ApkLite构建之后,第6行完成PackageLite类的创建,PackageLite主要描述的对于包的一个轻量即描述;这个与ApkLite还是有区别的,ApkLite描述的是apk,里面有包名,安装位置,证书,签名等信息,而PackageLite主要有包名,apk的位置等;这里不做具体区别,可将PackageLite和ApkLite理解为对apk的简单描述,里面包含了一些apk的简单信息
我们再回头看下PackageInstallerActivity.java中的onCreate方法
如果解析apk成功,则执行第62行的checkIfAllowedAndInitiateInstall方法,该方法主要用来对apk是否允许安装的检测,如果允许安装则进入安装流程,如果不允许安装则弹出类似不允许安装未知来源的apk这样的对话框
private void checkIfAllowedAndInitiateInstall(boolean ignoreUnknownSourcesSettings) {
// Block the install attempt on the Unknown Sources setting if necessary.
// 根据设置模块是否阻止未知来源设置来决定是否安装
// 如果允许安装,则调用initiateInstall方法,该方法会调用startInstallConfirm来完成其他界面的初始化工作
// 如显示权限列表页面等等
// 如果不允许安装则弹出类似不允许安装未知来源的apk这样的对话框
final boolean requestFromUnknownSource = isInstallRequestFromUnknownSource(getIntent());
if (!requestFromUnknownSource) {
initiateInstall();
return;
}
// 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) {
if (ignoreUnknownSourcesSettings) {
initiateInstall();
} else {
showDialogInner(DLG_UNKNOWN_SOURCES);
}
} else {
startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
clearCachedApkIfNeededAndFinish();
}
} else if (!isUnknownSourcesEnabled() && isManagedProfile) {
showDialogInner(DLG_ADMIN_RESTRICTS_UNKNOWN_SOURCES);
} else if (!isUnknownSourcesEnabled()) {
if (ignoreUnknownSourcesSettings) {
initiateInstall();
} else {
// Ask user to enable setting first
showDialogInner(DLG_UNKNOWN_SOURCES);
}
} else {
initiateInstall();
}
}
可以看出checkIfAllowedAndInitiateInstall方法最终会调用initiateInstall方法,再调用startInstallConfirm方法完成剩余UI内容的更新;
至此我们已经详细了解了安装apk的第一步工作,简单来说步骤如下
1: 对目标apk进行解析,生成这个apk的一些轻量级信息
2: 对apk是否允许安装做检测,如果允许安装,则更新PackageInstallerActivity.java其他界面
最后,我们看下安装apk的关键步骤,点击安装,代码依旧在PackageInstallerActivity.java的onClick方法中
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();
}
}
其最终调用startInstall方法跳转到安装进度Activity
private void startInstall() {
// Start subactivity to actually install the application
/*构建空的intent
Intent newIntent = new Intent();
//传入参数applicationInfo信息
newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
mPkgInfo.applicationInfo);
//传入mPackageURI,即通过intent.setDate传入的值
newIntent.setData(mPackageURI);
//跳转到安装进度activity,InstallAppProgress.java
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();
}
接下来我们分析安装apk的第二步,进入安装进度Activity即InstallAppProgress.java,其刚刚进入的界面如下
上图分别描述了apk的icon和label,以及一个被设置为模糊的进度条
先看下其onCreate方法
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
Intent intent = getIntent();
//获得传入的ApplicationInfo
mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
//获得传入的apk的url
mPackageURI = intent.getData();
//获得apk的url的scheme类型
final String scheme = mPackageURI.getScheme();
if (scheme != null && !"file".equals(scheme) && !"package".equals(scheme)) {
throw new IllegalArgumentException("unexpected scheme " + scheme);
}
//构建mInstallHandler完成消息收发
mInstallThread = new HandlerThread("InstallThread");
mInstallThread.start();
mInstallHandler = new Handler(mInstallThread.getLooper());
//注册广播接收器,监听com.android.packageinstaller.ACTION_INSTALL_COMMIT广播
//该广播接收器表示只有具有安装权限的即android.permission.INSTALL_PACKAGES的广播发送者才能发送
//才能触发这个广播接收器
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(BROADCAST_ACTION);
registerReceiver(
mBroadcastReceiver, intentFilter, BROADCAST_SENDER_PERMISSION, null /*scheduler*/);
//显示UI
initView();
}
在这个方法里面主要创建了用于处理消息的handler,和广播接收器,其作用会在后面的分析中得到解释,我们看下initView方法
void initView() {
/*布局activity,也就是显示安装进度条的界面
setContentView(R.layout.op_progress);
//AppSnippet可以连接为一个包含描述App的label和icon属性的类
final PackageUtil.AppSnippet as;
//PackageManager 管理应用程序包,例如 通过它,我们就可以获取应用程序信息。
final PackageManager pm = getPackageManager();
//getInstallFlags方法会根据传入的包名通过getPackageManager的方法获得PackageManager对象
//在通过PackageManager对象获得PackageInfo,如果PackageInfo不为空,则返回PackageManager.INSTALL_REPLACE_EXISTING
//表示替换已经存在的apk,否则返回0
//安装flag的值暗示着在调用安装的时候方法会不一致
final int installFlags = getInstallFlags(mAppInfo.packageName);
if((installFlags & PackageManager.INSTALL_REPLACE_EXISTING )!= 0) {
Log.w(TAG, "Replacing package:" + mAppInfo.packageName);
}
//根据示例代码,这里的mPackageURI的Scheme的值不为”package“而是”file“
if ("package".equals(mPackageURI.getScheme())) {
as = new PackageUtil.AppSnippet(pm.getApplicationLabel(mAppInfo),
pm.getApplicationIcon(mAppInfo));
} else {
//根据mPackageURI的地址创建目标文件对象sourceFile
final File sourceFile = new File(mPackageURI.getPath());
//getAppSnippet获得apk的label和icon,并将其赋值给AppSnippet的成员
as = PackageUtil.getAppSnippet(this, mAppInfo, sourceFile);
}
mLabel = as.label;
//initSnippetForNewApp方法主要是更新该界面中的imageview和textview的内容为上面获得的icon和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包含mDoneButton和mLaunchButton按钮,这两个起初是看不见的,直到安装完成
mOkPanel = findViewById(R.id.buttons_panel);
mDoneButton = (Button)findViewById(R.id.done_button);
mLaunchButton = (Button)findViewById(R.id.launch_button);
mOkPanel.setVisibility(View.INVISIBLE);
//此处的scheme为"file"
if ("package".equals(mPackageURI.getScheme())) {
try {
pm.installExistingPackage(mAppInfo.packageName);
onPackageInstalled(PackageInstaller.STATUS_SUCCESS);
} catch (PackageManager.NameNotFoundException e) {
onPackageInstalled(PackageInstaller.STATUS_FAILURE_INVALID);
}
} else {
//PackageInstaller:提供在设备上安装,更新,卸载一个app的能力
//Apk的安装是交由Session来进行的,而且任何一个应用都可以创建这样的Session
//SessionParams是用来创建Session的参数,这个参数保存了apk的一些信息,诸如进度,图标,sessionId,安装表示
//在传入的SessionParams中的参数,表示Session将如何与存在的app进行交互
//MODE_FULL_INSTALL表示完全取代现有的app,进行完整的安装
//SessionParams是用来创建Session的参数,这个参数保存了apk的一些信息,诸如进度,图标,sessionId,安装表示
//在传入的SessionParams中的参数,表示Session将如何与存在的app进行交互
//MODE_FULL_INSTALL表示完全取代现有的app,进行完整的安装
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 {
//如之前分析的,调用parsePackageLite方法将会根据文件是否是目录来做其他处理
//这列我们的测试代码为单一文件,顾parsePackageLite方法调用
//parseMonolithicPackageLite方法返回PackageLite
//PackageLite 表示精简版APK,里面包含一些包名,版本号等信息
PackageLite pkg = PackageParser.parsePackageLite(file, 0);
//设置Sesion参数params的包名
params.setAppPackageName(pkg.packageName);
//设置Sesion参数params的安装位置
params.setInstallLocation(pkg.installLocation);
//设置Sesion参数文件大小
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);
}
});
}
}
这个方法我写了比较详细的注释,还是比较好理解的,我们看下第25行的getAppSnippet方法,该方法正如注释那样主要是为了获得apk的icon和lab
public static AppSnippet getAppSnippet(
Activity pContext, ApplicationInfo appInfo, File sourceFile) {
final String archiveFilePath = sourceFile.getAbsolutePath();
Resources pRes = pContext.getResources();
AssetManager assmgr = new ANQPRequestManager();
assmgr.addAssetPath(archiveFilePath);
Resources res = new Resources(assmgr, pRes.getDisplayMetrics(), pRes.getConfiguration());
CharSequence label = null;
// Try to load the label from the package's resources. If an app has not explicitly
// specified any label, just use the package name.
if (appInfo.labelRes != 0) {
try {
label = res.getText(appInfo.labelRes);
} catch (Resources.NotFoundException e) {
}
}
if (label == null) {
label = (appInfo.nonLocalizedLabel != null) ?
appInfo.nonLocalizedLabel : appInfo.packageName;
}
Drawable icon = null;
// Try to load the icon from the package's resources. If an app has not explicitly
// specified any resource, just use the default icon for now.
if (appInfo.icon != 0) {
try {
icon = res.getDrawable(appInfo.icon);
} catch (Resources.NotFoundException e) {
}
}
if (icon == null) {
icon = pContext.getPackageManager().getDefaultActivityIcon();
}
return new PackageUtil.AppSnippet(label, icon);
}
第17行中,如果label为空,即manifest.xml没有指定android:label属性,则app的lab默认是应用程序的包名
第30行中,如果icon为空,即manifest.xml没有指定android:icon属性,则icon默认为系统内部图标,那这个图标到底是什么?
我们可以看下第31行,其调用getDefaultActivityIcon方法获得默认icon,在之前的分析中我们知道Context的实现者为ContextImpl,那调用getPackageManager方法根据代码即可查到返回的是ApplicationPackageManager对象,ApplicationPackageManager继承自PackageManager,所以现在我们就知道了Context.getPackageManager实际上的实现者是ApplicationPackageManager;那么追踪getDefaultActivityIcon方法便可知道默认图标是什么了
@Override public Drawable getDefaultActivityIcon() {
return Resources.getSystem().getDrawable(
com.android.internal.R.drawable.sym_def_app_icon);
}
即如下图标
我们返回到InstallAppProgress.java的initView方法中
第42行,我们获得scheme为“file”,因此跳转到第49行处;
第49行~91行,我们构建了用于创建Session的SessionParams对象params,有了Session,我们就可以安装Apk了,然后调用
doPackageStage(pm, params);
完成安装动作;
private void doPackageStage(PackageManager pm, PackageInstaller.SessionParams params) {
final PackageInstaller packageInstaller = pm.getPackageInstaller();
//Session是一个实现Closeable接口的类
//Closeable 是可以关闭的数据源或目标。调用 close 方法可释放对象保存的资源(如打开文件)。
//Closeable API: void close() throws IOException
//关闭此流并释放与此流关联的所有系统资源。如果已经关闭该流,则调用此方法无效。
//其作用表示正在进行一个安装,任何一个安装的apk都需要具备唯一的包名,版本号和签名文件
PackageInstaller.Session session = null;
try {
//安装位置
final String packageLocation = mPackageURI.getPath();
//根据安装位置创建文件
final File file = new File(packageLocation);
//createSession方法根据传入的session参数,即params创建一个session,并返回一个唯一的ID用于表示这个Session
//这个Session一个被创建,这个Session可以在多次重启的设备中多次打开,简单的就是说设备重启,未安装完成的apk的进度会被缓存起来
//在其安装的时候不会重新安装
final int sessionId = packageInstaller.createSession(params);
final byte[] buffer = new byte[65536];
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行中,通过pm.getPackageInstaller()获得PackageInstaller对象packageInstaller,PackageInstaller.java提供了安装,更新,卸载设备上App的能力;在PackageInstaller.java内部维护了一个属性
private final IPackageInstaller mInstaller;
正是这个mInstaller去帮助PackageInstaller.java实现app的安装,更新,卸载等;那这个属性是在何时赋值的?我们看下getPackageInstaller方法
@Override
public PackageInstaller getPackageInstaller() {
synchronized (mLock) {
if (mInstaller == null) {
try {
mInstaller = new PackageInstaller(mContext, this, mPM.getPackageInstaller(),
mContext.getPackageName(), mContext.getUserId());
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
return mInstaller;
}
}
在第6行中,构建PackageInstaller对象的第二个参数mPM.getPackageInstaller()中,正是这个参数完成了IPackageInstaller mInstaller对象的赋值;我们具体看下是如何赋值的.mPM对象为
private final IPackageManager mPM;
IPackageManager实际是一个aidl文件,因此我们可以这么理清思路了,在ApplicationPackageManager.java中维护了一个aidl的对象mPM,通过aidl的方式调用getPackageInstaller方法,返回一个代表aidl的对象IPackageInstaller,并将此对象赋值给PackageInstaller.java维护的mInstaller,这样PackageInstaller.java就可以通过mInstaller去实现app的安装,更新等操作了。
接下来,我们来看下ApplicationPackageManager是如何和PMS进行交互的?我们看下ApplicationPackageManager中的构造函数
ApplicationPackageManager(ContextImpl context,
IPackageManager pm) {
//IPackageManager mPM;
mContext = context;
mPM = pm;
}
ApplicationPackageManager的实现在ActivityThread.java中,具体方法为handleBindApplication方法中,里面有这样的一段代码
try {
ii = new ApplicationPackageManager(null, getPackageManager())
.getInstrumentationInfo(data.instrumentationName, 0);
} catch (PackageManager.NameNotFoundException e) {
throw new RuntimeException(
"Unable to find instrumentation info for: " + data.instrumentationName);
}
通过调用getPackageManager方法,完成mPM的赋值;
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
//获得packagemanagerservice的BinderProxy对象
IBinder b = ServiceManager.getService("package");
//将BinderProxy对象,转换成实际的业务代理对象sPackageManager
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}
在android系统内部,app层和framework层很多类都是通过这种binder的方式进行交互,如packagemanager,其实现者为applicationmanager,当客户端通过context的方式去获取packagemanager对象的时候,其执行者其实是applicationmanager,在applicationmanager中维护这mPM这样一个aidl成员,这个成员通过binder的方式和package manager service进行交互,将客户端的任务有效的传达给PKMS;PKMS继承自IPackageManager.Stub,其实上也是基于Binder的服务端;
这里我们简单看下android如何使用aidl进行进程交互,做个引子
1:首先像定义java类一样,新建一个aidl文件,后缀为aidl
2:经过编译之后会在gen目录生成一个aidl的java文件,查看java文件可知系统为我们创建一个抽象类Stub,其继承自android.os.Binder类并实现了aidl文件中的接口
3:在Service中定义aidl文件的stub对象,并实现aidl的方法
public class AidlService extends Service {
private final forService.Stub mBinder = new forService.Stub() {
@Override
public void invokCallBack() throws RemoteException {
}
};
}
4:在activiy中绑定服务,并在onServiceConnected方法中获取aidl类的对象
forService mService;
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
private ServiceConnection mConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className,IBinder service) {
mService = forService.Stub.asInterface(service);
mService.invokCallBack();
}
};
onServiceConnected()会在连接Service的时候被系统调用,她的值就来自于AidlService.java中的onBind方法返回的对象,而这个对象即forService.Stub
总结
在android的客户端与服务端的通信过程中,多使用binder的机制进行,service实现创建aidl文件的stub对象,并在其onbind方法中返回
客户端在bindserivce的时候,会回调onServiceConnected方法,onServiceConnected中的第二个参数及service返回的onbind参数
通过aidl的stub类的asInterface返回变可以返回aidl文件的产生的类对象
这样客户端使用返回的这个对象,就可以调用server中的方法;
我们跳出上面的引子,回到apk的安装上面,即getPackageManager方法
第7行,获得IBind对象,这里获得IBind的形式就像客户端通过onServiceConnected方法并传入第二个参数
第9行,通过asInterface的方式获得aidl文件产生的类对象,使用该对象和PackageManagerService中的方法进行交互
再回到InstallAppProgress.java的doPackageStage方法中,我们继续分析apk的后续流程
第17行,调用createSession方法,创建这个session的id,其内部实现为
//private final IPackageInstaller mInstaller;
public int createSession(@NonNull SessionParams params) throws IOException {
try {
return mInstaller.createSession(params, mInstallerPackageName, mUserId);
} catch (RuntimeException e) {
ExceptionUtils.maybeUnwrapIOException(e);
throw e;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
由之前可以,mInstaller也是一个aidl的文件,其内部实现为PackageInstallerService.java,我们查看其createSession内部实现
@Override
public int createSession(SessionParams params, String installerPackageName, int userId) {
try {
return createSessionInternal(params, installerPackageName, userId);
} catch (IOException e) {
throw ExceptionUtils.wrap(e);
}
}
查看具体的createSessionInternal方法可知,createSessionInternal内部创建了PackageInstallerSession.java的实例;并将其放入到
private final SparseArray<PackageInstallerSession> mSessions = new SparseArray<>();
进行保存;
第20行,调用openSession方法返回Session,其内部逻辑就是根据sessionid从上面的SparseArray中寻找到对应的Session;然后通过new Session的方式传入即可获得PackageInstaller.java内部的session;
因此,在PackageInstaller.java的内部,通过Session的构造函数,使得PackageInstaller.java有了PackageInstallerSession的能力,即PackageInstaller可以通过PackageInstallerSession来安装apk了
第24行,在PackageInstaller.java内部维护了一个静态的私有类Session,其实现了Closeable接口;通过session.openWrite方法返回输出流,指定输出位置;这里的session并非指向PackageInstallerSession,而是PackageInstaller.java内部维护的静态私有类Session;openWrite方法的作用为:打开一个流将apk文件写入到这个seeesion中,写入的session是指PackageInstallerSession;
第34行,调用fsync方法是为了确保输出流能够完整的写入到硬盘中
这里我们简单说下doPackageStage方法整体,因为有两处session,有可能会让人产生误解
1: 首先获得PackageInstaller对象,这个内部实现有binder进程交互的过程,分析见上面
2: 通过PackageInstaller类的createSession,通过binder的方式在PackageInstallerService类中完成对其成员PackageInstallerSession的赋值,并返回sessionid给createSession方法
3:通过PackageInstaller类的openSession,完成对PackageInstaller类中私有静态内部类Session的赋值,并通过其Session的构造函数,使得Session具有PackageInstallerSession的能力,即PackageInstaller通过其内部的Session来使用PackageInstallerSession的功能;
4:通过Session的openWrite,该方法将调用PackageInstallerSession的openWrite方法完成输入流的构建;即此刻,PackageInstallerSession已经拥有了管理目标文件的能力;
5: 通过Session的commit,调用PackageInstallerSession的commit方法,完成此session中的存储内容的提交,最终的提交结果将通过commit方法中的IntentSender进行回调;
我们看下PackageInstallerSession的commit方法
@Override
public void commit(IntentSender statusReceiver) {
/*省略代码*/
final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
//发送COMMIT的消息给handler进行处理
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
其处理消息的方法为
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
/*省略代码*/
//PackageInfo pkgInfo
//ApplicationInfo appInfo
commitLocked(pkgInfo, appInfo);
}
}
};
查看commitLocked方法
private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
throws PackageManagerException {
/*省略代码*/
//验证该阶段是否与现有应用程序相关。
//目前只能确保packageName,versionCode和证书一致性。
validateInstallLocked(pkgInfo, appInfo);
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);
}
//从现有安装的未被覆盖的包或者库中开始安装,此处params.mode等于MODE_FULL_INSTALL,这段代码先忽略
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);
}
}
//解压安装目录的lib目录下的库文件
extractNativeLibraries(mResolvedStageDir, params.abiOverride);
/*省略代码*/
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
installerPackageName, installerUid, user, mCertificates);
}
关键代码为调用PackageManagerService.java的installStage方法,该方法主要是发送INIT_COPY消息,交由PackageHandler进行处理,我们看下是如何处理INIT_COPY消息的
case INIT_COPY: {
//在INIT_COPY消息中,通过msg.obj传递来的是InstallParams类,其父类为HandlerParams
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
if (!mBound) {
//没有绑定DefaultContainerService服务,先进行绑定,绑定上之后,通过DefaultContainerConnection发送MCS_BOUND消息
//connectToService主要是连接上DefaultContainerService这个IntentService
//该类主要是用来提供检查和复制文件的服务在那些有可能贮存的设备上,绑定这个服务是用来后续复制apk文件用的
//绑定成功之后会发送MCS_BOUND消息
if (!connectToService()) {
Slog.e(TAG, "Failed to bind to media container service");
params.serviceError();
/*省略代码*/
return;
} else {
// Once we bind to the service, the first
// pending request will be processed.
mPendingInstalls.add(idx, params);
}
} else {
mPendingInstalls.add(idx, params);
// Already bound to the service. Just make
// sure we trigger off processing the first request.
if (idx == 0) {
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
}
通过以上注释可见,INIT_COPY消息主要是绑定DefaultContainerService服务,绑定上之后,通过DefaultContainerConnection发送MCS_BOUND消息
我们在看下MCS_BOUND消息如何处理
case MCS_BOUND: {
if (DEBUG_INSTALL) Slog.i(TAG, "mcs_bound");
if (msg.obj != null) {
//连接远端媒体控制服务,用来复制包的URI从外存媒体到外存或者内存,其实现为DefaultContainerService
mContainerService = (IMediaContainerService) msg.obj;
}
if (mContainerService == null) {
/*省略代码 服务绑定出错的处理,一般不会为null*/
}
//mPendingInstalls.size()指 等待安装的队列
else if (mPendingInstalls.size() > 0) {
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
//开始进入复制阶段
if (params.startCopy()) {
// We are done... look for more work or to
// go idle.
// Delete pending install
if (mPendingInstalls.size() > 0) {
mPendingInstalls.remove(0);
}
//当安装队列为0的时候,移除MCS_UNBIND消息,并解绑服务
if (mPendingInstalls.size() == 0) {
if (mBound) {
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting delayed MCS_UNBIND");
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
// Unbind after a little delay, to avoid
// continual thrashing.
sendMessageDelayed(ubmsg, 10000);
}
} else {
// There are more pending requests in queue.
// Just post MCS_BOUND message to trigger processing
// of next pending install.
if (DEBUG_SD_INSTALL) Log.i(TAG,
"Posting MCS_BOUND for next work");
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
}
由此可见,MCS_BOUND消息主要是通过binder的方式获得DefaultContainerService,然后调用HandlerParams类的startCopy方法开始进行文件的复制。
final boolean startCopy() {
boolean res;
try {
//最大尝试次数为4,超过尝试此时,安装失败
if (++mRetries > MAX_RETRIES) {
Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
//开始进行拷贝 该抽象方法主要实现在该类的12916行,类为InstallParams
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
//该抽象方法主要实现在该类的13173行
handleReturnCode();
return res;
}
我们可以看见,startCopy方法主要做了两个步骤
- 调用handleStartCopy完成复制
复制完成之后调用handleReturnCode告知处理结果
通过代码和注释,我们发现handleStartCopy和handleReturnCode均为抽象方法,其具体实现都是
InstallParams.java类中,我们一一看一下
/*
* 调用远程方法来获取包信息和安装位置值。
* 根据默认值覆盖安装位置策略,如果需要,然后创建新的安装位置
*/
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
// If we're already staged, we've firmly committed to an install location
//如果已经有安装,则在原始安装位置进行安装
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
} else if (origin.cid != null) {
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
//安装在外部
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
//安装在内部
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
//也是一个安装标志,表示这个app是一个轻量级短暂的app,具体用途暂不明
final boolean ephemeral = (installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
//PackageInfoLite描述的是一个manifest里面的信息,如安装位置,app版本号等等信息,实现了Parcelable接口
PackageInfoLite pkgLite = null;
//安装位置校验
if (onInt && onSd) {
// Check if both bits are set.安装冲突,一个apk不能即在内部安装又在外存安装
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (onSd && ephemeral) {
Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
//mContainerService的实现为DefaultContainerService,getMinimalPackageInfo方法主要是解析这个包,并返回
//少量的信息,如包名,版本号等等,解析也是采用PackageParser.PackageLite类
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
if (DEBUG_EPHEMERAL && ephemeral) {
Slog.v(TAG, "pkgLite for install: " + pkgLite);
}
/*
* 在安装的时候,如果剩余空间不足,先尝试释放缓存空间
*/
if (!origin.staged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
// TODO: focus freeing disk space on the target device
final StorageManager storage = StorageManager.from(mContext);
final long lowThreshold = storage.getStorageLowBytes(
Environment.getDataDirectory());
final long sizeBytes = mContainerService.calculateInstalledSize(
origin.resolvedPath, isForwardLocked(), packageAbiOverride);
try {
mInstaller.freeCache(null, sizeBytes + lowThreshold, 0);
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath,
installFlags, packageAbiOverride);
} catch (InstallerException e) {
Slog.w(TAG, "Failed to free cache", e);
}
/*
* The cache free must have deleted the file we
* downloaded to install.
*
* TODO: fix the "freeCache" call to not delete
* the file we care about.
*/
if (pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
pkgLite.recommendedInstallLocation
= PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE;
}
}
}
//检测可以进行安装
if (ret == PackageManager.INSTALL_SUCCEEDED) {
int loc = pkgLite.recommendedInstallLocation;
if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_LOCATION) {
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (loc == PackageHelper.RECOMMEND_FAILED_ALREADY_EXISTS) {
ret = PackageManager.INSTALL_FAILED_ALREADY_EXISTS;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
ret = PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_APK) {
ret = PackageManager.INSTALL_FAILED_INVALID_APK;
} else if (loc == PackageHelper.RECOMMEND_FAILED_INVALID_URI) {
ret = PackageManager.INSTALL_FAILED_INVALID_URI;
} else if (loc == PackageHelper.RECOMMEND_MEDIA_UNAVAILABLE) {
ret = PackageManager.INSTALL_FAILED_MEDIA_UNAVAILABLE;
} else {
// Override with defaults if needed.
loc = installLocationPolicy(pkgLite);
if (loc == PackageHelper.RECOMMEND_FAILED_VERSION_DOWNGRADE) {
ret = PackageManager.INSTALL_FAILED_VERSION_DOWNGRADE;
} else if (!onSd && !onInt) {
// Override install location with flags
if (loc == PackageHelper.RECOMMEND_INSTALL_EXTERNAL) {
// Set the flag to install on external media.
installFlags |= PackageManager.INSTALL_EXTERNAL;
installFlags &= ~PackageManager.INSTALL_INTERNAL;
} else if (loc == PackageHelper.RECOMMEND_INSTALL_EPHEMERAL) {
if (DEBUG_EPHEMERAL) {
Slog.v(TAG, "...setting INSTALL_EPHEMERAL install flag");
}
installFlags |= PackageManager.INSTALL_EPHEMERAL;
installFlags &= ~(PackageManager.INSTALL_EXTERNAL
|PackageManager.INSTALL_INTERNAL);
} else {
// Make sure the flag for installing on external
// media is unset
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
}
}
}
}
//根据安装属性,创建不同的InstallArgs,InstallArgs为抽象类
//其实现有 MoveInstallArgs,AsecInstallArgs ,FileInstallArgs
//MoveInstallArgs主要作用是createInstallArgs
//AsecInstallArgs主要作用是安装在外部存储空间上
//FileInstallArgs主要作用是FileInstallArgs
final InstallArgs args = createInstallArgs(this);
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
// TODO: http://b/22976637
// Apps installed for "all" users use the device owner to verify the app
UserHandle verifierUser = getUser();
if (verifierUser == UserHandle.ALL) {
verifierUser = UserHandle.SYSTEM;
}
/*
* Determine if we have any installed package verifiers. If we
* do, then we'll defer to them to verify the packages.
*/
final int requiredUid = mRequiredVerifierPackage == null ? -1
: getPackageUid(mRequiredVerifierPackage, MATCH_DEBUG_TRIAGED_MISSING,
verifierUser.getIdentifier());
//判断这个apk是否有包验证条件,
//那什么样的包开启包验证条件,即更多的是需要PACKAGE_VERIFICATION_AGENT权限的系统级app
//可以参考http://www.91r.net/ask/37352348.html
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(verifierUser.getIdentifier(), installFlags)) {
final Intent verification = new Intent(
Intent.ACTION_PACKAGE_NEEDS_VERIFICATION);
verification.addFlags(Intent.FLAG_RECEIVER_FOREGROUND);
verification.setDataAndType(Uri.fromFile(new File(origin.resolvedPath)),
PACKAGE_MIME_TYPE);
verification.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// Query all live verifiers based on current user state
final List<ResolveInfo> receivers = queryIntentReceiversInternal(verification,
PACKAGE_MIME_TYPE, 0, verifierUser.getIdentifier());
if (DEBUG_VERIFY) {
Slog.d(TAG, "Found " + receivers.size() + " verifiers for intent "
+ verification.toString() + " with " + pkgLite.verifiers.length
+ " optional verifiers");
}
final int verificationId = mPendingVerificationToken++;
verification.putExtra(PackageManager.EXTRA_VERIFICATION_ID, verificationId);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_PACKAGE,
installerPackageName);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALL_FLAGS,
installFlags);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_PACKAGE_NAME,
pkgLite.packageName);
verification.putExtra(PackageManager.EXTRA_VERIFICATION_VERSION_CODE,
pkgLite.versionCode);
if (verificationInfo != null) {
if (verificationInfo.originatingUri != null) {
verification.putExtra(Intent.EXTRA_ORIGINATING_URI,
verificationInfo.originatingUri);
}
if (verificationInfo.referrer != null) {
verification.putExtra(Intent.EXTRA_REFERRER,
verificationInfo.referrer);
}
if (verificationInfo.originatingUid >= 0) {
verification.putExtra(Intent.EXTRA_ORIGINATING_UID,
verificationInfo.originatingUid);
}
if (verificationInfo.installerUid >= 0) {
verification.putExtra(PackageManager.EXTRA_VERIFICATION_INSTALLER_UID,
verificationInfo.installerUid);
}
}
final PackageVerificationState verificationState = new PackageVerificationState(
requiredUid, args);
mPendingVerification.append(verificationId, verificationState);
final List<ComponentName> sufficientVerifiers = matchVerifiers(pkgLite,
receivers, verificationState);
/*
* If any sufficient verifiers were listed in the package
* manifest, attempt to ask them.
*/
if (sufficientVerifiers != null) {
final int N = sufficientVerifiers.size();
if (N == 0) {
Slog.i(TAG, "Additional verifiers required, but none installed.");
ret = PackageManager.INSTALL_FAILED_VERIFICATION_FAILURE;
} else {
for (int i = 0; i < N; i++) {
final ComponentName verifierComponent = sufficientVerifiers.get(i);
final Intent sufficientIntent = new Intent(verification);
sufficientIntent.setComponent(verifierComponent);
mContext.sendBroadcastAsUser(sufficientIntent, verifierUser);
}
}
}
final ComponentName requiredVerifierComponent = matchComponentForVerifier(
mRequiredVerifierPackage, receivers);
if (ret == PackageManager.INSTALL_SUCCEEDED
&& mRequiredVerifierPackage != null) {
Trace.asyncTraceBegin(
TRACE_TAG_PACKAGE_MANAGER, "verification", verificationId);
/*
* Send the intent to the required verification agent,
* but only start the verification timeout after the
* target BroadcastReceivers have run.
*/
verification.setComponent(requiredVerifierComponent);
mContext.sendOrderedBroadcastAsUser(verification, verifierUser,
android.Manifest.permission.PACKAGE_VERIFICATION_AGENT,
new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
final Message msg = mHandler
.obtainMessage(CHECK_PENDING_VERIFICATION);
msg.arg1 = verificationId;
mHandler.sendMessageDelayed(msg, getVerificationTimeout());
}
}, null, 0, null, null);
/*
* We don't want the copy to proceed until verification
* succeeds, so null out this field.
*/
mArgs = null;
}
} else {
/*
* No package verification is enabled, so immediately start
* the remote call to initiate copy using temporary file.
*/
// 没有开启包验证,调用copyApk完成文件复制
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
通过注释我们可以了解到,handleStartCopy方法经过一系列的校验之后,如果包没有开启包验证选项,则调用args.copyApk的方法完成文件的复制;
这里的args为抽象类InstallArgs对象
其实现有 MoveInstallArgs,AsecInstallArgs ,FileInstallArgs
MoveInstallArgs主要作用是移动app
AsecInstallArgs主要作用是安装在外部存储空间上
FileInstallArgs主要作用是安装在内部存储空间上
这里我们就以在内部安装apk为例子,讲解下FileInstallArgs.java的copyApk方法
int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "copyApk");
try {
return doCopyApk(imcs, temp);
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
}
其直接调用doCopyApk方法,传入的temp为true
private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
if (origin.staged) {
if (DEBUG_INSTALL) Slog.d(TAG, origin.file + " already staged; skipping copy");
codeFile = origin.file;
resourceFile = origin.file;
return PackageManager.INSTALL_SUCCEEDED;
}
try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
//创建临时存储目录,return new File(stagingDir, "vmdl" + sessionId + ".tmp");
//类似于data/app/vmdl + sessionId + ".tmp"这样的目录
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
//复制文件的回调
final IParcelFileDescriptorFactory target = new IParcelFileDescriptorFactory.Stub() {
@Override
public ParcelFileDescriptor open(String name, int mode) throws RemoteException {
if (!FileUtils.isValidExtFilename(name)) {
throw new IllegalArgumentException("Invalid filename: " + name);
}
try {
final File file = new File(codeFile, name);
final FileDescriptor fd = Os.open(file.getAbsolutePath(),
O_RDWR | O_CREAT, 0644);
Os.chmod(file.getAbsolutePath(), 0644);
return new ParcelFileDescriptor(fd);
} catch (ErrnoException e) {
throw new RemoteException("Failed to open: " + e.getMessage());
}
}
};
int ret = PackageManager.INSTALL_SUCCEEDED;
//调用DefaultContainerService的copyPackage完成复制
//copyPackage的流程是
//1:根据origin.file.getAbsolutePath()生成目标文件对象
//2:使用PackageParser的parsePackageLite方法解析第一步生成的文件
//3:调用copyPackageInner方法完成文件复制,其内部逻辑是通过流的方式,将文件写入到一个叫base.apk的临时文件中
//因此我们可以看出data/app/vmdl + sessionId + ".tmp"这样的目录里面包含文件就有了base.apk这个文件
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
Slog.e(TAG, "Failed to copy package");
return ret;
}
//public static final String LIB_DIR_NAME = "lib";
//创建复制lib的文件夹
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
//拷贝apk对应的库文件到指定目录
//因此data/app/vmdl + sessionId + ".tmp"这样的目录里面不仅包含base.apk这个文件,也有了lib这样的文件夹
ret = NativeLibraryHelper.copyNativeBinariesWithOverride(handle, libraryRoot,
abiOverride);
} catch (IOException e) {
Slog.e(TAG, "Copying native libraries failed", e);
ret = PackageManager.INSTALL_FAILED_INTERNAL_ERROR;
} finally {
IoUtils.closeQuietly(handle);
}
//至此完成了apk拷贝到临时文件夹的动作
return ret;
}
根据之前的分析调用handleStartCopy完成复制之后,会调用handleReturnCode方法完成处理结果的通知,其实现为InstallParams类的handleReturnCode方法
@Override
void handleReturnCode() {
// If mArgs is null, then MCS couldn't be reached. When it
// reconnects, it will try again to install. At that point, this
// will succeed.
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
// Queue up an async operation since the package installation may take a little while.
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
// Result object to be returned
PackageInstalledInfo res = new PackageInstalledInfo();
res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
res.removedInfo = null;
//正常情况下currentStatus传入的值为PackageManager.INSTALL_SUCCEEDED,此处虽然完成了
//apk的复制之后,但是还没有通知PMS进行更新,这里的代码就有这样的功能
//此处的args我们依旧是以内部安装为例,即FileInstallArgs
//doPreInstall主要是在安装出现异常的时候,删除拷贝的文件,这里我们可以不用细看
//installPackageTracedLI将调用installPackageLI方法,installPackageLI主要步骤为
//1:生成PackageParser pp = new PackageParser();
//2:调用pp的parsePackage方法生成PackageParser.Package pkg
//3:生成对应的权限信息后,根据Package中的信息,更改存储路径对应目录的名称。
//4:针对dex做一些优化,这些优化主要内容为
/*Apk文件其实就是一个归档zip压缩包,而我们编写的代码最终都编译成了.dex文件,
但为了提高运行性能,android系统并不会直接执行.dex,而是会在安装过程中执行dexopt操作来优化.dex文件,
最终android系统执行的时优化后的’odex’文件(注意:这个odex文件的后缀也是.dex,其路径在data/dalvik-cache)。
对于dalvik虚拟机,dexopt就是优化操作,而对于art虚拟机,dexopt执行的则是dex2oat操作,既将.dex文件翻译成oat文件。*/
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageTracedLI(args, res);
}
//doPostInstall函数和doPreInstall函数完全一样,
//正常流程下不需要进行任何操作;当之前的处理流程出现问题时,利用cleanUp清除创建的文件和资源。
args.doPostInstall(res.returnCode, res.uid);
}
// A restore should be performed at this point if (a) the install
// succeeded, (b) the operation is not an update, and (c) the new
// package has not opted out of backup participation.
//a:当安装成功的时候,执行备份操作
//b:这个操作不是更新
//c:新包没有退出备份操作
//这块代码主要是apk的备份操作,可以稍微看下,主要流程是备份之后doRestore为false,发送POST_INSTALL消息,交给PackageHandler进行处理
final boolean update = res.removedInfo != null
&& res.removedInfo.removedPackage != null;
final int flags = (res.pkg == null) ? 0 : res.pkg.applicationInfo.flags;
boolean doRestore = !update
&& ((flags & ApplicationInfo.FLAG_ALLOW_BACKUP) != 0);
// Set up the post-install work request bookkeeping. This will be used
// and cleaned up by the post-install event handling regardless of whether
// there's a restore pass performed. Token values are >= 1.
int token;
if (mNextInstallToken < 0) mNextInstallToken = 1;
token = mNextInstallToken++;
PostInstallData data = new PostInstallData(args, res);
mRunningInstalls.put(token, data);
if (DEBUG_INSTALL) Log.v(TAG, "+ starting restore round-trip " + token);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
// Pass responsibility to the Backup Manager. It will perform a
// restore if appropriate, then pass responsibility back to the
// Package Manager to run the post-install observer callbacks
// and broadcasts.
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
if (bm != null) {
if (DEBUG_INSTALL) Log.v(TAG, "token " + token
+ " to BM for possible restore");
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "restore", token);
try {
// TODO: http://b/22388012
if (bm.isBackupServiceActive(UserHandle.USER_SYSTEM)) {
bm.restoreAtInstall(res.pkg.applicationInfo.packageName, token);
} else {
doRestore = false;
}
} catch (RemoteException e) {
// can't happen; the backup manager is local
} catch (Exception e) {
Slog.e(TAG, "Exception trying to enqueue restore", e);
doRestore = false;
}
} else {
Slog.e(TAG, "Backup Manager not found!");
doRestore = false;
}
}
if (!doRestore) {
// No restore possible, or the Backup Manager was mysteriously not
// available -- just fire the post-install work request directly.
if (DEBUG_INSTALL) Log.v(TAG, "No restore - queue post-install for " + token);
Trace.asyncTraceBegin(TRACE_TAG_PACKAGE_MANAGER, "postInstall", token);
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
}
});
}
我们看下PackageHandler是如何处理POST_INSTALL消息的
/*关键代码是调用了handlePackagePostInstall方法*/
case POST_INSTALL: {
if (DEBUG_INSTALL) Log.v(TAG, "Handling post-install for " + msg.arg1);
PostInstallData data = mRunningInstalls.get(msg.arg1);
final boolean didRestore = (msg.arg2 != 0);
mRunningInstalls.delete(msg.arg1);
if (data != null) {
InstallArgs args = data.args;
PackageInstalledInfo parentRes = data.res;
final boolean grantPermissions = (args.installFlags
& PackageManager.INSTALL_GRANT_RUNTIME_PERMISSIONS) != 0;
final boolean killApp = (args.installFlags
& PackageManager.INSTALL_DONT_KILL_APP) == 0;
final String[] grantedPermissions = args.installGrantPermissions;
// Handle the parent package
handlePackagePostInstall(parentRes, grantPermissions, killApp,
grantedPermissions, didRestore, args.installerPackageName,
args.observer);
// Handle the child packages
/*如果这个包是被分割成多个的,则重复调用handlePackagePostInstall方法*/
final int childCount = (parentRes.addedChildPackages != null)
? parentRes.addedChildPackages.size() : 0;
for (int i = 0; i < childCount; i++) {
PackageInstalledInfo childRes = parentRes.addedChildPackages.valueAt(i);
handlePackagePostInstall(childRes, grantPermissions, killApp,
grantedPermissions, false, args.installerPackageName,
args.observer);
}
// Log tracing if needed
if (args.traceMethod != null) {
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, args.traceMethod,
args.traceCookie);
}
} else {
Slog.e(TAG, "Bogus post-install token " + msg.arg1);
}
Trace.asyncTraceEnd(TRACE_TAG_PACKAGE_MANAGER, "postInstall", msg.arg1);
}
break;
我们再看下handlePackagePostInstall方法
private void handlePackagePostInstall(PackageInstalledInfo res, boolean grantPermissions,
boolean killApp, String[] grantedPermissions,
boolean launchedForRestore, String installerPackage,
IPackageInstallObserver2 installObserver) {
//一般情况下个,res.returnCode == PackageManager.INSTALL_SUCCEEDED
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
// Send the removed broadcasts
if (res.removedInfo != null) {
res.removedInfo.sendPackageRemovedBroadcasts(killApp);
}
// Now that we successfully installed the package, grant runtime
// permissions if requested before broadcasting the install.handlePackagePostInstall
//如果apk的targetSdkVersion大于等于6.0,则开启动态权限检查,赋予apk相应的权限
if (grantPermissions && res.pkg.applicationInfo.targetSdkVersion
>= Build.VERSION_CODES.M) {
grantRequestedRuntimePermissions(res.pkg, res.newUsers, grantedPermissions);
}
final boolean update = res.removedInfo != null
&& res.removedInfo.removedPackage != null;
// If this is the first time we have child packages for a disabled privileged
// app that had no children, we grant requested runtime permissions to the new
// children if the parent on the system image had them already granted.
if (res.pkg.parentPackage != null) {
synchronized (mPackages) {
grantRuntimePermissionsGrantedToDisabledPrivSysPackageParentLPw(res.pkg);
}
}
synchronized (mPackages) {
mEphemeralApplicationRegistry.onPackageInstalledLPw(res.pkg);
}
final String packageName = res.pkg.applicationInfo.packageName;
Bundle extras = new Bundle(1);
extras.putInt(Intent.EXTRA_UID, res.uid);
// Determine the set of users who are adding this package for
// the first time vs. those who are seeing an update.
int[] firstUsers = EMPTY_INT_ARRAY;
int[] updateUsers = EMPTY_INT_ARRAY;
if (res.origUsers == null || res.origUsers.length == 0) {
firstUsers = res.newUsers;
} else {
for (int newUser : res.newUsers) {
boolean isNew = true;
for (int origUser : res.origUsers) {
if (origUser == newUser) {
isNew = false;
break;
}
}
if (isNew) {
firstUsers = ArrayUtils.appendInt(firstUsers, newUser);
} else {
updateUsers = ArrayUtils.appendInt(updateUsers, newUser);
}
}
}
// Send installed broadcasts if the install/update is not ephemeral
//当安装或者更新一个apk之后,会发送Intent.ACTION_PACKAGE_ADDED广播
//这里有一个逻辑,会判断该app是不是ephemeral,这里的ephemeral是指这样的一个场景,
//消费者在尝试一些不确定是否会安装在本地的应用时候会先在网络上进行试用,觉得可行然后再下载安装到本地
//参考链接http://www.lupaworld.com/article-233621-1.html
if (!isEphemeral(res.pkg)) {
mProcessLoggingHandler.invalidateProcessLoggingBaseApkHash(res.pkg.baseCodePath);
// Send added for users that see the package for the first time
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/, null /*targetPackage*/,
null /*finishedReceiver*/, firstUsers);
// Send added for users that don't see the package for the first time
//如果是更新则广播中添加EXTRA_REPLACING参数
if (update) {
extras.putBoolean(Intent.EXTRA_REPLACING, true);
}
sendPackageBroadcast(Intent.ACTION_PACKAGE_ADDED, packageName,
extras, 0 /*flags*/, null /*targetPackage*/,
null /*finishedReceiver*/, updateUsers);
// Send replaced for users that don't see the package for the first time
if (update) {
sendPackageBroadcast(Intent.ACTION_PACKAGE_REPLACED,
packageName, extras, 0 /*flags*/,
null /*targetPackage*/, null /*finishedReceiver*/,
updateUsers);
sendPackageBroadcast(Intent.ACTION_MY_PACKAGE_REPLACED,
null /*package*/, null /*extras*/, 0 /*flags*/,
packageName /*targetPackage*/,
null /*finishedReceiver*/, updateUsers);
} else if (launchedForRestore && !isSystemApp(res.pkg)) {
// First-install and we did a restore, so we're responsible for the
// first-launch broadcast.
if (DEBUG_BACKUP) {
Slog.i(TAG, "Post-restore of " + packageName
+ " sending FIRST_LAUNCH in " + Arrays.toString(firstUsers));
}
sendFirstLaunchBroadcast(packageName, installerPackage, firstUsers);
}
// Send broadcast package appeared if forward locked/external for all users
// treat asec-hosted packages like removable media on upgrade
if (res.pkg.isForwardLocked() || isExternal(res.pkg)) {
if (DEBUG_INSTALL) {
Slog.i(TAG, "upgrading pkg " + res.pkg
+ " is ASEC-hosted -> AVAILABLE");
}
final int[] uidArray = new int[]{res.pkg.applicationInfo.uid};
ArrayList<String> pkgList = new ArrayList<>(1);
pkgList.add(packageName);
sendResourcesChangedBroadcast(true, true, pkgList, uidArray, null);
}
}
// Work that needs to happen on first install within each user
if (firstUsers != null && firstUsers.length > 0) {
synchronized (mPackages) {
for (int userId : firstUsers) {
// If this app is a browser and it's newly-installed for some
// users, clear any default-browser state in those users. The
// app's nature doesn't depend on the user, so we can just check
// its browser nature in any user and generalize.
if (packageIsBrowser(packageName, userId)) {
mSettings.setDefaultBrowserPackageNameLPw(null, userId);
}
// We may also need to apply pending (restored) runtime
// permission grants within these users.
mSettings.applyPendingPermissionGrantsLPw(packageName, userId);
}
}
}
// Log current value of "unknown sources" setting
EventLog.writeEvent(EventLogTags.UNKNOWN_SOURCES_ENABLED,
getUnknownSourcesSettings());
// Force a gc to clear up things
Runtime.getRuntime().gc();
// Remove the replaced package's older resources safely now
// We delete after a gc for applications on sdcard.
if (res.removedInfo != null && res.removedInfo.args != null) {
synchronized (mInstallLock) {
res.removedInfo.args.doPostDeleteLI(true);
}
}
if (!isEphemeral(res.pkg)) {
// Notify DexManager that the package was installed for new users.
// The updated users should already be indexed and the package code paths
// should not change.
// Don't notify the manager for ephemeral apps as they are not expected to
// survive long enough to benefit of background optimizations.
for (int userId : firstUsers) {
PackageInfo info = getPackageInfo(packageName, /*flags*/ 0, userId);
mDexManager.notifyPackageInstalled(info, userId);
}
}
}
// If someone is watching installs - notify them
if (installObserver != null) {
try {
Bundle extras = extrasForInstallResult(res);
installObserver.onPackageInstalled(res.name, res.returnCode,
res.returnMsg, extras);
} catch (RemoteException e) {
Slog.i(TAG, "Observer no longer exists.");
}
}
}
这样就完成了整个APK的安装过程分析了