为什么NotificationManagerCompat :: cancelAll()会出现SecurityException?

28

使用NotificationManagerCompat取消所有通知。

NotificationManagerCompat manager =  
    NotificationManagerCompat.from(ctx.getApplicationContext());
manager.cancelAll();

有时会出现异常(大部分情况下能够正常工作)。

在Android 6上:

java.lang.SecurityException:权限拒绝:来自pid=22994,uid=10184的getCurrentUser()需要android.permission.INTERACT_ACROSS_USERS

Fatal Exception: java.lang.SecurityException: Permission Denial: getCurrentUser() from pid=22994, uid=10184 requires android.permission.INTERACT_ACROSS_USERS
   at android.os.Parcel.readException(Parcel.java:1602)
   at android.os.Parcel.readException(Parcel.java:1555)
   at android.app.INotificationManager$Stub$Proxy.cancelAllNotifications(INotificationManager.java:649)
   at android.app.NotificationManager.cancelAll(NotificationManager.java:323)
   at android.support.v4.app.NotificationManagerCompat.cancelAll(NotificationManagerCompat.java:197)

在Android 5.0、4.4.2版本中出现以下错误:

java.lang.SecurityException:拒绝授权的权限:getIntentSender() from pid=5460, uid=10135,(need uid=1000) 不允许作为android包发送 在android.os.Parcel.java:1465处读取异常

Fatal Exception: java.lang.SecurityException: Permission Denial: getIntentSender() from pid=3109, uid=10153, (need uid=1000) is not allowed to send as package android
   at android.os.Parcel.readException(Parcel.java:1472)
   at android.os.Parcel.readException(Parcel.java:1426)
   at android.app.INotificationManager$Stub$Proxy.cancelAllNotifications(INotificationManager.java:271)
   at android.app.NotificationManager.cancelAll(NotificationManager.java:220)
   at android.support.v4.app.NotificationManagerCompat.cancelAll(NotificationManagerCompat.java:197)

问题:

  1. 可能的原因是什么?
  2. 这些 ID 是什么?它是 ctx.getApplicationContext().getApplicationInfo().uid 还是 android.os.Process.myUid()

1
"是 ctx.getApplicationContext().getApplicationInfo().uid 还是 android.os.Process.myUid()?" -- 对于大多数 Android 应用程序来说,它们应该是相同的。不过,你的两个错误都相当奇怪。 - CommonsWare
1
谢谢CommonsWare!很奇怪,它只是使用应用程序的上下文来删除所有通知(从同一应用程序内部发布的通知),但会出现安全异常。可能的原因是什么? - lannyf
2
所以我猜得要用 try-catch 来处理这个问题。 - Rybzor
1
@66CLSjY 我认为,这取决于这些 piduid 是否属于您的应用程序,这可能是“Binder同步”中的一个错误,例如当使用 uidBinder 标识符调用 getIntentSender() 时,它应该使用系统的标识符 (uid=1000),或者在处理您的调用时,其他对象的Binders被调用时,首先没有清除原始调用者(您)的标识。我想你不能做太多事情,只能捕获问题调用抛出的异常。 - Onik
1
@Onik,这确实让人们对问题产生了一些了解。谢谢。 - AjOnFire
显示剩余12条评论
2个回答

6
这个答案并没有为问题提供一个确切的解决方案,而是试图给出原因的解释,既是针对问题发起者,也是针对 @66CLSjY 提供悬赏的人,并且还涉及到一个类似的问题(similar issue)。

检查堆栈跟踪信息

根据堆栈跟踪信息,在远程进程中抛出了SecurityException:您的应用程序进程的Binder对象(例如INotificationManager.StubActivityManagerProxy等)执行了一个Binder事务mRemote.transact())* 在远程Binder对象上并从该对象中读取了一个异常(_reply.readException()),这在远程调用中发生。如果有任何异常,则分析异常消息并在您的进程中抛出相应的异常

分析异常消息

两个异常消息(一个包含getIntentSender(),另一个包含getCurrentUser())都很简单 - 你的应用程序没有通过权限检查,换句话说,ActivityManagerService的代码片段本应该在system_server进程的标识UID=1000)下被调用 **,但实际上是在你的应用程序进程的标识下被调用。

可能的原因和解决方法

有时会出现异常(大部分时间正常)。

不做任何假设,你遇到的 "有时" 是不合适的 Android 行为。 将问题调用包装在 try/catch 看起来是一个解决方法,直到有人提出一个可靠的解决方案(如果存在的话)。


* ActivityManagerProxy.setRequestedOrientation()IAccessibilityManager$Stub$Proxy.sendAccessibilityEvent()相关。
** android.permission.INTERACT_ACROSS_USERS属于签名 | 系统保护级别


0

对我来说,这似乎有两种不同的可能性导致它无法工作:

最有可能的原因是您正在使用错误的上下文进行调用;getApplicationContext() 不是 100% 可靠,有时会产生奇怪的错误,最好避免使用此调用。如果您从 Service 或 Activity 调用 cancelAll(),请改用 YourClass.this 而不是 getApplicationContext(),如果是从 BroadcastReceiver 调用,则使用提供的 Context 变量。

如果仍然无法正常工作,则可能是 NotificationManagerCompat 中的错误,请尝试使用 NotificationManager 复现相同的问题。解决方法是将所有通知 ID 保存在列表中,然后使用 manager.cancel(id) 取消每个通知。这样系统就不会尝试取消任何不属于您应用程序的通知。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接