background

Today in NotificationManagerService to call PendingIntent. GetIntent () found the following error, the pid = 4901, uid = 10097, respectively, to push platform application process id and id, The pendingIntent.getintent () API is @hide, so it can’t be called by ordinary applications, and it can’t be called by reflection, so the solution given below is for developers with system permissions.

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:55 69) W System.err: at com.android.server.notification.NotificationManagerService.enqueueNotificationInternal(NotificationManagerService.java:5 193) 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)
Copy the code

Analysis of the

Reading the newspaper is wrong call stack can be found in the AMS do permission check (ActivityManagerService. EnforceCallingPermission ()) throw exceptions when:

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());
    }
Copy the code

Can see throw exceptions because checkCallingPermission function returns false, the caller is a push platform don’t have permission to adjust the interface, that is why we clearly in NotificationManagerService (this is a system service, We need to understand two concepts related to the binders here.

Binder. GetCallingPid () and the Binder. GetCallingUid ()

Each thread has a unique IPCThreadState object that records the current thread’s PID and UID, which are obtained by Binder. GetCallingPid () and Binder. GetCallingUid ().

When thread A calls thread B’s interface with Binder, the UID and PID stored in thread B’s IPCThreadState are the UID and PID of thread A. Usually, we call these two interfaces to obtain the ID of thread A for permission comparison.

While in thread B, B is the caller of the current process, if the thread B by Binder to invoke other methods, and other methods through these two methods to obtain the corresponding uid and pid permissions to do comparison, access to the value or the thread A, so we need before B by Binder to invoke other methods, The Binder refreshes the UID and PID stored in thread B. Two more interfaces are provided for this Binder:

Binder. ClearCallingIdentity () and the Binder. RestoreCallingIdentity (id)

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

The Binder. ClearCallingIdentity () will help us to clear the current thread id of the state, and return the results to us; And when we after executing the Binder operation, we need to recover the thread id value of B, where you need to call Binder. The restoreCallingIdentity (id) to perform this operation.

With these two concepts in mind, let’s look at the original problem. We said that this interface is @hide, and only applications with system permissions can call it. So the question becomes, how do we call this interface as a system? Here we need to use a few of the above interface, directly look at the code:

                        final long token = Binder.clearCallingIdentity();
                        Intent intent;
                        try {
                            intent = pendingIntent.getIntent();
                        } finally {
                            Binder.restoreCallingIdentity(token);
                        }
Copy the code

Code is very simple, that is, before the call with Binder. The clearCallingIdentity () will rid of thread state, at the end of the call using Binder. The restoreCallingIdentity (token) of restoring the thread state, Thus achieved the original call from a push platform into NotificationManagerService calls, and NotificationManagerService is system services, you can call to smoothly.

conclusion

This problem is caused by Android’s new permissions (see the submit). After that, normal applications will not be able to call the interface. In order to call pendingIntent.getintent (), you must have a system signature. Or, as in the example above, use the Binder’s interfaces to subtly upgrade the permissions of previously underprivileged applications to system permissions.

The main purpose of this article is to share the meaning and usage scenarios of several Binder related functions That have appeared above