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