diff options
-rw-r--r-- | api/15.txt | 1 | ||||
-rw-r--r-- | api/current.txt | 9 | ||||
-rw-r--r-- | core/java/android/nfc/FormatException.java | 4 | ||||
-rw-r--r-- | core/java/android/nfc/NdefMessage.java | 207 | ||||
-rw-r--r-- | core/java/android/nfc/NdefRecord.java | 790 | ||||
-rw-r--r-- | core/java/android/nfc/NfcAdapter.java | 5 | ||||
-rw-r--r-- | core/jni/Android.mk | 3 | ||||
-rw-r--r-- | core/jni/AndroidRuntime.cpp | 4 | ||||
-rw-r--r-- | core/jni/android_nfc_NdefMessage.cpp | 182 | ||||
-rw-r--r-- | core/jni/android_nfc_NdefRecord.cpp | 183 |
10 files changed, 802 insertions, 586 deletions
@@ -12659,7 +12659,6 @@ package android.nfc { method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], java.lang.String[][]); method public deprecated void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage); method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context); - method public static deprecated android.nfc.NfcAdapter getDefaultAdapter(); method public boolean isEnabled(); method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...); method public void setNdefPushMessageCallback(android.nfc.NfcAdapter.CreateNdefMessageCallback, android.app.Activity, android.app.Activity...); diff --git a/api/current.txt b/api/current.txt index b2f20c7..db3b30d 100644 --- a/api/current.txt +++ b/api/current.txt @@ -12602,10 +12602,12 @@ package android.nfc { public class FormatException extends java.lang.Exception { ctor public FormatException(); ctor public FormatException(java.lang.String); + ctor public FormatException(java.lang.String, java.lang.Throwable); } public final class NdefMessage implements android.os.Parcelable { ctor public NdefMessage(byte[]) throws android.nfc.FormatException; + ctor public NdefMessage(android.nfc.NdefRecord, android.nfc.NdefRecord...); ctor public NdefMessage(android.nfc.NdefRecord[]); method public int describeContents(); method public android.nfc.NdefRecord[] getRecords(); @@ -12616,8 +12618,10 @@ package android.nfc { public final class NdefRecord implements android.os.Parcelable { ctor public NdefRecord(short, byte[], byte[], byte[]); - ctor public NdefRecord(byte[]) throws android.nfc.FormatException; + ctor public deprecated NdefRecord(byte[]) throws android.nfc.FormatException; method public static android.nfc.NdefRecord createApplicationRecord(java.lang.String); + method public static android.nfc.NdefRecord createExternal(java.lang.String, java.lang.String, byte[]); + method public static android.nfc.NdefRecord createMime(java.lang.String, byte[]); method public static android.nfc.NdefRecord createUri(android.net.Uri); method public static android.nfc.NdefRecord createUri(java.lang.String); method public int describeContents(); @@ -12625,7 +12629,7 @@ package android.nfc { method public byte[] getPayload(); method public short getTnf(); method public byte[] getType(); - method public byte[] toByteArray(); + method public deprecated byte[] toByteArray(); method public void writeToParcel(android.os.Parcel, int); field public static final android.os.Parcelable.Creator CREATOR; field public static final byte[] RTD_ALTERNATIVE_CARRIER; @@ -12650,7 +12654,6 @@ package android.nfc { method public void enableForegroundDispatch(android.app.Activity, android.app.PendingIntent, android.content.IntentFilter[], java.lang.String[][]); method public deprecated void enableForegroundNdefPush(android.app.Activity, android.nfc.NdefMessage); method public static android.nfc.NfcAdapter getDefaultAdapter(android.content.Context); - method public static deprecated android.nfc.NfcAdapter getDefaultAdapter(); method public boolean isEnabled(); method public boolean isNdefPushEnabled(); method public void setNdefPushMessage(android.nfc.NdefMessage, android.app.Activity, android.app.Activity...); diff --git a/core/java/android/nfc/FormatException.java b/core/java/android/nfc/FormatException.java index 7045a03..a57de1e 100644 --- a/core/java/android/nfc/FormatException.java +++ b/core/java/android/nfc/FormatException.java @@ -24,4 +24,8 @@ public class FormatException extends Exception { public FormatException(String message) { super(message); } + + public FormatException(String message, Throwable e) { + super(message, e); + } } diff --git a/core/java/android/nfc/NdefMessage.java b/core/java/android/nfc/NdefMessage.java index c79fabf..38bc16d 100644 --- a/core/java/android/nfc/NdefMessage.java +++ b/core/java/android/nfc/NdefMessage.java @@ -16,90 +16,170 @@ package android.nfc; +import java.nio.ByteBuffer; +import java.util.Arrays; + import android.os.Parcel; import android.os.Parcelable; + /** - * Represents an NDEF (NFC Data Exchange Format) data message that contains one or more {@link - * NdefRecord}s. - * <p>An NDEF message includes "records" that can contain different sets of data, such as - * MIME-type media, a URI, or one of the supported RTD types (see {@link NdefRecord}). An NDEF - * message always contains zero or more NDEF records.</p> - * <p>This is an immutable data class. + * Represents an immutable NDEF Message. + * <p> + * NDEF (NFC Data Exchange Format) is a light-weight binary format, + * used to encapsulate typed data. It is specified by the NFC Forum, + * for transmission and storage with NFC, however it is transport agnostic. + * <p> + * NDEF defines messages and records. An NDEF Record contains + * typed data, such as MIME-type media, a URI, or a custom + * application payload. An NDEF Message is a container for + * one or more NDEF Records. + * <p> + * When an Android device receives an NDEF Message + * (for example by reading an NFC tag) it processes it through + * a dispatch mechanism to determine an activity to launch. + * The type of the <em>first</em> record in the message has + * special importance for message dispatch, so design this record + * carefully. + * <p> + * Use {@link #NdefMessage(byte[])} to construct an NDEF Message from + * binary data, or {@link #NdefMessage(NdefRecord[])} to + * construct from one or more {@link NdefRecord}s. + * <p class="note"> + * {@link NdefMessage} and {@link NdefRecord} implementations are + * always available, even on Android devices that do not have NFC hardware. + * <p class="note"> + * {@link NdefRecord}s are intended to be immutable (and thread-safe), + * however they may contain mutable fields. So take care not to modify + * mutable fields passed into constructors, or modify mutable fields + * obtained by getter methods, unless such modification is explicitly + * marked as safe. + * + * @see NfcAdapter#ACTION_NDEF_DISCOVERED + * @see NdefRecord */ public final class NdefMessage implements Parcelable { - private static final byte FLAG_MB = (byte) 0x80; - private static final byte FLAG_ME = (byte) 0x40; - private final NdefRecord[] mRecords; /** - * Create an NDEF message from raw bytes. - * <p> - * Validation is performed to make sure the Record format headers are valid, - * and the ID + TYPE + PAYLOAD fields are of the correct size. - * @throws FormatException + * Construct an NDEF Message by parsing raw bytes.<p> + * Strict validation of the NDEF binary structure is performed: + * there must be at least one record, every record flag must + * be correct, and the total length of the message must match + * the length of the input data.<p> + * This parser can handle chunked records, and converts them + * into logical {@link NdefRecord}s within the message.<p> + * Once the input data has been parsed to one or more logical + * records, basic validation of the tnf, type, id, and payload fields + * of each record is performed, as per the documentation on + * on {@link NdefRecord#NdefRecord(short, byte[], byte[], byte[])}<p> + * If either strict validation of the binary format fails, or + * basic validation during record construction fails, a + * {@link FormatException} is thrown<p> + * Deep inspection of the type, id and payload fields of + * each record is not performed, so it is possible to parse input + * that has a valid binary format and confirms to the basic + * validation requirements of + * {@link NdefRecord#NdefRecord(short, byte[], byte[], byte[])}, + * but fails more strict requirements as specified by the + * NFC Forum. + * + * <p class="note"> + * It is safe to re-use the data byte array after construction: + * this constructor will make an internal copy of all necessary fields. + * + * @param data raw bytes to parse + * @throws FormatException if the data cannot be parsed */ public NdefMessage(byte[] data) throws FormatException { - mRecords = null; // stop compiler complaints about final field - if (parseNdefMessage(data) == -1) { - throw new FormatException("Error while parsing NDEF message"); + if (data == null) { + throw new NullPointerException("null data"); + } + ByteBuffer buffer = ByteBuffer.wrap(data); + + mRecords = NdefRecord.parse(buffer, false); + + if (buffer.remaining() > 0) { + throw new FormatException("trailing data"); } } /** - * Create an NDEF message from NDEF records. + * Construct an NDEF Message from one or more NDEF Records. + * + * @param record first record (mandatory) + * @param records additional records (optional) + */ + public NdefMessage(NdefRecord record, NdefRecord ... records) { + // validate + if (record == null) { + throw new NullPointerException("record cannot be null"); + } + for (NdefRecord r : records) { + if (r == null) { + throw new NullPointerException("record cannot be null"); + } + } + + mRecords = new NdefRecord[1 + records.length]; + mRecords[0] = record; + System.arraycopy(records, 0, mRecords, 1, records.length); + } + + /** + * Construct an NDEF Message from one or more NDEF Records. + * + * @param records one or more records */ public NdefMessage(NdefRecord[] records) { - mRecords = new NdefRecord[records.length]; - System.arraycopy(records, 0, mRecords, 0, records.length); + // validate + if (records.length < 1) { + throw new IllegalArgumentException("must have at least one record"); + } + for (NdefRecord r : records) { + if (r == null) { + throw new NullPointerException("records cannot contain null"); + } + } + + mRecords = records; } /** - * Get the NDEF records inside this NDEF message. + * Get the NDEF Records inside this NDEF Message.<p> + * An NDEF Message always has one or more NDEF Records. * - * @return array of zero or more NDEF records. + * @return array of one or more NDEF records. */ public NdefRecord[] getRecords() { - return mRecords.clone(); + return mRecords; } /** - * Returns a byte array representation of this entire NDEF message. + * Return this NDEF MEssage as raw bytes.<p> + * The NDEF Message is formatted as per the NDEF 1.0 specification, + * and the byte array is suitable for network transmission or storage + * in an NFC Forum NDEF compatible tag.<p> + * This method will not chunk any records, and will always use the + * short record (SR) format and omit the identifier field when possible. + * + * @return NDEF Message in binary format */ public byte[] toByteArray() { - //TODO: allocate the byte array once, copy each record once - //TODO: process MB and ME flags outside loop - if ((mRecords == null) || (mRecords.length == 0)) - return new byte[0]; - - byte[] msg = {}; - - for (int i = 0; i < mRecords.length; i++) { - byte[] record = mRecords[i].toByteArray(); - byte[] tmp = new byte[msg.length + record.length]; - - /* Make sure the Message Begin flag is set only for the first record */ - if (i == 0) { - record[0] |= FLAG_MB; - } else { - record[0] &= ~FLAG_MB; - } - - /* Make sure the Message End flag is set only for the last record */ - if (i == (mRecords.length - 1)) { - record[0] |= FLAG_ME; - } else { - record[0] &= ~FLAG_ME; - } + int length = 0; + for (NdefRecord r : mRecords) { + length += r.getByteLength(); + } - System.arraycopy(msg, 0, tmp, 0, msg.length); - System.arraycopy(record, 0, tmp, msg.length, record.length); + ByteBuffer buffer = ByteBuffer.allocate(length); - msg = tmp; + for (int i=0; i<mRecords.length; i++) { + boolean mb = (i == 0); // first record + boolean me = (i == mRecords.length - 1); // last record + mRecords[i].writeToByteBuffer(buffer, mb, me); } - return msg; + return buffer.array(); } @Override @@ -128,5 +208,26 @@ public final class NdefMessage implements Parcelable { } }; - private native int parseNdefMessage(byte[] data); + @Override + public int hashCode() { + return Arrays.hashCode(mRecords); + } + + /** + * Returns true if the specified NDEF Message contains + * identical NDEF Records. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + NdefMessage other = (NdefMessage) obj; + return Arrays.equals(mRecords, other.mRecords); + } + + @Override + public String toString() { + return "NdefMessage " + Arrays.toString(mRecords); + } }
\ No newline at end of file diff --git a/core/java/android/nfc/NdefRecord.java b/core/java/android/nfc/NdefRecord.java index 26571ff..b4c488b 100644 --- a/core/java/android/nfc/NdefRecord.java +++ b/core/java/android/nfc/NdefRecord.java @@ -19,80 +19,139 @@ package android.nfc; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; - -import java.lang.UnsupportedOperationException; -import java.nio.charset.Charset; +import java.nio.BufferUnderflowException; +import java.nio.ByteBuffer; import java.nio.charset.Charsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; /** - * Represents a logical (unchunked) NDEF (NFC Data Exchange Format) record. - * <p>An NDEF record always contains: + * Represents an immutable NDEF Record. + * <p> + * NDEF (NFC Data Exchange Format) is a light-weight binary format, + * used to encapsulate typed data. It is specified by the NFC Forum, + * for transmission and storage with NFC, however it is transport agnostic. + * <p> + * NDEF defines messages and records. An NDEF Record contains + * typed data, such as MIME-type media, a URI, or a custom + * application payload. An NDEF Message is a container for + * one or more NDEF Records. + * <p> + * This class represents logical (complete) NDEF Records, and can not be + * used to represent chunked (partial) NDEF Records. However + * {@link NdefMessage#NdefMessage(byte[])} can be used to parse a message + * containing chunked records, and will return a message with unchunked + * (complete) records. + * <p> + * A logical NDEF Record always contains a 3-bit TNF (Type Name Field) + * that provides high level typing for the rest of the record. The + * remaining fields are variable length and not always present: * <ul> - * <li>3-bit TNF (Type Name Format) field: Indicates how to interpret the type field - * <li>Variable length type: Describes the record format - * <li>Variable length ID: A unique identifier for the record - * <li>Variable length payload: The actual data payload + * <li><em>type</em>: detailed typing for the payload</li> + * <li><em>id</em>: identifier meta-data, not commonly used</li> + * <li><em>payload</em>: the actual payload</li> * </ul> - * <p>The underlying record - * representation may be chunked across several NDEF records when the payload is - * large. - * <p>This is an immutable data class. + * <p> + * Helpers such as {@link NdefRecord#createUri}, {@link NdefRecord#createMime} + * and {@link NdefRecord#createExternal} are included to create well-formatted + * NDEF Records with correctly set tnf, type, id and payload fields, please + * use these helpers whenever possible. + * <p> + * Use the constructor {@link #NdefRecord(short, byte[], byte[], byte[])} + * if you know what you are doing and what to set the fields individually. + * Only basic validation is performed with this constructor, so it is possible + * to create records that do not confirm to the strict NFC Forum + * specifications. + * <p> + * The binary representation of an NDEF Record includes additional flags to + * indicate location with an NDEF message, provide support for chunking of + * NDEF records, and to pack optional fields. This class does not expose + * those details. To write an NDEF Record as binary you must first put it + * into an @{link NdefMessage}, then call {@link NdefMessage#toByteArray()}. + * <p class="note"> + * {@link NdefMessage} and {@link NdefRecord} implementations are + * always available, even on Android devices that do not have NFC hardware. + * <p class="note"> + * {@link NdefRecord}s are intended to be immutable (and thread-safe), + * however they may contain mutable fields. So take care not to modify + * mutable fields passed into constructors, or modify mutable fields + * obtained by getter methods, unless such modification is explicitly + * marked as safe. + * + * @see NfcAdapter#ACTION_NDEF_DISCOVERED + * @see NdefMessage */ public final class NdefRecord implements Parcelable { /** - * Indicates no type, id, or payload is associated with this NDEF Record. - * <p> - * Type, id and payload fields must all be empty to be a valid TNF_EMPTY - * record. + * Indicates the record is empty.<p> + * Type, id and payload fields are empty in a {@literal TNF_EMPTY} record. */ public static final short TNF_EMPTY = 0x00; /** - * Indicates the type field uses the RTD type name format. + * Indicates the type field contains a well-known RTD type name.<p> + * Use this tnf with RTD types such as {@link #RTD_TEXT}, {@link #RTD_URI}. * <p> - * Use this TNF with RTD types such as RTD_TEXT, RTD_URI. + * The RTD type name format is specified in NFCForum-TS-RTD_1.0. + * + * @see #RTD_URI + * @see #RTD_TEXT + * @see #RTD_SMART_POSTER + * @see #createUri */ public static final short TNF_WELL_KNOWN = 0x01; /** - * Indicates the type field contains a value that follows the media-type BNF - * construct defined by RFC 2046. + * Indicates the type field contains a media-type BNF + * construct, defined by RFC 2046.<p> + * Use this with MIME type names such as {@literal "image/jpeg"}, or + * using the helper {@link #createMime}. + * + * @see #createMime */ public static final short TNF_MIME_MEDIA = 0x02; /** - * Indicates the type field contains a value that follows the absolute-URI - * BNF construct defined by RFC 3986. + * Indicates the type field contains an absolute-URI + * BNF construct defined by RFC 3986.<p> + * When creating new records prefer {@link #createUri}, + * since it offers more compact URI encoding + * ({@literal #RTD_URI} allows compression of common URI prefixes). + * + * @see #createUri */ public static final short TNF_ABSOLUTE_URI = 0x03; /** - * Indicates the type field contains a value that follows the RTD external - * name specification. + * Indicates the type field contains an external type name.<p> + * Used to encode custom payloads. When creating new records + * use the helper {@link #createExternal}.<p> + * The external-type RTD format is specified in NFCForum-TS-RTD_1.0.<p> * <p> * Note this TNF should not be used with RTD_TEXT or RTD_URI constants. * Those are well known RTD constants, not external RTD constants. + * + * @see #createExternal */ public static final short TNF_EXTERNAL_TYPE = 0x04; /** - * Indicates the payload type is unknown. - * <p> - * This is similar to the "application/octet-stream" MIME type. The payload - * type is not explicitly encoded within the NDEF Message. + * Indicates the payload type is unknown.<p> + * NFC Forum explains this should be treated similarly to the + * "application/octet-stream" MIME type. The payload + * type is not explicitly encoded within the record. * <p> - * The type field must be empty to be a valid TNF_UNKNOWN record. + * The type field is empty in an {@literal TNF_UNKNOWN} record. */ public static final short TNF_UNKNOWN = 0x05; /** * Indicates the payload is an intermediate or final chunk of a chunked - * NDEF Record. - * <p> - * The payload type is specified in the first chunk, and subsequent chunks - * must use TNF_UNCHANGED with an empty type field. TNF_UNCHANGED must not - * be used in any other situation. + * NDEF Record.<p> + * {@literal TNF_UNCHANGED} can not be used with this class + * since all {@link NdefRecord}s are already unchunked, however they + * may appear in the binary format. */ public static final short TNF_UNCHANGED = 0x06; @@ -106,42 +165,49 @@ public final class NdefRecord implements Parcelable { public static final short TNF_RESERVED = 0x07; /** - * RTD Text type. For use with TNF_WELL_KNOWN. + * RTD Text type. For use with {@literal TNF_WELL_KNOWN}. + * @see #TNF_WELL_KNOWN */ public static final byte[] RTD_TEXT = {0x54}; // "T" /** - * RTD URI type. For use with TNF_WELL_KNOWN. + * RTD URI type. For use with {@literal TNF_WELL_KNOWN}. + * @see #TNF_WELL_KNOWN */ public static final byte[] RTD_URI = {0x55}; // "U" /** - * RTD Smart Poster type. For use with TNF_WELL_KNOWN. + * RTD Smart Poster type. For use with {@literal TNF_WELL_KNOWN}. + * @see #TNF_WELL_KNOWN */ public static final byte[] RTD_SMART_POSTER = {0x53, 0x70}; // "Sp" /** - * RTD Alternative Carrier type. For use with TNF_WELL_KNOWN. + * RTD Alternative Carrier type. For use with {@literal TNF_WELL_KNOWN}. + * @see #TNF_WELL_KNOWN */ public static final byte[] RTD_ALTERNATIVE_CARRIER = {0x61, 0x63}; // "ac" /** - * RTD Handover Carrier type. For use with TNF_WELL_KNOWN. + * RTD Handover Carrier type. For use with {@literal TNF_WELL_KNOWN}. + * @see #TNF_WELL_KNOWN */ public static final byte[] RTD_HANDOVER_CARRIER = {0x48, 0x63}; // "Hc" /** - * RTD Handover Request type. For use with TNF_WELL_KNOWN. + * RTD Handover Request type. For use with {@literal TNF_WELL_KNOWN}. + * @see #TNF_WELL_KNOWN */ public static final byte[] RTD_HANDOVER_REQUEST = {0x48, 0x72}; // "Hr" /** - * RTD Handover Select type. For use with TNF_WELL_KNOWN. + * RTD Handover Select type. For use with {@literal TNF_WELL_KNOWN}. + * @see #TNF_WELL_KNOWN */ public static final byte[] RTD_HANDOVER_SELECT = {0x48, 0x73}; // "Hs" /** - * RTD Android app type. For use with TNF_EXTERNAL. + * RTD Android app type. For use with {@literal TNF_EXTERNAL}. * <p> * The payload of a record with type RTD_ANDROID_APP * should be the package name identifying an application. @@ -161,8 +227,7 @@ public final class NdefRecord implements Parcelable { private static final byte FLAG_IL = (byte) 0x08; /** - * NFC Forum "URI Record Type Definition" - * + * NFC Forum "URI Record Type Definition"<p> * This is a mapping of "URI Identifier Codes" to URI string prefixes, * per section 3.2.2 of the NFC Forum URI Record Type Definition document. */ @@ -204,84 +269,247 @@ public final class NdefRecord implements Parcelable { "urn:epc:", // 0x22 }; - private final byte mFlags; + private static final int MAX_PAYLOAD_SIZE = 10 * (1 << 20); // 10 MB payload limit + + private static final byte[] EMPTY_BYTE_ARRAY = new byte[0]; + private final short mTnf; private final byte[] mType; private final byte[] mId; private final byte[] mPayload; /** - * Construct an NDEF Record. + * Create a new Android Application Record (AAR). + * <p> + * This record indicates to other Android devices the package + * that should be used to handle the entire NDEF message. + * You can embed this record anywhere into your message + * to ensure that the intended package receives the message. + * <p> + * When an Android device dispatches an {@link NdefMessage} + * containing one or more Android application records, + * the applications contained in those records will be the + * preferred target for the {@link NfcAdapter#ACTION_NDEF_DISCOVERED} + * intent, in the order in which they appear in the message. + * This dispatch behavior was first added to Android in + * Ice Cream Sandwich. + * <p> + * If none of the applications have a are installed on the device, + * a Market link will be opened to the first application. * <p> - * Applications should not attempt to manually chunk NDEF Records - the - * implementation of android.nfc will automatically chunk an NDEF Record - * when necessary (and only present a single logical NDEF Record to the - * application). So applications should not use TNF_UNCHANGED. + * Note that Android application records do not overrule + * applications that have called + * {@link NfcAdapter#enableForegroundDispatch}. * - * @param tnf a 3-bit TNF constant - * @param type byte array, containing zero to 255 bytes, must not be null - * @param id byte array, containing zero to 255 bytes, must not be null - * @param payload byte array, containing zero to (2 ** 32 - 1) bytes, - * must not be null + * @param packageName Android package name + * @return Android application NDEF record */ - public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload) { - /* New NDEF records created by applications will have FLAG_MB|FLAG_ME - * set by default; when multiple records are stored in a - * {@link NdefMessage}, these flags will be corrected when the {@link NdefMessage} - * is serialized to bytes. - */ - this(tnf, type, id, payload, (byte)(FLAG_MB|FLAG_ME)); + public static NdefRecord createApplicationRecord(String packageName) { + if (packageName.length() == 0) { + throw new IllegalArgumentException("empty package name"); + } + return new NdefRecord(TNF_EXTERNAL_TYPE, RTD_ANDROID_APP, null, + packageName.getBytes(Charsets.UTF_8)); } /** - * @hide + * 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 uri URI to encode. + * @return an NDEF Record containing the URI + * @throws IllegalArugmentException if a valid record cannot be created */ - /*package*/ NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload, byte flags) { - /* check arguments */ - if ((type == null) || (id == null) || (payload == null)) { - throw new IllegalArgumentException("Illegal null argument"); + public static NdefRecord createUri(Uri uri) { + return createUri(uri.toString()); + } + + /** + * 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"); } - if (tnf < 0 || tnf > 0x07) { - throw new IllegalArgumentException("TNF out of range " + tnf); + byte prefix = 0; + for (int i = 1; i < URI_PREFIX_MAP.length; i++) { + if (uriString.startsWith(URI_PREFIX_MAP[i])) { + prefix = (byte) i; + uriString = uriString.substring(URI_PREFIX_MAP[i].length()); + break; + } + } + byte[] uriBytes = uriString.getBytes(Charsets.UTF_8); + byte[] recordBytes = new byte[uriBytes.length + 1]; + recordBytes[0] = prefix; + System.arraycopy(uriBytes, 0, recordBytes, 1, uriBytes.length); + return new NdefRecord(TNF_WELL_KNOWN, RTD_URI, null, recordBytes); + } + + /** + * 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> + * 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 + * NdefRecord. + * + * @param mimeType MIME type, expects US-ASCII characters only + * @param mimeData MIME data as bytes + * @return an NDEF Record containing the MIME-typed data + * @throws IllegalArugmentException if a valid record cannot be created + */ + public static NdefRecord createMime(String mimeType, byte[] mimeData) { + if (mimeType.length() == 0) { + throw new IllegalArgumentException("empty mimeType"); } - /* Determine if it is a short record */ - if(payload.length < 0xFF) { - flags |= FLAG_SR; + return new NdefRecord(TNF_MIME_MEDIA, mimeType.getBytes(Charsets.US_ASCII), null, + mimeData); + } + + /** + * Create a new NDEF Record containing external (application-specific) data.<p> + * Use this method to encode application specific data into an NDEF Record. + * 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> + * 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 + * 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 + */ + 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); + + byte[] b = new byte[byteDomain.length + 1 + byteType.length]; + System.arraycopy(byteDomain, 0, b, 0, byteDomain.length); + b[byteDomain.length] = ':'; + System.arraycopy(byteType, 0, b, byteDomain.length + 1, byteType.length); + + return new NdefRecord(TNF_EXTERNAL_TYPE, b, null, data); + } - /* Determine if an id is present */ - if(id.length != 0) { - flags |= FLAG_IL; + /** + * Construct an NDEF Record from its component fields.<p> + * Recommend to use helpers such as {#createUri} or + * {{@link #createExternal} where possible, since they perform + * stricter validation that the record is correctly formatted + * as per NDEF specifications. However if you know what you are + * doing then this constructor offers the most flexibility.<p> + * An {@link NdefRecord} represents a logical (complete) + * record, and cannot represent NDEF Record chunks.<p> + * Basic validation of the tnf, type, id and payload is performed + * as per the following rules: + * <ul> + * <li>The tnf paramter must be a 3-bit value.</li> + * <li>Records with a tnf of {@link #TNF_EMPTY} cannot have a type, + * id or payload.</li> + * <li>Records with a tnf of {@link #TNF_UNKNOWN} or {@literal 0x07} + * cannot have a type.</li> + * <li>Records with a tnf of {@link #TNF_UNCHANGED} are not allowed + * since this class only represents complete (unchunked) records.</li> + * </ul> + * This minimal validation is specified by + * NFCForum-TS-NDEF_1.0 section 3.2.6 (Type Name Format).<p> + * If any of the above validation + * steps fail then {@link IllegalArgumentException} is thrown.<p> + * Deep inspection of the type, id and payload fields is not + * performed, so it is possible to create NDEF Records + * that conform to section 3.2.6 + * but fail other more strict NDEF specification requirements. For + * example, the payload may be invalid given the tnf and type. + * <p> + * To omit a type, id or payload field, set the parameter to an + * empty byte array or null. + * + * @param tnf a 3-bit TNF constant + * @param type byte array, containing zero to 255 bytes, or null + * @param id byte array, containing zero to 255 bytes, or null + * @param payload byte array, containing zero to (2 ** 32 - 1) bytes, + * or null + * @throws IllegalArugmentException if a valid record cannot be created + */ + public NdefRecord(short tnf, byte[] type, byte[] id, byte[] payload) { + /* convert nulls */ + if (type == null) type = EMPTY_BYTE_ARRAY; + if (id == null) id = EMPTY_BYTE_ARRAY; + if (payload == null) payload = EMPTY_BYTE_ARRAY; + + String message = validateTnf(tnf, type, id, payload); + if (message != null) { + throw new IllegalArgumentException(message); } - mFlags = flags; mTnf = tnf; - mType = type.clone(); - mId = id.clone(); - mPayload = payload.clone(); + mType = type; + mId = id; + mPayload = payload; } /** - * Construct an NDEF Record from raw bytes. - * <p> - * Validation is performed to make sure the header is valid, and that - * the id, type and payload sizes appear to be valid. + * Construct an NDEF Record from raw bytes.<p> + * This method is deprecated, use {@link NdefMessage#NdefMessage(byte[])} + * instead. This is because it does not make sense to parse a record: + * the NDEF binary format is only defined for a message, and the + * record flags MB and ME do not make sense outside of the context of + * an entire message.<p> + * This implementation will attempt to parse a single record by ignoring + * the MB and ME flags, and otherwise following the rules of + * {@link NdefMessage#NdefMessage(byte[])}.<p> * - * @throws FormatException if the data is not a valid NDEF record + * @param data raw bytes to parse + * @throws FormatException if the data cannot be parsed into a valid record + * @deprecated use {@link NdefMessage#NdefMessage(byte[])} instead. */ + @Deprecated public NdefRecord(byte[] data) throws FormatException { - /* Prevent compiler to complain about unassigned final fields */ - mFlags = 0; - mTnf = 0; - mType = null; - mId = null; - mPayload = null; - /* Perform actual parsing */ - if (parseNdefRecord(data) == -1) { - throw new FormatException("Error while parsing NDEF record"); + ByteBuffer buffer = ByteBuffer.wrap(data); + NdefRecord[] rs = parse(buffer, true); + + if (buffer.remaining() > 0) { + throw new FormatException("data too long"); } + + mTnf = rs[0].mTnf; + mType = rs[0].mType; + mId = rs[0].mId; + mPayload = rs[0].mPayload; } /** @@ -298,6 +526,9 @@ public final class NdefRecord implements Parcelable { * <p> * This should be used in conjunction with the TNF field to determine the * payload format. + * <p> + * Returns an empty byte array if this record + * does not have a type field. */ public byte[] getType() { return mType.clone(); @@ -305,6 +536,9 @@ public final class NdefRecord implements Parcelable { /** * Returns the variable length ID. + * <p> + * Returns an empty byte array if this record + * does not have an id field. */ public byte[] getId() { return mId.clone(); @@ -312,12 +546,34 @@ public final class NdefRecord implements Parcelable { /** * Returns the variable length payload. + * <p> + * Returns an empty byte array if this record + * does not have a payload field. */ public byte[] getPayload() { return mPayload.clone(); } /** + * Return this NDEF Record as a byte array.<p> + * This method is deprecated, use {@link NdefMessage#toByteArray} + * instead. This is because the NDEF binary format is not defined for + * a record outside of the context of a message: the MB and ME flags + * cannot be set without knowing the location inside a message.<p> + * This implementation will attempt to serialize a single record by + * always setting the MB and ME flags (in other words, assume this + * is a single-record NDEF Message).<p> + * + * @deprecated use {@link NdefMessage#toByteArray()} instead + */ + @Deprecated + public byte[] toByteArray() { + ByteBuffer buffer = ByteBuffer.allocate(getByteLength()); + writeToByteBuffer(buffer, true, true); + return buffer.array(); + } + + /** * 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 @@ -347,90 +603,230 @@ public final class NdefRecord implements Parcelable { return Uri.parse(new String(fullUri, Charsets.UTF_8)); } + private static byte[] concat(byte[]... arrays) { + int length = 0; + for (byte[] array : arrays) { + length += array.length; + } + byte[] result = new byte[length]; + int pos = 0; + for (byte[] array : arrays) { + System.arraycopy(array, 0, result, pos, array.length); + pos += array.length; + } + return result; + } + /** - * Creates an Android application NDEF record. - * <p> - * This record indicates to other Android devices the package - * that should be used to handle the rest of the NDEF message. - * You can embed this record anywhere into your NDEF message - * to ensure that the intended package receives the message. - * <p> - * When an Android device dispatches an {@link NdefMessage} - * containing one or more Android application records, - * the applications contained in those records will be the - * preferred target for the NDEF_DISCOVERED intent, in - * the order in which they appear in the {@link NdefMessage}. - * This dispatch behavior was first added to Android in - * Ice Cream Sandwich. - * <p> - * If none of the applications are installed on the device, - * a Market link will be opened to the first application. - * <p> - * Note that Android application records do not overrule - * applications that have called - * {@link NfcAdapter#enableForegroundDispatch}. + * Main 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 + * possible to encourage well-formatted NDEF.<p> + * Always returns 1 or more NdefRecord's, or throws FormatException. * - * @param packageName Android package name - * @return Android application NDEF record + * @param buffer ByteBuffer to read from + * @param ignoreMbMe ignore MB and ME flags, and read only 1 complete record + * @return one or more records + * @throws FormatException on any parsing error */ - public static NdefRecord createApplicationRecord(String packageName) { - return new NdefRecord(TNF_EXTERNAL_TYPE, RTD_ANDROID_APP, new byte[] {}, - packageName.getBytes(Charsets.US_ASCII)); + static NdefRecord[] parse(ByteBuffer buffer, boolean ignoreMbMe) throws FormatException { + List<NdefRecord> records = new ArrayList<NdefRecord>(); + + try { + byte[] type = null; + byte[] id = null; + byte[] payload = null; + ArrayList<byte[]> chunks = new ArrayList<byte[]>(); + boolean inChunk = false; + short chunkTnf = -1; + boolean me = false; + + while (!me) { + byte flag = buffer.get(); + + boolean mb = (flag & NdefRecord.FLAG_MB) != 0; + me = (flag & NdefRecord.FLAG_ME) != 0; + boolean cf = (flag & NdefRecord.FLAG_CF) != 0; + boolean sr = (flag & NdefRecord.FLAG_SR) != 0; + boolean il = (flag & NdefRecord.FLAG_IL) != 0; + short tnf = (short)(flag & 0x07); + + if (!mb && records.size() == 0 && !inChunk && !ignoreMbMe) { + throw new FormatException("expected MB flag"); + } else if (mb && records.size() != 0 && !ignoreMbMe) { + throw new FormatException("unexpected MB flag"); + } else if (inChunk && il) { + throw new FormatException("unexpected IL flag in non-leading chunk"); + } else if (cf && me) { + throw new FormatException("unexpected ME flag in non-trailing chunk"); + } else if (inChunk && tnf != NdefRecord.TNF_UNCHANGED) { + throw new FormatException("expected TNF_UNCHANGED in non-leading chunk"); + } else if (!inChunk && tnf == NdefRecord.TNF_UNCHANGED) { + throw new FormatException("" + + "unexpected TNF_UNCHANGED in first chunk or unchunked record"); + } + + int typeLength = buffer.get() & 0xFF; + long payloadLength = sr ? (buffer.get() & 0xFF) : (buffer.getInt() & 0xFFFFFFFFL); + int idLength = il ? (buffer.get() & 0xFF) : 0; + + if (inChunk && typeLength != 0) { + throw new FormatException("expected zero-length type in non-leading chunk"); + } + + if (!inChunk) { + type = (typeLength > 0 ? new byte[typeLength] : EMPTY_BYTE_ARRAY); + id = (idLength > 0 ? new byte[idLength] : EMPTY_BYTE_ARRAY); + buffer.get(type); + buffer.get(id); + } + + ensureSanePayloadSize(payloadLength); + payload = (payloadLength > 0 ? new byte[(int)payloadLength] : EMPTY_BYTE_ARRAY); + buffer.get(payload); + + if (cf && !inChunk) { + // first chunk + chunks.clear(); + chunkTnf = tnf; + } + if (cf || inChunk) { + // any chunk + chunks.add(payload); + } + if (!cf && inChunk) { + // last chunk, flatten the payload + payloadLength = 0; + for (byte[] p : chunks) { + payloadLength += p.length; + } + ensureSanePayloadSize(payloadLength); + payload = new byte[(int)payloadLength]; + int i = 0; + for (byte[] p : chunks) { + System.arraycopy(p, 0, payload, i, p.length); + i += p.length; + } + tnf = chunkTnf; + } + if (cf) { + // more chunks to come + inChunk = true; + continue; + } else { + inChunk = false; + } + + String error = validateTnf(tnf, type, id, payload); + if (error != null) { + throw new FormatException(error); + } + records.add(new NdefRecord(tnf, type, id, payload)); + if (ignoreMbMe) { // for parsing a single NdefRecord + break; + } + } + } catch (BufferUnderflowException e) { + throw new FormatException("expected more data", e); + } + return records.toArray(new NdefRecord[records.size()]); } - /** - * Creates an NDEF record of well known type URI. - */ - public static NdefRecord createUri(Uri uri) { - return createUri(uri.toString()); + private static void ensureSanePayloadSize(long size) throws FormatException { + if (size > MAX_PAYLOAD_SIZE) { + throw new FormatException( + "payload above max limit: " + size + " > " + MAX_PAYLOAD_SIZE); + } } /** - * Creates an NDEF record of well known type URI. + * Perform simple validation that the tnf is valid.<p> + * Validates the requirements of NFCForum-TS-NDEF_1.0 section + * 3.2.6 (Type Name Format). This just validates that the tnf + * is valid, and that the relevant type, id and payload + * fields are present (or empty) for this tnf. It does not + * perform any deep inspection of the type, id and payload fields.<p> + * Also does not allow TNF_UNCHANGED since this class is only used + * to present logical (unchunked) records. + * + * @return null if valid, or a string error if invalid. */ - public static NdefRecord createUri(String uriString) { - byte prefix = 0x0; - for (int i = 1; i < URI_PREFIX_MAP.length; i++) { - if (uriString.startsWith(URI_PREFIX_MAP[i])) { - prefix = (byte) i; - uriString = uriString.substring(URI_PREFIX_MAP[i].length()); - break; - } + static String validateTnf(short tnf, byte[] type, byte[] id, byte[] payload) { + switch (tnf) { + case TNF_EMPTY: + if (type.length != 0 || id.length != 0 || payload.length != 0) { + return "unexpected data in TNF_EMPTY record"; + } + return null; + case TNF_WELL_KNOWN: + case TNF_MIME_MEDIA: + case TNF_ABSOLUTE_URI: + case TNF_EXTERNAL_TYPE: + return null; + case TNF_UNKNOWN: + case TNF_RESERVED: + if (type.length != 0) { + return "unexpected type field in TNF_UNKNOWN or TNF_RESERVEd record"; + } + return null; + case TNF_UNCHANGED: + return "unexpected TNF_UNCHANGED in first chunk or logical record"; + default: + return String.format("unexpected tnf value: 0x%02x", tnf); } - byte[] uriBytes = uriString.getBytes(Charsets.UTF_8); - byte[] recordBytes = new byte[uriBytes.length + 1]; - recordBytes[0] = prefix; - System.arraycopy(uriBytes, 0, recordBytes, 1, uriBytes.length); - return new NdefRecord(TNF_WELL_KNOWN, RTD_URI, new byte[0], recordBytes); } - private static byte[] concat(byte[]... arrays) { - int length = 0; - for (byte[] array : arrays) { - length += array.length; + /** + * Serialize record for network transmission.<p> + * Uses specified MB and ME flags.<p> + * Does not chunk records. + */ + void writeToByteBuffer(ByteBuffer buffer, boolean mb, boolean me) { + boolean sr = mPayload.length < 256; + boolean il = mId.length > 0; + + byte flags = (byte)((mb ? FLAG_MB : 0) | (me ? FLAG_ME : 0) | + (sr ? FLAG_SR : 0) | (il ? FLAG_IL : 0) | mTnf); + buffer.put(flags); + + buffer.put((byte)mType.length); + if (sr) { + buffer.put((byte)mPayload.length); + } else { + buffer.putInt(mPayload.length); } - byte[] result = new byte[length]; - int pos = 0; - for (byte[] array : arrays) { - System.arraycopy(array, 0, result, pos, array.length); - pos += array.length; + if (il) { + buffer.put((byte)mId.length); } - return result; + + buffer.put(mType); + buffer.put(mId); + buffer.put(mPayload); } /** - * Returns this entire NDEF Record as a byte array. + * Get byte length of serialized record. */ - public byte[] toByteArray() { - return generate(mFlags, mTnf, mType, mId, mPayload); + int getByteLength() { + int length = 3 + mType.length + mId.length + mPayload.length; + + boolean sr = mPayload.length < 256; + boolean il = mId.length > 0; + + if (!sr) length += 3; + if (il) length += 1; + + return length; } + @Override public int describeContents() { return 0; } + @Override public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(mFlags); dest.writeInt(mTnf); dest.writeInt(mType.length); dest.writeByteArray(mType); @@ -442,8 +838,8 @@ public final class NdefRecord implements Parcelable { public static final Parcelable.Creator<NdefRecord> CREATOR = new Parcelable.Creator<NdefRecord>() { + @Override public NdefRecord createFromParcel(Parcel in) { - byte flags = (byte)in.readInt(); short tnf = (short)in.readInt(); int typeLength = in.readInt(); byte[] type = new byte[typeLength]; @@ -455,13 +851,93 @@ public final class NdefRecord implements Parcelable { byte[] payload = new byte[payloadLength]; in.readByteArray(payload); - return new NdefRecord(tnf, type, id, payload, flags); + return new NdefRecord(tnf, type, id, payload); } + @Override public NdefRecord[] newArray(int size) { return new NdefRecord[size]; } }; - private native int parseNdefRecord(byte[] data); - private native byte[] generate(short flags, short tnf, byte[] type, byte[] id, byte[] data); + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(mId); + result = prime * result + Arrays.hashCode(mPayload); + result = prime * result + mTnf; + result = prime * result + Arrays.hashCode(mType); + return result; + } + + /** + * Returns true if the specified NDEF Record contains + * identical tnf, type, id and payload fields. + */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + NdefRecord other = (NdefRecord) obj; + if (!Arrays.equals(mId, other.mId)) return false; + if (!Arrays.equals(mPayload, other.mPayload)) return false; + if (mTnf != other.mTnf) return false; + return Arrays.equals(mType, other.mType); + } + + @Override + public String toString() { + StringBuilder b = new StringBuilder(String.format("NdefRecord tnf=%X", mTnf)); + if (mType.length > 0) b.append(" type=").append(bytesToString(mType)); + if (mId.length > 0) b.append(" id=").append(bytesToString(mId)); + if (mPayload.length > 0) b.append(" payload=").append(bytesToString(mPayload)); + return b.toString(); + } + + private static StringBuilder bytesToString(byte[] bs) { + StringBuilder s = new StringBuilder(); + for (byte b : bs) { + s.append(String.format("%02X", b)); + } + 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 02096f2..f3c884d 100644 --- a/core/java/android/nfc/NfcAdapter.java +++ b/core/java/android/nfc/NfcAdapter.java @@ -376,9 +376,14 @@ public final class NfcAdapter { * for many NFC API methods. Those methods will fail when called on an NfcAdapter * object created from this method.<p> * @deprecated use {@link #getDefaultAdapter(Context)} + * @hide */ @Deprecated public static NfcAdapter getDefaultAdapter() { + // introduce 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... Log.w(TAG, "WARNING: NfcAdapter.getDefaultAdapter() is deprecated, use " + "NfcAdapter.getDefaultAdapter(Context) instead", new Exception()); diff --git a/core/jni/Android.mk b/core/jni/Android.mk index bafee0e..8be1996 100644 --- a/core/jni/Android.mk +++ b/core/jni/Android.mk @@ -76,8 +76,6 @@ LOCAL_SRC_FILES:= \ android_net_TrafficStats.cpp \ android_net_wifi_Wifi.cpp \ android_nio_utils.cpp \ - android_nfc_NdefMessage.cpp \ - android_nfc_NdefRecord.cpp \ android_text_format_Time.cpp \ android_util_AssetManager.cpp \ android_util_Binder.cpp \ @@ -214,7 +212,6 @@ LOCAL_SHARED_LIBRARIES := \ libmedia \ libwpa_client \ libjpeg \ - libnfc_ndef \ libusbhost \ libharfbuzz \ libz \ diff --git a/core/jni/AndroidRuntime.cpp b/core/jni/AndroidRuntime.cpp index 8db7b24..af37454 100644 --- a/core/jni/AndroidRuntime.cpp +++ b/core/jni/AndroidRuntime.cpp @@ -129,8 +129,6 @@ extern int register_android_database_SQLiteQuery(JNIEnv* env); extern int register_android_database_SQLiteStatement(JNIEnv* env); extern int register_android_debug_JNITest(JNIEnv* env); extern int register_android_nio_utils(JNIEnv* env); -extern int register_android_nfc_NdefMessage(JNIEnv *env); -extern int register_android_nfc_NdefRecord(JNIEnv *env); extern int register_android_text_format_Time(JNIEnv* env); extern int register_android_os_Debug(JNIEnv* env); extern int register_android_os_MessageQueue(JNIEnv* env); @@ -1161,8 +1159,6 @@ static const RegJNIRec gRegJNI[] = { REG_JNI(register_android_net_NetworkUtils), REG_JNI(register_android_net_TrafficStats), REG_JNI(register_android_net_wifi_WifiManager), - REG_JNI(register_android_nfc_NdefMessage), - REG_JNI(register_android_nfc_NdefRecord), REG_JNI(register_android_os_MemoryFile), REG_JNI(register_com_android_internal_os_ZygoteInit), REG_JNI(register_android_hardware_Camera), diff --git a/core/jni/android_nfc_NdefMessage.cpp b/core/jni/android_nfc_NdefMessage.cpp deleted file mode 100644 index 41099cb..0000000 --- a/core/jni/android_nfc_NdefMessage.cpp +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include <stdlib.h> - -#include "jni.h" -#include "JNIHelp.h" - -#include "android_nfc.h" - -namespace android { - -static jint android_nfc_NdefMessage_parseNdefMessage(JNIEnv *e, jobject o, - jbyteArray array) -{ - uint16_t status; - uint32_t i; - jbyte *raw_msg; - jsize raw_msg_size; - uint32_t num_of_records = 0; - uint8_t **records = NULL; - uint8_t *is_chunked = NULL; - jint ret = -1; - phFriNfc_NdefRecord_t record; - - jclass record_cls; - jobjectArray records_array; - jmethodID ctor; - - jclass msg_cls; - jfieldID mrecords; - - raw_msg_size = e->GetArrayLength(array); - raw_msg = e->GetByteArrayElements(array, NULL); - if (raw_msg == NULL) - return -1; - - /* Get the number of records in the message so we can allocate buffers */ - TRACE("phFriNfc_NdefRecord_GetRecords(NULL)"); - - status = phFriNfc_NdefRecord_GetRecords((uint8_t *)raw_msg, - (uint32_t)raw_msg_size, NULL, NULL, &num_of_records); - - if (status) { - LOGE("phFriNfc_NdefRecord_GetRecords(NULL) returned 0x%04x", status); - goto end; - } - TRACE("phFriNfc_NdefRecord_GetRecords(NULL) returned 0x%04x, with %d records", status, num_of_records); - - is_chunked = (uint8_t*)malloc(num_of_records); - if (is_chunked == NULL) - goto end; - records = (uint8_t**)malloc(num_of_records * sizeof(uint8_t *)); - if (records == NULL) - goto end; - - /* Now, actually retrieve records position in message */ - TRACE("phFriNfc_NdefRecord_GetRecords()"); - - status = phFriNfc_NdefRecord_GetRecords((uint8_t *)raw_msg, - (uint32_t)raw_msg_size, records, is_chunked, &num_of_records); - - if (status) { - LOGE("phFriNfc_NdefRecord_GetRecords() returned 0x%04x", status); - goto end; - } - TRACE("phFriNfc_NdefRecord_GetRecords() returned 0x%04x, with %d records", status, num_of_records); - - /* Build NDEF records array */ - record_cls = e->FindClass("android/nfc/NdefRecord"); - records_array = e->NewObjectArray((jsize)num_of_records, record_cls, - NULL); - if (records_array == NULL) - goto end; - - ctor = e->GetMethodID(record_cls, "<init>", "(S[B[B[BB)V"); - - for (i = 0; i < num_of_records; i++) { - jbyteArray type, id, payload; - jobject new_record; - - TRACE("phFriNfc_NdefRecord_Parse()"); - - status = phFriNfc_NdefRecord_Parse(&record, records[i]); - - if (status) { - LOGE("phFriNfc_NdefRecord_Parse() returned 0x%04x", status); - goto end; - } - TRACE("phFriNfc_NdefRecord_Parse() returned 0x%04x", status); - - // We don't exactly know what *is* a valid length, but a simple - // sanity check is to make sure that the length of the header - // plus all fields does not exceed raw_msg_size. The min length - // of the header is 3 bytes: TNF, Type Length, Payload Length - // (ID length field is optional!) - uint64_t indicatedMsgLength = 3 + record.TypeLength + record.IdLength + - (uint64_t)record.PayloadLength; - if (indicatedMsgLength > - (uint64_t)raw_msg_size) { - LOGE("phFri_NdefRecord_Parse: invalid length field"); - goto end; - } - - type = e->NewByteArray(record.TypeLength); - if (type == NULL) { - LOGD("NFC_Set Record Type Error\n"); - goto end; - } - - id = e->NewByteArray(record.IdLength); - if(id == NULL) { - LOGD("NFC_Set Record ID Error\n"); - goto end; - } - - payload = e->NewByteArray(record.PayloadLength); - if(payload == NULL) { - LOGD("NFC_Set Record Payload Error\n"); - goto end; - } - - e->SetByteArrayRegion(type, 0, record.TypeLength, - (jbyte *)record.Type); - e->SetByteArrayRegion(id, 0, record.IdLength, - (jbyte *)record.Id); - e->SetByteArrayRegion(payload, 0, record.PayloadLength, - (jbyte *)record.PayloadData); - - new_record = e->NewObject(record_cls, ctor, - (jshort)record.Tnf, type, id, payload, (jbyte)record.Flags); - - e->SetObjectArrayElement(records_array, i, new_record); - - /* Try not to clutter the Java stack too much */ - e->DeleteLocalRef(new_record); - e->DeleteLocalRef(type); - e->DeleteLocalRef(id); - e->DeleteLocalRef(payload); - } - - /* Store built array in our NDEFMessage instance */ - msg_cls = e->GetObjectClass(o); - mrecords = e->GetFieldID(msg_cls, "mRecords", "[Landroid/nfc/NdefRecord;"); - - e->SetObjectField(o, mrecords, (jobject)records_array); - - ret = 0; - -end: - if(is_chunked) - free(is_chunked); - if(records) - free(records); - e->ReleaseByteArrayElements(array, raw_msg, JNI_ABORT); - - return ret; -} - -static JNINativeMethod gMethods[] = { - {"parseNdefMessage", "([B)I", (void *)android_nfc_NdefMessage_parseNdefMessage}, -}; - -int register_android_nfc_NdefMessage(JNIEnv *e) -{ - return jniRegisterNativeMethods(e, "android/nfc/NdefMessage", gMethods, NELEM(gMethods)); -} - -} // namespace android diff --git a/core/jni/android_nfc_NdefRecord.cpp b/core/jni/android_nfc_NdefRecord.cpp deleted file mode 100644 index 67907b6..0000000 --- a/core/jni/android_nfc_NdefRecord.cpp +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "NdefRecord" - -#include <stdlib.h> - -#include "jni.h" -#include "JNIHelp.h" - -#include "android_nfc.h" - -#include <utils/Log.h> - -namespace android { - -static jbyteArray android_nfc_NdefRecord_generate( - JNIEnv *e, jobject o, jshort flags, jshort tnf, jbyteArray type, - jbyteArray id, jbyteArray payload) -{ - uint32_t status; - phFriNfc_NdefRecord_t record; - uint32_t buf_size; - uint32_t record_size; - uint8_t *buf = NULL; - jbyteArray result = NULL; - - /* Prepare NDEF record structure */ - record.Flags = (uint8_t)flags; - record.Tnf = (uint8_t)tnf; - record.TypeLength = (uint32_t)e->GetArrayLength(type); - record.Type = (uint8_t *)e->GetByteArrayElements(type, NULL); - record.IdLength = (uint32_t)e->GetArrayLength(id); - record.Id = (uint8_t *)e->GetByteArrayElements(id, NULL); - record.PayloadLength = (uint32_t)e->GetArrayLength(payload); - record.PayloadData = (uint8_t *)e->GetByteArrayElements(payload, NULL); - - buf_size = record.PayloadLength + record.IdLength + record.TypeLength + 8; - - buf = (uint8_t*)malloc(buf_size); - if (buf == NULL) - goto end; - - TRACE("phFriNfc_NdefRecord_Generate()"); - - status = phFriNfc_NdefRecord_Generate(&record, buf, buf_size, - &record_size); - - if (status) { - LOGE("phFriNfc_NdefRecord_Generate() returned 0x%04x", status); - goto end; - } - TRACE("phFriNfc_NdefRecord_Generate() returned 0x%04x", status); - - result = e->NewByteArray(record_size); - if (result == NULL) - goto end; - - e->SetByteArrayRegion(result, 0, record_size, (jbyte *)buf); - -end: - e->ReleaseByteArrayElements(type, (jbyte *)record.Type, JNI_ABORT); - e->ReleaseByteArrayElements(id, (jbyte *)record.Id, JNI_ABORT); - e->ReleaseByteArrayElements(payload, (jbyte *)record.PayloadData, JNI_ABORT); - - if(buf) - free(buf); - - return result; -} - -static jint android_nfc_NdefRecord_parseNdefRecord(JNIEnv *e, jobject o, - jbyteArray array) -{ - uint16_t status; - jbyte *raw_record; - jsize raw_record_size; - jint ret = -1; - phFriNfc_NdefRecord_t record; - - jfieldID mType, mId, mPayload, mTnf, mFlags; - jbyteArray type = NULL; - jbyteArray id = NULL; - jbyteArray payload = NULL; - - jclass record_cls = e->GetObjectClass(o); - - raw_record_size = e->GetArrayLength(array); - raw_record = e->GetByteArrayElements(array, NULL); - if (raw_record == NULL) { - goto clean_and_return; - } - - TRACE("phFriNfc_NdefRecord_Parse()"); - status = phFriNfc_NdefRecord_Parse(&record, (uint8_t *)raw_record); - if (status) { - LOGE("phFriNfc_NdefRecord_Parse() returned 0x%04x", status); - goto clean_and_return; - } - TRACE("phFriNfc_NdefRecord_Parse() returned 0x%04x", status); - - /* Set TNF field */ - mTnf = e->GetFieldID(record_cls, "mTnf", "S"); - e->SetShortField(o, mTnf, record.Tnf); - - /* Set type field */ - mType = e->GetFieldID(record_cls, "mType", "[B"); - type = e->NewByteArray(record.TypeLength); - if (type == NULL) { - goto clean_and_return; - } - e->SetByteArrayRegion(type, 0, record.TypeLength, - (jbyte *)record.Type); - e->SetObjectField(o, mType, type); - - /* Set id field */ - mId = e->GetFieldID(record_cls, "mId", "[B"); - id = e->NewByteArray(record.IdLength); - if (id == NULL) { - goto clean_and_return; - } - e->SetByteArrayRegion(id, 0, record.IdLength, - (jbyte *)record.Id); - e->SetObjectField(o, mId, id); - - /* Set payload field */ - mPayload = e->GetFieldID(record_cls, "mPayload", "[B"); - payload = e->NewByteArray(record.PayloadLength); - if (payload == NULL) { - goto clean_and_return; - } - - e->SetByteArrayRegion(payload, 0, record.PayloadLength, - (jbyte *)record.PayloadData); - e->SetObjectField(o, mPayload, payload); - - /* Set flags field */ - mFlags = e->GetFieldID(record_cls, "mFlags", "B"); - e->SetByteField(o, mFlags, record.Flags); - - ret = 0; - -clean_and_return: - if (type != NULL) { - e->DeleteLocalRef(type); - } - if (id != NULL) { - e->DeleteLocalRef(id); - } - if (payload != NULL) { - e->DeleteLocalRef(payload); - } - if (raw_record != NULL) { - e->ReleaseByteArrayElements(array, raw_record, JNI_ABORT); - } - - return ret; -} - -static JNINativeMethod gMethods[] = { - {"generate", "(SS[B[B[B)[B", (void *)android_nfc_NdefRecord_generate}, - {"parseNdefRecord", "([B)I", (void *)android_nfc_NdefRecord_parseNdefRecord}, -}; - -int register_android_nfc_NdefRecord(JNIEnv *e) -{ - return jniRegisterNativeMethods(e, "android/nfc/NdefRecord", gMethods, NELEM(gMethods)); -} - -} // namespace android |