Marshmallow:无法从服务执行 Settings.System.canWrite(Context)
Marshmallow: Can't execute Settings.System.canWrite(Context) from a service
我在后台有一个 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 是一项服务,因为从 activity 调用时不会发生这种情况。
这是一个 "bug",尽管如您所说,预计不会从 NotificationListenerService
调用此方法。您应该从普通的 Activty
或 Service
调用它。我稍后会解释为什么。
根据你的踪迹,情况如下。
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;
}
虽然在 Parcel
(android.os.Parcel.readException(Parcel.java:1599)) 中,它正在尝试捕获 RemoteException
:
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 is NOT a 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 上进行跟踪。
我在后台有一个 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 是一项服务,因为从 activity 调用时不会发生这种情况。
这是一个 "bug",尽管如您所说,预计不会从 NotificationListenerService
调用此方法。您应该从普通的 Activty
或 Service
调用它。我稍后会解释为什么。
根据你的踪迹,情况如下。
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;
}
虽然在 Parcel
(android.os.Parcel.readException(Parcel.java:1599)) 中,它正在尝试捕获 RemoteException
:
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 is NOT a 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 上进行跟踪。