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>
相关推荐
无极程序员31 分钟前
PHP常量
android·ide·android studio
萌面小侠Plus2 小时前
Android笔记(三十三):封装设备性能级别判断工具——低端机还是高端机
android·性能优化·kotlin·工具类·低端机
慢慢成长的码农2 小时前
Android Profiler 内存分析
android
大风起兮云飞扬丶2 小时前
Android——多线程、线程通信、handler机制
android
L72562 小时前
Android的Handler
android
清风徐来辽2 小时前
Android HandlerThread 基础
android
HerayChen3 小时前
HbuildderX运行到手机或模拟器的Android App基座识别不到设备 mac
android·macos·智能手机
顾北川_野3 小时前
Android 手机设备的OEM-unlock解锁 和 adb push文件
android·java
hairenjing11233 小时前
在 Android 手机上从SD 卡恢复数据的 6 个有效应用程序
android·人工智能·windows·macos·智能手机
小黄人软件3 小时前
android浏览器源码 可输入地址或关键词搜索 android studio 2024 可开发可改地址
android·ide·android studio