Add getMaxTransceiveLength() API.

Also moved canMakeReadOnly() down in the stack, and
cleaned up TransceiveResult.

Change-Id: I85576c52478ab79f0726606659b0c17d00b222e6
diff --git a/api/current.txt b/api/current.txt
index 033cccb..9e304bc 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12441,6 +12441,7 @@
     method public static android.nfc.tech.IsoDep get(android.nfc.Tag);
     method public byte[] getHiLayerResponse();
     method public byte[] getHistoricalBytes();
+    method public int getMaxTransceiveLength();
     method public void setTimeout(int);
     method public byte[] transceive(byte[]) throws java.io.IOException;
   }
@@ -12453,6 +12454,7 @@
     method public static android.nfc.tech.MifareClassic get(android.nfc.Tag);
     method public int getBlockCount();
     method public int getBlockCountInSector(int);
+    method public int getMaxTransceiveLength();
     method public int getSectorCount();
     method public int getSize();
     method public int getType();
@@ -12479,6 +12481,7 @@
 
   public final class MifareUltralight extends android.nfc.tech.BasicTagTechnology {
     method public static android.nfc.tech.MifareUltralight get(android.nfc.Tag);
+    method public int getMaxTransceiveLength();
     method public int getType();
     method public byte[] readPages(int) throws java.io.IOException;
     method public byte[] transceive(byte[]) throws java.io.IOException;
@@ -12515,6 +12518,7 @@
   public final class NfcA extends android.nfc.tech.BasicTagTechnology {
     method public static android.nfc.tech.NfcA get(android.nfc.Tag);
     method public byte[] getAtqa();
+    method public int getMaxTransceiveLength();
     method public short getSak();
     method public byte[] transceive(byte[]) throws java.io.IOException;
   }
@@ -12522,6 +12526,7 @@
   public final class NfcB extends android.nfc.tech.BasicTagTechnology {
     method public static android.nfc.tech.NfcB get(android.nfc.Tag);
     method public byte[] getApplicationData();
+    method public int getMaxTransceiveLength();
     method public byte[] getProtocolInfo();
     method public byte[] transceive(byte[]) throws java.io.IOException;
   }
@@ -12529,6 +12534,7 @@
   public final class NfcF extends android.nfc.tech.BasicTagTechnology {
     method public static android.nfc.tech.NfcF get(android.nfc.Tag);
     method public byte[] getManufacturer();
+    method public int getMaxTransceiveLength();
     method public byte[] getSystemCode();
     method public byte[] transceive(byte[]) throws java.io.IOException;
   }
@@ -12536,6 +12542,7 @@
   public final class NfcV extends android.nfc.tech.BasicTagTechnology {
     method public static android.nfc.tech.NfcV get(android.nfc.Tag);
     method public byte getDsfId();
+    method public int getMaxTransceiveLength();
     method public byte getResponseFlags();
     method public byte[] transceive(byte[]) throws java.io.IOException;
   }
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index 7bdefe7..bb5a9fd 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -46,4 +46,6 @@
     int setTimeout(int technology, int timeout);
     int getTimeout(int technology);
     void resetTimeouts();
+    boolean canMakeReadOnly(int ndefType);
+    int getMaxTransceiveLength(int technology);
 }
diff --git a/core/java/android/nfc/TransceiveResult.java b/core/java/android/nfc/TransceiveResult.java
index 16244b8..3538825 100644
--- a/core/java/android/nfc/TransceiveResult.java
+++ b/core/java/android/nfc/TransceiveResult.java
@@ -19,33 +19,38 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import java.io.IOException;
+
 /**
  * Class used to pipe transceive result from the NFC service.
  *
  * @hide
  */
 public final class TransceiveResult implements Parcelable {
-    private final boolean mTagLost;
-    private final boolean mSuccess;
-    private final byte[] mResponseData;
+    public static final int RESULT_SUCCESS = 0;
+    public static final int RESULT_FAILURE = 1;
+    public static final int RESULT_TAGLOST = 2;
+    public static final int RESULT_EXCEEDED_LENGTH = 3;
 
-    public TransceiveResult(final boolean success, final boolean tagIsLost,
-            final byte[] data) {
-        mSuccess = success;
-        mTagLost = tagIsLost;
+    final int mResult;
+    final byte[] mResponseData;
+
+    public TransceiveResult(final int result, final byte[] data) {
+        mResult = result;
         mResponseData = data;
     }
 
-    public boolean isSuccessful() {
-        return mSuccess;
-    }
-
-    public boolean isTagLost() {
-        return mTagLost;
-    }
-
-    public byte[] getResponseData() {
-        return mResponseData;
+    public byte[] getResponseOrThrow() throws IOException {
+        switch (mResult) {
+            case RESULT_SUCCESS:
+                return mResponseData;
+            case RESULT_TAGLOST:
+                throw new TagLostException("Tag was lost.");
+            case RESULT_EXCEEDED_LENGTH:
+                throw new IOException("Transceive length exceeds supported maximum");
+            default:
+                throw new IOException("Transceive failed");
+        }
     }
 
     @Override
@@ -55,9 +60,8 @@
 
     @Override
     public void writeToParcel(Parcel dest, int flags) {
-        dest.writeInt(mSuccess ? 1 : 0);
-        dest.writeInt(mTagLost ? 1 : 0);
-        if (mSuccess) {
+        dest.writeInt(mResult);
+        if (mResult == RESULT_SUCCESS) {
             dest.writeInt(mResponseData.length);
             dest.writeByteArray(mResponseData);
         }
@@ -67,18 +71,17 @@
             new Parcelable.Creator<TransceiveResult>() {
         @Override
         public TransceiveResult createFromParcel(Parcel in) {
-            boolean success = (in.readInt() == 1) ? true : false;
-            boolean tagLost = (in.readInt() == 1) ? true : false;
+            int result = in.readInt();
             byte[] responseData;
 
-            if (success) {
+            if (result == RESULT_SUCCESS) {
                 int responseLength = in.readInt();
                 responseData = new byte[responseLength];
                 in.readByteArray(responseData);
             } else {
                 responseData = null;
             }
-            return new TransceiveResult(success, tagLost, responseData);
+            return new TransceiveResult(result, responseData);
         }
 
         @Override
diff --git a/core/java/android/nfc/tech/BasicTagTechnology.java b/core/java/android/nfc/tech/BasicTagTechnology.java
index bcb7199f0a..913ae0e 100644
--- a/core/java/android/nfc/tech/BasicTagTechnology.java
+++ b/core/java/android/nfc/tech/BasicTagTechnology.java
@@ -129,6 +129,15 @@
         }
     }
 
+    /** Internal getMaxTransceiveLength() */
+    int getMaxTransceiveLengthInternal() {
+        try {
+            return mTag.getTagService().getMaxTransceiveLength(mSelectedTechnology);
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
+            return 0;
+        }
+    }
     /** Internal transceive */
     /*package*/ byte[] transceive(byte[] data, boolean raw) throws IOException {
         checkConnected();
@@ -139,16 +148,7 @@
             if (result == null) {
                 throw new IOException("transceive failed");
             } else {
-                if (result.isSuccessful()) {
-                    return result.getResponseData();
-                } else {
-                    if (result.isTagLost()) {
-                        throw new TagLostException("Tag was lost.");
-                    }
-                    else {
-                        throw new IOException("transceive failed");
-                    }
-                }
+                return result.getResponseOrThrow();
             }
         } catch (RemoteException e) {
             Log.e(TAG, "NFC service dead", e);
diff --git a/core/java/android/nfc/tech/IsoDep.java b/core/java/android/nfc/tech/IsoDep.java
index 0672a4e..6054fe8 100644
--- a/core/java/android/nfc/tech/IsoDep.java
+++ b/core/java/android/nfc/tech/IsoDep.java
@@ -156,6 +156,9 @@
      * will be automatically fragmented and defragmented by {@link #transceive} if
      * it exceeds FSD/FSC limits.
      *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
+     * that can be sent with {@link #transceive}.
+     *
      * <p>This is an I/O operation and will block until complete. It must
      * not be called from the main application thread. A blocked call will be canceled with
      * {@link IOException} if {@link #close} is called from another thread.
@@ -170,4 +173,12 @@
     public byte[] transceive(byte[] data) throws IOException {
         return transceive(data, true);
     }
+
+    /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
 }
diff --git a/core/java/android/nfc/tech/MifareClassic.java b/core/java/android/nfc/tech/MifareClassic.java
index 93e7cbd..ce923ae 100644
--- a/core/java/android/nfc/tech/MifareClassic.java
+++ b/core/java/android/nfc/tech/MifareClassic.java
@@ -560,6 +560,9 @@
      * and calling {@link NfcA#transceive}. Note that all MIFARE Classic
      * tags are based on {@link NfcA} technology.
      *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
+     * that can be sent with {@link #transceive}.
+     *
      * <p>This is an I/O operation and will block until complete. It must
      * not be called from the main application thread. A blocked call will be canceled with
      * {@link IOException} if {@link #close} is called from another thread.
@@ -573,6 +576,14 @@
     }
 
     /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
+
+    /**
      * Set the timeout of {@link #transceive} in milliseconds.
      * <p>The timeout only applies to MifareUltralight {@link #transceive},
      * and is reset to a default value when {@link #close} is called.
diff --git a/core/java/android/nfc/tech/MifareUltralight.java b/core/java/android/nfc/tech/MifareUltralight.java
index ca74ebe..890b735 100644
--- a/core/java/android/nfc/tech/MifareUltralight.java
+++ b/core/java/android/nfc/tech/MifareUltralight.java
@@ -200,6 +200,9 @@
      * and calling {@link NfcA#transceive}. Note that all MIFARE Classic
      * tags are based on {@link NfcA} technology.
      *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
+     * that can be sent with {@link #transceive}.
+     *
      * <p>This is an I/O operation and will block until complete. It must
      * not be called from the main application thread. A blocked call will be canceled with
      * {@link IOException} if {@link #close} is called from another thread.
@@ -213,6 +216,14 @@
     }
 
     /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
+
+    /**
      * Set the timeout of {@link #transceive} in milliseconds.
      * <p>The timeout only applies to MifareUltralight {@link #transceive},
      * and is reset to a default value when {@link #close} is called.
diff --git a/core/java/android/nfc/tech/Ndef.java b/core/java/android/nfc/tech/Ndef.java
index e4daa57..b266bb6 100644
--- a/core/java/android/nfc/tech/Ndef.java
+++ b/core/java/android/nfc/tech/Ndef.java
@@ -334,9 +334,11 @@
      * @return true if it is possible to make this tag read-only
      */
     public boolean canMakeReadOnly() {
-        if (mNdefType == TYPE_1 || mNdefType == TYPE_2) {
-            return true;
-        } else {
+        INfcTag tagService = mTag.getTagService();
+        try {
+            return tagService.canMakeReadOnly(mNdefType);
+        } catch (RemoteException e) {
+            Log.e(TAG, "NFC service dead", e);
             return false;
         }
     }
diff --git a/core/java/android/nfc/tech/NfcA.java b/core/java/android/nfc/tech/NfcA.java
index bd1f95a..bb8aec9 100644
--- a/core/java/android/nfc/tech/NfcA.java
+++ b/core/java/android/nfc/tech/NfcA.java
@@ -102,6 +102,9 @@
      * for example a SENS_REQ is not possible (these are used to
      * manage tag polling and initialization).
      *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
+     * that can be sent with {@link #transceive}.
+     *
      * <p>This is an I/O operation and will block until complete. It must
      * not be called from the main application thread. A blocked call will be canceled with
      * {@link IOException} if {@link #close} is called from another thread.
@@ -118,6 +121,14 @@
     }
 
     /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
+
+    /**
      * Set the timeout of {@link #transceive} in milliseconds.
      * <p>The timeout only applies to NfcA {@link #transceive}, and is
      * reset to a default value when {@link #close} is called.
diff --git a/core/java/android/nfc/tech/NfcB.java b/core/java/android/nfc/tech/NfcB.java
index 22cb11d..3ebd47f 100644
--- a/core/java/android/nfc/tech/NfcB.java
+++ b/core/java/android/nfc/tech/NfcB.java
@@ -97,6 +97,9 @@
      * <p>Applications must not send commands that manage the polling
      * loop and initialization (SENSB_REQ, SLOT_MARKER etc).
      *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum number of bytes
+     * that can be sent with {@link #transceive}.
+     *
      * <p>This is an I/O operation and will block until complete. It must
      * not be called from the main application thread. A blocked call will be canceled with
      * {@link IOException} if {@link #close} is called from another thread.
@@ -111,4 +114,12 @@
     public byte[] transceive(byte[] data) throws IOException {
         return transceive(data, true);
     }
+
+    /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
 }
diff --git a/core/java/android/nfc/tech/NfcF.java b/core/java/android/nfc/tech/NfcF.java
index 7b25a72..0938fb4 100644
--- a/core/java/android/nfc/tech/NfcF.java
+++ b/core/java/android/nfc/tech/NfcF.java
@@ -101,6 +101,9 @@
      * <p>Applications must not append the SoD (length) or EoD (CRC) to the payload,
      * it will be automatically calculated.
      *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum amount of bytes
+     * that can be sent with {@link #transceive}.
+     *
      * <p>This is an I/O operation and will block until complete. It must
      * not be called from the main application thread. A blocked call will be canceled with
      * {@link IOException} if {@link #close} is called from another thread.
@@ -117,6 +120,14 @@
     }
 
     /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
+
+    /**
      * Set the timeout of {@link #transceive} in milliseconds.
      * <p>The timeout only applies to NfcF {@link #transceive}, and is
      * reset to a default value when {@link #close} is called.
diff --git a/core/java/android/nfc/tech/NfcV.java b/core/java/android/nfc/tech/NfcV.java
index fe721c8..186c63b 100644
--- a/core/java/android/nfc/tech/NfcV.java
+++ b/core/java/android/nfc/tech/NfcV.java
@@ -97,6 +97,9 @@
      * it will be automatically calculated. The application does
      * provide FLAGS, CMD and PARAMETER bytes.
      *
+     * <p>Use {@link #getMaxTransceiveLength} to retrieve the maximum amount of bytes
+     * that can be sent with {@link #transceive}.
+     *
      * <p>This is an I/O operation and will block until complete. It must
      * not be called from the main application thread. A blocked call will be canceled with
      * {@link IOException} if {@link #close} is called from another thread.
@@ -111,4 +114,13 @@
     public byte[] transceive(byte[] data) throws IOException {
         return transceive(data, true);
     }
+
+
+    /**
+     * Return the maximum number of bytes that can be sent with {@link #transceive}.
+     * @return the maximum number of bytes that can be sent with {@link #transceive}.
+     */
+    public int getMaxTransceiveLength() {
+        return getMaxTransceiveLengthInternal();
+    }
 }