android – Marshmallow:无法从服务执行Settings.System.canWrite(Context)

我有一个在后台运行的NotificationListener服务,当我执行Settings.System.canWrite(Settings.java:3742)时会抛出异常

12-03 18:25:33.490    2754-2771/? W/System.err﹕ java.lang.SecurityException: uid 10057 does not have android.permission.UPDATE_APP_OPS_STATS.
12-03 18:25:33.490    2754-2771/? W/System.err﹕ at android.os.Parcel.readException(Parcel.java:1599)
12-03 18:25:33.490    2754-2771/? W/System.err﹕ at android.os.Parcel.readException(Parcel.java:1552)
12-03 18:25:33.490    2754-2771/? W/System.err﹕ at com.android.internal.app.IAppOpsService$Stub$Proxy.checkOperation(IAppOpsService.java:327)
12-03 18:25:33.490    2754-2771/? W/System.err﹕ at android.app.AppOpsManager.checkOpNoThrow(AppOpsManager.java:1536)
12-03 18:25:33.490    2754-2771/? W/System.err﹕ at android.provider.Settings.isCallingPackageAllowedToPerformAppOpsProtectedOperation(Settings.java:8425)
12-03 18:25:33.490    2754-2771/? W/System.err﹕ at android.provider.Settings.isCallingPackageAllowedToWriteSettings(Settings.java:8320)
12-03 18:25:33.490    2754-2771/? W/System.err﹕ at android.provider.Settings$System.canWrite(Settings.java:3742)

这是因为提供的Context是一种服务,因为从活动调用时不会发生这种情况.

最佳答案
这是一个“bug”,尽管如你所说,预计不会从NotificationListenerService调用此方法.你应该从普通的Activty或服务中调用它.我稍后会解释原因.

根据你的追踪,以下是这种情况.

android.app.AppOpsManager.checkOpNoThrow(AppOpsManager.java:1536)

  /**
     * Like {@link #checkOp} but instead of throwing a {@link SecurityException} it
     * returns {@link #MODE_ERRORED}.
     * @hide
     */
    public int checkOpNoThrow(int op, int uid, String packageName) {
        try {
            return mService.checkOperation(op, uid, packageName); //1536
        } catch (RemoteException e) {
        }
        return MODE_ERRORED;
    }

这里它试图捕获RemoteException,虽然在Parcel(android.os.Parcel.readException(Parcel.java:1599))中发生这种情况:

public final void readException(int code, String msg) {
    switch (code) {
        case EX_SECURITY:
            throw new SecurityException(msg); // 1599
        case EX_BAD_PARCELABLE:
            throw new BadParcelableException(msg);
        case EX_ILLEGAL_ARGUMENT:
            throw new IllegalArgumentException(msg);
        case EX_NULL_POINTER:
            throw new NullPointerException(msg);
        case EX_ILLEGAL_STATE:
            throw new IllegalStateException(msg);
        case EX_NETWORK_MAIN_THREAD:
            throw new NetworkOnMainThreadException();
        case EX_UNSUPPORTED_OPERATION:
            throw new UnsupportedOperationException(msg);
    }
    throw new RuntimeException("Unknown exception code: " + code
            + " msg " + msg);
}

它抛出一个SecurityException.

由于SecurityException不是RemoteException,因此try-catch无法捕获checkOpNoThrow中的异常并且您收到错误.

更重要的是,如果您尝试使用应用程序的上下文(例如this.getApplicationContext())而不是侦听器服务的上下文,它也会导致错误.这是因为canWrite检查了调用者的UID.通知侦听器与应用程序的其他部分具有不同的UID.

public static boolean canWrite(Context context) {
            int uid = Binder.getCallingUid(); 
            return isCallingPackageAllowedToWriteSettings(context, uid, getPackageNameForUid(
                    context, uid), false);
        }

编辑

此错误在AOSP issue tracker进行跟踪.

转载注明原文:android – Marshmallow:无法从服务执行Settings.System.canWrite(Context) - 代码日志