Implement multi-tech connect, enfore tech exclusivity.

- The NfcService now allows for connecting to a specific technology;
- The "active" parts of technology classes may not be used at the same time.

Change-Id: Ibb569f51cc6da4f3e24df9d0850c6f49a022b0c2
diff --git a/core/java/android/nfc/INfcTag.aidl b/core/java/android/nfc/INfcTag.aidl
index 0f96d6b..5d222d9 100644
--- a/core/java/android/nfc/INfcTag.aidl
+++ b/core/java/android/nfc/INfcTag.aidl
@@ -24,7 +24,7 @@
 interface INfcTag
 {
     int close(int nativeHandle);
-    int connect(int nativeHandle);
+    int connect(int nativeHandle, int technology);
     int reconnect(int nativeHandle);
     int[] getTechList(int nativeHandle);
     byte[] getUid(int nativeHandle);
diff --git a/core/java/android/nfc/Tag.java b/core/java/android/nfc/Tag.java
index d042634..7404950 100644
--- a/core/java/android/nfc/Tag.java
+++ b/core/java/android/nfc/Tag.java
@@ -63,6 +63,8 @@
     /*package*/ final Bundle[] mTechExtras;
     /*package*/ final int mServiceHandle;  // for use by NFC service, 0 indicates a mock
 
+    /*package*/ int mConnectedTechnology;
+
     /**
      * Hidden constructor to be used by NFC service and internal classes.
      * @hide
@@ -76,6 +78,8 @@
         // Ensure mTechExtras is as long as mTechList
         mTechExtras = Arrays.copyOf(techListExtras, techList.length);
         mServiceHandle = serviceHandle;
+
+        mConnectedTechnology = -1;
     }
 
     /**
@@ -244,4 +248,29 @@
             return new Tag[size];
         }
     };
+
+    /*
+     * @hide
+     */
+    public synchronized void setConnectedTechnology(int technology) {
+        if (mConnectedTechnology == -1) {
+            mConnectedTechnology = technology;
+        } else {
+            throw new IllegalStateException("Close other technology first!");
+        }
+    }
+
+    /*
+     * @hide
+     */
+    public int getConnectedTechnology() {
+        return mConnectedTechnology;
+    }
+
+    /*
+     * @hide
+     */
+    public void setTechnologyDisconnected() {
+        mConnectedTechnology = -1;
+    }
 }
diff --git a/core/java/android/nfc/technology/BasicTagTechnology.java b/core/java/android/nfc/technology/BasicTagTechnology.java
index a50c10b..6488ec2 100644
--- a/core/java/android/nfc/technology/BasicTagTechnology.java
+++ b/core/java/android/nfc/technology/BasicTagTechnology.java
@@ -22,6 +22,7 @@
 import android.nfc.INfcTag;
 import android.nfc.NfcAdapter;
 import android.nfc.Tag;
+import android.nfc.ErrorCodes;
 import android.os.RemoteException;
 import android.util.Log;
 
@@ -101,6 +102,13 @@
         return mTag;
     }
 
+    public void checkConnected() {
+       if ((mTag.getConnectedTechnology() != getTechnologyId()) ||
+               (mTag.getConnectedTechnology() == -1)) {
+           throw new IllegalStateException("Call connect() first!");
+       }
+    }
+
     /**
      * <p>Requires {@link android.Manifest.permission#NFC} permission.
      */
@@ -144,8 +152,19 @@
      */
     @Override
     public void connect() throws IOException {
-        //TODO(nxp): enforce exclusivity
-        mIsConnected = true;
+        try {
+            int errorCode = mTagService.connect(mTag.getServiceHandle(), getTechnologyId());
+
+            if (errorCode == ErrorCodes.SUCCESS) {
+                // Store this in the tag object
+                mTag.setConnectedTechnology(getTechnologyId());
+                mIsConnected = true;
+            } else {
+                throw new IOException();
+            }
+        } catch (RemoteException e) {
+            attemptDeadServiceRecovery(e);
+        }
     }
 
     /**
@@ -160,7 +179,6 @@
      */
     @Override
     public void close() {
-        mIsConnected = false;
         try {
             /* Note that we don't want to physically disconnect the tag,
              * but just reconnect to it to reset its state
@@ -168,6 +186,9 @@
             mTagService.reconnect(mTag.getServiceHandle());
         } catch (RemoteException e) {
             attemptDeadServiceRecovery(e);
+        } finally {
+            mIsConnected = false;
+            mTag.setTechnologyDisconnected();
         }
     }
 
@@ -183,6 +204,8 @@
      * @throws IOException if the target is lost or connection closed
      */
     public byte[] transceive(byte[] data) throws IOException {
+        checkConnected();
+
         try {
             byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, true);
             if (response == null) {
diff --git a/core/java/android/nfc/technology/IsoDep.java b/core/java/android/nfc/technology/IsoDep.java
index 5346c67..118bff7 100644
--- a/core/java/android/nfc/technology/IsoDep.java
+++ b/core/java/android/nfc/technology/IsoDep.java
@@ -75,6 +75,8 @@
      * @throws IOException, UnsupportedOperationException
      */
     public void selectAid(byte[] aid) throws IOException, UnsupportedOperationException {
+        checkConnected();
+
         throw new UnsupportedOperationException();
     }
 }
diff --git a/core/java/android/nfc/technology/MifareClassic.java b/core/java/android/nfc/technology/MifareClassic.java
index defdcf2..d5f0a31 100644
--- a/core/java/android/nfc/technology/MifareClassic.java
+++ b/core/java/android/nfc/technology/MifareClassic.java
@@ -229,6 +229,8 @@
      * Authenticate for a given sector.
      */
     public boolean authenticateSector(int sector, byte[] key, boolean keyA) {
+        checkConnected();
+
         byte[] cmd = new byte[12];
 
         // First byte is the command
@@ -264,6 +266,8 @@
      * @throws IOException
      */
     public byte[] readBlock(int sector, int block) throws IOException {
+        checkConnected();
+
         byte addr = (byte) ((firstBlockInSector(sector) + block) & 0xff);
         byte[] blockread_cmd = { 0x30, addr }; // phHal_eMifareRead
 
@@ -300,6 +304,8 @@
      */
     @Override
     public byte[] transceive(byte[] data) throws IOException {
+        checkConnected();
+
         try {
             byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false);
             if (response == null) {
diff --git a/core/java/android/nfc/technology/MifareUltralight.java b/core/java/android/nfc/technology/MifareUltralight.java
index 58d2645..35f561b 100644
--- a/core/java/android/nfc/technology/MifareUltralight.java
+++ b/core/java/android/nfc/technology/MifareUltralight.java
@@ -66,6 +66,8 @@
      * @throws IOException
      */
     public byte[] readBlock(int block) throws IOException {
+        checkConnected();
+
         byte[] blockread_cmd = { 0x30, (byte)block }; // phHal_eMifareRead
         return transceive(blockread_cmd);
     }
@@ -83,6 +85,8 @@
      */
     @Override
     public byte[] transceive(byte[] data) throws IOException {
+        checkConnected();
+
         try {
             byte[] response = mTagService.transceive(mTag.getServiceHandle(), data, false);
             if (response == null) {
diff --git a/core/java/android/nfc/technology/Ndef.java b/core/java/android/nfc/technology/Ndef.java
index 7e194aa..c45c97d 100644
--- a/core/java/android/nfc/technology/Ndef.java
+++ b/core/java/android/nfc/technology/Ndef.java
@@ -113,6 +113,8 @@
      * and requires a connection.
      */
     public NdefMessage getNdefMessage() throws IOException, FormatException {
+        checkConnected();
+
         try {
             int serviceHandle = mTag.getServiceHandle();
             if (mTagService.isNdef(serviceHandle)) {
@@ -143,6 +145,8 @@
      * @throws IOException
      */
     public void writeNdefMessage(NdefMessage msg) throws IOException, FormatException {
+        checkConnected();
+
         try {
             int errorCode = mTagService.ndefWrite(mTag.getServiceHandle(), msg);
             switch (errorCode) {
@@ -172,6 +176,8 @@
      * @throws IOException
      */
     public void writeExtraNdefMessage(int i, NdefMessage msg) throws IOException, FormatException {
+        checkConnected();
+
         throw new UnsupportedOperationException();
     }
 
@@ -180,6 +186,8 @@
      * @throws IOException
      */
     public boolean makeReadonly() throws IOException {
+        checkConnected();
+
         try {
             int errorCode = mTagService.ndefMakeReadOnly(mTag.getServiceHandle());
             switch (errorCode) {
@@ -205,11 +213,15 @@
      * For NFC Forum Type 1 and 2 only.
      */
     public void makeLowLevelReadonly() {
+        checkConnected();
+
         throw new UnsupportedOperationException();
     }
 
     @Override
     public byte[] transceive(byte[] data) {
+        checkConnected();
+
         throw new UnsupportedOperationException();
     }
 }
diff --git a/core/java/android/nfc/technology/NdefFormatable.java b/core/java/android/nfc/technology/NdefFormatable.java
index bd21e58..899b95f 100644
--- a/core/java/android/nfc/technology/NdefFormatable.java
+++ b/core/java/android/nfc/technology/NdefFormatable.java
@@ -49,7 +49,9 @@
      * NdefFormatable#format(NdefMessage)}
      */
     public boolean canBeFormatted() throws IOException {
-      throw new UnsupportedOperationException();
+        checkConnected();
+
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -57,6 +59,8 @@
      * NdefMessage to be written on the tag.
      */
     public void format(NdefMessage firstMessage) throws IOException, FormatException {
+        checkConnected();
+
         try {
             byte[] DEFAULT_KEY = {(byte)0xFF,(byte)0xFF,(byte)0xFF,
                                   (byte)0xFF,(byte)0xFF,(byte)0xFF};
@@ -97,6 +101,8 @@
 
     @Override
     public byte[] transceive(byte[] data) {
+        checkConnected();
+
         throw new UnsupportedOperationException();
     }
 }