在Android系统中,应用的文件是以 .apk为结尾的文件,这个文件是如何被安装到Android系统中的? 那就是利用 adb install 这个命令, 大家可以在命令行内 打入 adb install <.apk文件路径> 进行安装。下面我们来分析它的原理。
首先该命令对应代码在commandline.cpp内,内容如下:
int adb_commandline(int argc, const char **argv) {
....
....
else if (!strcmp(argv[0], "install")) {
if (argc < 2) return usage();
if (_use_legacy_install()) {
return install_app_legacy(transport_type, serial, argc, argv);
}
return install_app(transport_type, serial, argc, argv);
}
接下来会调用到 install_app函数. 在其内,会执行一个pm脚本,该脚本主要功能就是通过app_process来执行pm.jar 的main函数(有关于app_procee相关信息大家可以去查阅一下), pm.jar的main函数就相当于java进程的入口函数,下面对它进行分析(PM.java)
public static void main(String[] args) {
int exitCode = 1;
try {
exitCode = new Pm().run(args);
} catch (Exception e) {
Log.e(TAG, "Error", e);
System.err.println("Error: " + e);
if (e instanceof RemoteException) {
System.err.println(PM_NOT_RUNNING_ERR);
}
}
System.exit(exitCode);
}
可以看到,直接new Pm() 然后执行了 run()函数.
public int run(String[] args) throws RemoteException {
boolean validCommand = false;
if (args.length < 1) {
return showUsage();
}
mAm = IAccountManager.Stub.asInterface(ServiceManager.getService(Context.ACCOUNT_SERVICE));
mUm = IUserManager.Stub.asInterface(ServiceManager.getService(Context.USER_SERVICE));
mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
mInstaller = mPm.getPackageInstaller();
mArgs = args;
String op = args[0];
mNextArg = 1;
....
if ("install".equals(op)) {
return runInstall();
}
....
可以看到,上面获取了一个IPackageManager,它实际是一个PackageManagerService 的binder客户端,通过它调用pkms的方法,接下来处理 install 指令.
private int runInstall() throws RemoteException {
final InstallParams params = makeInstallParams();
final String inPath = nextArg();
....
final int sessionId =doCreateSession(params.sessionParams,params.installerPacka. geName, params.userId);
....
if (doCommitSession(sessionId, false
!= PackageInstaller.STATUS_SUCCESS) {
return 1;
}
....
由上面代码可以看到
- 调用makeInstallParams()获取 InstallParams
- 调用doCreateSession 并一 InstallParams为参数 来获取sessionId
- 通过sessionId 调用 doCommitSession
下面我们来看doCommitSession
private int doCommitSession(int sessionId, boolean logSuccess) throws RemoteException {
PackageInstaller.Session session = null;
try {
session = new PackageInstaller.Session(
mInstaller.openSession(sessionId));
final LocalIntentReceiver receiver = new LocalIntentReceiver();
session.commit(receiver.getIntentSender());
....
....
可以看到,通过 session 来创建了一个PackageInstall.Session对象(注意mInstall.openSession()), 然后通过该对象调用 commit方法,我们继续深入。
public void commit(@NonNull IntentSender statusReceiver) {
try {
mSession.commit(statusReceiver);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
可以看到直接在其内直接调用了 成员变量mSession的commit, 它的类型是IPackageInstallerSession对象,是通过上面的mInstaller.openSession来获取的,相当于PackageInstallerSession binder的客户端,既然客户端调用,那么就必然会调用到服务端相同名字的代码,下面我们来看PackageInstallerSession.commit()
@Override
public void commit(IntentSender statusReceiver) {
····
//原子操作,防止并发
mActiveCount.incrementAndGet();
final PackageInstallObserverAdapter adapter = new PackageInstallObserverAdapter(mContext,
statusReceiver, sessionId, mIsInstallerDeviceOwner, userId);
mHandler.obtainMessage(MSG_COMMIT, adapter.getBinder()).sendToTarget();
}
可以看到,它创建了一个PackageInstallObserverAdapter,它的作用是监听PKMS的安装结果。 然后通过mHandler发送了一个MSG_COMMIT的消息,该hander是通过传入callback方式来处理的,其callback为mHandlerCallback,
private final Handler.Callback mHandlerCallback = new Handler.Callback() {
@Override
public boolean handleMessage(Message msg) {
// Cache package manager data without the lock held
final PackageInfo pkgInfo = mPm.getPackageInfo(
params.appPackageName, PackageManager.GET_SIGNATURES /*flags*/, userId);
final ApplicationInfo appInfo = mPm.getApplicationInfo(
params.appPackageName, 0, userId);
synchronized (mLock) {
....
try {
commitLocked(pkgInfo, appInfo);
}
....
return true;
}
}
};
上面代码首先获取pkg与application 的信息,然后调用 commitLocked
private void commitLocked(PackageInfo pkgInfo, ApplicationInfo appInfo)
throws PackageManagerException {
····
mPm.installStage(mPackageName, stageDir, stageCid, localObserver, params,
installerPackageName, installerUid, user, mCertificates);
}
在上面方法中,我们直接到了最后一行,也就是通过PKMS的 binder客户端来调用了 installStage,最终会到PKMS内的installStage。
void installStage(String packageName, File stagedDir, String stagedCid,
IPackageInstallObserver2 observer, PackageInstaller.SessionParams sessionParams,
String installerPackageName, int installerUid, UserHandle user,
Certificate[][] certificates) {
····
final Message msg = mHandler.obtainMessage(INIT_COPY);//
final InstallParams params = new InstallParams(origin, null, observer,
sessionParams.installFlags, installerPackageName, sessionParams.volumeUuid,
verificationInfo, user, sessionParams.abiOverride,
sessionParams.grantedRuntimePermissions, certificates);//
····
mHandler.sendMessage(msg);
可以看到上面代码首先获取了一个 INIT_COPY消息,然后创建了一个InstallParams, 接着发送消息。
public void handleMessage(Message msg) {
try {
doHandleMessage(msg);
} finally {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
}
void doHandleMessage(Message msg) {
switch (msg.what) {
case INIT_COPY: {
....
if (!mBound) {
if (!connectToService()) {
params.serviceError();
....
} else {
mPendingInstalls.add(idx, params);
}
}else {
mPendingInstalls.add(idx, params);
if (idx == 0) { mHandler.sendEmptyMessage(MCS_BOUND);
}
}
可以看到在doHandleMessage 县调用了connectToService(),其内主要是开启一个服务(DefalutContainerService),它的主要作用就是安装apk。所以,安装apk是由系统提供的服务来完成的。最后会接着发送一个MCS_BOUND的消息。
case MCS_BOUND: {
....
if (params.startCopy()) {
....
}
....
}
在其内,最重要的一句就是 startCopy这句代码,下面我们来看
final boolean startCopy() {
boolean res;
try {
// max_retries =4, 最大可重试安装4次,超过则错误
if (++mRetries > MAX_RETRIES) {
....
mHandler.sendEmptyMessage(MCS_GIVE_UP);
handleServiceError();
return false;
} else {
handleStartCopy();
res = true;
}
} catch (RemoteException e) {
mHandler.sendEmptyMessage(MCS_RECONNECT);
res = false;
}
handleReturnCode();
return res;
}
可以看到,上面判断了安装失败次数,系统默认允许最大4次安装失败。
接着依次调用了。handleStartCopy 与 handleReturnCode(). 这两个方法都是abstract的,所以我们应该去找它的子类来查看。由前面代码可知,HandlerParams实际类型是 InstallerParams/
public void handleStartCopy() throws RemoteException {
final boolean onSd = (installFlags & PackageManager.INSTALL_EXTERNAL) != 0;
final boolean onInt = (installFlags & PackageManager.INSTALL_INTERNAL) != 0;
final boolean ephemeral = (installFlags & PackageManager.INSTALL_EPHEMERAL) != 0;
if (onInt && onSd) {
.....
} else if (onSd && ephemeral) {
.....
} else {
//1.
pkgLite = mContainerService.getMinimalPackageInfo(origin.resolvedPath, installFlags, packageAbiOverride);
.....
}
.....
loc = installLocationPolicy(pkgLite);
.....
final InstallArgs args = createInstallArgs(this);
....
ret = args.copyApk(mContainerService, true);
}
以上省去了大量代码,只留了我们分析流程的代码,以上代码主要干了以下几件事
- 调用mContainerService.getMinimalPackageInfo 获取一个用于描述apk结构的类
- 调用installLocationPolicy 获取推荐安装路径
- 根据params参数的不同创建不同的args,这里创建的是FileInstallArgs
- 调用FileInstallArgs 的 copyApk函数 (该函数完成会在data/app 下面多出一个.tmp为结尾的临时文件)
下面看handleReturnCode
@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() {
····
if (res.returnCode == PackageManager.INSTALL_SUCCEEDED) {
args.doPreInstall(res.returnCode);
synchronized (mInstallLock) {
installPackageTracedLI(args, res);
}
args.doPostInstall(res.returnCode, res.uid);
}
····
Message msg = mHandler.obtainMessage(POST_INSTALL, token, 0);
mHandler.sendMessage(msg);
}
}
由上面代码可知
- 调用FileInstallArgs的 doPreInstall
- 调用 installPackageTracedLI进行安装
- 调用FileInstallArgs的 doPostInstall
- 此时已安装完apk,无论成功还是失败都会发送POST_INSTALL消息,该消息携带一个token,通过该token可以取出mRunningInstalls数组中的一个PostInstallData对象
关于安装的代码,其内容由dex优化等内容,由于篇幅较长,所以大家只需要知道该函数是安装的代码,如果有想知道具体细节的,可以自行查阅。在POST_INSTALL中,会通知PM安装完成。