Merge "Rewrite NDEF parsing in Java, clean-up API."
This commit is contained in:
@@ -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...);
|
||||
|
||||
@@ -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...);
|
||||
|
||||
@@ -24,4 +24,8 @@ public class FormatException extends Exception {
|
||||
public FormatException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public FormatException(String message, Throwable e) {
|
||||
super(message, e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the NDEF records inside this NDEF message.
|
||||
*
|
||||
* @return array of zero or more NDEF records.
|
||||
*/
|
||||
public NdefRecord[] getRecords() {
|
||||
return mRecords.clone();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a byte array representation of this entire NDEF message.
|
||||
*/
|
||||
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;
|
||||
// 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");
|
||||
}
|
||||
|
||||
/* 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;
|
||||
}
|
||||
|
||||
System.arraycopy(msg, 0, tmp, 0, msg.length);
|
||||
System.arraycopy(record, 0, tmp, msg.length, record.length);
|
||||
|
||||
msg = tmp;
|
||||
}
|
||||
|
||||
return msg;
|
||||
mRecords = records;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the NDEF Records inside this NDEF Message.<p>
|
||||
* An NDEF Message always has one or more NDEF Records.
|
||||
*
|
||||
* @return array of one or more NDEF records.
|
||||
*/
|
||||
public NdefRecord[] getRecords() {
|
||||
return mRecords;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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() {
|
||||
int length = 0;
|
||||
for (NdefRecord r : mRecords) {
|
||||
length += r.getByteLength();
|
||||
}
|
||||
|
||||
ByteBuffer buffer = ByteBuffer.allocate(length);
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
@@ -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.
|
||||
* 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>
|
||||
* This is similar to the "application/octet-stream" MIME type. The payload
|
||||
* type is not explicitly encoded within the NDEF Message.
|
||||
* <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>
|
||||
* 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.
|
||||
* 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>
|
||||
* Note that Android application records do not overrule
|
||||
* applications that have called
|
||||
* {@link NfcAdapter#enableForegroundDispatch}.
|
||||
*
|
||||
* @param packageName Android package name
|
||||
* @return Android application NDEF record
|
||||
*/
|
||||
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));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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");
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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, must not be null
|
||||
* @param id byte array, containing zero to 255 bytes, must not be null
|
||||
* @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,
|
||||
* must not be null
|
||||
* or null
|
||||
* @throws IllegalArugmentException if a valid record cannot be created
|
||||
*/
|
||||
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));
|
||||
}
|
||||
/* convert nulls */
|
||||
if (type == null) type = EMPTY_BYTE_ARRAY;
|
||||
if (id == null) id = EMPTY_BYTE_ARRAY;
|
||||
if (payload == null) payload = EMPTY_BYTE_ARRAY;
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
/*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");
|
||||
String message = validateTnf(tnf, type, id, payload);
|
||||
if (message != null) {
|
||||
throw new IllegalArgumentException(message);
|
||||
}
|
||||
|
||||
if (tnf < 0 || tnf > 0x07) {
|
||||
throw new IllegalArgumentException("TNF out of range " + tnf);
|
||||
}
|
||||
|
||||
/* Determine if it is a short record */
|
||||
if(payload.length < 0xFF) {
|
||||
flags |= FLAG_SR;
|
||||
}
|
||||
|
||||
/* Determine if an id is present */
|
||||
if(id.length != 0) {
|
||||
flags |= FLAG_IL;
|
||||
}
|
||||
|
||||
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,11 +546,33 @@ 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
|
||||
@@ -347,63 +603,6 @@ public final class NdefRecord implements Parcelable {
|
||||
return Uri.parse(new String(fullUri, Charsets.UTF_8));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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}.
|
||||
*
|
||||
* @param packageName Android package name
|
||||
* @return Android application NDEF record
|
||||
*/
|
||||
public static NdefRecord createApplicationRecord(String packageName) {
|
||||
return new NdefRecord(TNF_EXTERNAL_TYPE, RTD_ANDROID_APP, new byte[] {},
|
||||
packageName.getBytes(Charsets.US_ASCII));
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an NDEF record of well known type URI.
|
||||
*/
|
||||
public static NdefRecord createUri(Uri uri) {
|
||||
return createUri(uri.toString());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an NDEF record of well known type URI.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@@ -419,18 +618,215 @@ public final class NdefRecord implements Parcelable {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this entire NDEF Record as a byte array.
|
||||
* 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 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 byte[] toByteArray() {
|
||||
return generate(mFlags, mTnf, mType, mId, mPayload);
|
||||
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()]);
|
||||
}
|
||||
|
||||
private static void ensureSanePayloadSize(long size) throws FormatException {
|
||||
if (size > MAX_PAYLOAD_SIZE) {
|
||||
throw new FormatException(
|
||||
"payload above max limit: " + size + " > " + MAX_PAYLOAD_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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);
|
||||
}
|
||||
if (il) {
|
||||
buffer.put((byte)mId.length);
|
||||
}
|
||||
|
||||
buffer.put(mType);
|
||||
buffer.put(mId);
|
||||
buffer.put(mPayload);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get byte length of serialized record.
|
||||
*/
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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),
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user