[toc]
0 综述 基于 Android 7.1.1 源码,分析系统的权限管理机制!
下面来看下系统中的 check permission 相关的接口:
1 Context 权限检查接口 - ContextImpl 在应用中申请权限离不开 Context,Context 中暴露了多个检查权限相关的接口,具体的实现是在 ContextImpl 中!
1.1 checkCallingPermission 检查来自其他进程的调用者的是否有权限,该接口不能用于检查自身是否具有权限!
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public int checkCallingPermission (String permission) { if (permission == null ) { throw new IllegalArgumentException("permission is null" ); } int pid = Binder.getCallingPid(); if (pid != Process.myPid()) { return checkPermission(permission, pid, Binder.getCallingUid()); } return PackageManager.PERMISSION_DENIED; }
应用程序权限检查,最终需要进入框架层!
1.2 checkCallingOrSelfPermission 该方法和 checkCallingPermission 不同,他还可以检查自身是否具有权限!1 2 3 4 5 6 7 8 9 @Override public int checkCallingOrSelfPermission (String permission) { if (permission == null ) { throw new IllegalArgumentException("permission is null" ); } return checkPermission(permission, Binder.getCallingPid(), Binder.getCallingUid()); }
1.3 checkPermission
该方法需要指定 pid 和 uid:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @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; } }
还有一个重载函数,需要额外传入一个 callerToken 实例,这里是一个 hide 方法,目前是隐藏的,只能由系统调用:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Override public int checkPermission (String permission, int pid, int uid, IBinder callerToken) { if (permission == null ) { throw new IllegalArgumentException("permission is null" ); } try { return ActivityManagerNative.getDefault().checkPermissionWithToken( permission, pid, uid, callerToken); } catch (RemoteException e) { return PackageManager.PERMISSION_DENIED; } }
1.4 Check Uri Permissions ContextImpl 还提供了检查 Uri Permissions 的接口,这些接口依赖于一个 modeFlags 标志位,取值有如下:
1 2 Intent.FLAG_GRANT_READ_URI_PERMISSION Intent.FLAG_GRANT_WRITE_URI_PERMISSION
下面我们来看下相关的接口的逻辑:
1.4.1 checkCallingUriPermission 同样的,这个方法只能检查其他进程是否具有访问 uri 权限!
1 2 3 4 5 6 7 8 9 10 @Override public int checkCallingUriPermission (Uri uri, int modeFlags) { int pid = Binder.getCallingPid(); if (pid != Process.myPid()) { return checkUriPermission(uri, pid, Binder.getCallingUid(), modeFlags); } return PackageManager.PERMISSION_DENIED; }
1.4.2 checkCallingOrSelfUriPermission 同样的,这个方法能检查其他进程或者自身是否具有访问 uri 权限!
1 2 3 4 5 6 @Override public int checkCallingOrSelfUriPermission (Uri uri, int modeFlags) { return checkUriPermission(uri, Binder.getCallingPid(), Binder.getCallingUid(), modeFlags); }
1.4.3 checkUriPermission checkUriPermission 方法一共有三个重载实现:
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 @Override public int checkUriPermission (Uri uri, String readPermission, String writePermission, int pid, int uid, int modeFlags) { if (DEBUG) { Log.i("foo" , "checkUriPermission: uri=" + uri + "readPermission=" + readPermission + " writePermission=" + writePermission + " pid=" + pid + " uid=" + uid + " mode" + modeFlags); } if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0 ) { if (readPermission == null || checkPermission(readPermission, pid, uid) == PackageManager.PERMISSION_GRANTED) { return PackageManager.PERMISSION_GRANTED; } } if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0 ) { if (writePermission == null || checkPermission(writePermission, pid, uid) == PackageManager.PERMISSION_GRANTED) { return PackageManager.PERMISSION_GRANTED; } } return uri != null ? checkUriPermission(uri, pid, uid, modeFlags) : PackageManager.PERMISSION_DENIED; }
第二个 checkUriPermission 会被第一个方法调用:
1 2 3 4 5 6 7 8 9 10 11 @Override public int checkUriPermission (Uri uri, int pid, int uid, int modeFlags) { try { return ActivityManagerNative.getDefault().checkUriPermission( ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags, resolveUserId(uri), null ); } catch (RemoteException e) { return PackageManager.PERMISSION_DENIED; } }
第三个 checkUriPermission 方法,需要传入一个 IBinder token 对象,这是一个 hide 方法,只能由系统调用!
1 2 3 4 5 6 7 8 9 10 11 12 @Override public int checkUriPermission (Uri uri, int pid, int uid, int modeFlags, IBinder callerToken) { try { return ActivityManagerNative.getDefault().checkUriPermission( ContentProvider.getUriWithoutUserId(uri), pid, uid, modeFlags, resolveUserId(uri), callerToken); } catch (RemoteException e) { return PackageManager.PERMISSION_DENIED; } }
2 框架层权限检查接口 - ActivityManagerService 我们来看看 Android 7.1.1 中框架层提供了那些用于校验权限的接口,这些接口主要位于 ActivityManager,ActivityManagerService 中,我们做一个简单的归类!
访问 ContentProvider 的权限校验;
1 String checkContentProviderPermissionLocked (ProviderInfo cpi, ProcessRecord r, int userId, boolean checkUser)
判断 pid,uid 对应的进程是否具有某个权限;
1 2 3 int checkCallingPermission (String permission) {...}int checkPermission (String permission, int pid, int uid) {...}
1 2 int checkComponentPermission (String permission, int pid, int uid, int owningUid, boolean exported) {...}
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 int checkGrantUriPermission (int callingUid, String targetPkg, Uri uri, final int modeFlags, int userId) {...}NeededUriGrants checkGrantUriPermissionFromIntentLocked (int callingUid, String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) {...} int checkGrantUriPermissionLocked (int callingUid, String targetPkg, GrantUri grantUri, final int modeFlags, int lastTargetUid) {...}int checkUriPermission (Uri uri, int pid, int uid, final int modeFlags, int userId, IBinder callerToken) {...} boolean checkUriPermissionLocked (GrantUri grantUri, int uid, final int modeFlags) {...}boolean checkHoldingPermissionsLocked (IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) {...} boolean checkHoldingPermissionsInternalLocked (IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) {...}
1 int checkPermissionWithToken (String permission, int pid, int uid, IBinder callerToken) {...}
同时,系统也提供了一些强制检查权限的接口,以 enforce 开头,我们在很多的系统调用过程中都会看到,这些接口会调用上面的接口来查询权限是否授予,没有授予会抛出异常,这里就不再列举!
2.1 checkCallingPermission 该方法用于判断调用者是否具有指定的权限,内部会自动通过 Binder 相关接口获得调用者的 pid 和 uid!!1 2 3 4 5 6 int checkCallingPermission (String permission) { return checkPermission(permission, Binder.getCallingPid(), UserHandle.getAppId(Binder.getCallingUid())); }
该方法内部会通过 Binder.getCallingPid() 和 Binder.getCallingUid() 方法,计算 pid 和 uid!
2.1.1 调用时机 checkCallingPermission 方法系统中调用的很多,一些重要服务都会调用它来检查权限,比如 InputManagerService,WindowManagerService,NotificationManagerService,DisplayManagerService 等等,下面只简单列出来几个:
2.1.1.1 NotificationManagerService.enforcePolicyAccess 1 2 3 4 5 6 7 8 9 10 11 12 private void enforcePolicyAccess (String pkg, String method) { if (PackageManager.PERMISSION_GRANTED == getContext().checkCallingPermission( android.Manifest.permission.MANAGE_NOTIFICATIONS)) { return ; } checkCallerIsSameApp(pkg); if (!checkPolicyAccess(pkg)) { Slog.w(TAG, "Notification policy access denied calling " + method); throw new SecurityException("Notification policy access denied" ); } }
NotificationManagerService 有很多方法设计到了权限校验,这里只是举例展示。
1 2 3 4 5 6 7 8 9 10 @Override public void addKeyboardLayoutForInputDevice (InputDeviceIdentifier identifier, String keyboardLayoutDescriptor) { if (!checkCallingPermission(android.Manifest.permission.SET_KEYBOARD_LAYOUT, "addKeyboardLayoutForInputDevice()" )) { throw new SecurityException("Requires SET_KEYBOARD_LAYOUT permission" ); } ... ... ... ... }
InputManagerService 有很多方法设计到了权限校验,这里只是举例展示。
2.1.1.3 WindowManagerService.startAppFreezingScreen 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Override public void startAppFreezingScreen (IBinder token, int configChanges) { if (!checkCallingPermission(android.Manifest.permission.MANAGE_APP_TOKENS, "setAppFreezingScreen()" )) { throw new SecurityException("Requires MANAGE_APP_TOKENS permission" ); } synchronized (mWindowMap) { if (configChanges == 0 && okToDisplay()) { if (DEBUG_ORIENTATION) Slog.v(TAG_WM, "Skipping set freeze of " + token); return ; } AppWindowToken wtoken = findAppWindowToken(token); if (wtoken == null || wtoken.appToken == null ) { Slog.w(TAG_WM, "Attempted to freeze screen with non-existing app token: " + wtoken); return ; } final long origId = Binder.clearCallingIdentity(); startAppFreezingScreenLocked(wtoken); Binder.restoreCallingIdentity(origId); } }
WindowManagerService 有很多方法设计到了权限校验,这里只是举例展示。
2.2 checkPermission 该方法用于判断 pid uid 是否有具体的权限,调用这个方法必须要知道对应的 pid 和 uid:
1 2 3 4 5 6 7 8 @Override public int checkPermission (String permission, int pid, int uid) { if (permission == null ) { return PackageManager.PERMISSION_DENIED; } return checkComponentPermission(permission, pid, uid, -1 , true ); }
2.2.1 调用时机 checkPermission 方法的调用也很多,所以简单的看几个:
2.2.1.1 ActivityManagerService.isGetTasksAllowed 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 private boolean isGetTasksAllowed (String caller, int callingPid, int callingUid) { boolean allowed = checkPermission(android.Manifest.permission.REAL_GET_TASKS, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED; if (!allowed) { if (checkPermission(android.Manifest.permission.GET_TASKS, callingPid, callingUid) == PackageManager.PERMISSION_GRANTED) { try { if (AppGlobals.getPackageManager().isUidPrivileged(callingUid)) { allowed = true ; if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid + " is using old GET_TASKS but privileged; allowing" ); } } catch (RemoteException e) { } } } if (!allowed) { if (DEBUG_TASKS) Slog.w(TAG, caller + ": caller " + callingUid + " does not hold REAL_GET_TASKS; limiting output" ); } return allowed; }
2.2.2 跨进程调用 调用方法:1 2 3 4 5 try { ActivityManagerNative.getDefault().checkPermission(...); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }
具体流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public int checkPermission (String permission, int pid, int uid) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeString(permission); data.writeInt(pid); data.writeInt(uid); mRemote.transact(CHECK_PERMISSION_TRANSACTION, data, reply, 0 ); reply.readException(); int res = reply.readInt(); data.recycle(); reply.recycle(); return res; }
1 2 3 4 5 6 7 8 9 10 case CHECK_PERMISSION_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); String perm = data.readString(); int pid = data.readInt(); int uid = data.readInt(); int res = checkPermission(perm, pid, uid); reply.writeNoException(); reply.writeInt(res); return true ; }
2.3 checkContentProviderPermissionLocked checkContentProviderPermissionLocked 方法用于判断:
进程 ProcessRecord 是否可以访问目标 ContentProvider 对象,该方法返回 null 表示该进程是有这个权限的!
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 private final String checkContentProviderPermissionLocked ( ProviderInfo cpi, ProcessRecord r, int userId, boolean checkUser) { final int callingPid = (r != null ) ? r.pid : Binder.getCallingPid(); final int callingUid = (r != null ) ? r.uid : Binder.getCallingUid(); boolean checkedGrants = false ; if (checkUser) { int tmpTargetUserId = mUserController.unsafeConvertIncomingUserLocked(userId); if (tmpTargetUserId != UserHandle.getUserId(callingUid)) { if (checkAuthorityGrants(callingUid, cpi, tmpTargetUserId, checkUser)) { return null ; } checkedGrants = true ; } userId = mUserController.handleIncomingUser(callingPid, callingUid, userId, false , ALLOW_NON_FULL, "checkContentProviderPermissionLocked " + cpi.authority, null ); if (userId != tmpTargetUserId) { checkedGrants = false ; } } if (checkComponentPermission(cpi.readPermission, callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported) == PackageManager.PERMISSION_GRANTED) { return null ; } if (checkComponentPermission(cpi.writePermission, callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported) == PackageManager.PERMISSION_GRANTED) { return null ; } PathPermission[] pps = cpi.pathPermissions; if (pps != null ) { int i = pps.length; while (i > 0 ) { i--; PathPermission pp = pps[i]; String pprperm = pp.getReadPermission(); if (pprperm != null && checkComponentPermission(pprperm, callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported) == PackageManager.PERMISSION_GRANTED) { return null ; } String ppwperm = pp.getWritePermission(); if (ppwperm != null && checkComponentPermission(ppwperm, callingPid, callingUid, cpi.applicationInfo.uid, cpi.exported) == PackageManager.PERMISSION_GRANTED) { return null ; } } } if (!checkedGrants && checkAuthorityGrants(callingUid, cpi, userId, checkUser)) { return null ; } String msg; if (!cpi.exported) { msg = "Permission Denial: opening provider " + cpi.name + " from " + (r != null ? r : "(null)" ) + " (pid=" + callingPid + ", uid=" + callingUid + ") that is not exported from uid " + cpi.applicationInfo.uid; } else { msg = "Permission Denial: opening provider " + cpi.name + " from " + (r != null ? r : "(null)" ) + " (pid=" + callingPid + ", uid=" + callingUid + ") requires " + cpi.readPermission + " or " + cpi.writePermission; } Slog.w(TAG, msg); return msg; }
我们看到了这里调用了 checkComponentPermission 方法,判断访问者对该 ContentProvider 是否具有读或写的权限!
2.3.1 调用时机 2.3.1.1 ActivityManagerService.getContentProviderImpl 该方法主要是在访问 provider 的时候做权限校验的,调用的方法在 getContentProviderImpl 方法;
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 38 39 private ContentProviderHolder getContentProviderImpl (IApplicationThread caller, String name, IBinder token, boolean stable, int userId) { ContentProviderRecord cpr; ContentProviderConnection conn = null ; ProviderInfo cpi = null ; synchronized (this ) { ... ... ... boolean providerRunning = cpr != null && cpr.proc != null && !cpr.proc.killed; if (providerRunning) { cpi = cpr.info; String msg; checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission" ); if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, checkCrossUser)) != null ) { throw new SecurityException(msg); } checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission" ); ... ... ... ... } if (!providerRunning) { ... ... ... ... String msg; checkTime(startTime, "getContentProviderImpl: before checkContentProviderPermission" ); if ((msg = checkContentProviderPermissionLocked(cpi, r, userId, !singleton)) != null ) { throw new SecurityException(msg); } checkTime(startTime, "getContentProviderImpl: after checkContentProviderPermission" ); ... ... ... ... } checkTime(startTime, "getContentProviderImpl: done!" ); } ... ... ... ... return cpr != null ? cpr.newHolder(conn) : null ; }
getContentProviderImpl 方法我就不多说了,之前分析总结过!
2.4 checkGrantUriPermission 判断 callingUid 是否能够授予 targetPkg 访问 uri 的权限,modeFlags 指定了访问模式!
1 2 3 4 5 6 7 8 9 public int checkGrantUriPermission (int callingUid, String targetPkg, Uri uri, final int modeFlags, int userId) { enforceNotIsolatedCaller("checkGrantUriPermission" ); synchronized (this ) { return checkGrantUriPermissionLocked(callingUid, targetPkg, new GrantUri(userId, uri, false ), modeFlags, -1 ); } }
该方法会对参数做一个简单的封装,然后调用 checkGrantUriPermissionLocked 继续处理!
2.4.1 调用时机 2.4.1.1 Clipboard.setPrimaryClip 这里我们通过一个简单的例子来看下这个方法的使用,Android 系统有一个 Clipboard,当我们机型拷贝的时候会调用其 setPrimaryClip 方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 public void setPrimaryClip (ClipData clip, String callingPackage) { synchronized (this ) { if (clip != null && clip.getItemCount() <= 0 ) { throw new IllegalArgumentException("No items" ); } final int callingUid = Binder.getCallingUid(); if (mAppOps.noteOp(AppOpsManager.OP_WRITE_CLIPBOARD, callingUid, callingPackage) != AppOpsManager.MODE_ALLOWED) { return ; } checkDataOwnerLocked(clip, callingUid); ... ... ...
调用了 checkDataOwnerLocked 方法:1 2 3 4 5 6 7 private final void checkDataOwnerLocked (ClipData data, int uid) { final int N = data.getItemCount(); for (int i=0 ; i<N; i++) { checkItemOwnerLocked(data.getItemAt(i), uid); } }
调用了 checkItemOwnerLocked 方法:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 private final void checkUriOwnerLocked (Uri uri, int uid) { if (!"content" .equals(uri.getScheme())) { return ; } long ident = Binder.clearCallingIdentity(); try { mAm.checkGrantUriPermission(uid, null , ContentProvider.getUriWithoutUserId(uri), Intent.FLAG_GRANT_READ_URI_PERMISSION, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(uid))); } catch (RemoteException e) { } finally { Binder.restoreCallingIdentity(ident); } }
这个调用流程很清晰!
2.4.1.2 VoiceInteractionSessionConnection.deliverSessionDataLocked 还有一个是语音识别服务,需要传递数据的时候:
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 private void deliverSessionDataLocked (AssistDataForActivity assistDataForActivity) { Bundle assistData = assistDataForActivity.data.getBundle( VoiceInteractionSession.KEY_DATA); AssistStructure structure = assistDataForActivity.data.getParcelable( VoiceInteractionSession.KEY_STRUCTURE); AssistContent content = assistDataForActivity.data.getParcelable( VoiceInteractionSession.KEY_CONTENT); int uid = assistDataForActivity.data.getInt(Intent.EXTRA_ASSIST_UID, -1 ); if (uid >= 0 && content != null ) { Intent intent = content.getIntent(); if (intent != null ) { ClipData data = intent.getClipData(); if (data != null && Intent.isAccessUriMode(intent.getFlags())) { grantClipDataPermissions(data, intent.getFlags(), uid, mCallingUid, mSessionComponentName.getPackageName()); } } ClipData data = content.getClipData(); if (data != null ) { grantClipDataPermissions(data, Intent.FLAG_GRANT_READ_URI_PERMISSION, uid, mCallingUid, mSessionComponentName.getPackageName()); } } try { mSession.handleAssist(assistData, structure, content, assistDataForActivity.activityIndex, assistDataForActivity.activityCount); } catch (RemoteException e) { } }
调用 grantClipDataPermissions 方法,这里的 srcUid 是 EXTRA_ASSIST_UID 对应的 uid!!1 2 3 4 5 6 7 8 void grantClipDataPermissions (ClipData data, int mode, int srcUid, int destUid, String destPkg) { final int N = data.getItemCount(); for (int i=0 ; i<N; i++) { grantClipDataItemPermission(data.getItemAt(i), mode, srcUid, destUid, destPkg); } }
再调用 grantClipDataItemPermission 方法:1 2 3 4 5 6 7 8 9 10 11 void grantClipDataItemPermission (ClipData.Item item, int mode, int srcUid, int destUid, String destPkg) { if (item.getUri() != null ) { grantUriPermission(item.getUri(), mode, srcUid, destUid, destPkg); } Intent intent = item.getIntent(); if (intent != null && intent.getData() != null ) { grantUriPermission(intent.getData(), mode, srcUid, destUid, destPkg); } }
继续调用 grantUriPermission 方法:1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 void grantUriPermission (Uri uri, int mode, int srcUid, int destUid, String destPkg) { if (!"content" .equals(uri.getScheme())) { return ; } long ident = Binder.clearCallingIdentity(); try { mAm.checkGrantUriPermission(srcUid, null , ContentProvider.getUriWithoutUserId(uri), mode, ContentProvider.getUserIdFromUri(uri, UserHandle.getUserId(srcUid))); int sourceUserId = ContentProvider.getUserIdFromUri(uri, mUser); uri = ContentProvider.getUriWithoutUserId(uri); mAm.grantUriPermissionFromOwner(mPermissionOwner, srcUid, destPkg, uri, Intent.FLAG_GRANT_READ_URI_PERMISSION, sourceUserId, mUser); } catch (RemoteException e) { } catch (SecurityException e) { Slog.w(TAG, "Can't propagate permission" , e); } finally { Binder.restoreCallingIdentity(ident); } }
不多说了!!
2.4.2 跨进程调用 调用方法:1 2 3 4 5 try { ActivityManagerNative.getDefault().checkGrantUriPermission(...); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }
具体流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 public int checkGrantUriPermission (int callingUid, String targetPkg, Uri uri, int modeFlags, int userId) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(callingUid); data.writeString(targetPkg); uri.writeToParcel(data, 0 ); data.writeInt(modeFlags); data.writeInt(userId); mRemote.transact(CHECK_GRANT_URI_PERMISSION_TRANSACTION, data, reply, 0 ); reply.readException(); int res = reply.readInt(); data.recycle(); reply.recycle(); return res; }
1 2 3 4 5 6 7 8 9 10 11 12 case CHECK_GRANT_URI_PERMISSION_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); int callingUid = data.readInt(); String targetPkg = data.readString(); Uri uri = Uri.CREATOR.createFromParcel(data); int modeFlags = data.readInt(); int userId = data.readInt(); int res = checkGrantUriPermission(callingUid, targetPkg, uri, modeFlags, userId); reply.writeNoException(); reply.writeInt(res); return true ; }
2.5 checkGrantUriPermissionFromIntentLocked 该方法用于判断是否能够授予 targetPkg 访问 uri 的权限,uri 来自接收到的 intent;
和 2.6 的 checkGrantUriPermissionLocked 很类似,区别是 checkGrantUriPermissionLocked 直接操作的是 Uri;而该方法直接作用于 Intent!
返回 null 表示不需要授予权限!
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 NeededUriGrants checkGrantUriPermissionFromIntentLocked (int callingUid, String targetPkg, Intent intent, int mode, NeededUriGrants needed, int targetUserId) { if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "Checking URI perm to data=" + (intent != null ? intent.getData() : null ) + " clip=" + (intent != null ? intent.getClipData() : null ) + " from " + intent + "; flags=0x" + Integer.toHexString(intent != null ? intent.getFlags() : 0 )); if (targetPkg == null ) { throw new NullPointerException("targetPkg" ); } if (intent == null ) { return null ; } Uri data = intent.getData(); ClipData clip = intent.getClipData(); if (data == null && clip == null ) { return null ; } int contentUserHint = intent.getContentUserHint(); if (contentUserHint == UserHandle.USER_CURRENT) { contentUserHint = UserHandle.getUserId(callingUid); } final IPackageManager pm = AppGlobals.getPackageManager(); int targetUid; if (needed != null ) { targetUid = needed.targetUid; } else { try { targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, targetUserId); } catch (RemoteException ex) { return null ; } if (targetUid < 0 ) { if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "Can't grant URI permission no uid for: " + targetPkg + " on user " + targetUserId); return null ; } } if (data != null ) { GrantUri grantUri = GrantUri.resolve(contentUserHint, data); targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode, targetUid); if (targetUid > 0 ) { if (needed == null ) { needed = new NeededUriGrants(targetPkg, targetUid, mode); } needed.add(grantUri); } } if (clip != null ) { for (int i=0 ; i<clip.getItemCount(); i++) { Uri uri = clip.getItemAt(i).getUri(); if (uri != null ) { GrantUri grantUri = GrantUri.resolve(contentUserHint, uri); targetUid = checkGrantUriPermissionLocked(callingUid, targetPkg, grantUri, mode, targetUid); if (targetUid > 0 ) { if (needed == null ) { needed = new NeededUriGrants(targetPkg, targetUid, mode); } needed.add(grantUri); } } else { Intent clipIntent = clip.getItemAt(i).getIntent(); if (clipIntent != null ) { NeededUriGrants newNeeded = checkGrantUriPermissionFromIntentLocked( callingUid, targetPkg, clipIntent, mode, needed, targetUserId); if (newNeeded != null ) { needed = newNeeded; } } } } } return needed; }
该方法会返回一个 NeededUriGrants 对象,用于保存该 intent 携带的所有需要授权访问了 GrantUri 对象,包括其 ClipData 中的 intent 携带的 uri:
1 2 3 4 5 6 7 8 9 10 11 static class NeededUriGrants extends ArrayList <GrantUri > { final String targetPkg; final int targetUid; final int flags; NeededUriGrants(String targetPkg, int targetUid, int flags) { this .targetPkg = targetPkg; this .targetUid = targetUid; this .flags = flags; } }
这里的 mode 可以设置下面的两个标志位!
1 2 3 public static final int FLAG_GRANT_READ_URI_PERMISSION = 0x00000001 ;public static final int FLAG_GRANT_WRITE_URI_PERMISSION = 0x00000002 ;
如果设置该标记,Intent 的接受者将会被赋予读/写 Intent 中 uri 数据和 Intent.ClipData 中的 uri 的权限。
当应用到 Intent 的 ClipData 时,其携带的所有 uri 都会被 intent 接收者读/写,同时其会递归处理其携带的所有 intent!
其实,mode 的值是为了标志授予的权限类型是读还是写的,都设置的话,那就是可读可写!
2.5.1 调用时机 该方法的调用实际比较少,ActiveServices 和 ActivityManagerService 中:
2.5.1.1 ActiveServices.startServiceLocked 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 ComponentName startServiceLocked (IApplicationThread caller, Intent service, String resolvedType, int callingPid, int callingUid, String callingPackage, final int userId) throws TransactionTooLargeException { if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE, "startService: " + service + " type=" + resolvedType + " args=" + service.getExtras()); ... ... ... ... if (!r.startRequested) { final long token = Binder.clearCallingIdentity(); try { final int allowed = mAm.checkAllowBackgroundLocked( r.appInfo.uid, r.packageName, callingPid, true ); if (allowed != ActivityManager.APP_START_MODE_NORMAL) { Slog.w(TAG, "Background start not allowed: service " + service + " to " + r.name.flattenToShortString() + " from pid=" + callingPid + " uid=" + callingUid + " pkg=" + callingPackage); return null ; } } finally {f Binder.restoreCallingIdentity(token); } } NeededUriGrants neededGrants = mAm.checkGrantUriPermissionFromIntentLocked( callingUid, r.packageName, service, service.getFlags(), null , r.userId); ... ... ... ... return startServiceInnerLocked(smap, service, r, callerFg, addToStarting); }
这里就不多说了!
2.6 checkGrantUriPermissionLocked 判断是否需要授予 targetPkg 访问 uri 的权限,modeFlags 指定了访问模式, callingUid 是调用者 uid!
如果 callingUid 不被允许,会抛出 SecurityException 异常! 如果方法返回了 targetPkg 的 uid,说明需要授予权限;如果返回 -1,表示不需要或者无法授予(比如目标包已经有权限访问 uri)!
lastTargetUid 表示 targetPkg 的 uid,如果已经知道了 targetPkg.uid,那么就设置 lastTargetUid 为 targetPkg.uid 即可;如果不知道,那么设置为 -1,方法内部会计算出对应的 uid!
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 int checkGrantUriPermissionLocked (int callingUid, String targetPkg, GrantUri grantUri, final int modeFlags, int lastTargetUid) { if (!Intent.isAccessUriMode(modeFlags)) { return -1 ; } if (targetPkg != null ) { if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "Checking grant " + targetPkg + " permission to " + grantUri); } final IPackageManager pm = AppGlobals.getPackageManager(); if (!ContentResolver.SCHEME_CONTENT.equals(grantUri.uri.getScheme())) { if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "Can't grant URI permission for non-content URI: " + grantUri); return -1 ; } final String authority = grantUri.uri.getAuthority(); final ProviderInfo pi = getProviderInfoLocked(authority, grantUri.sourceUserId, MATCH_DEBUG_TRIAGED_MISSING); if (pi == null ) { Slog.w(TAG, "No content provider found for permission check: " + grantUri.uri.toSafeString()); return -1 ; } int targetUid = lastTargetUid; if (targetUid < 0 && targetPkg != null ) { try { targetUid = pm.getPackageUid(targetPkg, MATCH_DEBUG_TRIAGED_MISSING, UserHandle.getUserId(callingUid)); if (targetUid < 0 ) { if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "Can't grant URI permission no uid for: " + targetPkg); return -1 ; } } catch (RemoteException ex) { return -1 ; } } if (targetUid >= 0 ) { if (checkHoldingPermissionsLocked(pm, pi, grantUri, targetUid, modeFlags)) { if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "Target " + targetPkg + " already has full permission to " + grantUri); return -1 ; } } else { boolean allowed = pi.exported; if ((modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) != 0 ) { if (pi.readPermission != null ) { allowed = false ; } } if ((modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) != 0 ) { if (pi.writePermission != null ) { allowed = false ; } } if (allowed) { return -1 ; } } boolean specialCrossUserGrant = UserHandle.getUserId(targetUid) != grantUri.sourceUserId && checkHoldingPermissionsInternalLocked(pm, pi, grantUri, callingUid, modeFlags, false ); if (!specialCrossUserGrant) { if (!pi.grantUriPermissions) { throw new SecurityException("Provider " + pi.packageName + "/" + pi.name + " does not allow granting of Uri permissions (uri " + grantUri + ")" ); } if (pi.uriPermissionPatterns != null ) { final int N = pi.uriPermissionPatterns.length; boolean allowed = false ; for (int i=0 ; i<N; i++) { if (pi.uriPermissionPatterns[i] != null && pi.uriPermissionPatterns[i].match(grantUri.uri.getPath())) { allowed = true ; break ; } } if (!allowed) { throw new SecurityException("Provider " + pi.packageName + "/" + pi.name + " does not allow granting of permission to path of Uri " + grantUri); } } } if (UserHandle.getAppId(callingUid) != Process.SYSTEM_UID) { if (!checkHoldingPermissionsLocked(pm, pi, grantUri, callingUid, modeFlags)) { if (!checkUriPermissionLocked(grantUri, callingUid, modeFlags)) { throw new SecurityException("Uid " + callingUid + " does not have permission to uri " + grantUri); } } } return targetUid; }
我们看到这里会有几种情况出现异常:
uri 所属的 provider 的 grantUriPermissions 为 false,说明 provider 禁止 uri 访问,会抛出异常;
如果 provider 的 grantUriPermissions 为 true,但是 provider 的 uriPermissionPatterns 中的 uri 没有和要访问的 uri 相匹配的,抛出异常;
如果调用者接口的程序,不具有对该 uri 的访问权限,那么就会抛出异常;
这里也用到了那 2 个 flags,不在多说了!
2.6.1 调用时机 该方法的调用比较少,在 ActivityManagerService 中:
一个是 2.4 checkGrantUriPermission 方法;
一个是 2.5 checkGrantUriPermissionFromIntentLocked 方法;
一个是在 grantUriPermissionLocked 方法,关于 grant 后面单独分析;
2.7 checkHoldingPermissionsLocked 判断 uid 是否已经有访问 uri 的权限!1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 private final boolean checkHoldingPermissionsLocked ( IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags) { if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "checkHoldingPermissionsLocked: uri=" + grantUri + " uid=" + uid); if (UserHandle.getUserId(uid) != grantUri.sourceUserId) { if (ActivityManager.checkComponentPermission(INTERACT_ACROSS_USERS, uid, -1 , true ) != PERMISSION_GRANTED) { return false ; } } return checkHoldingPermissionsInternalLocked(pm, pi, grantUri, uid, modeFlags, true ); }
2.7.1 调用时机 该方法的调用比较少,在 ActivityManagerService 中:
一个是 2.6 checkGrantUriPermissionLocked 方法;
一个是在 revokeUriPermissionLocked 方法,关于 revoke 后面单独分析;
2.8 checkHoldingPermissionsInternalLocked 判断 uid 是否已经具有访问 uri 的权限,该接口并不检查是否是跨用户的调用!
considerUidPermissions 参数表示是否检查 uid 权限,一般我们是会传入 true 的!
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 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 private final boolean checkHoldingPermissionsInternalLocked (IPackageManager pm, ProviderInfo pi, GrantUri grantUri, int uid, final int modeFlags, boolean considerUidPermissions) { if (pi.applicationInfo.uid == uid) { return true ; } else if (!pi.exported) { return false ; } boolean readMet = (modeFlags & Intent.FLAG_GRANT_READ_URI_PERMISSION) == 0 ; boolean writeMet = (modeFlags & Intent.FLAG_GRANT_WRITE_URI_PERMISSION) == 0 ; try { if (!readMet && pi.readPermission != null && considerUidPermissions && (pm.checkUidPermission(pi.readPermission, uid) == PERMISSION_GRANTED)) { readMet = true ; } if (!writeMet && pi.writePermission != null && considerUidPermissions && (pm.checkUidPermission(pi.writePermission, uid) == PERMISSION_GRANTED)) { writeMet = true ; } boolean allowDefaultRead = pi.readPermission == null ; boolean allowDefaultWrite = pi.writePermission == null ; final PathPermission[] pps = pi.pathPermissions; if (pps != null ) { final String path = grantUri.uri.getPath(); int i = pps.length; while (i > 0 && (!readMet || !writeMet)) { i--; PathPermission pp = pps[i]; if (pp.match(path)) { if (!readMet) { final String pprperm = pp.getReadPermission(); if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "Checking read perm for " + pprperm + " for " + pp.getPath() + ": match=" + pp.match(path) + " check=" + pm.checkUidPermission(pprperm, uid)); if (pprperm != null ) { if (considerUidPermissions && pm.checkUidPermission(pprperm, uid) == PERMISSION_GRANTED) { readMet = true ; } else { allowDefaultRead = false ; } } } if (!writeMet) { final String ppwperm = pp.getWritePermission(); if (DEBUG_URI_PERMISSION) Slog.v(TAG_URI_PERMISSION, "Checking write perm " + ppwperm + " for " + pp.getPath() + ": match=" + pp.match(path) + " check=" + pm.checkUidPermission(ppwperm, uid)); if (ppwperm != null ) { if (considerUidPermissions && pm.checkUidPermission(ppwperm, uid) == PERMISSION_GRANTED) { writeMet = true ; } else { allowDefaultWrite = false ; } } } } } } if (allowDefaultRead) readMet = true ; if (allowDefaultWrite) writeMet = true ; } catch (RemoteException e) { return false ; } return readMet && writeMet; }
通过逻辑可以知道:
1 2 3 4 pi.readPermission == null ; pi.writePermission == null ; pi.pathPermissions == null ; pi.exported == true
也就是说,provider 不设置任何权限,同时是暴露的!!
2.8.1 调用时机 该方法的调用比较少,在 ActivityManagerService 中:
一个是 2.7 checkHoldingPermissionsLocked 方法中;
一个是在 revokeUriPermissionLocked 方法,关于 revoke 后面单独分析;
2.9 checkUriPermission 用于检查 uid 是否持有 uri permissions:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 public int checkUriPermission (Uri uri, int pid, int uid, final int modeFlags, int userId, IBinder callerToken) { enforceNotIsolatedCaller("checkUriPermission" ); Identity tlsIdentity = sCallerIdentity.get(); if (tlsIdentity != null && tlsIdentity.token == callerToken) { uid = tlsIdentity.uid; pid = tlsIdentity.pid; } if (pid == MY_PID) { return PackageManager.PERMISSION_GRANTED; } synchronized (this ) { return checkUriPermissionLocked(new GrantUri(userId, uri, false ), uid, modeFlags) ? PackageManager.PERMISSION_GRANTED : PackageManager.PERMISSION_DENIED; } }
这里的 sCallerIdentity 是一个线程本地变量:1 private static final ThreadLocal<Identity> sCallerIdentity = new ThreadLocal<Identity>();
我们通过传入的 IBinder 对象和 Identity.token 进行匹配,然后就可以获得调用者的进程 pid 和 uid!
2.9.1 调用时机 - 跨进程 该方法主要是从 Context 的 checkUriPermission 调用过来的!
调用方法:1 2 3 4 5 try { ActivityManagerNative.getDefault().checkUriPermission(...); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }
具体流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 public int checkUriPermission (Uri uri, int pid, int uid, int mode, int userId, IBinder callerToken) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); uri.writeToParcel(data, 0 ); data.writeInt(pid); data.writeInt(uid); data.writeInt(mode); data.writeInt(userId); data.writeStrongBinder(callerToken); mRemote.transact(CHECK_URI_PERMISSION_TRANSACTION, data, reply, 0 ); reply.readException(); int res = reply.readInt(); data.recycle(); reply.recycle(); return res; }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 case CHECK_URI_PERMISSION_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); Uri uri = Uri.CREATOR.createFromParcel(data); int pid = data.readInt(); int uid = data.readInt(); int mode = data.readInt(); int userId = data.readInt(); IBinder callerToken = data.readStrongBinder(); int res = checkUriPermission(uri, pid, uid, mode, userId, callerToken); reply.writeNoException(); reply.writeInt(res); return true ; }
2.10 checkUriPermissionLocked 用于检查 uid 是否已经被授予uri permissions!
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 private final boolean checkUriPermissionLocked (GrantUri grantUri, int uid, final int modeFlags) { final boolean persistable = (modeFlags & Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION) != 0 ; final int minStrength = persistable ? UriPermission.STRENGTH_PERSISTABLE : UriPermission.STRENGTH_OWNED; if (uid == 0 ) { return true ; } final ArrayMap<GrantUri, UriPermission> perms = mGrantedUriPermissions.get(uid); if (perms == null ) return false ; final UriPermission exactPerm = perms.get(grantUri); if (exactPerm != null && exactPerm.getStrength(modeFlags) >= minStrength) { return true ; } final int N = perms.size(); for (int i = 0 ; i < N; i++) { final UriPermission perm = perms.valueAt(i); if (perm.uri.prefix && grantUri.uri.isPathPrefixMatch(perm.uri.uri) && perm.getStrength(modeFlags) >= minStrength) { return true ; } } return false ; }
对于 modeFlags,除了能设置上面的 2 个标志之外,还可以设置下面的标志位:
1 Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
FLAG_GRANT_READ_URI_PERMISSION 跟 FLAG_GRANT_WRITE_URI_PERMISSION 是用于授予临时权限,如果和 FLAG_GRANT_PERSISTABLE_URI_PERMISSION 结合使用,URI 权限会持久存在,重启也无法撤消,直到明确的用 revokeUriPermission(Uri, int) 撤销。
这个 flag 只提供可能持久授权。但是接收的应用必须调用 ContentResolver.takePersistableUriPermission(Uri, int) 方法实现 。
2.10.1 调用时机 该方法的调用比较少,在 ActivityManagerService 中:
一个是 2.9 checkUriPermission 方法中;
一个是 2.10 checkUriPermissionLocked 方法中;
2.11 checkPermissionWithToken 检查 IBinder 对象对应的进程是否具有指定权限!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 @Override public int checkPermissionWithToken (String permission, int pid, int uid, IBinder callerToken) { if (permission == null ) { return PackageManager.PERMISSION_DENIED; } Identity tlsIdentity = sCallerIdentity.get(); if (tlsIdentity != null && tlsIdentity.token == callerToken) { Slog.d(TAG, "checkComponentPermission() adjusting {pid,uid} to {" + tlsIdentity.pid + "," + tlsIdentity.uid + "}" ); uid = tlsIdentity.uid; pid = tlsIdentity.pid; } return checkComponentPermission(permission, pid, uid, -1 , true ); }
2.11.1 跨进程调用 调用方法:1 2 3 4 5 try { ActivityManagerNative.getDefault().checkPermissionWithToken(...); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); }
具体流程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 public int checkPermissionWithToken (String permission, int pid, int uid, IBinder callerToken) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeString(permission); data.writeInt(pid); data.writeInt(uid); data.writeStrongBinder(callerToken); mRemote.transact(CHECK_PERMISSION_WITH_TOKEN_TRANSACTION, data, reply, 0 ); reply.readException(); int res = reply.readInt(); data.recycle(); reply.recycle(); return res; }
1 2 3 4 5 6 7 8 9 10 11 12 case CHECK_PERMISSION_WITH_TOKEN_TRANSACTION: { data.enforceInterface(IActivityManager.descriptor); String perm = data.readString(); int pid = data.readInt(); int uid = data.readInt(); IBinder token = data.readStrongBinder(); int res = checkPermissionWithToken(perm, pid, uid, token); reply.writeNoException(); reply.writeInt(res); return true ; }
2.12 checkComponentPermission 1 2 3 4 5 6 7 8 9 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); }
2.12.1 ActivityM.checkComponentPermission 这个方法是一个 hide 方法,只能由系统调用!
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) { final int appId = UserHandle.getAppId(uid); if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) { return PackageManager.PERMISSION_GRANTED; } if (UserHandle.isIsolated(uid)) { return PackageManager.PERMISSION_DENIED; } if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) { return PackageManager.PERMISSION_GRANTED; } if (!exported) { return PackageManager.PERMISSION_DENIED; } if (permission == null ) { return PackageManager.PERMISSION_GRANTED; } try { return AppGlobals.getPackageManager() .checkUidPermission(permission, uid); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
2.12.2 ActivityM.checkUidPermission 同时 ActivityManager 也有 checkUidPermission 方法,这方法是一个 hide 方法,只能由系统调用!
该方法会直接访问 pms 的接口!
1 2 3 4 5 6 7 8 9 10 public static int checkUidPermission (String permission, int uid) { try { return AppGlobals.getPackageManager() .checkUidPermission(permission, uid); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
3 权限检查关键接口 - PackageManagerService 通过分析系统中的一些 checkPermissions 接口,我们发现最终都调用了 PackageManagerService.checkUidPermission 方法,下面我们去看下:
3.1 checkUidPermission 用于检查 uid 是否具有权限 permName!
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 38 39 40 41 @Override public int checkUidPermission (String permName, int uid) { final int userId = UserHandle.getUserId(uid); if (!sUserManager.exists(userId)) { return PackageManager.PERMISSION_DENIED; } synchronized (mPackages) { Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); if (obj != null ) { final SettingBase ps = (SettingBase) obj; final PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.hasPermission(permName, userId)) { return PackageManager.PERMISSION_GRANTED; } if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) { return PackageManager.PERMISSION_GRANTED; } } else { ArraySet<String> perms = mSystemPermissions.get(uid); if (perms != null ) { if (perms.contains(permName)) { return PackageManager.PERMISSION_GRANTED; } if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms .contains(Manifest.permission.ACCESS_FINE_LOCATION)) { return PackageManager.PERMISSION_GRANTED; } } } } return PackageManager.PERMISSION_DENIED; }
我们来看下 getUserIdLPr 方法的逻辑:
1 2 3 4 5 6 7 8 9 public Object getUserIdLPr (int uid) { if (uid >= Process.FIRST_APPLICATION_UID) { final int N = mUserIds.size(); final int index = uid - Process.FIRST_APPLICATION_UID; return index < N ? mUserIds.get(index) : null ; } else { return mOtherUserIds.get(uid); } }
从 Process.FIRST_APPLICATION_UID(10000) 开始的 uid 是普通 Android 的应用程序,即使用户安装的应用程序,最大值为 Process.LAST_APPLICATION_UID(19999)!
而 Process.FIRST_APPLICATION_UID 以下的 uid 都是系统应用和系统进程的!
PackageSetting 和 SharedUserSetting :
如果应用程序是独立 uid 的话,那么其会有一个 PackageSetting 对象,然后会根据其 uid 的类型,添加到 mUserIds 或者 mOtherUserIds;
如果应用程序是共享 uid 的话,除了会创建 PackageSetting 对象,还会创建一个 SharedUserSetting 对象,用来封装共享 uid 的信息 ,然后会根据其 uid 的类型,将 SharedUserSetting 对象添加到 mUserIds 或者 mOtherUserIds;
如果应用是独立 uid 的话,那么 getUserIdLPr 返回是 PackageSetting 对象!
如果应用是共享 uid 的话,那么 getUserIdLPr 返回的是 SharedUserSetting 对象,这是因为共享同一个 uid 的应用持有相同的权限!
PackageSetting 和 SharedUserSetting 都有一个 PermissionsState 对象,用于封装 package 在不同 userId 下的权限授予情况!
3.2 checkPermission PMS 还有另外一个用于检查权限的方法,判断包名对应的应用程序是否有权限!
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 @Override public int checkPermission (String permName, String pkgName, int userId) { if (!sUserManager.exists(userId)) { return PackageManager.PERMISSION_DENIED; } synchronized (mPackages) { final PackageParser.Package p = mPackages.get(pkgName); if (p != null && p.mExtras != null ) { final PackageSetting ps = (PackageSetting) p.mExtras; final PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.hasPermission(permName, userId)) { return PackageManager.PERMISSION_GRANTED; } if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) { return PackageManager.PERMISSION_GRANTED; } } } return PackageManager.PERMISSION_DENIED; }
该方法用于判断 pkgName 在 userId 下,是否有权限 permName!
3.2.1 调用时机 PMS 的接口可以被其他 check 接口调用,同时我们也可以直接调用其进行权限检查:
3.2.1.1 BroadcastQueue.processNextBroadcast 在我们发送广播的时候,如果广播指定了接收者的权限,那么我们会校验
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 final void processNextBroadcast (boolean fromMsg) { ... ... ... if (!skip && info.activityInfo.applicationInfo.uid != Process.SYSTEM_UID && r.requiredPermissions != null && r.requiredPermissions.length > 0 ) { for (int i = 0 ; i < r.requiredPermissions.length; i++) { String requiredPermission = r.requiredPermissions[i]; try { perm = AppGlobals.getPackageManager(). checkPermission(requiredPermission, info.activityInfo.applicationInfo.packageName, UserHandle .getUserId(info.activityInfo.applicationInfo.uid)); } catch (RemoteException e) { perm = PackageManager.PERMISSION_DENIED; } ... ... ... } } ... ... ... }