NFC の基本

このドキュメントでは、Android で行う基本的な NFC タスクについて説明します。NDEF メッセージの形式で NFC データを送受信する方法と、これらの機能をサポートする Android フレームワーク API について説明します。NDEF 以外のデータの処理方法など、詳細なトピックについては、高度な NFC をご覧ください。

NDEF データと Android を使用する場合、主に次の 2 つの使用例があります。

  • NDEF データを NFC タグから読み取る
  • Android ビーム™ を使用してデバイス間で NDEF メッセージをビームする

NFC タグからの NDEF データの読み取りは、タグ ディスパッチ システムで処理されます。このシステムは、検出された NFC タグを分析し、データを適切に分類して、分類されたデータに関連したアプリケーションを起動します。スキャンされた NFC タグを処理するアプリケーションでインテント フィルタを宣言して、データの処理をリクエストできます。

Android ビーム™ 機能により、デバイスを同時にタップして、一方のデバイスからもう一方へ NDEF メッセージをプッシュできます。NFC では手動のデバイス検出やペア設定が不要なため、こうしたインタラクションにより Bluetooth などの他の無線技術よりも簡単にデータを送信できます。2 つのデバイスが範囲内に入ると、接続が自動的に開始されます。Android ビームは NFC API セットを通じて利用できるため、どのアプリケーションでもデバイス間で情報を転送できます。たとえば、連絡先、ブラウザ、YouTube アプリでは Android ビームを使用して、連絡先、ウェブページ、動画を他のデバイスと共有できます。

タグ ディスパッチ システム

Android デバイスは通常、デバイスの設定メニューで NFC が無効になっていない限り、画面のロックが解除されたときに NFC タグを探します。Android デバイスが NFC タグを検出した場合、ユーザーに使用するアプリケーションを確認せずに、最も適切なアクティビティにインテントを処理させることが、期待される動作です。デバイスは NFC タグを至近距離でスキャンするため、ユーザーが手動でアクティビティを選択すると、デバイスがタグから離れて接続が切断される可能性があります。選択ダイアログが表示されないようにするには、アクティビティで重要な NFC タグのみを処理するようにアクティビティを開発する必要があります。

このために、Android には特別なタグ ディスパッチ システムが用意されており、スキャンされた NFC タグを分析、パースし、スキャンされたデータに関連のあるアプリケーションを見つけようとします。これは次の方法で行います。

  1. NFC タグをパースし、タグにあるデータ ペイロードを識別する MIME タイプや URI を見つけ出します。
  2. MIME タイプまたは URI、およびペイロードをインテントにカプセル化します。これらの最初 2 つの手順は、NFC タグが MIME タイプと URI にマッピングされる仕組みで説明します。
  3. インテントに基づいてアクティビティを開始します。これについては、NFC タグがアプリケーションにディスパッチされる仕組みをご覧ください。

NFC タグが MIME タイプと URI にマッピングされる仕組み

NFC アプリケーションの作成を開始する前に、NFC タグの種類、タグ ディスパッチ システムによる NFC タグのパース方法、タグ ディスパッチ システムが NDEF メッセージを検出したときの特別な処理について理解しておく必要があります。NFC タグは幅広いテクノロジーに対応しており、さまざまな方法でデータを書き込むことができます。Android は NFC フォーラムで定義されている NDEF 標準規格を最大限にサポートしています。

NDEF データは、1 つまたは複数のレコード(NdefRecord)を含むメッセージ(NdefMessage)内にカプセル化されます。各 NDEF レコードは、作成するレコードタイプの仕様に従って適切な形式にする必要があります。Android では、NDEF データを含まない他の種類のタグもサポートされています。これらのタグは、android.nfc.tech パッケージに含まれるクラスを使用して処理できます。これらの技術の詳細については、高度な NFC のトピックをご覧ください。このような他の種類のタグを処理する場合は、独自のプロトコル スタックを作成してタグと通信する必要があります。そのため、可能な限り NDEF を使用して、開発を容易にし、Android デバイスを最大限にサポートすることをおすすめします。

注: 完全な NDEF 仕様をダウンロードするには、NFC フォーラムの仕様とアプリケーション ドキュメントのサイトをご覧ください。NDEF レコードの作成例については、一般的な NDEF レコードタイプの作成をご覧ください。

NFC タグの技術的背景について説明したので、次のセクションでは、Android が NDEF 形式のタグを処理する方法について詳しく説明します。Android デバイスが NDEF 形式のデータを含む NFC タグをスキャンすると、メッセージをパースし、データの MIME タイプを見つけ出すか、URI を識別しようとします。これを行うために、システムは NdefMessage 内の最初の NdefRecord を読み取り、NDEF メッセージ全体(NDEF メッセージには複数の NDEF レコードが含まれる場合があります)の解釈方法を決定します。適切な NDEF 形式メッセージには、最初の NdefRecord に次のフィールドが含まれます。

3 ビットの TNF(Type Name Format)
可変長タイプのフィールドの解釈方法を示します。有効な値については、表 1 をご覧ください。
可変長タイプ
レコードのタイプを記述します。TNF_WELL_KNOWN を使用している場合、このフィールドを使用してレコードタイプ定義(RTD)を指定します。有効な RTD 値は表 2 に記載されています。
可変長 ID
当該レコードの一意の識別子。このフィールドは頻繁に使用されることはありませんが、タグを一意に識別する必要がある場合、タグの ID を作成できます。
可変長ペイロード
読み書きする実際のデータ ペイロード。NDEF メッセージに複数の NDEF レコードを含めることができるため、NDEF メッセージの最初の NDEF レコードに完全なペイロードがあるとは限りません。

タグ ディスパッチ システムは TNF とタイプ フィールドを使用して、MIME タイプまたは URI を NDEF メッセージにマッピングしようと試みます。成功した場合は、その情報を実際のペイロードとともに ACTION_NDEF_DISCOVERED インテント内にカプセル化します。ただし、タグ ディスパッチ システムが、最初の NDEF レコードに基づいてデータの種類を判別できない場合もあります。これは、NDEF データを MIME タイプや URI にマッピングできない場合や、初めから NFC タグに NDEF データが含まれていない場合に発生します。このような場合、タグのテクノロジーとペイロードに関する情報が含まれる Tag オブジェクトが代わりに ACTION_TECH_DISCOVERED インテント内にカプセル化されます。

表 1 は、タグ ディスパッチ システムが TNF とタイプ フィールドを MIME タイプまたは URI にマッピングする方法を示します。MIME タイプまたは URI にマッピングできない TNF についても記載されています。この場合、タグ ディスパッチ システムは ACTION_TECH_DISCOVERED にフォールバックします。

たとえば、タグ ディスパッチ システムが TNF_ABSOLUTE_URI タイプのレコードを検出した場合、そのレコードの可変長タイプ フィールドを URI にマッピングします。タグ ディスパッチ システムは、その URI をペイロードなどのタグに関する他の情報とともに、ACTION_NDEF_DISCOVERED インテントのデータ フィールドにカプセル化します。一方、TNF_UNKNOWN タイプのレコードが見つかった場合は、代わりにタグのテクノロジーをカプセル化するインテントを作成します。

表 1. サポートされている TNF とそのマッピング

Type Name Format(TNF) マッピング
TNF_ABSOLUTE_URI タイプ フィールドに基づく URI。
TNF_EMPTY ACTION_TECH_DISCOVERED にフォールバックします。
TNF_EXTERNAL_TYPE タイプ フィールドの URN に基づく URI。URN は NDEF タイプ フィールドに短縮形式(<domain_name>:<service_name>)でエンコードされます。Android はこれを vnd.android.nfc://ext/<domain_name>:<service_name> の形式の URI にマッピングします。
TNF_MIME_MEDIA タイプ フィールドに基づく MIME タイプ。
TNF_UNCHANGED 最初のレコードが無効なため、ACTION_TECH_DISCOVERED にフォールバックします。
TNF_UNKNOWN ACTION_TECH_DISCOVERED にフォールバックします。
TNF_WELL_KNOWN レコードタイプ定義(RTD)に応じた MIME タイプまたは URI。RTD はタイプ フィールドに設定されています。利用可能な RTD とそのマッピングの詳細については、表 2 をご覧ください。

表 2. TNF_WELL_KNOWN でサポートされている RTD とそのマッピング

レコードタイプ定義(RTD) マッピング
RTD_ALTERNATIVE_CARRIER ACTION_TECH_DISCOVERED にフォールバックします。
RTD_HANDOVER_CARRIER ACTION_TECH_DISCOVERED にフォールバックします。
RTD_HANDOVER_REQUEST ACTION_TECH_DISCOVERED にフォールバックします。
RTD_HANDOVER_SELECT ACTION_TECH_DISCOVERED にフォールバックします。
RTD_SMART_POSTER ペイロードのパースに基づく URI。
RTD_TEXT text/plain の MIME タイプ。
RTD_URI ペイロードに基づく URI。

NFC タグがアプリケーションにディスパッチされる仕組み

タグ ディスパッチ システムが、NFC タグとその識別情報をカプセル化するインテントの作成を完了すると、インテントをフィルタする関連アプリケーションにインテントを送信します。複数のアプリケーションがインテントを処理できる場合は、選択ダイアログが表示されるため、ユーザーがアクティビティを選択できます。タグ ディスパッチ システムは、3 つのインテントを定義します。このインテントは、優先度の高い順にリストされます。

  1. ACTION_NDEF_DISCOVERED: NDEF ペイロードを含むタグがスキャンされ、タイプが認識済みのものである場合は、このインテントを使用してアクティビティを開始します。これは最も優先度の高いインテントであり、タグ ディスパッチ システムは、可能な限り他のインテントよりも先にこのインテントでアクティビティを開始しようとします。
  2. ACTION_TECH_DISCOVERED: ACTION_NDEF_DISCOVERED インテントを処理するアクティビティが登録されていない場合、タグ ディスパッチ システムはこのインテントでアプリケーションを起動しようとします。スキャンされるタグに MIME タイプまたは URI にマッピングできない NDEF データが含まれている場合、または NDEF データが含まれていなくても既知のテクノロジーのタグである場合、このインテントは(最初に ACTION_NDEF_DISCOVERED を開始せずに)直接開始されます。
  3. ACTION_TAG_DISCOVERED: このインテントは、ACTION_NDEF_DISCOVERED または ACTION_TECH_DISCOVERED インテントを処理するアクティビティがない場合に開始されます。

タグ ディスパッチ システムの基本的な仕組みは次のとおりです。

  1. NFC タグのパース時にタグ ディスパッチ システムによって作成されたインテント(ACTION_NDEF_DISCOVERED または ACTION_TECH_DISCOVERED)でアクティビティの開始を試みます。
  2. そのインテントにアクティビティ フィルタがない場合は、次に優先度の低いインテント(ACTION_TECH_DISCOVERED または ACTION_TAG_DISCOVERED)でアクティビティを開始しようとします。これは、アプリケーションがインテントをフィルタするまで、またはタグ ディスパッチ システムが可能なすべてのインテントを試みるまで行います。
  3. アプリケーションがどのインテントもフィルタしない場合は、何もしません。
図 1. タグ ディスパッチ システム

可能な限り、NDEF メッセージと ACTION_NDEF_DISCOVERED インテントを処理します。これは、3 つの中で最も具体的であるためです。このインテントを使用すると、他の 2 つのインテントよりも適切なタイミングでアプリケーションを起動できるため、ユーザーのエクスペリエンスが向上します。

Android マニフェストで NFC アクセスをリクエストする

デバイスの NFC ハードウェアにアクセスし、NFC インテントを適切に処理する前に、AndroidManifest.xml ファイルで次の項目を宣言します。

  • NFC ハードウェアにアクセスするための NFC <uses-permission> 要素:
        <uses-permission android:name="android.permission.NFC" />
        
  • アプリケーションでサポート可能な SDK の最小バージョン。API レベル 9 では、ACTION_TAG_DISCOVERED による限定的なタグ ディスパッチのみをサポートし、EXTRA_NDEF_MESSAGES による NDEF メッセージへのアクセスのみを提供します。他のタグプロパティや I/O オペレーションにアクセスすることはできません。API レベル 10 には、リーダー / ライターの包括的サポートとフォアグラウンド NDEF プッシュ機能があります。API レベル 14 には、Android ビームで NDEF メッセージを他のデバイスにプッシュする簡単な方法と、NDEF レコードを作成するための便利な追加メソッドがあります。
        <uses-sdk android:minSdkVersion="10"/>
        
  • uses-feature 要素を使用すると、NFC ハードウェアを搭載したデバイスでのみアプリケーションが Google Play に表示されます。
        <uses-feature android:name="android.hardware.nfc" android:required="true" />
        

    NFC 機能を使用していても、その機能がアプリケーションにとって重要ではない場合は、uses-feature 要素を省略できます。getDefaultAdapter()null であるかどうかを確認することで、NFC の有効性を実行時にチェックできます。

NFC インテントのフィルタ

処理したい NFC タグがスキャンされたときにアプリケーションを起動するためには、1 つ、2 つ、または 3 つすべての NFC インテントを Android マニフェスト内でフィルタできます。ただし、通常は、アプリケーションの起動タイミングを最大限に制御するために、ACTION_NDEF_DISCOVERED インテントをフィルタする必要があります。ACTION_TECH_DISCOVERED インテントは、ACTION_NDEF_DISCOVERED をフィルタしているアプリケーションがない場合、またはペイロードが NDEF でない場合の ACTION_NDEF_DISCOVERED のフォールバックです。通常、ACTION_TAG_DISCOVERED のフィルタは、カテゴリのフィルタとしては一般的すぎます。多くのアプリケーションは、ACTION_TAG_DISCOVERED の前に ACTION_NDEF_DISCOVERED または ACTION_TECH_DISCOVERED をフィルタするため、アプリケーションが起動する可能性は低くなります。ACTION_TAG_DISCOVERED は、ACTION_NDEF_DISCOVERED または ACTION_TECH_DISCOVERED インテントを処理する他のアプリケーションがインストールされていない場合の最後の手段としてのみ利用できます。

NFC タグのデプロイメントはさまざまであり、多くの場合制御できず、これが常に可能であるとは限らないため、必要に応じて他の 2 つのインテントにフォールバックできます。タグのタイプと書き込みデータを制御できる場合は、NDEF を使用してタグをフォーマットすることをおすすめします。次のセクションでは、インテントのタイプごとにフィルタを適用する方法について説明します。

ACTION_NDEF_DISCOVERED

ACTION_NDEF_DISCOVERED インテントをフィルタするには、フィルタ対象のデータタイプとともにインテント フィルタを宣言します。MIME タイプが text/plainACTION_NDEF_DISCOVERED インテントをフィルタする例を以下に示します。

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain" />
    </intent-filter>
    

次の例では、URI を https://developer.android.com/index.html の形式でフィルタしています。

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
       <data android:scheme="http"
                  android:host="developer.android.com"
                  android:pathPrefix="/index.html" />
    </intent-filter>
    

ACTION_TECH_DISCOVERED

アクティビティで ACTION_TECH_DISCOVERED インテントをフィルタする場合、XML リソース ファイル(tech-list セット内でアクティビティがサポートするテクノロジーを指定します)を作成する必要があります。tech-list セットがタグでサポートされているテクノロジー(getTechList() の呼び出しで取得可能)のサブセットである場合、アクティビティは一致すると見なされます。

たとえば、スキャンされるタグが MifareClassic、NdefFormatable、NfcA をサポートしている場合、アクティビティを一致させるには、1 つ、2 つ、または 3 つすべてのテクノロジー(他のものは含めない)を tech-list セットで指定する必要があります。

次の例では、テクノロジーをすべて定義しています。必要のないものは削除できます。このファイル(名前は自由に指定できます)を <project-root>/res/xml フォルダに保存します。

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
        <tech-list>
            <tech>android.nfc.tech.IsoDep</tech>
            <tech>android.nfc.tech.NfcA</tech>
            <tech>android.nfc.tech.NfcB</tech>
            <tech>android.nfc.tech.NfcF</tech>
            <tech>android.nfc.tech.NfcV</tech>
            <tech>android.nfc.tech.Ndef</tech>
            <tech>android.nfc.tech.NdefFormatable</tech>
            <tech>android.nfc.tech.MifareClassic</tech>
            <tech>android.nfc.tech.MifareUltralight</tech>
        </tech-list>
    </resources>
    

また、複数の tech-list セットを指定できます。各 tech-list セットは個別に扱われ、getTechList() で返されるテクノロジーのサブセットに相当する tech-list セットが 1 つでも存在する場合、アクティビティは一致すると見なされます。これにより、テクノロジーの照合に ANDOR のセマンティクスが適用されます。次の例は、NfcA と Ndef テクノロジー、または NfcB と Ndef テクノロジーをサポートできるタグに一致します。

    <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
        <tech-list>
            <tech>android.nfc.tech.NfcA</tech>
            <tech>android.nfc.tech.Ndef</tech>
        </tech-list>
        <tech-list>
            <tech>android.nfc.tech.NfcB</tech>
            <tech>android.nfc.tech.Ndef</tech>
        </tech-list>
    </resources>
    

次の例のように、AndroidManifest.xml ファイルで、<activity> 要素内の <meta-data> 要素に、作成したばかりのリソース ファイルを指定します。

    <activity>
    ...
    <intent-filter>
        <action android:name="android.nfc.action.TECH_DISCOVERED"/>
    </intent-filter>

    <meta-data android:name="android.nfc.action.TECH_DISCOVERED"
        android:resource="@xml/nfc_tech_filter" />
    ...
    </activity>
    

タグ テクノロジーの処理方法と ACTION_TECH_DISCOVERED インテントの詳細については、高度な NFC に関するドキュメントのサポートされているタグ テクノロジーの処理をご覧ください。

ACTION_TAG_DISCOVERED

ACTION_TAG_DISCOVERED をフィルタするには、次のインテント フィルタを使用します。

<intent-filter>
        <action android:name="android.nfc.action.TAG_DISCOVERED"/>
    </intent-filter>
    

インテントから情報を取得する

NFC インテントに起因してアクティビティが開始された場合、スキャンされた NFC タグに関する情報をインテントから取得できます。インテントには、スキャンされたタグに応じて次の EXTRA 項目が含まれます。

  • EXTRA_TAG(必須): スキャンされたタグを表す Tag オブジェクト。
  • EXTRA_NDEF_MESSAGES(任意): タグからパースされた NDEF メッセージの配列。この EXTRA は ACTION_NDEF_DISCOVERED インテントに必須です。
  • EXTRA_ID(任意): タグの低レベル ID。

これらの EXTRA を取得するには、アクティビティが NFC インテントの 1 つで起動されたかどうかを確認し、タグがスキャンされたことを確認してから、インテントから EXTRA を取得します。ACTION_NDEF_DISCOVERED インテントを確認し、インテントの EXTRA から NDEF メッセージを取得する例を次に示します。

Kotlin

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        ...
        if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
            intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMessages ->
                val messages: List<NdefMessage> = rawMessages.map { it as NdefMessage }
                // Process the messages array.
                ...
            }
        }
    }
    

Java

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        ...
        if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(intent.getAction())) {
            Parcelable[] rawMessages =
                intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES);
            if (rawMessages != null) {
                NdefMessage[] messages = new NdefMessage[rawMessages.length];
                for (int i = 0; i < rawMessages.length; i++) {
                    messages[i] = (NdefMessage) rawMessages[i];
                }
                // Process the messages array.
                ...
            }
        }
    }
    

または、インテントから Tag オブジェクトを取得できます。これにはペイロードが含まれ、タグのテクノロジーを列挙することができます。

Kotlin

    val tag: Tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG)
    

Java

    Tag tag = intent.getParcelableExtra(NfcAdapter.EXTRA_TAG);
    

一般的なタイプの NDEF レコードを作成する

ここでは、NFC タグへの書き込みや Android ビームを使用したデータ送信に役立つ、一般的なタイプの NDEF レコードを作成する方法について説明します。Android 4.0(API レベル 14)以降では、URI レコードの自動作成に createUri() メソッドを使用できます。Android 4.1(API レベル 16)以降、createExternal()createMime() を使用して MIME および外部タイプの NDEF レコードを作成できます。NDEF レコードを手動で作成する際の間違いを避けるため、可能な限りこれらのヘルパー メソッドを使用してください。

このセクションでは、レコードに対応するインテント フィルタの作成方法についても説明します。これらの NDEF レコードの例はすべて、NDEF メッセージ(タグに書き込むかビームする)の最初の NDEF レコードに含まれている必要があります。

TNF_ABSOLUTE_URI

注: TNF_ABSOLUTE_URI の代わりに RTD_URI の使用をおすすめします。こちらの方がより効率的です。

TNF_ABSOLUTE_URI NDEF レコードの作成方法を以下に示します。

Kotlin

    val uriRecord = ByteArray(0).let { emptyByteArray ->
        NdefRecord(
                TNF_ABSOLUTE_URI,
                "https://developer.android.com/index.html".toByteArray(Charset.forName("US-ASCII")),
                emptyByteArray,
                emptyByteArray
        )
    }
    

Java

    NdefRecord uriRecord = new NdefRecord(
        NdefRecord.TNF_ABSOLUTE_URI ,
        "https://developer.android.com/index.html".getBytes(Charset.forName("US-ASCII")),
        new byte[0], new byte[0]);
    

前の NDEF レコードのインテント フィルタは次のようになります。

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="http"
            android:host="developer.android.com"
            android:pathPrefix="/index.html" />
    </intent-filter>
    

TNF_MIME_MEDIA

TNF_MIME_MEDIA NDEF レコードの作成方法を以下に示します。

createMime() メソッドを使用:

Kotlin

    val mimeRecord = NdefRecord.createMime(
            "application/vnd.com.example.android.beam",
            "Beam me up, Android".toByteArray(Charset.forName("US-ASCII"))
    )
    

Java

    NdefRecord mimeRecord = NdefRecord.createMime("application/vnd.com.example.android.beam",
        "Beam me up, Android".getBytes(Charset.forName("US-ASCII")));
    

手動で NdefRecord を作成:

Kotlin

    val mimeRecord = Charset.forName("US-ASCII").let { usAscii ->
        NdefRecord(
                NdefRecord.TNF_MIME_MEDIA,
                "application/vnd.com.example.android.beam".toByteArray(usAscii),
                ByteArray(0),
                "Beam me up, Android!".toByteArray(usAscii)
        )
    }
    

Java

    NdefRecord mimeRecord = new NdefRecord(
        NdefRecord.TNF_MIME_MEDIA ,
        "application/vnd.com.example.android.beam".getBytes(Charset.forName("US-ASCII")),
        new byte[0], "Beam me up, Android!".getBytes(Charset.forName("US-ASCII")));
    

前の NDEF レコードのインテント フィルタは次のようになります。

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="application/vnd.com.example.android.beam" />
    </intent-filter>
    

TNF_WELL_KNOWN(RTD_TEXT)

TNF_WELL_KNOWN NDEF レコードの作成方法を以下に示します。

Kotlin

    fun createTextRecord(payload: String, locale: Locale, encodeInUtf8: Boolean): NdefRecord {
        val langBytes = locale.language.toByteArray(Charset.forName("US-ASCII"))
        val utfEncoding = if (encodeInUtf8) Charset.forName("UTF-8") else Charset.forName("UTF-16")
        val textBytes = payload.toByteArray(utfEncoding)
        val utfBit: Int = if (encodeInUtf8) 0 else 1 shl 7
        val status = (utfBit + langBytes.size).toChar()
        val data = ByteArray(1 + langBytes.size + textBytes.size)
        data[0] = status.toByte()
        System.arraycopy(langBytes, 0, data, 1, langBytes.size)
        System.arraycopy(textBytes, 0, data, 1 + langBytes.size, textBytes.size)
        return NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_TEXT, ByteArray(0), data)
    }
    

Java

    public NdefRecord createTextRecord(String payload, Locale locale, boolean encodeInUtf8) {
        byte[] langBytes = locale.getLanguage().getBytes(Charset.forName("US-ASCII"));
        Charset utfEncoding = encodeInUtf8 ? Charset.forName("UTF-8") : Charset.forName("UTF-16");
        byte[] textBytes = payload.getBytes(utfEncoding);
        int utfBit = encodeInUtf8 ? 0 : (1 << 7);
        char status = (char) (utfBit + langBytes.length);
        byte[] data = new byte[1 + langBytes.length + textBytes.length];
        data[0] = (byte) status;
        System.arraycopy(langBytes, 0, data, 1, langBytes.length);
        System.arraycopy(textBytes, 0, data, 1 + langBytes.length, textBytes.length);
        NdefRecord record = new NdefRecord(NdefRecord.TNF_WELL_KNOWN,
        NdefRecord.RTD_TEXT, new byte[0], data);
        return record;
    }
    

前の NDEF レコードのインテント フィルタは次のようになります。

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
    

TNF_WELL_KNOWN(RTD_URI)

TNF_WELL_KNOWN NDEF レコードの作成方法を以下に示します。

createUri(String) メソッドを使用:

Kotlin

    val rtdUriRecord1 = NdefRecord.createUri("http://example.com")
    

Java

    NdefRecord rtdUriRecord1 = NdefRecord.createUri("http://example.com");
    

createUri(Uri) メソッドを使用:

Kotlin

    val rtdUriRecord2 = Uri.parse("http://example.com").let { uri ->
        NdefRecord.createUri(uri)
    }
    

Java

    Uri uri = Uri.parse("http://example.com");
    NdefRecord rtdUriRecord2 = NdefRecord.createUri(uri);
    

手動で NdefRecord を作成:

Kotlin

    val uriField = "example.com".toByteArray(Charset.forName("US-ASCII"))
    val payload = ByteArray(uriField.size + 1)                   //add 1 for the URI Prefix
    payload [0] = 0x01                                           //prefixes http://www. to the URI
    System.arraycopy(uriField, 0, payload, 1, uriField.size)     //appends URI to payload
    val rtdUriRecord = NdefRecord(NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, ByteArray(0), payload)
    

Java

    byte[] uriField = "example.com".getBytes(Charset.forName("US-ASCII"));
    byte[] payload = new byte[uriField.length + 1];              //add 1 for the URI Prefix
    payload[0] = 0x01;                                           //prefixes http://www. to the URI
    System.arraycopy(uriField, 0, payload, 1, uriField.length);  //appends URI to payload
    NdefRecord rtdUriRecord = new NdefRecord(
        NdefRecord.TNF_WELL_KNOWN, NdefRecord.RTD_URI, new byte[0], payload);
    

前の NDEF レコードのインテント フィルタは次のようになります。

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="http"
            android:host="example.com"
            android:pathPrefix="" />
    </intent-filter>
    

TNF_EXTERNAL_TYPE

TNF_EXTERNAL_TYPE NDEF レコードの作成方法を以下に示します。

createExternal() メソッドを使用:

Kotlin

    var payload: ByteArray //assign to your data
    val domain = "com.example" //usually your app's package name
    val type = "externalType"
    val extRecord = NdefRecord.createExternal(domain, type, payload)
    

Java

    byte[] payload; //assign to your data
    String domain = "com.example"; //usually your app's package name
    String type = "externalType";
    NdefRecord extRecord = NdefRecord.createExternal(domain, type, payload);
    

手動で NdefRecord を作成:

Kotlin

    var payload: ByteArray
    ...
    val extRecord = NdefRecord(
            NdefRecord.TNF_EXTERNAL_TYPE,
            "com.example:externalType".toByteArray(Charset.forName("US-ASCII")),
            ByteArray(0),
            payload
    )
    

Java

    byte[] payload;
    ...
    NdefRecord extRecord = new NdefRecord(
        NdefRecord.TNF_EXTERNAL_TYPE, "com.example:externalType".getBytes(Charset.forName("US-ASCII")),
        new byte[0], payload);
    

前の NDEF レコードのインテント フィルタは次のようになります。

    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="vnd.android.nfc"
            android:host="ext"
            android:pathPrefix="/com.example:externalType"/>
    </intent-filter>
    

TNF_EXTERNAL_TYPE を使用すると、より一般的な NFC タグをデプロイし、Android デバイスと Android 以外のデバイスの両方をより適切にサポートできます。

: TNF_EXTERNAL_TYPE の URN の標準形式は urn:nfc:ext:example.com:externalType ですが、NFC フォーラムの RTD 仕様では、URN の urn:nfc:ext: 部分を NDEF レコードから削除する必要があると宣言されています。したがって、必要となるのは、ドメイン(例では example.com)とタイプ(例では externalType)をコロンで区切って指定することだけです。TNF_EXTERNAL_TYPE をディスパッチすると、Android は urn:nfc:ext:example.com:externalType URN を vnd.android.nfc://ext/example.com:externalType URI に変換します。これは、この例のインテント フィルタで宣言されているとおりです。

Android アプリのレコード

Android 4.0(API レベル 14)で導入された Android Application Record(AAR)により、NFC タグのスキャン時にアプリケーションが起動される確実性が向上します。AAR には、NDEF レコード内に埋め込まれたアプリケーションのパッケージ名があります。Android は NDEF メッセージ全体で AAR を検索するため、NDEF メッセージの任意の NDEF レコードに AAR を追加できます。AAR が見つかると、AAR 内のパッケージ名に基づいてアプリケーションを起動します。アプリケーションがデバイスにない場合、Google Play を起動してアプリをダウンロードします。

AAR は、同じインテントをフィルタリングする他のアプリケーションにより、自分がデプロイした特定のタグが処理されるのを防ぐ場合に便利です。AAR は、パッケージ名の制約のため、アプリケーション レベルでのみサポートされ、インテント フィルタリングの場合のようにアクティビティ レベルではサポートされません。アクティビティ レベルでインテントを処理する場合は、インテント フィルタを使用します。

タグに AAR が含まれている場合、タグ ディスパッチ システムによるディスパッチ方法は以下のとおりです。

  1. 通常どおりに、インテント フィルタを使用したアクティビティの開始を試みます。インテントに一致するアクティビティが AAR にも一致する場合、アクティビティを開始します。
  2. インテントをフィルタするアクティビティが AAR と一致しない場合、複数のアクティビティがインテントを処理できる場合、インテントを処理するアクティビティがない場合は、AAR で指定されたアプリを起動します。
  3. AAR で開始できるアプリがない場合は、Google Play にアクセスして AAR ベースのアプリをダウンロードします。

注: AAR とインテント ディスパッチ システムを、フォアグラウンド ディスパッチ システムでオーバーライドできます。これにより、NFC タグが検出されたときにフォアグラウンド アクティビティを優先させることができます。この方法で AAR とインテント ディスパッチ システムをオーバーライドするには、アクティビティがフォアグラウンドにある必要があります。

AAR を含まないスキャンされたタグをフィルタする場合は、通常どおりインテント フィルタを宣言できます。これは、アプリケーションが AAR を含まない他のタグと関連している場合に便利です。たとえば、サードパーティがデプロイする一般的なタグだけでなく、自身がデプロイする独自のタグをアプリケーションで処理できるよう保証する場合などです。AAR は Android 4.0 以降のデバイスに固有であるため、タグをデプロイする場合は AAR と、MIME タイプまたは URI を組み合わせて使用し、幅広いデバイスをサポートすることをおすすめします。さらに、NFC タグをデプロイする際は、NFC タグの書き込み方法を検討して、ほとんどのデバイス(Android デバイスやその他のデバイス)への対応を可能にしてください。これを行うには、重複が比較的生じにくい MIME タイプや URI を定義して、アプリケーションが識別しやすいようにします。

Android は AAR を作成するシンプルな API createApplicationRecord() を提供します。AAR を NdefMessage の任意の場所に埋め込むだけですみます。AAR が NdefMessage 内の唯一のレコードでない限り、NdefMessage の最初のレコードを使用する必要はありません。これは、Android システムが NdefMessage の最初のレコードをチェックして、タグの MIME タイプまたは URI(アプリケーションがフィルタするインテントの作成に使用されます)を決定するためです。AAR を作成する方法を次のコードで示します。

Kotlin

    val msg = NdefMessage(
            arrayOf(
                    ...,
                    NdefRecord.createApplicationRecord("com.example.android.beam")
            )
    )
    

Java

    NdefMessage msg = new NdefMessage(
            new NdefRecord[] {
                ...,
                NdefRecord.createApplicationRecord("com.example.android.beam")}
            );
    )
    

NDEF メッセージを他のデバイスにビームする

Android ビームを使用すると、2 台の Android デバイス間でシンプルなピアツーピアのデータ交換が可能になります。別のデバイスにデータをビームするアプリケーションがフォアグラウンドで実行していること、およびデータを受信するデバイスではロックを解除することが必要です。ビーム送信側デバイスと受信側デバイスが十分に接近すると、送信側デバイスの UI に [タップしてビーム] が表示されます。ユーザーは受信側デバイスにメッセージをビームするかどうかを選択できます。

注: フォアグラウンド NDEF プッシュは API レベル 10 で利用可能であり、Android ビームと同様の機能を提供します。これらの API のサポートは終了しましたが、古いデバイスへの対応に使用できます。詳しくは、enableForegroundNdefPush() をご覧ください。

アプリケーションの Android ビームを有効にするには、次の 2 つのメソッドのいずれかを呼び出します。

  • setNdefPushMessage(): NdefMessage を受け入れて、ビームするメッセージとして設定します。2 つのデバイスが十分に近接したときに自動的にメッセージをビームします。
  • setNdefPushMessageCallback(): createNdefMessage()(デバイスがデータをビームする範囲内にあるときに呼び出されます)を含むコールバックを受け入れます。コールバックを使用すると、必要な場合にのみ NDEF メッセージを作成できます。

アクティビティは一度に 1 つの NDEF メッセージしかプッシュできないため、setNdefPushMessage() よりも setNdefPushMessageCallback() が優先されます(両方が設定されている場合)。Android ビームを使用するには、次の一般的なガイドラインを満たす必要があります。

  • データをビームするアクティビティは、フォアグラウンドでなければなりません。送受信デバイスの両方の画面ロックが解除されている必要があります。
  • ビームしているデータを NdefMessage オブジェクトにカプセル化する必要があります。
  • ビームされたデータを受信する NFC デバイスでは、com.android.npp NDEF プッシュ プロトコルまたは NFC フォーラムの SNEP(Simple NDEF Exchange Protocol)をサポートする必要があります。com.android.npp プロトコルは、API レベル 9(Android 2.3)から API レベル 13(Android 3.2)のデバイスに必須です。API レベル 14(Android 4.0)以降では com.android.npp と SNEP の両方が必須です。

注: Android ビームを有効にしたアクティビティがフォアグラウンドにある場合、標準のインテント ディスパッチ システムが無効になります。ただし、アクティビティでフォアグラウンド ディスパッチも有効にしている場合、フォアグラウンド ディスパッチで設定されたインテント フィルタに一致するタグをスキャンできます。

Android ビームを有効にするには:

  1. 他のデバイスにプッシュする NdefRecord を含む NdefMessage を作成します。
  2. アクティビティの onCreate() メソッドで、NdefMessage を使用して setNdefPushMessage() を呼び出すか、NfcAdapter.CreateNdefMessageCallback オブジェクトを渡して setNdefPushMessageCallback を呼び出します。これらのメソッドには、Android ビームで有効にするアクティビティ(最低 1 つ)と、アクティブにする他のアクティビティのオプション リストが必要です。

    一般に、2 つのデバイスが通信範囲内にあるときに、アクティビティが常に同じ NDEF メッセージをプッシュすればよい場合は、setNdefPushMessage() を使用します。setNdefPushMessageCallback を使用するのは、アプリケーションが現在のコンテキストを考慮し、ユーザーのアプリケーションでの操作に応じて NDEF メッセージをプッシュする場合です。

単純なアクティビティの onCreate() メソッドで NfcAdapter.CreateNdefMessageCallback を呼び出す例を以下に示します(完全なサンプルについては、AndroidBeamDemo をご覧ください)。この例には、MIME レコードの作成に便利なメソッドもあります。

Kotlin

    package com.example.android.beam

    import android.app.Activity
    import android.content.Intent
    import android.nfc.NdefMessage
    import android.nfc.NdefRecord
    import android.nfc.NfcAdapter
    import android.nfc.NfcAdapter.CreateNdefMessageCallback
    import android.nfc.NfcEvent
    import android.os.Bundle
    import android.os.Parcelable
    import android.widget.TextView
    import android.widget.Toast
    import java.nio.charset.Charset

    class Beam : Activity(), NfcAdapter.CreateNdefMessageCallback {

        private var nfcAdapter: NfcAdapter? = null
        private lateinit var textView: TextView

        override fun onCreate(savedInstanceState: Bundle?) {
            super.onCreate(savedInstanceState)
            setContentView(R.layout.main)
            textView = findViewById(R.id.textView)
            // Check for available NFC Adapter
            nfcAdapter = NfcAdapter.getDefaultAdapter(this)
            if (nfcAdapter == null) {
                Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show()
                finish()
                return
            }
            // Register callback
            nfcAdapter?.setNdefPushMessageCallback(this, this)
        }

        override fun createNdefMessage(event: NfcEvent): NdefMessage {
            val text = "Beam me up, Android!\n\n" +
                    "Beam Time: " + System.currentTimeMillis()
            return NdefMessage(
                    arrayOf(
                            createMime("application/vnd.com.example.android.beam", text.toByteArray())
                    )
                    /**
                     * The Android Application Record (AAR) is commented out. When a device
                     * receives a push with an AAR in it, the application specified in the AAR
                     * is guaranteed to run. The AAR overrides the tag dispatch system.
                     * You can add it back in to guarantee that this
                     * activity starts when receiving a beamed message. For now, this code
                     * uses the tag dispatch system.
                     *///,NdefRecord.createApplicationRecord("com.example.android.beam")
            )
        }

        override fun onResume() {
            super.onResume()
            // Check to see that the Activity started due to an Android Beam
            if (NfcAdapter.ACTION_NDEF_DISCOVERED == intent.action) {
                processIntent(intent)
            }
        }

        override fun onNewIntent(intent: Intent) {
            // onResume gets called after this to handle the intent
            setIntent(intent)
        }

        /**
         * Parses the NDEF Message from the intent and prints to the TextView
         */
        private fun processIntent(intent: Intent) {
            textView = findViewById(R.id.textView)
            // only one message sent during the beam
            intent.getParcelableArrayExtra(NfcAdapter.EXTRA_NDEF_MESSAGES)?.also { rawMsgs ->
                (rawMsgs[0] as NdefMessage).apply {
                    // record 0 contains the MIME type, record 1 is the AAR, if present
                    textView.text = String(records[0].payload)
                }
            }
        }
    }
    

Java

    package com.example.android.beam;

    import android.app.Activity;
    import android.content.Intent;
    import android.nfc.NdefMessage;
    import android.nfc.NdefRecord;
    import android.nfc.NfcAdapter;
    import android.nfc.NfcAdapter.CreateNdefMessageCallback;
    import android.nfc.NfcEvent;
    import android.os.Bundle;
    import android.os.Parcelable;
    import android.widget.TextView;
    import android.widget.Toast;
    import java.nio.charset.Charset;

    public class Beam extends Activity implements CreateNdefMessageCallback {
        NfcAdapter nfcAdapter;
        TextView textView;

        @Override
        public void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.main);
            TextView textView = (TextView) findViewById(R.id.textView);
            // Check for available NFC Adapter
            nfcAdapter = NfcAdapter.getDefaultAdapter(this);
            if (nfcAdapter == null) {
                Toast.makeText(this, "NFC is not available", Toast.LENGTH_LONG).show();
                finish();
                return;
            }
            // Register callback
            nfcAdapter.setNdefPushMessageCallback(this, this);
        }

        @Override
        public NdefMessage createNdefMessage(NfcEvent event) {
            String text = ("Beam me up, Android!\n\n" +
                    "Beam Time: " + System.currentTimeMillis());
            NdefMessage msg = new NdefMessage(
                    new NdefRecord[] { createMime(
                            "application/vnd.com.example.android.beam", text.getBytes())
             /**
              * The Android Application Record (AAR) is commented out. When a device
              * receives a push with an AAR in it, the application specified in the AAR
              * is guaranteed to run. The AAR overrides the tag dispatch system.
              * You can add it back in to guarantee that this
              * activity starts when receiving a beamed message. For now, this code
              * uses the tag dispatch system.
              */
              //,NdefRecord.createApplicationRecord("com.example.android.beam")
            });
            return msg;
        }

        @Override
        public void onResume() {
            super.onResume();
            // Check to see that the Activity started due to an Android Beam
            if (NfcAdapter.ACTION_NDEF_DISCOVERED.equals(getIntent().getAction())) {
                processIntent(getIntent());
            }
        }

        @Override
        public void onNewIntent(Intent intent) {
            // onResume gets called after this to handle the intent
            setIntent(intent);
        }

        /**
         * Parses the NDEF Message from the intent and prints to the TextView
         */
        void processIntent(Intent intent) {
            textView = (TextView) findViewById(R.id.textView);
            Parcelable[] rawMsgs = intent.getParcelableArrayExtra(
                    NfcAdapter.EXTRA_NDEF_MESSAGES);
            // only one message sent during the beam
            NdefMessage msg = (NdefMessage) rawMsgs[0];
            // record 0 contains the MIME type, record 1 is the AAR, if present
            textView.setText(new String(msg.getRecords()[0].getPayload()));
        }
    }
    

AAR に関するコードはコメントアウトされており、削除できます。AAR を有効にすると、AAR で指定されたアプリケーションは常に Android ビーム メッセージを受信します。アプリケーションが存在しない場合、Google Play によりアプリのダウンロードが開始されます。したがって、AAR を使用する場合、Android 4.0 以降のデバイスでは、次のインテント フィルタは技術的には必要ありません。

    <intent-filter>
      <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
      <category android:name="android.intent.category.DEFAULT"/>
      <data android:mimeType="application/vnd.com.example.android.beam"/>
    </intent-filter>
    

このインテント フィルタにより、NFC タグをスキャンするか、com.example.android.beam タイプの AAR を持つ Android ビームを受信したとき、または NDEF 形式のメッセージに application/vnd.com.example.android.beam タイプの MIME レコードが含まれている場合に、 アプリケーションを起動できるようになります。

AAR でアプリケーションの起動やダウンロードを保証する場合にも、インテント フィルタの使用をおすすめします。AAR で指定されたパッケージ内のメイン アクティビティを常に開始するのではなく、アプリケーションにおいて選択したアクティビティを開始できるためです。AAR にはアクティビティ レベルの粒度はありません。また、一部の Android デバイスでは AAR がサポートされていないため、NDEF メッセージの最初の NDEF レコードに識別情報を埋め込み、AAR が機能しない場合に備えてフィルタしておく必要があります。レコードの作成方法の詳細は、一般的なタイプの NDEF レコードの作成をご覧ください。