I have a NotificationListener service running in the background and it throws an exception when I execute 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)
Is this because the provided Context is a service because this doesn't happen when called from an activity.
This is a "bug", although as you said, this method is not expected to called from a NotificationListenerService
. You should call this from an ordinary Activty
or Service
. I will explan it later why.
According to your trace the following is the situation.
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;
}
Here it is trying to catch RemoteException
although in Parcel
(android.os.Parcel.readException(Parcel.java:1599)) this is happening:
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);
}
It is throwing a SecurityException
.
As SecurityException is NOT a RemoteException try-catch fails to catch the exception in the checkOpNoThrow
and you get the error.
What is more, if you try to use the application's context(e.g. this.getApplicationContext()
) instead of the listener service's context, it will also lead to an error. This is due to canWrite
's checking for the caller's UID. Notification listener has a different UID from the other parts of your application.
public static boolean canWrite(Context context) {
int uid = Binder.getCallingUid();
return isCallingPackageAllowedToWriteSettings(context, uid, getPackageNameForUid(
context, uid), false);
}
EDIT
This bug is tracked on AOSP issue tracker.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With