1. 前言
继上一篇文章说到Android权限汇总, 请移步笔者的文章Android 权限(一):权限大全_broadview_java的博客-CSDN博客_android 仅使用中允许权限
先要理清楚权限分类和定义,本篇文章继续说一下动态权限的申请和框架层的实现流程, 以及如何实现赋予系统应用默认的权限的需求.
2. 动态权限申请
在Android6.0之后, 在首次打开app时的时候,比如微信,钉钉等软件时,会有提示
1)有读取设备上的照片及文字
2)获取手机号,IMEI,IMSI
3)获取设备的定位信息
等权限申请的对话框, 需要我们通过手动点击确认.
我们来一步一步分析对话框显示的原理和流程
2.1 检查权限
我们来通过一个Demo演示一下, Activity中的代码如下
public class MainActivity extends AppCompatActivity {
private static final int REQUEST_CODE_TEST = 1024;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if(this.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
Toast.makeText(this, "请申请权限", Toast.LENGTH_LONG).show();
//申请权限的代码
// ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_TEST);
requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_TEST);
}
}
}
//回调方法
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
android.util.Log.e("test", "====int requestCode==== :" + requestCode
+ " 具体的 permission :" + permissions[0] + "\n"
+ "== grantResults数组长度 : " + grantResults.length + "====grantResults对应的int 值:" + grantResults[0]);
}
}
代码讲解:
checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) 这个方法是检测是否有具有READ_EXTERNAL_STORAGE 此权限, 返回值 :
/**
* 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;
0:表示已获取该权限, -1:表示未赋予该权限
我是用Android 10源码追溯的流程,我们来看看此代码的调用流程:
1. frameworks/base/core/java/android/content/ContextWrapper.java
@Override
public int checkSelfPermission(String permission) {
return mBase.checkSelfPermission(permission);
}
2.frameworks/base/core/java/android/content/Context.java
@PackageManager.PermissionResult
public abstract int checkSelfPermission(@NonNull String permission);
3. 我们继续看看Context 的实现类 ContextImpl.java
frameworks/base/core/java/android/app/ContextImpl.java
@Override
public int checkSelfPermission(String permission) {
if (permission == null) {
throw new IllegalArgumentException("permission is null");
}
return checkPermission(permission, Process.myPid(), Process.myUid());
}
4.紧接着跳转到 ActivityManagerService.java中的方法
@Override
public int checkPermission(String permission, int pid, int uid) {
if (permission == null) {
return PackageManager.PERMISSION_DENIED;
}
return checkComponentPermission(permission, pid, uid, -1, true);
}
public static 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);
}
5.接着继续调用ActivityManager.java文件中的checkComponentPermission方法
public static int checkComponentPermission(String permission, int uid,
int owningUid, boolean exported) {
//省略
...........
...........
try {
return AppGlobals.getPackageManager()
.checkUidPermission(permission, uid);
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
6.继续调用到 PackageManagerService.java中的checkUidPermission方法
public int checkUidPermission(String permName, int uid) {
final CheckPermissionDelegate checkPermissionDelegate;
synchronized (mPackages) {
if (mCheckPermissionDelegate == null) {
return checkUidPermissionImpl(permName, uid);
}
checkPermissionDelegate = mCheckPermissionDelegate;
}
return checkPermissionDelegate.checkUidPermission(permName, uid,
PackageManagerService.this::checkUidPermissionImpl);
}
private int checkUidPermissionImpl(String permName, int uid) {
synchronized (mPackages) {
final String[] packageNames = getPackagesForUid(uid);
PackageParser.Package pkg = null;
final int N = packageNames == null ? 0 : packageNames.length;
for (int i = 0; pkg == null && i < N; i++) {
pkg = mPackages.get(packageNames[i]);
}
return mPermissionManager.checkUidPermission(permName, pkg, uid, getCallingUid());
}
}
7. 接下来继续调用 PermissionManagerService.java去
public int checkUidPermission(String permName, PackageParser.Package pkg, int uid,
int callingUid) {
return PermissionManagerService.this.checkUidPermission(permName, pkg, uid, callingUid);
}
//这里是最终返回判断值的方法
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;
}
}
if (isImpliedPermissionGranted(permissionsState, permName, userId)) {
return PackageManager.PERMISSION_GRANTED;
}
} else {
ArraySet<String> perms = mSystemPermissions.get(uid);
if (perms != null) {
if (perms.contains(permName)) {
return PackageManager.PERMISSION_GRANTED;
}
if (FULLER_PERMISSION_MAP.containsKey(permName)
&& perms.contains(FULLER_PERMISSION_MAP.get(permName))) {
return PackageManager.PERMISSION_GRANTED;
}
}
}
return PackageManager.PERMISSION_DENIED;
}
这里是最终判断返回值的方法, 好了,到这里阶段一就完成了. 如果返回PERMISSION_GRANTED, 那没啥事情了, 如果返回PERMISSION_DENIED, 那你就得去动态申请了, 动态申请权限不多,就是固定的几组(在上一篇文章中以介绍说明). android系统也为我们设计好了动态申请权限的对话框. 我们来说一下它的显示流程.
2.2 申请权限
申请权限是requestPermissions(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, REQUEST_CODE_TEST)方法实现的
1. 跳转到Activity.java中的requestPermissions方法
frameworks/base/core/java/android/app/Activity.java
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;
}
//创建跳转的目标activity
Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
mHasCurrentPermissionsRequest = true;
}
2. 注意一下此段代码:buildRequestPermissionsIntent(permissions)
frameworks/base/core/java/android/content/pm/PackageManager.java
@NonNull
@UnsupportedAppUsage
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;
}
@SystemApi
public static final String ACTION_REQUEST_PERMISSIONS =
"android.content.pm.action.REQUEST_PERMISSIONS";
通过这个ACTION跳转到对应的activity中, 通过在源码工程下全局搜索 然后找到对应的Activity, 我们发现是GrantPermissionsActivity.java
<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>
确定是唯一的,出现这个界面对话框时,我们也可以通过adb shell dumpsys window | grep mFocus
命令确定一下Activity找的是不是对的.
知识储备: 在Android(<=9)版本上, 动态申请权限的对话框是集成在 packages/apps/PackageInstaller 模块中, 在Android(>=10)版本上,是在packages/apps/PermissionController中,单独把这块拿出来了
3. 对话框事件处理是在GrantPermissionsViewHandlerImpl.java中, 允许,拒绝, 仅在使用该应用时允许 对应的代码如下:
@Override
public void onClick(View view) {
switch (view.getId()) {
case R.id.permission_allow_button:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ALWAYS);
}
break;
case R.id.permission_allow_always_button:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ALWAYS);
}
break;
case R.id.permission_allow_foreground_only_button:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName,
GRANTED_FOREGROUND_ONLY);
}
break;
case R.id.permission_deny_button:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName, DENIED);
}
break;
case R.id.permission_deny_and_dont_ask_again_button:
if (mResultListener != null) {
view.performAccessibilityAction(
AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
mResultListener.onPermissionGrantResult(mGroupName,
DENIED_DO_NOT_ASK_AGAIN);
}
break;
case R.id.permission_more_info_button:
Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppPackageName);
intent.putExtra(Intent.EXTRA_USER, mUserHandle);
intent.putExtra(ManagePermissionsActivity.EXTRA_ALL_PERMISSIONS, true);
mActivity.startActivity(intent);
break;
}
}
3. 赋予应用默认运行时权限
有些用户觉得app要申请权限比较麻烦,就有定制软件默认就赋予某个应用运行时权限,不弹出对话框,我们看看怎么修改呢?
先来看frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java中的systemReady()方法
// If we upgraded grant all default permissions before kicking off.
for (int userId : grantPermissionsUserIds) {
mDefaultPermissionPolicy.grantDefaultPermissions(userId);
}
跳转到DefaultPermissionGrantPolicy.java中
public void grantDefaultPermissions(int userId) {
grantPermissionsToSysComponentsAndPrivApps(userId);
//默认赋予应用的运行时权限
grantDefaultSystemHandlerPermissions(userId);
grantDefaultPermissionExceptions(userId);
synchronized (mLock) {
mDefaultPermissionsGrantedUsers.put(userId, userId);
}
}
如下这些代码,就是默认赋予运行时权限的地方
private void grantDefaultSystemHandlerPermissions(int userId) {
.........
.........
// Downloads provider
grantSystemFixedPermissionsToSystemPackage(
getDefaultProviderAuthorityPackage("downloads", userId), userId,
STORAGE_PERMISSIONS);
// Downloads UI
grantSystemFixedPermissionsToSystemPackage(
getDefaultSystemHandlerActivityPackage(
DownloadManager.ACTION_VIEW_DOWNLOADS, userId),
userId, STORAGE_PERMISSIONS);
// Storage provider
grantSystemFixedPermissionsToSystemPackage(
getDefaultProviderAuthorityPackage("com.android.externalstorage.documents", userId),
userId, STORAGE_PERMISSIONS);
// CertInstaller
grantSystemFixedPermissionsToSystemPackage(
getDefaultSystemHandlerActivityPackage(Credentials.INSTALL_ACTION, userId), userId,
STORAGE_PERMISSIONS);
// Dialer
if (dialerAppPackageNames == null) {
String dialerPackage =
getDefaultSystemHandlerActivityPackage(Intent.ACTION_DIAL, userId);
grantDefaultPermissionsToDefaultSystemDialerApp(dialerPackage, userId);
} else {
for (String dialerAppPackageName : dialerAppPackageNames) {
grantDefaultPermissionsToDefaultSystemDialerApp(dialerAppPackageName, userId);
}
}
// Sim call manager
if (simCallManagerPackageNames != null) {
for (String simCallManagerPackageName : simCallManagerPackageNames) {
grantDefaultPermissionsToDefaultSystemSimCallManager(
simCallManagerPackageName, userId);
}
}
// Use Open Wifi
if (useOpenWifiAppPackageNames != null) {
for (String useOpenWifiPackageName : useOpenWifiAppPackageNames) {
grantDefaultPermissionsToDefaultSystemUseOpenWifiApp(
useOpenWifiPackageName, userId);
}
}
// 添加代码,赋予应用默认的运行时权限,不弹出申请对话框.
grantPermissionsToSystemPackage("com.android.mytestapp", userId, STORAGE_PERMISSIONS);
}
如果你想添加自己的应用默认赋予运行时权限的话,可以这样子修改,如下:
添加包名 和 运行时权限数组
grantPermissionsToSystemPackage("com.android.mytestapp", userId, STORAGE_PERMISSIONS);