日韩无码专区无码一级三级片|91人人爱网站中日韩无码电影|厨房大战丰满熟妇|AV高清无码在线免费观看|另类AV日韩少妇熟女|中文日本大黄一级黄色片|色情在线视频免费|亚洲成人特黄a片|黄片wwwav色图欧美|欧亚乱色一区二区三区

RELATEED CONSULTING
相關(guān)咨詢
選擇下列產(chǎn)品馬上在線溝通
服務(wù)時間:8:30-17:00
你可能遇到了下面的問題
關(guān)閉右側(cè)工具欄

新聞中心

這里有您想知道的互聯(lián)網(wǎng)營銷解決方案
Android權(quán)限管理原理(含6.0)

前言

在德惠等地區(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

 
 
 
 
  1. private void requestLocationUpdates(LocationRequest request, LocationListener listener,
  2.         Looper looper, PendingIntent intent) {
  3.      ...
  4.     try {
  5.         mService.requestLocationUpdates(request, transport, intent, packageName);
  6.      ...   

/com/android/server/LocationManagerService.java

 
 
 
 
  1. @Override 
  2. public void requestLocationUpdates(LocationRequest request, ILocationListener listener, 
  3.         PendingIntent intent, String packageName) { 
  4.     if (request == null) request = DEFAULT_LOCATION_REQUEST; 
  5.     checkPackageName(packageName); 
  6.      
  7.     int allowedResolutionLevel = getCallerAllowedResolutionLevel(); 
  8.     checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, 
  9.             request.getProvider()); 
  10.     。。。 
  11.      
  12.     final int pid = Binder.getCallingPid(); 
  13.     final int uid = Binder.getCallingUid(); 
  14.     // providers may use public location API's, need to clear identity 
  15.     long identity = Binder.clearCallingIdentity(); 
  16.     try { 
  17.      
  18.         checkLocationAccess(uid, packageName, allowedResolutionLevel); 
  19.  
  20.         synchronized (mLock) { 
  21.             Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid, 
  22.                     packageName, workSource, hideFromAppOps); 
  23.             if (receiver != null) { 
  24.                     requestLocationUpdatesLocked(sanitizedRequest, receiver, pid, 
  25.                                                  uid, packageName); 
  26.             } 
  27.         } 
  28.     } finally { 
  29.         Binder.restoreCallingIdentity(identity); 
  30.     } 

getCallerAllowedResolutionLevel主要通過調(diào)用getAllowedResolutionLevel查詢APP是否在Manifest中進(jìn)行了聲明

 
 
 
 
  1. private int getCallerAllowedResolutionLevel() {
  2.     return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid());
  3. }
  4.  private int getAllowedResolutionLevel(int pid, int uid) {
  5.      if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION,
  6.              pid, uid) == PackageManager.PERMISSION_GRANTED) {
  7.          return RESOLUTION_LEVEL_FINE;
  8.      } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION,
  9.              pid, uid) == PackageManager.PERMISSION_GRANTED) {
  10.          return RESOLUTION_LEVEL_COARSE;
  11.      } else {
  12.          return RESOLUTION_LEVEL_NONE;
  13.      }
  14.  } 

checkLocationAccess這里才是動態(tài)鑒權(quán)的入口,在checkLocationAccess函數(shù)中,會調(diào)用mAppOps.checkOp去鑒權(quán),mAppOps就是AppOpsManager實例,

 
 
 
 
  1. boolean checkLocationAccess(int uid, String packageName, int allowedResolutionLevel) {
  2.     int op = resolutionLevelToOp(allowedResolutionLevel);
  3.     if (op >= 0) {
  4.         int mode = mAppOps.checkOp(op, uid, packageName);
  5.         if (mode != AppOpsManager.MODE_ALLOWED && mode != AppOpsManager.MODE_ASK ) {
  6.             return false;
  7.         }
  8.     }
  9.     return true;

進(jìn)而通過Binder向AppOpsService服務(wù)發(fā)送鑒權(quán)請求

 
 
 
 
  1.  public int noteOp(int op, int uid, String packageName) {
  2.     try {
  3.         int mode = mService.noteOperation(op, uid, packageName);
  4.         if (mode == MODE_ERRORED) {
  5.             throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName));
  6.         }
  7.         return mode;
  8.     } catch (RemoteException e) {
  9.     }
  10.     return MODE_IGNORED;

AppOpsService負(fù)責(zé)動態(tài)權(quán)限的鑒定跟更新,接著看noteOperation代碼

 
 
 
 
  1. @Override
  2. public int noteOperation(int code, int uid, String packageName) {
  3.     final Result userDialogResult;
  4.     verifyIncomingUid(uid);
  5.     verifyIncomingOp(code);
  6.     synchronized (this) {
  7.         Ops ops = getOpsLocked(uid, packageName, true);
  8.           ...
  9.           
  10.         if (switchOp.mode == AppOpsManager.MODE_IGNORED ||
  11.             switchOp.mode == AppOpsManager.MODE_ERRORED) {
  12.             op.rejectTime = System.currentTimeMillis();
  13.             op.ignoredCount++;
  14.             return switchOp.mode;
  15.            
  16.         } else if(switchOp.mode == AppOpsManager.MODE_ALLOWED) {
  17.             op.time = System.currentTimeMillis();
  18.             op.rejectTime = 0;
  19.             op.allowedCount++;
  20.             return AppOpsManager.MODE_ALLOWED;
  21.         } else {
  22.             op.noteOpCount++;
  23.             
  24.             userDialogResult = askOperationLocked(code, uid, packageName,
  25.                 switchOp);
  26.         }
  27.     }
  28.     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é)果。

 
 
 
 
  1. mHandler = new Handler() {
  2.             public void handleMessage(Message msg) {
  3.                 switch (msg.what) {
  4.                 case SHOW_PERMISSION_DIALOG: {
  5.                     HashMap data =
  6.                         (HashMap) msg.obj;
  7.                     synchronized (this) {
  8.                         Op op = (Op) data.get("op");
  9.                         Result res = (Result) data.get("result");
  10.                         op.dialogResult.register(res);
  11.                         if(op.dialogResult.mDialog == null) {
  12.                             Integer code = (Integer) data.get("code");
  13.                             Integer uid  = (Integer) data.get("uid");
  14.                             String packageName =
  15.                                 (String) data.get("packageName");
  16.                             Dialog d = new PermissionDialog(mContext,
  17.                                 AppOpsService.this, code, uid,
  18.                                 packageName);
  19.                             op.dialogResult.mDialog = (PermissionDialog)d;
  20.                             d.show();
  21.                         }
  22.                     }
  23.                 }break;
  24.                 }
  25.             }
  26.         }; 

Android發(fā)行版源碼對于動態(tài)權(quán)限管理的支持(幾乎為零)

在Android4.3到5.1之間,雖然App可以獲得AppOpsManager的實例,但是真正動態(tài)操作權(quán)限的接口setMode卻被隱藏,如下

 
 
 
 
  1. /** @hide */
  2. public void setMode(int code, int uid, String packageName, int mode) {
  3.     try {
  4.         mService.setMode(code, uid, packageName, mode);
  5.     } catch (RemoteException e) {
  6.     }

遍歷源碼也只有NotificationManagerService這個系統(tǒng)應(yīng)用使用了setMode,也就是說發(fā)行版,只有通知是通過系統(tǒng)的通知管理進(jìn)行動態(tài)管理的。

 
 
 
 
  1. public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) {
  2.     checkCallerIsSystem();
  3.     Slog.v(TAG, (enabled?"en":"dis") + "abling notifications for " + pkg);
  4.     mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg,
  5.             enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED);
  6.     // Now, cancel any outstanding notifications that are part of a just-disabled app
  7.     if (ENABLE_BLOCKED_NOTIFICATIONS && !enabled) {
  8.         cancelAllNotificationsInt(pkg, 0, 0, true, UserHandle.getUserId(uid));
  9.     }

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

 
 
 
 
  1. public static int checkPermission(@NonNull Context context, @NonNull String permission,
  2.         int pid, int uid, String packageName) {
  3.     if (context.checkPermission(permission, pid, uid) == PackageManager.PERMISSION_DENIED) {
  4.         return PERMISSION_DENIED;
  5.     }
  6.     String op = AppOpsManagerCompat.permissionToOp(permission);
  7.     if (op == null) {
  8.         return PERMISSION_GRANTED;
  9.     }
  10.     if (packageName == null) {
  11.         String[] packageNames = context.getPackageManager().getPackagesForUid(uid);
  12.         if (packageNames == null || packageNames.length <= 0) {
  13.             return PERMISSION_DENIED;
  14.         }
  15.         packageName = packageNames[0];
  16.     }
  17.     if (AppOpsManagerCompat.noteProxyOp(context, op, packageName)
  18.             != AppOpsManagerCompat.MODE_ALLOWED) {
  19.         return PERMISSION_DENIED_APP_OP;
  20.     }
  21.     return PERMISSION_GRANTED;

這里我們只關(guān)心context.checkPermission,從上面對于4.3-5.1的APPOpsManager的分析,我們知道AppOpsManagerCompat本身的一些操作對于權(quán)限管理并沒有實際意義,只是用來做一些標(biāo)記,最多就是對于通知權(quán)限有些用,接下來看checkPermission:

ContextImple.java

 
 
 
 
  1. /** @hide */
  2. @Override
  3. public int checkPermission(String permission, int pid, int uid, IBinder callerToken) {
  4.     if (permission == null) {
  5.         throw new IllegalArgumentException("permission is null");
  6.     }
  7.     try {
  8.         return ActivityManagerNative.getDefault().checkPermissionWithToken(
  9.                 permission, pid, uid, callerToken);
  10.     } catch (RemoteException e) {
  11.         return PackageManager.PERMISSION_DENIED;
  12.     }

接著往下看

ActivityManagerNative.java

 
 
 
 
  1. public int checkPermission(String permission, int pid, int uid)
  2.         throws RemoteException {
  3.     Parcel data = Parcel.obtain();
  4.     Parcel reply = Parcel.obtain();
  5.     data.writeInterfaceToken(IActivityManager.descriptor);
  6.     data.writeString(permission);
  7.     data.writeInt(pid);
  8.     data.writeInt(uid);
  9.     mRemote.transact(CHECK_PERMISSION_TRANSACTION, data, reply, 0);
  10.     reply.readException();
  11.     int res = reply.readInt();
  12.     data.recycle();
  13.     reply.recycle();
  14.     return res;

ActivityManagerService

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

進(jìn)而調(diào)用ActivityManager.checkComponentPermission,調(diào)用AppGlobals.getPackageManager().checkUidPermission(permission, uid);

ActivityManager.java

 
 
 
 
  1. /** @hide */
  2. public static int checkComponentPermission(String permission, int uid,
  3.         int owningUid, boolean exported) {
  4.     // Root, system server get to do everything.
  5.     
  6.     
  7.     if (uid == 0 || uid == Process.SYSTEM_UID) {
  8.         return PackageManager.PERMISSION_GRANTED;
  9.     }
  10.         。。。
  11.     
  12.     try {
  13.         return AppGlobals.getPackageManager()
  14.                 .checkUidPermission(permission, uid);
  15.     } catch (RemoteException e) {
  16.         // Should never happen, but if it does... deny!
  17.         Slog.e(TAG, "PackageManager is dead?!?", e);
  18.     }
  19.     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

 
 
 
 
  1. public int checkUidPermission(String permName, int uid) {
  2.         final boolean enforcedDefault = isPermissionEnforcedDefault(permName);
  3.         synchronized (mPackages) {
  4.         
  5.             Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
  6.             if (obj != null) {
  7.                 GrantedPermissions gp = (GrantedPermissions)obj;
  8.                 if (gp.grantedPermissions.contains(permName)) {
  9.                     return PackageManager.PERMISSION_GRANTED;
  10.                 }
  11.             } else {
  12.             
  13. <sup id="cins3"><form id="cins3"><dd id="cins3"></dd></form></sup>
    <span id="cins3"><i id="cins3"></i></span>
      <span id="cins3"><code id="cins3"></code></span>