系统开机过程中扫描并安装apk的过程是针对已有的apk文件。针对外部apk的安装,pms提供了另外的接口。我们一般也就通过两种方式去安装外部apk,一种是通过adb的install命令安装外部应用。另外一种是通过系统应用PackageInstaller,通过界面引导的方式安装外部应用。下面分别来分析这两种安装方式的过程和相同的地方。
adb install命令实际上调用的是/system/bin目录下的pm可执行程序来实现安装应用的功能。
下面来分析下pm的实现源码:
[/frameworks/base/cmds/pm/src/com/android/commands/pm/Pm.java]
public static void main(String[] args) { int exitCode = 1; try { exitCode = new Pm().run(args); } catch (Exception e) { } System.exit(exitCode);}Pm程序的入口main函数中,创建一个Pm的实例并执行run方法,传入args参数(含apk路径等信息)。继续分析run方法:
public int run(String[] args) throws IOException, RemoteException { boolean validCommand = false; if (args.length < 1) { return showUsage(); } mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user")); mPm = ipackageManager.Stub.asInterface(ServiceManager.getService("package")); ... String op = args[0]; if ("install".equals(op)) { return runInstall(); } ... }run方法中首先读取第一个参数args[0],第一个参数是op(Operation),表示命令的操作是什么,这里我们来看op=”install”的情况,直接调用runInstall方法,下面来看看runInstall方法的实现:
PRivate int runInstall() { int installFlags = 0; int userId = UserHandle.USER_ALL; String installerPackageName = null; ... try { VerificationParams verificationParams = new VerificationParams(verificationURI, originatingURI, referrerURI, VerificationParams.NO_UID, null); mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags, installerPackageName, verificationParams, abi, userId); ... } catch (RemoteException e) { System.err.println(e.toString()); System.err.println(PM_NOT_RUNNING_ERR); return 1; }可以看到runInstall方法最终通过binder和pms通信,执行pms的installPackageAsUser方法,分别传入apk的文件路径,abi信息等。pms的installPackageAsUser方法在接下来会分析。
通过PackageInstaller应用安装外部应用
查看PackageInstaller安装界面代码:
[/package/app/PackageInstaller/src/com/android/packageinstaller /InstallAppProgress.java]
public void initView() { setContentView(R.layout.op_progress); int installFlags = 0; PackageManager pm = getPackageManager(); ... pm.installPackageWithVerificationAndEncryption(mPackageURI, observer, installFlags, installerPackageName, verificationParams, null); ... }在PackageInstaller安装界面初始化的时候就直接调用PackageManager提供的installPackageWithVerificationAndEncryption方法执行安装操作。下面来分析下installPackageWithVerificationAndEncryption的具体实现:
[/frameworks/base/core/java/android/app/applicationPackageManager.java]
public void installPackageWithVerificationAndEncryption(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { installCommon(packageURI, observer, flags, installerPackageName, verificationParams,encryptionParams); }继续分析installCommon方法:
private void installCommon(Uri packageURI, PackageInstallObserver observer, int flags, String installerPackageName, VerificationParams verificationParams, ContainerEncryptionParams encryptionParams) { if (!"file".equals(packageURI.getScheme())) { throw new UnsupportedOperationException("Only file:// URIs are supported"); } final String originPath = packageURI.getPath(); try { mPM.installPackage(originPath, observer.getBinder(), flags, installerPackageName, verificationParams, null); } catch (RemoteException ignored) { } }installCommon方法首先判断传入的Uri是否属于文件类型,如果不是,直接异常返回。如果是,则通过Uri获得apk文件路径,最后调用pms的installPackage方法(实际上同样是调用installPackageAsUser)执行具体的安装操作。
installPackageAsUser方法分析
上面两种主要的外部安装路径最后都指向了pms的installPakcageAsUser方法。这个方法提供外部安装功能,我们从应用安装的第一个阶段(复制文件)开始:
[/frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java]
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer, int installFlags, String installerPackageName, VerificationParams verificationParams, String packageAbiOverride, int userId) { mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES, null);第一步先检查调用端是否具有INSTALL_PACKAGES权限,如果没有该权限则抛出权限异常并终止运行。只有系统平台签名或者系统应用才会授予这个权限,这也是第三方应用无法实现静默安装的原因。
final int callingUid = Binder.getCallingUid(); if ((callingUid == Process.SHELL_UID) || (callingUid == Process.ROOT_UID)) { installFlags |= PackageManager.INSTALL_FROM_ADB; } else { // Caller holds INSTALL_PACKAGES permission, so we're less strict // about installerPackageName. installFlags &= ~PackageManager.INSTALL_FROM_ADB; installFlags &= ~PackageManager.INSTALL_ALL_USERS; }第二步检查callingUid是否等于SHELL或者ROOT,如果是表示通过adb shell命令发起的安装动作,给installFlags添加对应INSTALL_FROM_ADB标志,否则去掉。这个标志后面会用到。
verificationParams.setInstallerUid(callingUid); final File originFile = new File(originPath); final OriginInfo origin = OriginInfo.fromUntrustedFile(originFile); final Message msg = mHandler.obtainMessage(INIT_COPY); msg.obj = new InstallParams(origin, observer, installFlags, installerPackageName, verificationParams, user, packageAbiOverride); mHandler.sendMessage(msg);}最后创建一个what是INIT_COPY的Message,把调用的参数保存在InstallParams对象中并发送到mHandler的消息队列中。
下面分析下INIT_COPY类型消息的处理:
class PackageHandler extends Handler { private boolean mBound = false; final ArrayList<HandlerParams> mPendingInstalls = new ArrayList<HandlerParams>(); void doHandleMessage(Message msg) { switch (msg.what) { case INIT_COPY: { HandlerParams params = (HandlerParams) msg.obj; int idx = mPendingInstalls.size(); if (!mBound) { if (!connectToService()) { params.serviceError(); return; } else { 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,因为这是一个异步的过程,要等待绑定的结果通过onServiceConnected()返回,所以,这里将安装的参数信息放到mPendingInstalls列表中。如果这个service以前就绑定好了,现在不在需要再绑定,安装信息也会先放到mPendingInstalls中。如果有多个安装请求同时到达,通过mPendingInstalls列表可以对它们进行排队。如果列表中只有一项,说明没有更多的安装请求,此时会立即发送MCS_BOUND消息,进入下一步的处理。而onServiceConnected()方法的处理同样是发送MCS_BOUND消息,onServiceConnected方法如下:
class DefaultContainerConnection implements ServiceConnection { public void onServiceConnected(ComponentName name, IBinder service) { IMediaContainerService imcs = IMediaContainerService.Stub.asInterface(service); mHandler.sendMessage(mHandler.obtainMessage(MCS_BOUND, imcs)); } public void onServiceDisconnected(ComponentName name) { } };一旦DefaultContainerService(该服务用于外部存储卡应用安装拷贝用)服务绑定成功后,通过mHandler发MCS_BOUND消息,下面来分析下这个消息的处理过程:
case MCS_BOUND: { if (msg.obj != null) { mContainerService = (IMediaContainerService) msg.obj; } if (mPendingInstalls.size() > 0) { HandlerParams params = mPendingInstalls.get(0); if (params != null) { if (params.startCopy()) { // Delete pending install if (mPendingInstalls.size() > 0) { mPendingInstalls.remove(0); } if (mPendingInstalls.size() == 0) { if (mBound) { removeMessages(MCS_UNBIND); Message ubmsg = obtainMessage(MCS_UNBIND); sendMessageDelayed(ubmsg, 10000); } } else { mHandler.sendEmptyMessage(MCS_BOUND); } } } } }MCS_BOUND消息的处理过程就是调用InstallParams类的startCopy()方法来执行安装操作,一次安装完成后在待安装列表中删除。只要mPendingInstalls中还有安装信息,就会重复发送MCS_BOUND消息,直到所有的应用都安装完毕,最后发送一个延时10秒的MCS_UNBIND消息。MCS_UNBIND消息不细分析。
安装的开始在InstallParams类的startCopy方法,该方法定义在InstallParams的父类HandlerParams中:
private abstract class HandlerParams { private static final int MAX_RETRIES = 4; … final boolean startCopy() { boolean res; try { handleStartCopy(); res = true; } handleReturnCode(); return res; }父类的startCopy方法最后调用handleStartCopy方法,其中抽象的父类只声明了handleStartCopy抽象方法,具体实现在子类InstallParams中:
class InstallParams extends HandlerParams { final OriginInfo origin; final IPackageInstallObserver2 observer; int installFlags; final String installerPackageName; final VerificationParams verificationParams; private InstallArgs mArgs; private int mRet; final String packageAbiOverride; public void handleStartCopy() throws RemoteException { int ret = PackageManager.INSTALL_SUCCEEDED; … final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0; final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0; PackageInfoLite pkgLite = null; … final InstallArgs args = createInstallArgs(this); if (ret == PackageManager.INSTALL_SUCCEEDED) { … /* * No package verification is enabled, so immediately start * the remote call to initiate copy using temporary file. */ ret = args.copyApk(mContainerService, true); } mRet = ret; }如果忽略handleStartCopy中一堆繁杂的检验判断,最终调用InstallArgs的copyApk方法,传入ContainerService的binder实例。另外真正的copyApk方法在其子类FileInstallArgs的copyApk方法中实现:
class FileInstallArgs extends InstallArgs { private File codeFile; private File resourceFile; private File legacyNativeLibraryPath; int copyApk(IMediaContainerService imcs, boolean temp) throws RemoteException { … int ret = PackageManager.INSTALL_SUCCEEDED; ret = imcs.copyPackage(origin.file.getAbsolutePath(), target); if (ret != PackageManager.INSTALL_SUCCEEDED) { Slog.e(TAG, "Failed to copy package"); return ret; } 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; }发现copyApk方法同样是调用DefaultContainerService的copyPackage方法将应用的文件复制到/data/app下。如果应用中还有native的动态库,也会把包在apk文件中的动态库的文件提取出来。执行完copyApk后,安装的第一阶段工作就完成了,应用安装到了/data/app目录下。
第一阶段(复制apk文件)完成后,继续完成第二阶段的扫描安装工作:
在前面的startCopy()方法中已经看到,最后它会调用handleReturnCode()方法,其实现在InstallParams类中:
void handleReturnCode() { if (mArgs != null) { processPendingInstall(mArgs, mRet); } }接下来分析processPendingInstall方法的实现:
private void processPendingInstall(final InstallArgs args, final int currentStatus) { mHandler.post(new Runnable() { public void run() { mHandler.removeCallbacks(this); // Result object to be returned PackageInstalledInfo res = new PackageInstalledInfo(); res.returnCode = currentStatus; res.uid = -1; res.pkg = null; res.removedInfo = new PackageRemovedInfo(); if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) { args.doPreInstall(res.returnCode); synchronized (mInstallLock) { installPackageLI(args, res); } args.doPostInstall(res.returnCode, res.uid); } }); }processPendingInstall方法中post了一个消息,这样安装过程将以异步的方式继续执行。在post消息的处理中,首先调用installPackageLI来装载应用,接下来的一大段代码都是执行备份操作,备份是通过BackupManagerService来完成的。备份完成后,通过发送POST_INSTALL消息来继续处理。现在来分析下apk的装载过程:
private void installPackageLI(InstallArgs args, PackageInstalledInfo res) { final int installFlags = args.installFlags; String installerPackageName = args.installerPackageName; File tmpPackageFile = new File(args.getCodePath()); boolean forwardLocked = ((installFlags & PackageManager.INSTALL_FORWARD_LOCK) != 0); boolean onSd = ((installFlags & PackageManager.INSTALL_EXTERNAL) != 0); boolean replace = false; final int scanFlags = SCAN_NEW_INSTALL | SCAN_FORCE_DEX | SCAN_UPDATE_SIGNATURE; // Result object to be returned res.returnCode = PackageManager.INSTALL_SUCCEEDED; PackageParser pp = new PackageParser(); pp.setSeparateProcesses(mSeparateProcesses); pp.setDisplayMetrics(mMetrics); final PackageParser.Package pkg; pkg = pp.parsePackage(tmpPackageFile, parseFlags); // Mark that we have an install time CPU ABI override. pkg.cpuAbiOverride = args.abiOverride; String pkgName = res.name = pkg.packageName; pp.collectCertificates(pkg, parseFlags); pp.collectManifestDigest(pkg); pp = null; String oldCodePath = null; boolean systemApp = false; synchronized (mPackages) { // Check if installing already existing package if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) { String oldName = mSettings.mRenamedPackages.get(pkgName); if (pkg.mOriginalPackages != null && pkg.mOriginalPackages.contains(oldName) && mPackages.containsKey(oldName)) { pkg.setPackageName(oldName); pkgName = pkg.packageName; replace = true; } else if (mPackages.containsKey(pkgName)) { // This package, under its official name, already exists // on the device; we should replace it. replace = true; } } PackageSetting ps = mSettings.mPackages.get(pkgName); if (!ps.keySetData.isUsingUpgradeKeySets() || ps.sharedUser != null) { try { verifySignaturesLP(ps, pkg); } catch (PackageManagerException e) { res.setError(e.error, e.getMessage()); return; } } else { if (!checkUpgradeKeySetLP(ps, pkg)) { return; } } int N = pkg.permissions.size(); for (int i = N-1; i >= 0; i--) { PackageParser.Permission perm = pkg.permissions.get(i); BasePermission bp = mSettings.mPermissions.get(perm.info.name); if (bp != null) { final boolean sigsOk; if (!bp.sourcePackage.equals(pkg.packageName) || !(bp.packageSetting instanceof PackageSetting) || !bp.packageSetting.keySetData.isUsingUpgradeKeySets() || ((PackageSetting) bp.packageSetting).sharedUser != null) { sigsOk = compareSignatures(bp.packageSetting.signatures.mSignatures, pkg.mSignatures) == PackageManager.SIGNATURE_MATCH; } else { sigsOk = checkUpgradeKeySetLP((PackageSetting) bp.packageSetting, pkg); } } } } if (replace) { replacePackageLI(pkg, parseFlags, scanFlags | SCAN_REPLACING, args.user, installerPackageName, res); } else { installNewPackageLI(pkg, parseFlags, scanFlags | SCAN_DELETE_DATA_ON_FAILURES, args.user, installerPackageName, res); } synchronized (mPackages) { final PackageSetting ps = mSettings.mPackages.get(pkgName); if (ps != null) { res.newUsers = ps.queryInstalledUsers(sUserManager.getUserIds(), true); } } }installPackageLI方法首先解析了安装的应用文件,得到解析的结果后,主要是判断新安装的应用是否和系统中已安装的应用构成升级关系,如果是,调用replacePackageLI方法继续处理,否则调用installNewPackageLI方法处理。
新闻热点
疑难解答