まったりAndroid Framework Code Reading #3 に参加してきた

まったりAndroid Framework Code Reading というイベントに参加してきました。Androidソースコードをまったり読みましょう、という会です。仕事でNFCらへんのAPIを使ったのですが、裏側どうなってるのかなぁと思ってたので参加してみることにしました。

mandroidfcr.doorkeeper.jp

このイベントおかげでまったり読むことができて有意義な時間を過ごすことができました。そして、メルカリさんの会場は綺麗でビールも普段は出てこないようなおいしそうなものばかりでした(私は酒止めたのですが・・・)。

コードリーディング楽しかったのでこれからも参加したいなー。

コード読む

NFC絡みのコードを読む!

ブラウザだけでAndroidのコード読めました。便利。OpenGrokというものが使われているみたい。

https://sites.google.com/site/devcollaboration/codesearch

Full search で nfc と入力して検索。 /frameworks/base/core/java/android/nfc/ を開く。

android.nfc.NfcAdapter を読み始めます。適当に流しよみしてると getNfcAdapter というstaticメソッドが。このメソッドNfcAdapterインスタンス化してる。 getNfcAdapter を定義してる箇所をクリックすると getNfcAdapter で検索してくれた。OpenGrok便利。

getNfcAdapter を呼んでいるのは android.nfc.NfcManagerNfcManagerNfcAdapter を保持してるだけ。 NfcManagerコンストラクタを呼び出している箇所を探す。

android.app.SystemServiceRegistry のstatic初期化子で、 NfcManagerインスタンスを取得してます。

260         registerService(Context.NFC_SERVICE, NfcManager.class,
261                 new CachedServiceFetcher<NfcManager>() {
262             @Override
263             public NfcManager createService(ContextImpl ctx) {
264                 return new NfcManager(ctx);
265             }});

主催者の方に聞いたところ、 SystemServiceRegistryAndroid端末の起動時に各種システムサービスを登録してるらしい。

さて、ここまでで、Android端末起動時にシステムサービスが登録され NfcAdapterインスタンスが作られる、とこまでわかった。

次は、FeliCaカードを端末にタッチした時にIntentが発行されるところを読みたい。

いろいろ眺めてると、 com.android.nfc.NfcDispatcher に怪しいところが。

193         boolean tryStartActivity() {
194             // Ideally we'd have used startActivityForResult() to determine whether the
195             // NfcRootActivity was able to launch the intent, but startActivityForResult()
196             // is not available on Context. Instead, we query the PackageManager beforehand
197             // to determine if there is an Activity to handle this intent, and base the
198             // result of off that.
199             List<ResolveInfo> activities = packageManager.queryIntentActivitiesAsUser(intent, 0,
200                     ActivityManager.getCurrentUser());
201             if (activities.size() > 0) {
202                 context.startActivityAsUser(rootIntent, UserHandle.CURRENT);
203                 return true;
204             }
205             return false;
206         }

tryStartActivity を呼び出しているのは、多分 NfcDispatcherdispatchTag メソッドかな・・・。

220     /** Returns:
221      * <ul>
222      *  <li /> DISPATCH_SUCCESS if dispatched to an activity,
223      *  <li /> DISPATCH_FAIL if no activities were found to dispatch to,
224      *  <li /> DISPATCH_UNLOCK if the tag was used to unlock the device
225      * </ul>
226      */
227     public int dispatchTag(Tag tag) {

この dispatchTag メソッドを呼んでるのは、これかなー・・・。 com.android.nfc.NfcService

921         @Override
922         public void dispatch(Tag tag) throws RemoteException {
923             NfcPermissions.enforceAdminPermissions(mContext);
924             mNfcDispatcher.dispatchTag(tag);
925         }

もしくは、内部で定義されている NfcServiceHandler

だんだん分からなくなってきたけど、この NfcService を呼び出してるのはどこか、というところ全然分からなくなってきた。

NfcService のコードを読んでると、以下のような記述がある。

final class NfcAdapterService extends INfcAdapter.Stub {

INfcAdapter というは、 /frameworks/base/core/java/android/nfc にある、 INfcAdapter.aidl で定義されているインタフェース。実は最初からこの .aidl というのが気になっててちょっとだけ調べてた。

どうやら、プロセス間通信を行うコードを自動生成するための仕組みがAndroidにはあって、それをAIDLと言うみたい。

NfcAdapter の中の以下のコードが怪しいなぁというところで時間切れ。

471     /** get handle to NFC service interface */
472     private static INfcAdapter getServiceInterface() {
473         /* get a handle to NFC service */
474         IBinder b = ServiceManager.getService("nfc");
475         if (b == null) {
476             return null;
477         }
478         return INfcAdapter.Stub.asInterface(b);
479     }

AIDLをもっと調べてみよう。