伙伴们应该都了解,在Android6.0之后,对于部分权限,例如Camera、读写存储权限等,都需要用户授权才可以使用,除非你的应用为系统应用,否则这些“危险权限”将不会自动授予,那么为什么Google在Android 6.0之后会推出动态权限申请,主要是避免一些“流氓”软件在后台获取用户隐私,从而将责任从技术侧转移到用户侧,既然用户选择了允许这些权限使用,那么责任就由用户承担了。
那么我们在动态申请权限时,系统是如何处理并保存这些状态,接下来我们深入源码一看究竟。
1 权限申请
假如我们的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是一一对应的,我们可以知道,到底哪些权限我们拿到了,哪些没有拿到。
2 requestPermissions源码分析
前面在介绍权限申请时,我们调用的是ActivityCompat的requestPermissions方法,接下来我们跟随源码,看系统是如何完成权限申请的。
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以上的版本。
Api23Impl # requestPermissions
@DoNotInline
static void requestPermissions(Activity activity, String[] permissions, int requestCode) {
activity.requestPermissions(permissions, requestCode);
}
通过源码我们看到是调用了Api23Impl的requestPermissions方法,在这个方法内部,直接调用了Activity的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。
PackageManager # buildRequestPermissionsIntent
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其实也是一个系统应用,看名字应该知道它的作用是用来安装或者卸载应用程序的,除此之外,还可以管理应用程序的权限,在安装应用程序时,它会向用户显示应用程序要求的权限,并允许用户对这些权限进行管理和控制。
所以我们在安装或者卸载应用时,系统出现的弹窗,都是在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中处理逻辑,这里我把系统源码中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();
}
}
}
}
GrantPermissionsActivity # onPermissionGrantResult
当用户点击按钮时,会调用onPermissionGrantResult方法,在这个方法中,会判断用户行为,一般会有以下几种:始终允许、仅允许一次、禁止、禁止而且不需要再提醒,针对每种结果,都会调用onPermissionGrantResultSingleState方法来具体的实施。
在onPermissionGrantResultSingleState中,会判断granted参数,也就是否允许权限,调用AppPermissionGroup的grantRuntimePermissions方法做具体的运行时权限申请。
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;
}
}
PKMS # grantRuntimePermission
@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文件中。
PackageManagerInternal # writeSettings
@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,那么就不会再弹窗了。
网友评论