Android 6.0 fork Zygote时的存储权限管理
2016-02-24 17:37
351 查看
Android 6.0 存储权限管理
官方说明
先翻译一段Android的官方文档,原文在:https://source.android.com/devices/storage/Android 6.0开始支持运行时权限管理的功能。运行时权限管量中当然也包括对READ_EXTERNAL_STORAGE和WRITE_EXTERNAL_STORAGE这两个权限的动态管理。系统需要提供在不杀掉或重启已经运行的应用的情况下去动态授权的机制。目前系统是通过维护三个View来实现的:
/mnt/runtime/default: 针对对于存储权限没有特殊需求的情况。这也是adbd的其它系统组件使用的方式。
/mnt/runtime/read:对于申请READ_EXTERNAL_STORAGE权限的应用可见。
/mnt/runtime/write:对于申请WRITE_EXTERNAL_STORAGE权限的应用可见。
在Zygote fork的时刻,我们为每个运行的应用创建一个命名空间,然后将其绑定到上面所进的三个View中作为初始的View。在运行时获得新的授权后,vold会跳转到这个装载的命名空间并重新绑定新的View. 需要注意的一点是,如果权限降级,则一定会导致应用被杀。
setns()方法从Linux 3.8移植到了3.4就专为干这事儿。有个PermissionsHostTest的CTS测试用例用来保证这个功能的有效性。
在Android 6.0,第三方应用没有访问sdcard_r和sdcard_rw GID的权限。作为替代,通过上面所讲的View的方式来控制。使用everybody GID跨用户的交互会被阻止。
代码实现
看了上面的介绍,我们来看代码中是如何实现的。实现这个功能的函数在framework/base/core/jni/com_android_internal_os_Zygote.cpp中的MountEmulatedStorage函数。294// Create a private mount namespace and bind mount appropriate emulated 295// storage for the given user. 296static bool MountEmulatedStorage(uid_t uid, jint mount_mode, 297 bool force_mount_namespace) { 298 // See storage config details at http://source.android.com/tech/storage/[/code]
第一步,先调用unshare系统调用去禁止同享命名空间。
unshare函数定义于sched.h中,用于将部分进程上下文信息不共享父进程的,CLONE_NEWNS是指定不共享命名空间。
下面是ARM v8a AArch64下时调用unshare系统调用的代码:5ENTRY(unshare) 6 mov x8, __NR_unshare 7 svc #0 8 9 cmn x0, #(MAX_ERRNO + 1) 10 cneg x0, x0, hi 11 b.hi __set_errno_internal 12 13 ret 14END(unshare) 15
下面是第一步的代码:300 // Create a second private mount namespace for our process 301 if (unshare(CLONE_NEWNS) == -1) { 302 ALOGW("Failed to unshare(): %s", strerror(errno)); 303 return false; 304 }
第二步,调用UnmountTree函数:306 // Unmount storage provided by root namespace and mount requested view 307 UnmountTree("/storage");
我们转到UnmountTree函数:static int UnmountTree(const char* path) { size_t path_len = strlen(path); FILE* fp = setmntent("/proc/mounts", "r");
setmntent函数定义如下:#include <stdio.h> #include <mntent.h> FILE *setmntent(const char *filename, const char *type);
用于获取系统mount的信息。
具体去读每一行的时候使用getmntent()函数。
结束时调用endmntent()函数,相当于fclose()。
getmntent读取的是一个mntent结构体的结构,该结构定义于struct mntent { char *mnt_fsname; /* name of mounted file system */ char *mnt_dir; /* file system path prefix */ char *mnt_type; /* mount type (see mntent.h) */ char *mnt_opts; /* mount options (see mntent.h) */ int mnt_freq; /* dump frequency in days */ int mnt_passno; /* pass number on parallel fsck */ };
下面代码中,调用getmntent函数将目录信息读出来放在一个列表中:if (fp == NULL) { ALOGE("Error opening /proc/mounts: %s", strerror(errno)); return -errno; } // Some volumes can be stacked on each other, so force unmount in // reverse order to give us the best chance of success. std::list<std::string> toUnmount; mntent* mentry; while ((mentry = getmntent(fp)) != NULL) { if (strncmp(mentry->mnt_dir, path, path_len) == 0) { toUnmount.push_front(std::string(mentry->mnt_dir)); } } endmntent(fp);
接着,通过调用umount2函数将这些目录都unmount掉。for (auto path : toUnmount) { if (umount2(path.c_str(), MNT_DETACH)) { ALOGW("Failed to unmount %s: %s", path.c_str(), strerror(errno)); } } return 0; }
umount2函数原型如下,用于unmount文件系统。#include <sys/mount.h> int umount2(const char *target, int flags);
第三步,我们从UnmountTree中回来,按照上面所讲的几种模式,分别设置不同路径名:309 String8 storageSource; 310 if (mount_mode == MOUNT_EXTERNAL_DEFAULT) { 311 storageSource = "/mnt/runtime/default"; 312 } else if (mount_mode == MOUNT_EXTERNAL_READ) { 313 storageSource = "/mnt/runtime/read"; 314 } else if (mount_mode == MOUNT_EXTERNAL_WRITE) { 315 storageSource = "/mnt/runtime/write"; 316 } else { 317 // Sane default of no storage visible 318 return true; 319 }
第四步,根据第三步的模式值,重新mount。320 if (TEMP_FAILURE_RETRY(mount(storageSource.string(), "/storage", 321 NULL, MS_BIND | MS_REC | MS_SLAVE, NULL)) == -1) { 322 ALOGW("Failed to mount %s to /storage: %s", storageSource.string(), strerror(errno)); 323 return false; 324 }
第五步,针对多用户的情况,额外需要做符号链接。326 // Mount user-specific symlink helper into place 327 userid_t user_id = multiuser_get_user_id(uid); 328 const String8 userSource(String8::format("/mnt/user/%d", user_id)); 329 if (fs_prepare_dir(userSource.string(), 0751, 0, 0) == -1) { 330 return false; 331 } 332 if (TEMP_FAILURE_RETRY(mount(userSource.string(), "/storage/self", 333 NULL, MS_BIND, NULL)) == -1) { 334 ALOGW("Failed to mount %s to /storage/self: %s", userSource.string(), strerror(errno)); 335 return false; 336 } 337 338 return true; 339}
相关文章推荐
- 用Android代码实现打开USB调试
- 隐藏APK在Launcher中的启动图标 android开发教程
- 安装Android模拟器Genymotion【Android学习入门】
- 编译android源码
- Android Binder IPC分析
- Android 获取SD卡路径和判断SD卡是否存在.
- android Mp3播放器之音频文件扫描
- Android Gradle插件用户指南(转载)
- Android开发10——Activity的跳转与传值
- Android-使用XML布局文件实现游戏的开始界面
- Android Studio 插件--Findbugs
- Android 知识点
- 引入其他项目做为module之后,在编译时候提示“Error:Dependency Android_2015:projLib:unspecified on project projAPK resol”
- Android中的进程和线程
- android中修改framework层代码后怎样操作才能看到修改后的效果?
- 使用Android Studio阅读整个Android源码
- android txt文件名listview item 点击并传值
- android扫描sd卡下的所有.txt 并获取路径
- 从技术到产品,转型的这一年我明白了很多道理
- 自己动手做一个--手势解锁