Android 通知访问权限

问题背景

客户反馈手机扫描三方运动手表,下载app安装后,通知访问权限打不开。

点击提示"受限设置" "出于安全考虑,此设置目前不可用"。

问题分析

1、setting界面搜"授予通知访问权限",此按钮灰色不可点击,点击提示"受限设置" "出于安全考虑,此设置目前不可用"。

2、"授予通知访问权限"界面在setting中的notification_access_permission_details.xml

按钮类型: RestrictedSwitchPreference

对应controller: ApprovalPreferenceController

updateState中跟踪按钮状态

java 复制代码
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。

java 复制代码
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

java 复制代码
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的地方:

java 复制代码
   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

java 复制代码
    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会自动加载进去。

java 复制代码
        <!--通知访问权限-->
        <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>
相关推荐
Estar.Lee44 分钟前
时间操作[计算时间差]免费API接口教程
android·网络·后端·网络协议·tcp/ip
找藉口是失败者的习惯1 小时前
从传统到未来:Android XML布局 与 Jetpack Compose的全面对比
android·xml
Jinkey3 小时前
FlutterBasic - GetBuilder、Obx、GetX<Controller>、GetxController 有啥区别
android·flutter·ios
大白要努力!4 小时前
Android opencv使用Core.hconcat 进行图像拼接
android·opencv
天空中的野鸟5 小时前
Android音频采集
android·音视频
小白也想学C6 小时前
Android 功耗分析(底层篇)
android·功耗
曙曙学编程6 小时前
初级数据结构——树
android·java·数据结构
闲暇部落9 小时前
‌Kotlin中的?.和!!主要区别
android·开发语言·kotlin
诸神黄昏EX11 小时前
Android 分区相关介绍
android
大白要努力!12 小时前
android 使用SQLiteOpenHelper 如何优化数据库的性能
android·数据库·oracle