NFC の基本

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

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

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

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 ペイロードを含むタグがスキャンされ、タイプが認識済みのものである場合は、このインテントを使用してアクティビティを開始します。これは最も優先度の高いインテントであり、タグ ディスパッチ システムは、可能な限り他のインテントよりも先にこのインテントでアクティビティを開始しようとします。

    注: Android 16 以降では、ウェブリンクを保存する NFC タグ(URI スキームが「http://」または「https://」)をスキャンすると、ACTION_NDEF_DISCOVERED インテントではなく ACTION_VIEW インテントがトリガーされます。Android 17 以降では、このようなタグをスキャンすると「リンクを開く」通知が表示され、ACTION_VIEW インテントをトリガーするにはユーザー インタラクションの明示的な操作が必要になります。

  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" />
  • Android 17(API レベル 37)以降では、アクティビティに NFC インテントをディスパッチするには、アプリが SDK > Build.VERSION_CODES.BAKLAVA をターゲットとしている場合、android.permission.DISPATCH_NFC_MESSAGE 権限で保護されている必要があります。これにより、NFC システム サービスのみがアクティビティにインテントをディスパッチできるようになります。また、システムは停止状態のアプリ(ユーザーが一度も起動したことがないアプリや強制停止されたアプリなど)に NFC インテントをディスパッチしません。
    <activity
        android:name=".MyActivity"
        android:exported="true"
        android:permission="android.permission.DISPATCH_NFC_MESSAGE">
        ...
    </activity>
  • アプリケーションでサポート可能な SDK の最小バージョン。API レベル 9 では、ACTION_TAG_DISCOVERED による限定的なタグ ディスパッチのみをサポートし、EXTRA_NDEF_MESSAGES による NDEF メッセージへのアクセスのみを提供します。他のタグプロパティや I/O オペレーションにアクセスすることはできません。API レベル 10 には、リーダー/ライターの包括的サポートとフォアグラウンド NDEF プッシュ機能があります。API レベル 14 には、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/plain の ACTION_NDEF_DISCOVERED インテントをフィルタする例を以下に示します。

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

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

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED"/>
        <category android:name="android.intent.category.DEFAULT"/>
        <data android:scheme="https"
                  android:host="developer.android.com"
                  android:pathPrefix="/index.html" />
    </intent-filter>
</activity>

ACTION_TECH_DISCOVERED

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

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

次の例では、テクノロジーをすべて定義しています。NFC タグでサポートされていないものは削除する必要があります。このファイル(名前は自由に指定できます)を <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 つでも存在する場合、アクティビティは一致すると見なされます。これにより、テクノロジーの照合に AND と OR のセマンティクスが適用されます。次の例は、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
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <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 は Android 17(API レベル 37)以降で非推奨になりました。代わりに ACTION_NDEF_DISCOVERED または ACTION_TECH_DISCOVERED を使用してください。

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

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <intent-filter>
        <action android:name="android.nfc.action.TAG_DISCOVERED"/>
    </intent-filter>
</activity>

ACTION_VIEW

Android 16 以降では、ウェブリンクを保存する NFC タグ(つまり、URI スキームが「http://」または「https://」)をスキャンすると、ACTION_NDEF_DISCOVERED インテントではなく ACTION_VIEW インテントがトリガーされます。Android 17 以降では、このようなタグをスキャンすると「リンクを開く」通知が表示され、ACTION_VIEW インテントをトリガーするにはユーザー インタラクションが必要になります。ACTION_VIEW でフィルタするには、this をご覧ください。Android app links を使用して、URL のアプリを開きます。

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

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 タグへの書き込みに役立つ、一般的なタイプの 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 レコードのインテント フィルタは次のようになります。

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="https"
            android:host="developer.android.com"
            android:pathPrefix="/index.html" />
    </intent-filter>
</activity>

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 レコードのインテント フィルタは次のようになります。

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <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>
</activity>

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 レコードのインテント フィルタは次のようになります。

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:mimeType="text/plain" />
    </intent-filter>
</activity>

TNF_WELL_KNOWN(RTD_URI)

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

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

Kotlin

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

Java

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

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

Kotlin

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

Java

Uri uri = Uri.parse("https://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 https://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 https://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 レコードのインテント フィルタは次のようになります。

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <intent-filter>
        <action android:name="android.nfc.action.NDEF_DISCOVERED" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="https"
            android:host="example.com"
            android:pathPrefix="" />
    </intent-filter>
</activity>

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 レコードのインテント フィルタは次のようになります。

<activity
    android:name=".MyActivity"
    android:exported="true"
    android:permission="android.permission.DISPATCH_NFC_MESSAGE">
    <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>
</activity>

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")}
        );
)

NFC タグ スキャン用のアプリの許可リスト

Android 16 以降では、アプリが NFC タグをスキャンするための最初の NFC インテントを受信したときに、ユーザーに通知されます。通知で、アプリが NFC タグをスキャンすることを禁止するオプションがユーザーに提供されます。

  • アプリは、NfcAdapter.isTagIntentAllowed() を使用して、ユーザーがアプリに NFC タグのスキャンを許可しているかどうかを確認できます。
  • アプリは、インテント ACTION_CHANGE_TAG_INTENT_PREFERENCE を送信することで、NFC タグのスキャンを再度許可するようユーザーに求めることができます。

注: NFC タグ スキャンアプリの許可リストには、Settings > Apps > Special app access > Launch via NFC からアクセスできます。

このメカニズムは、NFC タグ インテントのインテント フィルタを登録した一部のアプリが、スマートフォンを NFC タグ(クレジットカード、別のスマートフォン/スマートウォッチなど)の近くに置くと、繰り返しフォアグラウンドに表示されるというユーザーからの懸念に対応するために追加されました。