Fall back to NDEF push if handover SNEP get fails.

This allows applications to be able to set both
a Uri (for handover) and an NDEF message, which
will only be sent if the handover request does
not work (as will be the case on pre-J devices).

Also, show a toast if the remote device does
not support connection handover.

Bug: 6561174
Change-Id: I9ffdcfdf0cded319fbb4ab646ae1da5382fcb74a
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 1776811..50cbf63 100755
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -25,6 +25,7 @@
     <string name="beam_canceled">Beam canceled</string>
     <string name="cancel">Cancel</string>
     <string name="beam_touch_to_view">Touch to view</string>
+    <string name="beam_handover_not_supported">The receiver\'s device doesn\'t support large file transfer via beam.</string>
 
     <string name="connecting_headset">Connecting</string>
     <string name="connected_headset">Connected</string>
diff --git a/src/com/android/nfc/P2pEventManager.java b/src/com/android/nfc/P2pEventManager.java
index 9050288..c024afc 100644
--- a/src/com/android/nfc/P2pEventManager.java
+++ b/src/com/android/nfc/P2pEventManager.java
@@ -87,6 +87,16 @@
     }
 
     @Override
+    public void onP2pHandoverNotSupported() {
+        mNfcService.playSound(NfcService.SOUND_ERROR);
+        mVibrator.vibrate(VIBRATION_PATTERN, -1);
+        mSendUi.finishAndToast(SendUi.FINISH_SCALE_UP,
+                mContext.getString(R.string.beam_handover_not_supported));
+        mSending = false;
+        mNdefSent = false;
+    }
+
+    @Override
     public void onP2pReceiveComplete(boolean playSound) {
         mVibrator.vibrate(VIBRATION_PATTERN, -1);
         if (playSound) mNfcService.playSound(NfcService.SOUND_END);
diff --git a/src/com/android/nfc/P2pLinkManager.java b/src/com/android/nfc/P2pLinkManager.java
index 2f70f39..2bf14a4 100755
--- a/src/com/android/nfc/P2pLinkManager.java
+++ b/src/com/android/nfc/P2pLinkManager.java
@@ -77,6 +77,11 @@
     public void onP2pSendComplete();
 
     /**
+     * Called to indicate the remote device does not support connection handover
+     */
+    public void onP2pHandoverNotSupported();
+
+    /**
      * Called to indicate a receive was successful.
      */
     public void onP2pReceiveComplete(boolean playSound);
@@ -127,6 +132,7 @@
     static final int MSG_SEND_COMPLETE = 4;
     static final int MSG_START_ECHOSERVER = 5;
     static final int MSG_STOP_ECHOSERVER = 6;
+    static final int MSG_HANDOVER_NOT_SUPPORTED = 7;
 
     // values for mLinkState
     static final int LINK_STATE_DOWN = 1;
@@ -138,6 +144,10 @@
     static final int SEND_STATE_NEED_CONFIRMATION = 2;
     static final int SEND_STATE_SENDING = 3;
 
+    // return values for doSnepProtocol
+    static final int SNEP_SUCCESS = 0;
+    static final int SNEP_FAILURE = 1;
+    static final int SNEP_HANDOVER_UNSUPPORTED = 2;
 
     static final Uri PROFILE_URI = Profile.CONTENT_VCARD_URI.buildUpon().
             appendQueryParameter(Contacts.QUERY_PARAMETER_VCARD_NO_PHOTO, "true").
@@ -349,6 +359,10 @@
          }
      }
 
+    void onHandoverUnsupported() {
+        mHandler.sendEmptyMessage(MSG_HANDOVER_NOT_SUPPORTED);
+    }
+
     void onSendComplete(NdefMessage msg, long elapsedRealtime) {
         if (mFirstBeam) {
             EventLogTags.writeNfcFirstShare();
@@ -397,7 +411,22 @@
 
             try {
                 if (DBG) Log.d(TAG, "Sending ndef via SNEP");
-                result = doSnepProtocol(mHandoverManager, m, uris);
+
+                int snepResult = doSnepProtocol(mHandoverManager, m, uris);
+
+                switch (snepResult) {
+                    case SNEP_HANDOVER_UNSUPPORTED:
+                        onHandoverUnsupported();
+                        return null;
+                    case SNEP_SUCCESS:
+                        result = true;
+                        break;
+                    case SNEP_FAILURE:
+                        result = false;
+                        break;
+                    default:
+                        result = false;
+                }
             } catch (IOException e) {
                 Log.i(TAG, "Failed to connect over SNEP, trying NPP");
 
@@ -422,7 +451,7 @@
         }
     }
 
-    static boolean doSnepProtocol(HandoverManager handoverManager,
+    static int doSnepProtocol(HandoverManager handoverManager,
             NdefMessage msg, Uri[] uris) throws IOException {
         SnepClient snepClient = new SnepClient();
         try {
@@ -440,17 +469,26 @@
                 NdefMessage response = snepResponse.getNdefMessage();
                 if (response != null) {
                     handoverManager.doHandoverUri(uris, response);
+                } else if (msg != null) {
+                    // For backwards-compatibility to pre-J devices,
+                    // try to push an NDEF message (if any) if the handover GET
+                    // does not work.
+                    snepClient.put(msg);
+                } else {
+                    // We had a failed handover and no alternate message to
+                    // send; indicate remote device doesn't support handover.
+                    return SNEP_HANDOVER_UNSUPPORTED;
                 }
             } else if (msg != null) {
                 snepClient.put(msg);
             }
-            return true;
+            return SNEP_SUCCESS;
         } catch (IOException e) {
             // SNEP available but had errors, don't fall back to NPP.
         } finally {
             snepClient.close();
         }
-        return false;
+        return SNEP_FAILURE;
     }
 
     final NdefPushServer.Callback mNppCallback = new NdefPushServer.Callback() {
@@ -552,6 +590,18 @@
                     NfcService.getInstance().sendMockNdefTag(m);
                 }
                 break;
+            case MSG_HANDOVER_NOT_SUPPORTED:
+                synchronized (P2pLinkManager.this) {
+                    mSendTask = null;
+
+                    if (mLinkState == LINK_STATE_DOWN || mSendState != SEND_STATE_SENDING) {
+                        break;
+                    }
+                    mSendState = SEND_STATE_NOTHING_TO_SEND;
+                    if (DBG) Log.d(TAG, "onP2pHandoverNotSupported()");
+                    mEventListener.onP2pHandoverNotSupported();
+                }
+                break;
             case MSG_SEND_COMPLETE:
                 synchronized (P2pLinkManager.this) {
                     mSendTask = null;
@@ -567,7 +617,6 @@
                             mCallbackNdef.onNdefPushComplete();
                         } catch (RemoteException e) { }
                     }
-                    mSendTask = null;
                 }
                 break;
         }
diff --git a/src/com/android/nfc/SendUi.java b/src/com/android/nfc/SendUi.java
index 5c20085..23602c9 100644
--- a/src/com/android/nfc/SendUi.java
+++ b/src/com/android/nfc/SendUi.java
@@ -45,6 +45,7 @@
 import android.view.animation.DecelerateInterpolator;
 import android.widget.ImageView;
 import android.widget.TextView;
+import android.widget.Toast;
 
 /**
  * This class is responsible for handling the UI animation
@@ -139,6 +140,7 @@
     final boolean mHardwareAccelerated;
     final FireflyRenderer mFireflyRenderer;
 
+    String mToastString;
     Bitmap mScreenshotBitmap;
 
     boolean mAttached;
@@ -305,6 +307,7 @@
         // Disable statusbar pull-down
         mStatusBarManager.disable(StatusBarManager.DISABLE_EXPAND);
 
+        mToastString = null;
         mSending = false;
         mAttached = true;
 
@@ -329,6 +332,13 @@
         mSlowSendAnimator.start();
     }
 
+    public void finishAndToast(int finishMode, String toast) {
+        if (!mAttached) return;
+        mToastString = toast;
+
+        finish(finishMode);
+    }
+
     /** Return to initial state */
     public void finish(int finishMode) {
         if (!mAttached) return;
@@ -389,6 +399,10 @@
         mWindowManager.removeView(mScreenshotLayout);
         mStatusBarManager.disable(StatusBarManager.DISABLE_NONE);
         releaseScreenshot();
+        if (mToastString != null) {
+            Toast.makeText(mContext, mToastString, Toast.LENGTH_LONG).show();
+        }
+        mToastString = null;
     }
 
     public void releaseScreenshot() {