安卓应用安装过程学习

news2024/11/24 15:18:43

声明:此文章来自http://shuwoom.com/?p=60的学习记录

启动式安装

public static final IPackageManager main(Context context, Installer installer,
    boolean factoryTest, boolean onlyCore) {
        PackageManagerService m = new PackageManagerService(context, installer, factoryTest, onlyCore);
        ServiceManager.addService("package", m);
        return m;
}

main函数中创建PackageManagerService服务对象,并把服务添加到ServiceManager中

ServiceManager是Android系统Binder进程通信机制的守护进程,一直运行在后台。它主要负责管理系统中的Binder对象。

应用程序在安装时涉及到如下几个重要目录:

system/app

系统应用程序的目录

data/app

用户程序安装的目录

data/data

存放应用程序数据的目录

data/dalvik-cache

存放的是经过优化的dex文件

PackageManagerService是Android系统的核心服务之一,在系统启动的时候由SystemServer组件负责启动起来。PackageManagerService用于管理系统中的所有安装包信息以及应用程序的安装和卸载,但是实际应用程序的安装卸载并不是由PackageManagerService亲自完成,而是通过socket通信,PackageManagerService来访问installd服务来实现应用程序的安装和卸载。

public class SystemServer {
    ...
    /**
     * Called to initialize native system services.
    */
    private static native void nativeInit();
    public static void main(String[] args) {
    ...
    System.loadLibrary("android_servers");

    Slog.i(TAG, "Entered the Android system server!");

    // Initialize native services.
    nativeInit();
    // This used to be its own separate thread, but now it is
    // just the loop we run on the main thread.
    ServerThread thr = new ServerThread();
    thr.initAndLoop();
    }
}

SystemServer中的ServerThread来启动PackageManagerService

class ServerThread {
    private static final String TAG = "SystemServer";
    ……
    ContentResolver mContentResolver;
    ……

    public void initAndLoop() {
        EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_SYSTEM_RUN,
        SystemClock.uptimeMillis());
        Looper.prepareMainLooper();

        ……
        boolean onlyCore = false;
        boolean firstBoot = false;
        ……
        try {
            Slog.i(TAG, "Display Manager");
            display = new DisplayManagerService(context, wmHandler);
            ServiceManager.addService(Context.DISPLAY_SERVICE, display, true);

            Slog.i(TAG, "Telephony Registry");
            telephonyRegistry = new TelephonyRegistry(context);
            ServiceManager.addService("telephony.registry", telephonyRegistry);

            Slog.i(TAG, "Scheduling Policy");
            ServiceManager.addService("scheduling_policy", new SchedulingPolicyService());

            AttributeCache.init(context);

            if (!display.waitForDefaultDisplay()) {
                reportWtf("Timeout waiting for default display to be initialized.",
                        new Throwable());
            }

            Slog.i(TAG, "Package Manager");
            // 处于加密状态时,紧解析核心应用
            String cryptState = SystemProperties.get("vold.decrypt");
            if (ENCRYPTING_STATE.equals(cryptState)) {
                Slog.w(TAG, "Detected encryption in progress - only parsing core apps");
                onlyCore = true;
            } else if (ENCRYPTED_STATE.equals(cryptState)) {
                Slog.w(TAG, "Device encrypted - only parsing core apps");
                onlyCore = true;
            }

            pm = PackageManagerService.main(context, installer,
                    factoryTest != SystemServer.FACTORY_TEST_OFF,
                    onlyCore);
            
           ……
      }
}

PackageManagerService构造函数如下

public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) {
    synchronized (mInstallLock) {
        // writer
        synchronized (mPackages) {
            mHandlerThread.start();
            mHandler = new PackageHandler(mHandlerThread.getLooper());
            Watchdog.getInstance().addThread(mHandler, mHandlerThread.getName(),
                    WATCHDOG_TIMEOUT);

            File dataDir = Environment.getDataDirectory();
            mAppDataDir = new File(dataDir, "data");  
            mAppInstallDir = new File(dataDir, "app");  
            mAppLibInstallDir = new File(dataDir, "app-lib"); 
            mAsecInternalPath = new File(dataDir, "app-asec").getPath();  
            mUserAppDataDir = new File(dataDir, "user");  
            mDrmAppPrivateInstallDir = new File(dataDir, "app-private"); 
            ……
            mFrameworkDir = new File(Environment.getRootDirectory(), "framework");
            mDalvikCacheDir = new File(dataDir, "dalvik-cache");
            ……
           // Find base frameworks (resource packages without code).
            mFrameworkInstallObserver = new AppDirObserver(
                frameworkDir.getPath(), OBSERVER_EVENTS, true, false);
            mFrameworkInstallObserver.startWatching();
            //扫描”/system/framework”目录下的apk
            scanDirLI(frameworkDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR
                    | PackageParser.PARSE_IS_PRIVILEGED,
                    scanMode | SCAN_NO_DEX, 0);

            // Collected privileged system packages.
            File privilegedAppDir = new File(Environment.getRootDirectory(), "priv-app");
            mPrivilegedInstallObserver = new AppDirObserver(
                    privilegedAppDir.getPath(), OBSERVER_EVENTS, true, true);
            mPrivilegedInstallObserver.startWatching();
            //扫描”/system/priv-app”目录下的apk
            scanDirLI(privilegedAppDir, PackageParser.PARSE_IS_SYSTEM
                        | PackageParser.PARSE_IS_SYSTEM_DIR
                        | PackageParser.PARSE_IS_PRIVILEGED, scanMode, 0);

            // Collect ordinary system packages.
            File systemAppDir = new File(Environment.getRootDirectory(), "app"); 
            mSystemInstallObserver = new AppDirObserver(
                systemAppDir.getPath(), OBSERVER_EVENTS, true, false);
            mSystemInstallObserver.startWatching();
            //扫描”/system/app”目录下的apk
            scanDirLI(systemAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

            // Collect all vendor packages.
            File vendorAppDir = new File("/vendor/app");
            mVendorInstallObserver = new AppDirObserver(
                vendorAppDir.getPath(), OBSERVER_EVENTS, true, false);
            mVendorInstallObserver.startWatching();
            // vender目录其实连接到/system/vendor,实际扫描:/system/vendor/app目录下的apk
            scanDirLI(vendorAppDir, PackageParser.PARSE_IS_SYSTEM
                    | PackageParser.PARSE_IS_SYSTEM_DIR, scanMode, 0);

            if (DEBUG_UPGRADE) Log.v(TAG, "Running installd update commands");
            mInstaller.moveFiles();

            // Prune any system packages that no longer exist.
            final List<String> possiblyDeletedUpdatedSystemApps = new ArrayList<String>();
           //由上一部分内容知道,默认情况下磁盘加密状态关闭,即mOnlyCore状态false
           if (!mOnlyCore) {
                EventLog.writeEvent(EventLogTags.BOOT_PROGRESS_PMS_DATA_SCAN_START,
                        SystemClock.uptimeMillis());
                mAppInstallObserver = new AppDirObserver(
                    mAppInstallDir.getPath(), OBSERVER_EVENTS, false, false);
                mAppInstallObserver.startWatching();
                //扫描”/data/app”目录下的apk
                scanDirLI(mAppInstallDir, 0, scanMode, 0);
    
                mDrmAppInstallObserver = new AppDirObserver(
                    mDrmAppPrivateInstallDir.getPath(), OBSERVER_EVENTS, false, false);
                mDrmAppInstallObserver.startWatching();
                //扫描”/data/app-private”目录下的apk
                scanDirLI(mDrmAppPrivateInstallDir, PackageParser.PARSE_FORWARD_LOCK,
                        scanMode, 0);

                ……
            } 
        ……..
}

这里调用了很多scanDirLI,这个函数调用了scanPackageLI

private void scanDirLI(File dir, int flags, int scanMode, long currentTime) {
    String[] files = dir.list();
    ……

    int i;
    for (i=0; i<files.length; i++) {
        File file = new File(dir, files[i]);
        //只扫描apk后缀的文件
        if (!isPackageFilename(files[i])) {
            // Ignore entries which are not apk's
            continue;
        }
        
        PackageParser.Package pkg = scanPackageLI(file,
                flags|PackageParser.PARSE_MUST_BE_APK, scanMode, currentTime, null);
       ……
    }
}

scanPackageLI功能如下

private PackageParser.Package scanPackageLI(File scanFile,
    int parseFlags, int scanMode, long currentTime, UserHandle user) {
    ……
    String scanPath = scanFile.getPath();
    parseFlags |= mDefParseFlags;
    PackageParser pp = new PackageParser(scanPath);
    pp.setSeparateProcesses(mSeparateProcesses);
    pp.setOnlyCoreApps(mOnlyCore);
    //解析安装包
    final PackageParser.Package pkg = pp.parsePackage(scanFile, scanPath, mMetrics, parseFlags);
    ……
    //保存解析后的安装包
    PackageParser.Package scannedPkg = scanPackageLI(pkg, parseFlags, scanMode
            | SCAN_UPDATE_SIGNATURE, currentTime, user);
    ……
    return scannedPkg;
}

其调用了parsePackage用于获取apk中的资源对象res和对Androidmanifest.xml文件进行格式化,并调用另一个parsePackage函数进一步做解析,也是package类真正生成的地方

啥是package?

package类是安卓框架中用于表示一个应用程序包的结构和信息的类。它包含了应用程序的多个方面的信息,包括:

  • 包名:唯一标识应用程序的字符串,通常是反向域名格式(例如,com.example.myapp)。
  • 版本信息:包括应用程序的版本号和版本代码,帮助识别应用的更新和兼容性。

  • 权限:声明应用所需的权限,例如访问互联网、读取联系人等。

  • 组件:描述应用程序包含的组件,如活动(Activity)、服务(Service)、内容提供者(Content Provider)和广播接收器(Broadcast Receiver)。

保存了app的信息之后 调用createDataDirsLI进行真正的安装操作

private int createDataDirsLI(String packageName, int uid, String seinfo) {
    int[] users = sUserManager.getUserIds();
    int res = mInstaller.install(packageName, uid, uid, seinfo);
    if (res < 0) {
        return res;
    }
    for (int user : users) {
        if (user != 0) {
            res = mInstaller.createUserData(packageName,
                    UserHandle.getUid(user, uid), user);
            if (res < 0) {
                return res;
            }
        }
    }
    return res;
}

调用了install类的install方法 install调用execute execute调用transaction transaction进行客户端-服务端通信,首先判断客户端-服务端是否连接

然后调用writeCommand向服务端通过socket,向服务端发送指令

如果发送的指令是install 服务端就会调用do_install进行安装 do_install调用了commands.c文件的install函数执行安装

int install(const char *pkgname, uid_t uid, gid_t gid, const char *seinfo)
{
    char pkgdir[PKG_PATH_MAX];  //程序目录路径最长256
    char libsymlink[PKG_PATH_MAX];
    char applibdir[PKG_PATH_MAX];
    struct stat libStat;
    // 权限判断
    if ((uid < AID_SYSTEM) || (gid < AID_SYSTEM)) {
        ALOGE("invalid uid/gid: %d %d\n", uid, gid);
        return -1;
    }
    // 组合应用程序安装目录pkgdir=”/data/data/应用程序包名”
    if (create_pkg_path(pkgdir, pkgname, PKG_DIR_POSTFIX, 0)) {
        ALOGE("cannot create package path\n");
        return -1;
    }
    ……
    // 创建应用程序安装目录pkgdir=”/data/data/应用程序包名”
    if (mkdir(pkgdir, 0751) < 0) {
        ALOGE("cannot create dir '%s': %s\n", pkgdir, strerror(errno));
        return -1;
    }
    // 修改应用程序安装目录pkgdir=”/data/data/应用程序包名”权限
    if (chmod(pkgdir, 0751) < 0) {
        ALOGE("cannot chmod dir '%s': %s\n", pkgdir, strerror(errno));
        unlink(pkgdir);
        return -1;
    }
    ……
    return 0;
}

点击安装

ADB安装

商店安装

总结:实际上四种安装方式都有用到PackageManagerService,而PackageManagerService也都是通过客户端-服务端的方式,向服务端发送命令进行安装的,也是四种安装方式的共同点。

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

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

相关文章

如何通过OpenSSL来创建自签名的CA证书?

通过创建自签名CA证书可以让我们在没有商业支持的情况下学习与研究PKI&#xff08;公钥基础设施&#xff09;和SSL/TLS技术&#xff0c;本文将详细介绍如何通过OpenSSL来创建自签名的CA证书。 1. 初衷&#xff1a;为什么需要创建自签名CA证书&#xff1f; 除了开篇引言中提到的…

mac安装Pytest、Allure、brew

安装环境 安装pytest 命令 pip3 install pytest 安装allure 命令&#xff1a;brew install allure 好吧 那我们在安装allure之前 我们先安装brew 安装brew 去了官网复制了命令 还是无法下载 如果你们也和我一样可以用这个方法哦 使用国内的代码仓库来执行brew的安装脚本…

Python中“暂停”(time.sleep?input?)

input函数最是经典&#xff0c;在多种实现中简单粗暴单纯而经济。 (笔记模板由python脚本于2024年11月22日 10:58:38创建&#xff0c;本篇笔记适合比较熟悉python的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大…

刷题——【模板】二维前缀和

前缀和 题目题目链接题解方法一方法二 题目 描述 给你一个 n 行 m 列的矩阵 A &#xff0c;下标从1开始。 接下来有 q 次查询&#xff0c;每次查询输入 4 个参数 x1 , y1 , x2 , y2 请输出以 (x1, y1) 为左上角 , (x2,y2) 为右下角的子矩阵的和&#xff0c; 输入描述&#x…

10 - Clickhouse集群部署以及副本和分片

目 一、副本 1、简介 2、副本写入流程 3、副本配置步骤 3.1、启动zookeeper集群 3.2、在 hallo100 的/etc/clickhouse-server/config.d 目录下创建一个名为metrika.xml 的配置文件,内容如下&#xff1a; 3.3、在 hallo100 的/etc/clickhouse-server/config.xml 中增加如…

Ubuntu24.04LTS设置root用户可远程登录

Ubuntu24.04LTS设置root用户可远程登录 文章目录 Ubuntu24.04LTS设置root用户可远程登录1. 设置root密码2. 设置root用户可远程登录1. 查看ssh服务是否安装2. 安装ssh服务3. 再次查看ssh服务是否安装4. 配置ssh文件5. 重启ssh服务6. root远程登录 1. 设置root密码 Ubuntu安装后…

DMA理论篇

DMA理论篇 简介 传统的数据传输都是需要CPU来实现&#xff0c;从一个地方拷贝到另一个地方&#xff1b;而DMA(Direct Memory Access)则不完全依赖CPU&#xff0c;DMA更新芯片SOC的一个控制器&#xff0c;他可以控制数据从内存中传输到另一个地方(外设、soc其它模块)&#xff…

理解原子变量之三:原子性与memory_order_relaxed

目录 CPU与内存的关系 原子性 典型使用场景 在本系列的第一篇文章理解原子变量之一&#xff1a;从互斥锁到原子变量&#xff0c;最粗浅的认识_原子互斥-CSDN博客&#xff0c;我通过几个实例从感性认识的角度介绍了原子性。本文在第一篇文章的基础上&#xff0c;从理性认识的…

医院信息化与智能化系统(22)

医院信息化与智能化系统(22) 这里只描述对应过程&#xff0c;和可能遇到的问题及解决办法以及对应的参考链接&#xff0c;并不会直接每一步详细配置 如果你想通过文字描述或代码画流程图&#xff0c;可以试试PlantUML&#xff0c;告诉GPT你的文件结构&#xff0c;让他给你对应…

青少年编程等级考试C++一级,硬币反转问题

代码 #include<iostream>using namespace std;bool a[300];int main(){ int n,m; cin >> n >> m; for(int i 1;i < m;i) { for (int j 1;j < n;j) { if( j % i 0) { a[j] !a[j];…

数字化工厂 MES试点方案全解析(二)

生产过程监控与数据采集 在生产线上部署各类传感器、数据采集终端等设备&#xff0c;与 MES 系统相连&#xff0c;实时采集生产数据&#xff0c;如设备运行参数&#xff08;温度、压力、转速等&#xff09;、产品加工数据&#xff08;尺寸、重量、加工时间等&#xff09;、物料…

动态规划子数组系列一>最长湍流子数组

1.题目&#xff1a; 解析&#xff1a; 代码&#xff1a; public int maxTurbulenceSize(int[] arr) {int n arr.length;int[] f new int[n];int[] g new int[n];for(int i 0; i < n; i)f[i] g[i] 1;int ret 1;for(int i 1; i < n-1; i,m. l.kmddsfsdafsd){int…

(十一)Python字符串常用操作

一、访问字符串值 Python访问子字符串变量&#xff0c;可以使用方括号来截取字符串。与列表的索引一样&#xff0c;字符串索引从0开始。 hh"LaoTie 666" hh[2] mm"床前明月光" mm[3] 字符串的索引值可以为负值。若索引值为负数&#xff0c;则表示由字符…

Sigrity SPEED2000 DDR simulation模式如何生成和解读DDR仿真报告-SODIMM-Write模式

Sigrity SPEED2000 DDR simulation模式如何生成和解读DDR仿真报告-SODIMM-Write模式 Sigrity SPEED2000 DDR simulation模式如何进行DDR仿真分析操作指导-SODIMM-Write模式详细介绍了如何进行DDR Write模式的仿真分析,下面基于此仿真结果进行DDR报告的输出和解读分析 在workfl…

【图像检测】深度学习与传统算法的区别(识别逻辑、学习能力、泛化能力)

识别逻辑 深度学习 使用了端到端的学习策略&#xff0c;直接学习从图像到检测结果的映射关系&#xff0c;自动提取特征&#xff0c;并且根据特征与特征之间的关系&#xff0c;计算出检测结果。 传统算法 则是人工提取特征&#xff0c;比如边缘特征&#xff0c;直线特征&#x…

TypeScript 系统学习 章节3

想学习 TypeScript 的小伙伴看过来&#xff0c;本文将带你一步步学习 TypeScript 入门相关的十四个知识点&#xff0c;详细的内容大纲请看下图&#xff1a; 一、TypeScript 是什么 TypeScript 是一种由微软开发的自由和开源的编程语言。它是 JavaScript 的一个超集&#xff0c…

opencv undefined reference to `cv::noarray()‘ 。window系统配置opencv,找到opencv库,但连接不了

之前都是在ubuntu里用opencv&#xff0c;今天为了方便在平时用Window10系统也用下c版的cv&#xff0c;就想配置一下vscode的cv环境&#xff0c;直接下载了一个编译好的opencv库&#xff08;带build文件夹的&#xff09;&#xff0c;刚开始用的是visual studio的编译器&#xff…

利用FileZilla搭建ftp服务器

一 利用windows自带的ftp服务搭建服务器&#xff0c;要复杂一些&#xff0c;好处是无需借用外部软件。 也有一些好的工具&#xff0c;如FileZilla的Server版&#xff0c;构建过程简单&#xff0c;好用。 下面看看。 二 安装FileZilla Server 当前下载版本是0.9.43&#xf…

【刷题21】BFS解决FloodFill算法专题

目录 一、图像渲染二、岛屿数量三、岛屿的最大面积四、被环绕的区域 一、图像渲染 题目&#xff1a; 思路&#xff1a; 如果起始位置的颜色(数值)与color相同&#xff0c;直接返回该数组上下左右一层一层的找与当前位置颜色相同的&#xff0c;并且该位置不越界&#xff0c;然…

DICOM核心概念:显式 VR(Explicit VR)与隐式 VR(Implicit VR)在DICOM中的定义与区别

在DICOM&#xff08;Digital Imaging and Communications in Medicine&#xff09;标准中&#xff0c;VR&#xff08;Value Representation&#xff09; 表示数据元素的值的类型和格式。理解显式 VR&#xff08;Explicit VR&#xff09;与隐式 VR&#xff08;Implicit VR&#…