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