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>
相关推荐
千里马学框架5 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation
美狐美颜SDK开放平台5 天前
开发直播APP时如何接入视频美颜SDK?开发流程与注意事项
android·人工智能·计算机视觉·音视频·直播美颜sdk
AFinalStone5 天前
Android7 SystemUI源码解析(七)Keyguard锁屏模块深度解析
android·systemui
致远ccc5 天前
Google Play 上架前如何测试 App?多国家 Android 环境测试
android·app测试·googleplay·多国家应用测试
ttyyttemo5 天前
Kotlin 协程中的 Job 结构化并发与取消
android
sun0077005 天前
tbox 4g/5g切换,导致wan ip 改变,导致车机旧网络不可用。需要重启车机才行
android
其实防守也摸鱼5 天前
内网穿透与反向代理:原理、工具与实战指南
android·大数据·运维·安全·网络安全·自动化·渗透
AFinalStone5 天前
Android7 SystemUI 源码解析(四)NavigationBar 导航栏与 SystemBars
android·systemui
JMchen5 天前
属性动画原理与高级动画实现
android·kotlin·canvas
AFinalStone5 天前
Android7 SystemUI 源码解析(二)启动流程深度解析
android·systemui