adb install 流程分析
来源:互联网 发布:北京现代软件学院网站 编辑:程序博客网 时间:2024/06/06 00:09
在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安装完成。
到这里安装的流程就分析完了,其中还有很多细节没有分析,这里就留给大家了。
- adb install 流程分析
- adb install 流程
- adb install 流程
- 【android x86 5.1.1】 adb install 流程分析
- Adb install
- adb:) adb install
- 【ADB】adb install 使用
- android adb tcpip 协议流程分析
- android adb install 失败的错误分析 PartII
- adb install 和adb uninstall
- adb命令之adb install
- adb install 和adb uninstall
- adb install 和adb uninstall
- adb install 和adb uninstall
- adb install com.。。。.apk
- adb install apkt
- adb install parameter
- adb install问题
- mysql 批量修改表前缀
- JS鼠标事件大全 推荐收藏
- 使用jQuery的hover事件在IE中不停闪动的解决方法
- Android java到Kotlin的基本语法
- Web安全知多少
- adb install 流程分析
- PAT 甲级 1020. Tree Traversals
- Android 蓝牙Bluetooth搜索设备。
- CSS3的calc()使用
- 【转】秒杀系统架构分析与实战
- linux下用cmake对caffe静态编译时-static-libstdc++参数无效的问题
- svn服务器安装
- Apache 2.4.16、PHP5.6.11安装教程
- js中toString、toLocaleString、valueOf函数区别