Reconcile with jb-release nakasi-factoryrom-release

Change-Id: Id94f334e6a92cf48ac774f1f4233df95d9241274
diff --git a/jni/com_android_nfc.h b/jni/com_android_nfc.h
index a44bcf0..b876dad 100644
--- a/jni/com_android_nfc.h
+++ b/jni/com_android_nfc.h
@@ -112,9 +112,11 @@
 #if 0
   #define LOG_CALLBACK(funcName, status)  LOG_PRI(GET_LEVEL(status), LOG_TAG, "Callback: %s() - status=0x%04x[%s]", funcName, status, nfc_jni_get_status_name(status));
   #define TRACE(...) ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)
+  #define TRACE_ENABLED 1
 #else
   #define LOG_CALLBACK(...)
   #define TRACE(...)
+  #define TRACE_ENABLED 0
 #endif
 
 struct nfc_jni_native_data
diff --git a/jni/com_android_nfc_NativeNfcManager.cpp b/jni/com_android_nfc_NativeNfcManager.cpp
index 704ee6a..e6da0fa 100644
--- a/jni/com_android_nfc_NativeNfcManager.cpp
+++ b/jni/com_android_nfc_NativeNfcManager.cpp
@@ -1164,13 +1164,14 @@
 
                     if(aid != NULL)
                     {
-                        char aid_str[AID_MAXLEN * 2 + 1];
-                        aid_str[0] = '\0';
-                        for (i = 0; i < (int) (aid->length) && i < AID_MAXLEN; i++) {
-                          snprintf(&aid_str[i*2], 3, "%02x", aid->buffer[i]);
+                        if (TRACE_ENABLED == 1) {
+                            char aid_str[AID_MAXLEN * 2 + 1];
+                            aid_str[0] = '\0';
+                            for (i = 0; i < (int) (aid->length) && i < AID_MAXLEN; i++) {
+                              snprintf(&aid_str[i*2], 3, "%02x", aid->buffer[i]);
+                            }
+                            ALOGD("> AID: %s", aid_str);
                         }
-                        ALOGD("> AID: %s", aid_str);
-
                         tmp_array = e->NewByteArray(aid->length);
                         if (tmp_array == NULL)
                         {
diff --git a/res/drawable-hdpi/ic_menu_cancel_holo_dark.png b/res/drawable-hdpi/ic_menu_cancel_holo_dark.png
new file mode 100644
index 0000000..2956109
--- /dev/null
+++ b/res/drawable-hdpi/ic_menu_cancel_holo_dark.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_menu_cancel_holo_dark.png b/res/drawable-mdpi/ic_menu_cancel_holo_dark.png
new file mode 100644
index 0000000..6ed1327
--- /dev/null
+++ b/res/drawable-mdpi/ic_menu_cancel_holo_dark.png
Binary files differ
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 06cc01c..005f770 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -22,6 +22,8 @@
     <string name="beam_progress">Incoming beam...</string>
     <string name="beam_complete">Beam complete</string>
     <string name="beam_failed">Beam failed</string>
+    <string name="beam_canceled">Beam canceled</string>
+    <string name="cancel">Cancel</string>
     <string name="beam_touch_to_view">Touch to view</string>
 
     <string name="connecting_headset">Connecting</string>
diff --git a/src/com/android/nfc/NfcService.java b/src/com/android/nfc/NfcService.java
index 9e4e21b..7b3c456 100755
--- a/src/com/android/nfc/NfcService.java
+++ b/src/com/android/nfc/NfcService.java
@@ -554,6 +554,14 @@
 
             mP2pLinkManager.enableDisable(false, false);
 
+            synchronized (NfcService.this) {
+                if (mOpenEe != null) {
+                    try {
+                        _nfcEeClose(-1, mOpenEe.binder);
+                    } catch (IOException e) { }
+                }
+            }
+
             // Stop watchdog if tag present
             // A convenient way to stop the watchdog properly consists of
             // disconnecting the tag. The polling loop shall be stopped before
@@ -1178,7 +1186,7 @@
         // operations. However this is not supported by current hardware.
 
         synchronized (NfcService.this) {
-            if (!isNfcEnabled()) {
+            if (!isNfcEnabledOrShuttingDown()) {
                 throw new IOException("NFC adapter is disabled");
             }
             if (mOpenEe == null) {
diff --git a/src/com/android/nfc/P2pLinkManager.java b/src/com/android/nfc/P2pLinkManager.java
index ecded75..2f70f39 100755
--- a/src/com/android/nfc/P2pLinkManager.java
+++ b/src/com/android/nfc/P2pLinkManager.java
@@ -405,7 +405,11 @@
                     return null;
                 }
 
-                result = new NdefPushClient().push(m);
+                if (m != null) {
+                    result = new NdefPushClient().push(m);
+                } else {
+                    result = false;
+                }
             }
             time = SystemClock.elapsedRealtime() - time;
 
diff --git a/src/com/android/nfc/handover/BluetoothOppHandover.java b/src/com/android/nfc/handover/BluetoothOppHandover.java
index 1835aa8..3180e83 100644
--- a/src/com/android/nfc/handover/BluetoothOppHandover.java
+++ b/src/com/android/nfc/handover/BluetoothOppHandover.java
@@ -2,6 +2,7 @@
 
 import android.bluetooth.BluetoothAdapter;
 import android.bluetooth.BluetoothDevice;
+import android.content.ActivityNotFoundException;
 import android.content.BroadcastReceiver;
 import android.content.ContentResolver;
 import android.content.Context;
@@ -116,11 +117,9 @@
     void sendIntent() {
         //TODO: either open up BluetoothOppLauncherActivity to all MIME types
         //      or gracefully handle mime types that can't be sent
-        Log.d(TAG, "Sending handover intent for " + mDevice.getAddress());
         Intent intent = new Intent();
         intent.setPackage("com.android.bluetooth");
         String mimeType = getMimeTypeForUri(mContext, mUris[0]);
-        Log.d(TAG, "Determined mime type as " + mimeType);
         intent.setType(mimeType);
         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mDevice);
         if (mUris.length == 1) {
@@ -133,8 +132,11 @@
         }
         intent.putExtra(EXTRA_CONNECTION_HANDOVER, true);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        mContext.startActivity(intent);
-
+        try {
+            mContext.startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.e(TAG, "Failed to handover file to bluetooth, mimeType not allowed.");
+        }
         complete();
     }
 
diff --git a/src/com/android/nfc/handover/HandoverManager.java b/src/com/android/nfc/handover/HandoverManager.java
index 79bcef1..a1458b9 100644
--- a/src/com/android/nfc/handover/HandoverManager.java
+++ b/src/com/android/nfc/handover/HandoverManager.java
@@ -117,6 +117,11 @@
     static final String ACTION_WHITELIST_DEVICE =
             "android.btopp.intent.action.WHITELIST_DEVICE";
 
+    static final String ACTION_CANCEL_HANDOVER_TRANSFER =
+            "com.android.nfc.handover.action.CANCEL_HANDOVER_TRANSFER";
+    static final String EXTRA_SOURCE_ADDRESS =
+            "com.android.nfc.handover.extra.SOURCE_ADDRESS";
+
     static final int SOURCE_BLUETOOTH_INCOMING = 0;
 
     static final int SOURCE_BLUETOOTH_OUTGOING = 1;
@@ -131,7 +136,9 @@
     final NotificationManager mNotificationManager;
     final HandoverPowerManager mHandoverPowerManager;
 
-    // synchronized on HandoverManager.this
+    // Variables below synchronized on HandoverManager.this
+    final HashMap<Pair<String, Boolean>, HandoverTransfer> mTransfers;
+
     BluetoothHeadset mBluetoothHeadset;
     BluetoothA2dp mBluetoothA2dp;
     BluetoothHeadsetHandover mBluetoothHeadsetHandover;
@@ -139,7 +146,6 @@
 
     String mLocalBluetoothAddress;
     int mNotificationId;
-    HashMap<Pair<String, Boolean>, HandoverTransfer> mTransfers;
 
     static class BluetoothHandoverData {
         public boolean valid = false;
@@ -232,15 +238,19 @@
      */
     class HandoverTransfer implements Handler.Callback,
             MediaScannerConnection.OnScanCompletedListener {
+        // In the states below we still accept new file transfer
         static final int STATE_NEW = 0;
         static final int STATE_IN_PROGRESS = 1;
         static final int STATE_W4_NEXT_TRANSFER = 2;
+
+        // In the states below no new files are accepted.
         static final int STATE_W4_MEDIA_SCANNER = 3;
         static final int STATE_FAILED = 4;
         static final int STATE_SUCCESS = 5;
         static final int STATE_CANCELLED = 6;
 
         static final int MSG_NEXT_TRANSFER_TIMER = 0;
+        static final int MSG_TRANSFER_TIMEOUT = 1;
 
         // We need to receive an update within this time period
         // to still consider this transfer to be "alive" (ie
@@ -258,6 +268,7 @@
         final boolean incoming;  // whether this is an incoming transfer
         final int notificationId; // Unique ID of this transfer used for notifications
         final Handler handler;
+        final PendingIntent cancelIntent;
 
         int state;
         Long lastUpdate; // Last time an event occurred for this transfer
@@ -285,8 +296,11 @@
             this.sourceAddress = sourceAddress;
             this.incoming = incoming;
             this.handler = new Handler(mContext.getMainLooper(), this);
+            this.cancelIntent = buildCancelIntent();
             this.urisScanned = 0;
             this.device = mBluetoothAdapter.getRemoteDevice(sourceAddress);
+
+            handler.sendEmptyMessageDelayed(MSG_TRANSFER_TIMEOUT, ALIVE_CHECK_MS);
         }
 
         public synchronized void updateFileProgress(float progress) {
@@ -330,17 +344,23 @@
         public synchronized boolean isRunning() {
             if (state != STATE_NEW && state != STATE_IN_PROGRESS && state != STATE_W4_NEXT_TRANSFER) {
                 return false;
-            }
-
-            // Check that we've made progress
-            Long currentTime = SystemClock.elapsedRealtime();
-            if (currentTime - lastUpdate > ALIVE_CHECK_MS) {
-                return false;
             } else {
                 return true;
             }
         }
 
+        synchronized void cancel() {
+            if (!isRunning()) return;
+
+            // Delete all files received so far
+            for (Uri uri : btUris) {
+                File file = new File(uri.getPath());
+                if (file.exists()) file.delete();
+            }
+
+            updateStateAndNotification(STATE_CANCELLED);
+        }
+
         synchronized void updateNotification() {
             if (!incoming) return; // No notifications for outgoing transfers
 
@@ -352,6 +372,10 @@
                 notBuilder.setSmallIcon(android.R.drawable.stat_sys_download);
                 notBuilder.setTicker(mContext.getString(R.string.beam_progress));
                 notBuilder.setContentTitle(mContext.getString(R.string.beam_progress));
+                notBuilder.addAction(R.drawable.ic_menu_cancel_holo_dark,
+                        mContext.getString(R.string.cancel), cancelIntent);
+                notBuilder.setDeleteIntent(cancelIntent);
+                notBuilder.setWhen(0);
                 // We do have progress indication on a per-file basis, but in a multi-file
                 // transfer we don't know the total progress. So for now, just show an
                 // indeterminate progress bar.
@@ -362,6 +386,7 @@
                 notBuilder.setTicker(mContext.getString(R.string.beam_complete));
                 notBuilder.setContentTitle(mContext.getString(R.string.beam_complete));
                 notBuilder.setContentText(mContext.getString(R.string.beam_touch_to_view));
+                notBuilder.setWhen(0);
 
                 Intent viewIntent = buildViewIntent();
                 PendingIntent contentIntent = PendingIntent.getActivity(mContext, 0, viewIntent, 0);
@@ -371,10 +396,17 @@
                 // Play Beam success sound
                 NfcService.getInstance().playSound(NfcService.SOUND_END);
             } else if (state == STATE_FAILED) {
-                notBuilder.setAutoCancel(true);
+                notBuilder.setAutoCancel(false);
                 notBuilder.setSmallIcon(android.R.drawable.stat_sys_download_done);
                 notBuilder.setTicker(mContext.getString(R.string.beam_failed));
                 notBuilder.setContentTitle(mContext.getString(R.string.beam_failed));
+                notBuilder.setWhen(0);
+            } else if (state == STATE_CANCELLED) {
+                notBuilder.setAutoCancel(false);
+                notBuilder.setSmallIcon(android.R.drawable.stat_sys_download_done);
+                notBuilder.setTicker(mContext.getString(R.string.beam_canceled));
+                notBuilder.setContentTitle(mContext.getString(R.string.beam_canceled));
+                notBuilder.setWhen(0);
             } else {
                 return;
             }
@@ -385,6 +417,12 @@
         synchronized void updateStateAndNotification(int newState) {
             this.state = newState;
             this.lastUpdate = SystemClock.elapsedRealtime();
+
+            if (handler.hasMessages(MSG_TRANSFER_TIMEOUT)) {
+                // Update timeout timer
+                handler.removeMessages(MSG_TRANSFER_TIMEOUT);
+                handler.sendEmptyMessageDelayed(MSG_TRANSFER_TIMEOUT, ALIVE_CHECK_MS);
+            }
             updateNotification();
         }
 
@@ -414,7 +452,9 @@
                 String mimeType = btMimeTypes.get(i);
 
                 File srcFile = new File(uri.getPath());
-                File dstFile = new File(beamPath + "/" + uri.getLastPathSegment());
+
+                File dstFile = generateUniqueDestination(beamPath + "/" +
+                        uri.getLastPathSegment());
                 if (!srcFile.renameTo(dstFile)) {
                     if (DBG) Log.d(TAG, "Failed to rename from " + srcFile + " to " + dstFile);
                     srcFile.delete();
@@ -447,8 +487,18 @@
         public boolean handleMessage(Message msg) {
             if (msg.what == MSG_NEXT_TRANSFER_TIMER) {
                 // We didn't receive a new transfer in time, finalize this one
-                processFiles();
+                if (incoming) {
+                    processFiles();
+                } else {
+                    updateStateAndNotification(STATE_SUCCESS);
+                }
                 return true;
+            } else if (msg.what == MSG_TRANSFER_TIMEOUT) {
+                // No update on this transfer for a while, check
+                // to see if it's still running, and fail it if it is.
+                if (isRunning()) {
+                    updateStateAndNotification(STATE_FAILED);
+                }
             }
             return false;
         }
@@ -492,6 +542,25 @@
             return viewIntent;
         }
 
+        PendingIntent buildCancelIntent() {
+            Intent intent = new Intent(ACTION_CANCEL_HANDOVER_TRANSFER);
+            intent.putExtra(EXTRA_SOURCE_ADDRESS, sourceAddress);
+            PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0);
+
+            return pi;
+        }
+
+        synchronized File generateUniqueDestination(String baseFileName) {
+            File dstFile = new File(baseFileName);
+            int count = 0;
+            while (dstFile.exists()) {
+                dstFile = new File(baseFileName + "-" + Integer.toString(count));
+                count++;
+            }
+
+            return dstFile;
+        }
+
         synchronized File generateMultiplePath(String beamRoot) {
             // Generate a unique directory with the date
             String format = "yyyy-MM-dd";
@@ -508,24 +577,27 @@
 
             return newFile;
         }
-
-
     }
 
-    synchronized HandoverTransfer getHandoverTransfer(String sourceAddress, boolean incoming) {
+    synchronized HandoverTransfer getOrCreateHandoverTransfer(String sourceAddress, boolean incoming,
+            boolean create) {
         Pair<String, Boolean> key = new Pair<String, Boolean>(sourceAddress, incoming);
         if (mTransfers.containsKey(key)) {
             HandoverTransfer transfer = mTransfers.get(key);
             if (transfer.isRunning()) {
                 return transfer;
             } else {
-                // Remove old transfer; new one will be created below
-                mTransfers.remove(key);
+                if (create) mTransfers.remove(key); // new one created below
             }
         }
-        HandoverTransfer transfer = new HandoverTransfer(sourceAddress, incoming);
-        mTransfers.put(key, transfer);
-        return transfer;
+        if (create) {
+            HandoverTransfer transfer = new HandoverTransfer(sourceAddress, incoming);
+            mTransfers.put(key, transfer);
+
+            return transfer;
+        } else {
+            return null;
+        }
     }
 
     public HandoverManager(Context context) {
@@ -543,6 +615,7 @@
         IntentFilter filter = new IntentFilter(ACTION_BT_OPP_TRANSFER_DONE);
         filter.addAction(ACTION_BT_OPP_TRANSFER_PROGRESS);
         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
+        filter.addAction(ACTION_CANCEL_HANDOVER_TRANSFER);
         mContext.registerReceiver(mReceiver, filter, HANDOVER_STATUS_PERMISSION, null);
     }
 
@@ -666,8 +739,8 @@
             }
 
             // Create the initial transfer object
-            HandoverTransfer transfer = getHandoverTransfer(bluetoothData.device.getAddress(),
-                    true);
+            HandoverTransfer transfer = getOrCreateHandoverTransfer(
+                    bluetoothData.device.getAddress(), true, true);
             transfer.updateNotification();
         }
 
@@ -715,6 +788,8 @@
     public void doHandoverUri(Uri[] uris, NdefMessage m) {
         BluetoothHandoverData data = parse(m);
         if (data != null && data.valid) {
+            // Register a new handover transfer object
+            getOrCreateHandoverTransfer(data.device.getAddress(), false, true);
             BluetoothOppHandover handover = new BluetoothOppHandover(mContext, data.device,
                 uris, mHandoverPowerManager, data.carrierActivating);
             handover.start();
@@ -928,9 +1003,16 @@
                 }
 
                 return;
+            } else if (action.equals(ACTION_CANCEL_HANDOVER_TRANSFER)) {
+                String sourceAddress = intent.getStringExtra(EXTRA_SOURCE_ADDRESS);
+                HandoverTransfer transfer = getOrCreateHandoverTransfer(sourceAddress, true,
+                        false);
+                if (transfer != null) {
+                    transfer.cancel();
+                }
             } else if (action.equals(ACTION_BT_OPP_TRANSFER_PROGRESS) ||
                     action.equals(ACTION_BT_OPP_TRANSFER_DONE)) {
-                // Clean up old transfers in progress
+                // Clean up old transfers no longer in progress
                 cleanupTransfers();
 
                 int direction = intent.getIntExtra(EXTRA_BT_OPP_TRANSFER_DIRECTION, -1);
@@ -939,8 +1021,18 @@
 
                 if (direction == -1 || id == -1 || sourceAddress == null) return;
                 boolean incoming = (direction == DIRECTION_BLUETOOTH_INCOMING);
-                HandoverTransfer transfer = getHandoverTransfer(sourceAddress, incoming);
-                if (transfer == null) return;
+
+                HandoverTransfer transfer = getOrCreateHandoverTransfer(sourceAddress, incoming,
+                        false);
+                if (transfer == null) {
+                    // There is no transfer running for this source address; most likely
+                    // the transfer was cancelled. We need to tell BT OPP to stop transferring
+                    // in case this was an incoming transfer
+                    Intent cancelIntent = new Intent("android.btopp.intent.action.STOP_HANDOVER_TRANSFER");
+                    cancelIntent.putExtra(EXTRA_BT_OPP_TRANSFER_ID, id);
+                    mContext.sendBroadcast(cancelIntent);
+                    return;
+                }
 
                 if (action.equals(ACTION_BT_OPP_TRANSFER_DONE)) {
                     int handoverStatus = intent.getIntExtra(EXTRA_BT_OPP_TRANSFER_STATUS,
@@ -963,8 +1055,6 @@
                 }
             }
         }
-
     };
 
-
 }