1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.nfc;
18 
19 import android.content.Intent;
20 import android.content.pm.UserInfo;
21 
22 import com.android.nfc.beam.BeamManager;
23 import com.android.nfc.beam.BeamSendService;
24 import com.android.nfc.beam.BeamTransferRecord;
25 
26 import android.os.UserManager;
27 import com.android.nfc.echoserver.EchoServer;
28 import com.android.nfc.handover.HandoverClient;
29 import com.android.nfc.handover.HandoverDataParser;
30 import com.android.nfc.handover.HandoverServer;
31 import com.android.nfc.ndefpush.NdefPushClient;
32 import com.android.nfc.ndefpush.NdefPushServer;
33 import com.android.nfc.snep.SnepClient;
34 import com.android.nfc.snep.SnepMessage;
35 import com.android.nfc.snep.SnepServer;
36 
37 import android.content.Context;
38 import android.content.SharedPreferences;
39 import android.content.pm.ApplicationInfo;
40 import android.content.pm.PackageManager;
41 import android.content.pm.PackageManager.NameNotFoundException;
42 import android.net.Uri;
43 import android.nfc.BeamShareData;
44 import android.nfc.IAppCallback;
45 import android.nfc.NdefMessage;
46 import android.nfc.NdefRecord;
47 import android.nfc.NfcAdapter;
48 import android.os.AsyncTask;
49 import android.os.Handler;
50 import android.os.Message;
51 import android.os.SystemClock;
52 import android.os.UserHandle;
53 import android.util.Log;
54 import java.io.FileDescriptor;
55 import java.io.IOException;
56 import java.io.PrintWriter;
57 import java.nio.charset.StandardCharsets;
58 import java.util.Arrays;
59 import java.util.List;
60 
61 /**
62  * Interface to listen for P2P events.
63  * All callbacks are made from the UI thread.
64  */
65 interface P2pEventListener {
66     /**
67      * Indicates the user has expressed an intent to share
68      * over NFC, but a remote device has not come into range
69      * yet. Prompt the user to NFC tap.
70      */
onP2pNfcTapRequested()71     public void onP2pNfcTapRequested();
72 
73     /**
74      * Indicates the user has expressed an intent to share over
75      * NFC, but the link hasn't come up yet and we no longer
76      * want to wait for it
77      */
onP2pTimeoutWaitingForLink()78     public void onP2pTimeoutWaitingForLink();
79 
80     /**
81      * Indicates a P2P device is in range.
82      * <p>onP2pInRange() and onP2pOutOfRange() will always be called
83      * alternately.
84      */
onP2pInRange()85     public void onP2pInRange();
86 
87     /**
88      * Called when a NDEF payload is prepared to send, and confirmation is
89      * required. Call Callback.onP2pSendConfirmed() to make the confirmation.
90      */
onP2pSendConfirmationRequested()91     public void onP2pSendConfirmationRequested();
92 
93     /**
94      * Called to indicate a send was successful.
95      */
onP2pSendComplete()96     public void onP2pSendComplete();
97 
98     /**
99      *
100      * Called to indicate the link has broken while we were trying to send
101      * a message. We'll start a debounce timer for the user to get the devices
102      * back together. UI may show a hint to achieve that
103      */
onP2pSendDebounce()104     public void onP2pSendDebounce();
105 
106     /**
107      * Called to indicate a link has come back up after being temporarily
108      * broken, and sending is resuming
109      */
onP2pResumeSend()110     public void onP2pResumeSend();
111 
112     /**
113      * Called to indicate the remote device does not support connection handover
114      */
onP2pHandoverNotSupported()115     public void onP2pHandoverNotSupported();
116 
117     /**
118      * Called to indicate the device is busy with another handover transfer
119      */
onP2pHandoverBusy()120     public void onP2pHandoverBusy();
121 
122     /**
123      * Called to indicate a receive was successful.
124      */
onP2pReceiveComplete(boolean playSound)125     public void onP2pReceiveComplete(boolean playSound);
126 
127     /**
128      * Indicates the P2P device went out of range.
129      */
onP2pOutOfRange()130     public void onP2pOutOfRange();
131 
132     public interface Callback {
onP2pSendConfirmed()133         public void onP2pSendConfirmed();
onP2pCanceled()134         public void onP2pCanceled();
135     }
136 }
137 
138 /**
139  * Manages sending and receiving NDEF message over LLCP link.
140  * Does simple debouncing of the LLCP link - so that even if the link
141  * drops and returns the user does not know.
142  */
143 class P2pLinkManager implements Handler.Callback, P2pEventListener.Callback {
144     static final String TAG = "NfcP2pLinkManager";
145     static final boolean DBG = true;
146 
147     /** Include this constant as a meta-data entry in the manifest
148      *  of an application to disable beaming the market/AAR link, like this:
149      *  <pre>{@code
150      *  <application ...>
151      *      <meta-data android:name="android.nfc.disable_beam_default"
152      *          android:value="true" />
153      *  </application>
154      *  }</pre>
155      */
156     static final String DISABLE_BEAM_DEFAULT = "android.nfc.disable_beam_default";
157 
158     /** Enables the LLCP EchoServer, which can be used to test the android
159      * LLCP stack against nfcpy.
160      */
161     static final boolean ECHOSERVER_ENABLED = false;
162 
163     // TODO dynamically assign SAP values
164     static final int NDEFPUSH_SAP = 0x10;
165     static final int HANDOVER_SAP = 0x14;
166 
167     static final int LINK_SEND_PENDING_DEBOUNCE_MS = 3000;
168     static final int LINK_SEND_CONFIRMED_DEBOUNCE_MS = 5000;
169     static final int LINK_SEND_COMPLETE_DEBOUNCE_MS = 500;
170     static final int LINK_SEND_CANCELED_DEBOUNCE_MS = 250;
171 
172     // The amount of time we wait for the link to come up
173     // after a user has manually invoked Beam.
174     static final int WAIT_FOR_LINK_TIMEOUT_MS = 10000;
175 
176     static final int MSG_DEBOUNCE_TIMEOUT = 1;
177     static final int MSG_RECEIVE_COMPLETE = 2;
178     static final int MSG_RECEIVE_HANDOVER = 3;
179     static final int MSG_SEND_COMPLETE = 4;
180     static final int MSG_START_ECHOSERVER = 5;
181     static final int MSG_STOP_ECHOSERVER = 6;
182     static final int MSG_HANDOVER_NOT_SUPPORTED = 7;
183     static final int MSG_SHOW_CONFIRMATION_UI = 8;
184     static final int MSG_WAIT_FOR_LINK_TIMEOUT = 9;
185     static final int MSG_HANDOVER_BUSY = 10;
186 
187     // values for mLinkState
188     static final int LINK_STATE_DOWN = 1;
189     static final int LINK_STATE_UP = 2;
190     static final int LINK_STATE_DEBOUNCE = 3;
191 
192     // values for mSendState
193     static final int SEND_STATE_NOTHING_TO_SEND = 1;
194     static final int SEND_STATE_NEED_CONFIRMATION = 2;
195     static final int SEND_STATE_PENDING = 3;
196     static final int SEND_STATE_SENDING = 4;
197     static final int SEND_STATE_COMPLETE = 5;
198     static final int SEND_STATE_CANCELED = 6;
199 
200     // return values for doSnepProtocol
201     static final int SNEP_SUCCESS = 0;
202     static final int SNEP_FAILURE = 1;
203 
204     // return values for doHandover
205     static final int HANDOVER_SUCCESS = 0;
206     static final int HANDOVER_FAILURE = 1;
207     static final int HANDOVER_UNSUPPORTED = 2;
208     static final int HANDOVER_BUSY = 3;
209 
210     final NdefPushServer mNdefPushServer;
211     final SnepServer mDefaultSnepServer;
212     final HandoverServer mHandoverServer;
213     final EchoServer mEchoServer;
214     final Context mContext;
215     final P2pEventListener mEventListener;
216     final Handler mHandler;
217     final HandoverDataParser mHandoverDataParser;
218     final ForegroundUtils mForegroundUtils;
219 
220     final int mDefaultMiu;
221     final int mDefaultRwSize;
222 
223     // Locked on NdefP2pManager.this
224     PackageManager mPackageManager;
225     int mLinkState;
226     int mSendState;  // valid during LINK_STATE_UP or LINK_STATE_DEBOUNCE
227     boolean mIsSendEnabled;
228     boolean mIsReceiveEnabled;
229     NdefMessage mMessageToSend;  // not valid in SEND_STATE_NOTHING_TO_SEND
230     Uri[] mUrisToSend;  // not valid in SEND_STATE_NOTHING_TO_SEND
231     UserHandle mUserHandle; // not valid in SEND_STATE_NOTHING_TO_SEND
232     int mSendFlags; // not valid in SEND_STATE_NOTHING_TO_SEND
233     IAppCallback mCallbackNdef;
234     int mNdefCallbackUid;
235     SendTask mSendTask;
236     SharedPreferences mPrefs;
237     SnepClient mSnepClient;
238     HandoverClient mHandoverClient;
239     NdefPushClient mNdefPushClient;
240     ConnectTask mConnectTask;
241     boolean mLlcpServicesConnected;
242     long mLastLlcpActivationTime;
243     byte mPeerLlcpVersion;
244 
P2pLinkManager(Context context, HandoverDataParser handoverDataParser, int defaultMiu, int defaultRwSize)245     public P2pLinkManager(Context context, HandoverDataParser handoverDataParser, int defaultMiu,
246             int defaultRwSize) {
247         mNdefPushServer = new NdefPushServer(NDEFPUSH_SAP, mNppCallback);
248         mDefaultSnepServer = new SnepServer(mDefaultSnepCallback, defaultMiu, defaultRwSize);
249         mHandoverServer = new HandoverServer(context, HANDOVER_SAP, handoverDataParser, mHandoverCallback);
250 
251         if (ECHOSERVER_ENABLED) {
252             mEchoServer = new EchoServer();
253         } else {
254             mEchoServer = null;
255         }
256         mPackageManager = context.getPackageManager();
257         mContext = context;
258         mEventListener = new P2pEventManager(context, this);
259         mHandler = new Handler(this);
260         mLinkState = LINK_STATE_DOWN;
261         mSendState = SEND_STATE_NOTHING_TO_SEND;
262         mIsSendEnabled = false;
263         mIsReceiveEnabled = false;
264         mPrefs = context.getSharedPreferences(NfcService.PREF, Context.MODE_PRIVATE);
265         mHandoverDataParser = handoverDataParser;
266         mDefaultMiu = defaultMiu;
267         mDefaultRwSize = defaultRwSize;
268         mLlcpServicesConnected = false;
269         mNdefCallbackUid = -1;
270         mForegroundUtils = ForegroundUtils.getInstance();
271      }
272 
273     /**
274      * May be called from any thread.
275      * Assumes that NFC is already on if any parameter is true.
276      */
enableDisable(boolean sendEnable, boolean receiveEnable)277     public void enableDisable(boolean sendEnable, boolean receiveEnable) {
278         synchronized (this) {
279             if (!mIsReceiveEnabled && receiveEnable) {
280                 mDefaultSnepServer.start();
281                 mNdefPushServer.start();
282                 mHandoverServer.start();
283                 if (mEchoServer != null) {
284                     mHandler.sendEmptyMessage(MSG_START_ECHOSERVER);
285                 }
286             } else if (mIsReceiveEnabled && !receiveEnable) {
287                 if (DBG) Log.d(TAG, "enableDisable: llcp deactivate");
288                 onLlcpDeactivated ();
289                 mDefaultSnepServer.stop();
290                 mNdefPushServer.stop();
291                 mHandoverServer.stop();
292                 if (mEchoServer != null) {
293                     mHandler.sendEmptyMessage(MSG_STOP_ECHOSERVER);
294                 }
295             }
296             mIsSendEnabled = sendEnable;
297             mIsReceiveEnabled = receiveEnable;
298         }
299     }
300 
301     /**
302      * May be called from any thread.
303      * @return whether the LLCP link is in an active or debounce state
304      */
isLlcpActive()305     public boolean isLlcpActive() {
306         synchronized (this) {
307             return mLinkState != LINK_STATE_DOWN;
308         }
309     }
310 
311     /**
312      * Set NDEF callback for sending.
313      * May be called from any thread.
314      * NDEF callbacks may be set at any time (even if NFC is
315      * currently off or P2P send is currently off). They will become
316      * active as soon as P2P send is enabled.
317      */
setNdefCallback(IAppCallback callbackNdef, int callingUid)318     public void setNdefCallback(IAppCallback callbackNdef, int callingUid) {
319         synchronized (this) {
320             mCallbackNdef = callbackNdef;
321             mNdefCallbackUid = callingUid;
322         }
323     }
324 
325 
onManualBeamInvoke(BeamShareData shareData)326     public void onManualBeamInvoke(BeamShareData shareData) {
327         synchronized (P2pLinkManager.this)    {
328             if (mLinkState != LINK_STATE_DOWN) {
329                 return;
330             }
331             if (mForegroundUtils.getForegroundUids().contains(mNdefCallbackUid)) {
332                 // Try to get data from the registered NDEF callback
333                 prepareMessageToSend(false);
334             } else {
335                 mMessageToSend = null;
336                 mUrisToSend = null;
337             }
338             if (mMessageToSend == null && mUrisToSend == null && shareData != null) {
339                 // No data from the NDEF callback, get data from ShareData
340                 if (shareData.uris != null) {
341                     mUrisToSend = shareData.uris;
342                 } else if (shareData.ndefMessage != null) {
343                     mMessageToSend = shareData.ndefMessage;
344                 }
345 
346                 mUserHandle = shareData.userHandle;
347             }
348             if (mMessageToSend != null ||
349                     (mUrisToSend != null && mHandoverDataParser.isHandoverSupported())) {
350                 mSendState = SEND_STATE_PENDING;
351                 mEventListener.onP2pNfcTapRequested();
352                 scheduleTimeoutLocked(MSG_WAIT_FOR_LINK_TIMEOUT, WAIT_FOR_LINK_TIMEOUT_MS);
353             }
354         }
355     }
356 
357     /**
358      * Must be called on UI Thread.
359      */
onLlcpActivated(byte peerLlcpVersion)360     public void onLlcpActivated(byte peerLlcpVersion) {
361         Log.i(TAG, "LLCP activated");
362         synchronized (P2pLinkManager.this) {
363             if (mEchoServer != null) {
364                 mEchoServer.onLlcpActivated();
365             }
366             mLastLlcpActivationTime = SystemClock.elapsedRealtime();
367             mPeerLlcpVersion = peerLlcpVersion;
368             switch (mLinkState) {
369                 case LINK_STATE_DOWN:
370                     if (DBG) Log.d(TAG, "onP2pInRange()");
371                     // Start taking a screenshot
372                     mEventListener.onP2pInRange();
373                     mLinkState = LINK_STATE_UP;
374                     // If we had a pending send (manual Beam invoke),
375                     // mark it as sending
376                     if (mSendState == SEND_STATE_PENDING) {
377                         mSendState = SEND_STATE_SENDING;
378                         mHandler.removeMessages(MSG_WAIT_FOR_LINK_TIMEOUT);
379                         // Immediately try to connect LLCP services
380                         connectLlcpServices();
381                     } else {
382                         mSendState = SEND_STATE_NOTHING_TO_SEND;
383                         prepareMessageToSend(true);
384                         if (mMessageToSend != null ||
385                                 (mUrisToSend != null && mHandoverDataParser.isHandoverSupported())) {
386                             // We have data to send, connect LLCP services
387                             connectLlcpServices();
388                             if ((mSendFlags & NfcAdapter.FLAG_NDEF_PUSH_NO_CONFIRM) != 0) {
389                                 mSendState = SEND_STATE_SENDING;
390                             } else {
391                                 mSendState = SEND_STATE_NEED_CONFIRMATION;
392                             }
393                         }
394                     }
395                     break;
396                 case LINK_STATE_UP:
397                     if (DBG) Log.d(TAG, "Duplicate onLlcpActivated()");
398                     return;
399                 case LINK_STATE_DEBOUNCE:
400                     // Immediately connect and try to send again
401                     mLinkState = LINK_STATE_UP;
402                     if (mSendState == SEND_STATE_SENDING ||
403                             mSendState == SEND_STATE_NEED_CONFIRMATION) {
404                         // If we have something to send still, connect LLCP
405                         connectLlcpServices();
406                     }
407                     mHandler.removeMessages(MSG_DEBOUNCE_TIMEOUT);
408                     break;
409             }
410         }
411     }
412 
413     /**
414      * Must be called on UI Thread.
415      */
onLlcpFirstPacketReceived()416     public void onLlcpFirstPacketReceived() {
417         synchronized (P2pLinkManager.this) {
418             long totalTime = SystemClock.elapsedRealtime() - mLastLlcpActivationTime;
419             if (DBG) Log.d(TAG, "Took " + Long.toString(totalTime) + " to get first LLCP PDU");
420         }
421     }
422 
onUserSwitched(int userId)423     public void onUserSwitched(int userId) {
424         // Update the cached package manager in case of user switch
425         synchronized (P2pLinkManager.this) {
426             try {
427                 mPackageManager  = mContext.createPackageContextAsUser("android", 0,
428                         new UserHandle(userId)).getPackageManager();
429             } catch (NameNotFoundException e) {
430                 Log.e(TAG, "Failed to retrieve PackageManager for user");
431             }
432         }
433     }
434 
prepareMessageToSend(boolean generatePlayLink)435     void prepareMessageToSend(boolean generatePlayLink) {
436         synchronized (P2pLinkManager.this) {
437             mMessageToSend = null;
438             mUrisToSend = null;
439             if (!mIsSendEnabled) {
440                 return;
441             }
442 
443             List<Integer> foregroundUids = mForegroundUtils.getForegroundUids();
444             if (foregroundUids.isEmpty()) {
445                 Log.e(TAG, "Could not determine foreground UID.");
446                 return;
447             }
448 
449             if (isBeamDisabled(foregroundUids.get(0))) {
450                 if (DBG) Log.d(TAG, "Beam is disabled by policy.");
451                 return;
452             }
453 
454             if (mCallbackNdef != null) {
455                 if (foregroundUids.contains(mNdefCallbackUid)) {
456                     try {
457                         BeamShareData shareData = mCallbackNdef.createBeamShareData(mPeerLlcpVersion);
458                         mMessageToSend = shareData.ndefMessage;
459                         mUrisToSend = shareData.uris;
460                         mUserHandle = shareData.userHandle;
461                         mSendFlags = shareData.flags;
462                         return;
463                     } catch (Exception e) {
464                         Log.e(TAG, "Failed NDEF callback: ", e);
465                     }
466                 } else {
467                     // This is not necessarily an error - we no longer unset callbacks from
468                     // the app process itself (to prevent IPC calls on every pause).
469                     // Hence it may simply be a stale callback.
470                     if (DBG) Log.d(TAG, "Last registered callback is not running in the foreground.");
471                 }
472             }
473 
474             // fall back to default NDEF for the foreground activity, unless the
475             // application disabled this explicitly in their manifest.
476             String[] pkgs = mPackageManager.getPackagesForUid(foregroundUids.get(0));
477             if (pkgs != null && pkgs.length >= 1) {
478                 if (!generatePlayLink || beamDefaultDisabled(pkgs[0])) {
479                     if (DBG) Log.d(TAG, "Disabling default Beam behavior");
480                     mMessageToSend = null;
481                     mUrisToSend = null;
482                 } else {
483                     mMessageToSend = createDefaultNdef(pkgs[0]);
484                     mUrisToSend = null;
485                 }
486             }
487 
488             if (DBG) Log.d(TAG, "mMessageToSend = " + mMessageToSend);
489             if (DBG) Log.d(TAG, "mUrisToSend = " + mUrisToSend);
490         }
491     }
492 
isBeamDisabled(int uid)493     private boolean isBeamDisabled(int uid) {
494         UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
495         UserInfo userInfo = userManager.getUserInfo(UserHandle.getUserId(uid));
496         return userManager.hasUserRestriction(
497                         UserManager.DISALLOW_OUTGOING_BEAM, userInfo.getUserHandle());
498 
499     }
500 
beamDefaultDisabled(String pkgName)501     boolean beamDefaultDisabled(String pkgName) {
502         try {
503             ApplicationInfo ai = mPackageManager.getApplicationInfo(pkgName,
504                     PackageManager.GET_META_DATA);
505             if (ai == null || ai.metaData == null) {
506                 return false;
507             }
508             return ai.metaData.getBoolean(DISABLE_BEAM_DEFAULT);
509         } catch (NameNotFoundException e) {
510             return false;
511         }
512     }
513 
createDefaultNdef(String pkgName)514     NdefMessage createDefaultNdef(String pkgName) {
515         NdefRecord appUri = NdefRecord.createUri(Uri.parse(
516                 "http://play.google.com/store/apps/details?id=" + pkgName + "&feature=beam"));
517         NdefRecord appRecord = NdefRecord.createApplicationRecord(pkgName);
518         return new NdefMessage(new NdefRecord[] { appUri, appRecord });
519     }
520 
disconnectLlcpServices()521     void disconnectLlcpServices() {
522         synchronized (this) {
523             if (mConnectTask != null) {
524                 mConnectTask.cancel(true);
525                 mConnectTask = null;
526             }
527             // Close any already connected LLCP clients
528             if (mNdefPushClient != null) {
529                 mNdefPushClient.close();
530                 mNdefPushClient = null;
531             }
532             if (mSnepClient != null) {
533                 mSnepClient.close();
534                 mSnepClient = null;
535             }
536             if (mHandoverClient != null) {
537                 mHandoverClient.close();
538                 mHandoverClient = null;
539             }
540             mLlcpServicesConnected = false;
541         }
542     }
543 
544     /**
545      * Must be called on UI Thread.
546      */
onLlcpDeactivated()547     public void onLlcpDeactivated() {
548         Log.i(TAG, "LLCP deactivated.");
549         synchronized (this) {
550             if (mEchoServer != null) {
551                 mEchoServer.onLlcpDeactivated();
552             }
553 
554             switch (mLinkState) {
555                 case LINK_STATE_DOWN:
556                 case LINK_STATE_DEBOUNCE:
557                     Log.i(TAG, "Duplicate onLlcpDectivated()");
558                     break;
559                 case LINK_STATE_UP:
560                     // Debounce
561                     mLinkState = LINK_STATE_DEBOUNCE;
562                     int debounceTimeout = 0;
563                     switch (mSendState) {
564                         case SEND_STATE_NOTHING_TO_SEND:
565                             debounceTimeout = 0;
566                             break;
567                         case SEND_STATE_NEED_CONFIRMATION:
568                             debounceTimeout = LINK_SEND_PENDING_DEBOUNCE_MS;
569                             break;
570                         case SEND_STATE_SENDING:
571                             debounceTimeout = LINK_SEND_CONFIRMED_DEBOUNCE_MS;
572                             break;
573                         case SEND_STATE_COMPLETE:
574                             debounceTimeout = LINK_SEND_COMPLETE_DEBOUNCE_MS;
575                             break;
576                         case SEND_STATE_CANCELED:
577                             debounceTimeout = LINK_SEND_CANCELED_DEBOUNCE_MS;
578                     }
579                     scheduleTimeoutLocked(MSG_DEBOUNCE_TIMEOUT, debounceTimeout);
580                     if (mSendState == SEND_STATE_SENDING) {
581                         Log.e(TAG, "onP2pSendDebounce()");
582                         mEventListener.onP2pSendDebounce();
583                     }
584                     cancelSendNdefMessage();
585                     disconnectLlcpServices();
586                     break;
587             }
588          }
589      }
590 
onHandoverUnsupported()591     void onHandoverUnsupported() {
592         mHandler.sendEmptyMessage(MSG_HANDOVER_NOT_SUPPORTED);
593     }
594 
onHandoverBusy()595     void onHandoverBusy() {
596         mHandler.sendEmptyMessage(MSG_HANDOVER_BUSY);
597     }
598 
onSendComplete(NdefMessage msg, long elapsedRealtime)599     void onSendComplete(NdefMessage msg, long elapsedRealtime) {
600         // Make callbacks on UI thread
601         mHandler.sendEmptyMessage(MSG_SEND_COMPLETE);
602     }
603 
sendNdefMessage()604     void sendNdefMessage() {
605         synchronized (this) {
606             cancelSendNdefMessage();
607             mSendTask = new SendTask();
608             mSendTask.execute();
609         }
610     }
611 
cancelSendNdefMessage()612     void cancelSendNdefMessage() {
613         synchronized (P2pLinkManager.this) {
614             if (mSendTask != null) {
615                 mSendTask.cancel(true);
616             }
617         }
618     }
619 
connectLlcpServices()620     void connectLlcpServices() {
621         synchronized (P2pLinkManager.this) {
622             if (mConnectTask != null) {
623                 Log.e(TAG, "Still had a reference to mConnectTask!");
624             }
625             mConnectTask = new ConnectTask();
626             mConnectTask.execute();
627         }
628     }
629 
630     // Must be called on UI-thread
onLlcpServicesConnected()631     void onLlcpServicesConnected() {
632         if (DBG) Log.d(TAG, "onLlcpServicesConnected");
633         synchronized (P2pLinkManager.this) {
634             if (mLinkState != LINK_STATE_UP) {
635                 return;
636             }
637             mLlcpServicesConnected = true;
638             if (mSendState == SEND_STATE_NEED_CONFIRMATION) {
639                 if (DBG) Log.d(TAG, "onP2pSendConfirmationRequested()");
640                 mEventListener.onP2pSendConfirmationRequested();
641             } else if (mSendState == SEND_STATE_SENDING) {
642                 mEventListener.onP2pResumeSend();
643                 sendNdefMessage();
644             } else {
645                 // Either nothing to send or canceled/complete, ignore
646             }
647         }
648     }
649 
650     final class ConnectTask extends AsyncTask<Void, Void, Boolean> {
651         @Override
onPostExecute(Boolean result)652         protected void onPostExecute(Boolean result)  {
653             if (isCancelled()) {
654                 if (DBG) Log.d(TAG, "ConnectTask was cancelled");
655                 return;
656             }
657             if (result) {
658                 onLlcpServicesConnected();
659             } else {
660                 Log.e(TAG, "Could not connect required NFC transports");
661             }
662         }
663 
664         @Override
doInBackground(Void... params)665         protected Boolean doInBackground(Void... params) {
666             boolean needsHandover = false;
667             boolean needsNdef = false;
668             boolean success = false;
669             HandoverClient handoverClient = null;
670             SnepClient snepClient = null;
671             NdefPushClient nppClient = null;
672 
673             synchronized(P2pLinkManager.this) {
674                 if (mUrisToSend != null) {
675                     needsHandover = true;
676                 }
677 
678                 if (mMessageToSend != null) {
679                     needsNdef = true;
680                 }
681             }
682             // We know either is requested - otherwise this task
683             // wouldn't have been started.
684             if (needsHandover) {
685                 handoverClient = new HandoverClient();
686                 try {
687                     handoverClient.connect();
688                     success = true; // Regardless of NDEF result
689                 } catch (IOException e) {
690                     handoverClient = null;
691                 }
692             }
693 
694             if (needsNdef || (needsHandover && handoverClient == null)) {
695                 snepClient = new SnepClient();
696                 try {
697                     snepClient.connect();
698                     success = true;
699                 } catch (IOException e) {
700                     snepClient = null;
701                 }
702 
703                 if (!success) {
704                     nppClient = new NdefPushClient();
705                     try {
706                         nppClient.connect();
707                         success = true;
708                     } catch (IOException e) {
709                         nppClient = null;
710                     }
711                 }
712             }
713 
714             synchronized (P2pLinkManager.this) {
715                 if (isCancelled()) {
716                     // Cancelled by onLlcpDeactivated on UI thread
717                     if (handoverClient != null) {
718                         handoverClient.close();
719                     }
720                     if (snepClient != null) {
721                         snepClient.close();
722                     }
723                     if (nppClient != null) {
724                         nppClient.close();
725                     }
726                     return false;
727                 } else {
728                     // Once assigned, these are the responsibility of
729                     // the code on the UI thread to release - typically
730                     // through onLlcpDeactivated().
731                     mHandoverClient = handoverClient;
732                     mSnepClient = snepClient;
733                     mNdefPushClient = nppClient;
734                     return success;
735                 }
736             }
737         }
738     };
739 
740     final class SendTask extends AsyncTask<Void, Void, Void> {
741         NdefPushClient nppClient;
742         SnepClient snepClient;
743         HandoverClient handoverClient;
744 
doHandover(Uri[] uris, UserHandle userHandle)745         int doHandover(Uri[] uris, UserHandle userHandle) throws IOException {
746             NdefMessage response = null;
747             BeamManager beamManager = BeamManager.getInstance();
748 
749             if (beamManager.isBeamInProgress()) {
750                 return HANDOVER_BUSY;
751             }
752 
753             NdefMessage request = mHandoverDataParser.createHandoverRequestMessage();
754             if (request != null) {
755                 if (handoverClient != null) {
756                     response = handoverClient.sendHandoverRequest(request);
757                 }
758                 if (response == null && snepClient != null) {
759                     // Remote device may not support handover service,
760                     // try the (deprecated) SNEP GET implementation
761                     // for devices running Android 4.1
762                     SnepMessage snepResponse = snepClient.get(request);
763                     response = snepResponse.getNdefMessage();
764                 }
765                 if (response == null) {
766                     return HANDOVER_UNSUPPORTED;
767                 }
768             } else {
769                 return HANDOVER_UNSUPPORTED;
770             }
771 
772             if (!beamManager.startBeamSend(mContext,
773                     mHandoverDataParser.getOutgoingHandoverData(response), uris, userHandle)) {
774                 return HANDOVER_BUSY;
775             }
776 
777             return HANDOVER_SUCCESS;
778         }
779 
doSnepProtocol(NdefMessage msg)780         int doSnepProtocol(NdefMessage msg) throws IOException {
781             if (msg != null) {
782                 snepClient.put(msg);
783                 return SNEP_SUCCESS;
784             } else {
785                 return SNEP_FAILURE;
786             }
787         }
788 
789         @Override
doInBackground(Void... args)790         public Void doInBackground(Void... args) {
791             NdefMessage m;
792             Uri[] uris;
793             UserHandle userHandle;
794             boolean result = false;
795 
796             synchronized (P2pLinkManager.this) {
797                 if (mLinkState != LINK_STATE_UP || mSendState != SEND_STATE_SENDING) {
798                     return null;
799                 }
800                 m = mMessageToSend;
801                 uris = mUrisToSend;
802                 userHandle = mUserHandle;
803                 snepClient = mSnepClient;
804                 handoverClient = mHandoverClient;
805                 nppClient = mNdefPushClient;
806             }
807 
808             long time = SystemClock.elapsedRealtime();
809 
810             if (uris != null) {
811                 if (DBG) Log.d(TAG, "Trying handover request");
812                 try {
813                     int handoverResult = doHandover(uris, userHandle);
814                     switch (handoverResult) {
815                         case HANDOVER_SUCCESS:
816                             result = true;
817                             break;
818                         case HANDOVER_FAILURE:
819                             result = false;
820                             break;
821                         case HANDOVER_UNSUPPORTED:
822                             result = false;
823                             onHandoverUnsupported();
824                             break;
825                         case HANDOVER_BUSY:
826                             result = false;
827                             onHandoverBusy();
828                             break;
829                     }
830                 } catch (IOException e) {
831                     result = false;
832                 }
833             }
834 
835             if (!result && m != null && snepClient != null) {
836                 if (DBG) Log.d(TAG, "Sending ndef via SNEP");
837                 try {
838                     int snepResult = doSnepProtocol(m);
839                     switch (snepResult) {
840                         case SNEP_SUCCESS:
841                             result = true;
842                             break;
843                         case SNEP_FAILURE:
844                             result = false;
845                             break;
846                         default:
847                             result = false;
848                     }
849                 } catch (IOException e) {
850                     result = false;
851                 }
852             }
853 
854             if (!result && m != null && nppClient != null) {
855                 result = nppClient.push(m);
856             }
857 
858             time = SystemClock.elapsedRealtime() - time;
859             if (DBG) Log.d(TAG, "SendTask result=" + result + ", time ms=" + time);
860             if (result) {
861                 onSendComplete(m, time);
862             }
863 
864             return null;
865         }
866     };
867 
868 
869     final HandoverServer.Callback mHandoverCallback = new HandoverServer.Callback() {
870         @Override
871         public void onHandoverRequestReceived() {
872             onReceiveHandover();
873         }
874 
875         @Override
876         public void onHandoverBusy() {
877             P2pLinkManager.this.onHandoverBusy();
878         }
879     };
880 
881     final NdefPushServer.Callback mNppCallback = new NdefPushServer.Callback() {
882         @Override
883         public void onMessageReceived(NdefMessage msg) {
884             onReceiveComplete(msg);
885         }
886     };
887 
888     final SnepServer.Callback mDefaultSnepCallback = new SnepServer.Callback() {
889         @Override
890         public SnepMessage doPut(NdefMessage msg) {
891             onReceiveComplete(msg);
892             return SnepMessage.getMessage(SnepMessage.RESPONSE_SUCCESS);
893         }
894 
895         @Override
896         public SnepMessage doGet(int acceptableLength, NdefMessage msg) {
897             // The NFC Forum Default SNEP server is not allowed to respond to
898             // SNEP GET requests - see SNEP 1.0 TS section 6.1. However,
899             // since Android 4.1 used the NFC Forum default server to
900             // implement connection handover, we will support this
901             // until we can deprecate it.
902             NdefMessage response = mHandoverDataParser.getIncomingHandoverData(msg).handoverSelect;
903             if (response != null) {
904                 onReceiveHandover();
905                 return SnepMessage.getSuccessResponse(response);
906             } else {
907                 return SnepMessage.getMessage(SnepMessage.RESPONSE_NOT_IMPLEMENTED);
908             }
909         }
910     };
911 
onReceiveHandover()912     void onReceiveHandover() {
913         mHandler.obtainMessage(MSG_RECEIVE_HANDOVER).sendToTarget();
914     }
915 
onReceiveComplete(NdefMessage msg)916     void onReceiveComplete(NdefMessage msg) {
917         // Make callbacks on UI thread
918         mHandler.obtainMessage(MSG_RECEIVE_COMPLETE, msg).sendToTarget();
919     }
920 
921     @Override
handleMessage(Message msg)922     public boolean handleMessage(Message msg) {
923         switch (msg.what) {
924             case MSG_START_ECHOSERVER:
925                 synchronized (this) {
926                     mEchoServer.start();
927                     break;
928                 }
929             case MSG_STOP_ECHOSERVER:
930                 synchronized (this) {
931                     mEchoServer.stop();
932                     break;
933                 }
934             case MSG_WAIT_FOR_LINK_TIMEOUT:
935                 synchronized (this) {
936                     // User wanted to send something but no link
937                     // came up. Just cancel the send
938                     mSendState = SEND_STATE_NOTHING_TO_SEND;
939                     mEventListener.onP2pTimeoutWaitingForLink();
940                 }
941                 break;
942             case MSG_DEBOUNCE_TIMEOUT:
943                 synchronized (this) {
944                     if (mLinkState != LINK_STATE_DEBOUNCE) {
945                         break;
946                     }
947                     if (DBG) Log.d(TAG, "Debounce timeout");
948                     mLinkState = LINK_STATE_DOWN;
949                     mSendState = SEND_STATE_NOTHING_TO_SEND;
950                     mMessageToSend = null;
951                     mUrisToSend = null;
952                     if (DBG) Log.d(TAG, "onP2pOutOfRange()");
953                     mEventListener.onP2pOutOfRange();
954                 }
955                 break;
956             case MSG_RECEIVE_HANDOVER:
957                 // We're going to do a handover request
958                 synchronized (this) {
959                     if (mLinkState == LINK_STATE_DOWN) {
960                         break;
961                     }
962                     if (mSendState == SEND_STATE_SENDING) {
963                         cancelSendNdefMessage();
964                     }
965                     mSendState = SEND_STATE_NOTHING_TO_SEND;
966                     if (DBG) Log.d(TAG, "onP2pReceiveComplete()");
967                     mEventListener.onP2pReceiveComplete(false);
968                 }
969                 break;
970             case MSG_RECEIVE_COMPLETE:
971                 NdefMessage m = (NdefMessage) msg.obj;
972                 synchronized (this) {
973                     if (mLinkState == LINK_STATE_DOWN) {
974                         break;
975                     }
976                     if (mSendState == SEND_STATE_SENDING) {
977                         cancelSendNdefMessage();
978                     }
979                     mSendState = SEND_STATE_NOTHING_TO_SEND;
980                     if (DBG) Log.d(TAG, "onP2pReceiveComplete()");
981                     mEventListener.onP2pReceiveComplete(true);
982                     NfcService.getInstance().sendMockNdefTag(m);
983                 }
984                 break;
985             case MSG_HANDOVER_NOT_SUPPORTED:
986                 synchronized (P2pLinkManager.this) {
987                     mSendTask = null;
988 
989                     if (mLinkState == LINK_STATE_DOWN || mSendState != SEND_STATE_SENDING) {
990                         break;
991                     }
992                     mSendState = SEND_STATE_NOTHING_TO_SEND;
993                     if (DBG) Log.d(TAG, "onP2pHandoverNotSupported()");
994                     mEventListener.onP2pHandoverNotSupported();
995                 }
996                 break;
997             case MSG_SEND_COMPLETE:
998                 synchronized (P2pLinkManager.this) {
999                     mSendTask = null;
1000 
1001                     if (mLinkState == LINK_STATE_DOWN || mSendState != SEND_STATE_SENDING) {
1002                         break;
1003                     }
1004                     mSendState = SEND_STATE_COMPLETE;
1005                     mHandler.removeMessages(MSG_DEBOUNCE_TIMEOUT);
1006                     if (DBG) Log.d(TAG, "onP2pSendComplete()");
1007                     mEventListener.onP2pSendComplete();
1008                     if (mCallbackNdef != null) {
1009                         try {
1010                             mCallbackNdef.onNdefPushComplete(mPeerLlcpVersion);
1011                         } catch (Exception e) {
1012                             Log.e(TAG, "Failed NDEF completed callback: " + e.getMessage());
1013                         }
1014                     }
1015                 }
1016                 break;
1017             case MSG_HANDOVER_BUSY:
1018                 synchronized (P2pLinkManager.this) {
1019                     mSendTask = null;
1020 
1021                     if (mLinkState == LINK_STATE_DOWN || mSendState != SEND_STATE_SENDING) {
1022                         break;
1023                     }
1024                     mSendState = SEND_STATE_NOTHING_TO_SEND;
1025                     if (DBG) Log.d(TAG, "onP2pHandoverBusy()");
1026                     mEventListener.onP2pHandoverBusy();
1027                 }
1028         }
1029         return true;
1030     }
1031 
1032 
1033     @Override
onP2pSendConfirmed()1034     public void onP2pSendConfirmed() {
1035         onP2pSendConfirmed(true);
1036     }
1037 
onP2pSendConfirmed(boolean requireConfirmation)1038     private void onP2pSendConfirmed(boolean requireConfirmation) {
1039         if (DBG) Log.d(TAG, "onP2pSendConfirmed()");
1040         synchronized (this) {
1041             if (mLinkState == LINK_STATE_DOWN || (requireConfirmation
1042                     && mSendState != SEND_STATE_NEED_CONFIRMATION)) {
1043                 return;
1044             }
1045             mSendState = SEND_STATE_SENDING;
1046             if (mLinkState == LINK_STATE_UP) {
1047                 if (mLlcpServicesConnected) {
1048                     sendNdefMessage();
1049                 } // else, will send messages when link comes up
1050             } else if (mLinkState == LINK_STATE_DEBOUNCE) {
1051                 // Restart debounce timeout and tell user to tap again
1052                 scheduleTimeoutLocked(MSG_DEBOUNCE_TIMEOUT, LINK_SEND_CONFIRMED_DEBOUNCE_MS);
1053                 mEventListener.onP2pSendDebounce();
1054             }
1055         }
1056     }
1057 
1058 
1059     @Override
onP2pCanceled()1060     public void onP2pCanceled() {
1061         synchronized (this) {
1062             mSendState = SEND_STATE_CANCELED;
1063             if (mLinkState == LINK_STATE_DOWN) {
1064                 // If we were waiting for the link to come up, stop doing so
1065                 mHandler.removeMessages(MSG_WAIT_FOR_LINK_TIMEOUT);
1066             } else if (mLinkState == LINK_STATE_DEBOUNCE) {
1067                 // We're in debounce state so link is down. Reschedule debounce
1068                 // timeout to occur sooner, we don't want to wait any longer.
1069                 scheduleTimeoutLocked(MSG_DEBOUNCE_TIMEOUT, LINK_SEND_CANCELED_DEBOUNCE_MS);
1070             } else {
1071                 // Link is up, nothing else to do but wait for link to go down
1072             }
1073         }
1074     }
1075 
scheduleTimeoutLocked(int what, int timeout)1076     void scheduleTimeoutLocked(int what, int timeout) {
1077         // Cancel any outstanding debounce timeouts.
1078         mHandler.removeMessages(what);
1079         mHandler.sendEmptyMessageDelayed(what, timeout);
1080     }
1081 
sendStateToString(int state)1082     static String sendStateToString(int state) {
1083         switch (state) {
1084             case SEND_STATE_NOTHING_TO_SEND:
1085                 return "SEND_STATE_NOTHING_TO_SEND";
1086             case SEND_STATE_NEED_CONFIRMATION:
1087                 return "SEND_STATE_NEED_CONFIRMATION";
1088             case SEND_STATE_SENDING:
1089                 return "SEND_STATE_SENDING";
1090             case SEND_STATE_COMPLETE:
1091                 return "SEND_STATE_COMPLETE";
1092             case SEND_STATE_CANCELED:
1093                 return "SEND_STATE_CANCELED";
1094             default:
1095                 return "<error>";
1096         }
1097     }
1098 
linkStateToString(int state)1099     static String linkStateToString(int state) {
1100         switch (state) {
1101             case LINK_STATE_DOWN:
1102                 return "LINK_STATE_DOWN";
1103             case LINK_STATE_DEBOUNCE:
1104                 return "LINK_STATE_DEBOUNCE";
1105             case LINK_STATE_UP:
1106                 return "LINK_STATE_UP";
1107             default:
1108                 return "<error>";
1109         }
1110     }
1111 
dump(FileDescriptor fd, PrintWriter pw, String[] args)1112     void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1113         synchronized (this) {
1114             pw.println("mIsSendEnabled=" + mIsSendEnabled);
1115             pw.println("mIsReceiveEnabled=" + mIsReceiveEnabled);
1116             pw.println("mLinkState=" + linkStateToString(mLinkState));
1117             pw.println("mSendState=" + sendStateToString(mSendState));
1118 
1119             pw.println("mCallbackNdef=" + mCallbackNdef);
1120             pw.println("mMessageToSend=" + mMessageToSend);
1121             pw.println("mUrisToSend=" + mUrisToSend);
1122         }
1123     }
1124 }
1125