在4.0专案的进行中遇到了一个BUG,SD卡中的文件无法删除,重启之后文件还是会出现在卡中,解决办法如下,加入media权限.另外特别研究下权限解析的代码。
2.3中声明
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
可以读写SDCARD,包括其他storage,比如内部flash,usb等等
4.0中如果只声明这个,会发现其他mount的设备有可能没有写权限,需要同时声明
<uses-permission android:name="android.permission.WRITE_MEDIA_STORAGE"/>
这样才可以读写/mnf/flash , /mnt/usb, /mnt/externa等目录
可能4.0增加了对其他存储设备的内建支持,其他设备不再共享gid "sdcard_rw"
而改用"media_rw",相应的细分了权限声明
2.3中 dr-xrwxr-x system sdcard_rw 1969-12-31 16:00 flash
4.0中 d---rwxr-x system media_rw 1970-01-01 08:00 flash
权限控制文件在 frameworks/base/data/etc/platform.xml:
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
<group gid="sdcard_rw" />
</permission>
<permission name="android.permission.WRITE_MEDIA_STORAGE" >
<group gid="media_rw" />
</permission>
Androidmanifest.xml解析流程:
在安装apk的时候,会解析这个AndroidManifest.xml,把相应的信息保存起来。
代码路径:frameworks\base\core\java\android\content\pm\PackageParser.java
最终调用的是private Package parsePackage(
Resources res, XmlResourceParser parser, int flags, 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中。
当应用程序启动的过程中会调用
private final void startProcessLocked(ProcessRecord app,
String hostingType, String hostingNameStr)
代码路径: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.Process process = Runtime.getRuntime().exec("id");
InputStream input = process.getInputStream();
byte[] bytes = new byte[1204];
int len;
while((len = (input.read(bytes))) > 0)
{
System.out.print(new String(bytes, 0, len));
}
input.close();
这里运行运行linux的id命令的代码,id命令的功能是输出当前用户的uid,主要的group id和所在的group
在其AndroidManifest.xml添加如下权限:
<uses-permission android: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
有如下配置
<permission name="android.permission.WRITE_EXTERNAL_STORAGE" >
<group gid="sdcard_rw" />
</permission>
这样就把android.permission.WRITE_EXTERNAL_STORAGE 、"sdcard_rw"、以及 1015组关联起来了
我们再看下当sd卡挂载上去后目录的权限:
ls -l
drwxr-xr-x root system 1970-01-01 08:00 obb
drwxr-xr-x root system 1970-01-01 08:00 asec
drwx------ root root 1970-01-01 08:00 secure
d---rwxr-x system sdcard_rw 2012-03-29 17:11 sdcard
可以看到对于sd卡,组用户具有读写权限,而我们的应用也加入了这个组,这样它就可以操作sdcard了。
当一个应用需要操作sdcard而没有在AndroidManifest.xml添加相应的权限时,就不能成功完成。
以下是同一个程序,一个有在AndroidManifest.xml添加WRITE_EXTERNAL_STORAGE权限,一个没有,它们的对比,可以看到由于没有权限程序运行异常了。