之前以为在framework中不需要申请权限就可以直接使用,直到最近移植android6.0时,发现原来4.4上的代码移植到6.0上后无法正常使用,排查原因发现是在读写sdcard时因为没有读写权限导致出错,这才直到原来framework中的服务也是需要配置权限相关的东西的,只是方法和app中配置的方法不一样。如下记录该问题解决的方法:
1、测试代码
我在system server中自定义的服务中加入一段读写外置存储的测试代码,代码实现的功能非常简单,主要用三个方法构成,一个用于检测外置存储设备的挂载状态,一个用于读取显示外置存储中的文件列表,第三个方法用于向外置存储设备中创建一个测试文件,创建前如果存在就先删除。
[java] view plain copy
1. private final static String[] sStorageArray = { "/mnt/extsd",
2. "/mnt/extsd2", "/mnt/usbhost/Storage01", "/mnt/usbhost/Storage02",
3. "/mnt/usbhost/Storage03" };
4.
5. @RequiresPermission(anyOf = {"android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"})
6. private static void checkStorageState() {
7. String state = Environment.getExtsdStorageState();
8. "getExtsdStorageState=" + state);
9. state = Environment.getExtsd2StorageState();
10. "getExtsd2StorageState=" + state);
11. state = Environment.getUsbStorageState();
12. "getUsbStorageState=" + state);
13. state = Environment.getExtsd2StorageState();
14. "getExtsd2StorageState=" + state);
15. }
16.
17. @RequiresPermission(anyOf = {"android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"})
18. private static void listFile(final String home) {
19. "Home ="+home);
20. new File(home);
21. if (null != f && f.exists()) {
22. "Home " + home + " is avaiable!");
23. File list[] = f.listFiles();
24. if (null != list && 0 < list.length) {
25. "File list is not empty.");
26. for (File tf : list) {
27. if (null != tf) {
28. "File name=" + tf.getName());
29. }
30. }
31. }
32. else {
33. "File list is empty.");
34. }
35. else {
36. "Home " + home + " is not avaiable.");
37. }
38. }
39.
40. @RequiresPermission(anyOf = {"android.permission.READ_EXTERNAL_STORAGE", "android.permission.WRITE_EXTERNAL_STORAGE"})
41. private static void writeFile(final String home) {
42. "Home ="+home);
43. new File(home);
44. if (null != f && f.exists()) {
45. "Home " + home + " is avaiable!");
46. final String fname = new StringBuilder().append(home).append(File.separatorChar).append("test.file").toString();
47. new File(fname);
48. try {
49. tf.deleteOnExit();
50. tf.createNewFile();
51. "Create "+fname+" success!");
52. catch (IOException e) {
53. "Write file:"+fname+" failed.");
54. e.printStackTrace();
55. }
56. else {
57. "Home " + home + " is not avaiable.");
58. }
59. }
2、问题描述
执行上述的测试代码后,发现获取存储设备状态成功,但是读取设备列表时始终为null,而创建文件时则直接抛出异常,异常原因是无权限。报错的主要日志如下:
[html] view plain copy
1. 01-01 08:00:50.661 E/UpdateManagerService( 2591): Write file:/mnt/usbhost/Storage02/test.file failed.
2. 01-01 08:00:50.661 W/System.err( 2591): java.io.IOException: open failed: EACCES (Permission denied)
3. 01-01 08:00:50.665 W/System.err( 2591): at java.io.File.createNewFile(File.java:939)
4. 01-01 08:00:50.665 W/System.err( 2591): at com.android.server.update.UpdateManagerService.writeFile(UpdateManagerService.java:77)
5. 01-01 08:00:50.665 W/System.err( 2591): at com.android.server.update.UpdateManagerService.-wrap2(UpdateManagerService.java)
6. 01-01 08:00:50.665 W/System.err( 2591): at com.android.server.update.UpdateManagerService$Lifecycle$1.run(UpdateManagerService.java:119)
7. 01-01 08:00:50.665 W/System.err( 2591): at java.lang.Thread.run(Thread.java:818)
8. 01-01 08:00:50.665 W/System.err( 2591): Caused by: android.system.ErrnoException: open failed: EACCES (Permission denied)
9. 01-01 08:00:50.669 W/System.err( 2591): at libcore.io.Posix.open(Native Method)
10. 01-01 08:00:50.670 W/System.err( 2591): at libcore.io.BlockGuardOs.open(BlockGuardOs.java:186)
11. 01-01 08:00:50.670 W/System.err( 2591): at java.io.File.createNewFile(File.java:932)
12. 01-01 08:00:50.670 W/System.err( 2591): ... 4 more
3、问题分析
(1)、查看当前进程所属的用户组
通过上述错误日志,找到对应的进程号,如本例中的进程号为2591。进入/proc虚拟文件系统下进程的相关目录,查看进程的运行信息,如本例中的进程目录:
[html] view plain copy
1. cd /proc/2951
通过如下命令查看进程的信息:
[html] view plain copy
1. cat status
进程信息中有一个Groups属性,包含了当前进程所属的用户组,该进程仅仅拥有所属组所具备的权限。
上面的里面是我修改过具备读写外置存储设备后的status,修改之前Groups中不包含1015组。
(2)、查看所属用户组具备的权限
上一步得到的用户组都是int型,其定义在android/system/core/include/private/android_filesystem_config.h文件中,本文列出其中部分定义说明,具体的可以自行查阅相关代码。从代码中可以看到外置存储卡写权限属于AID_SDCARD_RW组,即1015(上面说过,我在修改前system_server是不具备1015的用户组的,所以导致读写外置存储卡时抛出了权限问题)。
[html] view plain copy
1. /* This is the master Users and Groups config for the platform.
2. * DO NOT EVER RENUMBER
3. */
4.
5. #define AID_ROOT 0 /* traditional unix root user */
6.
7. #define AID_SYSTEM 1000 /* system server */
8.
9. #define AID_RADIO 1001 /* telephony subsystem, RIL */
10. #define AID_BLUETOOTH 1002 /* bluetooth subsystem */
11. #define AID_GRAPHICS 1003 /* graphics devices */
12. #define AID_INPUT 1004 /* input devices */
13. #define AID_AUDIO 1005 /* audio devices */
14. #define AID_CAMERA 1006 /* camera devices */
15. #define AID_LOG 1007 /* log devices */
16. #define AID_COMPASS 1008 /* compass device */
17. #define AID_MOUNT 1009 /* mountd socket */
18. #define AID_WIFI 1010 /* wifi subsystem */
19. #define AID_ADB 1011 /* android debug bridge (adbd) */
20. #define AID_INSTALL 1012 /* group for installing packages */
21. #define AID_MEDIA 1013 /* mediaserver process */
22. #define AID_DHCP 1014 /* dhcp client */
23. #define AID_SDCARD_RW 1015 /* external storage write access */
24. #define AID_VPN 1016 /* vpn system */
25. #define AID_KEYSTORE 1017 /* keystore subsystem */
26. #define AID_USB 1018 /* USB devices */
27. #define AID_DRM 1019 /* DRM server */
28. #define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */
29. #define AID_GPS 1021 /* GPS daemon */
30. #define AID_UNUSED1 1022 /* deprecated, DO NOT USE */
31. #define AID_MEDIA_RW 1023 /* internal media storage write access */
32. #define AID_MTP 1024 /* MTP USB driver access */
33. #define AID_UNUSED2 1025 /* deprecated, DO NOT USE */
34. #define AID_DRMRPC 1026 /* group for drm rpc */
35. #define AID_NFC 1027 /* nfc subsystem */
36. #define AID_SDCARD_R 1028 /* external storage read access */
37. #define AID_CLAT 1029 /* clat part of nat464 */
38. #define AID_LOOP_RADIO 1030 /* loop radio devices */
39. #define AID_MEDIA_DRM 1031 /* MediaDrm plugins */
40. #define AID_PACKAGE_INFO 1032 /* access to installed package details */
41. #define AID_SDCARD_PICS 1033 /* external storage photos access */
42. #define AID_SDCARD_AV 1034 /* external storage audio/video access */
43. #define AID_SDCARD_ALL 1035 /* access all users external storage */
44. #define AID_LOGD 1036 /* log daemon */
45. #define AID_SHARED_RELRO 1037 /* creator of shared GNU RELRO files */
46.
47. #define AID_SHELL 2000 /* adb and debug shell user */
48. #define AID_CACHE 2001 /* cache access */
49. #define AID_DIAG 2002 /* access to diagnostic resources */
50.
51. /* The range 2900-2999 is reserved for OEM, and must never be
52. * used here */
53. #define AID_OEM_RESERVED_START 2900
54. #define AID_OEM_RESERVED_END 2999
55.
56. /* The 3000 series are intended for use as supplemental group id's only.
57. * They indicate special Android capabilities that the kernel is aware of. */
58. #define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */
59. #define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */
60. #define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
61. #define AID_NET_RAW 3004 /* can create raw INET sockets */
62. #define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */
63. #define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
64. #define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
65. #define AID_NET_BT_STACK 3008 /* bluetooth: access config files */
66.
67. #define AID_EVERYBODY 9997 /* shared between all apps in the same profile */
68. #define AID_MISC 9998 /* access to misc storage */
69. #define AID_NOBODY 9999
70.
71. #define AID_APP 10000 /* first app user */
72.
73. #define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
74. #define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */
75.
76. #define AID_USER 100000 /* offset for uid ranges for each user */
77.
78. #define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
79. #define AID_SHARED_GID_END 59999 /* start of gids for apps in each user to share */
(3)、查看system_server启动参数
system_server是由ZygoteInit类负责初始化和启动的,相关的代码在sdk的android/frameworks/base/core/java/com/android/internal/os/ZygoteInit.java文件中。其中关键的启动代码如下。从代码中可以看到,在启动sysetm_server时通过--setgroups为其设置了所属用户组。
[java] view plain copy
1. private static boolean startSystemServer(String abiList, String socketName)
2. throws MethodAndArgsCaller, RuntimeException {
3. long capabilities = posixCapabilitiesAsBits(
4. OsConstants.CAP_BLOCK_SUSPEND,
5. OsConstants.CAP_KILL,
6. OsConstants.CAP_NET_ADMIN,
7. OsConstants.CAP_NET_BIND_SERVICE,
8. OsConstants.CAP_NET_BROADCAST,
9. OsConstants.CAP_NET_RAW,
10. OsConstants.CAP_SYS_MODULE,
11. OsConstants.CAP_SYS_NICE,
12. OsConstants.CAP_SYS_RESOURCE,
13. OsConstants.CAP_SYS_TIME,
14. OsConstants.CAP_SYS_TTY_CONFIG
15. );
16. /* Hardcoded command line to start the system server */
17. String args[] = {
18. "--setuid=1000",
19. "--setgid=1000",
20. "--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1015,1018,1021,1032,3001,3002,3003,3006,3007",
21. "--capabilities=" + capabilities + "," + capabilities,
22. "--nice-name=system_server",
23. "--runtime-args",
24. "com.android.server.SystemServer",
25. };
26. null;
27.
28. int pid;
29.
30. try {
31. new ZygoteConnection.Arguments(args);
32. ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
33. ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
34.
35. /* Request to fork the system server process */
36. pid = Zygote.forkSystemServer(
37. parsedArgs.uid, parsedArgs.gid,
38. parsedArgs.gids,
39. parsedArgs.debugFlags,
40. null,
41. parsedArgs.permittedCapabilities,
42. parsedArgs.effectiveCapabilities);
43. catch (IllegalArgumentException ex) {
44. throw new RuntimeException(ex);
45. }
46.
47. /* For child process */
48. if (pid == 0) {
49. if (hasSecondZygote(abiList)) {
50. waitForSecondaryZygote(socketName);
51. }
52.
53. handleSystemServerProcess(parsedArgs);
54. }
55.
56. return true;
57. }
注意:上述代码已经是我修改后(即添加用户组1015后)的代码,此时编译的system_server已经具备了读写外置存储的权限。
4、解决方法
在第三步的“问题分析”的最中其实已经给出了解决办法,但是这里还是重新说明下。问题总结主要几个方面:
(1)、查看进程当前所属的组;
(2)、查看所需权限所属的组;
(3)、比对当前进程是否属于需要权限的组;
(4)、修改启动参数添加组;
故,本文最后修改了ZygoteInit.java类,然后在--setgroups参数中增加了1015用户组。