Android 权限控制代码分析


前在文章介绍过android系统管理层次:http://blog.csdn.net/andyhuabing/article/details/7030212,这里就核心代码分析一下


android系统充分利用了linux的用户权限管理方法,所以如果需要移植到其它系统,这一块也是一个相当不小的工作量。那么android系统到底是如何使用这些的有利因素呢?


首先需要知道linux权限的两个基本知识:

1、一个用户可以属于多个组.

2、一个文件只能属于某个组。


这里主要是在AndroidManifest.xml中声明权限,主要是通过在AndroidManifest.xml中显示地声明应用程序需要的权限,防止应用程序错误的使用服务,不恰当访问资源。

Android中每种权限都用一个独立的标签表示.如:

<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

当在安装(Install)应用程序时,Android就会给予一个UID。这个UID可连结到该应用程序的AndroidManifest.xml档案的内容。所User在安装你的应用程序时,在屏幕上的窗口里可以检视这个AndroidManifest.xml档案的内容。在检视时,用户会看到你对应用程序的目的、权限等说明。当你接受这支程序的意图、权限说明之后,Android就安装它,并给它一个UID。万一在你的应用程序执行期间有越轨(企图做出非权限范围)的行为时,用户将会得到Android的警告讯息。

下面是两个安装程序安装时的界面

Android 权限控制代码分析_第1张图片Android 权限控制代码分析_第2张图片


这两个应用其实代码是一样的,唯一的不同就是它们的AndroidManifest.xml,图2AndroidManifest.xml中多了如下内容:

<uses-permissionandroid:name="android.permission.RECORD_AUDIO"/>

<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

即需要使用存储设备和录音设备,在安装的时候,就会提示用户它需要的权限。Android里面是怎么去控制的呢?


在安装apk的时候,会解析这个AndroidManifest.xml,把相应的信息保存起来。

代码路径:frameworks\base\core\java\android\content\pm\PackageParser.java

最终调用的是privatePackageparsePackage(

Resourcesres,XmlResourceParserparser,intflags,String[]outError)

这个函数,在里面对AndroidManifest.xml进行了解析,其中就有

private Package parsePackage(
Resources res, XmlResourceParser parser, int flags, String[] outError)
throws XmlPullParserException, IOException {

...

String tagName = parser.getName();
if (tagName.equals("application")) {
...
} else if (tagName.equals("permission-group")) {
if (parsePermissionGroup(pkg, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("permission")) {
if (parsePermission(pkg, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("permission-tree")) {
if (parsePermissionTree(pkg, res, parser, attrs, outError) == null) {
return null;
}
} else if (tagName.equals("uses-permission")) {
sa = res.obtainAttributes(attrs,
com.android.internal.R.styleable.AndroidManifestUsesPermission);

// Note: don't allow this value to be a reference to a resource
// that may change.
String name = sa.getNonResourceString(
com.android.internal.R.styleable.AndroidManifestUsesPermission_name);

sa.recycle();

if (name != null && !pkg.requestedPermissions.contains(name)) {
pkg.requestedPermissions.add(name.intern());
}

XmlUtils.skipCurrentTag(parser);
}

...

}


这里对它使用的权限进行了解析。

这里保存的都是"android.permission.WRITE_EXTERNAL_STORAGE"这样的字符串,在解析完后,会调用grantPermissionsLP函数获取对应的group_id

代码路径:frameworks\base\services\java\com\android\server\PackageManagerService.java

private void grantPermissionsLP(PackageParser.Package pkg, boolean replace) {
...
if (allowed) {
if (!gp.grantedPermissions.contains(perm)) {
changedPermission = true;
gp.grantedPermissions.add(perm);
gp.gids = appendInts(gp.gids, bp.gids);

} else if (!ps.haveGids) {
gp.gids = appendInts(gp.gids, bp.gids);
}
} else {
Slog.w(TAG, "Not granting permission " + perm
+ " to package " + pkg.packageName
+ " because it was previously installed without");
}

...
}

这里把相应的组都保存到了gids中。

当应用程序启动的过程中会调用

privatefinalvoidstartProcessLocked(ProcessRecordapp,

StringhostingType,StringhostingNameStr)

代码路径:frameworks\base\services\java\com\android\server\am\ActivityManagerService.java

private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr) {
...
try {
int uid = app.info.uid;
int[] gids = null;
try {
gids = mContext.getPackageManager().getPackageGids(
app.info.packageName);

} catch (PackageManager.NameNotFoundException e) {
Slog.w(TAG, "Unable to retrieve gids", e);
}

...
int pid = Process.start("android.app.ActivityThread",
mSimpleProcessManagement ? app.processName : null, uid, uid,
gids, debugFlags, null);

}


这里就是获取前面保存的gids,再后面调用创建了一个新的进程,这里传的参数就有gids

创建新进程利用jni最终调用 forkAndSpecializeCommon 函数 (路径:\dalvik\vm\native\dalvik_system_Zygote.c)


static pid_t forkAndSpecializeCommon(const u4* args, bool isSystemServer)
{
pid_t pid;


uid_t uid = (uid_t) args[0];
gid_t gid = (gid_t) args[1];
ArrayObject* gids = (ArrayObject *)args[2];
u4 debugFlags = args[3];
ArrayObject *rlimits = (ArrayObject *)args[4];
int64_t permittedCapabilities, effectiveCapabilities;
...
pid = fork();
if (pid == 0) {
int err;
/* The child process */

err = setgroupsIntarray(gids);

if (err < 0) {
LOGE("cannot setgroups(): %s", strerror(errno));
dvmAbort();
}

err = setrlimitsFromArray(rlimits);

if (err < 0) {
LOGE("cannot setrlimit(): %s", strerror(errno));
dvmAbort();
}


err = setgid(gid);
if (err < 0) {
LOGE("cannot setgid(%d): %s", gid, strerror(errno));
dvmAbort();
}

err = setuid(uid);
if (err < 0) {
LOGE("cannot setuid(%d): %s", uid, strerror(errno));
dvmAbort();
}
...
}


我们看到在子进程里调用setgroupsIntarray设置该进程所属的组,这样它就拥有了该组的权限。也通过setgid及setuid决定了应用程序的uid及gid值。

举个例子:

我们新建一Android工程,读取其应用的uid/gid值,贴入如下代码:

try{

java.lang.Processprocess=Runtime.getRuntime().exec("id");

InputStreaminput=process.getInputStream();

byte[]bytes=newbyte[1204];

intlen;

while((len=(input.read(bytes)))>0)

{

System.out.print(newString(bytes,0,len));

}

input.close();

这里运行运行linuxid命令的代码,id命令的功能是输出当前用户的uid,主要的groupid和所在的group

在其AndroidManifest.xml添加如下权限:

<uses-permissionandroid:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

运行后看log里面有:


这里面就有groups=1015


再看下android_filesystem_config.h里面

代码路径:

system\core\include\private

有如下代码

#define AID_ROOT 0 /* traditional unix root user */

#define AID_SYSTEM 1000 /* system server */

#define AID_RADIO 1001 /* telephony subsystem, RIL */
#define AID_BLUETOOTH 1002 /* bluetooth subsystem */
#define AID_GRAPHICS 1003 /* graphics devices */

...

#define AID_WIFI 1010 /* wifi subsystem */
#define AID_ADB 1011 /* android debug bridge (adbd) */
#define AID_INSTALL 1012 /* group for installing packages */
#define AID_MEDIA 1013 /* mediaserver process */
#define AID_DHCP 1014 /* dhcp client */
#define AID_SDCARD_RW 1015 /* external storage write access */


再看下platform.xml

路径:frameworks\base\data\etc

有如下配置

<permissionname="android.permission.WRITE_EXTERNAL_STORAGE">

<groupgid="sdcard_rw"/>

</permission>

这样就把android.permission.WRITE_EXTERNAL_STORAGE"sdcard_rw"、以及1015关联起来了


我们再看下当sd卡挂载上去后目录的权限:

ls-l

drwxr-xr-xrootsystem1970-01-0108:00obb

drwxr-xr-xrootsystem1970-01-0108:00asec

drwx------rootroot1970-01-0108:00secure

d---rwxr-xsystemsdcard_rw2012-03-2917:11sdcard

可以看到对于sd卡,组用户具有读写权限,而我们的应用也加入了这个组,这样它就可以操作sdcard了。


当一个应用需要操作sdcard而没有在AndroidManifest.xml添加相应的权限时,就不能成功完成。

以下是同一个程序,一个有在AndroidManifest.xml添加WRITE_EXTERNAL_STORAGE权限,一个没有,它们的对比,可以看到由于没有权限程序运行异常了。

Android 权限控制代码分析_第3张图片


对于管理权限的xml文件补充说明一下:

源代码中权限文件位于:frameworks\base\data\etc 下面,镜像生成在system\etc\permissions\platform.xml中

文件权限读取点:

readPermissions @PackageManagerService.java

void readPermissions() {
// Read permissions from .../etc/permission directory.
File libraryDir = new File(Environment.getRootDirectory(), "etc/permissions");

if (!libraryDir.exists() || !libraryDir.isDirectory()) {
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
for (File f : libraryDir.listFiles()) {
// We'll read platform.xml last
if (f.getPath().endsWith("etc/permissions/platform.xml")) {
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);
}

// Read permissions from .../etc/permissions/platform.xml last so it will take precedence
final File permFile = new File(Environment.getRootDirectory(),
"etc/permissions/platform.xml");
readPermissionsFromXml(permFile);

}


platform.xml 的重要性在于:

<!-- This file is used to define the mappings between lower-level system
user and group IDs and the higher-level permission names managed
by the platform.


Be VERY careful when editing this file! Mistakes made here can open
big security holes.
-->

原文地址:点击打开链接

更多相关文章

  1. Android 3.0之后开机无法接收系统广播权限原因
  2. 最新历史版本 :Android修改system只读权限:remount
  3. 在 Android 上使用 XML 和 JSON,第 1 部分: 在 Android 应用程序
  4. 关于Android使用proguard进行代码混淆
  5. Android实现动态改变app图标的示例代码

随机推荐

  1. Android画图Path的使用
  2. Android开发日记 --(1)Android系统开发环境
  3. Android: Android(安卓)Resources
  4. Android(安卓)textView字间距自定义Lette
  5. Android密码明文密文切换
  6. Android原始XML的读写操作
  7. Android(安卓)DHT11驱动
  8. android百度地图标记点代码
  9. Android(安卓)_Android登录对话框实现(1)
  10. 直播源码Android实现 曲线路径动画