Added support for (re)-connecting to tag technologies.

- Connect() now takes a technology as an argument.
- NativeNfcTag now knows to which technology it is connected.

The default connected handle is for now always the first in the
tech list; also, NDEF is only checked on the first tech.

Still to be done:
- Check ndef on other techs if not found on first
- Expose the connect(technology) API to the framework
- Fix some calls that still use nfc_jni_get_tag_handle and need to work
    on the selected technology instead.
- Get rid of mHandle and the globals for keeping tag handles

Change-Id: If76d4d458565ab0be7ca986c080a59ed8fd0668f
diff --git a/jni/com_android_nfc.cpp b/jni/com_android_nfc.cpp
index 8395350..fc63210 100644
--- a/jni/com_android_nfc.cpp
+++ b/jni/com_android_nfc.cpp
@@ -232,6 +232,64 @@
 }
 
 
+int nfc_jni_get_connected_tech_index(JNIEnv *e, jobject o)
+{
+
+   jclass c;
+   jfieldID f;
+
+   c = e->GetObjectClass(o);
+   f = e->GetFieldID(c, "mConnectedTechnology", "I");
+
+   return e->GetIntField(o, f);
+
+}
+
+jint nfc_jni_get_connected_technology(JNIEnv *e, jobject o)
+{
+   jclass c;
+   jfieldID f;
+   int connectedTech = -1;
+
+   int connectedTechIndex = nfc_jni_get_connected_tech_index(e,o);
+   jintArray techTypes = nfc_jni_get_nfc_tag_type(e, o);
+
+   if ((connectedTechIndex != -1) && (techTypes != NULL) &&
+           (connectedTechIndex < e->GetArrayLength(techTypes))) {
+       jint* technologies = e->GetIntArrayElements(techTypes, 0);
+       if (technologies != NULL) {
+           connectedTech = technologies[connectedTechIndex];
+           e->ReleaseIntArrayElements(techTypes, technologies, JNI_ABORT);
+       }
+   }
+
+   return connectedTech;
+
+}
+
+phLibNfc_Handle nfc_jni_get_connected_handle(JNIEnv *e, jobject o)
+{
+   jclass c;
+   jfieldID f;
+   phLibNfc_Handle connectedHandle = -1;
+
+   int connectedTechIndex = nfc_jni_get_connected_tech_index(e,o);
+   c = e->GetObjectClass(o);
+   f = e->GetFieldID(c, "mTechHandles", "[I");
+   jintArray techHandles =  (jintArray) e->GetObjectField(o, f);
+
+   if ((connectedTechIndex != -1) && (techHandles != NULL) &&
+           (connectedTechIndex < e->GetArrayLength(techHandles))) {
+       jint* handles = e->GetIntArrayElements(techHandles, 0);
+       if (handles != NULL) {
+           connectedHandle = handles[connectedTechIndex];
+           e->ReleaseIntArrayElements(techHandles, handles, JNI_ABORT);
+       }
+   }
+   return connectedHandle;
+
+}
+
 phLibNfc_Handle nfc_jni_get_nfc_tag_handle(JNIEnv *e, jobject o)
 {
    jclass c;
diff --git a/jni/com_android_nfc.h b/jni/com_android_nfc.h
index 04a4249..375bafa 100644
--- a/jni/com_android_nfc.h
+++ b/jni/com_android_nfc.h
@@ -194,6 +194,8 @@
 jshort nfc_jni_get_p2p_device_mode(JNIEnv *e, jobject o);
 
 /* TAG */
+jint nfc_jni_get_connected_technology(JNIEnv *e, jobject o);
+phLibNfc_Handle nfc_jni_get_connected_handle(JNIEnv *e, jobject o);
 phLibNfc_Handle nfc_jni_get_nfc_tag_handle(JNIEnv *e, jobject o);
 jintArray nfc_jni_get_nfc_tag_type(JNIEnv *e, jobject o);
 
diff --git a/jni/com_android_nfc_NativeNfcManager.cpp b/jni/com_android_nfc_NativeNfcManager.cpp
index 0a54c20..9ac00b4 100644
--- a/jni/com_android_nfc_NativeNfcManager.cpp
+++ b/jni/com_android_nfc_NativeNfcManager.cpp
@@ -959,6 +959,9 @@
 
         f = e->GetFieldID(tag_cls, "mTechHandles", "[I");
         e->SetObjectField(tag, f, handleList);
+
+        f = e->GetFieldID(tag_cls, "mConnectedTechnology", "I");
+        e->SetIntField(tag, f,(jint)-1);
       }
 
       /* Set tag handle */
diff --git a/jni/com_android_nfc_NativeNfcTag.cpp b/jni/com_android_nfc_NativeNfcTag.cpp
index 2d9ecf8..8ca7388 100644
--- a/jni/com_android_nfc_NativeNfcTag.cpp
+++ b/jni/com_android_nfc_NativeNfcTag.cpp
@@ -458,9 +458,8 @@
 }
 
 static jboolean com_android_nfc_NativeNfcTag_doConnect(JNIEnv *e,
-   jobject o)
+   jobject o, phLibNfc_Handle handle)
 {
-   phLibNfc_Handle handle = 0;
    jclass cls;
    jfieldID f;
    NFCSTATUS status;
@@ -470,8 +469,6 @@
 
    CONCURRENCY_LOCK();
 
-   handle = nfc_jni_get_nfc_tag_handle(e, o);
-
    /* Create the local semaphore */
    if (!nfc_cb_data_init(&cb_data, &pRemDevInfo))
    {
@@ -519,7 +516,21 @@
 {
     // Reconnect is provided by libnfc by just calling connect again
     // on the same handle.
-    return com_android_nfc_NativeNfcTag_doConnect(e, o);
+    // Note that some tag types are stateless, hence we do not reconnect
+    // those. Currently those are the Jewel and Iso15693 technologies.
+    int selectedTech = nfc_jni_get_connected_technology(e, o);
+    if (selectedTech != -1) {
+        if (selectedTech != TARGET_TYPE_ISO15693) {
+            phLibNfc_Handle handle = nfc_jni_get_connected_handle(e,o);
+            return com_android_nfc_NativeNfcTag_doConnect(e, o, handle);
+        }
+        else {
+            return JNI_TRUE;
+        }
+    }
+    else {
+        return JNI_FALSE;
+    }
 }
 
 
@@ -665,6 +676,7 @@
     }
 
 }
+
 static jbyteArray com_android_nfc_NativeNfcTag_doTransceive(JNIEnv *e,
    jobject o, jbyteArray data, jboolean raw)
 {
@@ -680,7 +692,6 @@
     phLibNfc_sTransceiveInfo_t transceive_info;
     jbyteArray result = NULL;
     int res;
-    jintArray techtypes = nfc_jni_get_nfc_tag_type(e, o);
     phLibNfc_Handle handle = nfc_jni_get_nfc_tag_handle(e, o);
     NFCSTATUS status;
     struct nfc_jni_callback_data cb_data;
@@ -688,6 +699,7 @@
     jint* technologies = NULL;
     bool checkResponseCrc = false;
 
+    memset(&transceive_info, 0, sizeof(transceive_info));
     CONCURRENCY_LOCK();
 
     /* Create the local semaphore */
@@ -696,15 +708,7 @@
        goto clean_and_return;
     }
 
-    if ((techtypes == NULL) || (e->GetArrayLength(techtypes) == 0)) {
-      goto clean_and_return;
-    }
-    // TODO: code to determine the selected technology
-    // For now, take the first
-    technologies = e->GetIntArrayElements(techtypes, 0);
-    selectedTech = technologies[0];
-
-    TRACE("Transceive thinks selected tag technology = %d\n", selectedTech);
+    selectedTech = nfc_jni_get_connected_technology(e, o);
 
     buf = outbuf = (uint8_t *)e->GetByteArrayElements(data, NULL);
     buflen = outlen = (uint32_t)e->GetArrayLength(data);
@@ -830,9 +834,6 @@
     {
       free(transceive_info.sRecvData.buffer);
     }
-    if (technologies != NULL) {
-      e->ReleaseIntArrayElements(techtypes, technologies, JNI_ABORT);
-    }
 
     if ((outbuf != buf) && (outbuf != NULL)) {
         // Buf was extended and re-alloced with crc bytes, free separately
@@ -1024,7 +1025,7 @@
  */
 static JNINativeMethod gMethods[] =
 {
-   {"doConnect", "()Z",
+   {"doConnect", "(I)Z",
       (void *)com_android_nfc_NativeNfcTag_doConnect},
    {"doDisconnect", "()Z",
       (void *)com_android_nfc_NativeNfcTag_doDisconnect},
diff --git a/src/com/android/nfc/NativeNfcTag.java b/src/com/android/nfc/NativeNfcTag.java
index 51f5987..26595d5 100755
--- a/src/com/android/nfc/NativeNfcTag.java
+++ b/src/com/android/nfc/NativeNfcTag.java
@@ -41,6 +41,8 @@
     private byte[][] mTechActBytes;
     private byte[] mUid;
 
+    private int mConnectedTechnology; // Index in mTechList
+
     private final String TAG = "NativeNfcTag";
 
     private PresenceCheckWatchdog mWatchdog;
@@ -78,12 +80,35 @@
         }
     }
 
-    private native boolean doConnect();
+    private native boolean doConnect(int handle);
+    public synchronized boolean connect(int technology) {
+        boolean isSuccess = false;
+        for (int i = 0; i < mTechList.length; i++) {
+            if (mTechList[i] == technology) {
+                // Get the handle and connect
+                isSuccess = doConnect(mTechHandles[i]);
+                if (isSuccess) {
+                    mConnectedTechnology = i;
+                    mWatchdog = new PresenceCheckWatchdog();
+                    mWatchdog.start();
+                }
+                break;
+            }
+        }
+        return isSuccess;
+    }
+
     public synchronized boolean connect() {
-        boolean isSuccess = doConnect();
-        if (isSuccess) {
-            mWatchdog = new PresenceCheckWatchdog();
-            mWatchdog.start();
+        // Just take the first handle in the list
+        boolean isSuccess = false;
+        if ((mTechHandles.length > 0) && (mTechList.length > 0)) {
+            int handle = mTechHandles[0];
+            isSuccess = doConnect(handle);
+            if (isSuccess) {
+                mConnectedTechnology = 0;
+                mWatchdog = new PresenceCheckWatchdog();
+                mWatchdog.start();
+            }
         }
         return isSuccess;
     }
@@ -93,6 +118,7 @@
         if (mWatchdog != null) {
             mWatchdog.end();
         }
+        mConnectedTechnology = -1;
         return doDisconnect();
     }
 
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index a5e6b2d..2e857da 100755
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -1610,7 +1610,7 @@
 
         @Override
         public int getLastError(int nativeHandle) throws RemoteException {
-            throw new UnsupportedOperationException();
+            return(mManager.doGetLastError());
         }
 
         @Override
@@ -2483,6 +2483,9 @@
            case MSG_NDEF_TAG:
                if (DBG) Log.d(TAG, "Tag detected, notifying applications");
                NativeNfcTag nativeTag = (NativeNfcTag) msg.obj;
+               // TODO: should check on next handle if ndef not available
+               // on the first. For now, just connect to the first handle
+               // in the list.
                if (nativeTag.connect()) {
                    int[] ndefinfo = new int[2];
                    if (nativeTag.checkNdef(ndefinfo)) {
@@ -2496,6 +2499,7 @@
                                msgNdef[0] = new NdefMessage(buff);
                                nativeTag.addNdefTechnology(msgNdef[0],
                                        supportedNdefLength, cardState);
+                               nativeTag.reconnect();
                                dispatchNativeTag(nativeTag, msgNdef);
                            } catch (FormatException e) {
                                // Create an intent anyway, without NDEF messages
@@ -2511,6 +2515,7 @@
                            nativeTag.addNdefTechnology(null, supportedNdefLength, cardState);
                            if (DBG) Log.d(TAG, "NDEF tag found, but length 0 or invalid format, " +
                                    "starting corresponding activity");
+                           nativeTag.reconnect();
                            dispatchNativeTag(nativeTag, new NdefMessage[] { });
                        }
                    } else {