Android之PackageManagerService构造函数部分分析

时间:2021-12-07 11:29:07

1.Settings
在PackageManagerService构造方法中初始化如下:

mSettings = new Settings(context);

Settings构造方法如下:

    Settings(Context context) {
this(context, Environment.getDataDirectory());
}

Settings(Context context, File dataDir) {
mSystemDir = new File(dataDir, "system");
mSystemDir.mkdirs();
FileUtils.setPermissions(mSystemDir.toString(),
FileUtils.S_IRWXU|FileUtils.S_IRWXG
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
mSettingsFilename = new File(mSystemDir, "packages.xml");
mBackupSettingsFilename = new File(mSystemDir, "packages-backup.xml");
mPackageListFilename = new File(mSystemDir, "packages.list");
FileUtils.setPermissions(mPackageListFilename, 0660, SYSTEM_UID, PACKAGE_INFO_GID);

// Deprecated: Needed for migration
mStoppedPackagesFilename = new File(mSystemDir, "packages-stopped.xml");
mBackupStoppedPackagesFilename = new File(mSystemDir, "packages-stopped-backup.xml");
}

构造函数中知识创建了系统目录system, 设置文件packages.xml,设置备份文件packages-backup.xml,应用列表文件packages.list等.
下面是:

        mSettings.addSharedUserLPw(
"android.uid.system", //字符串
Process.SYSTEM_UID, //系统进程使用的用户id,值为1000
ApplicationInfo.FLAG_SYSTEM|ApplicationInfo.FLAG_PRIVILEGED); //标识系统package

来看一下addSharedUserLPw函数:

    SharedUserSetting addSharedUserLPw(String name, int uid, int pkgFlags) {
SharedUserSetting s = mSharedUsers.get(name);
if (s != null) {
if (s.userId == uid) {
return s;
}
PackageManagerService.reportSettingsProblem(Log.ERROR,
"Adding duplicate shared user, keeping first: " + name);
return null;
}
s = new SharedUserSetting(name, pkgFlags);
s.userId = uid;
if (addUserIdLPw(uid, s, name)) {
mSharedUsers.put(name, s);
return s;
}
return null;
}

mSharedUsers是一个HashMap,key为字符串,值为SharedUserSetting对象 HashMap<String, SharedUserSetting> mSharedUsers
SharedUserSetting类的成员变量如下,此类的作用就是存储特定共享用户ID的设置数据:

    final String name;
int userId;
// 与该uid有关的任何flags
int uidFlags;
final HashSet<PackageSetting> packages = new HashSet<PackageSetting>();

final PackageSignatures signatures = new PackageSignatures();

在androidManifest.xml文件中声明sharedUserId属性如下:

android:sharedUserId="android.uid.system"

其作用如下:
1)两个或多个生命了同一种sharedUserId的APK可共享彼此的数据,并且可运行在同一个进程中.
2)更重要的是,通过声明特定的sharedUserId,该APK所在进程将被赋予指定的UID.例如,上面应用system的uid,运行该进程的就可享有system用户所有对应的权限(实际上就是将该进程的uid设置为system的uid).

2.SystemConfig
在PKMS初始化的过程中初始化SystemConfig,方法如下:

    public static SystemConfig getInstance() {
synchronized (SystemConfig.class) {
if (sInstance == null) {
sInstance = new SystemConfig();
}
return sInstance;
}
}

SystemConfig的构造方法如下:

    SystemConfig() {
// 从系统中读取配置文件
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig")
, false)
;
// Read configuration from the old permissions dir
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions")
, false)
;
// Only read features from OEM config
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig")
, true)
;
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "permissions")
, true)
;
}

从注释可以看出初始化的过程就是读取配置文件的过程,那么看一下readPermissions函数,readPermissions函数最终调用readPermissionsFromXML函数,读取各xml文件中的配置信息,如下:

    private void readPermissionsFromXml(File permFile, boolean onlyFeatures) {
FileReader permReader = null;
try {
permReader = new FileReader(permFile);
} catch (FileNotFoundException e) {
Slog.w(TAG, "Couldn't find or open permissions file " + permFile);
return;
}

try {
XmlPullParser parser = Xml.newPullParser();
parser.setInput(permReader);

int type;
while ((type=parser.next()) != parser.START_TAG
&& type != parser.END_DOCUMENT) {
;
}

if (type != parser.START_TAG) {
throw new XmlPullParserException("No start tag found");
}

if (!parser.getName().equals("permissions") && !parser.getName().equals("config")) {
throw new XmlPullParserException("Unexpected start tag: found " + parser.getName() +
", expected 'permissions' or 'config'");
}

while (true) {
XmlUtils.nextElement(parser);
if (parser.getEventType() == XmlPullParser.END_DOCUMENT) {
break;
}

String name = parser.getName();
if ("group".equals(name) && !onlyFeatures) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = android.os.Process.getGidForName(gidStr);
mGlobalGids = appendInt(mGlobalGids, gid);
} else {
Slog.w(TAG, "<group> without gid at "
+ parser.getPositionDescription());
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("permission".equals(name) && !onlyFeatures) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<permission> without name at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
perm = perm.intern();
readPermission(parser, perm);

} else if ("assign-permission".equals(name) && !onlyFeatures) {
String perm = parser.getAttributeValue(null, "name");
if (perm == null) {
Slog.w(TAG, "<assign-permission> without name at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
String uidStr = parser.getAttributeValue(null, "uid");
if (uidStr == null) {
Slog.w(TAG, "<assign-permission> without uid at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
int uid = Process.getUidForName(uidStr);
if (uid < 0) {
Slog.w(TAG, "<assign-permission> with unknown uid \""
+ uidStr + "\" at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
.......

从代码中看出,从xml文件解析出来的字段有:group,permission,assign-permission,library,feature,allow-in-power-save,fixed-ime-app等字段,这些字段分别保存到一下数组或者列表中:

     //从etc/permission/*.xml文件中读取给定的Group-id
int[] mGlobalGids;
//从系统文件中读取内置的uid-permission对
final SparseArray<HashSet<String>> mSystemPermissions = new SparseArray<>();

// 从系统配置文件中读取内置的库,keys是库的名称,string是库的路径
final ArrayMap<String, String> mSharedLibraries = new ArrayMap<>();

// 从系统配置文件中读取的设备支持的特征
final HashMap<String, FeatureInfo> mAvailableFeatures = new HashMap<>();

// 从系统文件中读取内置的uid-permission对
final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>();

// 从配置文件中读取在省电模式下可以运行的packages的白名单
final ArraySet<String> mAllowInPowerSave = new ArraySet<>();

// 不允许进行输入法切换的应用名称列表
final ArraySet<String> mFixedImeApps = new ArraySet<>();

然后通过get方法提供给其它类使用.

3.此后就是扫描系统pkg和非系统package,进行安装工作.最后收集信息,将信息保存到文件中