This method will work on Android 4.2.2 and above:
/**
* Return the Intent for PendingIntent.
* Return null in case of some (impossible) errors: see Android source.
* @throws IllegalStateException in case of something goes wrong.
* See {@link Throwable#getCause()} for more details.
*/
public Intent getIntent(PendingIntent pendingIntent) throws IllegalStateException {
try {
Method getIntent = PendingIntent.class.getDeclaredMethod("getIntent");
return (Intent) getIntent.invoke(pendingIntent);
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
Below is incomplete implementation for Android 2.3 and above. It requires to write an additional piece of native (JNI) code. Then maybe it will work. See TODO
comment for more details.
/**
* Return the Intent for PendingIntent.
* Return null in case of some (impossible) errors: see Android source.
* @throws IllegalStateException in case of something goes wrong.
* See {@link Throwable#getCause()} and {@link Throwable#getMessage()} for more details.
*/
public Intent getIntent(PendingIntent pendingIntent) throws IllegalStateException {
try {
Method getIntent = PendingIntent.class.getDeclaredMethod("getIntent");
return (Intent) getIntent.invoke(pendingIntent);
} catch (NoSuchMethodException e) {
return getIntentDeep(pendingIntent);
} catch (InvocationTargetException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
}
private Intent getIntentDeep(PendingIntent pendingIntent) throws IllegalStateException {
try {
Class<?> activityManagerNativeClass = Class.forName("android.app.ActivityManagerNative");
Method getDefault = activityManagerNativeClass.getDeclaredMethod("getDefault");
Object defaultManager = getDefault.invoke(null);
if (defaultManager == null) {
throw new IllegalStateException("ActivityManagerNative.getDefault() returned null");
}
Field mTargetField = PendingIntent.class.getDeclaredField("mTarget");
mTargetField.setAccessible(true);
Object mTarget = mTargetField.get(pendingIntent);
if (mTarget == null) {
throw new IllegalStateException("PendingIntent.mTarget field is null");
}
String defaultManagerClassName = defaultManager.getClass().getName();
switch (defaultManagerClassName) {
case "android.app.ActivityManagerProxy":
try {
return getIntentFromProxy(defaultManager, mTarget);
} catch (RemoteException e) {
// Note from PendingIntent.getIntent(): Should never happen.
return null;
}
case "com.android.server.am.ActivityManagerService":
return getIntentFromService(mTarget);
default:
throw new IllegalStateException("Unsupported IActivityManager inheritor: " + defaultManagerClassName);
}
} catch (ClassNotFoundException | NoSuchMethodException | IllegalAccessException | InvocationTargetException | NoSuchFieldException e) {
throw new IllegalStateException(e);
}
}
private Intent getIntentFromProxy(Object defaultManager, Object sender) throws RemoteException {
Class<?> activityManagerProxyClass;
IBinder mRemote;
int GET_INTENT_FOR_INTENT_SENDER_TRANSACTION = IBinder.FIRST_CALL_TRANSACTION + 160;
String iActivityManagerDescriptor = "android.app.IActivityManager";
try {
activityManagerProxyClass = Class.forName("android.app.ActivityManagerProxy");
Field mRemoteField = activityManagerProxyClass.getDeclaredField("mRemote");
mRemoteField.setAccessible(true);
mRemote = (IBinder) mRemoteField.get(defaultManager);
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
// From ActivityManagerProxy.getIntentForIntentSender()
Parcel data = Parcel.obtain();
Parcel reply = Parcel.obtain();
data.writeInterfaceToken(iActivityManagerDescriptor);
data.writeStrongBinder(((IInterface) sender).asBinder());
transact(mRemote, data, reply, 0);
reply.readException();
Intent res = reply.readInt() != 0
? Intent.CREATOR.createFromParcel(reply) : null;
data.recycle();
reply.recycle();
return res;
}
private boolean transact(IBinder remote, Parcel data, Parcel reply, int i) {
// TODO: Here must be some native call to convert ((BinderProxy) remote).mObject int
// to IBinder* native pointer and do some more magic with it.
// See android_util_Binder.cpp: android_os_BinderProxy_transact() in the Android sources.
}
private Intent getIntentFromService(Object sender) {
String pendingIntentRecordClassName = "com.android.server.am.PendingIntentRecord";
if (!(sender.getClass().getName().equals(pendingIntentRecordClassName))) {
return null;
}
try {
Class<?> pendingIntentRecordClass = Class.forName(pendingIntentRecordClassName);
Field keyField = pendingIntentRecordClass.getDeclaredField("key");
Object key = keyField.get(sender);
Class<?> keyClass = Class.forName("com.android.server.am.PendingIntentRecord$Key");
Field requestIntentField = keyClass.getDeclaredField("requestIntent");
requestIntentField.setAccessible(true);
Intent requestIntent = (Intent) requestIntentField.get(key);
return requestIntent != null ? new Intent(requestIntent) : null;
} catch (ClassCastException e) {
} catch (ClassNotFoundException | NoSuchFieldException | IllegalAccessException e) {
throw new IllegalStateException(e);
}
return null;
}