上篇文章Apk安装之——点击安装确认页面的确认按钮,到将安装过程交给PMS的过程分析分析到apk安装的流程,交给了PMS,这篇文章继续分析,PMS是如何安装apk包的。首先调用PMS的installStage进行安装。下面看看这个方法:
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
void installStage(String packageName, File stagedDir,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
PackageParser.SigningDetails signingDetails) {
...
final VerificationInfo verificationInfo = new VerificationInfo(
sessionParams.originatingUri, sessionParams.referrerUri,
sessionParams.originatingUid, installerUid);
final OriginInfo origin = OriginInfo.fromStagedFile(stagedDir);
//关键代码 msg.what是INIT_COPY的消息
final Message msg = mHandler.obtainMessage(INIT_COPY);
final int installReason = fixUpInstallReason(installerPackageName, installerUid,
sessionParams.installReason);
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
sessionParams.grantedRuntimePermissions, signingDetails, installReason);
params.setTraceMethod("installStage").setTraceCookie(System.identityHashCode(params));
//注意,这里的params是一个InstallParams类型的对象,赋值给了msg.obj
msg.obj = params;
...
//关键代码 发送一个msg.what是INIT_COPY的消息
mHandler.sendMessage(msg);
}
这个方法内部,会发送一个msg.what是INIT_COPY的消息,下面看PackageHandler对这个消息的处理
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
class PackageHandler extends Handler {
...
private boolean connectToService() {
if (DEBUG_INSTALL) Log.i(TAG, "Trying to bind to DefaultContainerService");
Intent service = new Intent().setComponent(DEFAULT_CONTAINER_COMPONENT);
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
if (mContext.bindServiceAsUser(service, mDefContainerConn,
Context.BIND_AUTO_CREATE, UserHandle.SYSTEM)) {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
mBound = true;
return true;
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return false;
}
...
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
HandlerParams params = (HandlerParams) msg.obj;
int idx = mPendingInstalls.size();
//mBound默认是false
if (!mBound) {
//关键代码1 发送连接DefaultContainerService的请求,这是system_server进程和com.android.defcontainer进程进行通信,
if (!connectToService()) {
params.serviceError();
...
//如果绑定DefaultContainerService失败,则return
return;
} else {
//关键代码2 如果连接上DefaultContainerService,则将这个安装请求保存到这个mPendingInstalls这个List集合中
mPendingInstalls.add(idx, params);
}
} else {
//如果已经绑定了DefaultContainerService,则将这个安装请求保存到这个mPendingInstalls这个List集合中
mPendingInstalls.add(idx, params);
//如果是第一个安装请求,则发送绑定MCS_BOUND消息,在未给mPendingInstalls添加安装请求前,上面mPendingInstalls.size()的值是0,
//当添加按章请求后,这是mPendingInstalls.size()的值是1,所以,这里idx==0,就是给第一个安装请求发送MCS_BOUND消息
if (idx == 0) {
//关键代码3
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
break;
...
}
}
final private DefaultContainerConnection mDefContainerConn =
new DefaultContainerConnection();
class DefaultContainerConnection implements ServiceConnection {
public void onServiceConnected(ComponentName name, IBinder service) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceConnected");
final IMediaContainerService imcs = IMediaContainerService.Stub
.asInterface(Binder.allowBlocking(service));
//关键代码 这里发送msg.what为MCS_BOUND的消息是,msg.obj的值是imcs,其实就是DefaultContainerService在system_server的代理对象
mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs));
}
public void onServiceDisconnected(ComponentName name) {
if (DEBUG_SD_INSTALL) Log.i(TAG, "onServiceDisconnected");
}
}
在INIT_COPY这个case中,如果本次安装时,没有其他安装请求,则mBound的值是false,这会执行到关键代码1处,调用connectToService()方法进行绑定DefaultContainerService的连接操作,这是个进程间通信的过程,是system_server进程和com.android.defcontainer进程进行通信。如果连接上DefaultContainerService,则将安装请求添加进mPendingInstalls这个集合中,如果连接失败,则return。如果本次安装时,已经有其他安装请求绑定DefaultContainerService服务了,则将这次安装请求添加进mPendingInstalls,并通过handler发送MCS_BOUND消息。注意发送MCS_BOUND消息时,msg.obj被赋值了,这个值是DefaultContainerService在system_server进程中的代理对象.下面看看handler对MCS_BOUND消息的处理:
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
class PackageHandler extends Handler {
void doHandleMessage(Message msg) {
switch (msg.what) {
...
case MCS_BOUND: {
if (msg.obj != null) { //关键代码1 如果msg.obj不为null,则给mContainerService赋值
mContainerService = (IMediaContainerService) msg.obj;
...
}
if (mContainerService == null) { //关键代码2 如果mContainerService为null
if (!mBound) { //关键代码3 如果是连接上DefaultContainerService,mBound的值会true,如果这里的mBound的值为false,则安装过程出错了,
for (HandlerParams params : mPendingInstalls) {
// Indicate service bind error
params.serviceError();
...
}
//安装过程出错,清除这个mPendingInstalls集合
mPendingInstalls.clear();
} else {
Slog.w(TAG, "Waiting to connect to media container service");
}
} else if (mPendingInstalls.size() > 0) { //关键代码5 需要安装的请求的个数大于0
HandlerParams params = mPendingInstalls.get(0);
if (params != null) {
...
if (params.startCopy()) { //关键代码6 开始复制
...
if (mPendingInstalls.size() > 0) { //复制成功,则将本次安装请求重mPendingInstalls集合中移除
mPendingInstalls.remove(0);
}
if (mPendingInstalls.size() == 0) { // 如果安装请求的个数为0
if (mBound) {
//如果需要安装的apk的个数为0,并且是绑定状态,则发送消息和ContainerService解绑
removeMessages(MCS_UNBIND);
Message ubmsg = obtainMessage(MCS_UNBIND);
sendMessageDelayed(ubmsg, 10000);
}
} else {
// 接着处理下一个绑定ContainerService的请求
mHandler.sendEmptyMessage(MCS_BOUND);
}
}
...
}
} else {
// Should never happen ideally.
Slog.w(TAG, "Empty queue");
}
break;
...
}
}
}
handler中处理MCS_BOUND消息的逻辑是,由于发送MCS_BOUND时msg.obj赋值过,导致关键代码1处成立,则执行关键代码5处的逻辑,这是就会执行关键代码6处的逻辑,在发送Init_copy消息时,msg.obj是InstallParams类型的对象,所以,这里mPendingInstalls中取出的就是InstallParams类型的对象,调用InstallParams类的startCopy方法进行复制工作。当复制成功后,会则将本次安装请求重mPendingInstalls集合中移除,如果需要安装的apk的个数为0,并且是绑定状态,则发送消息和ContainerService解绑,如果不为0,则继续处理下一个安装请求。这个方法中,还有另外的一个逻辑,这里也顺便阐述一下,如果关键代码1出的逻辑成立则执行关键代码2处的逻辑,关键代码2中逻辑是,如果当前状态是为绑定,则肯定是出错了,因为connectToServcie方法中如果连接成功,这个mBound这个值就复制为true了这里这个值如果是false,则if(!mBound)这个判断条件成立,这样安装过程肯定出错了,则将mPendingInstalls集合清除。正常情况下,这个mBound的值应该是true,所以不做处理。表示已经连接上了。下面接着分析,InstallParams类的startCopy方法是如何进行复制的:
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
final boolean startCopy() {
boolean res;
try {
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 {
// 关键代码1
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
//关键代码2
handleReturnCode();
return res;
}
InstallParams是继承自HandlerParams的startCopy()这个方法是HandlerParams的方法,这个方法内部,会判断安装重试的次数是否大于4次(MAX_RETRIES是个常量,值是4),如果大于4次,则发送放弃安装。如果不大于4次,则执行handleStartCopy()方法,下面看看这个方法的具体实现:
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
public void handleStartCopy() throws RemoteException {
int ret = PackageManager.INSTALL_SUCCEEDED;
// If we're already staged, we've firmly committed to an install location
//如果是一个新的app,则staged为false
if (origin.staged) {
if (origin.file != null) {
installFlags |= PackageManager.INSTALL_INTERNAL;
installFlags &= ~PackageManager.INSTALL_EXTERNAL;
} else {
throw new IllegalStateException("Invalid stage location");
}
}
// 确定APK的安装位置,onSd表示安装到SD卡上,
// onInt表示安装到内部存储,即Data分区
// ephemeral:安装到临时存储(Instant Apps安装)
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
final boolean ephemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
PackageInfoLite pkgLite = null;
if (onInt && onSd) {
// 不能同时安装到sd卡和内部存储位置
Slog.w(TAG, "Conflicting flags specified for installing on both internal and external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else if (onSd && ephemeral) {
//flags冲突,不能在sd卡安装Instant app
Slog.w(TAG, "Conflicting flags specified for installing ephemeral on external");
ret = PackageManager.INSTALL_FAILED_INVALID_INSTALL_LOCATION;
} else {
//获取apk的轻量信息
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags,
packageAbiOverride);
if (!origin.staged && pkgLite.recommendedInstallLocation
== PackageHelper.RECOMMEND_FAILED_INSUFFICIENT_STORAGE) {
...
//省略这个判断内部的代码,这块的主要逻辑是判断,如果存储空间不足,则需要释放Cache的一些空间
}
}
if (ret == PackageManager.INSTALL_SUCCEEDED) {
...
//省略这个判断内部的代码,这块的主要逻辑是判断安装的位置
}
//创建InstallArgs对象,createInstallArgs方法内部返回的是FileInstallArgs类型的对象。FileInstallArgs是InstallArgs的子类
final InstallArgs args = createInstallArgs(this);
mArgs = args;
if (ret == PackageManager.INSTALL_SUCCEEDED) {
...
if (!origin.existing && requiredUid != -1
&& isVerificationEnabled(
verifierUser.getIdentifier(), installFlags, installerUid)) {
...
// 此处省略大段APK的检查逻辑。
} else {
//调用FileInstallArgs类的copyApk方法
ret = args.copyApk(mContainerService, true);
}
}
mRet = ret;
}
这个方法内部,根据安装方式,判断安装的位置是否冲突,在判断,安装的位置是否有足够的存储空间,如果没有,释放缓存空间,接着确定安装位置,在接着对apk进行检测,最后创建FileInstallArgs类型的对象,并调用其调用copyApk方法,copyApk方法中直接调用了doCopyApk方法,下面看看这个方法:
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private int doCopyApk(IMediaContainerService imcs, boolean temp) throws RemoteException {
...
try {
final boolean isEphemeral = (installFlags & PackageManager.INSTALL_INSTANT_APP) != 0;
//创建临时文件
final File tempDir =
mInstallerService.allocateStageDirLegacy(volumeUuid, isEphemeral);
codeFile = tempDir;
resourceFile = tempDir;
} catch (IOException e) {
Slog.w(TAG, "Failed to create copy file: " + e);
return PackageManager.INSTALL_FAILED_INSUFFICIENT_STORAGE;
}
...
int ret = PackageManager.INSTALL_SUCCEEDED;
//调用DefaultContainerService在system_server进程中的代理对象的copyPackage方法,这是进程间通信的过程,
//跨进程调用DefaultContainerService进程中的copyPackage()函数
ret = imcs.copyPackage(origin.file.getAbsolutePath(), target);
if (ret != PackageManager.INSTALL_SUCCEEDED) {
return ret;
}
//复制Native库文件
final File libraryRoot = new File(codeFile, LIB_DIR_NAME);
NativeLibraryHelper.Handle handle = null;
try {
handle = NativeLibraryHelper.Handle.create(codeFile);
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);
}
return ret;
}
FileInstallArgs类的copyApk方法中,又会调用doCopyApk方法,在doCopyApk方法中,会创建一个临时路径,这里之所以创建临时目录,命名如/data/app/vmdl29300388.tmp,其中数字部分29300388在每一次安装过程都不同,是安装时的SessionId。由于PMS监控了/data/app目录,如果该目录下有后缀名为.apk的文件生成,便会触发PMS扫描,为了避免这种情况,安装APK时,先用了临时的文件名。 并跨进程调用DefaultContainerService的copyPackage方法完成apk的复制工作,然后复制Native库。完成了apk的复制工作后,apk就在data/app目录下,不过此时还是临时文件的,后续还需要重新命名。如果复制过程为出现异常,则返回INSTALL_SUCCEEDED(1)这个整数;否则,会返回对应的错误码。到这里,分析完了PMS H a n d l e r P a r a m s 类 的 的 s t a r t C o p y ( ) 中 的 h a n d l e S t a r t C o p y 方 法 的 处 理 逻 辑 , 下 面 继 续 看 P M S HandlerParams类的的startCopy()中的handleStartCopy方法的处理逻辑,下面继续看PMS HandlerParams类的的startCopy()中的handleStartCopy方法的处理逻辑,下面继续看PMSHandlerParams类的的startCopy()方法的后续对复制apk的结果的处理逻辑:
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
final boolean startCopy() {
boolean res;
try {
if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);
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 {
// 关键代码1
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
//关键代码2
handleReturnCode();
return res;
}
在关键代码2处,就是对复制apk后的返回结果的处理,下面看看这个handleReturnCode()方法的具体实现
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
@Override
void handleReturnCode() {
if (mArgs != null) {
processPendingInstall(mArgs, mRet);
}
}
private void processPendingInstall(final InstallArgs args, final int currentStatus) {
mHandler.post(new Runnable() {
public void run() {
mHandler.removeCallbacks(this);
PackageInstalledInfo res = new PackageInstalledInfo();
res.setReturnCode(currentStatus);
res.uid = -1;
res.pkg = null;
res.removedInfo = null;
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
//安装前的准备工作,检查包状态,确保环境ok,如果环境不ok,那么会清理拷贝的文件
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
//安装apk,调用installPackageTracedLI进行安装
installPackageTracedLI(args, res);
}
//安装完后的处理
args.doPostInstall(res.returnCode, res.uid);
}
...
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED && doRestore) {
IBackupManager bm = IBackupManager.Stub.asInterface(
ServiceManager.getService(Context.BACKUP_SERVICE));
...
//这块省略的逻辑,是对apk的备份过程
}
...
}
});
}
handleReturnCode方法内部,直接调用了processPendingInstall来处理具体的安装逻辑。processPendingInstall方法中,是将安装过程放在一个线程中的,这个方法内部,主要做了三件事,第一安装前的检查,检查安装环境是否正常,如果不正常,则清理复制的apk文件,如果安装环境正常,则进行进行安装工作,安装完后,处理一些收尾的事情。installPackageTracedLI方法内部直接调用了installPackageLI方法,下面具体看看这个方法的具体实现:
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) {
...
PackageParser pp = new PackageParser();
...
final PackageParser.Package pkg;
try {
//关键代码1
pkg = pp.parsePackage(tmpPackageFile, parseFlags);
DexMetadataHelper.validatePackageDexMetadata(pkg);
} catch (PackageParserException e) {
res.setError("Failed parse during installPackageLI", e);
return;
} finally {
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
...
try {
//收集签名信息
if (args.signingDetails != PackageParser.SigningDetails.UNKNOWN) {
pkg.setSigningDetails(args.signingDetails);
} else {
PackageParser.collectCertificates(pkg, false /* skipVerify */);
}
} catch (PackageParserException e) {
res.setError("Failed collect during installPackageLI", e);
return;
}
...
pp = null;
String oldCodePath = null;
boolean systemApp = false;
synchronized (mPackages) {
// 检查安装包是否存在
if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
String oldName = mSettings.getRenamedPackageLPr(pkgName);
if (pkg.mOriginalPackages != null
&& pkg.mOriginalPackages.contains(oldName)
&& mPackages.containsKey(oldName)) {
pkg.setPackageName(oldName);
pkgName = pkg.packageName;
//替换安装,也就是升级app
replace = true;
} else if (mPackages.containsKey(pkgName)) {
//替换安装,也就是升级app
replace = true;
if (DEBUG_INSTALL) Slog.d(TAG, "Replace existing pacakge: " + pkgName);
}
...
}
//如果Settings中已经记录了待安装的APK信息,需要验证APK的签名
PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
PackageSetting signatureCheckPs = ps;
...
//省略的代码是 验证apk的签名信息
}
int N = pkg.permissions.size();
for (int i = N-1; i >= 0; i--) {
final PackageParser.Permission perm = pkg.permissions.get(i);
final BasePermission bp =
(BasePermission) mPermissionManager.getPermissionTEMP(perm.info.name);
...
//省略的代码部分处理安装的apk中的权限的逻辑
}
}
if (systemApp) {
if (onExternal) {
//系统app不能被sd卡上的app替换,return
res.setError(INSTALL_FAILED_INVALID_INSTALL_LOCATION,
"Cannot install updates to system apps on sdcard");
return;
} else if (instantApp) {
//系统app不能被instant app替换
res.setError(INSTALL_FAILED_INSTANT_APP_INVALID,
"Cannot update a system app with an instant app");
return;
}
}
...
//将之前的临时文件名重新命名
if (!args.doRename(res.returnCode, pkg, oldCodePath)) {
res.setError(INSTALL_FAILED_INSUFFICIENT_STORAGE, "Failed rename");
return;
}
...
try (PackageFreezer freezer = freezePackageForInstall(pkgName, installFlags,
"installPackageLI")) {
if (replace) {
if (pkg.applicationInfo.isStaticSharedLibrary()) {
PackageParser.Package existingPkg = mPackages.get(pkg.packageName);
if (existingPkg != null &&
existingPkg.getLongVersionCode() != pkg.getLongVersionCode()) {
res.setError(INSTALL_FAILED_DUPLICATE_PACKAGE, "Packages declaring "
+ "static-shared libs cannot be updated");
return;
}
}
//替换安装,也就是升级app
replacePackageLIF(pkg, parseFlags, scanFlags, args.user,
installerPackageName, res, args.installReason);
} else {
//安装一个新的app
installNewPackageLIF(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES,
args.user, installerPackageName, volumeUuid, res, args.installReason);
}
}
mArtManagerService.prepareAppProfiles(pkg, resolveUserIds(args.user.getIdentifier()));
// 检查是否需要优化app中的dex文件,默认是不给instant app做dex优化的,只有满足下面4个条件,才做dex的优化
// 1) it is not forward locked.
// 2) it is not on on an external ASEC container.
// 3) it is not an instant app or if it is then dexopt is enabled via gservices.
// 4) it is not debuggable.
final boolean performDexopt = (res.returnCode == PackageManager.INSTALL_SUCCEEDED)
&& !forwardLocked
&& !pkg.applicationInfo.isExternalAsec()
&& (!instantApp || Global.getInt(mContext.getContentResolver(),
Global.INSTANT_APP_DEXOPT_ENABLED, 0) != 0)
&& ((pkg.applicationInfo.flags & ApplicationInfo.FLAG_DEBUGGABLE) == 0);
if (performDexopt) {
DexoptOptions dexoptOptions = new DexoptOptions(pkg.packageName,
REASON_INSTALL,
DexoptOptions.DEXOPT_BOOT_COMPLETE |
DexoptOptions.DEXOPT_INSTALL_WITH_DEX_METADATA_FILE);
mPackageDexOptimizer.performDexOpt(pkg, pkg.usesLibraryFiles,
null /* instructionSets */,
getOrCreateCompilerPackageStats(pkg),
mDexManager.getPackageUseInfoOrDefault(pkg.packageName),
dexoptOptions);
}
...
synchronized (mPackages) {
final PackageSetting ps = mSettings.mPackages.get(pkgName);
if (ps != null) {
//更新APK的所属用户
res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true);
ps.setUpdateAvailable(false /*updateAvailable*/);
}
...
}
}
这个方法内部,处理的逻辑还是很复杂的,下面列出主要的处理流程:
1.通过PackageParser的parsePackage方法对apk进行解析,具体的解析工作,很复杂,主要是解析apk文件中的manifest文件,并将manifest文件中的application标签,activity,service,broadcast,contentprovider,这四大组件,intentfileter等标签解析成对应的数据结构,比如activity标签就解析为Activity类,这里的这个Activity类不是我们平时开发时继承的Activity,这个Activity是继承Component类的,将这些解析的标签对应的数据保存到PackageParser$Package类的各个集合中。这里由于篇幅的原因,省略了这个解析过程的具体分析,但是读者一定要仔细去分析这个过程,如果做插件化开发,这个方法是很重要的,必须要了解。
2.收集apk中的签名信息和验证签名信息,如果是替换安装,则要和原来的apk的签名信息进行对比,主要是为了安全升级app
3.判断需要安装的apk的信息,是否有记录,如果有则是替换安装,如果没有,则是安装一个新的app
4.处理apk中的权限
5.将原来的临时文件重命名
6.如果是替换安装,则走具体的替换安装的逻辑,如果需要替换的是系统APP,则调用Settings$disableSystemPackageLPw来disable旧的APK;如果替换的是非系统APP,则调用deletePackageLI删除旧的APK。不管是更新系统还是非系统apk,都会先清除之前的packages信息,然后通过scanPackageTracedLI去安装apk,安装完后更新permissions和setting,最后通过writeLPr更新packages.xml.如果是一个安装一个新的app,则执行installNewPackageLIF方法,这个方法,后面会进行分析。
7.对满足条件的apk进行dex文件的优化处理
8.晚装完成后,更新apk所属的用户
下面下分析installNewPackageLIF方法:
http://androidxref.com/9.0.0_r3/xref/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java
private void installNewPackageLIF(PackageParser.Package pkg, final @ParseFlags int parseFlags,
final @ScanFlags int scanFlags, UserHandle user, String installerPackageName,
String volumeUuid, PackageInstalledInfo res, int installReason) {
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "installNewPackage");
// Remember this for later, in case we need to rollback this install
String pkgName = pkg.packageName;
synchronized(mPackages) {
final String renamedPackage = mSettings.getRenamedPackageLPr(pkgName);
if (renamedPackage != null) {
// A package with the same name is already installed, though
// it has been renamed to an older name. The package we
// are trying to install should be installed as an update to
// the existing one, but that has not been requested, so bail.
res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
+ " without first uninstalling package running as "
+ renamedPackage);
return;
}
if (mPackages.containsKey(pkgName)) {
// Don't allow installation over an existing package with the same name.
res.setError(INSTALL_FAILED_ALREADY_EXISTS, "Attempt to re-install " + pkgName
+ " without first uninstalling.");
return;
}
}
try {
//扫描apk
PackageParser.Package newPackage = scanPackageTracedLI(pkg, parseFlags, scanFlags,
System.currentTimeMillis(), user);
//更新Settings中的信息
updateSettingsLI(newPackage, installerPackageName, null, res, user, installReason);
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
//如果安装成功,则为新安装的应用准备数据
prepareAppDataAfterInstallLIF(newPackage);
} else {
// 安装失败,则删除apk
deletePackageLIF(pkgName, UserHandle.ALL, false, null,
PackageManager.DELETE_KEEP_DATA, res.removedInfo, true, null);
}
} catch (PackageManagerException e) {
res.setError("Package couldn't be installed in " + pkg.codePath, e);
}
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
}
这个方法内部,通过调用scanPackageLI()函数,这样一来APK的各种信息都会记录在PMS中;描完以后,会调用updateSettingsLI()函数来更新APK的权限,设置安装状态等。如果安装成功,则为新安装的app准备数据,如果安装失败,则将apk删除。
以上便是PMS安装apk的过程,下面对这个过程进行总结:
总结:PMS处理apk的安装过程,是从installStage方法开始的,这个方法内部,会通过handler发送一个msg.what是INIT_COPY的消息,handler内部处理这个消息,发起apk文件的复制,这个工作是在com.android.defcontainer进程中进行的,但是复制apk文件时,会先判断是否绑定了DefaultContainerService这个服务,因为只有绑定了DefaultContainerService这个服务,才能和com.android.defcontainer进程进行通信,如果未绑定DefaultContainerService这个服务,则会发起绑定这个服务的请求,绑定完成后,调用InstallParams类的startCopy方法开始复制,这个方法内部会先判断安装的重试次数是否超过了4次,如果超过了,就放弃安装,如果未超过,就开始安装,安装的过程中,根据安装方式,判断安装的位置是否冲突,在判断,安装的位置是否有足够的存储空间,如果没有,释放缓存空间,接着确定安装位置,在接着对apk进行检测。接着会创建一个临时文件路径, 然后在通过进程间通信的方式,在com.android.defcontainer进程中进行apk文件的复制工作。然后复制Native库。完成了apk的复制工作后,apk就在data/app目录下,不过此时还是临时文件的,后续还需要重新命名。如果复制过程为出现异常,则返回INSTALL_SUCCEEDED(1)这个整数;否则,会返回对应的错误码。接着处理返回的复制结果,如果复制正常,则会进入到预安装,预安装这个步骤中,主要是检测包的状态,安装环境,比如sd卡的上的apk安装,如果在安装时,sd卡被拔出了或者sd卡上的文件不存了,所以这里要再次确认下安装的环境,如果安装环境不正常,则会清除复制的apk,如果安装环境正常,则进入实质的安装阶段,这个阶段,会解析apk,并将信息保存到PackageParser$Package类的成员变量中,在接着验证apk的签名信息,处理apk中的权限,接着将临时文件重命名,判断是安装一个新的app还是升级app,然后进行相应的安装和升级,如果安装过程中,安装失败,会删除apk,安装的本质是对apk中文件manifest文件进行解析,并将保存到PackageParser.Package中的信息赋值给Settings这个对象中,完成对Settings对象中对apk信息的更新。在接着是对满足一定条件的apk中的dex文件进行优化,完成安装后,更新apk所属的用户。
参考:
包管理机制分析