Android 6.0 动态权限实战(二 - 终结章 - 高手必看!)------ 关于动态权限的正确使用与理解
来源:互联网 发布:程序员电脑桌面壁纸 编辑:程序博客网 时间:2024/06/05 01:27
关于上一章节提到的 23.06ADT和6.0的SDK 资源,现在马上贴出来
链接:http://pan.baidu.com/s/1dEO5eb3 密码:kkwr
链接:http://pan.baidu.com/s/1bOBhDk 密码:nsv5
如果链接不存在,或者资源有问题,请在本博留言,勿发私信,谢谢!
在本篇开篇前先大致了解一下 6.0 动态权限的大致过程,废话不多说,linux打开源码,开始探究
1.首先,比如我们需要手动授权的时候,怎么去到对应包名的那个设置的界面呢?
我这里以 EasyPermissions 的跳转设置为例
dialogBuilder.setPositiveButton(positiveButtonText, new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {// Create app settings intentIntent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);Uri uri = Uri.fromParts("package", context.getPackageName(), null);intent.setData(uri);// Start for resultstartForResult(activityOrFragment, intent, settingsRequestCode);}});其中 Settings.ACTION_APPLICATION_DETAILS_SETTINGS 来自 Settings 的一个静态的字符串类型,这个就不多说了,可以看到该行的下一行是将自己的包名打包通过uri以Intent的形式发送出去了,那到底谁注册了 Settings.ACTION_APPLICATION_DETAILS_SETTINGS 这个action呢?我们接着往下看
2.Settings.ACTION_APPLICATION_DETAILS_SETTINGS 的注册页面
3.找到对应的静态常量对应的action
4.找到对应action的注册文件
5.InstalledAppDetails extends AppInfoBase,AppInfoBase extends SettingsPreferenceFragment,SettingsPreferenceFragment extends InstrumentedPreferenceFragment , InstrumentedFragment extends PreferenceFragment
PreferenceFragment 来自 frameworks\base\core\java\android\preference 目录
abstract class PreferenceFragment extends Fragment implements PreferenceManager.OnPreferenceTreeClickListener 是一个抽象类
6.查看 InstalledAppDetails
public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); if (mFinishing) { return; } handleHeader(); mNotificationPreference = findPreference(KEY_NOTIFICATION); mNotificationPreference.setOnPreferenceClickListener(this); mStoragePreference = findPreference(KEY_STORAGE); mStoragePreference.setOnPreferenceClickListener(this);// 在这里添加对应的 PermissionsPreference mPermissionsPreference = findPreference(KEY_PERMISSION);// 在这里添加对应的 Preference 点击事件 mPermissionsPreference.setOnPreferenceClickListener(this); mDataPreference = findPreference(KEY_DATA); if (mDataPreference != null) { mDataPreference.setOnPreferenceClickListener(this); } mBatteryPreference = findPreference(KEY_BATTERY); mBatteryPreference.setEnabled(false); mBatteryPreference.setOnPreferenceClickListener(this); mMemoryPreference = findPreference(KEY_MEMORY); mMemoryPreference.setOnPreferenceClickListener(this); mLaunchPreference = findPreference(KEY_LAUNCH); if (mAppEntry != null && mAppEntry.info != null) { if ((mAppEntry.info.flags&ApplicationInfo.FLAG_INSTALLED) == 0 || !mAppEntry.info.enabled) { mLaunchPreference.setEnabled(false); } else { mLaunchPreference.setOnPreferenceClickListener(this); } } else { mLaunchPreference.setEnabled(false); } }
7.PermissionsPreference 点击事件的响应
@Override public boolean onPreferenceClick(Preference preference) { if (preference == mStoragePreference) { startAppInfoFragment(AppStorageSettings.class, mStoragePreference.getTitle()); } else if (preference == mNotificationPreference) { startAppInfoFragment(AppNotificationSettings.class, getString(R.string.app_notifications_title));// 这里为点击权限 Preference 的响应 } else if (preference == mPermissionsPreference) { startManagePermissionsActivity(); } else if (preference == mLaunchPreference) { startAppInfoFragment(AppLaunchSettings.class, mLaunchPreference.getTitle()); } else if (preference == mMemoryPreference) { ProcessStatsBase.launchMemoryDetail((SettingsActivity) getActivity(), mStatsManager.getMemInfo(), mStats); } else if (preference == mDataPreference) { Bundle args = new Bundle(); args.putString(DataUsageSummary.EXTRA_SHOW_APP_IMMEDIATE_PKG, mAppEntry.info.packageName); SettingsActivity sa = (SettingsActivity) getActivity(); sa.startPreferencePanel(DataUsageSummary.class.getName(), args, -1, getString(R.string.app_data_usage), this, SUB_INFO_FRAGMENT); } else if (preference == mBatteryPreference) { BatteryEntry entry = new BatteryEntry(getActivity(), null, mUserManager, mSipper); PowerUsageDetail.startBatteryDetailPage((SettingsActivity) getActivity(), mBatteryHelper, BatteryStats.STATS_SINCE_CHARGED, entry, true); } else { return false; } return true; }
8.PermissionsPreference 点击事件响应的执行方法
private void startManagePermissionsActivity() { // start new activity to manage app permissions Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS); intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppEntry.info.packageName); intent.putExtra(AppInfoWithHeader.EXTRA_HIDE_INFO_BUTTON, true); try { startActivity(intent); } catch (ActivityNotFoundException e) { Log.w(LOG_TAG, "No app can handle android.intent.action.MANAGE_APP_PERMISSIONS"); } }
由上述代码可知,该函数将包名打包到intent中发送给了另一个页面处理,所有权限开关操作实际上 settings 只是充当了一个代理
9.查看 action 的注册
10.查看该页面的代码实现
/** Copyright (C) 2015 The Android Open Source Project** Licensed under the Apache License, Version 2.0 (the "License");* you may not use this file except in compliance with the License.* You may obtain a copy of the License at** http://www.apache.org/licenses/LICENSE-2.0** Unless required by applicable law or agreed to in writing, software* distributed under the License is distributed on an "AS IS" BASIS,* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.* See the License for the specific language governing permissions and* limitations under the License.*/package com.android.packageinstaller.permission.ui;import android.app.Fragment;import android.content.Intent;import android.os.Bundle;import android.util.Log;public final class ManagePermissionsActivity extends OverlayTouchActivity { private static final String LOG_TAG = "ManagePermissionsActivity"; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (savedInstanceState != null) { return; }// 这里定义一个 Fragment 对象 Fragment fragment;// 接收intent的action String action = getIntent().getAction();// 根据action进行fragment的实例化 switch (action) { case Intent.ACTION_MANAGE_PERMISSIONS: { fragment = ManagePermissionsFragment.newInstance(); } break;// 此处为我们要找的逻辑 case Intent.ACTION_MANAGE_APP_PERMISSIONS: { String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME); if (packageName == null) { Log.i(LOG_TAG, "Missing mandatory argument EXTRA_PACKAGE_NAME"); finish(); return; } fragment = AppPermissionsFragment.newInstance(packageName); } break; case Intent.ACTION_MANAGE_PERMISSION_APPS: { String permissionName = getIntent().getStringExtra(Intent.EXTRA_PERMISSION_NAME); if (permissionName == null) { Log.i(LOG_TAG, "Missing mandatory argument EXTRA_PERMISSION_NAME"); finish(); return; } fragment = PermissionAppsFragment.newInstance(permissionName); } break; default: { Log.w(LOG_TAG, "Unrecognized action " + action); finish(); return; } } getFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commit(); }}
11.查看 AppPermissionsFragment 的静态函数newInstance
public static AppPermissionsFragment newInstance(String packageName) { return setPackageName(new AppPermissionsFragment(), packageName); } private static <T extends Fragment> T setPackageName(T fragment, String packageName) { Bundle arguments = new Bundle(); arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);// 将包名以 Bundle String 键值对存储,并调用setArguments fragment.setArguments(arguments); return fragment; }@Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setLoading(true /* loading */, false /* animate */); setHasOptionsMenu(true); final ActionBar ab = getActivity().getActionBar(); if (ab != null) { ab.setDisplayHomeAsUpEnabled(true); }// getArguments() 从setArguments 函数取出 Bundle String 的包名键值对 String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME); Activity activity = getActivity();// 根据包名获取 PackageInfo PackageInfo packageInfo = getPackageInfo(activity, packageName); if (packageInfo == null) { Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show(); activity.finish(); return; }// 传入 PackageInfo 进行权限相关操作 mAppPermissions = new AppPermissions(activity, packageInfo, null, true, new Runnable() { @Override public void run() { getActivity().finish(); } });// 初始化GUI loadPreferences(); }
12.AppPermissions 构造函数
// AppPermissions 构造函数public AppPermissions(Context context, PackageInfo packageInfo, String[] permissions, boolean sortGroups, Runnable onErrorCallback) { mContext = context; mPackageInfo = packageInfo; mFilterPermissions = permissions;// 加载 app 的lable 也就是你的app在桌面的名字 mAppLabel = loadEllipsizedAppLabel(context, packageInfo); mSortGroups = sortGroups; mOnErrorCallback = onErrorCallback;// 加载权限组 loadPermissionGroups(); }// 加载权限组private void loadPermissionGroups() {// 当前对象初次调用实例化,清空 ArrayList<AppPermissionGroup> mGroups 对象的数据 mGroups.clear();// 当 PackageInfo 为 null 不再往下执行 if (mPackageInfo.requestedPermissions == null) { return; }// mFilterPermissions 在本地对象第一次实例化,是默认为 null的,因为参数来自 AppPermissions 且 null,所以会走 else 的逻辑 if (mFilterPermissions != null) { for (String filterPermission : mFilterPermissions) { for (String requestedPerm : mPackageInfo.requestedPermissions) { if (!filterPermission.equals(requestedPerm)) { continue; } if (hasGroupForPermission(requestedPerm)) { break; } AppPermissionGroup group = AppPermissionGroup.create(mContext, mPackageInfo, requestedPerm); if (group == null) { break; } mGroups.add(group); break; } } } else {// 遍历 app 的请求权限列表 for (String requestedPerm : mPackageInfo.requestedPermissions) {// 如果 ArrayList<AppPermissionGroup> mGroups 对象已存在该权限,不添加,继续下一个 if (hasGroupForPermission(requestedPerm)) { continue; } AppPermissionGroup group = AppPermissionGroup.create(mContext, mPackageInfo, requestedPerm);// 若 group 为 null ,继续下一个 if (group == null) { continue; } mGroups.add(group); } }// 默认权限排序 if (mSortGroups) { Collections.sort(mGroups); }// 清空权限组的 键值名称,如:电话或存储空间 mNameToGroupMap.clear();// 变量新的app所有的请求权限组,新增至 mNameToGroupMap for (AppPermissionGroup group : mGroups) { mNameToGroupMap.put(group.getName(), group); } } private boolean hasGroupForPermission(String permission) { for (AppPermissionGroup group : mGroups) { if (group.hasPermission(permission)) { return true; } } return false; }
13.AppPermissionsFragment 的UI初始化
@Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); if (mAppPermissions != null) { bindUi(this, mAppPermissions.getPackageInfo()); } } private static void bindUi(SettingsWithHeader fragment, PackageInfo packageInfo) { Activity activity = fragment.getActivity(); PackageManager pm = activity.getPackageManager(); ApplicationInfo appInfo = packageInfo.applicationInfo; Intent infoIntent = null; if (!activity.getIntent().getBooleanExtra(EXTRA_HIDE_INFO_BUTTON, false)) { infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) .setData(Uri.fromParts("package", packageInfo.packageName, null)); }// 图标 Drawable icon = appInfo.loadIcon(pm);// app label CharSequence label = appInfo.loadLabel(pm); fragment.setHeader(icon, label, infoIntent); ActionBar ab = activity.getActionBar(); if (ab != null) { ab.setTitle(R.string.app_permissions); } ViewGroup rootView = (ViewGroup) fragment.getView(); ImageView iconView = (ImageView) rootView.findViewById(R.id.lb_icon); if (iconView != null) { iconView.setImageDrawable(icon); } TextView titleView = (TextView) rootView.findViewById(R.id.lb_title); if (titleView != null) { titleView.setText(R.string.app_permissions); } TextView breadcrumbView = (TextView) rootView.findViewById(R.id.lb_breadcrumb); if (breadcrumbView != null) { breadcrumbView.setText(label); } }
14.权限改变,用户手点的实现
@Override public boolean onPreferenceChange(final Preference preference, Object newValue) { // 当 Preference change 也就是权限被用户手动改变时回调的对应的 Preference String groupName = preference.getKey();// 将 key 去AppPermissionGroup中取出 final AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);// 当group为null默认关 if (group == null) { return false; }// 取出宿主 OverlayTouchActivity OverlayTouchActivity activity = (OverlayTouchActivity) getActivity();// 监听当前权限界面是否被覆盖和叠加 if (activity.isObscuredTouch()) {// 弹出一个警告框,检测到屏幕叠加层,会弹出此框,并提示 要更改此权限设置,您必须首先在“设置”>“应用”中关闭屏幕叠加层" activity.showOverlayDialog(); return false; }// 添加一个新的权限 group 到Togglelist中 addToggledGroup(group);// 检测是否添加了Manifest.permission_group.LOCATION 权限组并且该应用为系统应用,包名 equals(ILocationManager.Stub.asInterface( ServiceManager.getService(Context.LOCATION_SERVICE)).getNetworkProviderPackage()) if (LocationUtils.isLocationGroupAndProvider(group.getName(), group.getApp().packageName)) {// 弹出一个警告,提示:是此设备的一个位置信息服务提供程序。您可以在位置信息设置中修改位置信息使用权 LocationUtils.showLocationDialog(getContext(), mAppPermissions.getAppLabel()); return false; }// Object newValue == true,用户赋予权限 if (newValue == Boolean.TRUE) {/**** 此处由AppPermissionGroup间接调用抽象类PackageManager的grantRuntimePermissions函数后面会细说**/ group.grantRuntimePermissions(false); } else {// grantedByDefault为false 提示:此应用专为旧版 Android 打造。拒绝权限可能会导致其无法正常运行// grantedByDefault为true 提示:如果您拒绝此权限,您设备的基本功能可能会无法正常使用 final boolean grantedByDefault = group.hasGrantedByDefaultPermission();// grantedByDefault || 当前app targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1 && 是否为旧版(即SDK<23),默认 false if (grantedByDefault || (!group.hasRuntimePermission() && !mHasConfirmedRevoke)) { new AlertDialog.Builder(getContext()) .setMessage(grantedByDefault ? R.string.system_warning : R.string.old_sdk_deny_warning) .setNegativeButton(R.string.cancel, null) .setPositiveButton(R.string.grant_dialog_button_deny, new OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) {// 更新权限开关 ((SwitchPreference) preference).setChecked(false);// 关闭权限/**** 此处由AppPermissionGroup间接调用抽象类PackageManager的revokeRuntimePermissions函数后面会细说**/ group.revokeRuntimePermissions(false);// 倘若是旧版,mHasConfirmedRevoke = true if (!grantedByDefault) { mHasConfirmedRevoke = true; } } }) .show(); return false; } else { group.revokeRuntimePermissions(false); } } return true; }
15.AppPermissionGroup grantRuntimePermissions() revokeRuntimePermissions() 函数具体实现
public boolean grantRuntimePermissions(boolean fixedByTheUser) { final boolean isSharedUser = mPackageInfo.sharedUserId != null; final int uid = mPackageInfo.applicationInfo.uid; // 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()) {// 具备运行时权限的 app if (mAppSupportsRuntimePermissions) { // Do not touch permissions fixed by the system. if (permission.isSystemFixed()) { return false; } // Ensure the permission app op enabled before the permission grant. if (permission.hasAppOp() && !permission.isAppOpAllowed()) { permission.setAppOpAllowed(true); mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_ALLOWED); } // Grant the permission if needed.// 如果没有授权,授予权限// mPackageManager 来自 Context.getPackageManager(),而 Context 是一个抽象类,具体方法实现由其子类 ContextImpl 实现// ContextImpl 下 getPackageManager() 函数调用 ActivityThread.getPackageManager() 函数得到 IPackageManager 实例,倘若这个实例不为 null ,函数返回new ApplicationPackageManager(this, pm)// ApplicationPackageManager 对象,但是ApplicationPackageManager也继承自 PackageManager 且间接调用 IPackageManager ,而 IPackageManager 最终是以aidl的形式通过 IBinder 传递,由 PackageManagerService 继承// 抽象方法在该类实现 if (!permission.isGranted()) { permission.setGranted(true); mPackageManager.grantRuntimePermission(mPackageInfo.packageName, permission.getName(), mUserHandle); } // 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(true); mPackageManager.updatePermissionFlags(permission.getName(), mPackageInfo.packageName, PackageManager.FLAG_PERMISSION_USER_FIXED | PackageManager.FLAG_PERMISSION_USER_SET, 0, mUserHandle); } } } else { // Legacy apps cannot have a not granted permission but just in case. // Also 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.isGranted() || !permission.hasAppOp()) { continue; } if (!permission.isAppOpAllowed()) { permission.setAppOpAllowed(true); // It this is a shared user we want to enable the app op for all // packages in the shared user to match the behavior of this // shared user having a runtime permission. if (isSharedUser) { // Enable the app op. String[] packageNames = mPackageManager.getPackagesForUid(uid); for (String packageName : packageNames) { mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_ALLOWED); } } else { // Enable the app op. mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_ALLOWED); } // Mark that the permission should not be be granted on upgrade // when the app begins supporting runtime permissions. if (permission.shouldRevokeOnUpgrade()) { permission.setRevokeOnUpgrade(false); mPackageManager.updatePermissionFlags(permission.getName(), mPackageInfo.packageName, PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, 0, mUserHandle); } // 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. mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE); } } } return true; } public boolean revokeRuntimePermissions(boolean fixedByTheUser) { final boolean isSharedUser = mPackageInfo.sharedUserId != null; final int uid = mPackageInfo.applicationInfo.uid; // 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()) {// 具备运行时权限的 app if (mAppSupportsRuntimePermissions) { // Do not touch permissions fixed by the system. if (permission.isSystemFixed()) { return false; } // Revoke the permission if needed.// 如果有授权,取消授予权限// mPackageManager 来自 Context.getPackageManager(),而 Context 是一个抽象类,具体方法实现由其子类 ContextImpl 实现// ContextImpl 下 getPackageManager() 函数调用 ActivityThread.getPackageManager() 函数得到 IPackageManager 实例,倘若这个实例不为 null ,函数返回new ApplicationPackageManager(this, pm)// ApplicationPackageManager 对象,但是ApplicationPackageManager也继承自 PackageManager 且间接调用 IPackageManager ,而 IPackageManager 最终是以aidl的形式通过 IBinder 传递,由 PackageManagerService 继承// 抽象方法在该类实现 if (permission.isGranted()) { permission.setGranted(false); mPackageManager.revokeRuntimePermission(mPackageInfo.packageName, permission.getName(), mUserHandle); } // Update the permission flags. if (fixedByTheUser) { // Take a note that the user fixed the permission. if (permission.isUserSet() || !permission.isUserFixed()) { permission.setUserSet(false); permission.setUserFixed(true); mPackageManager.updatePermissionFlags(permission.getName(), mPackageInfo.packageName, PackageManager.FLAG_PERMISSION_USER_SET | PackageManager.FLAG_PERMISSION_USER_FIXED, PackageManager.FLAG_PERMISSION_USER_FIXED, mUserHandle); } } else { if (!permission.isUserSet()) { permission.setUserSet(true); // Take a note that the user already chose once. mPackageManager.updatePermissionFlags(permission.getName(), mPackageInfo.packageName, PackageManager.FLAG_PERMISSION_USER_SET, PackageManager.FLAG_PERMISSION_USER_SET, mUserHandle); } } } else { // Legacy apps cannot have a non-granted permission but just in case. // Also if the permission has no corresponding app op, then it is a // third-party one and we do not offer toggling of such permissions. if (!permission.isGranted() || !permission.hasAppOp()) { continue; } if (permission.isAppOpAllowed()) { permission.setAppOpAllowed(false); // It this is a shared user we want to enable the app op for all // packages the the shared user to match the behavior of this // shared user having a runtime permission. if (isSharedUser) { String[] packageNames = mPackageManager.getPackagesForUid(uid); for (String packageName : packageNames) { // Disable the app op. mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_IGNORED); } } else { // Disable the app op. mAppOps.setUidMode(permission.getAppOp(), uid, AppOpsManager.MODE_IGNORED); } // Mark that the permission should not be granted on upgrade // when the app begins supporting runtime permissions. if (!permission.shouldRevokeOnUpgrade()) { permission.setRevokeOnUpgrade(true); mPackageManager.updatePermissionFlags(permission.getName(), mPackageInfo.packageName, PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE, mUserHandle); } // 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. mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE); } } } return true; }
16.PackageManagerService grantRuntimePermissions() revokeRuntimePermissions() 函数的具体实现
/*** * ApplicationPackageManager * 在介绍 PackageManagerService grantRuntimePermissions() revokeRuntimePermissions() 函数之前需要介绍如下两个函数* 即继承至 PackageManager 的 ApplicationPackageManager 类,因为抽象类 Context.getPackageManager()函数由其子类ContextImpl实现,并且调用 ActivityThread.getPackageManager() 函数得到 IPackageManager * 实例,当实例不为 null ,连同 ContextImpl 一起传入构造函数new ApplicationPackageManager(this, pm)得到PackageManager 子类对象 ApplicationPackageManager* this 指 ContextImpl 对象,pm 为 IPackageManager 对象,最后又由 ApplicationPackageManager 对象间接调用和执行IPackageManager的 grantRuntimePermission() revokeRuntimePermission() 函数* 并且取出 UserHandle 对象的一个整型值传入,IPackageManager 的aidl对象类的子类 PackageManagerService ,这就是为什么 PackageManagerService 最后一个函数为什么有泛型UserHandle变成了 int 类型* UserHandle implements Parcelable ,有点类似 Bundle 的作用,只不过 Bundle 用于上层,UserHandle 用于系统层这个样子*/@Override public void grantRuntimePermission(String packageName, String permissionName, UserHandle user) { try {// mPM 为 IPackageManager 对象 mPM.grantRuntimePermission(packageName, permissionName, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } } @Override public void revokeRuntimePermission(String packageName, String permissionName, UserHandle user) { try {// mPM 为 IPackageManager 对象 mPM.revokeRuntimePermission(packageName, permissionName, user.getIdentifier()); } catch (RemoteException e) { throw new RuntimeException("Package manager has died", e); } }/** * * UserHandle.java * Returns the userId stored in this UserHandle. * @hide */ @SystemApi public int getIdentifier() { return mHandle; }// ++++++++++++++++++++++++++++++++++ 分割线 +++++++++++++++++++++++++++++++++++++++++++++++++// PackageManagerService.java@Override public void grantRuntimePermission(String packageName, String name, final int userId) { if (!sUserManager.exists(userId)) { Log.e(TAG, "No such user:" + userId); return; } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, "grantRuntimePermission"); enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "grantRuntimePermission"); final int uid; final SettingBase sb; synchronized (mPackages) {// 检测包 final PackageParser.Package pkg = mPackages.get(packageName); if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); }// 检测权限 final BasePermission bp = mSettings.mPermissions.get(name); if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + name); } enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp); uid = UserHandle.getUid(userId, pkg.applicationInfo.uid); sb = (SettingBase) pkg.mExtras; if (sb == null) { throw new IllegalArgumentException("Unknown package: " + packageName); }// 权限状态 final PermissionsState permissionsState = sb.getPermissionsState();// 权限状态标识 final int flags = permissionsState.getPermissionFlags(name, userId);// PackageManager.FLAG_PERMISSION_SYSTEM_FIXED 为系统固定权限 if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { throw new SecurityException("Cannot grant system fixed permission: " + name + " for package: " + packageName); } if (bp.isDevelopment()) { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. if (permissionsState.grantInstallPermission(bp) != PermissionsState.PERMISSION_OPERATION_FAILURE) { scheduleWriteSettingsLocked(); } return; } final int result = permissionsState.grantRuntimePermission(bp, userId); switch (result) {// 操作失败 case PermissionsState.PERMISSION_OPERATION_FAILURE: { return; } // 操作成功,根据当前 uid 获取 appid 即包名 case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: { final int appId = UserHandle.getAppId(pkg.applicationInfo.uid); mHandler.post(new Runnable() { @Override public void run() { // 将包名,uid,以及操作标志位传递给 killUid 函数,实质这里就是,为什么用户在设置界面取消权限,app 莫名奇妙就挂掉了 // 后面会详细介绍这个 killUid函数 killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED); } }); } break; }// 权限改变监听回调 mOnPermissionChangeListeners.onPermissionsChanged(uid); // Not critical if that is lost - app has to request again. mSettings.writeRuntimePermissionsForUserLPr(userId, false); } // Only need to do this if user is initialized. Otherwise it's a new user // and there are no processes running as the user yet and there's no need // to make an expensive call to remount processes for the changed permissions. if (READ_EXTERNAL_STORAGE.equals(name) || WRITE_EXTERNAL_STORAGE.equals(name)) { final long token = Binder.clearCallingIdentity(); try { if (sUserManager.isInitialized(userId)) { MountServiceInternal mountServiceInternal = LocalServices.getService( MountServiceInternal.class); mountServiceInternal.onExternalStoragePolicyChanged(uid, packageName); } } finally { Binder.restoreCallingIdentity(token); } } } @Override public void revokeRuntimePermission(String packageName, String name, int userId) { if (!sUserManager.exists(userId)) { Log.e(TAG, "No such user:" + userId); return; } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, "revokeRuntimePermission"); enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false, "revokeRuntimePermission"); final int appId; synchronized (mPackages) { final PackageParser.Package pkg = mPackages.get(packageName);// 检测包 if (pkg == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } final BasePermission bp = mSettings.mPermissions.get(name);// 检测权限 if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + name); } enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp); SettingBase sb = (SettingBase) pkg.mExtras; if (sb == null) { throw new IllegalArgumentException("Unknown package: " + packageName); }// 权限状态 final PermissionsState permissionsState = sb.getPermissionsState();// 权限状态标识 final int flags = permissionsState.getPermissionFlags(name, userId); if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { throw new SecurityException("Cannot revoke system fixed permission: " + name + " for package: " + packageName); } if (bp.isDevelopment()) { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. if (permissionsState.revokeInstallPermission(bp) != PermissionsState.PERMISSION_OPERATION_FAILURE) { scheduleWriteSettingsLocked(); } return; }// 失败,return if (permissionsState.revokeRuntimePermission(bp, userId) == PermissionsState.PERMISSION_OPERATION_FAILURE) { return; }// 权限改变监听回调 mOnPermissionChangeListeners.onPermissionsChanged(pkg.applicationInfo.uid); // Critical, after this call app should never have the permission. mSettings.writeRuntimePermissionsForUserLPr(userId, true); appId = UserHandle.getAppId(pkg.applicationInfo.uid); }// 将包名,uid,以及操作标志位传递给 killUid 函数,实质这里就是,为什么用户在设置界面取消权限,app 莫名奇妙就挂掉了// 后面会详细介绍这个 killUid函数 killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED); }
17 killUid 函数具体实现
/*** PackageManagerService* 关于这个函数,官方给出的解释如下,若翻译不对,请见谅!* 大意:旧版应用程序不知道由于运行时权限(在此情况下为应用操作)的更改,他们必须重试对资源的访问权限。 因此,我们在应用程序更改时重新启动它们,以便他们可以接受更改*/private void killUid(int appId, int userId, String reason) { final long identity = Binder.clearCallingIdentity(); try {// getDefault() 函数返回 IActivityManager IActivityManager am = ActivityManagerNative.getDefault(); if (am != null) { try {// 由其实现类 ActivityManagerProxy killUid() 函数实现,ActivityManagerProxy 为 ActivityManagerNative 的内部类 am.killUid(appId, userId, reason); } catch (RemoteException e) { /* ignore - same process */ } } } finally { Binder.restoreCallingIdentity(identity); } }/*** ActivityManagerNative 的内部类 ActivityManagerProxy* killUid 函数实现**/ public void killUid(int appId, int userId, String reason) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeInt(appId); data.writeInt(userId); data.writeString(reason);// mRemote 为 IBinder ,该 IBinder 来自 ActivityManagerProxy 构造函数,该构造函数由外部类 ActivityManagerNative asInterface(IBinder obj) 调用 mRemote.transact(KILL_UID_TRANSACTION, data, reply, 0); reply.readException(); data.recycle(); reply.recycle(); }/**** ActivityManagerNative asInterface(IBinder obj) 函数* 该静态函数由 frameworks/base/services/core/java/com/android/server/power/ShutdownThread.java* 调用**/ /** * Cast a Binder object into an activity manager interface, generating * a proxy if needed. */ static public IActivityManager asInterface(IBinder obj) { if (obj == null) { return null; } IActivityManager in = (IActivityManager)obj.queryLocalInterface(descriptor); if (in != null) { return in; } return new ActivityManagerProxy(obj); }/**** ShutdownThread.java**/// 通过 ServiceManager checkService() 函数传入String值,得到对应的 Service IBinder 实例 final IActivityManager am = ActivityManagerNative.asInterface(ServiceManager.checkService("activity")); if (am != null) { try { am.shutdown(MAX_BROADCAST_TIME); } catch (RemoteException e) { } } if (mRebootUpdate) { sInstance.setRebootProgress(ACTIVITY_MANAGER_STOP_PERCENT, null); }/**** ServiceManager.java**/ /** * Retrieve an existing service called @a name from the * service manager. Non-blocking. */ public static IBinder checkService(String name) { try { IBinder service = sCache.get(name); if (service != null) { return service; } else {// 间接调用 aidl IServiceManager 的函数,而 IServiceManager 由 ServiceManagerNative 实现,ServiceManagerNative 也继承 Binder// ServiceManagerNative 内部类 ServiceManagerProxy 也实现 IServiceManager 类 return getIServiceManager().checkService(name); } } catch (RemoteException e) { Log.e(TAG, "error in checkService", e); return null; } }/** * Context.java * ServiceManagerNative checkService() 函数最终调用的系统服务为 android.app.ActivityManager 类 * Use with {@link #getSystemService} to retrieve a * {@link android.app.ActivityManager} for interacting with the global * system state. * * @see #getSystemService * @see android.app.ActivityManager */ public static final String ACTIVITY_SERVICE = "activity";
至此整个应用的一些activity等会被回收,如果回收不够全面就会出现重引用的问题,我之前的主页activity就是存在自定义的静态activity堆栈里面管理的,后来改
为自定义 application来管理,然后去掉了静态activity的代理管理类ActivityManger类以及该项目下所有的 Fragment ,改为全自定义 view,才把这个问题从根本上
解决!
造成这个原因我个人认为,当用户手动去设置界面取消权限的时候(你的项目已经正常运行的情况下,且已经授权过了),系统会获取该app的applicationinfo下的相
关信息,然后进行热启动,回收掉了一些不必要不可见的对象和view,但如果存在类似静态强引用等,可能会造成gc失败,在jvm上也不是不可能!反正最终这个
bug我是完美搞定了!
下面还分享一下关于这个问题的一些心得:
当我们的应用程序在targetSdkVersion>=23的时候就会有很多需要我们考虑的东西,比如一个应用需要在 application 下初始化一个 imageloader 或者百度地图,但
是 google 官方提供的 v4 权限检测的兼容库需要这个权限判断添加在 Activity 或 Fragment ,而整个程序的开始初始化application的时候,界面都还没起来,这时候
该怎么办呢?而逻辑恰好又需要这个操作在application中初始化,这时候我们可以采用 google 官方提供的一个目录进行读写,可以不需要 Storage 权限组,
即 android.content.Context.getExternalCacheDir() 目录,而百度的SDK初始化需要 read_phone_state 这个权限,但这个权限是被动,意思就是说,当我去掉用百度
地图的时候才会用到那个读取电话状态的权限,虽然百度SDK初始化的时候也会调一次,但不会 crash 掉,也是不影响的
还有就是当app已经启动了,用户手动按下 home 键之后,回到桌面,打开设置,进入该 app 的权限管理界面,对该app的权限进行手动取消和给与权限,这时候
应用就会莫名其妙的重启,且不会执行 onDestroy onDestroyView onDetach 类似的函数,但实质 app 已经挂掉了,很奇怪的是 app 不会被crash 掉,且没有任何日
志,这时候如果你去做一些之前初始化的对象的相关操作,比如在 Fragment 中写了一个下拉刷新的操作,这时候就会报空,很不能让人理解!按道理这个对象应
该也随上一次用户手动去设置界面取消授权的时候,应用莫名奇妙异常那次gc掉了啊!为什么会这样呢?那么问题来了。。。
这个问题我也遇到了,而且还去了 segmentfault 求助,发现没有什么卵用,全都没答到点上,一点帮助都没有。。。
地址还在:https://segmentfault.com/q/1010000007930966 就连神奇的 stackoverflow 也没有找到相关的资料,有一个类似的提问,但是回答也不令人满意,最后
还是自己琢磨,把所有可能涉及的问题一个一个的过,首先 Fragment 既然没有随应用一起gc,那我就去掉 Fragment ,于是我把项目所有的 Fragment 全部去掉
了!采用自定义的view来取代 Fragment ,然后去掉了所有不相关的且无关紧要或没有被gc的 static 字段修饰的变量,并且把静态的 ActivityStack 堆栈管理写到了
自定义的 application 下,再次走那样的不正常的流程的时候,我发现问题就已经被我解决掉了!真是坑爹啊!花了2天的时间去研究源码,还是有些作用的!哈哈...
- Android 6.0 动态权限实战(二 - 终结章 - 高手必看!)------ 关于动态权限的正确使用与理解
- Android实战二:动态权限的BaseActivity封装
- 关于Android6.0动态权限的理解
- Android 6.0(API 23)及其以上动态申请的权限与申请权限的方法
- 关于android 6.0手机权限动态请求
- Android 6.0动态权限
- android 6.0动态权限
- android 6.0 动态权限
- Android 6.0 动态权限
- android 6.0动态权限
- Android-6.0动态权限
- android 6.0 动态权限
- Android 6.0动态权限
- Android 6.0动态权限
- android 6.0动态权限
- Android的权限大全和动态使用Android权限方法
- Android自定义权限与动态申请权限
- Android 6.0动态权限介绍与处理
- jq监听input文本变化
- TODO:小程序的使用体验
- JSF Converter介绍与使用
- DEDECMS 管理员帐号重设工具
- Flask-admin使用经验技巧总结
- Android 6.0 动态权限实战(二 - 终结章 - 高手必看!)------ 关于动态权限的正确使用与理解
- [Leetcode] 47. Permutations II 解题报告
- Python Notebook简介
- 解决json中文乱码问题
- windows下修改磁盘扇区数据
- Java基本数据类型总结
- Java 获取IP/MAC地址等网络信息的方法
- AndroidStudio打包跑出来一堆错误解决方法:
- 【杭电OJ从头刷】HDU1002