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