问题背景
客户反馈手机扫描三方运动手表,下载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_ALLOWED
new 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.Throwable
at 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: 2
setPackageMode 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会自动加载进去。
<!--通知访问权限-->
<service
android:name=".NotificationListener"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE"
android:enabled="true"
android:exported="true">
<intent-filter>
<action
android:name="android.service.notification.NotificationListenerService" />
</intent-filter>
</service>