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