adb install安装流程

2023-11-15

把一个编译好的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等。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

adb install安装流程 的相关文章

随机推荐

  • 容器化部署 Jib

    概念 Google Jib 容器化构建工具 Jib是google开源的Java容器化工具 可以直接构建 Java 应用的 Docker 和 OCI 镜像的类库 以 Maven 和 Gradle 插件形式提供 通过 Jib Java 开发者可
  • 【省带宽、压成本专题】从产品架构来看,PCDN如何节流50%

    过去几年 我们一直在视频省流量方面潜心钻研 取得不俗的成果 本次 省带宽 压成本 系列一共会推出六篇文章 从技术迭代 硬件更新等角度出发 向大家介绍节省CDN流量 降低视频播放成本的方法 第一篇 从产品架构来看 PCDN如何节流50 目前国
  • 华为OD机试 - 欢乐的周末(Java)

    题目描述 小华和小为是很要好的朋友 他们约定周末一起吃饭 通过手机交流 他们在地图上选择了多个聚餐地点 由于自然地形等原因 部分聚餐地点不可达 求小华和小为都能到达的聚餐地点有多少个 输入描述 第一行输入m和n m代表地图的长度 n代表地图
  • 哈佛商学院私人笔记:如何一天拥有48小时?

    你的身边有没有这样一群人 永远精力充沛 永远有用不完的时间 工作 社交 生活 兴趣什么都不落下 谁都知道这得益于他们对时间的高效利用 但具体的妙招是什么呢 刚来到学校 哈佛 的时候我就被告知 你们的第一年是故意设计成很紧张的时间表 以锻炼你
  • C 标准库 - 《stdarg.h》

    原文链接 https www runoob com cprogramming c standard library stdarg h html 简介 stdarg h 头文件定义了一个变量类型 va list 和三个宏 这三个宏可用于在参数
  • STM32——DS18B20温度传感器

    一 DS18B20介绍 一 DS18B20技术性能特征 1 独特的单总线接口方式 DS18B20在与微处理器连接时仅需要一条口线即可实现微处理器与DS18B20的双向通讯 大大提高了系统的抗干扰性 2 测温范围 55 C 125 C 3 支
  • 串口服务器485转以太网

    串口服务器485转以太网可以将485等串口设备连接到网络中 让这些设备采集的数据发往网络 建立串口和网络的透明传输通道 实现设备联网 用户可以使用组态软件或者自己编写网络通信程序和设备通信 上海卓岚串口服务器可支持虚拟串口协议 使得您也可以
  • [编程入门]自定义函数之字符串拷贝

    题目要求 有一字符串 包含n个字符 写一函数 将此字符串中从第m个字符开始的全部字符复制成为另一个字符串 include
  • java判断字符串String是否为空

    java判断字符串String是否为空 1 判空的四个方法 2 区别 和equals null和 3 推荐使用 1 判空的四个方法 1 str null length就是取得字符串的长度 2 str length 0 3 equals st
  • 亚马逊S3Client实现上传下载功能

    首先引入依赖
  • Vue3通透教程【二】更高效的构建工具—Vite

    文章目录 写在前面 webpack Vite是什么 使用Vite创建项目 写在最后 写在前面 专栏介绍 凉哥作为 Vue 的忠实 粉丝输出过大量的 Vue 文章 应粉丝要求开始更新 Vue3 的相关技术文章 Vue 框架目前的地位大家应该都
  • 2019/10/3 CSP-S 模拟测

    T1 Permut 题意 求 1 n 的排列中逆序对数量为 k 的排列的个数 SOL 排除法我们知道一定不是 O n 的算法 考虑 dp 现在已经有 n 1 的答案了 考虑新加入一个数产生多少新的逆序对 设 dp i j 表示 1 i 的排
  • flea-cache使用之Redis集群模式接入

    Redis集群模式接入 1 参考 2 依赖 3 基础接入 3 1 定义Flea缓存接口 3 2 定义抽象Flea缓存类 3 3 定义Redis客户端接口类 3 4 定义集群模式Redis客户端实现类 3 5 定义Redis集群连接池 3 6
  • DataV:可能是我用过最可怕的数据可视化神器

    每年的双十一 天猫都会在剁手狂欢节中直播战绩 除了可怕的数字之外 不知道大家有没有留意到这些同样可怕的 数据可视化大屏 2015双十一大屏 2016双十一大屏 所谓大屏 顾名思义就是一个 很大的屏 一般应用在交易大厅 展览中心 管控中心 老
  • 关于java中IO的个人理解

    一 什么是java的I O I O中的i为input即输入的意思 O为output输出的意思 所以io为java中数据的输入和输出 这里的数据即包括网络上的数据 socket 也包括本地的文件数据 IO使用流的概念来进行数据的输入和输出也就
  • 希沃展台如何使用_【希沃视频展台--让课堂展示从未如此轻松!】PjTime.COM 综合导购 希沃...

    无论是作业试卷的讲解 还是实验过程展示 课堂展示对于课堂效率的提升始终起着重要的作用 然而目前市场上还是充斥着不少操作复杂 清晰度十分尴尬的展台产品 影响着老师的课堂效果 为此我们特意打造了希沃 7系列视频展台 一款简单又强大的视频展台 希
  • 架构师之道 秒杀系统优化思路

    本文曾在 架构师之路 上发布过 近期支援Qcon AS大会 在微信群里分享了该话题 故对原文进行重新整理与发布 一 秒杀业务为什么难做 1 im系统 例如qq或者微博 每个人都读自己的数据 好友列表 群列表 个人信息 2 微博系统 每个人读
  • Flex布局(一:基本概念和容器属性)

    前言 算上来快2个月没写博客呢 一是赶项目 二是中途接到一个朋友公司需要帮忙 周末都在TA们公司兼职 然后空下来就快12月初 1 Flex 传统的布局方案 基于css盒子模型 float display position TA对于很多特殊布
  • Spring中的AOP

    1 概述 在软件业 AOP为Aspect Oriented Programming的缩写 意为 面向切面编程 通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术 AOP是OOP的延续 是软件开发中的一个热点 也是Spring框
  • adb install安装流程

    把一个编译好的APK通过 include BUILD PREBUILT 预制到系统中 但是启动后一直crash log中显示 dlopen failed cannot locate symbol 02 25 16 18 20 143 126