Android 进阶——系统启动之SystemServer创建并启动PackageManagerService服务

Posted CrazyMo_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android 进阶——系统启动之SystemServer创建并启动PackageManagerService服务相关的知识,希望对你有一定的参考价值。

文章大纲

引言

前一篇文章Android 进阶——系统启动之SystemServer创建并启动PackageManagerService服务(十一) 介绍了SystemServer创建并启动PackageManagerService服务的前两步,接下来是第三步。

一、PKMS 遍历安装系统目录下的App

android 中对于App的权限管理是在Linux权限基础上的,一般说来,每一个进程都会有一个对应的 UID(即表示该进程属于哪个 user,不同 user 有不同权限),一个进程也可分属不同的用户组(每个用户组都有对应的权限)。

  • UID——用户ID,USERID的缩写。
  • GID ——用户组 ID,USERGROUPID 的缩写

这两个概念均与 Linux 系统中进程的权限管理有关。

1、Apk的结构

APK本质上也是一个压缩类型的文件,由以下部分组成:

目录/文件说明
assert存放的原始资源文件不会被编译,通过Asset Manager类访问
res存放资源文件,res中除了raw子目录,其他的子目录都参与编译,这些子目录下的资源是通过编译出的R类在代码中访问。
META-INF保存应用的签名信息
lib存放库文件
Android Manifest. xml清单文件,用于声明应用程序的包名称、版本、组件和权限等数据,因为经过压缩,需要使用AXMLPrinter2工具解开。
classes. dexJava源码编译后生成的dex字节码文件(Android虚拟机定制化的字节码文件)
resources. arsc编译后的二进制资源文件,保存着资源和ID的映射关系。

2、PackageManagerService#main方法触发App 安装流程

  • 通过PackageManagerService构造对应的对象(Binder对象)

  • 把Binder对象注册到Java层 ServiceManager中,注册完毕之后系统中其他需要使用到PackageManagerService都可以通过ServiceManager#getService方法来来获取对应的Binder对象,Binder 服务的常规流程,主要你想让你的Binder的对象可以被ServiceManager 获取到就都可以注册到ServiceManager里。

    所谓注册到ServiceManager 里其实就是在ServiceManager里维护了一个Map对象,注册时传入第一个参数为key,第二个参数为binder对象,这就有点类似对象池的设计思想。

    public static PackageManagerService main(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) 
        // Self-check for initial settings.
        PackageManagerServiceCompilerMapping.checkProperties();
        PackageManagerService m = new PackageManagerService(context, installer,factoryTest, onlyCore);
        m.enableSystemUserPackages();
        ServiceManager.addService("package", m);
        return m;
    

一句话总结就是,创建PKMS对象并注册到ServiceManager Binder 服务管理中心。

3、构造PackageManagerService 对象

回到构造PMKS 对象方法内部,继续往下分析,涉及到以下的类

  • \\frameworks\\base\\services\\core\\java\\com\\android\\server\\pm\\PackageManagerService.java
  • \\frameworks\\base\\services\\core\\java\\com\\android\\server\\pm\\Settings.java

3.1、保存应用的安装和运行信息

Android 系统每次启动后都需要把原来安装过的App 全部还原回来(因为不可能每一次都执行全新的安装,这样会丢失掉用户信息),而且对于应用程序的用户ID来说,每一次重启后,有可能不尽相同,如果不还原回来可能会导致不可知的权限安全问题。因此PKMS 每次在安装完成应用之后,都会将其对应的信息保存起来,供下次还原使用,而这些信息就是保存到android.server.pm.Settings类里。

省略掉一些不重要的步骤:

3.1.1、android.server.pm.Settings

  final class Settings 
  		final ArrayMap<String, SharedUserSetting> mSharedUsers =new ArrayMap<String, SharedUserSetting>();
      private final ArrayList<Object> mUserIds = new ArrayList<Object>();
    	private final SparseArray<Object> mOtherUserIds =new SparseArray<Object>();
  		...

此Settings 非我们比较熟悉的系统设置中的Settings,这里的Settings 只是相当于个JavaBean对象。暂时先关注Settings 中的成员变量:

  • mUserIds——ArrayList 类型的 mUserIds保存已经分配给普通用户使用的UID(普通应用UID),如果第index 的值不为null则说明值为Process.FIRST_APPLICATION_UID+index的UID 已经被分配使用了。
  • mOtherUserIds——SparseArray类型的保存已经分配给特权用户使用的UID(系统应用的UID),可以 UID 为索引找到对应的 SharedUserSettings 对象。(并非直接把UID作为索引)
  • mSharedUsers ——Settings 的成员变量有一个类型为SharedUserSetting 的 mSharedUsers 该成员存储的是字符串与 SharedUserSetting 键值对。

SharedUserSetting (继承自 GrantedPermissions 类,SharedUserSetting 定义了一个成员变量 packages,类型为 HashSet,用于保存声明了相同 sharedUserId 的 Package 的权限设置信息和我们在清单文件里android:sharedUserId,每个 Package 有自己的权限设置 PackageSetting (继承自PackagesettingBase,而PackagesettingBase又继承自GrantedPermissions ) 关系密切,当解析到这个属性是说明

  • 同一种 sharedUserId 的 不同的App 可共享彼此的数据且也可运行在同一进程中。

  • 通过在清单文件中指定对应的 sharedUserId,可以使得 App 所在进程将被赋予指定的 UID。例如在应用清单里配置了android:sharedUserId="android.uid.system"属性,如果该App的签名也是使用platform级别的签名证书(即在其 Android.mk 中需要额外声明 LOCAL_CERTIFICATE := platform)时,相当于是把该进程的UID设置为system的UID,也就拥有了系统权限。

作用就在于在 Android 系统中,多个应用(package) 通过在清单设置 android:sharedUserId 属性可以运行在同一个进程,共享同一个 UID,拥有同样的权限

虽然保存在Setting 两个成员变量集合的对象都是Object,实际保存的对象要么是PackageSetting,要么是SharedUserSetting,区别在于前者锁描述的Linux UID是独立的;而后者是共享的。

3.1.2、创建Settings 对象备用,接着把系统App的部分信息封装到SharedUserSetting 对象保存到Settings中

而这里通过Setting#addSharedUserLPw方法把这些信息先缓存起来

//传入的形式是 "android.uid.system" 作为key  系统进程使用的用户id的值为 1000 包的标志
SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) 
    //mSharedUsers 是Settings的成员变量,本身是一个 ArrayMap,以name为key,SharedUserSetting 对象为值
    SharedUserSetting s = mSharedUsers.get(name);
    	...
    //创建一个新的 SharedUserSettings 对象,并设置的 userId 为 uid
    s = new SharedUserSetting(name, pkgFlags);
    s.userId = uid;
    if (addUserIdLPw(uid, s, name)) 
        mSharedUsers.put(name, s);//将name与s键值对添加到mSharedUsers中保存
        return s;
    


然后进入到Settings#addUserIdLPw 方法真正把信息存储并在系统中保留一个指定的Linux用户UID并在对应的位置存入SharedUserSetting

  • 应用的进程名称就是package 名称
  • 系统应用所在的进程的UID <10000,虽然小于10000 的Linux 用户ID不能单独做为UID 给普通应用使用,但是可以通过共享的方式被应用使用,比如说一个应用想要修改系统设置,那么需要在清单文件配置了android:sharedUserId="android.uid.system"
  • 普通应用的进程的UID>=10000
    private boolean addUserIdLPw(int uid, Object obj, Object name) 
        //应用所在进程的uid范围在 19999>UID>=10000
        if (uid > Process.LAST_APPLICATION_UID) 
            return false;
        
		 //	
        if (uid >= Process.FIRST_APPLICATION_UID) 
            int N = mUserIds.size();
            //索引=uid-10000
            final int index = uid - Process.FIRST_APPLICATION_UID;
            while (index >= N) 
                mUserIds.add(null);
                N++;
              //判断该索引位置的内容是否为空,为空才保存
            if (mUserIds.get(index) != null) 
                PackageManagerService.reportSettingsProblem(Log.ERROR,"Adding duplicate user id: " + uid+ " name=" + name);
                return false;
            //保存普通应用进程的 UID 
            mUserIds.set(index, obj);
         else 
            if (mOtherUserIds.get(uid) != null) 
                PackageManagerService.reportSettingsProblem(Log.ERROR,"Adding duplicate shared id: " + uid+ " name=" + name);
                return false;
            //保存系统应用进程的 UID 
            mOtherUserIds.put(uid, obj);
        
        return true;
    

PackageManagerService的构造方法:

 public PackageManagerService(Context context, Installer installer,
            boolean factoryTest, boolean onlyCore) 
        //创建屏幕显示信息对象
        mMetrics = new DisplayMetrics();
     	 // 初始化Settings对象备用,
        mSettings = new Settings(mPackages);
        //创建 SharedUserSetting 对象并添加到 Settings,把这些系统应用的uid 保存起来
        mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
        mSettings.addSharedUserLPw("android.uid.log", LOG_UID,
                ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);
       ...
        String separateProcesses = SystemProperties.get("debug.separate_processes");
        if (separateProcesses != null && separateProcesses.length() > 0) 
            if ("*".equals(separateProcesses)) 
                mDefParseFlags = PackageParser.PARSE_IGNORE_PROCESSES;
                mSeparateProcesses = null;
                Slog.w(TAG, "Running with debug.separate_processes: * (ALL)");
             else 
                mDefParseFlags = 0;
                mSeparateProcesses = separateProcesses.split(",");
                Slog.w(TAG, "Running with debug.separate_processes: "
                        + separateProcesses);
            
         else 
            mDefParseFlags = 0;
            mSeparateProcesses = null;
        

        mInstaller = installer;
        mPackageDexOptimizer = new PackageDexOptimizer(installer, mInstallLock, context,"*dexopt*");
        mMoveCallbacks = new MoveCallbacks(FgThread.get().getLooper());

        mOnPermissionChangeListeners = new OnPermissionChangeListeners(
                FgThread.get().getLooper());

        getDefaultDisplayMetrics(context, mMetrics);

未完待续…

以上是关于Android 进阶——系统启动之SystemServer创建并启动PackageManagerService服务的主要内容,如果未能解决你的问题,请参考以下文章

Android 进阶——系统启动之SystemServer创建并启动Installer服务

Android 进阶——系统启动之SystemServer创建并启动Installer服务

Android 进阶——系统启动之Framework 核心ActivitityManagerService服务启动

Android 进阶——系统启动之Framework 核心ActivitityManagerService服务启动

Android 进阶——系统启动之Framework 核心ActivitityManagerService服务启动

Android 进阶——系统启动之核心SystemServer进程启动详解