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