Android7.0以上調PendingIntent.getIntent()報錯

背景

今天在NotificationManagerService中去調用PendingIntent.getIntent()時發現了下面這個報錯,其中pid=4901, uid=10097分別是來push平臺的進程id和應用id,而PendingIntent.getIntent()這個API是@hide的,因此普通應用是沒法調用的,反射也調用不到,因此後面給出的解法是針對具備系統權限的開發者的。java

W System.err: java.lang.SecurityException: Permission Denial: getIntentForIntentSender() from pid=4901, uid=10097 requires android.permission.GET_INTENT_SENDER_INTENT
W System.err: 	at com.android.server.am.ActivityManagerService.enforceCallingPermission(ActivityManagerService.java:6291)
W System.err: 	at com.android.server.am.ActivityManagerService.getIntentForIntentSender(ActivityManagerService.java:5855)
W System.err: 	at android.app.PendingIntent.getIntent(PendingIntent.java:1135)
W System.err: 	at com.android.server.notification.xxx.callReplyIntent(xxx.java:64)
W System.err: 	at com.android.server.notification.NotificationManagerService.checkDisqualifyingFeatures(NotificationManagerService.java:5569)
W System.err: 	at com.android.server.notification.NotificationManagerService.enqueueNotificationInternal(NotificationManagerService.java:5193)
W System.err: 	at com.android.server.notification.NotificationManagerService$10.enqueueNotificationWithTag(NotificationManagerService.java:2609)
W System.err: 	at android.app.INotificationManager$Stub.onTransact(INotificationManager.java:1149)
W System.err: 	at android.os.Binder.execTransactInternal(Binder.java:1027)
W System.err: 	at android.os.Binder.execTransact(Binder.java:1000)
複製代碼

分析

看報錯的調用棧能發現是在AMS中作權限檢查(ActivityManagerService.enforceCallingPermission())的時候拋的異常:android

// 拋異常的代碼
    void enforceCallingPermission(String permission, String func) {
        if (checkCallingPermission(permission)
                == PackageManager.PERMISSION_GRANTED) {
            return;
        }

        String msg = "Permission Denial: " + func + " from pid="
                + Binder.getCallingPid()
                + ", uid=" + Binder.getCallingUid()
                + " requires " + permission;
        Slog.w(TAG, msg);
        throw new SecurityException(msg);
    }

// checkCallingPermission(),往下走就是去檢查該用戶是否具備android.permission.GET_INTENT_SENDER_INTENT 這個權限了
    int checkCallingPermission(String permission) {
        return checkPermission(permission,
                Binder.getCallingPid(),
                Binder.getCallingUid());
    }
複製代碼

能夠看到拋異常是由於checkCallingPermission函數返回false了,也就是調用方push平臺沒有權限去調這個接口,那爲何咱們明明是在NotificationManagerService(這是一個系統服務,管理通知的發送和刪除等事務)中去調用這個接口的,這裏卻判斷出來是來自push平臺的呢,這裏咱們須要瞭解兩個Binder相關的概念。git

Binder.getCallingPid() 與 Binder.getCallingUid()

每一個線程都有一個惟一的IPCThreadState對象記錄着當前線程的pid和uid,而這兩個id的獲取就是經過Binder.getCallingPid() 與 Binder.getCallingUid()這兩個方法來獲取的。bash

當線程A經過Binder調用線程B的接口時,B線程的IPCThreadState中保存的uid和pid是線程A的uid和pid,通常咱們調用這兩個接口來獲取線程A的id值來作權限對比app

而此時在線程B中,B是當前進程中的調用方了,若此時線程B經過Binder去調用其餘方法,而其餘方法又經過這兩個方法去獲取對應的uid和pid去作權限對比時,獲取到的值就仍是線程A的,因此咱們須要在B經過Binder去調用其餘方法前,將線程B中保存的uid和pid作一遍刷新,對於這個Binder提供了另外兩個接口:ide

Binder.clearCallingIdentity() 與 Binder.restoreCallingIdentity(id)

  • long id = Binder.clearCallingIdentity()
  • Binder.restoreCallingIdentity(id)

其中Binder.clearCallingIdentity()會幫咱們去清除當前線程中的id狀態,並將結果返回給咱們;而當咱們執行完Binder操做後,咱們須要恢復線程B中的id值,此時就須要調用Binder.restoreCallingIdentity(id)去執行這個操做。函數

瞭解完這兩個概念,咱們再來看看原來的問題,前面咱們說過,這個接口是@hide的,只有具備系統權限的應用才能調用,那麼問題就轉化爲,咱們在這裏怎麼以系統的身份去調這個接口呢?這裏就須要用到咱們上面說的幾個接口了,直接看代碼吧:gitlab

final long token = Binder.clearCallingIdentity();
                        Intent intent;
                        try {
                            intent = pendingIntent.getIntent();
                        } finally {
                            Binder.restoreCallingIdentity(token);
                        }
複製代碼

代碼很簡單,也就是在調用前利用Binder.clearCallingIdentity()將線程狀態清除掉,在調用結束時利用Binder.restoreCallingIdentity(token)將線程狀態恢復,這樣就實現了將原來來自push平臺的調用轉化爲NotificationManagerService的調用,而NotificationManagerService是系統服務,就能夠順利調用到了。ui

總結

其實這個問題是Android在7.0以上的系統中新增了相關的權限判斷後纔出現的(感興趣的能夠看下這筆提交),在這以後普通應用就沒法調用該接口了,想要調用PendingIntent.getIntent()就必須擁有系統簽名,或者像上面這個例子同樣,經過Binder提供的接口,巧妙的將原來權限不足的應用的權限升級爲系統權限。spa

本文主要想分享下上文出現的幾個Binder相關函數的意義和使用場景,That'all

相關文章
相關標籤/搜索