系统开机过程中扫描并安装apk的过程是针对已有的apk文件。针对外部apk的安装,pms提供了另外的接口。我们一般也就通过两种方式去安装外部apk,一种是通过adb的install命令安装外部应用。另外一种是通过系统应用PackageInstaller,通过界面引导的方式安装外部应用。下面分别来分析这两种安装方式的过程和相同的地方。
通过adb install命令安装外部应用
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方法处理。