• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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                     mSendFlags = 0;
486                 }
487             }
488 
489             if (DBG) Log.d(TAG, "mMessageToSend = " + mMessageToSend);
490             if (DBG) Log.d(TAG, "mUrisToSend = " + mUrisToSend);
491         }
492     }
493 
isBeamDisabled(int uid)494     private boolean isBeamDisabled(int uid) {
495         UserManager userManager = (UserManager) mContext.getSystemService(Context.USER_SERVICE);
496         UserInfo userInfo = userManager.getUserInfo(UserHandle.getUserId(uid));
497         return userManager.hasUserRestriction(
498                         UserManager.DISALLOW_OUTGOING_BEAM, userInfo.getUserHandle());
499 
500     }
501 
beamDefaultDisabled(String pkgName)502     boolean beamDefaultDisabled(String pkgName) {
503         try {
504             ApplicationInfo ai = mPackageManager.getApplicationInfo(pkgName,
505                     PackageManager.GET_META_DATA);
506             if (ai == null || ai.metaData == null) {
507                 return false;
508             }
509             return ai.metaData.getBoolean(DISABLE_BEAM_DEFAULT);
510         } catch (NameNotFoundException e) {
511             return false;
512         }
513     }
514 
createDefaultNdef(String pkgName)515     NdefMessage createDefaultNdef(String pkgName) {
516         NdefRecord appUri = NdefRecord.createUri(Uri.parse(
517                 "http://play.google.com/store/apps/details?id=" + pkgName + "&feature=beam"));
518         NdefRecord appRecord = NdefRecord.createApplicationRecord(pkgName);
519         return new NdefMessage(new NdefRecord[] { appUri, appRecord });
520     }
521 
disconnectLlcpServices()522     void disconnectLlcpServices() {
523         synchronized (this) {
524             if (mConnectTask != null) {
525                 mConnectTask.cancel(true);
526                 mConnectTask = null;
527             }
528             // Close any already connected LLCP clients
529             if (mNdefPushClient != null) {
530                 mNdefPushClient.close();
531                 mNdefPushClient = null;
532             }
533             if (mSnepClient != null) {
534                 mSnepClient.close();
535                 mSnepClient = null;
536             }
537             if (mHandoverClient != null) {
538                 mHandoverClient.close();
539                 mHandoverClient = null;
540             }
541             mLlcpServicesConnected = false;
542         }
543     }
544 
545     /**
546      * Must be called on UI Thread.
547      */
onLlcpDeactivated()548     public void onLlcpDeactivated() {
549         Log.i(TAG, "LLCP deactivated.");
550         synchronized (this) {
551             if (mEchoServer != null) {
552                 mEchoServer.onLlcpDeactivated();
553             }
554 
555             switch (mLinkState) {
556                 case LINK_STATE_DOWN:
557                 case LINK_STATE_DEBOUNCE:
558                     Log.i(TAG, "Duplicate onLlcpDectivated()");
559                     break;
560                 case LINK_STATE_UP:
561                     // Debounce
562                     mLinkState = LINK_STATE_DEBOUNCE;
563                     int debounceTimeout = 0;
564                     switch (mSendState) {
565                         case SEND_STATE_NOTHING_TO_SEND:
566                             debounceTimeout = 0;
567                             break;
568                         case SEND_STATE_NEED_CONFIRMATION:
569                             debounceTimeout = LINK_SEND_PENDING_DEBOUNCE_MS;
570                             break;
571                         case SEND_STATE_SENDING:
572                             debounceTimeout = LINK_SEND_CONFIRMED_DEBOUNCE_MS;
573                             break;
574                         case SEND_STATE_COMPLETE:
575                             debounceTimeout = LINK_SEND_COMPLETE_DEBOUNCE_MS;
576                             break;
577                         case SEND_STATE_CANCELED:
578                             debounceTimeout = LINK_SEND_CANCELED_DEBOUNCE_MS;
579                     }
580                     scheduleTimeoutLocked(MSG_DEBOUNCE_TIMEOUT, debounceTimeout);
581                     if (mSendState == SEND_STATE_SENDING) {
582                         Log.e(TAG, "onP2pSendDebounce()");
583                         mEventListener.onP2pSendDebounce();
584                     }
585                     cancelSendNdefMessage();
586                     disconnectLlcpServices();
587                     break;
588             }
589          }
590      }
591 
onHandoverUnsupported()592     void onHandoverUnsupported() {
593         mHandler.sendEmptyMessage(MSG_HANDOVER_NOT_SUPPORTED);
594     }
595 
onHandoverBusy()596     void onHandoverBusy() {
597         mHandler.sendEmptyMessage(MSG_HANDOVER_BUSY);
598     }
599 
onSendComplete(NdefMessage msg, long elapsedRealtime)600     void onSendComplete(NdefMessage msg, long elapsedRealtime) {
601         // Make callbacks on UI thread
602         mHandler.sendEmptyMessage(MSG_SEND_COMPLETE);
603     }
604 
sendNdefMessage()605     void sendNdefMessage() {
606         synchronized (this) {
607             cancelSendNdefMessage();
608             mSendTask = new SendTask();
609             mSendTask.execute();
610         }
611     }
612 
cancelSendNdefMessage()613     void cancelSendNdefMessage() {
614         synchronized (P2pLinkManager.this) {
615             if (mSendTask != null) {
616                 mSendTask.cancel(true);
617             }
618         }
619     }
620 
connectLlcpServices()621     void connectLlcpServices() {
622         synchronized (P2pLinkManager.this) {
623             if (mConnectTask != null) {
624                 Log.e(TAG, "Still had a reference to mConnectTask!");
625             }
626             mConnectTask = new ConnectTask();
627             mConnectTask.execute();
628         }
629     }
630 
631     // Must be called on UI-thread
onLlcpServicesConnected()632     void onLlcpServicesConnected() {
633         if (DBG) Log.d(TAG, "onLlcpServicesConnected");
634         synchronized (P2pLinkManager.this) {
635             if (mLinkState != LINK_STATE_UP) {
636                 return;
637             }
638             mLlcpServicesConnected = true;
639             if (mSendState == SEND_STATE_NEED_CONFIRMATION) {
640                 if (DBG) Log.d(TAG, "onP2pSendConfirmationRequested()");
641                 mEventListener.onP2pSendConfirmationRequested();
642             } else if (mSendState == SEND_STATE_SENDING) {
643                 mEventListener.onP2pResumeSend();
644                 sendNdefMessage();
645             } else {
646                 // Either nothing to send or canceled/complete, ignore
647             }
648         }
649     }
650 
651     final class ConnectTask extends AsyncTask<Void, Void, Boolean> {
652         @Override
onPostExecute(Boolean result)653         protected void onPostExecute(Boolean result)  {
654             if (isCancelled()) {
655                 if (DBG) Log.d(TAG, "ConnectTask was cancelled");
656                 return;
657             }
658             if (result) {
659                 onLlcpServicesConnected();
660             } else {
661                 Log.e(TAG, "Could not connect required NFC transports");
662             }
663         }
664 
665         @Override
doInBackground(Void... params)666         protected Boolean doInBackground(Void... params) {
667             boolean needsHandover = false;
668             boolean needsNdef = false;
669             boolean success = false;
670             HandoverClient handoverClient = null;
671             SnepClient snepClient = null;
672             NdefPushClient nppClient = null;
673 
674             synchronized(P2pLinkManager.this) {
675                 if (mUrisToSend != null) {
676                     needsHandover = true;
677                 }
678 
679                 if (mMessageToSend != null) {
680                     needsNdef = true;
681                 }
682             }
683             // We know either is requested - otherwise this task
684             // wouldn't have been started.
685             if (needsHandover) {
686                 handoverClient = new HandoverClient();
687                 try {
688                     handoverClient.connect();
689                     success = true; // Regardless of NDEF result
690                 } catch (IOException e) {
691                     handoverClient = null;
692                 }
693             }
694 
695             if (needsNdef || (needsHandover && handoverClient == null)) {
696                 snepClient = new SnepClient();
697                 try {
698                     snepClient.connect();
699                     success = true;
700                 } catch (IOException e) {
701                     snepClient = null;
702                 }
703 
704                 if (!success) {
705                     nppClient = new NdefPushClient();
706                     try {
707                         nppClient.connect();
708                         success = true;
709                     } catch (IOException e) {
710                         nppClient = null;
711                     }
712                 }
713             }
714 
715             synchronized (P2pLinkManager.this) {
716                 if (isCancelled()) {
717                     // Cancelled by onLlcpDeactivated on UI thread
718                     if (handoverClient != null) {
719                         handoverClient.close();
720                     }
721                     if (snepClient != null) {
722                         snepClient.close();
723                     }
724                     if (nppClient != null) {
725                         nppClient.close();
726                     }
727                     return false;
728                 } else {
729                     // Once assigned, these are the responsibility of
730                     // the code on the UI thread to release - typically
731                     // through onLlcpDeactivated().
732                     mHandoverClient = handoverClient;
733                     mSnepClient = snepClient;
734                     mNdefPushClient = nppClient;
735                     return success;
736                 }
737             }
738         }
739     };
740 
741     final class SendTask extends AsyncTask<Void, Void, Void> {
742         NdefPushClient nppClient;
743         SnepClient snepClient;
744         HandoverClient handoverClient;
745 
doHandover(Uri[] uris, UserHandle userHandle)746         int doHandover(Uri[] uris, UserHandle userHandle) throws IOException {
747             NdefMessage response = null;
748             BeamManager beamManager = BeamManager.getInstance();
749 
750             if (beamManager.isBeamInProgress()) {
751                 return HANDOVER_BUSY;
752             }
753 
754             NdefMessage request = mHandoverDataParser.createHandoverRequestMessage();
755             if (request != null) {
756                 if (handoverClient != null) {
757                     response = handoverClient.sendHandoverRequest(request);
758                 }
759                 if (response == null && snepClient != null) {
760                     // Remote device may not support handover service,
761                     // try the (deprecated) SNEP GET implementation
762                     // for devices running Android 4.1
763                     SnepMessage snepResponse = snepClient.get(request);
764                     response = snepResponse.getNdefMessage();
765                 }
766                 if (response == null) {
767                     if (snepClient != null)
768                         return HANDOVER_UNSUPPORTED;
769                     else
770                         return HANDOVER_FAILURE;
771                 }
772             } else {
773                 return HANDOVER_UNSUPPORTED;
774             }
775 
776             if (!beamManager.startBeamSend(mContext,
777                     mHandoverDataParser.getOutgoingHandoverData(response), uris, userHandle)) {
778                 return HANDOVER_BUSY;
779             }
780 
781             return HANDOVER_SUCCESS;
782         }
783 
doSnepProtocol(NdefMessage msg)784         int doSnepProtocol(NdefMessage msg) throws IOException {
785             if (msg != null) {
786                 snepClient.put(msg);
787                 return SNEP_SUCCESS;
788             } else {
789                 return SNEP_FAILURE;
790             }
791         }
792 
793         @Override
doInBackground(Void... args)794         public Void doInBackground(Void... args) {
795             NdefMessage m;
796             Uri[] uris;
797             UserHandle userHandle;
798             boolean result = false;
799 
800             synchronized (P2pLinkManager.this) {
801                 if (mLinkState != LINK_STATE_UP || mSendState != SEND_STATE_SENDING) {
802                     return null;
803                 }
804                 m = mMessageToSend;
805                 uris = mUrisToSend;
806                 userHandle = mUserHandle;
807                 snepClient = mSnepClient;
808                 handoverClient = mHandoverClient;
809                 nppClient = mNdefPushClient;
810             }
811 
812             long time = SystemClock.elapsedRealtime();
813 
814             if (uris != null) {
815                 if (DBG) Log.d(TAG, "Trying handover request");
816                 try {
817                     int handoverResult = doHandover(uris, userHandle);
818                     switch (handoverResult) {
819                         case HANDOVER_SUCCESS:
820                             result = true;
821                             break;
822                         case HANDOVER_FAILURE:
823                             result = false;
824                             break;
825                         case HANDOVER_UNSUPPORTED:
826                             result = false;
827                             onHandoverUnsupported();
828                             break;
829                         case HANDOVER_BUSY:
830                             result = false;
831                             onHandoverBusy();
832                             break;
833                     }
834                 } catch (IOException e) {
835                     result = false;
836                 }
837             }
838 
839             if (!result && m != null && snepClient != null) {
840                 if (DBG) Log.d(TAG, "Sending ndef via SNEP");
841                 try {
842                     int snepResult = doSnepProtocol(m);
843                     switch (snepResult) {
844                         case SNEP_SUCCESS:
845                             result = true;
846                             break;
847                         case SNEP_FAILURE:
848                             result = false;
849                             break;
850                         default:
851                             result = false;
852                     }
853                 } catch (IOException e) {
854                     result = false;
855                 }
856             }
857 
858             if (!result && m != null && nppClient != null) {
859                 result = nppClient.push(m);
860             }
861 
862             time = SystemClock.elapsedRealtime() - time;
863             if (DBG) Log.d(TAG, "SendTask result=" + result + ", time ms=" + time);
864             if (result) {
865                 onSendComplete(m, time);
866             }
867 
868             return null;
869         }
870     };
871 
872 
873     final HandoverServer.Callback mHandoverCallback = new HandoverServer.Callback() {
874         @Override
875         public void onHandoverRequestReceived() {
876             onReceiveHandover();
877         }
878 
879         @Override
880         public void onHandoverBusy() {
881             P2pLinkManager.this.onHandoverBusy();
882         }
883     };
884 
885     final NdefPushServer.Callback mNppCallback = new NdefPushServer.Callback() {
886         @Override
887         public void onMessageReceived(NdefMessage msg) {
888             onReceiveComplete(msg);
889         }
890     };
891 
892     final SnepServer.Callback mDefaultSnepCallback = new SnepServer.Callback() {
893         @Override
894         public SnepMessage doPut(NdefMessage msg) {
895             onReceiveComplete(msg);
896             return SnepMessage.getMessage(SnepMessage.RESPONSE_SUCCESS);
897         }
898 
899         @Override
900         public SnepMessage doGet(int acceptableLength, NdefMessage msg) {
901             // The NFC Forum Default SNEP server is not allowed to respond to
902             // SNEP GET requests - see SNEP 1.0 TS section 6.1. However,
903             // since Android 4.1 used the NFC Forum default server to
904             // implement connection handover, we will support this
905             // until we can deprecate it.
906             NdefMessage response = mHandoverDataParser.getIncomingHandoverData(msg).handoverSelect;
907             if (response != null) {
908                 onReceiveHandover();
909                 return SnepMessage.getSuccessResponse(response);
910             } else {
911                 return SnepMessage.getMessage(SnepMessage.RESPONSE_NOT_IMPLEMENTED);
912             }
913         }
914     };
915 
onReceiveHandover()916     void onReceiveHandover() {
917         mHandler.obtainMessage(MSG_RECEIVE_HANDOVER).sendToTarget();
918     }
919 
onReceiveComplete(NdefMessage msg)920     void onReceiveComplete(NdefMessage msg) {
921         // Make callbacks on UI thread
922         mHandler.obtainMessage(MSG_RECEIVE_COMPLETE, msg).sendToTarget();
923     }
924 
925     @Override
handleMessage(Message msg)926     public boolean handleMessage(Message msg) {
927         switch (msg.what) {
928             case MSG_START_ECHOSERVER:
929                 synchronized (this) {
930                     mEchoServer.start();
931                     break;
932                 }
933             case MSG_STOP_ECHOSERVER:
934                 synchronized (this) {
935                     mEchoServer.stop();
936                     break;
937                 }
938             case MSG_WAIT_FOR_LINK_TIMEOUT:
939                 synchronized (this) {
940                     // User wanted to send something but no link
941                     // came up. Just cancel the send
942                     mSendState = SEND_STATE_NOTHING_TO_SEND;
943                     mEventListener.onP2pTimeoutWaitingForLink();
944                 }
945                 break;
946             case MSG_DEBOUNCE_TIMEOUT:
947                 synchronized (this) {
948                     if (mLinkState != LINK_STATE_DEBOUNCE) {
949                         break;
950                     }
951                     if (DBG) Log.d(TAG, "Debounce timeout");
952                     mLinkState = LINK_STATE_DOWN;
953                     mSendState = SEND_STATE_NOTHING_TO_SEND;
954                     mMessageToSend = null;
955                     mUrisToSend = null;
956                     if (DBG) Log.d(TAG, "onP2pOutOfRange()");
957                     mEventListener.onP2pOutOfRange();
958                 }
959                 break;
960             case MSG_RECEIVE_HANDOVER:
961                 // We're going to do a handover request
962                 synchronized (this) {
963                     if (mLinkState == LINK_STATE_DOWN) {
964                         break;
965                     }
966                     if (mSendState == SEND_STATE_SENDING) {
967                         cancelSendNdefMessage();
968                     }
969                     mSendState = SEND_STATE_NOTHING_TO_SEND;
970                     if (DBG) Log.d(TAG, "onP2pReceiveComplete()");
971                     mEventListener.onP2pReceiveComplete(false);
972                 }
973                 break;
974             case MSG_RECEIVE_COMPLETE:
975                 NdefMessage m = (NdefMessage) msg.obj;
976                 synchronized (this) {
977                     if (mLinkState == LINK_STATE_DOWN) {
978                         break;
979                     }
980                     if (mSendState == SEND_STATE_SENDING) {
981                         cancelSendNdefMessage();
982                     }
983                     mSendState = SEND_STATE_NOTHING_TO_SEND;
984                     if (DBG) Log.d(TAG, "onP2pReceiveComplete()");
985                     mEventListener.onP2pReceiveComplete(true);
986                     NfcService.getInstance().sendMockNdefTag(m);
987                 }
988                 break;
989             case MSG_HANDOVER_NOT_SUPPORTED:
990                 synchronized (P2pLinkManager.this) {
991                     mSendTask = null;
992 
993                     if (mLinkState == LINK_STATE_DOWN || mSendState != SEND_STATE_SENDING) {
994                         break;
995                     }
996                     mSendState = SEND_STATE_NOTHING_TO_SEND;
997                     if (DBG) Log.d(TAG, "onP2pHandoverNotSupported()");
998                     mEventListener.onP2pHandoverNotSupported();
999                 }
1000                 break;
1001             case MSG_SEND_COMPLETE:
1002                 synchronized (P2pLinkManager.this) {
1003                     mSendTask = null;
1004 
1005                     if (mLinkState == LINK_STATE_DOWN || mSendState != SEND_STATE_SENDING) {
1006                         break;
1007                     }
1008                     mSendState = SEND_STATE_COMPLETE;
1009                     mHandler.removeMessages(MSG_DEBOUNCE_TIMEOUT);
1010                     if (DBG) Log.d(TAG, "onP2pSendComplete()");
1011                     mEventListener.onP2pSendComplete();
1012                     if (mCallbackNdef != null) {
1013                         try {
1014                             mCallbackNdef.onNdefPushComplete(mPeerLlcpVersion);
1015                         } catch (Exception e) {
1016                             Log.e(TAG, "Failed NDEF completed callback: " + e.getMessage());
1017                         }
1018                     }
1019                 }
1020                 break;
1021             case MSG_HANDOVER_BUSY:
1022                 synchronized (P2pLinkManager.this) {
1023                     mSendTask = null;
1024 
1025                     if (mLinkState == LINK_STATE_DOWN || mSendState != SEND_STATE_SENDING) {
1026                         break;
1027                     }
1028                     mSendState = SEND_STATE_NOTHING_TO_SEND;
1029                     if (DBG) Log.d(TAG, "onP2pHandoverBusy()");
1030                     mEventListener.onP2pHandoverBusy();
1031                 }
1032         }
1033         return true;
1034     }
1035 
1036 
1037     @Override
onP2pSendConfirmed()1038     public void onP2pSendConfirmed() {
1039         onP2pSendConfirmed(true);
1040     }
1041 
onP2pSendConfirmed(boolean requireConfirmation)1042     private void onP2pSendConfirmed(boolean requireConfirmation) {
1043         if (DBG) Log.d(TAG, "onP2pSendConfirmed()");
1044         synchronized (this) {
1045             if (mLinkState == LINK_STATE_DOWN || (requireConfirmation
1046                     && mSendState != SEND_STATE_NEED_CONFIRMATION)) {
1047                 return;
1048             }
1049             mSendState = SEND_STATE_SENDING;
1050             if (mLinkState == LINK_STATE_UP) {
1051                 if (mLlcpServicesConnected) {
1052                     sendNdefMessage();
1053                 } // else, will send messages when link comes up
1054             } else if (mLinkState == LINK_STATE_DEBOUNCE) {
1055                 // Restart debounce timeout and tell user to tap again
1056                 scheduleTimeoutLocked(MSG_DEBOUNCE_TIMEOUT, LINK_SEND_CONFIRMED_DEBOUNCE_MS);
1057                 mEventListener.onP2pSendDebounce();
1058             }
1059         }
1060     }
1061 
1062 
1063     @Override
onP2pCanceled()1064     public void onP2pCanceled() {
1065         synchronized (this) {
1066             mSendState = SEND_STATE_CANCELED;
1067             if (mLinkState == LINK_STATE_DOWN) {
1068                 // If we were waiting for the link to come up, stop doing so
1069                 mHandler.removeMessages(MSG_WAIT_FOR_LINK_TIMEOUT);
1070             } else if (mLinkState == LINK_STATE_DEBOUNCE) {
1071                 // We're in debounce state so link is down. Reschedule debounce
1072                 // timeout to occur sooner, we don't want to wait any longer.
1073                 scheduleTimeoutLocked(MSG_DEBOUNCE_TIMEOUT, LINK_SEND_CANCELED_DEBOUNCE_MS);
1074             } else {
1075                 // Link is up, nothing else to do but wait for link to go down
1076             }
1077         }
1078     }
1079 
scheduleTimeoutLocked(int what, int timeout)1080     void scheduleTimeoutLocked(int what, int timeout) {
1081         // Cancel any outstanding debounce timeouts.
1082         mHandler.removeMessages(what);
1083         mHandler.sendEmptyMessageDelayed(what, timeout);
1084     }
1085 
sendStateToString(int state)1086     static String sendStateToString(int state) {
1087         switch (state) {
1088             case SEND_STATE_NOTHING_TO_SEND:
1089                 return "SEND_STATE_NOTHING_TO_SEND";
1090             case SEND_STATE_NEED_CONFIRMATION:
1091                 return "SEND_STATE_NEED_CONFIRMATION";
1092             case SEND_STATE_SENDING:
1093                 return "SEND_STATE_SENDING";
1094             case SEND_STATE_COMPLETE:
1095                 return "SEND_STATE_COMPLETE";
1096             case SEND_STATE_CANCELED:
1097                 return "SEND_STATE_CANCELED";
1098             default:
1099                 return "<error>";
1100         }
1101     }
1102 
linkStateToString(int state)1103     static String linkStateToString(int state) {
1104         switch (state) {
1105             case LINK_STATE_DOWN:
1106                 return "LINK_STATE_DOWN";
1107             case LINK_STATE_DEBOUNCE:
1108                 return "LINK_STATE_DEBOUNCE";
1109             case LINK_STATE_UP:
1110                 return "LINK_STATE_UP";
1111             default:
1112                 return "<error>";
1113         }
1114     }
1115 
dump(FileDescriptor fd, PrintWriter pw, String[] args)1116     void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1117         synchronized (this) {
1118             pw.println("mIsSendEnabled=" + mIsSendEnabled);
1119             pw.println("mIsReceiveEnabled=" + mIsReceiveEnabled);
1120             pw.println("mLinkState=" + linkStateToString(mLinkState));
1121             pw.println("mSendState=" + sendStateToString(mSendState));
1122 
1123             pw.println("mCallbackNdef=" + mCallbackNdef);
1124             pw.println("mMessageToSend=" + mMessageToSend);
1125             pw.println("mUrisToSend=" + mUrisToSend);
1126         }
1127     }
1128 }
1129