keep history after reconcile of gingerbread-release
diff --git a/jni/com_android_nfc_NativeNfcManager.cpp b/jni/com_android_nfc_NativeNfcManager.cpp
index dc233ca..e724524 100644
--- a/jni/com_android_nfc_NativeNfcManager.cpp
+++ b/jni/com_android_nfc_NativeNfcManager.cpp
@@ -34,6 +34,7 @@
 static phNfc_sData_t gOutputParam;
 
 uint8_t device_connected_flag;
+static bool driverConfigured = FALSE;
 
 static phLibNfc_Handle              hLlcpHandle;
 static NFCSTATUS                    lastErrorStatus = NFCSTATUS_FAILED;
@@ -286,13 +287,21 @@
        goto clean_and_return;
     }
 
-    /* Download Status */
-    if(cb_data.status != NFCSTATUS_SUCCESS)
+    /* NOTE: we will get NFCSTATUS_FEATURE_NOT_SUPPORTED when we
+       try to download an old-style firmware on top of a new-style
+       firmware.  Hence, this is expected behavior, and not an
+       error condition. */
+    if(cb_data.status != NFCSTATUS_SUCCESS && cb_data.status != NFCSTATUS_FEATURE_NOT_SUPPORTED)
     {
         status = cb_data.status;
         goto clean_and_return;
     }
 
+    if(cb_data.status == NFCSTATUS_FEATURE_NOT_SUPPORTED)
+    {
+        LOGW("Old-style firmware not installed on top of new-style firmware. Using existing firmware in the chip.");
+    }
+
 reinit:
     TRACE("phLibNfc_HW_Reset()");
     phLibNfc_HW_Reset();
@@ -332,13 +341,14 @@
     }
     else
     {
-        LOGD("NFC capabilities: HAL = %x, FW = %x, HW = %x, Model = %x, HCI = %x, Full_FW = %d, FW Update Info = %d",
+        LOGD("NFC capabilities: HAL = %x, FW = %x, HW = %x, Model = %x, HCI = %x, Full_FW = %d, Rev = %d, FW Update Info = %d",
               caps.psDevCapabilities.hal_version,
               caps.psDevCapabilities.fw_version,
               caps.psDevCapabilities.hw_version,
               caps.psDevCapabilities.model_id,
               caps.psDevCapabilities.hci_version,
               caps.psDevCapabilities.full_version[NXP_FULL_VERSION_LEN-1],
+              caps.psDevCapabilities.full_version[NXP_FULL_VERSION_LEN-2],
               caps.psDevCapabilities.firmware_update_info);
     }
 
@@ -350,20 +360,83 @@
    return status;
 }
 
+static int nfc_jni_configure_driver(struct nfc_jni_native_data *nat)
+{
+    char value[PROPERTY_VALUE_MAX];
+    int result = FALSE;
+    NFCSTATUS status;
+
+    /* ====== CONFIGURE DRIVER ======= */
+    /* Configure hardware link */
+    gDrvCfg.nClientId = phDal4Nfc_msgget(0, 0600);
+
+    property_get("ro.nfc.port", value, "unknown");
+    gDrvCfg.nLinkType = parseLinkType(value);
+
+    TRACE("phLibNfc_Mgt_ConfigureDriver(0x%08x, 0x%08x)", gDrvCfg.nClientId, gDrvCfg.nLinkType);
+    REENTRANCE_LOCK();
+    status = phLibNfc_Mgt_ConfigureDriver(&gDrvCfg, &gHWRef);
+    REENTRANCE_UNLOCK();
+    if(status == NFCSTATUS_ALREADY_INITIALISED) {
+           LOGW("phLibNfc_Mgt_ConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
+    }
+    else if(status != NFCSTATUS_SUCCESS)
+    {
+        LOGE("phLibNfc_Mgt_ConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
+        goto clean_and_return;
+    }
+    TRACE("phLibNfc_Mgt_ConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
+
+    if(pthread_create(&(nat->thread), NULL, nfc_jni_client_thread, nat) != 0)
+    {
+        LOGE("pthread_create failed");
+        goto clean_and_return;
+    }
+
+    driverConfigured = TRUE;
+
+clean_and_return:
+    return result;
+}
+
+static int nfc_jni_unconfigure_driver(struct nfc_jni_native_data *nat)
+{
+    int result = FALSE;
+    NFCSTATUS status;
+
+    /* Unconfigure driver */
+    TRACE("phLibNfc_Mgt_UnConfigureDriver()");
+    REENTRANCE_LOCK();
+    status = phLibNfc_Mgt_UnConfigureDriver(gHWRef);
+    REENTRANCE_UNLOCK();
+    if(status != NFCSTATUS_SUCCESS)
+    {
+        LOGE("phLibNfc_Mgt_UnConfigureDriver() returned error 0x%04x[%s] -- this should never happen", status, nfc_jni_get_status_name( status));
+    }
+    else
+    {
+       LOGD("phLibNfc_Mgt_UnConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
+       result = TRUE;
+    }
+
+    driverConfigured = FALSE;
+
+    return result;
+}
+
 /* Initialization function */
 static int nfc_jni_initialize(struct nfc_jni_native_data *nat) {
    struct timespec ts;
    uint8_t resp[16];
    NFCSTATUS status;
    phLibNfc_StackCapabilities_t caps;
-   char value[PROPERTY_VALUE_MAX];
-   int result = FALSE;
    phLibNfc_SE_List_t SE_List[PHLIBNFC_MAXNO_OF_SE];
    uint8_t i, No_SE = PHLIBNFC_MAXNO_OF_SE, SmartMX_index = 0, SmartMX_detected = 0;
    phLibNfc_Llcp_sLinkParameters_t LlcpConfigInfo;
    struct nfc_jni_callback_data cb_data;
    uint8_t firmware_status;
    uint8_t update = TRUE;
+   int result = JNI_FALSE;
 
    LOGD("Start Initialization\n");
 
@@ -379,33 +452,10 @@
    /* Reset stored handle */
    storedHandle = 0;
 
-   /* Configure hardware link */
-   gDrvCfg.nClientId = phDal4Nfc_msgget(0, 0600);
-
-   property_get("ro.nfc.port", value, "unknown");
-   gDrvCfg.nLinkType = parseLinkType(value);
-
-   /* ====== CONFIGURE DRIVER ======= */
-
-   TRACE("phLibNfc_Mgt_ConfigureDriver(0x%08x, 0x%08x)", gDrvCfg.nClientId, gDrvCfg.nLinkType);
-   REENTRANCE_LOCK();
-   status = phLibNfc_Mgt_ConfigureDriver(&gDrvCfg, &gHWRef);
-   REENTRANCE_UNLOCK();
-   if(status == NFCSTATUS_ALREADY_INITIALISED) {
-      LOGW("phLibNfc_Mgt_ConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
-   }
-   else if(status != NFCSTATUS_SUCCESS)
+   /* Initialize Driver */
+   if(!driverConfigured)
    {
-      LOGE("phLibNfc_Mgt_ConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
-      goto clean_and_return;
-   }
-   TRACE("phLibNfc_Mgt_ConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
-
-   if(pthread_create(&(nat->thread), NULL, nfc_jni_client_thread,
-         nat) != 0)
-   {
-      LOGE("pthread_create failed");
-      goto clean_and_return;
+       nfc_jni_configure_driver(nat);
    }
 
    /* ====== INITIALIZE ======= */
@@ -472,6 +522,7 @@
                break;
            }
            LOGW("Firmware update FAILED");
+           update = FALSE;
        }
        if(i>=3)
        {
@@ -1729,11 +1780,11 @@
       LOGD("Native Structure initialization failed");
       return FALSE;   
    }
-
-   TRACE("****** Init Native Structure OK ******"); 
+   TRACE("****** Init Native Structure OK ******");
    return TRUE;
+
 }
- 
+
 /* Init/Deinit method */
 static jboolean com_android_nfc_NfcManager_initialize(JNIEnv *e, jobject o)
 {
@@ -1772,17 +1823,20 @@
 {
    struct timespec ts;
    NFCSTATUS status;
+   int result = JNI_FALSE;
    struct nfc_jni_native_data *nat;
    int bStackReset = FALSE;
    struct nfc_jni_callback_data cb_data;
 
+   CONCURRENCY_LOCK();
+
    /* Retrieve native structure address */
    nat = nfc_jni_get_nat(e, o);
 
    /* Clear previous configuration */
    memset(&nat->discovery_cfg, 0, sizeof(phLibNfc_sADD_Cfg_t));
    memset(&nat->registry_info, 0, sizeof(phLibNfc_Registry_Info_t));
-   
+
    /* Create the local semaphore */
    if (nfc_cb_data_init(&cb_data, NULL))
    {
@@ -1832,22 +1886,12 @@
       emergency_recovery(nat);
    }
 
-   /* Unconfigure driver */
-   TRACE("phLibNfc_Mgt_UnConfigureDriver()");
-   REENTRANCE_LOCK();
-   status = phLibNfc_Mgt_UnConfigureDriver(gHWRef);
-   REENTRANCE_UNLOCK();
-   if(status != NFCSTATUS_SUCCESS)
-   {
-      LOGE("phLibNfc_Mgt_UnConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
-   }
-   else
-   {
-      LOGD("phLibNfc_Mgt_UnConfigureDriver() returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
-   }
+   result = nfc_jni_unconfigure_driver(nat);
 
    TRACE("NFC Deinitialized");
 
+   CONCURRENCY_UNLOCK();
+
    return TRUE;
 }
 
@@ -2469,14 +2513,123 @@
     emergency_recovery(NULL);
 }
 
+static jboolean com_android_nfc_NfcManager_doDownload(JNIEnv *e, jobject o)
+{
+    char* firmware_version;
+    jboolean result = FALSE;
+    int load_result;
+    int unconfigure_status;
+    bool drive_state = FALSE;
+    uint8_t OutputBuffer[1];
+    uint8_t InputBuffer[1];
+    struct timespec ts;
+    NFCSTATUS status = NFCSTATUS_FAILED;
+    struct nfc_jni_callback_data cb_data;
+    struct nfc_jni_native_data *nat = NULL;
+    char value[PROPERTY_VALUE_MAX];
+
+    /* Create the local semaphore */
+    if (!nfc_cb_data_init(&cb_data, NULL))
+    {
+       result = FALSE;
+       goto clean_and_return;
+    }
+
+    /* Retrieve native structure address */
+    nat = nfc_jni_get_nat(e, o);
+
+    CONCURRENCY_LOCK();
+
+    /* Initialize Driver */
+    if(!driverConfigured)
+    {
+        result = nfc_jni_configure_driver(nat);
+        drive_state = TRUE;
+    }
+
+    TRACE("com_android_nfc_NfcManager_doDownload()");
+
+    TRACE("Go in Download Mode");
+    phLibNfc_Download_Mode();
+
+    TRACE("Load new Firmware Image");
+    load_result = phLibNfc_Load_Firmware_Image();
+    if(load_result != 0)
+    {
+        TRACE("Load new Firmware Image - status = %d",load_result);
+        result = FALSE;
+        goto clean_and_return;
+    }
+
+    // Download
+    gInputParam.buffer  = InputBuffer;
+    gInputParam.length  = 0x01;
+    gOutputParam.buffer = OutputBuffer;
+    gOutputParam.length = 0x01;
+
+    LOGD("Download new Firmware");
+    REENTRANCE_LOCK();
+    status = phLibNfc_Mgt_IoCtl(gHWRef,NFC_FW_DOWNLOAD, &gInputParam, &gOutputParam, nfc_jni_ioctl_callback, (void *)&cb_data);
+    REENTRANCE_UNLOCK();
+    if(status != NFCSTATUS_PENDING)
+    {
+        LOGE("phLibNfc_Mgt_IoCtl() (download) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
+        result = FALSE;
+        goto clean_and_return;
+    }
+    TRACE("phLibNfc_Mgt_IoCtl() (download) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
+
+    /* Wait for callback response */
+    if(sem_wait(&cb_data.sem))
+    {
+       LOGE("Failed to wait for semaphore (errno=0x%08x)", errno);
+       result = FALSE;
+       goto clean_and_return;
+    }
+
+    /* NOTE: we will get NFCSTATUS_FEATURE_NOT_SUPPORTED when we
+       try to download an old-style firmware on top of a new-style
+       firmware.  Hence, this is expected behavior, and not an
+       error condition. */
+    if(cb_data.status != NFCSTATUS_SUCCESS && cb_data.status != NFCSTATUS_FEATURE_NOT_SUPPORTED)
+    {
+        TRACE("phLibNfc_Mgt_IoCtl() (download) returned 0x%04x[%s]", status, nfc_jni_get_status_name(status));
+        result = FALSE;
+        goto clean_and_return;
+    }
+
+    if(cb_data.status == NFCSTATUS_FEATURE_NOT_SUPPORTED)
+    {
+        LOGW("Old-style firmware not installed on top of new-style firmware. Using existing firmware in the chip.");
+    }
+
+    /*Download is successful*/
+    result = TRUE;
+
+clean_and_return:
+    TRACE("phLibNfc_HW_Reset()");
+    phLibNfc_HW_Reset();
+    /* Deinitialize Driver */
+    if(drive_state)
+    {
+        result = nfc_jni_unconfigure_driver(nat);
+    }
+    CONCURRENCY_UNLOCK();
+    nfc_cb_data_deinit(&cb_data);
+    return result;
+}
+
 /*
  * JNI registration.
  */
 static JNINativeMethod gMethods[] =
 {
+   {"doDownload", "()Z",
+        (void *)com_android_nfc_NfcManager_doDownload},
+
    {"initializeNativeStructure", "()Z",
       (void *)com_android_nfc_NfcManager_init_native_struc},
-      
+
    {"initialize", "()Z",
       (void *)com_android_nfc_NfcManager_initialize},
  
diff --git a/src/com/android/nfc/NativeNfcManager.java b/src/com/android/nfc/NativeNfcManager.java
index 10ff1ff..5938613 100755
--- a/src/com/android/nfc/NativeNfcManager.java
+++ b/src/com/android/nfc/NativeNfcManager.java
@@ -104,6 +104,8 @@
         doSetIsoDepTimeout(timeout);
     }
 
+    public native boolean doDownload();
+
     /**
      * Notifies Ndef Message (TODO: rename into notifyTargetDiscovered)
      */
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index acbfc5e..51cbfa4 100755
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -70,6 +70,7 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.DataInputStream;
+import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
@@ -89,6 +90,7 @@
 
     private static final String MY_TAG_FILE_NAME = "mytag";
     private static final String SE_RESET_SCRIPT_FILE_NAME = "/system/etc/se-reset-script";
+    private static final String NFC_CONTROLLER_FIRMWARE_FILE_NAME = "/system/lib/libpn544_fw.so";
 
     static {
         System.loadLibrary("nfc_jni");
@@ -189,6 +191,9 @@
     private static final String PREF_DISCOVERY_NFCIP = "discovery_nfcip";
     private static final boolean DISCOVERY_NFCIP_DEFAULT = true;
 
+    private static final String PREF_FIRMWARE_MODTIME = "firmware_modtime";
+    private static final long FIRMWARE_MODTIME_DEFAULT = -1;
+
     /** NFC Reader Discovery mode for enableDiscovery() */
     private static final int DISCOVERY_MODE_READER = 0;
 
@@ -355,9 +360,14 @@
         Thread t = new Thread() {
             @Override
             public void run() {
+                Log.d(TAG,"checking on firmware download");
                 boolean nfc_on = mPrefs.getBoolean(PREF_NFC_ON, NFC_ON_DEFAULT);
                 if (nfc_on) {
+                    Log.d(TAG,"NFC is on. Doing normal stuff");
                     _enable(false, true);
+                } else {
+                    Log.d(TAG,"NFC is off.  Checking firmware version");
+                    _maybeUpdateFirmware();
                 }
                 resetSeOnFirstBoot();
             }
@@ -1997,6 +2007,48 @@
         return isSuccess;
     }
 
+    // Check that the NFC controller firmware is up to date.  This
+    // ensures that firmware updates are applied in a timely fashion,
+    // and makes it much less likely that the user will have to wait
+    // for a firmware download when they enable NFC in the settings
+    // app.  Firmware download can take some time, so this should be
+    // run in a separate thread.
+    private void _maybeUpdateFirmware() {
+        // check the timestamp of the firmware file
+        File firmwareFile;
+        int nbRetry = 0;
+        try {
+            firmwareFile = new File(NFC_CONTROLLER_FIRMWARE_FILE_NAME);
+        } catch(NullPointerException npe) {
+            Log.e(TAG,"path to firmware file was null");
+            return;
+        }
+
+        long modtime = firmwareFile.lastModified();
+
+        long prev_fw_modtime = mPrefs.getLong(PREF_FIRMWARE_MODTIME, FIRMWARE_MODTIME_DEFAULT);
+        Log.d(TAG,"prev modtime: " + prev_fw_modtime);
+        Log.d(TAG,"new modtime: " + modtime);
+        if (prev_fw_modtime == modtime) {
+            return;
+        }
+
+        // FW download.
+        while(nbRetry < 5) {
+            Log.d(TAG,"Perform Download");
+            if(mManager.doDownload()) {
+                Log.d(TAG,"Download Success");
+                // Now that we've finished updating the firmware, save the new modtime.
+                mPrefsEditor.putLong(PREF_FIRMWARE_MODTIME, modtime);
+                mPrefsEditor.apply();
+                break;
+            } else {
+                Log.d(TAG,"Download Failed");
+                nbRetry++;
+            }
+        }
+    }
+
     private class WatchDogThread extends Thread {
         boolean mWatchDogCanceled = false;
         @Override