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>
相关推荐
sun0077004 小时前
android ndk编译valgrind
android
AI视觉网奇5 小时前
android studio 断点无效
android·ide·android studio
jiaxi的天空5 小时前
android studio gradle 访问不了
android·ide·android studio
No Silver Bullet6 小时前
android组包时会把从maven私服获取的包下载到本地吗
android
catchadmin7 小时前
PHP serialize 序列化完全指南
android·开发语言·php
tangweiguo030519878 小时前
Kable使用指南:Android BLE开发的现代化解决方案
android·kotlin
00后程序员张10 小时前
iOS App 混淆与资源保护:iOS配置文件加密、ipa文件安全、代码与多媒体资源防护全流程指南
android·安全·ios·小程序·uni-app·cocoa·iphone
柳岸风12 小时前
Android Studio Meerkat | 2024.3.1 Gradle Tasks不展示
android·ide·android studio
编程乐学12 小时前
安卓原创--基于 Android 开发的菜单管理系统
android
whatever who cares14 小时前
android中ViewModel 和 onSaveInstanceState 的最佳使用方法
android