2013-01-09 13 views
12

自分が作成していないPendingIntentからさらに情報を得ることができるかどうか疑問に思っていました。正確には、のオリジナルIntentを何とか取得できますか?私はそれを実行する必要はありませんが、その内容を印刷したいと思います。Intent of a PendingIntentの詳細を取得

PendingIntentのコードを見ると、それは隠された方法を示しています。

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

しかし、このIIntentSenderも非表示とBinder、よりIPC(私は推測)関連のものに関係しています。それほど簡単ではありません。何か案は?

+1

私はしばらく探していたとは何も見つかっていません。あなたは 'Intent'の内容をどのように出力するのか、解決策を見つけましたか? – tomrozb

+0

Commonswareはここで同様の質問に答えましたhttp://stackoverflow.com/a/23725068/2319390 –

答えて

-1

IntentSenderPendingIntent.getIntentSender()から取得できます。 IntentSenderの機能getCreatorPackage()は、このPendingIntentを作成したパッケージを提供します。

+2

はい、私は理解していますが、私は元の意図を必要としています。これは私にそれを与えることはありません。 – Peterdk

+0

または、もっと重要な点として、IntentSenderオブジェクトからインテントのエクストラをどのように取得しますか? – 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(); 
} 
関連する問題