Android应用权限控制源码分析

*郑重声明:本文内容只为学习研究之用,如有人用于非法用途,产生的后果笔者不负任何责任。

Android权限控制是对应用访问设备信息和接口的限制,其作用是保护Android用户的隐私。

Android安全架构的设计主旨是:在默认情况下,任何应用都没有权限执行会对其他应用、操作系统或用户带来不利影响的任何操作。

对于第三方应用来说,权限分为三个保护级别:普通、签名和危险。

  • 普通权限:应用需要访问自身沙盒之外的数据或资源,但对用户隐私或其他应用带来的风险很小,如设置时区、壁纸、闹钟;
  • 签名权限:用于两个签名相同的应用间进行安全的数据共享;
  • 危险权限:应用需要的数据或资源设计用户隐私,或者可能对用户存储的数据或其他应用的操作产生影响,如读取联系人,拍照、定位等。

系统对用户隐私保护的演进

不论系统如何更新,应用申请权限首要的一部一直没变,就是在AndroidManifest.xml中进行注册,如下图:

而在用户主动管理权限的部分,Android正在日益完善,分水岭是在Android 6.0。

Android 6

在Android6.0发布之前,应用申请权限只需在AndroidManifest文件中列举出来即可,在应用安装时会一一列举出来,但是用户并不能选择“允许”或“禁止”,甚至应用安装后,在“设置”中也仅仅能看到应用申请了哪些权限,用户并不能做任何修改。下图为Android5.0系统应用安装时和安装完成后的权限管理情况:

在这一阶段,用户的隐私并不属于用户自己,全凭第三方应用开发者做主,“人为刀俎,我为鱼肉”。好在国内一些手机厂商深度定制的ROM(如MIUI等)在Android 6.0之前就加入了权限管理功能。

Google在Android 6.0中推出了新的运行时权限管理机制。应用安装时不再直接授予应用危险权限,而是在应用启动时需要通过弹框的方式向用户请求。

“道高一尺魔高一丈”,这一时期也出现了许多用户拒绝权限,应用就直接退出的操作,如支付宝、微博、淘宝等。而微信等一批应用就很“机智”了,编译APP时的targetSdkVersion属性直接设置为22(即Android5.1),应用安装上就直接授予了权限,大多数用户并不会手动去“设置”中关闭权限。

Android 7

Android 7增加了“私有目录限制访问”和“StrictMode API政策”两个权限限制,私有文件的权限放宽会触发SecurityException,应用间分享私有文件也应该使用FileProvider,而不是像之前一样直接发送file://URI。

Android 8

Android 8之前,应用被授予某权限,系统会将授予该权限同一权限组的所有权限(Manifest中申请的)。比如,用户授予了读短信的权限,则与之同组的发送短信、接收短信等权限都会一同授予,这显然是不合理的。Android8纠正了这一点,但是并不彻底。如果用户同意了读短信的权限申请,那么后续应用申请发送短信的权限时,系统将直接授予该权限,不会再提示用户同意。

Android 9

Android 9为了增强用户隐私保护增加了若干限制。比如,限制后台应用访问设备传感器,限制通过WiFi扫描检索到的信息,修改了通话、手机状态(READ_PHONE_STATE)和WiFi扫描的新权限规则。

  • 应用访问麦克风、摄像头、位置等需要前台服务
  • 将读/写通话日志,处理呼出电话等权限分配一个新的权限组CALL_LOG
  • 限制访问电话号码:READ_CALL_LOG权限
  • 通过WiFi广播不再能获取到SSID、BSSID(Wifi名和路由器MAC),想要获取SSID和BSSID需要位置权限和ACCESS_WIFI_STATE权限

Android 10

Android 10提供了更加严格的隐私保护:

  • 用户授予位置权限时多了一种选择,即仅在应用实际使用时访问位置;

  • 应用无法访问设备不可重置的标识符,如IMEI、序列号等。设备MAC地址也会默认在连接到WLAN时随机分配。

  • 阻止应用从后台启动,若要通过Service唤醒ACtivity,需要将Service设置为前台服务

  • 文件分区存储,应用只能访问自己目录下的文件和公共媒体文件,/storage/emulated/0/Android/data/包名/files/storage/emulated/0/Downloads(Pictures)

Android 11

Android 11的几项重大变更:

  • TargetSDK为Android 11的应用,强制执行分区存储机制;
  • 对位置、麦克风和摄像头权限进行单次授权;
  • 已经授予权限的应用,若用户几个月未使用,自动重置权限;
  • 在前台服务中申请摄像头、麦克风等权限时,需要声明camera和microphone前台服务类型;
  • 应用对设备上安装的其他应用的可见性可以在Manifest中添加queries元素进行配置,告知系统本应用对哪些应用可见。

各版本更新情况:

https://developer.android.google.cn/about/versions/11

权限控制源码分析

Android 5.1

Android5.1以下的系统不需要弹框申请权限,但有时为了防止抛异常,需要检查该权限是否被授予,检查权限调用了Context类的checkPermission方法,传入参数为permission,pid, uid。

具体实现在:/framework/base/core/jaca/android/app/ContextImpl.java.

http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/app/ContextImpl.java

1
2
3
4
5
6
7
8
9
10
11
12
13
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}

try {
return ActivityManagerNative.getDefault().checkPermission(
permission, pid, uid);
} catch (RemoteException e) {
return PackageManager.PERMISSION_DENIED;
}
}

ActivityManagerNative.getDefault()方法的返回值为IActivityManager类型,该类是一个接口,其实现类ActivityManagerService中的checkPermission():

http://androidxref.com/5.1.1_r6/xref/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java

1
2
3
4
5
6
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
return checkComponentPermission(permission, pid, UserHandle.getAppId(uid), -1, true);
}

进入checkComponentPermission():

1
2
3
4
5
6
7
8
int checkComponentPermission(String permission, int pid, int uid,
int owningUid, boolean exported) {
if (pid == MY_PID) {
return PackageManager.PERMISSION_GRANTED;
}
return ActivityManager.checkComponentPermission(permission, uid,
owningUid, exported);
}

进入ActivityManager.checkComponentPermission:

http://androidxref.com/5.1.1_r6/xref/frameworks/base/core/java/android/app/ActivityManager.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
// Root, system server get to do everything.
if (uid == 0 || uid == Process.SYSTEM_UID) {
return PackageManager.PERMISSION_GRANTED;
}
// Isolated processes don't get any permissions.
if (UserHandle.isIsolated(uid)) {
return PackageManager.PERMISSION_DENIED;
}
// If there is a uid that owns whatever is being accessed, it has
// blanket access to it regardless of the permissions it requires.
if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
return PackageManager.PERMISSION_GRANTED;
}
// If the target is not exported, then nobody else can get to it.
if (!exported) {
/*
RuntimeException here = new RuntimeException("here");
here.fillInStackTrace();
Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
here);
*/
return PackageManager.PERMISSION_DENIED;
}
if (permission == null) {
return PackageManager.PERMISSION_GRANTED;
}
try {
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
// Should never happen, but if it does... deny!
Slog.e(TAG, "PackageManager is dead?!?", e);
}
return PackageManager.PERMISSION_DENIED;
}

普通应用的权限申请会进入最后一个分支:AppGlobals.getPackageManager() .checkUidPermission(permission, uid);

进入AppGlobals.getPackageManager():

1
2
3
public static IPackageManager getPackageManager() {
return ActivityThread.getPackageManager();
}

ActivityThread.getPackageManager():

1
2
3
4
5
6
7
8
9
10
11
public static IPackageManager getPackageManager() {
if (sPackageManager != null) {
//Slog.v("PackageManager", "returning cur default = " + sPackageManager);
return sPackageManager;
}
IBinder b = ServiceManager.getService("package");
//Slog.v("PackageManager", "default service binder = " + b);
sPackageManager = IPackageManager.Stub.asInterface(b);
//Slog.v("PackageManager", "default service = " + sPackageManager);
return sPackageManager;
}

这里涉及到进程间通信,在ServiceManager中找到注册的“package”服务,并返回PackageManagerService。PackageManagerService继承了IPackageManager.Stub, IPackageManager.Stub继承了Binder,并实现了IpackageManager。

(与前面ActivityManagerService类似,AMS继承了ActivityManagerNative, AMN继承了binder, 并实现了IActivityManager)

接下来进入PackageManagerService.checkUidPermission:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
2390    @Override
2391 public int checkUidPermission(String permName, int uid) {
2392 synchronized (mPackages) {
2393 Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
2394 if (obj != null) {
2395 GrantedPermissions gp = (GrantedPermissions)obj;
2396 if (gp.grantedPermissions.contains(permName)) {
2397 return PackageManager.PERMISSION_GRANTED;
2398 }
2399 } else {
2400 ArraySet<String> perms = mSystemPermissions.get(uid);
2401 if (perms != null && perms.contains(permName)) {
2402 return PackageManager.PERMISSION_GRANTED;
2403 }
2404 }
2405 }
2406 return PackageManager.PERMISSION_DENIED;
2407 }

mSettings是Setting类的对象,保存了动态设置。

Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));即为获取某uid对应设置。GrantedPermission.grantedPermissions是一个ArraySet,保存了已授予的权限。

另一个分支,mSystemPermissions = SystemConfig.getSystemPermissions(),获取系统全局配置。mSystemPermissions.get(uid)即获取系统全局配置中,本应用已授予的权限。

SystemConfig类初始化时,就会去读取/etc/sysconfig,/etc/permissions下的platform.xml文件,此文件保存了所有已安装应用注册的权限,经过解析保存在`mSystemPermissions中。

至此,检查是否已授予某权限完成,返回PackageManager.PERMISSION_GRANTED = 0PackageManager.PERMISSION_DENIED = -1

Android 6

Android6及以上版本的系统,在使用危险权限的接口时必须加入检查权限的代码。以申请位置权限为例,需要先检查是否已被授予,如果没有再请求该权限:

1
2
3
4
5
6
7
if (ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED
&& ActivityCompat.checkSelfPermission(activity, Manifest.permission.ACCESS_COARSE_LOCATION) != PackageManager.PERMISSION_GRANTED) {

ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 1);

return;
}
  • 检查权限使用了ActivityCompat.checkSelfPermission()方法,打开它的实现,进入ContextCompat类:
1
2
3
4
5
6
7
public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}

return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
}

可以看到调用了Context类的checkPermission方法,其实现是在ContextImpl.java中,与Android5相同不再赘述。

直到最后,在PackageManagerService中的checkUidPermission方法做校验时与之前版本的不同在于将位置权限做了另外的设置,即授予ACCESS_COARSE_LOCATION的前提是授予了ACCESS_FINE_LOCATION权限:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
3170    @Override
3171 public int checkUidPermission(String permName, int uid) {
3172 final int userId = UserHandle.getUserId(uid);
3173
3174 if (!sUserManager.exists(userId)) {
3175 return PackageManager.PERMISSION_DENIED;
3176 }
3177
3178 synchronized (mPackages) {
3179 Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
3180 if (obj != null) {
3181 final SettingBase ps = (SettingBase) obj;
3182 final PermissionsState permissionsState = ps.getPermissionsState();
3183 if (permissionsState.hasPermission(permName, userId)) {
3184 return PackageManager.PERMISSION_GRANTED;
3185 }
3186 // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
3187 if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
3188 .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
3189 return PackageManager.PERMISSION_GRANTED;
3190 }
3191 } else {
3192 ArraySet<String> perms = mSystemPermissions.get(uid);
3193 if (perms != null) {
3194 if (perms.contains(permName)) {
3195 return PackageManager.PERMISSION_GRANTED;
3196 }
3197 if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
3198 .contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
3199 return PackageManager.PERMISSION_GRANTED;
3200 }
3201 }
3202 }
3203 }
3204
3205 return PackageManager.PERMISSION_DENIED;
3206 }
  • 如果检查权限返回-1,则需要请求权限:ActivityCompat.requestPermissions(activity, new String[]{Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION}, 1);

进入activity.requestPermissions(permissions, requestCode);:

http://androidxref.com/7.1.2_r36/xref/frameworks/base/core/java/android/app/Activity.java

1
2
3
4
5
6
7
8
9
10
11
    public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
4116 if (mHasCurrentPermissionsRequest) {
4117 Log.w(TAG, "Can reqeust only one set of permissions at a time");
4118 // Dispatch the callback with empty arrays which means a cancellation.
4119 onRequestPermissionsResult(requestCode, new String[0], new int[0]);
4120 return;
4121 }
4122 Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
4123 startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
4124 mHasCurrentPermissionsRequest = true;
4125 }

弹出对话框,等待用户响应。

用户响应后,系统会调用onRequestPermissionsResult()执行后续操作。

1
2
3
4
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
4145 @NonNull int[] grantResults) {
4146 /* callback - no nothing */
4147 }

那么这里的结果是从哪来的呢。

回到上面弹出对话框的方法,这里的第一个参数who传入的值为REQUEST_PERMISSIONS_WHO_PREFIX = "@android:requestPermissions:"

我们发现Activity类中有一个方法dispatchActivityResult,顾名思义是可以分发Activity执行结果的方法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
6928    void dispatchActivityResult(String who, int requestCode,
6929 int resultCode, Intent data) {
6930 if (false) Log.v(
6931 TAG, "Dispatching result: who=" + who + ", reqCode=" + requestCode
6932 + ", resCode=" + resultCode + ", data=" + data);
6933 mFragments.noteStateNotSaved();
6934 if (who == null) {
6935 ......
6936 } else if (who.startsWith(REQUEST_PERMISSIONS_WHO_PREFIX)) {
6937 who = who.substring(REQUEST_PERMISSIONS_WHO_PREFIX.length());
6938 if (TextUtils.isEmpty(who)) {
6939 dispatchRequestPermissionsResult(requestCode, data);
6940 } else {
6941 Fragment frag = mFragments.findFragmentByWho(who);
6942 if (frag != null) {
6943 dispatchRequestPermissionsResultToFragment(requestCode, data, frag);
6944 }
6945 }
6946 } else if (who.startsWith("@android:view:")) {
6947 ......
6956 } else {
6957 ......
6961 }
6962 }

进入dispatchRequestPermissionsResult

1
2
3
4
5
6
7
8
9
    private void dispatchRequestPermissionsResult(int requestCode, Intent data) {
7081 mHasCurrentPermissionsRequest = false;
7082 // If the package installer crashed we may have not data - best effort.
7083 String[] permissions = (data != null) ? data.getStringArrayExtra(
7084 PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES) : new String[0];
7085 final int[] grantResults = (data != null) ? data.getIntArrayExtra(
7086 PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS) : new int[0];
7087 onRequestPermissionsResult(requestCode, permissions, grantResults);
7088 }

可以看到把传进来的Intent中的权限申请结果传递给onRequestPermissionsResult。在申请权限的Activity中,开发者可以覆写onRequestPermissionsResult,执行后续操作。

至于结果是怎么来的,就涉及到ActivityThread类,它是应用Activity的入口,有一个main()方法,能够管理Activity,包括Activity执行结果传递。

http://androidxref.com/7.1.2_r36/xref/frameworks/base/core/java/android/app/ActivityThread.java

回到打开申请权限的弹框Activity部分。通过Intent隐式启动该Activity,Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);.

getPackageManager()返回一个PackageManager:

1
2
3
4
5
6
7
8
9
    public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
3403 if (ArrayUtils.isEmpty(permissions)) {
3404 throw new IllegalArgumentException("permission cannot be null or empty");
3405 }
3406 Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
3407 intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
3408 intent.setPackage(getPermissionControllerPackageName());
3409 return intent;
3410 }

在设备上打开一个权限弹框,通过命令adb shell dumpsys activity top | grep ACTIVITY得到弹框的Activity名:

com.android.packageinstaller.permission.ui.GrantPermissionsActivity

http://androidxref.com/7.1.2_r36/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java

oncreate中,首先从传入的Intent中读取Permissions:

1
2
mRequestedPermissions = getIntent().getStringArrayExtra(
PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);

加载应用的权限组,遍历所有权限,找到其对应的权限组,根据设备授予权限的策略,重新设置权限组的状态。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 if (!group.isUserFixed() && !group.isPolicyFixed()) {
148 switch (permissionPolicy) {
149 case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: {
150 if (!group.areRuntimePermissionsGranted()) {
151 group.grantRuntimePermissions(false);
152 }
153 group.setPolicyFixed();
154 } break;
155
156 case DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY: {
157 if (group.areRuntimePermissionsGranted()) {
158 group.revokeRuntimePermissions(false);
159 }
160 group.setPolicyFixed();
161 } break;
162
163 default: {
164 if (!group.areRuntimePermissionsGranted()) {
165 mRequestGrantPermissionGroups.put(group.getName(),
166 new GroupState(group));
167 } else {
168 group.grantRuntimePermissions(false);
169 updateGrantResults(group);
170 }
171 } break;
172 }
173 }

可以看到重点在group.grantRuntimePermissionsgroup.revokeRuntimePermissions,一个授予权限,一个撤销权限,属于一体两面,以下之分析grantRuntimePermissions

group即AppPermissionGroup,找到grantRuntimePermissions

http://androidxref.com/7.1.2_r36/xref/packages/apps/PackageInstaller/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
 // Grant the permission if needed.
344 if (!permission.isGranted()) {
345 permission.setGranted(true);
346 mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
347 permission.getName(), mUserHandle);
348 }
349
350 // Update the permission flags.
351 if (!fixedByTheUser) {
352 // Now the apps can ask for the permission as the user
353 // no longer has it fixed in a denied state.
354 if (permission.isUserFixed() || permission.isUserSet()) {
355 permission.setUserFixed(false);
356 permission.setUserSet(false);
357 mPackageManager.updatePermissionFlags(permission.getName(),
358 mPackageInfo.packageName,
359 PackageManager.FLAG_PERMISSION_USER_FIXED
360 | PackageManager.FLAG_PERMISSION_USER_SET,
361 0, mUserHandle);
362 }
363 }
364 }

这里调用PackageManager类的grantRuntimePermission方法授予权限,updatePermissionFlags方法更新权限状态,PackageManager类为抽象类,实现在ApplicationPackageManagerService

1
2
3
4
5
6
7
8
9
    @Override
641 public void grantRuntimePermission(String packageName, String permissionName,
642 UserHandle user) {
643 try {
644 mPM.grantRuntimePermission(packageName, permissionName, user.getIdentifier());
645 } catch (RemoteException e) {
646 throw e.rethrowFromSystemServer();
647 }
648 }

mPM为IPackageManager,涉及到进程间通信,ApplicationPackageManagerService为客户端,IPackageManager为服务端,实现类为PackageManagerService

PackageManagerService类负责设备上所有应用的权限管理,根据Intent匹配四大组件,安装/删除应用。最终更新过的应用权限状态被写在/data/system/users/0/runtime-permissions.xml中中。