基于 Android6.0
package com.android.server;
import android.app.ActivityManager;
import android.content.pm.FeatureInfo;
import android.os.*;
import android.os.Process;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Slog;
import android.util.SparseArray;
import android.util.Xml;
import libcore.io.IoUtils;
import com.android.internal.util.XmlUtils;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import static com.android.internal.util.ArrayUtils.appendInt;
/**
* Loads global system configuration info.
*/
public class SystemConfig {
static final String TAG = "SystemConfig";
//采取了单例模式
static SystemConfig sInstance;
// Group-ids that are given to all packages as read from etc/permissions/*.xml.
///”group"节点信息
int[] mGlobalGids;
// These are the built-in uid -> permission mappings that were read from the
// system configuration files.
//"assign-permission"节点信息
final SparseArray<ArraySet<String>> mSystemPermissions = new SparseArray<>();
// These are the built-in shared libraries that were read from the
// system configuration files. Keys are the library names; strings are the
// paths to the libraries.
//“library"节点信息
final ArrayMap<String, String> mSharedLibraries = new ArrayMap<>();
// These are the features this devices supports that were read from the
// system configuration files.
//"feature"节点信息
final ArrayMap<String, FeatureInfo> mAvailableFeatures = new ArrayMap<>();
// These are the features which this device doesn't support; the OEM
// partition uses these to opt-out of features from the system image.
//"unavailable-feature"节点信息
final ArraySet<String> mUnavailableFeatures = new ArraySet<>();
public static final class PermissionEntry {
public final String name;
public int[] gids;
public boolean perUser;
PermissionEntry(String name, boolean perUser) {
this.name = name;
this.perUser = perUser;
}
}
// These are the permission -> gid mappings that were read from the
// system configuration files.
//"permission"节点信息
final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>();
// These are the packages that are white-listed to be able to run in the
// background while in power save mode (but not whitelisted from device idle modes),
// as read from the configuration files.
//"allow-in-power-save-except-idle"节点信息
final ArraySet<String> mAllowInPowerSaveExceptIdle = new ArraySet<>();
// These are the packages that are white-listed to be able to run in the
// background while in power save mode, as read from the configuration files.
//”allow-in-power-save"节点信息
final ArraySet<String> mAllowInPowerSave = new ArraySet<>();
// These are the app package names that should not allow IME switching.
//"fixed-ime-app"节点信息
final ArraySet<String> mFixedImeApps = new ArraySet<>();
// These are the package names of apps which should be in the 'always'
// URL-handling state upon factory reset.
//"app-link"节点信息
final ArraySet<String> mLinkedApps = new ArraySet<>();
//单例模式
public static SystemConfig getInstance() {
synchronized (SystemConfig.class) {
if (sInstance == null) {
sInstance = new SystemConfig();
}
return sInstance;
}
}
public int[] getGlobalGids() {
return mGlobalGids;
}
public SparseArray<ArraySet<String>> getSystemPermissions() {
return mSystemPermissions;
}
public ArrayMap<String, String> getSharedLibraries() {
return mSharedLibraries;
}
public ArrayMap<String, FeatureInfo> getAvailableFeatures() {
return mAvailableFeatures;
}
public ArrayMap<String, PermissionEntry> getPermissions() {
return mPermissions;
}
public ArraySet<String> getAllowInPowerSaveExceptIdle() {
return mAllowInPowerSaveExceptIdle;
}
public ArraySet<String> getAllowInPowerSave() {
return mAllowInPowerSave;
}
public ArraySet<String> getFixedImeApps() {
return mFixedImeApps;
}
public ArraySet<String> getLinkedApps() {
return mLinkedApps;
}
SystemConfig() {
// Read configuration from system
// /system/etc/sysconfig 在我的小米Note(Android 6.0.1)手机下没有这个目录
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "sysconfig"), false);
// Read configuration from the old permissions dir
// /system/etc/permissions
readPermissions(Environment.buildPath(
Environment.getRootDirectory(), "etc", "permissions"), false);
// Only read features from OEM config
// /oem/etc/sysconfig
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "sysconfig"), true);
// /oem/etc/permissions
readPermissions(Environment.buildPath(
Environment.getOemDirectory(), "etc", "permissions"), true);
}
// /system/etc/sysconfig-false
// /system/etc/permissions-false
// /oem/etc/systemconfig-true
// /oem/etc/permissions
void readPermissions(File libraryDir, boolean onlyFeatures) {
// Read permissions from given directory.
if (!libraryDir.exists() || !libraryDir.isDirectory()) {
if (!onlyFeatures) {
Slog.w(TAG, "No directory " + libraryDir + ", skipping");
}
return;
}
if (!libraryDir.canRead()) {
Slog.w(TAG, "Directory " + libraryDir + " cannot be read");
return;
}
// Iterate over the files in the directory and scan .xml files
//可以从下面的代码看出来,对于配置文件的处理都是同一个方法,但是将/system/etc/permissions/platform.xml是在其他配置文件处理完毕后才进行处理??为什么
File platformFile = null;
for (File f : libraryDir.listFiles()) {
// We'll read platform.xml last
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
platformFile = f;
continue;
}
if (!f.getPath().endsWith(".xml")) {
Slog.i(TAG, "Non-xml file " + f + " in " + libraryDir + " directory, ignoring");
continue;
}
if (!f.canRead()) {
Slog.w(TAG, "Permissions library file " + f + " cannot be read");
continue;
}
readPermissionsFromXml(f, onlyFeatures);
}
// Read platform permissions last so it will take precedence
if (platformFile != null) {
readPermissionsFromXml(platformFile, onlyFeatures);
}
}
//处理/system/etc/permissions下的 xml 配置文件,根据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;
}
final boolean lowRam = ActivityManager.isLowRamDeviceStatic();
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 in " + permFile
+ ": 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 in " + permFile + " 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 in " + permFile + " 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 in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
String uidStr = parser.getAttributeValue(null, "uid");
if (uidStr == null) {
Slog.w(TAG, "<assign-permission> without uid in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
int uid = Process.getUidForName(uidStr);
if (uid < 0) {
Slog.w(TAG, "<assign-permission> with unknown uid \""
+ uidStr + " in " + permFile + " at "
+ parser.getPositionDescription());
XmlUtils.skipCurrentTag(parser);
continue;
}
//intern()@String 在常量池中增加这个 String
perm = perm.intern();
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms == null) {
perms = new ArraySet<String>();
mSystemPermissions.put(uid, perms);
}
perms.add(perm);
XmlUtils.skipCurrentTag(parser);
} else if ("library".equals(name) && !onlyFeatures) {
String lname = parser.getAttributeValue(null, "name");
String lfile = parser.getAttributeValue(null, "file");
if (lname == null) {
Slog.w(TAG, "<library> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (lfile == null) {
Slog.w(TAG, "<library> without file in " + permFile + " at "
+ parser.getPositionDescription());
} else {
//Log.i(TAG, "Got library " + lname + " in " + lfile);
mSharedLibraries.put(lname, lfile);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("feature".equals(name)) {
String fname = parser.getAttributeValue(null, "name");
boolean allowed;
if (!lowRam) {
allowed = true;
} else {
String notLowRam = parser.getAttributeValue(null, "notLowRam");
allowed = !"true".equals(notLowRam);
}
if (fname == null) {
Slog.w(TAG, "<feature> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else if (allowed) {
//Log.i(TAG, "Got feature " + fname);
FeatureInfo fi = new FeatureInfo();
fi.name = fname;
mAvailableFeatures.put(fname, fi);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("unavailable-feature".equals(name)) {
String fname = parser.getAttributeValue(null, "name");
if (fname == null) {
Slog.w(TAG, "<unavailable-feature> without name in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mUnavailableFeatures.add(fname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("allow-in-power-save-except-idle".equals(name) && !onlyFeatures) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<allow-in-power-save-except-idle> without package in "
+ permFile + " at " + parser.getPositionDescription());
} else {
mAllowInPowerSaveExceptIdle.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("allow-in-power-save".equals(name) && !onlyFeatures) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<allow-in-power-save> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mAllowInPowerSave.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("fixed-ime-app".equals(name) && !onlyFeatures) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<fixed-ime-app> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mFixedImeApps.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
continue;
} else if ("app-link".equals(name)) {
String pkgname = parser.getAttributeValue(null, "package");
if (pkgname == null) {
Slog.w(TAG, "<app-link> without package in " + permFile + " at "
+ parser.getPositionDescription());
} else {
mLinkedApps.add(pkgname);
}
XmlUtils.skipCurrentTag(parser);
} else {
XmlUtils.skipCurrentTag(parser);
continue;
}
}
} catch (XmlPullParserException e) {
Slog.w(TAG, "Got exception parsing permissions.", e);
} catch (IOException e) {
Slog.w(TAG, "Got exception parsing permissions.", e);
} finally {
IoUtils.closeQuietly(permReader);
}
for (String fname : mUnavailableFeatures) {
if (mAvailableFeatures.remove(fname) != null) {
Slog.d(TAG, "Removed unavailable feature " + fname);
}
}
}
void readPermission(XmlPullParser parser, String name)
throws IOException, XmlPullParserException {
if (mPermissions.containsKey(name)) {
throw new IllegalStateException("Duplicate permission definition for " + name);
}
final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
final PermissionEntry perm = new PermissionEntry(name, perUser);
mPermissions.put(name, perm);
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG
|| type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if ("group".equals(tagName)) {
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
int gid = Process.getGidForName(gidStr);
perm.gids = appendInt(perm.gids, gid);
} else {
Slog.w(TAG, "<group> without gid at "
+ parser.getPositionDescription());
}
}
XmlUtils.skipCurrentTag(parser);
}
}
}
/system/etc/permissions/下的 xml 文件的 TAG一览
permission节点的解析
platform.xml的解析工作由SystemConfig完成,其解析<permission>的代码如下,以为例
<permission name="android.permission.BLUETOOTH_ADMIN" >
<group gid="net_bt_admin" />
</permission>
// These are the permission -> gid mappings that were read from the
// system configuration files.
final ArrayMap<String, PermissionEntry> mPermissions = new ArrayMap<>();
void readPermission(XmlPullParser parser, String name)
throws IOException, XmlPullParserException {
if (mPermissions.containsKey(name)) {
throw new IllegalStateException("Duplicate permission definition for " + name);
}
//platform.xml中都为 false
final boolean perUser = XmlUtils.readBooleanAttribute(parser, "perUser", false);
final PermissionEntry perm = new PermissionEntry(name, perUser);
//name="android.permission.BLUETOOTH_ADMIN"
mPermissions.put(name, perm);
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
&& (type != XmlPullParser.END_TAG
|| parser.getDepth() > outerDepth)) {
if (type == XmlPullParser.END_TAG
|| type == XmlPullParser.TEXT) {
continue;
}
String tagName = parser.getName();
if ("group".equals(tagName)) {
//gid=net_bt_admin
String gidStr = parser.getAttributeValue(null, "gid");
if (gidStr != null) {
//得到 gid=3001
//ps:可以从.../system/core/include/privateandroid_filesystem_config.h中得到
int gid = Process.getGidForName(gidStr);
perm.gids = appendInt(perm.gids, gid);
} else {
Slog.w(TAG, "<group> without gid at "
+ parser.getPositionDescription());
}
}
XmlUtils.skipCurrentTag(parser);
}
}
从上可以得到mPermissions表中,key为上层的权限,形如"android.permission.BLUETOOTH_ADMIN",value为改上层权限映射的 Linux 底层的 group id, 可以从Android权限中得到
小米 Note 6.0.1下 /system/etc/permiisions 文件 提取码: 3xiq
public ArrayMap<String, PermissionEntry> getPermissions() {
return mPermissions;
}
public static final class PermissionEntry {
public final String name;
public int[] gids;
public boolean perUser;
PermissionEntry(String name, boolean perUser) {
this.name = name;
this.perUser = perUser;
}
}
- android.permission.BLUETOOTH_ADMIN
- net_bt_admin
- 3001
- android.permission.BLUETOOTH
- net_bt
- 3002
- android.permission.BLUETOOTH_STACK
- net_bt_stack
- 3008
- android.permission.NET_TUNNELING
- vpn
- 1016
- android.permission.INTERNET
- inet
- 3003
- android.permission.READ_LOGS
- log
- 1007
- android.permission.WRITE_MEDIA_STORAGE
- media_rw/sdcard_rw
- 1023/1015
- android.permission.ACCESS_MTP
- mtp
- 1024
- android.permission.NET_ADMIN
- net_admin
- 3005
- android.permission.ACCESS_CACHE_FILESYSTEM
- cache
- 2001
- android.permission.DIAGNOSTIC
- input/diag
- 1004/2002
- android.permission.READ_NETWORK_USAGE_HISTORY
- net_bw_stats
- 3006
- android.permission.MODIFY_NETWORK_ACCOUNTING
- net_bw_acct
- 3007
- android.permission.LOOP_RADIO
- loop_radio
- 1030
- android.permission.MANAGE_VOICE_KEYPHRASES
- audio
- 1005
- android.permission.ACCESS_FM_RADIO
- media
- 1013
其他的解析也可以相应的分析,不再重复了
网友评论