adb install安装流程

把一个编译好的APK通过"include $(BUILD_PREBUILT)"预制到系统中,

但是启动后一直crash,log中显示“dlopen failed: cannot locate symbol”:

02-25 16:18:20.143  1266  1414 E AndroidRuntime: java.lang.UnsatisfiedLinkError: dlopen failed: cannot locate symbol "_ZN2cv3MatC1ENS_5Size_IiEEiRKNS_7Scalar_IdEE" referenced by "/system/app/MexicoLauncher/MexicoLauncher.apk!/lib/arm64-v8a/libsbs-lib.so"...
02-25 16:18:20.143  1266  1414 E AndroidRuntime: 	at java.lang.Runtime.loadLibrary0(Runtime.java:1087)
02-25 16:18:20.143  1266  1414 E AndroidRuntime: 	at java.lang.Runtime.loadLibrary0(Runtime.java:1008)
02-25 16:18:20.143  1266  1414 E AndroidRuntime: 	at java.lang.System.loadLibrary(System.java:1664)

但是通过“adb install”的方式安装应用就没有这个问题。

后来发现是这个APK中使用了动态链接库 libopencv-java4.so 中的方法,打包时一起把这个链接库一起放在APK中,普通的应用安装流程会将APK解压,使用解压目录中的链接库,而预制到系统/system/app中的应用则是共用/system/lib/, /system/lib64/目录中的链接库,还有安装目录codePath,问题就出在系统/system/lib64/也预制了libopencv-java4.so,但是和APK中使用的版本不一样,导致APK调用系统预制的libopencv-java4.so时找不到对应的符号。

library_path:

  • 预制应用
    1. /system/lib
    2. /system_ext/lib
    3. /system/app/{MOUDLE_NAME}/lib/arm
    4. /system/app/{MOUDLE_NAME}/{MOUDLE_NAME}.apk!/lib/armeabi-v7a/
  • 普通安装应用
    1. /data/app/~~sXptd4S4_IAEHpqTg8H9Cw==/com.example.test-O21bYYvtlIp81Gi4iGnhPA==/lib/arm
    2. /data/app/~~sXptd4S4_IAEHpqTg8H9Cw==/com.example.test-O21bYYvtlIp81Gi4iGnhPA==/base.apk!/lib/armeabi-v7a

这让我对“adb install”的流程产生了兴趣。

adb

commandline.cpp中判断参数为“install”:

else if (!strcmp(argv[0], "install")) {
    
    
        if (argc < 2) error_exit("install requires an argument");
        return install_app(argc, argv);
} 

adb_install.cpp中install_app()主要通过run_install_mode返回对应的处理方法:

int install_app(int argc, const char** argv) {
    
    
    ...
	auto run_install_mode = [&](InstallMode install_mode, bool silent) {
    
    
        switch (install_mode) {
    
    
            case INSTALL_PUSH:
                return install_app_legacy(passthrough_argv.size(), passthrough_argv.data(),
                                          use_fastdeploy);
            case INSTALL_STREAM:
                return install_app_streamed(passthrough_argv.size(), passthrough_argv.data(),
                                            use_fastdeploy);
            case INSTALL_INCREMENTAL:
                return install_app_incremental(passthrough_argv.size(), passthrough_argv.data(),
                                               incremental_wait, silent);
            case INSTALL_DEFAULT:
            default:
                error_exit("invalid install mode");
        }
    };
    ...
}

从执行adb程序时打印的log就能分辨安装时具体调用的方法了:
在这里插入图片描述

adb_install.cpp–>install_app_streamed()方法:

static int install_app_streamed(int argc, const char** argv, bool use_fastdeploy) {
    
    
    ...
	std::vector<std::string> cmd_args = {
    
    use_abb_exec ? "package" : "exec:cmd package"};
        cmd_args.reserve(argc + 3);

    // don't copy the APK name, but, copy the rest of the arguments as-is
    while (argc-- > 1) {
    
    
        if (use_abb_exec) {
    
    
            cmd_args.push_back(*argv++);
        } else {
    
    
            cmd_args.push_back(escape_arg(*argv++));
        }
    }

    // add size parameter [required for streaming installs]
    // do last to override any user specified value
    cmd_args.push_back("-S");
    cmd_args.push_back(android::base::StringPrintf("%" PRIu64, static_cast<uint64_t>(sb.st_size)));

    unique_fd remote_fd = send_command(cmd_args, &error);
    ...
}

install_app_streamed()在拼接组合cmd_args时会判断是否支持abb_exec,如果支持通过abb程序获取package服务,否则的话通过cmd程序获取package服务。

  • abb (Android Binder Bridge),通过abb可直接与service通信。代码位于/system/core/adb/daemon/abb

Android Binder Bridge gives the ability to communicate directly with the services on the devices. An example of service is package which is handling the package management. To see the full list available services use -l on the abb request.

abb和cmd都是/system/bin/目录下的可执行程序,可以通过这两个程序与service进行通信,cmd代码目录/frameworks/native/cmds/cmd。

最终拼接得到的cmd_args:

“package install -S 6654465”

PackageManagerService

"package"对应着PackageManagerService。

PackageManagerShellCommand.java—onCommand()方法中监听命令,接受到"install"命令后调用runInstall() --> doRunInstall()。

    private int doRunInstall(final InstallParams params) throws RemoteException {
    
    
   		...
        final int sessionId = doCreateSession(params.sessionParams,
                params.installerPackageName, params.userId);
        boolean abandonSession = true;
        Log.d(TAG, "doRunInstall: isStreaming = " + isStreaming);
        try {
    
    
            if (isStreaming) {
    
    
                if (doAddFiles(sessionId, args, params.sessionParams.sizeBytes, isApex)
                        != PackageInstaller.STATUS_SUCCESS) {
    
    
                    return 1;
                }
            } else {
    
    
                if (doWriteSplits(sessionId, args, params.sessionParams.sizeBytes, isApex)
                        != PackageInstaller.STATUS_SUCCESS) {
    
    
                    return 1;
                }
            }
            if (doCommitSession(sessionId, false /*logSuccess*/)
                    != PackageInstaller.STATUS_SUCCESS) {
    
    
                return 1;
            }
            abandonSession = false;

            if (!params.sessionParams.isStaged || !params.mWaitForStagedSessionReady) {
    
    
                pw.println("Success");
                return 0;
            }
            return doWaitForStagedSessionRead(sessionId, params.timeoutMs, pw);
        } finally {
    
    
            if (abandonSession) {
    
    
                try {
    
    
                    doAbandonSession(sessionId, false /*logSuccess*/);
                } catch (Exception ignore) {
    
    
                }
            }
        }
    }

doRunInstall()中主要关注doCreateSession()和doCommitSession()两个方法。

  • doCreateSession()中主要处理一些参数权限等问题;

  • doCommitSession()处理主要安装逻辑。

PackageManagerShellCommand.java—doCommitSession():

   private int doCommitSession(int sessionId, boolean logSuccess)
            throws RemoteException {
    
    

        final PrintWriter pw = getOutPrintWriter();
        PackageInstaller.Session session = null;
        try {
    
    
            // 1.openSession()
            session = new PackageInstaller.Session(
                    mInterface.getPackageInstaller().openSession(sessionId));
            if (!session.isMultiPackage() && !session.isStaged()) {
    
    
                // Sanity check that all .dm files match an apk.
                // (The installer does not support standalone .dm files and will not process them.)
                try {
    
    
                    DexMetadataHelper.validateDexPaths(session.getNames());
                } catch (IllegalStateException | IOException e) {
    
    
                    pw.println(
                            "Warning [Could not validate the dex paths: " + e.getMessage() + "]");
                }
            }
            final LocalIntentReceiver receiver = new LocalIntentReceiver();
            // 2. commit()
            session.commit(receiver.getIntentSender());
            final Intent result = receiver.getResult();
            final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,
                    PackageInstaller.STATUS_FAILURE);
            if (status == PackageInstaller.STATUS_SUCCESS) {
    
    
                if (logSuccess) {
    
    
                    pw.println("Success");
                }
            } else {
    
    
                pw.println("Failure ["
                        + result.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE) + "]");
            }
            return status;
        } finally {
    
    
            IoUtils.closeQuietly(session);
        }
    }

openSession(sessionId));中调用PackageInstallerService.java—prepareStageDir()创建存储客户端传入数据的临时文件夹:/data/app/vmdl298069140.tmp

session.commit(receiver.getIntentSender()); 调用 PackageInstallerSession.java—commit():

commit() --> dispatchStreamValidateAndCommit() --> handleStreamValidateAndCommit() --> handleInstall() --> installNonStagedLocked() --> installStage() --> startCopy() --> handleReturnCode() --> processPendingInstall() --> processInstallRequestsAsync()

重点关注PackageManagerService.java — processInstallRequestsAsync():

    // Queue up an async operation since the package installation may take a little while.
    private void processInstallRequestsAsync(boolean success,
            List<InstallRequest> installRequests) {
    
    
        mHandler.post(() -> {
    
    
            if (success) {
    
    
                // 1.清理目录中的文件,准备工作
                for (InstallRequest request : installRequests) {
    
    
                    request.args.doPreInstall(request.installResult.returnCode);
                }
                // 2.安装,重点工作
                synchronized (mInstallLock) {
    
    
                    installPackagesTracedLI(installRequests);
                }
                for (InstallRequest request : installRequests) {
    
    
                    request.args.doPostInstall(
                            request.installResult.returnCode, request.installResult.uid);
                }
            }
            for (InstallRequest request : installRequests) {
    
    
                restoreAndPostInstall(request.args.user.getIdentifier(), request.installResult,
                        new PostInstallData(request.args, request.installResult, null));
            }
        });
    }

installPackagesTracedLI() --> installPackagesLI():

installPackagesLI()原子式的安装一个或多个安装包,将其分为四个阶段:

  • Prepare

    Analyzes any current install state, parses the package and does initial validation on it.

  • Scan
    Interrogates the parsed packages given the context collected in prepare.

  • Reconcile

    Validates scanned packages in the context of each other and the current system state to ensure that the install will be successful.

  • Commit

    Commits all scanned packages and updates system state. This is the only place that system state may be modified in the install flow and all predictable errors must be determined before this phase.

    @GuardedBy("mInstallLock")
    private void installPackagesLI(List<InstallRequest> requests) {
    
    
        ...
        try {
    
    
            for (InstallRequest request : requests) {
    
    
                //1.preparePackageLI
                    prepareResult =
                            preparePackageLI(request.args, request.installResult);

                //2.scanPackageTracedLI
                    final ScanResult result = scanPackageTracedLI(
                            prepareResult.packageToScan, prepareResult.parseFlags,
                            prepareResult.scanFlags, System.currentTimeMillis(),
                            request.args.user, request.args.abiOverride);
                
			//3.ReconcileRequest
            ReconcileRequest reconcileRequest = new ReconcileRequest(preparedScans, installArgs,
                    installResults,
                    prepareResults,
                    mSharedLibraries,
                    Collections.unmodifiableMap(mPackages), versionInfos,
                    lastStaticSharedLibSettings);
                
            //4.commit    
            synchronized (mLock) {
    
    
                    commitRequest = new CommitRequest(reconciledPackages,
                            mUserManager.getUserIds());
                    commitPackagesLocked(commitRequest);
            }
            executePostCommitSteps(commitRequest);
        } finally {
    
    
                ...
        }
    }

1、preparePackageLI()调用reName()方法将/data/app/vmdl1257894690.tmp 重命名为/data/app/~~jX7cyrYyaORxsbMqR_xqHg==/com.example.myapplication-9ejCNZtC4adn0grNnctMQQ==

2、scanPackageTracedLI()调用scanPackageOnlyLI()方法将APK的nativeLibraryRoot目录确定为/data/app/~~jX7cyrYyaORxsbMqR_xqHg==/com.example.myapplication-9ejCNZtC4adn0grNnctMQQ==/lib

3、commit调用updateSettingsLI()方法更新packages.xml等。

猜你喜欢

转载自blog.csdn.net/qq_36063677/article/details/123361846
今日推荐