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