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. dex | Java源码编译后生成的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服务启动