一、原因
原因:Android具有system权限为什么不可以访问SDcard
官方文档解释
Processes that continue holding open fds on the sdcard a little after it is
requested to be unmounted will be killed so that it can unmount.
We don’t want the system process to be able to access the sdcard to avoid
these kinds of issues (and just general security cleanliness), so that it
does not have permission to access it.
中文翻译
sdcard属于易插拔的外部设备,如果说我们系统app可以访问sdcard,并且一直持有sdcard中相应文件的fds(文件句柄),如果我们sdcard这时候拔除,
这时系统app就有可能因为fds文件句柄没有被killed,但是系统app一般是不可以被killed,因为你想想如果我们的主页launcher被killed了或者其他重要系统app被killed
就有可能导致一系列安全问题,如主页crash或者手机重启等等,也就是我一拔sdcard结果手机死机了的悲剧结果
简单总结就是以前的sdcard卡是可插拔的,如果系统进程去访问sdcard目录,会持有sdcard文件句柄,如果sdcard被拔除,可能会导致系统崩溃,故官方做了这个限制。但是目前的sdcard一般是内置的,不可插拔,一般不会存在这种情况。
二、解决方法
1、方法一
参考链接:【framework】framework中为systemserver添加权限
问题解决思路:查看system_server是否属于sdcard读写相关用户组**
解决过程
- 查看sdcard目录信息
红框标注的地方为所属用户组信息,进程属于这个用户组才能访问sdcard目录
- 查看
sdcard_r
对应的用户组id
头文件位置:system/core/include/private/android_filesystem_config.h
#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_INPUT 1004 /* input devices */
#define AID_AUDIO 1005 /* audio devices */
#define AID_CAMERA 1006 /* camera devices */
#define AID_LOG 1007 /* log devices */
#define AID_COMPASS 1008 /* compass device */
#define AID_MOUNT 1009 /* mountd socket */
#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 */
#define AID_VPN 1016 /* vpn system */
#define AID_KEYSTORE 1017 /* keystore subsystem */
#define AID_USB 1018 /* USB devices */
#define AID_DRM 1019 /* DRM server */
#define AID_MDNSR 1020 /* MulticastDNSResponder (service discovery) */
#define AID_GPS 1021 /* GPS daemon */
#define AID_UNUSED1 1022 /* deprecated, DO NOT USE */
#define AID_MEDIA_RW 1023 /* internal media storage write access */
#define AID_MTP 1024 /* MTP USB driver access */
#define AID_UNUSED2 1025 /* deprecated, DO NOT USE */
#define AID_DRMRPC 1026 /* group for drm rpc */
#define AID_NFC 1027 /* nfc subsystem */
#define AID_SDCARD_R 1028 /* external storage read access */
#define AID_CLAT 1029 /* clat part of nat464 */
#define AID_LOOP_RADIO 1030 /* loop radio devices */
#define AID_MEDIA_DRM 1031 /* MediaDrm plugins */
#define AID_PACKAGE_INFO 1032 /* access to installed package details */
#define AID_SDCARD_PICS 1033 /* external storage photos access */
#define AID_SDCARD_AV 1034 /* external storage audio/video access */
#define AID_SDCARD_ALL 1035 /* access all users external storage */
#define AID_SHELL 2000 /* adb and debug shell user */
#define AID_CACHE 2001 /* cache access */
#define AID_DIAG 2002 /* access to diagnostic resources */
/* The 3000 series are intended for use as supplemental group id's only.
* They indicate special Android capabilities that the kernel is aware of. */
#define AID_NET_BT_ADMIN 3001 /* bluetooth: create any socket */
#define AID_NET_BT 3002 /* bluetooth: create sco, rfcomm or l2cap sockets */
#define AID_INET 3003 /* can create AF_INET and AF_INET6 sockets */
#define AID_NET_RAW 3004 /* can create raw INET sockets */
#define AID_NET_ADMIN 3005 /* can configure interfaces and routing tables. */
#define AID_NET_BW_STATS 3006 /* read bandwidth statistics */
#define AID_NET_BW_ACCT 3007 /* change bandwidth statistics accounting */
#define AID_NET_BT_STACK 3008 /* bluetooth: access config files */
#define AID_MISC 9998 /* access to misc storage */
#define AID_NOBODY 9999
#define AID_APP 10000 /* first app user */
#define AID_ISOLATED_START 99000 /* start of uids for fully isolated sandboxed processes */
#define AID_ISOLATED_END 99999 /* end of uids for fully isolated sandboxed processes */
#define AID_USER 100000 /* offset for uid ranges for each user */
#define AID_SHARED_GID_START 50000 /* start of gids for apps in each user to share */
#define AID_SHARED_GID_END 59999 /* start of gids for apps in each user to share */
由以上头文件可查看到一下这行定义
#define AID_SDCARD_R 1028 /* external storage read access */
所以sdcard_r
对应的groud_id 为1028
- 查看system_server 进程信息
① 查看uid
② 查看进程详细信息
cd proc/1863
cat status
从上图可以看出system_server并不属于
sdcard_r(1028)
用户组,所以没有读写sdcard目录的权限,所以system_server在读写sdcard文件的时候会报(Permission denied)
错误(不过也可能是别的原因)
- 修改system_server 用户组
system_server是由ZygoteInit类负责初始化和启动的,相关的代码在
frameworks/base/core/java/com/android/internal/os/ZygoteInit.java
文件中。其中关键的启动代码如下。从代码中可以看到,在启动sysetm_server时通过–setgroups为其设置了所属用户组。
private static boolean startSystemServer()
throws MethodAndArgsCaller, RuntimeException {
long capabilities = posixCapabilitiesAsBits(
OsConstants.CAP_KILL,
OsConstants.CAP_NET_ADMIN,
OsConstants.CAP_NET_BIND_SERVICE,
OsConstants.CAP_NET_BROADCAST,
OsConstants.CAP_NET_RAW,
OsConstants.CAP_SYS_MODULE,
OsConstants.CAP_SYS_NICE,
OsConstants.CAP_SYS_RESOURCE,
OsConstants.CAP_SYS_TIME,
OsConstants.CAP_SYS_TTY_CONFIG
);
/* Hardcoded command line to start the system server */
String args[] = {
"--setuid=1000",
"--setgid=1000",
"--setgroups=1001,1002,1003,1004,1005,1006,1007,1008,1009,1010,1018,1032,3001,3002,3003,3006,3007",
"--capabilities=" + capabilities + "," + capabilities,
"--runtime-init",
"--nice-name=system_server",
"com.android.server.SystemServer",
};
ZygoteConnection.Arguments parsedArgs = null;
int pid;
try {
parsedArgs = new ZygoteConnection.Arguments(args);
ZygoteConnection.applyDebuggerSystemProperty(parsedArgs);
ZygoteConnection.applyInvokeWithSystemProperty(parsedArgs);
/* Request to fork the system server process */
pid = Zygote.forkSystemServer(
parsedArgs.uid, parsedArgs.gid,
parsedArgs.gids,
parsedArgs.debugFlags,
null,
parsedArgs.permittedCapabilities,
parsedArgs.effectiveCapabilities);
} catch (IllegalArgumentException ex) {
throw new RuntimeException(ex);
}
/* For child process */
if (pid == 0) {
handleSystemServerProcess(parsedArgs);
}
return true;
}
我们可以在--setgroups
添加1028
(不是固定的,根据实际情况)
重新编译framework
并烧录验证,可以查看进程信息
验证用户组是否添加成功。
1、方法二
解决思路:提高system_server权限
代码路径:dalvik/vm/native/dalvik_system_Zygote.cpp
/*
* Utility routine to fork zygote and specialize the child process.
*/
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];
u4 mountMode = MOUNT_EXTERNAL_NONE;
int64_t permittedCapabilities, effectiveCapabilities;
char *seInfo = NULL;
char *niceName = NULL;
if (isSystemServer) {
/*
* Don't use GET_ARG_LONG here for now. gcc is generating code
* that uses register d8 as a temporary, and that's coming out
* scrambled in the child process. b/3138621
*/
//permittedCapabilities = GET_ARG_LONG(args, 5);
//effectiveCapabilities = GET_ARG_LONG(args, 7);
permittedCapabilities = args[5] | (int64_t) args[6] << 32;
effectiveCapabilities = args[7] | (int64_t) args[8] << 32;
} else {
mountMode = args[5];
permittedCapabilities = effectiveCapabilities = 0;
StringObject* seInfoObj = (StringObject*)args[6];
if (seInfoObj) {
seInfo = dvmCreateCstrFromString(seInfoObj);
if (!seInfo) {
ALOGE("seInfo dvmCreateCstrFromString failed");
dvmAbort();
}
}
StringObject* niceNameObj = (StringObject*)args[7];
if (niceNameObj) {
niceName = dvmCreateCstrFromString(niceNameObj);
if (!niceName) {
ALOGE("niceName dvmCreateCstrFromString failed");
dvmAbort();
}
}
}
......
从以上代码分析中可以看到,如果是
system_server(isSystemServer)
u4 mountMode = MOUNT_EXTERNAL_NONE;
从代码分析,如果是系统进程的情况下,通过mountMode
控制,让系统进程不能访问sdcard
目录
(但是海思方案也是这样定义,但是system_sever也能正常访问sdcard目录,可能是底层文件系统不一样导致,还有待研究)
我们再查看mountMode
参数枚举,可以看到
//代码位置:dalvik/vm/native/dalvik_system_Zygote.cpp
enum {
MOUNT_EXTERNAL_NONE = 0,
MOUNT_EXTERNAL_SINGLEUSER = 1,
MOUNT_EXTERNAL_MULTIUSER = 2,
MOUNT_EXTERNAL_MULTIUSER_ALL = 3,
};
我们修改默认的mountMode
参数
如上图,修改
mountMode
参数为MOUNT_EXTERNAL_MULTIUSER_ALL
,允许所有的用户访问(可能会造成别的问题,谨慎修改),修改完成后单编/整编进行验证。
三、总结
因为在项目中,有个需求是需要在system_server中去读取sdcard文件,发现了system_server竟然不能访问sdcard目录文件,起先感觉到很奇怪,在我们印象中系统进程的权限一般都高于普通应用进程,普通应用都能正常读写,system_server竟然不能?通过查询资料,发现其中端倪,故写此文章记录一下问题,方便以后翻阅。
以上分析属于笔者自己理解如果有错误请大神指出,欢迎转载