Android4.0 SD卡无法删除文件solution与Androidmanifest.xml解析流程

 

在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权限,一个没有,它们的对比,可以看到由于没有权限程序运行异常了。

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值