summaryrefslogtreecommitdiffstats
path: root/core/java
diff options
context:
space:
mode:
authorNick Pelly <npelly@google.com>2012-01-05 15:13:01 +1100
committerNick Pelly <npelly@google.com>2012-01-25 13:17:19 -0800
commitc97a552023c3c71079b39092e80c9b44f25a789b (patch)
tree639e700cdd538f2ebd080143182fa30bb32fdd2a /core/java
parentdc828acd5fadb266b13cce459b1cacfad8ef7aef (diff)
downloadframeworks_base-c97a552023c3c71079b39092e80c9b44f25a789b.zip
frameworks_base-c97a552023c3c71079b39092e80c9b44f25a789b.tar.gz
frameworks_base-c97a552023c3c71079b39092e80c9b44f25a789b.tar.bz2
Improve NDEF API's
o Add NdefRecord.toMimeType() Maps the record to a MIME type o Add NdefRecord.toUri() Maps the record to a URI o Add hidden NfcAdapter.dispatch() Helps test the dispatch path. o Modify createMime(), createUri() and createExternal(): Do not try and strictly follow RFC requirements for URI or MIME content types. This just leads to heartbreak - the RFC requirements are too strict. For example RFC1341 forbids the use of '.' in a MIME type, however this is in common use in types such as "application/vnd.companyname". I think the best approach is to only remove 'obvious' whitespace issues, and to convert uppercase to lowercase as per Android guidelines. Change-Id: Id686f5f3b05b2dceafad48e1cfcbdb2b3890b854
Diffstat (limited to 'core/java')
-rw-r--r--core/java/android/nfc/INfcAdapter.aidl3
-rw-r--r--core/java/android/nfc/NdefMessage.java16
-rw-r--r--core/java/android/nfc/NdefRecord.java307
-rw-r--r--core/java/android/nfc/NfcAdapter.java36
4 files changed, 231 insertions, 131 deletions
diff --git a/core/java/android/nfc/INfcAdapter.aidl b/core/java/android/nfc/INfcAdapter.aidl
index 0b93ad0..d2afbb9 100644
--- a/core/java/android/nfc/INfcAdapter.aidl
+++ b/core/java/android/nfc/INfcAdapter.aidl
@@ -17,7 +17,6 @@
package android.nfc;
import android.app.PendingIntent;
-import android.content.ComponentName;
import android.content.IntentFilter;
import android.nfc.NdefMessage;
import android.nfc.Tag;
@@ -44,4 +43,6 @@ interface INfcAdapter
void setForegroundDispatch(in PendingIntent intent,
in IntentFilter[] filters, in TechListParcel techLists);
void setForegroundNdefPush(in NdefMessage msg, in INdefPushCallback callback);
+
+ void dispatch(in Tag tag, in NdefMessage message);
}
diff --git a/core/java/android/nfc/NdefMessage.java b/core/java/android/nfc/NdefMessage.java
index 38bc16d..c83144f 100644
--- a/core/java/android/nfc/NdefMessage.java
+++ b/core/java/android/nfc/NdefMessage.java
@@ -92,9 +92,7 @@ public final class NdefMessage implements Parcelable {
* @throws FormatException if the data cannot be parsed
*/
public NdefMessage(byte[] data) throws FormatException {
- if (data == null) {
- throw new NullPointerException("null data");
- }
+ if (data == null) throw new NullPointerException("data is null");
ByteBuffer buffer = ByteBuffer.wrap(data);
mRecords = NdefRecord.parse(buffer, false);
@@ -112,9 +110,8 @@ public final class NdefMessage implements Parcelable {
*/
public NdefMessage(NdefRecord record, NdefRecord ... records) {
// validate
- if (record == null) {
- throw new NullPointerException("record cannot be null");
- }
+ if (record == null) throw new NullPointerException("record cannot be null");
+
for (NdefRecord r : records) {
if (r == null) {
throw new NullPointerException("record cannot be null");
@@ -147,7 +144,12 @@ public final class NdefMessage implements Parcelable {
/**
* Get the NDEF Records inside this NDEF Message.<p>
- * An NDEF Message always has one or more NDEF Records.
+ * An {@link NdefMessage} always has one or more NDEF Records: so the
+ * following code to retrieve the first record is always safe
+ * (no need to check for null or array length >= 1):
+ * <pre>
+ * NdefRecord firstRecord = ndefMessage.getRecords()[0];
+ * </pre>
*
* @return array of one or more NDEF records.
*/
diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java
index b4c488b..0e9e8f4 100644
--- a/core/java/android/nfc/NdefRecord.java
+++ b/core/java/android/nfc/NdefRecord.java
@@ -16,6 +16,7 @@
package android.nfc;
+import android.content.Intent;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
@@ -25,6 +26,7 @@ import java.nio.charset.Charsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
+import java.util.Locale;
/**
* Represents an immutable NDEF Record.
@@ -305,9 +307,9 @@ public final class NdefRecord implements Parcelable {
* @return Android application NDEF record
*/
public static NdefRecord createApplicationRecord(String packageName) {
- if (packageName.length() == 0) {
- throw new IllegalArgumentException("empty package name");
- }
+ if (packageName == null) throw new NullPointerException("packageName is null");
+ if (packageName.length() == 0) throw new IllegalArgumentException("packageName is empty");
+
return new NdefRecord(TNF_EXTERNAL_TYPE, RTD_ANDROID_APP, null,
packageName.getBytes(Charsets.UTF_8));
}
@@ -318,32 +320,27 @@ public final class NdefRecord implements Parcelable {
* Uses the well known URI type representation: {@link #TNF_WELL_KNOWN}
* and {@link #RTD_URI}. This is the most efficient encoding
* of a URI into NDEF.<p>
+ * The uri parameter will be normalized with
+ * {@link Uri#normalize} to set the scheme to lower case to
+ * follow Android best practices for intent filtering.
+ * However the unchecked exception
+ * {@link IllegalArgumentException} may be thrown if the uri
+ * parameter has serious problems, for example if it is empty, so always
+ * catch this exception if you are passing user-generated data into this
+ * method.<p>
+ *
* Reference specification: NFCForum-TS-RTD_URI_1.0
*
* @param uri URI to encode.
* @return an NDEF Record containing the URI
- * @throws IllegalArugmentException if a valid record cannot be created
+ * @throws IllegalArugmentException if the uri is empty or invalid
*/
public static NdefRecord createUri(Uri uri) {
- return createUri(uri.toString());
- }
+ if (uri == null) throw new NullPointerException("uri is null");
- /**
- * Create a new NDEF Record containing a URI.<p>
- * Use this method to encode a URI (or URL) into an NDEF Record.<p>
- * Uses the well known URI type representation: {@link #TNF_WELL_KNOWN}
- * and {@link #RTD_URI}. This is the most efficient encoding
- * of a URI into NDEF.<p>
- * Reference specification: NFCForum-TS-RTD_URI_1.0
- *
- * @param uriString string URI to encode.
- * @return an NDEF Record containing the URI
- * @throws IllegalArugmentException if a valid record cannot be created
- */
- public static NdefRecord createUri(String uriString) {
- if (uriString.length() == 0) {
- throw new IllegalArgumentException("empty uriString");
- }
+ uri = uri.normalize();
+ String uriString = uri.toString();
+ if (uriString.length() == 0) throw new IllegalArgumentException("uri is empty");
byte prefix = 0;
for (int i = 1; i < URI_PREFIX_MAP.length; i++) {
@@ -361,28 +358,72 @@ public final class NdefRecord implements Parcelable {
}
/**
+ * Create a new NDEF Record containing a URI.<p>
+ * Use this method to encode a URI (or URL) into an NDEF Record.<p>
+ * Uses the well known URI type representation: {@link #TNF_WELL_KNOWN}
+ * and {@link #RTD_URI}. This is the most efficient encoding
+ * of a URI into NDEF.<p>
+ * The uriString parameter will be normalized with
+ * {@link Uri#normalize} to set the scheme to lower case to
+ * follow Android best practices for intent filtering.
+ * However the unchecked exception
+ * {@link IllegalArgumentException} may be thrown if the uriString
+ * parameter has serious problems, for example if it is empty, so always
+ * catch this exception if you are passing user-generated data into this
+ * method.<p>
+ *
+ * Reference specification: NFCForum-TS-RTD_URI_1.0
+ *
+ * @param uriString string URI to encode.
+ * @return an NDEF Record containing the URI
+ * @throws IllegalArugmentException if the uriString is empty or invalid
+ */
+ public static NdefRecord createUri(String uriString) {
+ return createUri(Uri.parse(uriString));
+ }
+
+ /**
* Create a new NDEF Record containing MIME data.<p>
* Use this method to encode MIME-typed data into an NDEF Record,
* such as "text/plain", or "image/jpeg".<p>
- * Expects US-ASCII characters in mimeType. The encoding of the
- * mimeData depends on the mimeType.<p>
+ * The mimeType parameter will be normalized with
+ * {@link Intent#normalizeMimeType} to follow Android best
+ * practices for intent filtering, for example to force lower-case.
+ * However the unchecked exception
+ * {@link IllegalArgumentException} may be thrown
+ * if the mimeType parameter has serious problems,
+ * for example if it is empty, so always catch this
+ * exception if you are passing user-generated data into this method.
+ * <p>
* For efficiency, This method might not make an internal copy of the
* mimeData byte array, so take care not
- * to re-use the mimeData byte array while still using the returned
+ * to modify the mimeData byte array while still using the returned
* NdefRecord.
*
- * @param mimeType MIME type, expects US-ASCII characters only
+ * @param mimeType a valid MIME type
* @param mimeData MIME data as bytes
* @return an NDEF Record containing the MIME-typed data
- * @throws IllegalArugmentException if a valid record cannot be created
+ * @throws IllegalArugmentException if the mimeType is empty or invalid
+ *
*/
public static NdefRecord createMime(String mimeType, byte[] mimeData) {
- if (mimeType.length() == 0) {
- throw new IllegalArgumentException("empty mimeType");
+ if (mimeType == null) throw new NullPointerException("mimeType is null");
+
+ // We only do basic MIME type validation: trying to follow the
+ // RFCs strictly only ends in tears, since there are lots of MIME
+ // types in common use that are not strictly valid as per RFC rules
+ mimeType = Intent.normalizeMimeType(mimeType);
+ if (mimeType.length() == 0) throw new IllegalArgumentException("mimeType is empty");
+ int slashIndex = mimeType.indexOf('/');
+ if (slashIndex == 0) throw new IllegalArgumentException("mimeType must have major type");
+ if (slashIndex == mimeType.length() - 1) {
+ throw new IllegalArgumentException("mimeType must have minor type");
}
+ // missing '/' is allowed
- return new NdefRecord(TNF_MIME_MEDIA, mimeType.getBytes(Charsets.US_ASCII), null,
- mimeData);
+ // MIME RFCs suggest ASCII encoding for content-type
+ byte[] typeBytes = mimeType.getBytes(Charsets.US_ASCII);
+ return new NdefRecord(TNF_MIME_MEDIA, typeBytes, null, mimeData);
}
/**
@@ -391,32 +432,38 @@ public final class NdefRecord implements Parcelable {
* The data is typed by a domain name (usually your Android package name) and
* a domain-specific type. This data is packaged into a "NFC Forum External
* Type" NDEF Record.<p>
- * Both the domain and type used to construct an external record are case
- * insensitive, and this implementation will encode all characters to lower
- * case. Only a subset of ASCII characters are allowed for the domain
- * and type. There are no restrictions on the payload data.<p>
+ * NFC Forum requires that the domain and type used in an external record
+ * are treated as case insensitive, however Android intent filtering is
+ * always case sensitive. So this method will force the domain and type to
+ * lower-case before creating the NDEF Record.<p>
+ * The unchecked exception {@link IllegalArgumentException} will be thrown
+ * if the domain and type have serious problems, for example if either field
+ * is empty, so always catch this
+ * exception if you are passing user-generated data into this method.<p>
+ * There are no such restrictions on the payload data.<p>
* For efficiency, This method might not make an internal copy of the
* data byte array, so take care not
- * to re-use the data byte array while still using the returned
+ * to modify the data byte array while still using the returned
* NdefRecord.
*
* Reference specification: NFCForum-TS-RTD_1.0
* @param domain domain-name of issuing organization
* @param type domain-specific type of data
* @param data payload as bytes
- * @throws IllegalArugmentException if a valid record cannot be created
+ * @throws IllegalArugmentException if either domain or type are empty or invalid
*/
public static NdefRecord createExternal(String domain, String type, byte[] data) {
- if (domain.length() == 0 || type.length() == 0) {
- throw new IllegalArgumentException("empty domain or type");
- }
- byte[] byteDomain = domain.getBytes(Charsets.US_ASCII);
- ensureValidDomain(byteDomain);
- toLowerCase(byteDomain);
- byte[] byteType = type.getBytes(Charsets.US_ASCII);
- ensureValidWkt(byteType);
- toLowerCase(byteType);
+ if (domain == null) throw new NullPointerException("domain is null");
+ if (type == null) throw new NullPointerException("type is null");
+
+ domain = domain.trim().toLowerCase(Locale.US);
+ type = type.trim().toLowerCase(Locale.US);
+
+ if (domain.length() == 0) throw new IllegalArgumentException("domain is empty");
+ if (type.length() == 0) throw new IllegalArgumentException("type is empty");
+ byte[] byteDomain = domain.getBytes(Charsets.UTF_8);
+ byte[] byteType = type.getBytes(Charsets.UTF_8);
byte[] b = new byte[byteDomain.length + 1 + byteType.length];
System.arraycopy(byteDomain, 0, b, 0, byteDomain.length);
b[byteDomain.length] = ':';
@@ -574,51 +621,113 @@ public final class NdefRecord implements Parcelable {
}
/**
- * Helper to return the NdefRecord as a URI.
- * TODO: Consider making a member method instead of static
- * TODO: Consider more validation that this is a URI record
- * TODO: Make a public API
- * @hide
+ * Map this record to a MIME type, or return null if it cannot be mapped.<p>
+ * Currently this method considers all {@link #TNF_MIME_MEDIA} records to
+ * be MIME records, as well as some {@link #TNF_WELL_KNOWN} records such as
+ * {@link #RTD_TEXT}. If this is a MIME record then the MIME type as string
+ * is returned, otherwise null is returned.<p>
+ * This method does not perform validation that the MIME type is
+ * actually valid. It always attempts to
+ * return a string containing the type if this is a MIME record.<p>
+ * The returned MIME type will by normalized to lower-case using
+ * {@link Intent#normalizeMimeType}.<p>
+ * The MIME payload can be obtained using {@link #getPayload}.
+ *
+ * @return MIME type as a string, or null if this is not a MIME record
*/
- public static Uri parseWellKnownUriRecord(NdefRecord record) throws FormatException {
- byte[] payload = record.getPayload();
- if (payload.length < 2) {
- throw new FormatException("Payload is not a valid URI (missing prefix)");
+ public String toMimeType() {
+ switch (mTnf) {
+ case NdefRecord.TNF_WELL_KNOWN:
+ if (Arrays.equals(mType, NdefRecord.RTD_TEXT)) {
+ return "text/plain";
+ }
+ break;
+ case NdefRecord.TNF_MIME_MEDIA:
+ String mimeType = new String(mType, Charsets.US_ASCII);
+ return Intent.normalizeMimeType(mimeType);
}
+ return null;
+ }
- /*
- * payload[0] contains the URI Identifier Code, per the
- * NFC Forum "URI Record Type Definition" section 3.2.2.
- *
- * payload[1]...payload[payload.length - 1] contains the rest of
- * the URI.
- */
- int prefixIndex = (payload[0] & 0xff);
- if (prefixIndex < 0 || prefixIndex >= URI_PREFIX_MAP.length) {
- throw new FormatException("Payload is not a valid URI (invalid prefix)");
+ /**
+ * Map this record to a URI, or return null if it cannot be mapped.<p>
+ * Currently this method considers the following to be URI records:
+ * <ul>
+ * <li>{@link #TNF_ABSOLUTE_URI} records.</li>
+ * <li>{@link #TNF_WELL_KNOWN} with a type of {@link #RTD_URI}.</li>
+ * <li>{@link #TNF_WELL_KNOWN} with a type of {@link #RTD_SMART_POSTER}
+ * and containing a URI record in the NDEF message nested in the payload.
+ * </li>
+ * <li>{@link #TNF_EXTERNAL_TYPE} records.</li>
+ * </ul>
+ * If this is not a URI record by the above rules, then null is returned.<p>
+ * This method does not perform validation that the URI is
+ * actually valid: it always attempts to create and return a URI if
+ * this record appears to be a URI record by the above rules.<p>
+ * The returned URI will be normalized to have a lower case scheme
+ * using {@link Uri#normalize}.<p>
+ *
+ * @return URI, or null if this is not a URI record
+ */
+ public Uri toUri() {
+ return toUri(false);
+ }
+
+ private Uri toUri(boolean inSmartPoster) {
+ switch (mTnf) {
+ case TNF_WELL_KNOWN:
+ if (Arrays.equals(mType, RTD_SMART_POSTER) && !inSmartPoster) {
+ try {
+ // check payload for a nested NDEF Message containing a URI
+ NdefMessage nestedMessage = new NdefMessage(mPayload);
+ for (NdefRecord nestedRecord : nestedMessage.getRecords()) {
+ Uri uri = nestedRecord.toUri(true);
+ if (uri != null) {
+ return uri;
+ }
+ }
+ } catch (FormatException e) { }
+ } else if (Arrays.equals(mType, RTD_URI)) {
+ return parseWktUri().normalize();
+ }
+ break;
+
+ case TNF_ABSOLUTE_URI:
+ Uri uri = Uri.parse(new String(mType, Charsets.UTF_8));
+ return uri.normalize();
+
+ case TNF_EXTERNAL_TYPE:
+ if (inSmartPoster) {
+ break;
+ }
+ return Uri.parse("vnd.android.nfc://ext/" + new String(mType, Charsets.US_ASCII));
}
- String prefix = URI_PREFIX_MAP[prefixIndex];
- byte[] fullUri = concat(prefix.getBytes(Charsets.UTF_8),
- Arrays.copyOfRange(payload, 1, payload.length));
- return Uri.parse(new String(fullUri, Charsets.UTF_8));
+ return null;
}
- private static byte[] concat(byte[]... arrays) {
- int length = 0;
- for (byte[] array : arrays) {
- length += array.length;
+ /**
+ * Return complete URI of {@link #TNF_WELL_KNOWN}, {@link #RTD_URI} records.
+ * @return complete URI, or null if invalid
+ */
+ private Uri parseWktUri() {
+ if (mPayload.length < 2) {
+ return null;
}
- byte[] result = new byte[length];
- int pos = 0;
- for (byte[] array : arrays) {
- System.arraycopy(array, 0, result, pos, array.length);
- pos += array.length;
+
+ // payload[0] contains the URI Identifier Code, as per
+ // NFC Forum "URI Record Type Definition" section 3.2.2.
+ int prefixIndex = (mPayload[0] & (byte)0xFF);
+ if (prefixIndex < 0 || prefixIndex >= URI_PREFIX_MAP.length) {
+ return null;
}
- return result;
+ String prefix = URI_PREFIX_MAP[prefixIndex];
+ String suffix = new String(Arrays.copyOfRange(mPayload, 1, mPayload.length),
+ Charsets.UTF_8);
+ return Uri.parse(prefix + suffix);
}
/**
- * Main parsing method.<p>
+ * Main record parsing method.<p>
* Expects NdefMessage to begin immediately, allows trailing data.<p>
* Currently has strict validation of all fields as per NDEF 1.0
* specification section 2.5. We will attempt to keep this as strict as
@@ -902,42 +1011,4 @@ public final class NdefRecord implements Parcelable {
}
return s;
}
-
- /** Ensure valid 'DNS-char' as per RFC2234 */
- private static void ensureValidDomain(byte[] bs) {
- for (int i = 0; i < bs.length; i++) {
- byte b = bs[i];
- if ((b >= 'A' && b <= 'Z') ||
- (b >= 'a' && b <= 'z') ||
- (b >= '0' && b <= '9') ||
- b == '.' || b == '-') {
- continue;
- }
- throw new IllegalArgumentException("invalid character in domain");
- }
- }
-
- /** Ensure valid 'WKT-char' as per RFC2234 */
- private static void ensureValidWkt(byte[] bs) {
- for (int i = 0; i < bs.length; i++) {
- byte b = bs[i];
- if ((b >= 'A' && b <= 'Z') ||
- (b >= 'a' && b <= 'z') ||
- (b >= '0' && b <= '9') ||
- b == '(' || b == ')' || b == '+' || b == ',' || b == '-' ||
- b == ':' || b == '=' || b == '@' || b == ';' || b == '$' ||
- b == '_' || b == '!' || b == '*' || b == '\'' || b == '.') {
- continue;
- }
- throw new IllegalArgumentException("invalid character in type");
- }
- }
-
- private static void toLowerCase(byte[] b) {
- for (int i = 0; i < b.length; i++) {
- if (b[i] >= 'A' && b[i] <= 'Z') {
- b[i] += 0x20;
- }
- }
- }
}
diff --git a/core/java/android/nfc/NfcAdapter.java b/core/java/android/nfc/NfcAdapter.java
index 53a0341..224a8bc 100644
--- a/core/java/android/nfc/NfcAdapter.java
+++ b/core/java/android/nfc/NfcAdapter.java
@@ -66,6 +66,9 @@ public final class NfcAdapter {
* <p>If the tag has an NDEF payload this intent is started before
* {@link #ACTION_TECH_DISCOVERED}. If any activities respond to this intent neither
* {@link #ACTION_TECH_DISCOVERED} or {@link #ACTION_TAG_DISCOVERED} will be started.
+ *
+ * <p>The MIME type or data URI of this intent are normalized before dispatch -
+ * so that MIME, URI scheme and URI host are always lower-case.
*/
@SdkConstant(SdkConstantType.ACTIVITY_INTENT_ACTION)
public static final String ACTION_NDEF_DISCOVERED = "android.nfc.action.NDEF_DISCOVERED";
@@ -151,9 +154,13 @@ public final class NfcAdapter {
public static final String EXTRA_TAG = "android.nfc.extra.TAG";
/**
- * Optional extra containing an array of {@link NdefMessage} present on the discovered tag for
- * the {@link #ACTION_NDEF_DISCOVERED}, {@link #ACTION_TECH_DISCOVERED}, and
- * {@link #ACTION_TAG_DISCOVERED} intents.
+ * Extra containing an array of {@link NdefMessage} present on the discovered tag.<p>
+ * This extra is mandatory for {@link #ACTION_NDEF_DISCOVERED} intents,
+ * and optional for {@link #ACTION_TECH_DISCOVERED}, and
+ * {@link #ACTION_TAG_DISCOVERED} intents.<p>
+ * When this extra is present there will always be at least one
+ * {@link NdefMessage} element. Most NDEF tags have only one NDEF message,
+ * but we use an array for future compatibility.
*/
public static final String EXTRA_NDEF_MESSAGES = "android.nfc.extra.NDEF_MESSAGES";
@@ -386,10 +393,10 @@ public final class NfcAdapter {
*/
@Deprecated
public static NfcAdapter getDefaultAdapter() {
- // introduce in API version 9 (GB 2.3)
+ // introduced in API version 9 (GB 2.3)
// deprecated in API version 10 (GB 2.3.3)
// removed from public API in version 16 (ICS MR2)
- // will need to maintain this as a hidden API for a while longer...
+ // should maintain as a hidden API for binary compatibility for a little longer
Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " +
"NfcAdapter.getDefaultAdapter(Context) instead", new Exception());
@@ -803,6 +810,7 @@ public final class NfcAdapter {
* @throws IllegalStateException if the Activity has already been paused
* @deprecated use {@link #setNdefPushMessage} instead
*/
+ @Deprecated
public void disableForegroundNdefPush(Activity activity) {
if (activity == null) {
throw new NullPointerException();
@@ -875,6 +883,24 @@ public final class NfcAdapter {
}
/**
+ * Inject a mock NFC tag.<p>
+ * Used for testing purposes.
+ * <p class="note">Requires the
+ * {@link android.Manifest.permission#WRITE_SECURE_SETTINGS} permission.
+ * @hide
+ */
+ public void dispatch(Tag tag, NdefMessage message) {
+ if (tag == null) {
+ throw new NullPointerException("tag cannot be null");
+ }
+ try {
+ sService.dispatch(tag, message);
+ } catch (RemoteException e) {
+ attemptDeadServiceRecovery(e);
+ }
+ }
+
+ /**
* @hide
*/
public INfcAdapterExtras getNfcAdapterExtrasInterface() {