问题背景
客户反馈手机扫描三方运动手表,下载app安装后,通知访问权限打不开。
点击提示“受限设置” “出于安全考虑,此设置目前不可用”。
问题分析
1、setting界面搜“授予通知访问权限”,此按钮灰色不可点击,点击提示“受限设置” “出于安全考虑,此设置目前不可用”。
2、“授予通知访问权限”界面在setting中的notification_access_permission_details.xml
按钮类型: RestrictedSwitchPreference
对应controller: ApprovalPreferenceController
updateState中跟踪按钮状态
public void updateState(@NonNull String packageName, int uid, boolean isEnableAllowed, boolean isEnabled) {mHelper.updatePackageDetails(packageName, uid);if (mAppOpsManager == null) {mAppOpsManager = getContext().getSystemService(AppOpsManager.class);}final int mode = mAppOpsManager.noteOpNoThrow(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,uid, packageName);final boolean ecmEnabled = getContext().getResources().getBoolean(com.android.internal.R.bool.config_enhancedConfirmationModeEnabled);final boolean appOpsAllowed = !ecmEnabled || mode == AppOpsManager.MODE_ALLOWED;if (!isEnableAllowed && !isEnabled) {setEnabled(false);} else if (isEnabled) {setEnabled(true);} else if (appOpsAllowed && isDisabledByAppOps()) {setEnabled(true);} else if (!appOpsAllowed){setDisabledByAppOps(true);}
}
config_enhancedConfirmationModeEnabled这个值是framework写死的值。
mode = mAppOpsManager.noteOpNoThrow这个是根据apk动态变化的。跟踪这个值异常的原因。
3、AppOpsManager.java noteOpNoThrow
AppOpsService.java noteOperation --> noteOperationUnchecked
打开log开关,单编services。
AppOps system_server D noteOperation: package 000 com.huawei.health
AppOps system_server D noteOperation: package 222 com.huawei.health
AppOps system_server D noteOperationUnchecked: package com.huawei.health
LegacyAppOpsServiceInterfaceImpl system_server W getPackageMode packageName com.huawei.health op: 119 userId: 0
//默认值是0
LegacyAppOpsServiceInterfaceImpl system_server W getPackageMode packageName 222 com.huawei.health AppOpsManager.opToDefaultMode(op): 0
//实际拿到是2
LegacyAppOpsServiceInterfaceImpl system_server W getPackageMode packageName 444 com.huawei.health opModes.get(op, AppOpsManager.opToDefaultMode(op): 2
AppOps system_server D noteOperationUnchecked: ops 2
LegacyAppOpsServiceInterfaceImpl system_server W getPackageMode packageName com.huawei.health op: 119 userId: 0
LegacyAppOpsServiceInterfaceImpl system_server W getPackageMode packageName 222 com.huawei.health AppOpsManager.opToDefaultMode(op): 0
LegacyAppOpsServiceInterfaceImpl system_server W getPackageMode packageName 444 com.huawei.health opModes.get(op, AppOpsManager.opToDefaultMode(op): 2
AppOps system_server D noteOperationUnchecked: op 2
LegacyAppOpsServiceInterfaceImpl system_server W getPackageMode packageName com.huawei.health op: 119 userId: 0
LegacyAppOpsServiceInterfaceImpl system_server W getPackageMode packageName 222 com.huawei.health AppOpsManager.opToDefaultMode(op): 0
LegacyAppOpsServiceInterfaceImpl system_server W getPackageMode packageName 444 com.huawei.health opModes.get(op, AppOpsManager.opToDefaultMode(op): 2
//reject #2 权限被拒绝
AppOps system_server D noteOperation: reject #2 for code 119 (119) uid 10243 package com.huawei.health flags: s
AppOps system_server D noteOperationUnchecked: package 555 com.huawei.health
4、AppOpsManager.opToDefaultMode
public static @Mode int opToDefaultMode(int op) {return sAppOpInfos[op].defaultMode;
}
//sAppOpInfos是个内部数组,存的好多权限的默认值
//119 OP_ACCESS_RESTRICTED_SETTINGS 默认0 MODE_ALLOWEDnew AppOpInfo.Builder(OP_ACCESS_RESTRICTED_SETTINGS, OPSTR_ACCESS_RESTRICTED_SETTINGS,"ACCESS_RESTRICTED_SETTINGS").setDefaultMode(AppOpsManager.MODE_ALLOWED).setDisableReset(true).setRestrictRead(true).build()
5、opModes.get(op, AppOpsManager.opToDefaultMode(op): 2
设置2的地方:
setMode packageName com.huawei.health op: 119 userId: 10243 # java.lang.Throwableat com.android.server.appop.AppOpsService$Op.setMode(AppOpsService.java:637)at com.android.server.appop.AppOpsService.setMode(AppOpsService.java:2006)at com.android.server.appop.AppOpsService.setMode(AppOpsService.java:1973)at android.app.AppOpsManager.setMode(AppOpsManager.java:7609)at com.android.server.pm.InstallPackageHelper.enableRestrictedSettings(InstallPackageHelper.java:2514)at com.android.server.pm.InstallPackageHelper.updateSettingsInternalLI(InstallPackageHelper.java:2493)at com.android.server.pm.InstallPackageHelper.updateSettingsLI(InstallPackageHelper.java:2277)at com.android.server.pm.InstallPackageHelper.commitPackagesLocked(InstallPackageHelper.java:2246)at com.android.server.pm.InstallPackageHelper.installPackagesLI(InstallPackageHelper.java:1120)at com.android.server.pm.InstallPackageHelper.installPackagesTraced(InstallPackageHelper.java:987)at com.android.server.pm.InstallingSession.processApkInstallRequests(InstallingSession.java:547)at com.android.server.pm.InstallingSession.processInstallRequests(InstallingSession.java:536)at com.android.server.pm.InstallingSession.lambda$processPendingInstall$0(InstallingSession.java:295)at com.android.server.pm.InstallingSession.$r8$lambda$tqRjKCgCJYNNnnY7Qw5M5BHLup8(InstallingSession.java:0)at com.android.server.pm.InstallingSession$$ExternalSyntheticLambda2.run(R8$$SyntheticClass:0)at android.os.Handler.handleCallback(Handler.java:958)at android.os.Handler.dispatchMessage(Handler.java:99)at android.os.Looper.loopOnce(Looper.java:243)at android.os.Looper.loop(Looper.java:338)at android.os.HandlerThread.run(HandlerThread.java:67)at com.android.server.ServiceThread.run(ServiceThread.java:46)setPackageMode packageName com.huawei.health op: 119 mode: 2setPackageMode packageModes.put packageName com.huawei.health op: 119 mode: 2
6、安装应用时就设置了限制: InstallPackageHelper.enableRestrictedSettings
private void enableRestrictedSettings(String pkgName, int appId) {final AppOpsManager appOpsManager = mPm.mContext.getSystemService(AppOpsManager.class);final int[] allUsersList = mPm.mUserManager.getUserIds();for (int userId : allUsersList) {final int uid = UserHandle.getUid(userId, appId);appOpsManager.setMode(AppOpsManager.OP_ACCESS_RESTRICTED_SETTINGS,uid,pkgName,AppOpsManager.MODE_ERRORED);}}
//调用的地方// Apply restricted settings on potentially dangerous packages.if (installRequest.getPackageSource() == PackageInstaller.PACKAGE_SOURCE_LOCAL_FILE|| installRequest.getPackageSource()== PackageInstaller.PACKAGE_SOURCE_DOWNLOADED_FILE) {enableRestrictedSettings(pkgName, pkg.getUid());}...
//PackageInstaller中
/*** Code indicating that the package being installed comes from a local file on the device. A* file manager that is facilitating the installation of an APK file would use this.*/
public static final int PACKAGE_SOURCE_LOCAL_FILE = 3;/*** Code indicating that the package being installed comes from a file that was downloaded to* the device by the user. For use in place of {@link #PACKAGE_SOURCE_LOCAL_FILE} when the* installer knows the package was downloaded.*/
public static final int PACKAGE_SOURCE_DOWNLOADED_FILE = 4;
可以看出,只有当是本地apk文件安装时,才会设置此限制。
解决方案
此弹框主要是为了防止未知来源的apk文件请求权限,正规途径安装不受影响。
用户也可以在应用信息中手动解除限制。
1、打开受限设置
setting—app management—app list—“ xxx Health”—“…”—“allow restricted settings”
这里其实也是调用的setMode MODE_ALLOWED
2、通过play store安装。(或者adb 绕过上面的if就可以)
如何让自己的应用显示在这里
注册action android:name=“android.service.notification.NotificationListenerService”
setting会自动加载进去。
<!--通知访问权限--><serviceandroid:name=".NotificationListener"android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"android:enabled="true"android:exported="true"><intent-filter><actionandroid:name="android.service.notification.NotificationListenerService" /></intent-filter></service>