PKMS(二) 一、權限的分類 二、權限申請代碼 三、權限申請源碼流程 四、校驗權限流程 五、總結

Android知識總結

一、權限的分類

系統權限分爲兩類:正常權限危險權限

  • 正常權限不會直接給用戶隱私權帶來風險。如果您的應用在其清單中列出了正常權限,系統將自動授予該權限。
  • 危險權限會授予應用訪問用戶機密數據的權限。如果您的應用在其清單中列出了正常權限,系統將自動授予該權限。如果您列出了危險權限,則用戶必須明確批准您的應用使用這些權限。

那麼,那些是危險權限呢,爲什麼是危險權限呢?

  <!-- 權限組:CALENDAR == 日曆讀取的權限申請 -->
  <uses-permission android:name="android.permission.READ_CALENDAR" />
  <uses-permission android:name="android.permission.WRITE_CALENDAR" />
  <!-- 權限組:CAMERA == 相機打開的權限申請 -->
  <uses-permission android:name="android.permission.CAMERA" />
  <!-- 權限組:CONTACTS == 聯繫人通訊錄信息獲取/寫入的權限申請 -->
  <uses-permission android:name="android.permission.READ_CONTACTS" />
  <uses-permission android:name="android.permission.WRITE_CONTACTS" />
  <!-- 權限組:LOCATION == 位置相關的權限申請 -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <!-- 權限組:PHONE == 撥號相關的權限申請 -->
  <uses-permission android:name="android.permission.CALL_PHONE" />
  <uses-permission android:name="android.permission.READ_PHONE_STATE" />
  <!-- 權限組:SMS == 短信相關的權限申請 -->
  <uses-permission android:name="android.permission.SEND_SMS" />
  <uses-permission android:name="android.permission.READ_SMS" />
  <!-- 權限組:STORAGE == 讀取存儲相關的權限申請 -->
  <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
  <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

二、權限申請代碼

<!-- 第一步:在AndroidManifest.xml中添加所需權限。 -->
<uses-permission android:name="android.permission.READ_CONTACTS" />
public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();
    private static final int MY_PERMISSIONS_REQUEST_READ_CONTACTS = 999;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // 申請 危險權限
        requestPermission();
    }

    // 第二步:封裝了一個requestPermission方法來動態檢查和申請權限
    private void requestPermission() {

        Log.i(TAG, "requestPermission");

        //先檢查之前有沒有申請過這個READ_CONTACTS權限,如果麼有申請過,才申請
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED) {
            Log.i(TAG, "checkSelfPermission");
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS)) {
                Log.i(TAG, "shouldShowRequestPermissionRationale 原來你個貨,之前拒絕過申請權限");
                // 申請 聯繫人讀取權限
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.READ_CONTACTS},
                        MY_PERMISSIONS_REQUEST_READ_CONTACTS);

            } else {
                Log.i(TAG, "requestPermissions 之前沒有拒絕過,正常的申請權限");
                // No explanation needed, we can request the permission.
                // 申請 聯繫人讀取權限
                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.READ_CONTACTS},
                        MY_PERMISSIONS_REQUEST_READ_CONTACTS);
            }
        }
    }

    // 第三步:重寫onRequestPermissionsResult方法根據用戶的不同選擇做出響應。
    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        switch (requestCode) {
            case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    Log.i(TAG, "onRequestPermissionsResult granted");.
                } else {
                    Log.i(TAG, "onRequestPermissionsResult denied");
                    showWaringDialog();
                }
                return;
            }
        }
    }

    // 如果點擊 拒絕,就會彈出這個 提示框
    private void showWaringDialog() {
        new AlertDialog.Builder(this)
                .setTitle("警告!")
                .setMessage("請前往設置->應用->PermissionDemo->權限中打開相關權限,否則功能無法正常運行!")
                .setPositiveButton("確定", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        // 一般情況下如果用戶不授權的話,功能是無法運行的,做退出處理
                        finish();
                    }
                }).show();
    }
    
}

2.1、requestPermissions源碼整體

總結上面的幾個 核心函數

//檢查應用是否具有某個危險權限。
//如果應用具有此權限,方法將返回 PackageManager.PERMISSION_GRANTED,並且應用可以繼續操作。
//如果應用不具有此權限,方法將返回 PackageManager.PERMISSION_DENIED,且應用必須明確向用戶要求權限
checkSelfPermission(@NonNull String permission)

//應用可以通過這個方法動態申請權限,調用後會彈出一個對話框提示用戶授權所申請的權限。
requestPermissions(@NonNull String[] permissions, int requestCode)

//處理結果回調
onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults)

//是否需要顯示UI界面提示用戶爲什麼需要這個權限
//如果應用之前請求過此權限但用戶拒絕了請求,此方法將返回 true。如果用戶在過去拒絕了權限請求,並在
//權限請求系統對話框中選擇了 Don't ask again 選項,此方法將返回 false。如果設備規範禁止應用具有該權限,此方法也會返回 false
ActivityCompat.shouldShowRequestPermissionRationale(@NonNull String permission)

三、權限申請源碼流程

重申請權限開始

 ActivityCompat.requestPermissions(this,new String[]{Manifest.permission.READ_CONTACTS}, MY_PERMISSIONS_REQUEST_READ_CONTACTS);

調用ActivityCompat中的requestPermissions方法

    public static void requestPermissions(final @NonNull Activity activity,
            final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode) {
        if (sDelegate != null
                && sDelegate.requestPermissions(activity, permissions, requestCode)) {
            // Delegate has handled the permission request.
            return;
        }
        //API 23以上執行
        if (Build.VERSION.SDK_INT >= 23) {
            if (activity instanceof RequestPermissionsRequestCodeValidator) {
                ((RequestPermissionsRequestCodeValidator) activity)
                        .validateRequestPermissionsRequestCode(requestCode);
            }
            //權限申請
            activity.requestPermissions(permissions, requestCode);
        } else if (activity instanceof OnRequestPermissionsResultCallback) {
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    final int[] grantResults = new int[permissions.length];

                    PackageManager packageManager = activity.getPackageManager();
                    String packageName = activity.getPackageName();

                    final int permissionCount = permissions.length;
                    for (int i = 0; i < permissionCount; i++) {
                        grantResults[i] = packageManager.checkPermission(
                                permissions[i], packageName);
                    }

                    ((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult(
                            requestCode, permissions, grantResults);
                }
            });
        }
    }

執行ActivityrequestPermissions

    public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
        if (requestCode < 0) {
            throw new IllegalArgumentException("requestCode should be >= 0");
        }
        if (mHasCurrentPermissionsRequest) {
            Log.w(TAG, "Can request only one set of permissions at a time");
            // 返回結果
            onRequestPermissionsResult(requestCode, new String[0], new int[0]);
            return;
        }
        //從 PackageManager 獲取 Intent
        Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
        startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
        mHasCurrentPermissionsRequest = true;
    }

執行PackageManager獲取Intent

    public static final String ACTION_REQUEST_PERMISSIONS =
            "android.content.pm.action.REQUEST_PERMISSIONS";

    public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
        if (ArrayUtils.isEmpty(permissions)) {
           throw new IllegalArgumentException("permission cannot be null or empty");
        }
        //隱士意圖
        Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
        intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
        intent.setPackage(getPermissionControllerPackageName());
        return intent;
    }

接下來我們可以在AndroidManifest.xml中找的系統的Activity


<activity android:name=".permission.ui.GrantPermissionsActivity"
        android:configChanges="orientation|keyboardHidden|screenSize"
        android:excludeFromRecents="true"
        android:theme="@style/GrantPermissions"
        android:visibleToInstantApps="true">
     
     <!-- 那麼我們就根據”android.content.pm.action.REQUEST_PERMISSIONS“ 
表示動作來找到需要激活的某個Activity不就行了 -->
        <intent-filter android:priority="1">
          <action
android:name="android.content.pm.action.REQUEST_PERMISSIONS" />
          <category android:name="android.intent.category.DEFAULT" />
        </intent-filter>
    </activity>

接下來我們就對GrantPermissionsActivity進行分析。
原來GrantPermissionsActivity也就是我們常見的權限申請界面,用戶可以根據提示選擇是否授權給應用相應的權限。用戶操作後的結果會通過回調GrantPermissionsActivity的onPermissionGrantResult方法返回。在onPermissionGrantResult方法中會根據返回結果去決定是走授予權限還是撤銷權限流程,然後會更新授權結果,最後返回結果並結束自己:

  @Override
  public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) {
    GroupState groupState = mRequestGrantPermissionGroups.get(name);
    if (groupState.mGroup != null) {
      if (granted) {
        // 授予權限
        groupState.mGroup.grantRuntimePermissions(doNotAskAgain,
            groupState.affectedPermissions);
        groupState.mState = GroupState.STATE_ALLOWED;
     } else {
        // 撤銷權限
        groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
            groupState.affectedPermissions);
        groupState.mState = GroupState.STATE_DENIED;
        int numRequestedPermissions = mRequestedPermissions.length;
        for (int i = 0; i < numRequestedPermissions; i++) {
          String permission = mRequestedPermissions[i];
          if (groupState.mGroup.hasPermission(permission)) {
            EventLogger.logPermissionDenied(this, permission,
                mAppPermissions.getPackageInfo().packageName);
         }
       }
     }
      // 更新授權結果
      updateGrantResults(groupState.mGroup);
   }
    if (!showNextPermissionGroupGrantRequest()) {
      // 返回授權結果並結束自己
      setResultAndFinish();
   }
 }

接下來繼續跟蹤AppPermissionGroup.grantRuntimePermissions方法分析授權流程。AppPermissionGroup.grantRuntimePermissions方法中會判斷targetSdkVersion是否大於LOLLIPOP_MR1(22),如果大於則做動態權限申請處理。


    public boolean grantRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
        final int uid = mPackageInfo.applicationInfo.uid;

        for (Permission permission : mPermissions.values()) {
            if (mAppSupportsRuntimePermissions) {
                //  在Android 5.1後,就需要支持動態申請權限啦
                // LOLLIPOP_MR1之後版本,支持動態權限申請
                // Do not touch permissions fixed by the system.
                if (permission.isSystemFixed()) {
                    return false;
                }
                if (permission.hasAppOp() && !permission.isAppOpAllowed()) {
                    permission.setAppOpAllowed(true);
                    mAppOps.setUidMode(permission.getAppOp(), uid,
                            AppOpsManager.MODE_ALLOWED);
                }
                if (!permission.isGranted()) {
                    permission.setGranted(true);
                    // 這裏很關鍵, 通過mPackageManager.grantRuntimePermission 跨進程到 PKMS
                    // 下面我們就分析這個操作了哦,注意哦
                    // 熟悉Android源碼的同學都知道XXXManager只是一個輔助類,其真正提供服
                    //務的都是XXXManagerService,所以 直接跳轉PackageManagerService中的grantRuntimePermission方法。
                    mPackageManager.grantRuntimePermission(mPackageInfo.packageName, permission.getName(), mUserHandle);
                }

                if (!fixedByTheUser) {
                    if (permission.isUserFixed() || permission.isUserSet()) {
                        permission.setUserFixed(false);
                        permission.setUserSet(false);
                        mPackageManager.updatePermissionFlags(permission.getName(),
                                mPackageInfo.packageName,
                                PackageManager.FLAG_PERMISSION_USER_FIXED
                                PackageManager.FLAG_PERMISSION_USER_SET, 0, mUserHandle);
                    }
                }
            } else {
                // LOLLIPOP_MR1之前版本,不支持動態權限申請
                ...
            }
        }
        return true;
    }

接下來看PackageManagerServicegrantRuntimePermission方法,涉及跨進程通信

    private final PermissionManagerInternal mPermissionManager;

    public void grantRuntimePermission(String packageName, String permName, final int userId) {
        mPermissionManager.grantRuntimePermission(permName, packageName, false /*overridePolicy*/,
                getCallingUid(), userId, mPermissionCallback);
    }

PermissionManagerService中實現PermissionManagerInternalgrantRuntimePermission方法

   private class PermissionManagerInternalImpl extends PermissionManagerInternal {
        public void grantRuntimePermission(String permName, String packageName,
                boolean overridePolicy, int callingUid, int userId,
                PermissionCallback callback) {
            PermissionManagerService.this.grantRuntimePermission(
                    permName, packageName, overridePolicy, callingUid, userId, callback);
        }
}
    private void grantRuntimePermission(String permName, String packageName, boolean overridePolicy,
            int callingUid, final int userId, PermissionCallback callback) {
        // 檢查用戶是否存在
        if (!mUserManagerInt.exists(userId)) {
            Log.e(TAG, "No such user:" + userId);
            return;
        }
        //檢查PackageInstaller是否有動態權限授權權限
        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
                "grantRuntimePermission");

        enforceCrossUserPermission(callingUid, userId,
                true,  // requireFullPermission
                true,  // checkShell
                false, // requirePermissionWhenSameUser
                "grantRuntimePermission");

        final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
        if (pkg == null || pkg.mExtras == null) {
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }
        final BasePermission bp;
        synchronized(mLock) {
            bp = mSettings.getPermissionLocked(permName);
        }
        if (bp == null) {
            throw new IllegalArgumentException("Unknown permission: " + permName);
        }
        if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
            throw new IllegalArgumentException("Unknown package: " + packageName);
        }

        bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
        if (mSettings.mPermissionReviewRequired
                && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
                && bp.isRuntime()) {
            return;
        }

        final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);

        final PackageSetting ps = (PackageSetting) pkg.mExtras;
        final PermissionsState permissionsState = ps.getPermissionsState();

        final int flags = permissionsState.getPermissionFlags(permName, userId);
        if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
            throw new SecurityException("Cannot grant system fixed permission "
                    + permName + " for package " + packageName);
        }
        if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
            throw new SecurityException("Cannot grant policy fixed permission "
                    + permName + " for package " + packageName);
        }

        if (bp.isDevelopment()) {
            if (permissionsState.grantInstallPermission(bp) !=
                    PermissionsState.PERMISSION_OPERATION_FAILURE) {
                if (callback != null) {
                    callback.onInstallPermissionGranted();
                }
            }
            return;
        }

        if (ps.getInstantApp(userId) && !bp.isInstant()) {
            throw new SecurityException("Cannot grant non-ephemeral permission"
                    + permName + " for package " + packageName);
        }

        if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
            Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
            return;
        }

        final int result = permissionsState.grantRuntimePermission(bp, userId);
        switch (result) {
            case PermissionsState.PERMISSION_OPERATION_FAILURE: {
                return;
            }

            case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
                if (callback != null) {
                      //權限改變
                    callback.onGidsChanged(UserHandle.getAppId(pkg.applicationInfo.uid), userId);
                }
            }
            break;
        }

        if (bp.isRuntime()) {
            logPermission(MetricsEvent.ACTION_PERMISSION_GRANTED, permName, packageName);
        }

        if (callback != null) {
            //回調PermissionCallback的onPermissionGranted方法通知授予權限
            callback.onPermissionGranted(uid, userId);
        }

        // to make an expensive call to remount processes for the changed permissions.
        if (READ_EXTERNAL_STORAGE.equals(permName)
                || WRITE_EXTERNAL_STORAGE.equals(permName)) {
            final long token = Binder.clearCallingIdentity();
            try {
                if (mUserManagerInt.isUserInitialized(userId)) {
                    StorageManagerInternal storageManagerInternal = LocalServices.getService(
                            StorageManagerInternal.class);
                    storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
    }

然後又回到PackageManagerService

    private PermissionCallback mPermissionCallback = new PermissionCallback() {
        public void onPermissionGranted(int uid, int userId) {
            mOnPermissionChangeListeners.onPermissionsChanged(uid);
            // Not critical; if this is lost, the application has to request again.
            synchronized (mPackages) {
                //下面會分鐘分析這個函數
                mSettings.writeRuntimePermissionsForUserLPr(userId, false);
            }
        }
}

回調的是PackageManagerService中的PermissionCallback,在其實現的onPermissionGranted方法中會去通知觀察者權 限發生變化,並調用PackageManager的Settings記錄動態權限授權狀態。

看在Settings的執行流程

public final class Settings {
    public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) {
        if (sync) {
            mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId);
        } else {
            mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId);
        }
    }

    private final class RuntimePermissionPersistence {
        public void writePermissionsForUserSyncLPr(int userId) {
            mHandler.removeMessages(userId);
            writePermissionsSync(userId);
        }

        // writePermissionsSync方法來完成最後的記錄工作。
        // writePermissionsSync方法的代碼很長,但是邏輯很清晰,就是先查詢與應用相關的所有權限狀態,
        // 然後創建 runtime-permissions.xml 文件把這些信息記錄進去。
        private void writePermissionsSync(int userId) {
            // 動態權限文件(runtime-permissions.xml
            AtomicFile destination = new AtomicFile(getUserRuntimePermissionsFile(userId),
                    "package-perms-" + userId);

            ArrayMap<String, List<PermissionState>> permissionsForPackage = new ArrayMap<>();
            ArrayMap<String, List<PermissionState>> permissionsForSharedUser = new ArrayMap<>();

            synchronized (mPersistenceLock) {
                mWriteScheduled.delete(userId);
                //獲得Package權限狀態
                final int packageCount = mPackages.size();
                for (int i = 0; i < packageCount; i++) {
                    String packageName = mPackages.keyAt(i);
                    PackageSetting packageSetting = mPackages.valueAt(i);
                    if (packageSetting.sharedUser == null) {
                        PermissionsState permissionsState = packageSetting.getPermissionsState();
                        List<PermissionState> permissionsStates = permissionsState
                                .getRuntimePermissionStates(userId);
                        if (!permissionsStates.isEmpty()) {
                            permissionsForPackage.put(packageName, permissionsStates);
                        }
                    }
                }
                //獲得SharedUser權限狀態
                final int sharedUserCount = mSharedUsers.size();
                for (int i = 0; i < sharedUserCount; i++) {
                    String sharedUserName = mSharedUsers.keyAt(i);
                    SharedUserSetting sharedUser = mSharedUsers.valueAt(i);
                    PermissionsState permissionsState = sharedUser.getPermissionsState();
                    List<PermissionState> permissionsStates = permissionsState
                            .getRuntimePermissionStates(userId);
                    if (!permissionsStates.isEmpty()) {
                        permissionsForSharedUser.put(sharedUserName, permissionsStates);
                    }
                }
            }

            FileOutputStream out = null;
            try {
                out = destination.startWrite();
                // 創建xml文件用於記錄權限狀態
                XmlSerializer serializer = Xml.newSerializer();
                serializer.setOutput(out, StandardCharsets.UTF_8.name());
                serializer.setFeature(
                        "http://xmlpull.org/v1/doc/features.html#indent-output", true);
                serializer.startDocument(null, true);

                serializer.startTag(null, TAG_RUNTIME_PERMISSIONS);

                String fingerprint = mFingerprints.get(userId);
                if (fingerprint != null) {
                    serializer.attribute(null, ATTR_FINGERPRINT, fingerprint);
                }
                // 寫入Package權限狀態
                final int packageCount = permissionsForPackage.size();
                for (int i = 0; i < packageCount; i++) {
                    String packageName = permissionsForPackage.keyAt(i);
                    List<PermissionState> permissionStates = permissionsForPackage.valueAt(i);
                    serializer.startTag(null, TAG_PACKAGE);
                    serializer.attribute(null, ATTR_NAME, packageName);
                    writePermissions(serializer, permissionStates);
                    serializer.endTag(null, TAG_PACKAGE);
                }
                // 寫入SharedUser權限狀態
                final int sharedUserCount = permissionsForSharedUser.size();
                for (int i = 0; i < sharedUserCount; i++) {
                    String packageName = permissionsForSharedUser.keyAt(i);
                    List<PermissionState> permissionStates = permissionsForSharedUser.valueAt(i);
                    serializer.startTag(null, TAG_SHARED_USER);
                    serializer.attribute(null, ATTR_NAME, packageName);
                    writePermissions(serializer, permissionStates);
                    serializer.endTag(null, TAG_SHARED_USER);
                }

                serializer.endTag(null, TAG_RUNTIME_PERMISSIONS);

                // Now any restored permission grants that are waiting for the apps
                // in question to be installed.  These are stored as per-package
                // TAG_RESTORED_RUNTIME_PERMISSIONS blocks, each containing some
                // number of individual permission grant entities.
                if (mRestoredUserGrants.get(userId) != null) {
                    ArrayMap<String, ArraySet<RestoredPermissionGrant>> restoredGrants =
                            mRestoredUserGrants.get(userId);
                    if (restoredGrants != null) {
                        final int pkgCount = restoredGrants.size();
                        for (int i = 0; i < pkgCount; i++) {
                            final ArraySet<RestoredPermissionGrant> pkgGrants =
                                    restoredGrants.valueAt(i);
                            if (pkgGrants != null && pkgGrants.size() > 0) {
                                final String pkgName = restoredGrants.keyAt(i);
                                serializer.startTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
                                serializer.attribute(null, ATTR_PACKAGE_NAME, pkgName);

                                final int N = pkgGrants.size();
                                for (int z = 0; z < N; z++) {
                                    RestoredPermissionGrant g = pkgGrants.valueAt(z);
                                    serializer.startTag(null, TAG_PERMISSION_ENTRY);
                                    serializer.attribute(null, ATTR_NAME, g.permissionName);

                                    if (g.granted) {
                                        serializer.attribute(null, ATTR_GRANTED, "true");
                                    }

                                    if ((g.grantBits&FLAG_PERMISSION_USER_SET) != 0) {
                                        serializer.attribute(null, ATTR_USER_SET, "true");
                                    }
                                    if ((g.grantBits&FLAG_PERMISSION_USER_FIXED) != 0) {
                                        serializer.attribute(null, ATTR_USER_FIXED, "true");
                                    }
                                    if ((g.grantBits&FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
                                        serializer.attribute(null, ATTR_REVOKE_ON_UPGRADE, "true");
                                    }
                                    serializer.endTag(null, TAG_PERMISSION_ENTRY);
                                }
                                serializer.endTag(null, TAG_RESTORED_RUNTIME_PERMISSIONS);
                            }
                        }
                    }
                }
                // 寫入結束
                serializer.endDocument();
                destination.finishWrite(out);

                if (Build.FINGERPRINT.equals(fingerprint)) {
                    mDefaultPermissionsGranted.put(userId, true);
                }
                // Any error while writing is fatal.
            } catch (Throwable t) {
                Slog.wtf(PackageManagerService.TAG,
                        "Failed to write settings, restoring backup", t);
                destination.failWrite(out);
            } finally {
                IoUtils.closeQuietly(out);
            }
        }
    }    
}

權限申請源碼流程總結:

  • 第一步:MainActivity 調用 requestPermissions 進行動態權限申請;
  • 第二步:requestPermissions函數通過隱士意圖,激活PackageInstaller的GrantPermissionsActivity界面,讓用戶選擇是否授權;
  • 第三步:經過PKMS把相關信息傳遞給PermissionManagerService處理;
  • 第四步:PermissionManagerService處理結束後回調給---->PKMS中的onPermissionGranted方法把處理結果返回;
  • 第五步:PKMS通知過程中權限變化,並調用writeRuntimePermissionsForUserLPr函數讓PackageManager的settings記錄下相關授 權信息;

四、校驗權限流程

從權限的鑑定開始

     if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS)
            != PackageManager.PERMISSION_GRANTED) {
         
    }

調用ContextCompat.checkSelfPermission方法

    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運用裝飾模式,最終會調用ContextImpl中的checkPermission

    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        final IActivityManager am = ActivityManager.getService();
        if (am == null) {
            // Well this is super awkward; we somehow don't have an active
            // ActivityManager instance. If we're testing a root or system
            // UID, then they totally have whatever permission this is.
            final int appId = UserHandle.getAppId(uid);
            if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
                Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
                return PackageManager.PERMISSION_GRANTED;
            }
            Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " does not hold "
                    + permission);
            return PackageManager.PERMISSION_DENIED;
        }

        try {
            //進入AMS 進行跨進程通信
            return am.checkPermission(permission, pid, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

調用ActivityManagerService中的checkComponentPermission

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

    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);
    }

進入大管家ActivityManagercheckComponentPermission

    public static int checkComponentPermission(String permission, int uid,
            int owningUid, boolean exported) {
        // Root, system server get to do everything.
        final int appId = UserHandle.getAppId(uid);
        if (appId == Process.ROOT_UID || appId == 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 {
            //會進入PKMS中,進行跨進程通信
            return AppGlobals.getPackageManager()
                    .checkUidPermission(permission, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

進入PackageManagerService中的checkUidPermission

    public int checkUidPermission(String permName, int uid) {
        synchronized (mPackages) {
            final String[] packageNames = getPackagesForUid(uid);
            final PackageParser.Package pkg = (packageNames != null && packageNames.length > 0)
                    ? mPackages.get(packageNames[0])
                    : null;
            //進入 PermissionManagerService
            return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid());
        }
    }

進入PermissionManagerService權限管理中

public class PermissionManagerService {
    private int checkUidPermission(String permName, PackageParser.Package pkg, int uid,
            int callingUid) {
        final int callingUserId = UserHandle.getUserId(callingUid);
        final boolean isCallerInstantApp =
                mPackageManagerInt.getInstantAppPackageName(callingUid) != null;
        final boolean isUidInstantApp =
                mPackageManagerInt.getInstantAppPackageName(uid) != null;
        final int userId = UserHandle.getUserId(uid);
        if (!mUserManagerInt.exists(userId)) {
            return PackageManager.PERMISSION_DENIED;
        }

        if (pkg != null) {
            if (pkg.mSharedUserId != null) {
                if (isCallerInstantApp) {
                    return PackageManager.PERMISSION_DENIED;
                }
            } else if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) {
                return PackageManager.PERMISSION_DENIED;
            }
            final PermissionsState permissionsState =
                    ((PackageSetting) pkg.mExtras).getPermissionsState();
            if (permissionsState.hasPermission(permName, userId)) {
                if (isUidInstantApp) {
                    if (mSettings.isPermissionInstant(permName)) {
                        return PackageManager.PERMISSION_GRANTED;
                    }
                } else {
                    return PackageManager.PERMISSION_GRANTED;
                }
            }
            // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
            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;
    }


    private class PermissionManagerInternalImpl extends PermissionManagerInternal {
        @Override
        public int checkUidPermission(String permName, PackageParser.Package pkg, int uid,
                int callingUid) {
            return PermissionManagerService.this.checkUidPermission(permName, pkg, uid, callingUid);
        }
    }
}

五、總結

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章