Android PackageManagerService源码分析和APK安装原理详解

news2025/1/12 6:18:52

前些天发现了一个蛮有意思的人工智能学习网站,8个字形容一下"通俗易懂,风趣幽默",感觉非常有意思,忍不住分享一下给大家。
👉点击跳转到教程

一、PackageManagerService简称PMS:PackageManagerService是Android系统中核心的
服务之一,负责应用程序的查询,卸载和应用信息查询,相当于应用程序的大管家。
在这里插入图片描述

    try {
            //调用系统的方法,获取应用程序信息
            context.getPackageManager().getPackageInfo(null, 0);
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }

1、首先是在抽象类PackageManager中定义了抽象方法getPackageInfo()

public abstract PackageInfo getPackageInfo(@NonNull String packageName,
            @PackageInfoFlags int flags)
            throws NameNotFoundException;

2、类ApplicationPackageManager继承PackageManager类,重写了getPackageInfo()方法

	@Override
    public PackageInfo getPackageInfo(String packageName, int flags)
            throws NameNotFoundException {
        return getPackageInfoAsUser(packageName, flags, mContext.getUserId());
    }

实际调用了getPackageInfoAsUser()方法

    @Override
    public PackageInfo getPackageInfoAsUser(String packageName, int flags, int userId)
            throws NameNotFoundException {
        try {
            PackageInfo pi = mPM.getPackageInfo(packageName, flags, userId);
            if (pi != null) {
                return pi;
            }
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }

        throw new NameNotFoundException(packageName);
    }

mPM是系统中写的AIDL文件,通过mPM.getPackageInfo(),调用到代理对象Proxy类中重写的getPackageInfo()方法,代理对象又调用到PackageManagerService中的getPackageInfo()方法。

二、APK安装原理
1.APK安装的两种方式
1.静默安装,又叫无界面的安装,从各大手机厂商应用商店下载的APK,便是无界面的安装。
2.有界面的安装,从三方托管平台下载一个APK包,需要一步一步来操作的。
下面分析有界面的安装
1、点击安装后,会跳转到系统提供的PackageInstallerActivity和其对应的布局
install_start.xml.
2、之后通过PackageUtil类获取APK包里面的信息

 PackageParser.Package parsed = PackageUtil.getPackageInfo(sourceFile);

3、如果用户点击安装按钮进行安装,或者取消,两种情况。

public void onClick(View v) {
        if (v == mOk) { //点击安装
            if (mOkCanInstall || mScrollView == null) {
                if (mSessionId != -1) {
                    mInstaller.setPermissionsResult(mSessionId, true);
                    clearCachedApkIfNeededAndFinish();
                } else {
                    startInstall(); //跳转到一个新的页面,显示正在安装
                }
            } else {
                mScrollView.pageScroll(View.FOCUS_DOWN);
            }
        } else if (v == mCancel) { //点击取消
            // Cancel and finish
            setResult(RESULT_CANCELED);
            if (mSessionId != -1) {
                mInstaller.setPermissionsResult(mSessionId, false);
            }
            clearCachedApkIfNeededAndFinish();
        }
    }

4、正在安装中的Activity类名为:InstallAppProgress 继承Activity。
以上是有界面的安装方式的简单分析。

下面分析无界面的安装方式:
1、没有界面的安装方式,最开始是从C代码开始执行的。
首先通过adb install 输入包名后,一敲回车,会执行到commandline.c文件下的,adb_commandline()方法。
2.最终会调用到install_app()方法

    char* apk_file = argv[last_apk];
    char apk_dest[PATH_MAX]; //APK安装路径
    snprintf(apk_dest, sizeof apk_dest, where, get_basename(apk_file));
    int err = do_sync_push(apk_file, apk_dest, 0 /* no show progress */);//把APK,push到手机存储里面。
    if (err) {
        goto cleanup_apk;
    } else {
        argv[last_apk] = apk_dest; /* destination name, not source location */
    }

    pm_command(transport, serial, argc, argv); //这个方法是为了执行shell:pm以命名的方式去安装

3、pm_command()方法如下

static int pm_command(transport_type transport, char* serial,
                      int argc, char** argv)
{
    char buf[4096];

    snprintf(buf, sizeof(buf), "shell:pm"); //借助pm的脚本文件,实现安装,通过pm命名的方式实现安装操作

    while(argc-- > 0) {
        char *quoted = escape_arg(*argv++);
        strncat(buf, " ", sizeof(buf) - 1);
        strncat(buf, quoted, sizeof(buf) - 1);
        free(quoted);
    }

    send_shellcommand(transport, serial, buf);
    return 0;
}

4、pm脚本文件

# Script to start "pm" on the device, which has a very rudimentary
# shell.
#
base=/system
export CLASSPATH=$base/framework/pm.jar //重点是找到pm.jar
exec app_process $base/bin com.android.commands.pm.Pm "$@"

5、Android源码frameworks/base/cmds/pm/src/com/android/commands/pm.java 源码中有一个main方法如下

public static void main(String[] args) {
        int exitCode = 1;
        try {
            exitCode = new Pm().run(args); //重点关注run()方法
        } 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);
    }

6、run方法代码如下

public int run(String[] args) throws IOException, RemoteException {
        boolean validCommand = false;
        if (args.length < 1) {
            return showUsage();
        }

        mUm = IUserManager.Stub.asInterface(ServiceManager.getService("user"));
        mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
        if (mPm == null) {
            System.err.println(PM_NOT_RUNNING_ERR);
            return 1;
        }
        mInstaller = mPm.getPackageInstaller();

        mArgs = args;
        String op = args[0];
        mNextArg = 1;

        if ("list".equals(op)) {
            return runList();
        }

        if ("path".equals(op)) {
            return runPath();
        }

        if ("dump".equals(op)) {
            return runDump();
        }

        if ("install".equals(op)) { //安装
            return runInstall();
        }

        if ("install-create".equals(op)) {
            return runInstallCreate();
        }

        if ("install-write".equals(op)) {
            return runInstallWrite();
        }

        if ("install-commit".equals(op)) {
            return runInstallCommit();
        }

        if ("install-abandon".equals(op) || "install-destroy".equals(op)) {
            return runInstallAbandon();
        }

        if ("set-installer".equals(op)) {
            return runSetInstaller();
        }

        if ("uninstall".equals(op)) { //卸载
            return runUninstall();
        }

        if ("clear".equals(op)) {
            return runClear();
        }

        if ("enable".equals(op)) {
            return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
        }

        if ("disable".equals(op)) {
            return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED);
        }

        if ("disable-user".equals(op)) {
            return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_USER);
        }

        if ("disable-until-used".equals(op)) {
            return runSetEnabledSetting(PackageManager.COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED);
        }

        if ("hide".equals(op)) {
            return runSetHiddenSetting(true);
        }

        if ("unhide".equals(op)) {
            return runSetHiddenSetting(false);
        }

        if ("grant".equals(op)) {
            return runGrantRevokePermission(true);
        }

        if ("revoke".equals(op)) {
            return runGrantRevokePermission(false);
        }

        if ("set-permission-enforced".equals(op)) {
            return runSetPermissionEnforced();
        }

        if ("set-install-location".equals(op)) {
            return runSetInstallLocation();
        }

        if ("get-install-location".equals(op)) {
            return runGetInstallLocation();
        }

        if ("trim-caches".equals(op)) {
            return runTrimCaches();
        }

        if ("create-user".equals(op)) {
            return runCreateUser();
        }

        if ("remove-user".equals(op)) {
            return runRemoveUser();
        }

        if ("get-max-users".equals(op)) {
            return runGetMaxUsers();
        }

        if ("force-dex-opt".equals(op)) {
            return runForceDexOpt();
        }

        try {
            if (args.length == 1) {
                if (args[0].equalsIgnoreCase("-l")) {
                    validCommand = true;
                    return runListPackages(false);
                } else if (args[0].equalsIgnoreCase("-lf")){
                    validCommand = true;
                    return runListPackages(true);
                }
            } else if (args.length == 2) {
                if (args[0].equalsIgnoreCase("-p")) {
                    validCommand = true;
                    return displayPackageFilePath(args[1]);
                }
            }
            return 1;
        } finally {
            if (validCommand == false) {
                if (op != null) {
                    System.err.println("Error: unknown command '" + op + "'");
                }
                showUsage();
            }
        }
    }

7、在runInstall()方法中,主要是调用了该方法

mPm = IPackageManager.Stub.asInterface(ServiceManager.getService("package"));
//实际调用的是PackageManagerService中的installPackageAsUser()方法
mPm.installPackageAsUser(apkFilePath, obs.getBinder(), installFlags,
                    installerPackageName, verificationParams, abi, userId);

8、实际调用的是PackageManagerService中的installPackageAsUser()方法

/**
     * 1、originPath:代表应用程序文件的路径。这是一个字符串类型的参数,表示应用程序安装包的位置。
     *
     * 2、observer:代表应用程序安装的观察者。它是IPackageInstallObserver2接口的一个实例,通过观察者模式来监听安装过程的状态和结果。
     *
     * 3、installFlags:代表安装标志。这是一个整型参数,用于指定安装时的特定选项。例如,可以通过设置INSTALL_REPLACE_EXISTING标志来替换已存在的应用。
     *
     * 4、installerPackageName:代表安装程序的名称。这是一个字符串类型的参数,表示执行安装的应用程序的包名。
     *
     * 5、userId:代表要安装应用的用户ID。这是一个整型参数,用于指定要安装应用的用户。在多用户系统中,每个用户拥有自己的应用安装目录。
     */
@Override
public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
            int installFlags, String installerPackageName, int userId)

9.最后通过Handler发送消息,最后执行到startCopy()方法

final boolean startCopy() {
            boolean res;
            try {
                if (DEBUG_INSTALL) Slog.i(TAG, "startCopy " + mUser + ": " + this);

                if (++mRetries > MAX_RETRIES) { //安装次数大于4次,安装失败
                    Slog.w(TAG, "Failed to invoke remote methods on default container service. Giving up");
                    mHandler.sendEmptyMessage(MCS_GIVE_UP);
                    handleServiceError();
                    return false;
                } else {
                    handleStartCopy();
                    res = true;
                }
            } catch (RemoteException e) {
                if (DEBUG_INSTALL) Slog.i(TAG, "Posting install MCS_RECONNECT");
                mHandler.sendEmptyMessage(MCS_RECONNECT);
                res = false;
            }
            handleReturnCode();
            return res;
        }

9、最后执行安装操作的方法handleReturnCode()

		@Override
        void handleReturnCode() {
            if (mObserver != null) {
                try {
                    mObserver.onGetStatsCompleted(mStats, mSuccess);
                } catch (RemoteException e) {
                    Slog.i(TAG, "Observer no longer exists.");
                }
            }
        }

APK无界面安装流程图
在这里插入图片描述
APK安装原理如下:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/751570.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

城市餐饮油烟的监测与治理

摘要&#xff1a;为控制餐饮油烟污染&#xff0c;改善城市大气污染和生态环境&#xff0c;针对城市餐饮油烟污染现状&#xff0c;提出相应的治理政策。加快餐饮油烟污染立法进度&#xff0c;推进相关法律法规修订&#xff0c;加大油烟污染执法力度&#xff1b;维护清洗油烟净化…

windows安装使用 tesseract-ocr

OCR&#xff08;Optical character recognition&#xff0c;光学字符识别&#xff09;是一种将图像中的手写字或者印刷文本转换为机器编码文本的技术。 tesseract-ocr 是由Google开发&#xff0c;支持100多种语言 文档 tessdoc&#xff1a; https://tesseract-ocr.github.io…

3Ds max创建闪烁的星星效果

在这个简单的教程中&#xff0c;您将学习如何通过几个步骤创建闪烁的星星效果。 推荐&#xff1a; NSDT场景编辑器助你快速搭建可二次开发的3D应用场景 步骤-1 在顶视图中创建球体 步骤-2 应用星形材料。 步骤-3 复制球体并稍微调整其大小&#xff08;两个球体必须完全相同&…

Ubuntu 的安装及其设置

文章目录 安装 Ubuntu屏幕分辨率设置修改软件源服务器锁屏时间设置设置 dash跨系统拖拽复制文件的设置 安装 Ubuntu 首先安装 VMware 虚拟机&#xff0c;虚拟机的安装比较简单&#xff0c;一步步点击Next即可完成安装。 安装完成后启动虚拟机&#xff0c;点击创建新的虚拟机。…

modelscope本地模型使用教程

阿里魔塔社区modelscope&#xff08;https://modelscope.cn/home&#xff09; 如果使用过模型&#xff0c;那么模型文件默认缓存地址&#xff1a;C:\Users\Administrator.cache\modelscope\hub 魔塔社区本地使用&#xff1a; 1、安装python环境&#xff1a;使用miniconda&…

Apache Knox Gateway

简介&#xff1a; Knox是一个提供认证和访问集群中hadoop服务的单个端点服务。目标是为用户和操作者简化hadoop安全。knox运行为一个服务或者集群服务&#xff0c;并提供集中访问一个或者多个hadoop集群。通常网关的目标如下&#xff1a; 1、为hadoop rest api 提供外层的安全…

实战攻防之积极防御体系建设 | 中睿天下受邀参与诸子云沙龙

7月8日&#xff0c;中睿天下受邀参与由诸子云举办的“网络与数据安全”主题沙龙&#xff0c;中睿天下技术经理徐丹丹就《实战攻防之积极防御体系建设》这一主题进行了分享交流。 本次沙龙由南京分会会长宋士明主持&#xff0c;活动邀请到BASF、江苏省联社、华泰证券、宁证期货、…

微软浏览器连不上网络

针对微软浏览器连不上网络&#xff0c;但其他浏览器仍能连上网络 控制面板 -> 网络和Internet -> Internet 选项 -> 连接 -> 局域网设置 -> 取消代理服务器

理清ROS通信的一些细节

目标&#xff1a;掌握ros的python编程 基本教程&#xff1a;https://www.bilibili.com/video/BV1sU4y1z7mw/?spm_id_from333.788&vd_source32148098d54c83926572ec0bab6a3b1d terminator 快捷键需要自己去重新启用 ctrlshifte 横向分屏 ctrlshifto 纵向分屏 ctrlshiftw …

前端AES加密,后端解密,有效防止数据外泄

在工作中经常遇到密码明文传输这个问题&#xff0c;为了让密码安全些会让加密&#xff0c;现在有个比较方便的AES加密&#xff08;前端密钥可能存在泄露风险&#xff0c;应该放到配置项中&#xff09;&#xff1a; 一、前端加密 1、首先引入前端需要用到的js&#xff1a;crypt…

CocoaPods私有库的创建

第一步&#xff1a;为了方便寻找&#xff0c;我先cd 桌面路径 /Users/xxx/Desktop 第二步 &#xff1a;创建私有库的名字 pod lib creat KMWaterMark 创建之后会有个文件夹&#xff0c;打开文件夹将自己的工具类放到classes文件夹里面&#xff0c;然后在在Example中打开自己的…

【通过迭代相位检索重建衍射图案和全息图中缺失信息】不完整衍射图案的迭代重建和缺失像素的恢复(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

【编译之美】【1. JS闭包问题】

什么是闭包 在 JavaScript 和 Python 等语言里&#xff0c;函数可以像数值一样使用&#xff0c;比如给变量赋值、作为参数传递给其他函数&#xff0c;作为函数返回值等等。比如下面这一段代码&#xff1a; var a 0;var fun1 function(){var b 0; // 函数内…

UE4/5c++基于BlueprintAsyncActionBase和FTickableGameObject的异步节点【多流程的创建、异步执行】

目录 简单的多流程制作 取消除流程外&#xff0c;原本的蓝图执行线【如果要进行异步执行可以不取消看效果】 结果 代码&#xff1a; 继承FTickableGameObject&#xff0c;时刻判断是否结束&#xff0c;来执行不同引脚 结果&#xff1a; 代码&#xff1a; 在之前笔者讲解…

数据结构(王道)——顺序表的基本操作(插入、删除)

顺序表之实现插入&#xff1a; 插入的基础实现&#xff1a; 更加有健壮性的插入 插入实现的时间复杂度分析&#xff1a; 顺序表之实现删除&#xff1a; 删除的实现 删除实现的时间复杂度分析&#xff1a; 总结&#xff1a;

Ubuntu18.04 系统安装 Docker

1、首先更新软件源&#xff1a; sudo apt-get updatesudo apt-get upgrade 2、安装Docker&#xff1a; sudo apt install docker -y 3、查看安装的Docker apt list docker 4、查看docker 进程 ps -ef|grep docker 5、查看docker 版本有问题 6、开启Docker服务 systemctl…

​LeetCode解法汇总931. 下降路径最小和

目录链接&#xff1a; 力扣编程题-解法汇总_分享记录-CSDN博客 GitHub同步刷题项目&#xff1a; https://github.com/September26/java-algorithms 原题链接&#xff1a;力扣 描述&#xff1a; 给你一个 n x n 的 方形 整数数组 matrix &#xff0c;请你找出并返回通过 matr…

企业视角下的个人信息保护影响评估具体实施

个人信息保护影响评估&#xff08;PIA&#xff09;是指企业对个人信息的收集、存储、使用、加工、传输、提供、公开、删除等贯穿数据全生命周期活动的检验&#xff0c;判断活动合规程度、保护措施的有效程度以及对个人信息主体合法权益造成损害的风险。个人信息保护影响评估旨在…

力扣 55. 跳跃游戏

题目来源&#xff1a;https://leetcode.cn/problems/jump-game/description/ C题解&#xff08;来源代码随想录&#xff09;&#xff1a;不断更新可覆盖范围&#xff0c;能达到最后一个元素即返回true&#xff0c;否则返回false。 class Solution { public:bool canJump(vecto…

【Github】推荐一个简介好用的导航站项目

导航在我们的生活中具有重要意义——它为我们提供方向和指引&#xff0c;赋予我们力量和信心&#xff0c;开阔我们的胸膛。在数字时代&#xff0c;我们也需要一种轻量级的导航方式&#xff0c;能够整合各种服务&#xff0c;提供便捷的访问途径。 正是基于这样的需求&#xff0…