新聞中心
前言

在德惠等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供成都網(wǎng)站建設(shè)、成都網(wǎng)站制作 網(wǎng)站設(shè)計制作定制開發(fā),公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),成都品牌網(wǎng)站建設(shè),成都營銷網(wǎng)站建設(shè),外貿(mào)網(wǎng)站建設(shè),德惠網(wǎng)站建設(shè)費用合理。
Android系統(tǒng)在MarshMallow之前,權(quán)限都是在安裝的時候授予的,雖然在4.3時,Google就試圖在源碼里面引入AppOpsManager來達(dá)到動態(tài)控制權(quán)限的目的,但由于不太成熟,在Release版本中都是把這個功能給隱藏掉的。在6.0之后,Google為了簡化安裝流程且方便用戶控制權(quán)限,正式引入了runtime-permission,允許用戶在運行的時候動態(tài)控制權(quán)限。對于開發(fā)而言就是將targetSdkVersion設(shè)置為23,并且在相應(yīng)的時機(jī)動態(tài)申請權(quán)限,在適配了Android6.0的App運行在Android 6.0+的手機(jī)上時,就會調(diào)用6.0相關(guān)的API,不過在低版本的手機(jī)上,仍然是按安裝時權(quán)限處理。
AppOpsManager動態(tài)權(quán)限管理:官方預(yù)演的權(quán)限管理
AppOpsManager是Google在Android4.3引入的動態(tài)權(quán)限管理方式,不過,Google覺得不成熟,所以在每個發(fā)行版的時候,總是會將這個功能給屏蔽掉。該功能跟國內(nèi)的權(quán)限動態(tài)管理表現(xiàn)類似,這里用CyanogenMod12里面的實現(xiàn)講述一下,(國內(nèi)的ROM源碼拿不到,不過從表現(xiàn)來看,實現(xiàn)應(yīng)該類似)。AppOpsManager實現(xiàn)的動態(tài)管理的本質(zhì)是:將鑒權(quán)放在每個服務(wù)內(nèi)部,比如,如果App要申請定位權(quán)限,定位服務(wù)LocationManagerService會向AppOpsService查詢是否授予了App定位權(quán)限,如果需要授權(quán),就彈出一個系統(tǒng)對話框讓用戶操作,并根據(jù)用戶的操作將結(jié)果持久化在文件中,如果在Setting里設(shè)置了響應(yīng)的權(quán)限,也會去更新相應(yīng)的權(quán)限操作持久化文件/data/system/appops.xml,下次再次申請服務(wù)的時候,服務(wù)會再次鑒定權(quán)限。
舉個栗子-定位服務(wù)LocationManagerService: CM12源碼
App在使用定位服務(wù)的時候,一般是通過LocationManager的requestLocationUpdates獲取定位,其實是通過Binder請求LocationManagerService去定位。
/android/location/LocationManager.java
- private void requestLocationUpdates(LocationRequest request, LocationListener listener,
- Looper looper, PendingIntent intent) {
- ...
- try {
- mService.requestLocationUpdates(request, transport, intent, packageName);
- ...
/com/android/server/LocationManagerService.java
- @Override
- public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
- PendingIntent intent, String packageName) {
- if (request == null) request = DEFAULT_LOCATION_REQUEST;
- checkPackageName(packageName);
- int allowedResolutionLevel = getCallerAllowedResolutionLevel();
- checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel,
- request.getProvider());
- 。。。
- final int pid = Binder.getCallingPid();
- final int uid = Binder.getCallingUid();
- // providers may use public location API's, need to clear identity
- long identity = Binder.clearCallingIdentity();
- try {
- checkLocationAccess(uid, packageName, allowedResolutionLevel);
- synchronized (mLock) {
- Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid,
- packageName, workSource, hideFromAppOps);
- if (receiver != null) {
- requestLocationUpdatesLocked(sanitizedRequest, receiver, pid,
- uid, packageName);
- }
- }
- } finally {
- Binder.restoreCallingIdentity(identity);
- }
- }
getCallerAllowedResolutionLevel主要通過調(diào)用getAllowedResolutionLevel查詢APP是否在Manifest中進(jìn)行了聲明
- private int getCallerAllowedResolutionLevel() {
- return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
- }
- private int getAllowedResolutionLevel(int pid, int uid) {
- if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
- pid, uid) == PackageManager.PERMISSION_GRANTED) {
- return RESOLUTION_LEVEL_FINE;
- } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
- pid, uid) == PackageManager.PERMISSION_GRANTED) {
- return RESOLUTION_LEVEL_COARSE;
- } else {
- return RESOLUTION_LEVEL_NONE;
- }
- }
checkLocationAccess這里才是動態(tài)鑒權(quán)的入口,在checkLocationAccess函數(shù)中,會調(diào)用mAppOps.checkOp去鑒權(quán),mAppOps就是AppOpsManager實例,
- boolean checkLocationAccess(int uid, String packageName, int allowedResolutionLevel) {
- int op = resolutionLevelToOp(allowedResolutionLevel);
- if (op >= 0) {
- int mode = mAppOps.checkOp(op, uid, packageName);
- if (mode != AppOpsManager.MODE_ALLOWED && mode != AppOpsManager.MODE_ASK ) {
- return false;
- }
- }
- return true;
- }
進(jìn)而通過Binder向AppOpsService服務(wù)發(fā)送鑒權(quán)請求
- public int noteOp(int op, int uid, String packageName) {
- try {
- int mode = mService.noteOperation(op, uid, packageName);
- if (mode == MODE_ERRORED) {
- throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
- }
- return mode;
- } catch (RemoteException e) {
- }
- return MODE_IGNORED;
- }
AppOpsService負(fù)責(zé)動態(tài)權(quán)限的鑒定跟更新,接著看noteOperation代碼
- @Override
- public int noteOperation(int code, int uid, String packageName) {
- final Result userDialogResult;
- verifyIncomingUid(uid);
- verifyIncomingOp(code);
- synchronized (this) {
- Ops ops = getOpsLocked(uid, packageName, true);
- ...
- if (switchOp.mode == AppOpsManager.MODE_IGNORED ||
- switchOp.mode == AppOpsManager.MODE_ERRORED) {
- op.rejectTime = System.currentTimeMillis();
- op.ignoredCount++;
- return switchOp.mode;
- } else if(switchOp.mode == AppOpsManager.MODE_ALLOWED) {
- op.time = System.currentTimeMillis();
- op.rejectTime = 0;
- op.allowedCount++;
- return AppOpsManager.MODE_ALLOWED;
- } else {
- op.noteOpCount++;
- userDialogResult = askOperationLocked(code, uid, packageName,
- switchOp);
- }
- }
- return userDialogResult.get();
- }
在上面的代碼里面,1、2是對已經(jīng)處理過的場景直接返回已授權(quán),或者已經(jīng)拒絕,而3就是我們常見授權(quán)入口對話框,這里是統(tǒng)一在AppOpsServie中進(jìn)行授權(quán)處理的。askOperationLocked會顯示一個系統(tǒng)對話框,用戶選擇授權(quán)或者拒絕后,AppOpsServie會將選擇記錄在案,并通知申請服務(wù)提供或者拒絕服務(wù)。askOperationLocked通過mHandler發(fā)送鑒權(quán)Message,看一下實現(xiàn)其實就是新建了一個PermissionDialog授權(quán)對話框,并且將AppOpsService的引用傳了進(jìn)去,授權(quán)后會通過mService.notifyOperation通知授權(quán)結(jié)果。
- mHandler = new Handler() {
- public void handleMessage(Message msg) {
- switch (msg.what) {
- case SHOW_PERMISSION_DIALOG: {
- HashMap
data = - (HashMap
) msg.obj; - synchronized (this) {
- Op op = (Op) data.get("op");
- Result res = (Result) data.get("result");
- op.dialogResult.register(res);
- if(op.dialogResult.mDialog == null) {
- Integer code = (Integer) data.get("code");
- Integer uid = (Integer) data.get("uid");
- String packageName =
- (String) data.get("packageName");
- Dialog d = new PermissionDialog(mContext,
- AppOpsService.this, code, uid,
- packageName);
- op.dialogResult.mDialog = (PermissionDialog)d;
- d.show();
- }
- }
- }break;
- }
- }
- };
Android發(fā)行版源碼對于動態(tài)權(quán)限管理的支持(幾乎為零)
在Android4.3到5.1之間,雖然App可以獲得AppOpsManager的實例,但是真正動態(tài)操作權(quán)限的接口setMode卻被隱藏,如下
- /** @hide */
- public void setMode(int code, int uid, String packageName, int mode) {
- try {
- mService.setMode(code, uid, packageName, mode);
- } catch (RemoteException e) {
- }
- }
遍歷源碼也只有NotificationManagerService這個系統(tǒng)應(yīng)用使用了setMode,也就是說發(fā)行版,只有通知是通過系統(tǒng)的通知管理進(jìn)行動態(tài)管理的。
- public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
- checkCallerIsSystem();
- Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
- mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
- enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
- // Now, cancel any outstanding notifications that are part of a just-disabled app
- if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
- cancelAllNotificationsInt(pkg, 0, 0, true, UserHandle.getUserId(uid));
- }
- }
Android 6.0權(quán)限管理原理
Android6.0的runtime-permission機(jī)制讓用戶在任何時候都可以取消授權(quán),因此,每次在申請系統(tǒng)服務(wù)的時候,都要動態(tài)查詢是否獲取了相應(yīng)的權(quán)限,如果沒有獲取,就需要動態(tài)去申請,首先先看一下權(quán)限的查詢:
Android6.0權(quán)限查詢
support-v4兼容包里面提供了一個工具類PermissionChecker,可以用來檢查權(quán)限獲取情況。
PermissionChecker
- public static int checkPermission(@NonNull Context context, @NonNull String permission,
- int pid, int uid, String packageName) {
- if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
- return PERMISSION_DENIED;
- }
- String op = AppOpsManagerCompat.permissionToOp(permission);
- if (op == null) {
- return PERMISSION_GRANTED;
- }
- if (packageName == null) {
- String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
- if (packageNames == null || packageNames.length <= 0) {
- return PERMISSION_DENIED;
- }
- packageName = packageNames[0];
- }
- if (AppOpsManagerCompat.noteProxyOp(context, op, packageName)
- != AppOpsManagerCompat.MODE_ALLOWED) {
- return PERMISSION_DENIED_APP_OP;
- }
- return PERMISSION_GRANTED;
- }
這里我們只關(guān)心context.checkPermission,從上面對于4.3-5.1的APPOpsManager的分析,我們知道AppOpsManagerCompat本身的一些操作對于權(quán)限管理并沒有實際意義,只是用來做一些標(biāo)記,最多就是對于通知權(quán)限有些用,接下來看checkPermission:
ContextImple.java
- /** @hide */
- @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;
- }
- }
接著往下看
ActivityManagerNative.java
- 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;
- }
ActivityManagerService
- 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);
- }
進(jìn)而調(diào)用ActivityManager.checkComponentPermission,調(diào)用AppGlobals.getPackageManager().checkUidPermission(permission, uid);
ActivityManager.java
- /** @hide */
- 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;
- }
- 。。。
- 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;
- }
最終調(diào)用PackageManagerService.java去查看是否有權(quán)限,到這里,我們只需要知道權(quán)限的查詢其實是通過PKMS來進(jìn)行的。心里先有個底,權(quán)限的更新,持久化,恢復(fù)都是通過PKMS來進(jìn)行的。
PKMS不同版本的權(quán)限查詢
Android5.0的checkUidPermission
- public int checkUidPermission(String permName, int uid) {
- final boolean enforcedDefault = isPermissionEnforcedDefault(permName);
- synchronized (mPackages) {
- Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
- if (obj != null) {
- GrantedPermissions gp = (GrantedPermissions)obj;
- if (gp.grantedPermissions.contains(permName)) {
- return PackageManager.PERMISSION_GRANTED;
- }
- } else {


咨詢
建站咨詢
