I’ll write the 1 out front
Speaking of which, 6.0 has been around for a long time, and we have a lot of wheels available for permission adaptation. On how to adapt to the 6.0 authority, there are many online information is very perfect, so it will not be discussed.
Without a third-party framework, we would deal with these apis (methods are too long and omit arguments) :
Context.checkSelfPermission()
Activity.requestPermissions()
Activity.onRequestPermissionsResult()
Activity.shouldShowRequestPermissionRationale()
Obviously, the whole analysis is too long, and according to the 80-20 rule, we only analyze the main line and ignore the complex branches.
This article assumes that you have been familiar with 6.0 permissions, trying to share with you from requestPermissions up behind the onRequestPermissionsResult, what has experienced.
2 Scene Tracing
The following is a query for camera permissions in the RxPermissions Demo.
2.1 Evoke the dialog box
We see that requestPermissions triggers a dialog box. In fact, it is a transparent Activity that uses the startActivityForResult logic.
// Activity
public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
.
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
.
}
Copy the code
We from with buildRequestPermissionsIntent, according to the implicit Intent to find the corresponding Activity – GrantPermissionsActivity soon.
// PackageManager
public static final String ACTION_REQUEST_PERMISSIONS =
"android.content.pm.action.REQUEST_PERMISSIONS";
public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
.
Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
intent.setPackage(getPermissionControllerPackageName());
return intent;
}
Copy the code
2.2 Click Events
Find the Activity and go further to the pop-up and click events. First look at the onCreate () :
// GrantPermissionsActivity
private GrantPermissionsViewHandler mViewHandler;
@Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setFinishOnTouchOutside(false);
setTitle(R.string.permission_request_title);
if (DeviceUtils.isTelevision(this)) { // TV
mViewHandler = new com.android.packageinstaller.permission.ui.television
.GrantPermissionsViewHandlerImpl(this)
.setResultListener(this);
} else if (deviceutils.iswear (this)) {//
mViewHandler = new GrantPermissionsWatchViewHandler(this)
.setResultListener(this);
} else {// Handheld device (mobile phone)
mViewHandler = new com.android.packageinstaller.permission.ui.handheld
.GrantPermissionsViewHandlerImpl(this)
.setResultListener(this);
}
.
setContentView(mViewHandler.createView());
}
Copy the code
On a glance, the original permissions also support bracelet and TV, only to see us concerned about handheld.
// handheld.GrantPermissionsViewHandlerImpl
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.permission_allow_button:
if (mResultListener ! = null) {
view.clearAccessibilityFocus();
mResultListener.onPermissionGrantResult(mGroupName, true, false);
}
break;
case R.id.permission_deny_button:
mAllowButton.setEnabled(true);
if (mResultListener ! = null) {
view.clearAccessibilityFocus();
mResultListener.onPermissionGrantResult(mGroupName, false,
mDoNotAskCheckbox.isChecked());
}
break;
case R.id.do_not_ask_checkbox:
mAllowButton.setEnabled(! mDoNotAskCheckbox.isChecked());
break;
}
}
Copy the code
So here, the UI part is already working. There is an option box to stop asking and two buttons to allow and reject. Just the logical implementation of the click is out there.
2.3 the callback onAcrivityResult ()
We’re back to GrantPermissionsActivity, which implements the UI callback and returns the authorization result to our app page via setResult().
// GrantPermissionsActivity implements GrantPermissionsViewHandler.ResultListener
@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.mState = GroupState.STATE_ALLOWED;
} else {
groupState.mGroup.revokeRuntimePermissions(doNotAskAgain);
groupState.mState = GroupState.STATE_DENIED;
}
updateGrantResults(groupState.mGroup);
}
if (! showNextPermissionGroupGrantRequest()) {
setResultAndFinish();
}
}
private void setResultAndFinish() {
setResultIfNeeded(RESULT_OK);
finish();
}
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);
result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, mGrantResults);
setResult(resultCode, result);
}
}
Copy the code
2.4 the callback onRequestPermissionsResult ()
Back to the Activity, we find that dispatchActivityResult is interspersed with the distribution of permissions. Then, naturally, we arrived at our destination.
// Activity
void dispatchActivityResult(String who, int requestCode,
int resultCode, Intent data) {
.
if (who == null) {
onActivityResult(requestCode, resultCode, data);
} else if (who.startsWith(REQUEST_PERMISSIONS_WHO_PREFIX)) {
who = who.substring(REQUEST_PERMISSIONS_WHO_PREFIX.length());
if (TextUtils.isEmpty(who)) {
dispatchRequestPermissionsResult(requestCode, data);
} else ...
} else ...
}
private void dispatchRequestPermissionsResult(int requestCode, Intent data) {
mHasCurrentPermissionsRequest = false;
// If the package installer crashed we may have not data - best effort.
String[] permissions = (data ! = null) ? data.getStringArrayExtra(
PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES) : new String[0];
final int[] grantResults = (data ! = null) ? data.getIntArrayExtra(
PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS) : new int[0];
onRequestPermissionsResult(requestCode, permissions, grantResults); // The destination is here
}
Copy the code
2.5 Permission Permit/Denial
The previous logic is basically UI level, the big problem is to go through. If you are careful, you may notice that permission/denial is missing in 2.3. Binder and AIDL calls are involved.
Using permission permission as an example, walk through the call stack roughly:
- GrantPermissionsActivity.onPermissionGrantResult()
- AppPermissionGroup.grantRuntimePermissions()
- PackageManager.grantRuntimePermission()
- IPackageManager.grantRuntimePermission()
- PackageManagerService.grantRuntimePermission()
The last call is to the system-level PMS, which basically changes the permission list in sb.PermissionsState based on the application package name and permission name, and then asynchronously saves it to a file. PS: where sb is SettingBase, the instance is PackageSetting. As the name implies, it holds the Settings at the package level.
A combination of code and breakpoint flavors is better:
// PackageManagerService
@Override
public void grantRuntimePermission(String packageName, String name, final int userId) {
.
final int uid;
final SettingBase sb;
synchronized (mPackages) {
final PackageParser.Package pkg = mPackages.get(packageName);
final BasePermission bp = mSettings.mPermissions.get(name);
.
uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
sb = (SettingBase) pkg.mExtras;
final PermissionsState permissionsState = sb.getPermissionsState();
.
// Grant permission, that is, change the state of the permission list in sb.PermissionsState
final int result = permissionsState.grantRuntimePermission(bp, userId);
switch (result) {
case PermissionsState.PERMISSION_OPERATION_FAILURE: {
return;
}
case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
.
}
break;
}
/ / provides a callback PackageManager. OnPermissionsChangedListener, however it is hide for application layer
mOnPermissionChangeListeners.onPermissionsChanged(uid);
// Save the permission changes and write them to the file
mSettings.writeRuntimePermissionsForUserLPr(userId, false);
}
if (READ_EXTERNAL_STORAGE.equals(name)
|| WRITE_EXTERNAL_STORAGE.equals(name)) {
.
}
}
Copy the code
3 summary
6.0 permission of the main line about a time, if there is a flaw, please comment, welcome to discuss.
Also, don’t get bogged down in trivial details when looking at the source code, such as the last written file, which is basically a FileOutputStream, if you are interested.
My life is limited, and knowledge is limited, and knowledge is limited
4 thank you
Android source code environment – Gityuan
How to debug the Android Framework – Weishu