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