2013-01-09 108 views
12

我想知道是否有可能從PendingIntent中獲得一些我還沒有創建的信息。更確切地說:是否有可能以某種方式檢索PendingIntent的原始Intent?我不需要執行它,但想打印它的內容。獲取有關PendingIntent的意圖的詳細信息

通過PendingIntent代碼看它顯示了一個隱藏的方法:

/** @hide */ 
public IIntentSender getTarget() { 
    return mTarget; 
} 

然而,這IIntentSender還隱藏有Binder多的IPC(我猜)相關的東西做的。不那麼容易。有任何想法嗎?

+1

我一直在尋找了一會兒,沒有發現任何東西。你有沒有找到解決方案如何打印'意圖'的內容? – tomrozb

+0

Commonsware在這裏回答了一個類似的問題http://stackoverflow.com/a/23725068/2319390 –

回答

-1

您可以使用IntentSender可以從PendingIntent.getIntentSender()獲得。 IntentSender的功能getCreatorPackage(),會給出創建這個PendingIntent的包。

+2

是的,我明白,但我需要最初的意圖。這不會給我的。 – Peterdk

+0

或者更重要的是,如何從IntentSender對象的Intent中獲取附加功能? – Michael

16

該方法將在Android 4.2.2及以上工作:

/** 
* 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); 
    } 
} 

下面是Android 2.3及以上未完全執行。它需要編寫一段本地代碼(JNI)。然後也許它會工作。有關更多詳細信息,請參閱TODO評論。

/** 
* 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; 
} 
+0

該方法用'@ hide'標記,這意味着它不能被正常調用。儘管如此,反思可能會奏效,除非它需要系統級許可或其他內容。 – matiash

+0

當然,我們需要在這裏進行思考,因爲沒有正常的方法來做到這一點。我不認爲需要一些額外的權限。無論如何,這很容易檢查。唯一明顯的問題是這個調用在Android 4.2.2以後可用,所以不能用於以前的版本。可能(但不太可能)它可能會在未來的版本中消失。 –

+0

我測試過這個方法,看起來不錯。對於通過Google+登錄創建的PendingIntent可以很好地工作。對於4.2.2以前的設備可以使用類似的解決方案,因爲這些代碼在生產環境中使用並不安全。我認爲公共方法應該捕獲所有異常,這意味着無法獲取'Intent'並返回'null'而不是崩潰應用程序。 – tomrozb

2

如果你想爲測試目的在Robolectric意圖,然後用ShadowPendingIntent

public static Intent getIntent(PendingIntent pendingIntent) { 
    return ((ShadowPendingIntent) ShadowExtractor.extract(pendingIntent)) 
     .getSavedIntent(); 
} 
相關問題