Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit eca6d909 authored by Nick Pelly's avatar Nick Pelly Committed by Android (Google) Code Review
Browse files

Merge "Rewrite NDEF parsing in Java, clean-up API."

parents 01583ef7 a356bf1c
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -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...);
+6 −3
Original line number Diff line number Diff line
@@ -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...);
+4 −0
Original line number Diff line number Diff line
@@ -24,4 +24,8 @@ public class FormatException extends Exception {
    public FormatException(String message) {
        super(message);
    }

    public FormatException(String message, Throwable e) {
        super(message, e);
    }
}
+154 −53
Original line number Diff line number Diff line
@@ -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");
        }
    }

    /**
     * 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);
    }

    /**
     * Create an NDEF message from NDEF records.
     * 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
+633 −157

File changed.

Preview size limit exceeded, changes collapsed.

Loading