一、權限的分類
系統權限分爲兩類:正常權限
和 危險權限
。
-
正常權限
不會直接給用戶隱私權帶來風險。如果您的應用在其清單中列出了正常權限,系統將自動授予該權限。 -
危險權限
會授予應用訪問用戶機密數據的權限。如果您的應用在其清單中列出了正常權限,系統將自動授予該權限。如果您列出了危險權限,則用戶必須明確批准您的應用使用這些權限。
那麼,那些是危險權限呢,爲什麼是危險權限呢?
<!-- 權限組: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);
}
});
}
}
執行Activity
的requestPermissions
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;
}
接下來看PackageManagerService
的grantRuntimePermission
方法,涉及跨進程通信
private final PermissionManagerInternal mPermissionManager;
public void grantRuntimePermission(String packageName, String permName, final int userId) {
mPermissionManager.grantRuntimePermission(permName, packageName, false /*overridePolicy*/,
getCallingUid(), userId, mPermissionCallback);
}
在PermissionManagerService
中實現PermissionManagerInternal
的grantRuntimePermission
方法
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);
}
進入大管家ActivityManager
的checkComponentPermission
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);
}
}
}