安卓应用安装过程学习

声明:此文章来自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也都是通过客户端-服务端的方式,向服务端发送命令进行安装的,也是四种安装方式的共同点。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Java Springboot医疗垃圾分类系统

一、作品包含 源码数据库全套环境和工具资源部署教程 二、项目技术 前端技术&#xff1a;Html、Css、Js、Vue、Element-ui 数据库&#xff1a;MySQL 后端技术&#xff1a;Java、Spring Boot、MyBatis 三、运行环境 开发工具&#xff1a;IDEA/eclipse 数据库&#xff1a;…

SQL99版全外连接和交叉连接和总结

全外连接MySQL不支持 elect 查询列表 from 表名1 表别名1 cross join 表名2 表别名2 on 连接条件 ...... ; 交叉连接 就两个记录做笛卡尔积&#xff01;没什么好说的&#xff0c;基本也没用过&#xff01; 总结

推荐一款开源电子书阅读器Koodo Reader

Koodo Reader 是一个开源的电子书阅读器&#xff0c;支持多达15种主流电子书格式&#xff0c; 内置笔记、高亮、翻译功能&#xff0c;助力高效书籍阅读和学习。 官网地址&#xff1a;https://www.koodoreader.com/zh 一、下载软件 下载地址&#xff1a;https://dl.koodoreader.…

WebStorm 2024.3/IntelliJ IDEA 2024.3出现elementUI提示未知 HTML 标记、组件引用爆红等问题处理

WebStorm 2024.3/IntelliJ IDEA 2024.3出现elementUI提示未知 HTML 标记、组件引用爆红等问题处理 1. 标题识别elementUI组件爆红 这个原因是&#xff1a; 在官网说明里&#xff0c;才版本2024.1开始&#xff0c;默认启用的 Vue Language Server&#xff0c;但是在 Vue 2 项目…

Harbor2.11.1生成自签证和配置HTTPS访问

文章目录 HTTPS的工作流程部署Harbor可参考上一篇文章生成自签证书1.修改/etc/hosts文件2.生成证书a.创建存放证书路径b.创建ca.key密钥c.创建ca.crtd.创建给Harbor服务器使用密钥 yunzhidong.harbor.com.keye.创建给Harbor服务器使用证书签名请求文件 yunzhidong.harbor.com.c…

【深度学习之二】正则化函数(weight decay, dropout, label smoothing, and etc)详解,以及不同的函数适用的场景

在深度学习中正则化函数的重要性不言而喻&#xff0c;今天主要总结一些当前常用的一些正则化函数 在深度学习中&#xff0c;正则化&#xff08;Regularization&#xff09;是一种防止模型过拟合的技术。过拟合指的是模型在训练数据上表现很好&#xff0c;但在未见过的测试数据…

uni-app 修改复选框checkbox选中后背景和字体颜色

编写css&#xff08;注意&#xff1a;这个样式必须写在App.vue里&#xff09; /* 复选框 */ /* 复选框-圆角 */ checkbox.checkbox-round .wx-checkbox-input, checkbox.checkbox-round .uni-checkbox-input {border-radius: 100rpx; } /* 复选框-背景颜色 */ checkbox.checkb…

Ngrok实现内网穿透(Windows)

Ngrok实现内网穿透&#xff08;Windows&#xff09; 什么是内网穿透&#xff0c;内网穿透有什么用 内网穿透&#xff08;NAT traversal&#xff09;是一种技术手段&#xff0c;使得位于内网或防火墙后面的设备能够通过外网访问。例如&#xff0c;如果你的计算机、服务器等设备…

Simulink中Model模块的模型保护功能

在开发工作过程中&#xff0c;用户为想要知道供应商的开发能力&#xff0c;想要供应商的模型进行测试。面对如此要求&#xff0c;为了能够尽快拿到定点项目&#xff0c;供应商会选择一小块算法或是模型以黑盒的形式供客户测试。Simulink的Model模块除了具有模块引用的功能之外&…

Linux内核USB2.0驱动框架分析--USB包

一&#xff0c; 包的组成 每个包都由SOP&#xff08;包起始域&#xff09;、SYNC&#xff08;同步域&#xff09;、Packet Content&#xff08;包内容&#xff09;、EOP&#xff08;包结束域&#xff09;四部分组成&#xff0c;其中SOP、SYNC、EOP为所有包共有的域&#xff0c…

STM32F4----ADC模拟量转换成数字量

STM32F4----ADC模拟量转换成数字量 基本原理 当需要测量和记录外部电压的变化&#xff0c;或者根据外部电压的变化量来决定是否触发某个动作时&#xff0c;我们可以使用ADC&#xff08;模拟—数字转换器&#xff09;功能。这个功能可以将模拟的电压信号转换为数字信号&#x…

大数据学习18之Spark-SQL

1.概述 1.1.简介 Spark SQL 是 Apache Spark 用于处理结构化数据的模块。 1.2.历史 1.2.1.Shark Hadoop诞生初期&#xff0c;Hive是唯一在Hadoop上运行的SQL-on-Hadoop工具&#xff0c;MR的中间计算过程产生了大量的磁盘落地操作&#xff0c;消耗了大量的I/O&#xff0c;降低…

医学AI公开课·第一期|Machine LearningTransformers in Med AI

小罗碎碎念 从这周开始&#xff0c;我计划每个周末录一个视频&#xff0c;分享一些医学人工智能领域的进展。 作为第一期视频&#xff0c;我打算介绍一下机器学习和Transformer在医学AI领域中的应用。 为了准备这期视频&#xff0c;总共做了24页PPT&#xff08;三部分内容&…

小白投资理财 - 解读威廉指标 WR

小白投资理财 - 解读威廉指标 WR WR 指标WR 指标特点WR 指标解读WR 与其他指标的结合实战案例&#xff1a;WR 计算WR 的优缺点WR 和 Williams Fractals 的主要区别总结 上篇《小白投资理财 - 解读威廉分形指标 Williams Fractals》&#xff0c;今天我们来了解另外一个威廉指标 …

前端速通(HTML)

1. HTML HTML基础&#xff1a; 什么是HTML&#xff1f; 超文本&#xff1a; "超文本"是指通过链接连接不同网页或资源的能力。HTML支持通过<a>标签创建超链接&#xff0c;方便用户从一个页面跳转到另一个页面。 标记语言&#xff1a; HTML使用一组预定义的标签…

电商一件发货软件闲管家使用教程

闲鱼闲管家是一款专为闲鱼卖家设计的电脑版工作台&#xff0c;旨在帮助卖家更高效地管理其在闲鱼平台上的业务。以下是关于闲鱼闲管家的一些主要特点和功能&#xff1a; 主要特点&#xff1a; 多账号管理&#xff1a;支持同时管理多达30个闲鱼账号&#xff0c;方便大型卖家或…

第一个autogen与docker项目

前提条件&#xff1a;在windows上安装docker 代码如下&#xff1a; import os import autogen from autogen import AssistantAgent, UserProxyAgentllm_config {"config_list": [{"model": "GLM-4-Plus","api_key": "your api…

JavaEE 【知识改变命运】02 多线程(1)

文章目录 线程是什么&#xff1f;1.1概念1.1.1 线程是什么&#xff1f;1.1.2 为什么要有线程1.1.3 进程和线程的区别1.1.4 思考&#xff1a;执行一个任务&#xff0c;是不是创建的线程或者越多是不是越好&#xff1f;&#xff08;比如吃包子比赛&#xff09;1.1.5 ) Java 的线程…

LeetCode 力扣 热题 100道(八)相交链表(C++)

给你两个单链表的头节点 headA 和 headB &#xff0c;请你找出并返回两个单链表相交的起始节点。如果两个链表不存在相交节点&#xff0c;返回 null 。 图示两个链表在节点 c1 开始相交&#xff1a; 题目数据 保证 整个链式结构中不存在环。 注意&#xff0c;函数返回结果后&…

全面解析 JMeter 后置处理器:概念、工作原理与应用场景

在性能测试中&#xff0c;Apache JMeter是一个非常流行的工具&#xff0c;它不仅能够模拟大量用户进行并发访问&#xff0c;还提供了丰富的扩展机制来满足各种复杂的测试需求。后置处理器&#xff08;Post-Processor&#xff09;是JMeter中非常重要的组件之一&#xff0c;用于在…