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