为啥 NotificationManagerCompat::cancelAll() 得到 SecurityException?

Posted

技术标签:

【中文标题】为啥 NotificationManagerCompat::cancelAll() 得到 SecurityException?【英文标题】:Why NotificationManagerCompat::cancelAll() gets SecurityException?为什么 NotificationManagerCompat::cancelAll() 得到 SecurityException? 【发布时间】:2016-08-06 15:37:29 【问题描述】:

使用NotificationManagerCompat 取消所有通知。

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

它有时会出现异常(大部分时间都有效)。

在安卓 6 上:

java.lang.SecurityException: Permission Denial: getCurrentUser() from pid=22994, uid=10184 需要 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 上:

ava.lang.SecurityException: Permission Denial: getIntentSender() from pid=5460, uid=10135, (need uid=1000) is not allowed to send as package android 在 android.os.Parcel.readException(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)

问题:

    可能是什么原因? 这里的 id 是什么?是ctx.getApplicationContext().getApplicationInfo().uid 还是android.os.Process.myUid()

【问题讨论】:

"是 ctx.getApplicationContext().getApplicationInfo().uid 还是 android.os.Process.myUid()?" -- 对于大多数 Android 应用程序,这些应该是相同的。不过,您的两个错误都很奇怪。 感谢 CommonsWare!很奇怪,它只是使用应用程序的上下文来删除所有通知(从同一个应用程序内部发布),但得到安全异常。可能的原因是什么? 所以我想我得试着抓住这个癌症。 @66CLSjY 我假设,取决于这些 piduid 是否属于您的应用程序,这可能是“活页夹同步”中的错误,例如,getIntentSender() 是使用uidBinder 身份调用,而它是使用系统的身份(uid=1000)调用的,或者在处理您的调用时,其他对象的Binders 使用原始调用者(您的)身份调用而不清除首先它。我想除了捕获问题调用引发的异常之外,您无能为力。 @Onik,这确实揭示了这个问题是如何产生的。谢谢。 【参考方案1】:

答案并没有为问题提供可靠的解决方案,而是试图为提供赏金的 OP 和 @66CLSjY 解释原因, similar issue。


检查堆栈跟踪

根据stacktraceSecurityException在远程进程中被抛出:你的app进程'Binder对象(例如INotificationManager.StubActivityManagerProxy等)makes a Binder transaction(mRemote.transact())*在远程@ 987654339@ 对象并从对象中读取远程调用中发生的异常 (_reply.readException())。如果有的话,你的进程中的exception message is analyzed and a corresponding exception is thrown。

分析异常消息

两个异常消息(一个带有getIntentSender(),另一个带有getCurrentUser())都非常简单——您的应用没有通过权限检查,或者换句话说,ActivityManagerService 的代码 sn-ps本来应该叫under the system_server process' identity (UID=1000) **,但实际上叫under your app process' identity。

可能的原因和解决方法

它有时会出现异常(大部分时间都有效)。

如果不做任何假设,你得到的“一段时间”是不恰当的Android 行为。 Wrapping the problem call with try/catch 似乎是一种解决方法,直到有人提出可靠的解决方案(如果存在)。


* ActivityManagerProxy.setRequestedOrientation() 和 IAccessibilityManager$Stub$Proxy.sendAccessibilityEvent() ** android.permission.INTERACT_ACROSS_USERS 属于 signature | system protection level

【讨论】:

【参考方案2】:

在我看来,这不起作用有两种不同的可能性:

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

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

【讨论】:

以上是关于为啥 NotificationManagerCompat::cancelAll() 得到 SecurityException?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 DataGridView 上的 DoubleBuffered 属性默认为 false,为啥它受到保护?

为啥需要softmax函数?为啥不简单归一化?

为啥 g++ 需要 libstdc++.a?为啥不是默认值?

为啥或为啥不在 C++ 中使用 memset? [关闭]

为啥临时变量需要更改数组元素以及为啥需要在最后取消设置?

为啥 CAP 定理中的 RDBMS 分区不能容忍,为啥它可用?