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>
相关推荐
ac-er88883 小时前
Yii框架中的队列:如何实现异步操作
android·开发语言·php
流氓也是种气质 _Cookie5 小时前
uniapp 在线更新应用
android·uniapp
zhangphil7 小时前
Android ValueAnimator ImageView animate() rotation,Kotlin
android·kotlin
徊忆羽菲8 小时前
CentOS7使用源码安装PHP8教程整理
android
编程、小哥哥9 小时前
python操作mysql
android·python
Couvrir洪荒猛兽9 小时前
Android实训十 数据存储和访问
android
五味香11 小时前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
十二测试录12 小时前
【自动化测试】—— Appium使用保姆教程
android·经验分享·测试工具·程序人生·adb·appium·自动化
Couvrir洪荒猛兽14 小时前
Android实训九 数据存储和访问
android
aloneboyooo14 小时前
Android Studio安装配置
android·ide·android studio