adb install 流程分析

在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;
            }
            ....

由上面代码可以看到

  1. 调用makeInstallParams()获取 InstallParams
  2. 调用doCreateSession 并一 InstallParams为参数 来获取sessionId
  3. 通过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);
}

以上省去了大量代码,只留了我们分析流程的代码,以上代码主要干了以下几件事

  1. 调用mContainerService.getMinimalPackageInfo 获取一个用于描述apk结构的类
  2. 调用installLocationPolicy 获取推荐安装路径
  3. 根据params参数的不同创建不同的args,这里创建的是FileInstallArgs
  4. 调用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);

             }
          }

由上面代码可知

  1. 调用FileInstallArgs的 doPreInstall
  2. 调用 installPackageTracedLI进行安装
  3. 调用FileInstallArgs的 doPostInstall
  4. 此时已安装完apk,无论成功还是失败都会发送POST_INSTALL消息,该消息携带一个token,通过该token可以取出mRunningInstalls数组中的一个PostInstallData对象

关于安装的代码,其内容由dex优化等内容,由于篇幅较长,所以大家只需要知道该函数是安装的代码,如果有想知道具体细节的,可以自行查阅。在POST_INSTALL中,会通知PM安装完成。

到这里安装的流程就分析完了,其中还有很多细节没有分析,这里就留给大家了。
  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 5
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值