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的警告讯息。

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



这两个应用其实代码是一样的,唯一的不同就是它们的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权限,一个没有,它们的对比,可以看到由于没有权限程序运行异常了。


对于管理权限的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体系结构的理解--后续会补充
  3. Android(安卓)Manifest文件
  4. Android(安卓)引用外部字体
  5. 关于那些Android中不常用的设置属性
  6. Android中监听Home键的4种方法总结
  7. 20155323 第四次实验 Android程序设计实验报告
  8. Android(安卓)Activity生命周期
  9. Android应用程序启动过程源代码分析

随机推荐

  1. Fragment与FragmentActivity间的数据通讯
  2. android系统自动浮动查询框的使用
  3. android .classpath
  4. Android开发基础————通过内部类实现
  5. Android 判断Root的方法
  6. android 存储图片到data目录和读取data目
  7. 修改android usb的VID PID
  8. android 自定义属性实现 ImageView 透明
  9. Android 百度地图
  10. 【android build】 如何在android.mk中获