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

由上面代码可以看到

  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安装完成。

到这里安装的流程就分析完了,其中还有很多细节没有分析,这里就留给大家了。

原创粉丝点击