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