赞
踩
伙伴们应该都了解,在Android6.0之后,对于部分权限,例如Camera、读写存储权限等,都需要用户授权才可以使用,除非你的应用为系统应用,否则这些“危险权限”将不会自动授予,那么为什么Google在Android 6.0之后会推出动态权限申请,主要是避免一些“流氓”软件在后台获取用户隐私,从而将责任从技术侧转移到用户侧,既然用户选择了允许这些权限使用,那么责任就由用户承担了。
那么我们在动态申请权限时,系统是如何处理并保存这些状态,接下来我们深入源码一看究竟。
假如我们的app需要申请读写权限,那么就可以在清单文件中进行权限的声明
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
当然我们仅仅声明并不起作用,我们需要在页面启动时进行权限的动态声明,我们常用的做法就是:
class MainActivity : AppCompatActivity() { private val REQUEST_WRITE_STORAGE_PERMISSION = 0 override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) //首先判断权限是否申请过,或者说是否已经有这个权限了 if (ContextCompat.checkSelfPermission( this, Manifest.permission.WRITE_EXTERNAL_STORAGE ) != PackageManager.PERMISSION_GRANTED ) { //如果已经拿到过了,就不需要申请了 //没拿到过,需要动态申请 if (ActivityCompat.shouldShowRequestPermissionRationale( this, Manifest.permission.WRITE_EXTERNAL_STORAGE ) ) { Log.e("TAG", "之前拒绝过权限,现在重新再次申请") ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_WRITE_STORAGE_PERMISSION ) return } Log.e("TAG", "之前没有拒绝过权限,现在第一次申请") ActivityCompat.requestPermissions( this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), REQUEST_WRITE_STORAGE_PERMISSION ) } } override fun onRequestPermissionsResult( requestCode: Int, permissions: Array<out String>, grantResults: IntArray ) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) when (requestCode) { REQUEST_WRITE_STORAGE_PERMISSION -> { Log.e("TAG", "onRequestPermissionsResult 获取到了权限") permissions.forEach { Log.e("TAG", "permissions $it") } grantResults.forEach { Log.e("TAG", "grantResults $it") } } } } }
这是官方的写法,在申请权限之前,首先会判断 是否获取过这个权限,如果没有获取过,那么就会进行动态权限的申请。当然用户可能之前拒绝过权限,所以这里也会进行一次用户是否拒绝过申请权限的操作,可以做对应的一些交互上的处理,以及权限申请的目的让用户打消顾虑。下面就是用户可能存在的居中操作,做了对应日志的打印:
2023-06-17 13:43:22.111 30040-30040/com.lay.nowinandroid E/TAG: 之前没有拒绝过权限,现在第一次申请
2023-06-17 13:43:31.824 30040-30040/com.lay.nowinandroid E/TAG: onRequestPermissionsResult 获取到了权限
2023-06-17 13:43:31.824 30040-30040/com.lay.nowinandroid E/TAG: permissions android.permission.WRITE_EXTERNAL_STORAGE
2023-06-17 13:43:31.824 30040-30040/com.lay.nowinandroid E/TAG: grantResults -1
通过日志发现,这时已经走到了shouldShowRequestPermissionRationale代码块中,系统是知道用户之前拒绝过了权限。
2023-06-17 13:44:32.813 30868-30868/com.lay.nowinandroid E/TAG: 之前拒绝过权限,现在重新再次申请
2023-06-17 13:44:49.968 30868-30868/com.lay.nowinandroid E/TAG: onRequestPermissionsResult 获取到了权限
2023-06-17 13:44:49.969 30868-30868/com.lay.nowinandroid E/TAG: permissions android.permission.WRITE_EXTERNAL_STORAGE
2023-06-17 13:44:49.969 30868-30868/com.lay.nowinandroid E/TAG: grantResults -1
2023-06-17 13:45:14.135 30868-30868/com.lay.nowinandroid E/TAG: onRequestPermissionsResult 获取到了权限
2023-06-17 13:45:14.135 30868-30868/com.lay.nowinandroid E/TAG: permissions android.permission.WRITE_EXTERNAL_STORAGE
2023-06-17 13:45:14.135 30868-30868/com.lay.nowinandroid E/TAG: grantResults 0
我们看到,当我们申请权限时,其实在onRequestPermissionsResult回调中,无论是同意还是拒绝都会有对应的输出,如果没有同意权限,那么此时grantResults的值就为-1,如果接受了权限,那么grantResults的值就为0,可以看下面对应的介绍。
/**
* Permission check result: this is returned by {@link #checkPermission}
* if the permission has been granted to the given package.
*/
public static final int PERMISSION_GRANTED = 0;
/**
* Permission check result: this is returned by {@link #checkPermission}
* if the permission has not been granted to the given package.
*/
public static final int PERMISSION_DENIED = -1;
我们看到这里其实结果为一个数组,因为我们在申请权限时,可以以组为单位,与permissions是一一对应的,我们可以知道,到底哪些权限我们拿到了,哪些没有拿到。
前面在介绍权限申请时,我们调用的是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; } for (String permission : permissions) { if (TextUtils.isEmpty(permission)) { throw new IllegalArgumentException("Permission request for permissions " + Arrays.toString(permissions) + " must not contain null or empty values"); } } if (Build.VERSION.SDK_INT >= 23) { if (activity instanceof RequestPermissionsRequestCodeValidator) { ((RequestPermissionsRequestCodeValidator) activity) .validateRequestPermissionsRequestCode(requestCode); } Api23Impl.requestPermissions(activity, 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); } }); } }
首先在requestPermissions方法中,判断了SDK的版本,如果大于等于23,也就是包括Android 6.0以上的版本,会执行对应的逻辑,在Android 6.0版本以下,则是直接通过PKMS来检查权限,并做onRequestPermissionsResult的回调,我们关注的重点不在这里,我们重点看Android 6.0以上的版本。
@DoNotInline
static void requestPermissions(Activity activity, String[] permissions, int requestCode) {
activity.requestPermissions(permissions, requestCode);
}
通过源码我们看到是调用了Api23Impl的requestPermissions方法,在这个方法内部,直接调用了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"); // Dispatch the callback with empty arrays which means a cancellation. onRequestPermissionsResult(requestCode, new String[0], new int[0]); return; } if (!getAttributionSource().getRenouncedPermissions().isEmpty()) { final int permissionCount = permissions.length; for (int i = 0; i < permissionCount; i++) { if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) { throw new IllegalArgumentException("Cannot request renounced permission: " + permissions[i]); } } } final Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions); startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null); mHasCurrentPermissionsRequest = true; }
在这个方法中,前面主要做了一些判断,包括requestCode的校验、并发的处理(同一时间只能进行一次权限申请)等,最后通过PackManager创建一个Intent对象,因为需要回调状态到onRequestPermissionsResult,所以通过startActivityForResult方式启动了一个Activity。
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;
}
这个方法非常简单,就是创建了一个Intent对象,但是这个Intent对象的具体配置我们需要看一下。
public static final String ACTION_REQUEST_PERMISSIONS =
"android.content.pm.action.REQUEST_PERMISSIONS";
public static final String EXTRA_REQUEST_PERMISSIONS_NAMES =
"android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";
在创建一个Intent对象之后,将ACTION_REQUEST_PERMISSIONS作为参数传递到Intent构造函数中,意味着这里是创建了一个隐式意图,这里会启动一个系统的页面。
因为通常我们在启动一个Activity的时候,通常是一个显示意图,通过setClass将目的地声明在此,而buildRequestPermissionsIntent中则是创建了一个隐式意图,这里的Activity就是我们看到的那个弹窗页面。那么这些Activity是存在哪里呢,其实就是隐式安装器PackageInstaller提供的。
这里我简单提一下,PackageInstaller其实也是一个系统应用,看名字应该知道它的作用是用来安装或者卸载应用程序的,除此之外,还可以管理应用程序的权限,在安装应用程序时,它会向用户显示应用程序要求的权限,并允许用户对这些权限进行管理和控制。
所以我们在安装或者卸载应用时,系统出现的弹窗,都是在PackageInstaller app中提供的,前面我们提到的android.content.pm.action.REQUEST_PERMISSIONS这个action,在PackageInstaller中就对应一个页面,我们可以去看下PackageInstaller的清单文件。
<activity android:name="com.android.packageinstaller.permission.ui.GrantPermissionsActivity"
android:configChanges="keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:theme="@style/GrantPermissions"
android:visibleToInstantApps="true"
android:inheritShowWhenLocked="true">
<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中处理逻辑,这里我把系统源码中GrantPermissionsActivity拷贝了一份,方便分析权限申请的流程。
public class GrantPermissionsActivity extends Activity implements GrantPermissionsViewHandler.ResultListener { private static final String LOG_TAG = "GrantPermissionsActivity"; private static final String KEY_REQUEST_ID = GrantPermissionsActivity.class.getName() + "_REQUEST_ID"; public static int NUM_BUTTONS = 5; public static int LABEL_ALLOW_BUTTON = 0; public static int LABEL_ALLOW_ALWAYS_BUTTON = 1; public static int LABEL_ALLOW_FOREGROUND_BUTTON = 2; public static int LABEL_DENY_BUTTON = 3; public static int LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON = 4; /** Unique Id of a request */ private long mRequestId; private String[] mRequestedPermissions; private CharSequence[] mButtonLabels; private ArrayMap<Pair<String, Boolean>, GroupState> mRequestGrantPermissionGroups = new ArrayMap<>(); private GrantPermissionsViewHandler mViewHandler; private AppPermissions mAppPermissions; boolean mResultSet; /** * Listens for changes to the permission of the app the permissions are currently getting * granted to. {@code null} when unregistered. */ private @Nullable PackageManager.OnPermissionsChangedListener mPermissionChangeListener; /** * Listens for changes to the app the permissions are currently getting granted to. {@code null} * when unregistered. */ private @Nullable PackageRemovalMonitor mPackageRemovalMonitor; // ..... /** * Report the result of a grant of a permission. * * @param permission The permission that was granted or denied * @param result The permission grant result */ private void reportRequestResult(@NonNull String permission, int result) { boolean isImplicit = !ArrayUtils.contains(mRequestedPermissions, permission); Log.v(LOG_TAG, "Permission grant result requestId=" + mRequestId + " callingUid=" + mCallingUid + " callingPackage=" + mCallingPackage + " permission=" + permission + " isImplicit=" + isImplicit + " result=" + result); PermissionControllerStatsLog.write( PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED, mRequestId, mCallingUid, mCallingPackage, permission, isImplicit, result); } /** * Report the result of a grant of a permission. * * @param permissions The permissions that were granted or denied * @param result The permission grant result */ private void reportRequestResult(@NonNull String[] permissions, int result) { for (String permission : permissions) { reportRequestResult(permission, result); } } @Override public void onCreate(Bundle icicle) { super.onCreate(icicle); // ...... mRequestedPermissions = getIntent().getStringArrayExtra( PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES); if (mRequestedPermissions == null) { mRequestedPermissions = new String[0]; } //...... //这里会通过判断硬件设备类型,展示不同的UI if (DeviceUtils.isTelevision(this)) { mViewHandler = new com.android.packageinstaller.permission.ui.television .GrantPermissionsViewHandlerImpl(this, mCallingPackage).setResultListener(this); } else if (DeviceUtils.isWear(this)) { mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this); } else if (DeviceUtils.isAuto(this)) { mViewHandler = new GrantPermissionsAutoViewHandler(this, mCallingPackage, userHandle) .setResultListener(this); } else { mViewHandler = new com.android.packageinstaller.permission.ui.handheld .GrantPermissionsViewHandlerImpl(this, mCallingPackage, userHandle) .setResultListener(this); } // ...... setContentView(mViewHandler.createView()); // ...... } //...... @Override public void onPermissionGrantResult(String name, @GrantPermissionsViewHandler.Result int result) { logGrantPermissionActivityButtons(name, result); GroupState foregroundGroupState = getForegroundGroupState(name); GroupState backgroundGroupState = getBackgroundGroupState(name); if (result == GRANTED_ALWAYS || result == GRANTED_FOREGROUND_ONLY || result == DENIED_DO_NOT_ASK_AGAIN) { KeyguardManager kgm = getSystemService(KeyguardManager.class); if (kgm.isDeviceLocked()) { kgm.requestDismissKeyguard(this, new KeyguardManager.KeyguardDismissCallback() { @Override public void onDismissError() { Log.e(LOG_TAG, "Cannot dismiss keyguard perm=" + name + " result=" + result); } @Override public void onDismissCancelled() { // do nothing (i.e. stay at the current permission group) } @Override public void onDismissSucceeded() { // Now the keyguard is dismissed, hence the device is not locked // anymore onPermissionGrantResult(name, result); } }); return; } } switch (result) { case GRANTED_ALWAYS : if (foregroundGroupState != null) { onPermissionGrantResultSingleState(foregroundGroupState, true, false); } if (backgroundGroupState != null) { onPermissionGrantResultSingleState(backgroundGroupState, true, false); } break; case GRANTED_FOREGROUND_ONLY : if (foregroundGroupState != null) { onPermissionGrantResultSingleState(foregroundGroupState, true, false); } if (backgroundGroupState != null) { onPermissionGrantResultSingleState(backgroundGroupState, false, false); } break; case DENIED : if (foregroundGroupState != null) { onPermissionGrantResultSingleState(foregroundGroupState, false, false); } if (backgroundGroupState != null) { onPermissionGrantResultSingleState(backgroundGroupState, false, false); } break; case DENIED_DO_NOT_ASK_AGAIN : if (foregroundGroupState != null) { onPermissionGrantResultSingleState(foregroundGroupState, false, true); } if (backgroundGroupState != null) { onPermissionGrantResultSingleState(backgroundGroupState, false, true); } break; } if (!showNextPermissionGroupGrantRequest()) { setResultAndFinish(); } } /** * Grants or revoked the affected permissions for a single {@link groupState}. * * @param groupState The group state with the permissions to grant/revoke * @param granted {@code true} if the permissions should be granted, {@code false} if they * should be revoked * @param doNotAskAgain if the permissions should be revoked should be app be allowed to ask * again for the same permissions? */ private void onPermissionGrantResultSingleState(GroupState groupState, boolean granted, boolean doNotAskAgain) { if (groupState != null && groupState.mGroup != null && groupState.mState == GroupState.STATE_UNKNOWN) { if (granted) { groupState.mGroup.grantRuntimePermissions(doNotAskAgain, groupState.affectedPermissions); groupState.mState = GroupState.STATE_ALLOWED; reportRequestResult(groupState.affectedPermissions, PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED); } else { groupState.mGroup.revokeRuntimePermissions(doNotAskAgain, groupState.affectedPermissions); groupState.mState = GroupState.STATE_DENIED; reportRequestResult(groupState.affectedPermissions, doNotAskAgain ? PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE : PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED); } } } // ...... private void setResultIfNeeded(int resultCode) { if (!mResultSet) { mResultSet = true; logRequestedPermissionGroups(); Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS); result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions); PackageManager pm = getPackageManager(); int numRequestedPermissions = mRequestedPermissions.length; int[] grantResults = new int[numRequestedPermissions]; for (int i = 0; i < numRequestedPermissions; i++) { grantResults[i] = pm.checkPermission(mRequestedPermissions[i], mCallingPackage); } result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, grantResults); setResult(resultCode, result); } } private void setResultAndFinish() { setResultIfNeeded(RESULT_OK); finish(); } // ...... private static final class GroupState { static final int STATE_UNKNOWN = 0; static final int STATE_ALLOWED = 1; static final int STATE_DENIED = 2; static final int STATE_SKIPPED = 3; final AppPermissionGroup mGroup; int mState = STATE_UNKNOWN; /** Permissions of this group that need to be granted, null == no permissions of group */ String[] affectedPermissions; GroupState(AppPermissionGroup group) { mGroup = group; } } private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener { final int mCallingPackageUid; PermissionChangeListener() throws NameNotFoundException { mCallingPackageUid = getPackageManager().getPackageUid(mCallingPackage, 0); } @Override public void onPermissionsChanged(int uid) { if (uid == mCallingPackageUid) { updateIfPermissionsWereGranted(); } } } }
当用户点击按钮时,会调用onPermissionGrantResult方法,在这个方法中,会判断用户行为,一般会有以下几种:始终允许、仅允许一次、禁止、禁止而且不需要再提醒,针对每种结果,都会调用onPermissionGrantResultSingleState方法来具体的实施。
在onPermissionGrantResultSingleState中,会判断granted参数,也就是否允许权限,调用AppPermissionGroup的grantRuntimePermissions方法做具体的运行时权限申请。
注意这个方法中的参数,fixedByTheUser其实对应的就是dontAskAgain,是否需要再次询问,如果用户选择拒绝权限而且不再询问,那么就只能去设置中打开权限。
public boolean grantRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) { boolean killApp = false; boolean wasAllGranted = true; // We toggle permissions only to apps that support runtime // permissions, otherwise we toggle the app op corresponding // to the permission if the permission is granted to the app. for (Permission permission : mPermissions.values()) { if (filterPermissions != null && !ArrayUtils.contains(filterPermissions, permission.getName())) { continue; } if (!permission.isGrantingAllowed(mIsEphemeralApp, mAppSupportsRuntimePermissions)) { // Skip unallowed permissions. continue; } boolean wasGranted = permission.isGrantedIncludingAppOp(); if (mAppSupportsRuntimePermissions) { // Do not touch permissions fixed by the system. if (permission.isSystemFixed()) { wasAllGranted = false; break; } // Ensure the permission app op enabled before the permission grant. if (permission.affectsAppOp() && !permission.isAppOpAllowed()) { permission.setAppOpAllowed(true); } // Grant the permission if needed. if (!permission.isGranted()) { permission.setGranted(true); } // Update the permission flags. if (!fixedByTheUser) { // Now the apps can ask for the permission as the user // no longer has it fixed in a denied state. if (permission.isUserFixed() || permission.isUserSet()) { permission.setUserFixed(false); permission.setUserSet(false); } } } else { // Legacy apps cannot have a not granted permission but just in case. if (!permission.isGranted()) { continue; } // If the permissions has no corresponding app op, then it is a // third-party one and we do not offer toggling of such permissions. if (permission.affectsAppOp()) { if (!permission.isAppOpAllowed()) { permission.setAppOpAllowed(true); // Legacy apps do not know that they have to retry access to a // resource due to changes in runtime permissions (app ops in this // case). Therefore, we restart them on app op change, so they // can pick up the change. killApp = true; } // Mark that the permission should not be be granted on upgrade // when the app begins supporting runtime permissions. if (permission.shouldRevokeOnUpgrade()) { permission.setRevokeOnUpgrade(false); } } // Granting a permission explicitly means the user already // reviewed it so clear the review flag on every grant. if (permission.isReviewRequired()) { permission.unsetReviewRequired(); } } // If we newly grant background access to the fine location, double-guess the user some // time later if this was really the right choice. if (!wasGranted && permission.isGrantedIncludingAppOp()) { if (permission.getName().equals(ACCESS_FINE_LOCATION)) { Permission bgPerm = permission.getBackgroundPermission(); if (bgPerm != null) { if (bgPerm.isGrantedIncludingAppOp()) { mTriggerLocationAccessCheckOnPersist = true; } } } else if (permission.getName().equals(ACCESS_BACKGROUND_LOCATION)) { ArrayList<Permission> fgPerms = permission.getForegroundPermissions(); if (fgPerms != null) { int numFgPerms = fgPerms.size(); for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) { Permission fgPerm = fgPerms.get(fgPermNum); if (fgPerm.getName().equals(ACCESS_FINE_LOCATION)) { if (fgPerm.isGrantedIncludingAppOp()) { mTriggerLocationAccessCheckOnPersist = true; } break; } } } } } } //默认 mDelayChanges = false,这里真正进行权限申请 if (!mDelayChanges) { persistChanges(false); if (killApp) { killApp(KILL_REASON_APP_OP_CHANGE); } } return wasAllGranted; }
前面主要是进行一系列的配置,persistChanges方法中会向PKMS发起进程间通信,调用PKMS的grantRuntimePermission方法。
void persistChanges(boolean mayKillBecauseOfAppOpsChange) { int uid = mPackageInfo.applicationInfo.uid; int numPermissions = mPermissions.size(); boolean shouldKillApp = false; for (int i = 0; i < numPermissions; i++) { Permission permission = mPermissions.valueAt(i); if (!permission.isSystemFixed()) { if (permission.isGranted()) { mPackageManager.grantRuntimePermission(mPackageInfo.packageName, permission.getName(), mUserHandle); } else { boolean isCurrentlyGranted = mContext.checkPermission(permission.getName(), -1, uid) == PERMISSION_GRANTED; if (isCurrentlyGranted) { mPackageManager.revokeRuntimePermission(mPackageInfo.packageName, permission.getName(), mUserHandle); } } } int flags = (permission.isUserSet() ? PackageManager.FLAG_PERMISSION_USER_SET : 0) | (permission.isUserFixed() ? PackageManager.FLAG_PERMISSION_USER_FIXED : 0) | (permission.shouldRevokeOnUpgrade() ? PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE : 0) | (permission.isPolicyFixed() ? PackageManager.FLAG_PERMISSION_POLICY_FIXED : 0) | (permission.isReviewRequired() ? PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED : 0); mPackageManager.updatePermissionFlags(permission.getName(), mPackageInfo.packageName, PackageManager.FLAG_PERMISSION_USER_SET | PackageManager.FLAG_PERMISSION_USER_FIXED | PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE | PackageManager.FLAG_PERMISSION_POLICY_FIXED | PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, flags, mUserHandle); if (permission.affectsAppOp()) { if (!permission.isSystemFixed()) { // Enabling/Disabling an app op may put the app in a situation in which it has // a handle to state it shouldn't have, so we have to kill the app. This matches // the revoke runtime permission behavior. if (permission.isAppOpAllowed()) { shouldKillApp |= allowAppOp(permission, uid); } else { shouldKillApp |= disallowAppOp(permission, uid); } } } } if (mayKillBecauseOfAppOpsChange && shouldKillApp) { killApp(KILL_REASON_APP_OP_CHANGE); } if (mTriggerLocationAccessCheckOnPersist) { new LocationAccessCheck(mContext, null).checkLocationAccessSoon(); mTriggerLocationAccessCheckOnPersist = false; } }
@Override
public void grantRuntimePermission(String packageName, String permName, final int userId) {
// Because this is accessed via the package manager service AIDL,
// go through the permission manager service AIDL
mContext.getSystemService(PermissionManager.class)
.grantRuntimePermission(packageName, permName, UserHandle.of(userId));
}
这里我们看到,是拿到PermissionManager服务对象,调用PMMS的grantRuntimePermission方法,所以我们需要去PermissionManagerService中查找。
@Override public void grantRuntimePermission(String packageName, String permName, final int userId) { final int callingUid = Binder.getCallingUid(); final boolean overridePolicy = checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY) == PackageManager.PERMISSION_GRANTED; grantRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId, mDefaultPermissionCallback); } private void grantRuntimePermissionInternal(String packageName, String permName, boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) { // ..... final int uid = UserHandle.getUid(userId, pkg.getUid()); if (callback != null) { if (isRuntimePermission) { callback.onPermissionGranted(uid, userId); } else { callback.onInstallPermissionGranted(); } if (permissionHasGids) { callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId); } } if (isRuntimePermission) { notifyRuntimePermissionStateChanged(packageName, userId); } }
其实在PermissionManagerService的grantRuntimePermissionInternal前面所有的判断,都是在判断当前这个权限是不是已经获取到了,如果获取到了就退出;如果没有获取到,那么就会通过PermissionCallback回调,并判断是否为运行时权限,如果是运行时权限,会回调onPermissionGranted方法,非运行时权限会回调onInstallPermissionGranted方法。
private final PermissionCallback mDefaultPermissionCallback = new PermissionCallback() { @Override public void onGidsChanged(int appId, int userId) { mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED)); } @Override public void onPermissionGranted(int uid, int userId) { mOnPermissionChangeListeners.onPermissionsChanged(uid); // Not critical; if this is lost, the application has to request again. mPackageManagerInt.writeSettings(true); } @Override public void onInstallPermissionGranted() { mPackageManagerInt.writeSettings(true); } @Override public void onPermissionRevoked(int uid, int userId, String reason) { mOnPermissionChangeListeners.onPermissionsChanged(uid); // Critical; after this call the application should never have the permission mPackageManagerInt.writeSettings(false); final int appId = UserHandle.getAppId(uid); if (reason == null) { mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED)); } else { mHandler.post(() -> killUid(appId, userId, reason)); } } @Override public void onInstallPermissionRevoked() { mPackageManagerInt.writeSettings(true); } @Override public void onPermissionUpdated(int[] userIds, boolean sync) { mPackageManagerInt.writePermissionSettings(userIds, !sync); } @Override public void onInstallPermissionUpdated() { mPackageManagerInt.writeSettings(true); } @Override public void onPermissionRemoved() { mPackageManagerInt.writeSettings(false); } public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync, int uid) { onPermissionUpdated(updatedUserIds, sync); for (int i = 0; i < updatedUserIds.length; i++) { int userUid = UserHandle.getUid(updatedUserIds[i], UserHandle.getAppId(uid)); mOnPermissionChangeListeners.onPermissionsChanged(userUid); } } public void onInstallPermissionUpdatedNotifyListener(int uid) { onInstallPermissionUpdated(); mOnPermissionChangeListeners.onPermissionsChanged(uid); } };
在onPermissionGranted回调方法中,会调用PackageManagerInternal的writeSettings方法,将权限信息写入到xml文件中。
@Override
public void writeSettings(boolean async) {
synchronized (mLock) {
if (async) {
scheduleWriteSettingsLocked();
} else {
writeSettingsLPrTEMP();
}
}
}
这里是可以选择同步或者异步,因为涉及到了IO操作,所以这里传入的是true。
case WRITE_SETTINGS: {
Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
synchronized (mLock) {
removeMessages(WRITE_SETTINGS);
removeMessages(WRITE_PACKAGE_RESTRICTIONS);
writeSettingsLPrTEMP();
mDirtyUsers.clear();
}
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
}
break;
最终是调用writeLPr方法,创建xml文件
void writeLPr() { //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024); final long startTime = SystemClock.uptimeMillis(); // Whenever package manager changes something on the system, it writes out whatever it // changed in the form of a settings object change, and it does so under its internal // lock --- so if we invalidate the package cache here, we end up invalidating at the // right time. invalidatePackageCache(); // Keep the old settings around until we know the new ones have // been successfully written. if (mSettingsFilename.exists()) { // Presence of backup settings file indicates that we failed // to persist settings earlier. So preserve the older // backup for future reference since the current settings // might have been corrupted. if (!mBackupSettingsFilename.exists()) { if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) { Slog.wtf(PackageManagerService.TAG, "Unable to backup package manager settings, " + " current changes will be lost at reboot"); return; } } else { mSettingsFilename.delete(); Slog.w(PackageManagerService.TAG, "Preserving older settings backup"); } } mPastSignatures.clear(); try { final FileOutputStream fstr = new FileOutputStream(mSettingsFilename); final TypedXmlSerializer serializer = Xml.resolveSerializer(fstr); serializer.startDocument(null, true); serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); serializer.startTag(null, "packages"); for (int i = 0; i < mVersion.size(); i++) { final String volumeUuid = mVersion.keyAt(i); final VersionInfo ver = mVersion.valueAt(i); serializer.startTag(null, TAG_VERSION); XmlUtils.writeStringAttribute(serializer, ATTR_VOLUME_UUID, volumeUuid); serializer.attributeInt(null, ATTR_SDK_VERSION, ver.sdkVersion); serializer.attributeInt(null, ATTR_DATABASE_VERSION, ver.databaseVersion); XmlUtils.writeStringAttribute(serializer, ATTR_FINGERPRINT, ver.fingerprint); serializer.endTag(null, TAG_VERSION); } if (mVerifierDeviceIdentity != null) { serializer.startTag(null, "verifier"); serializer.attribute(null, "device", mVerifierDeviceIdentity.toString()); serializer.endTag(null, "verifier"); } serializer.startTag(null, "permission-trees"); mPermissions.writePermissionTrees(serializer); serializer.endTag(null, "permission-trees"); serializer.startTag(null, "permissions"); mPermissions.writePermissions(serializer); serializer.endTag(null, "permissions"); for (final PackageSetting pkg : mPackages.values()) { writePackageLPr(serializer, pkg); } for (final PackageSetting pkg : mDisabledSysPackages.values()) { writeDisabledSysPackageLPr(serializer, pkg); } for (final SharedUserSetting usr : mSharedUsers.values()) { serializer.startTag(null, "shared-user"); serializer.attribute(null, ATTR_NAME, usr.name); serializer.attributeInt(null, "userId", usr.userId); usr.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage()); serializer.endTag(null, "shared-user"); } if (mRenamedPackages.size() > 0) { for (Map.Entry<String, String> e : mRenamedPackages.entrySet()) { serializer.startTag(null, "renamed-package"); serializer.attribute(null, "new", e.getKey()); serializer.attribute(null, "old", e.getValue()); serializer.endTag(null, "renamed-package"); } } mDomainVerificationManager.writeSettings(serializer, false /* includeSignatures */, UserHandle.USER_ALL); mKeySetManagerService.writeKeySetManagerServiceLPr(serializer); serializer.endTag(null, "packages"); serializer.endDocument(); fstr.flush(); FileUtils.sync(fstr); fstr.close(); // New settings successfully written, old ones are no longer // needed. mBackupSettingsFilename.delete(); FileUtils.setPermissions(mSettingsFilename.toString(), FileUtils.S_IRUSR|FileUtils.S_IWUSR |FileUtils.S_IRGRP|FileUtils.S_IWGRP, -1, -1); writeKernelMappingLPr(); writePackageListLPr(); writeAllUsersPackageRestrictionsLPr(); writeAllRuntimePermissionsLPr(); com.android.internal.logging.EventLogTags.writeCommitSysConfigFile( "package", SystemClock.uptimeMillis() - startTime); return; } catch(java.io.IOException e) { Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, " + "current changes will be lost at reboot", e); } // Clean up partially written files if (mSettingsFilename.exists()) { if (!mSettingsFilename.delete()) { Slog.wtf(PackageManagerService.TAG, "Failed to clean up mangled file: " + mSettingsFilename); } } //Debug.stopMethodTracing(); }
具体文件为:
mSettingsFilename = new File(mSystemDir, "packages.xml");
也就是在data/system/packages.xml文件中永久保存,如果应用卸载那么就会清除权限.
<package name="com.lay.nowinandroid" codePath="/data/app/~~Z3KlFzqUZ3vXhgizpl-R4Q==/com.lay.nowinandroid-NcpT91M9gQfcPkeJJhPoxA==" nativeLibraryPath="/data/app/~~Z3KlFzqUZ3vXhgizpl-R4Q==/com.lay.nowinandroid-NcpT91M9gQfcPkeJJhPoxA==/lib" publicFlags="810073926" privateFlags="-1400893440" ft="188c89ab490" it="188c7e11f01" ut="188c89ab614" version="1" userId="10131">
<sigs count="1" schemeVersion="2">
<cert index="9" key="/>
</sigs>
<perms>
<item name="android.permission.INTERNET" granted="true" flags="0" />
</perms>
<proper-signing-keyset identifier="10" />
</package>
我们可以看到,在xml文件中的perms标签下有对应权限的声明,以及granted的参数,也就是说下次再进来之后,会检查这个xml文件中我们要申请的这个权限是不是已经获取到了,如果granted = true || flags = 0,那么就不会再弹窗了。
作者:layz4android
其实要轻松掌握一门语言很简单,要点就两个:
你不需要是天才,也不需要具备强悍的天赋,只要做到这两点,短期内成功的概率是非常高的。
对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。下面资料部分截图是我花费几个月时间整理的,诚意满满:特别适合有3-5年开发经验的Android程序员们学习。
- 自行下载直达领取链接
- 以上进阶BATJ大厂学习资料可以免费分享给大家,需要完整版的朋友,扫描下方二维码。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。