Home | History | Annotate | Download | only in sap
      1 package com.android.bluetooth.sap;
      2 
      3 import android.annotation.TargetApi;
      4 import android.app.AlarmManager;
      5 import android.app.PendingIntent;
      6 import android.bluetooth.BluetoothAdapter;
      7 import android.bluetooth.BluetoothDevice;
      8 import android.bluetooth.BluetoothProfile;
      9 import android.bluetooth.BluetoothSap;
     10 import android.bluetooth.BluetoothServerSocket;
     11 import android.bluetooth.BluetoothSocket;
     12 import android.bluetooth.BluetoothUuid;
     13 import android.bluetooth.IBluetoothSap;
     14 import android.content.BroadcastReceiver;
     15 import android.content.Context;
     16 import android.content.Intent;
     17 import android.content.IntentFilter;
     18 import android.os.Build;
     19 import android.os.Handler;
     20 import android.os.Message;
     21 import android.os.ParcelUuid;
     22 import android.os.PowerManager;
     23 import android.provider.Settings;
     24 import android.support.annotation.VisibleForTesting;
     25 import android.text.TextUtils;
     26 import android.util.Log;
     27 
     28 import com.android.bluetooth.BluetoothMetricsProto;
     29 import com.android.bluetooth.R;
     30 import com.android.bluetooth.Utils;
     31 import com.android.bluetooth.btservice.MetricsLogger;
     32 import com.android.bluetooth.btservice.ProfileService;
     33 import com.android.bluetooth.sdp.SdpManager;
     34 
     35 import java.io.IOException;
     36 import java.util.ArrayList;
     37 import java.util.List;
     38 import java.util.Set;
     39 
     40 @TargetApi(Build.VERSION_CODES.ECLAIR)
     41 public class SapService extends ProfileService {
     42 
     43     private static final String SDP_SAP_SERVICE_NAME = "SIM Access";
     44     private static final int SDP_SAP_VERSION = 0x0102;
     45     private static final String TAG = "SapService";
     46     public static final boolean DEBUG = false;
     47     public static final boolean VERBOSE = false;
     48 
     49     /* Message ID's */
     50     private static final int START_LISTENER = 1;
     51     private static final int USER_TIMEOUT = 2;
     52     private static final int SHUTDOWN = 3;
     53 
     54     public static final int MSG_SERVERSESSION_CLOSE = 5000;
     55     public static final int MSG_SESSION_ESTABLISHED = 5001;
     56     public static final int MSG_SESSION_DISCONNECTED = 5002;
     57 
     58     public static final int MSG_ACQUIRE_WAKE_LOCK = 5005;
     59     public static final int MSG_RELEASE_WAKE_LOCK = 5006;
     60 
     61     public static final int MSG_CHANGE_STATE = 5007;
     62 
     63     /* Each time a transaction between the SIM and the BT Client is detected a wakelock is taken.
     64      * After an idle period of RELEASE_WAKE_LOCK_DELAY ms the wakelock is released.
     65      *
     66      * NOTE: While connected the the Nokia 616 car-kit it was noticed that the carkit do
     67      *       TRANSFER_APDU_REQ with 20-30 seconds interval, and it sends no requests less than 1 sec
     68      *       apart. Additionally the responses from the RIL seems to come within 100 ms, hence a
     69      *       one second timeout should be enough.
     70      */
     71     private static final int RELEASE_WAKE_LOCK_DELAY = 1000;
     72 
     73     /* Intent indicating timeout for user confirmation. */
     74     public static final String USER_CONFIRM_TIMEOUT_ACTION =
     75             "com.android.bluetooth.sap.USER_CONFIRM_TIMEOUT";
     76     private static final int USER_CONFIRM_TIMEOUT_VALUE = 25000;
     77 
     78     private PowerManager.WakeLock mWakeLock = null;
     79     private BluetoothAdapter mAdapter;
     80     private SocketAcceptThread mAcceptThread = null;
     81     private BluetoothServerSocket mServerSocket = null;
     82     private int mSdpHandle = -1;
     83     private BluetoothSocket mConnSocket = null;
     84     private BluetoothDevice mRemoteDevice = null;
     85     private static String sRemoteDeviceName = null;
     86     private volatile boolean mInterrupted;
     87     private int mState;
     88     private SapServer mSapServer = null;
     89     private AlarmManager mAlarmManager = null;
     90     private boolean mRemoveTimeoutMsg = false;
     91 
     92     private boolean mIsWaitingAuthorization = false;
     93     private boolean mIsRegistered = false;
     94 
     95     private static SapService sSapService;
     96 
     97     private static final ParcelUuid[] SAP_UUIDS = {
     98             BluetoothUuid.SAP,
     99     };
    100 
    101 
    102     public SapService() {
    103         mState = BluetoothSap.STATE_DISCONNECTED;
    104     }
    105 
    106     /***
    107      * Call this when ever an activity is detected to renew the wakelock
    108      *
    109      * @param messageHandler reference to the handler to notify
    110      *  - typically mSessionStatusHandler, but it cannot be accessed in a static manner.
    111      */
    112     public static void notifyUpdateWakeLock(Handler messageHandler) {
    113         if (messageHandler != null) {
    114             Message msg = Message.obtain(messageHandler);
    115             msg.what = MSG_ACQUIRE_WAKE_LOCK;
    116             msg.sendToTarget();
    117         }
    118     }
    119 
    120     private void removeSdpRecord() {
    121         if (mAdapter != null && mSdpHandle >= 0 && SdpManager.getDefaultManager() != null) {
    122             if (VERBOSE) {
    123                 Log.d(TAG, "Removing SDP record handle: " + mSdpHandle);
    124             }
    125             boolean status = SdpManager.getDefaultManager().removeSdpRecord(mSdpHandle);
    126             mSdpHandle = -1;
    127         }
    128     }
    129 
    130     private void startRfcommSocketListener() {
    131         if (VERBOSE) {
    132             Log.v(TAG, "Sap Service startRfcommSocketListener");
    133         }
    134 
    135         if (mAcceptThread == null) {
    136             mAcceptThread = new SocketAcceptThread();
    137             mAcceptThread.setName("SapAcceptThread");
    138             mAcceptThread.start();
    139         }
    140     }
    141 
    142     private static final int CREATE_RETRY_TIME = 10;
    143 
    144     private boolean initSocket() {
    145         if (VERBOSE) {
    146             Log.v(TAG, "Sap Service initSocket");
    147         }
    148 
    149         boolean initSocketOK = false;
    150 
    151         // It's possible that create will fail in some cases. retry for 10 times
    152         for (int i = 0; i < CREATE_RETRY_TIME && !mInterrupted; i++) {
    153             initSocketOK = true;
    154             try {
    155                 // It is mandatory for MSE to support initiation of bonding and encryption.
    156                 // TODO: Consider reusing the mServerSocket - it is indented to be reused
    157                 //       for multiple connections.
    158                 mServerSocket = mAdapter.listenUsingRfcommOn(
    159                         BluetoothAdapter.SOCKET_CHANNEL_AUTO_STATIC_NO_SDP, true, true);
    160                 removeSdpRecord();
    161                 mSdpHandle = SdpManager.getDefaultManager()
    162                         .createSapsRecord(SDP_SAP_SERVICE_NAME, mServerSocket.getChannel(),
    163                                 SDP_SAP_VERSION);
    164             } catch (IOException e) {
    165                 Log.e(TAG, "Error create RfcommServerSocket ", e);
    166                 initSocketOK = false;
    167             }
    168 
    169             if (!initSocketOK) {
    170                 // Need to break out of this loop if BT is being turned off.
    171                 if (mAdapter == null) {
    172                     break;
    173                 }
    174                 int state = mAdapter.getState();
    175                 if ((state != BluetoothAdapter.STATE_TURNING_ON) && (state
    176                         != BluetoothAdapter.STATE_ON)) {
    177                     Log.w(TAG, "initServerSocket failed as BT is (being) turned off");
    178                     break;
    179                 }
    180                 try {
    181                     if (VERBOSE) {
    182                         Log.v(TAG, "wait 300 ms");
    183                     }
    184                     Thread.sleep(300);
    185                 } catch (InterruptedException e) {
    186                     Log.e(TAG, "socketAcceptThread thread was interrupted (3)", e);
    187                 }
    188             } else {
    189                 break;
    190             }
    191         }
    192 
    193         if (initSocketOK) {
    194             if (VERBOSE) {
    195                 Log.v(TAG, "Succeed to create listening socket ");
    196             }
    197 
    198         } else {
    199             Log.e(TAG, "Error to create listening socket after " + CREATE_RETRY_TIME + " try");
    200         }
    201         return initSocketOK;
    202     }
    203 
    204     private synchronized void closeServerSocket() {
    205         // exit SocketAcceptThread early
    206         if (mServerSocket != null) {
    207             try {
    208                 // this will cause mServerSocket.accept() return early with IOException
    209                 mServerSocket.close();
    210                 mServerSocket = null;
    211             } catch (IOException ex) {
    212                 Log.e(TAG, "Close Server Socket error: ", ex);
    213             }
    214         }
    215     }
    216 
    217     private synchronized void closeConnectionSocket() {
    218         if (mConnSocket != null) {
    219             try {
    220                 mConnSocket.close();
    221                 mConnSocket = null;
    222             } catch (IOException e) {
    223                 Log.e(TAG, "Close Connection Socket error: ", e);
    224             }
    225         }
    226     }
    227 
    228     private void closeService() {
    229         if (VERBOSE) {
    230             Log.v(TAG, "SAP Service closeService in");
    231         }
    232 
    233         // exit initSocket early
    234         mInterrupted = true;
    235         closeServerSocket();
    236 
    237         if (mAcceptThread != null) {
    238             try {
    239                 mAcceptThread.shutdown();
    240                 mAcceptThread.join();
    241                 mAcceptThread = null;
    242             } catch (InterruptedException ex) {
    243                 Log.w(TAG, "mAcceptThread close error", ex);
    244             }
    245         }
    246 
    247         if (mWakeLock != null) {
    248             mSessionStatusHandler.removeMessages(MSG_ACQUIRE_WAKE_LOCK);
    249             mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    250             mWakeLock.release();
    251             mWakeLock = null;
    252         }
    253 
    254         closeConnectionSocket();
    255 
    256         if (VERBOSE) {
    257             Log.v(TAG, "SAP Service closeService out");
    258         }
    259     }
    260 
    261     private void startSapServerSession() throws IOException {
    262         if (VERBOSE) {
    263             Log.v(TAG, "Sap Service startSapServerSession");
    264         }
    265 
    266         // acquire the wakeLock before start SAP transaction thread
    267         if (mWakeLock == null) {
    268             PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
    269             mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "StartingSapTransaction");
    270             mWakeLock.setReferenceCounted(false);
    271             mWakeLock.acquire();
    272         }
    273 
    274         /* Start the SAP I/O thread and associate with message handler */
    275         mSapServer = new SapServer(mSessionStatusHandler, this, mConnSocket.getInputStream(),
    276                 mConnSocket.getOutputStream());
    277         mSapServer.start();
    278         /* Warning: at this point we most likely have already handled the initial connect
    279          *          request from the SAP client, hence we need to be prepared to handle the
    280          *          response. (the SapHandler should have been started before this point)*/
    281 
    282         mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    283         mSessionStatusHandler.sendMessageDelayed(
    284                 mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK),
    285                 RELEASE_WAKE_LOCK_DELAY);
    286 
    287         if (VERBOSE) {
    288             Log.v(TAG, "startSapServerSession() success!");
    289         }
    290     }
    291 
    292     private void stopSapServerSession() {
    293 
    294         /* When we reach this point, the SapServer is closed down, and the client is
    295          * supposed to close the RFCOMM connection. */
    296         if (VERBOSE) {
    297             Log.v(TAG, "SAP Service stopSapServerSession");
    298         }
    299 
    300         mAcceptThread = null;
    301         closeConnectionSocket();
    302         closeServerSocket();
    303 
    304         setState(BluetoothSap.STATE_DISCONNECTED);
    305 
    306         if (mWakeLock != null) {
    307             mWakeLock.release();
    308             mWakeLock = null;
    309         }
    310 
    311         // Last SAP transaction is finished, we start to listen for incoming
    312         // rfcomm connection again
    313         if (mAdapter.isEnabled()) {
    314             startRfcommSocketListener();
    315         }
    316     }
    317 
    318     /**
    319      * A thread that runs in the background waiting for remote rfcomm
    320      * connect.Once a remote socket connected, this thread shall be
    321      * shutdown.When the remote disconnect,this thread shall run again waiting
    322      * for next request.
    323      */
    324     private class SocketAcceptThread extends Thread {
    325 
    326         private boolean mStopped = false;
    327 
    328         @Override
    329         public void run() {
    330             BluetoothServerSocket serverSocket;
    331             if (mServerSocket == null) {
    332                 if (!initSocket()) {
    333                     return;
    334                 }
    335             }
    336 
    337             while (!mStopped) {
    338                 try {
    339                     if (VERBOSE) {
    340                         Log.v(TAG, "Accepting socket connection...");
    341                     }
    342                     serverSocket = mServerSocket;
    343                     if (serverSocket == null) {
    344                         Log.w(TAG, "mServerSocket is null");
    345                         break;
    346                     }
    347                     mConnSocket = mServerSocket.accept();
    348                     if (VERBOSE) {
    349                         Log.v(TAG, "Accepted socket connection...");
    350                     }
    351                     synchronized (SapService.this) {
    352                         if (mConnSocket == null) {
    353                             Log.w(TAG, "mConnSocket is null");
    354                             break;
    355                         }
    356                         mRemoteDevice = mConnSocket.getRemoteDevice();
    357                     }
    358                     if (mRemoteDevice == null) {
    359                         Log.i(TAG, "getRemoteDevice() = null");
    360                         break;
    361                     }
    362 
    363                     sRemoteDeviceName = mRemoteDevice.getName();
    364                     // In case getRemoteName failed and return null
    365                     if (TextUtils.isEmpty(sRemoteDeviceName)) {
    366                         sRemoteDeviceName = getString(R.string.defaultname);
    367                     }
    368                     int permission = mRemoteDevice.getSimAccessPermission();
    369 
    370                     if (VERBOSE) {
    371                         Log.v(TAG, "getSimAccessPermission() = " + permission);
    372                     }
    373 
    374                     if (permission == BluetoothDevice.ACCESS_ALLOWED) {
    375                         try {
    376                             if (VERBOSE) {
    377                                 Log.v(TAG, "incoming connection accepted from: " + sRemoteDeviceName
    378                                         + " automatically as trusted device");
    379                             }
    380                             startSapServerSession();
    381                         } catch (IOException ex) {
    382                             Log.e(TAG, "catch exception starting obex server session", ex);
    383                         }
    384                     } else if (permission != BluetoothDevice.ACCESS_REJECTED) {
    385                         Intent intent =
    386                                 new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_REQUEST);
    387                         intent.setPackage(getString(R.string.pairing_ui_package));
    388                         intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    389                                 BluetoothDevice.REQUEST_TYPE_SIM_ACCESS);
    390                         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    391                         intent.putExtra(BluetoothDevice.EXTRA_PACKAGE_NAME, getPackageName());
    392 
    393                         mIsWaitingAuthorization = true;
    394                         setUserTimeoutAlarm();
    395                         sendBroadcast(intent, BLUETOOTH_ADMIN_PERM);
    396 
    397                         if (VERBOSE) {
    398                             Log.v(TAG, "waiting for authorization for connection from: "
    399                                     + sRemoteDeviceName);
    400                         }
    401 
    402                     } else {
    403                         // Close RFCOMM socket for current connection and start listening
    404                         // again for new connections.
    405                         Log.w(TAG, "Can't connect with " + sRemoteDeviceName
    406                                 + " as access is rejected");
    407                         if (mSessionStatusHandler != null) {
    408                             mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE);
    409                         }
    410                     }
    411                     mStopped = true; // job done ,close this thread;
    412                 } catch (IOException ex) {
    413                     mStopped = true;
    414                     if (VERBOSE) {
    415                         Log.v(TAG, "Accept exception: ", ex);
    416                     }
    417                 }
    418             }
    419         }
    420 
    421         void shutdown() {
    422             mStopped = true;
    423             interrupt();
    424         }
    425     }
    426 
    427     private final Handler mSessionStatusHandler = new Handler() {
    428         @Override
    429         public void handleMessage(Message msg) {
    430             if (VERBOSE) {
    431                 Log.v(TAG, "Handler(): got msg=" + msg.what);
    432             }
    433 
    434             switch (msg.what) {
    435                 case START_LISTENER:
    436                     if (mAdapter.isEnabled()) {
    437                         startRfcommSocketListener();
    438                     }
    439                     break;
    440                 case USER_TIMEOUT:
    441                     if (mIsWaitingAuthorization) {
    442                         sendCancelUserConfirmationIntent(mRemoteDevice);
    443                         cancelUserTimeoutAlarm();
    444                         mIsWaitingAuthorization = false;
    445                         stopSapServerSession(); // And restart RfcommListener if needed
    446                     }
    447                     break;
    448                 case MSG_SERVERSESSION_CLOSE:
    449                     stopSapServerSession();
    450                     break;
    451                 case MSG_SESSION_ESTABLISHED:
    452                     break;
    453                 case MSG_SESSION_DISCONNECTED:
    454                     // handled elsewhere
    455                     break;
    456                 case MSG_ACQUIRE_WAKE_LOCK:
    457                     if (VERBOSE) {
    458                         Log.i(TAG, "Acquire Wake Lock request message");
    459                     }
    460                     if (mWakeLock == null) {
    461                         PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
    462                         mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
    463                                 "StartingObexMapTransaction");
    464                         mWakeLock.setReferenceCounted(false);
    465                     }
    466                     if (!mWakeLock.isHeld()) {
    467                         mWakeLock.acquire();
    468                         if (DEBUG) {
    469                             Log.i(TAG, "  Acquired Wake Lock by message");
    470                         }
    471                     }
    472                     mSessionStatusHandler.removeMessages(MSG_RELEASE_WAKE_LOCK);
    473                     mSessionStatusHandler.sendMessageDelayed(
    474                             mSessionStatusHandler.obtainMessage(MSG_RELEASE_WAKE_LOCK),
    475                             RELEASE_WAKE_LOCK_DELAY);
    476                     break;
    477                 case MSG_RELEASE_WAKE_LOCK:
    478                     if (VERBOSE) {
    479                         Log.i(TAG, "Release Wake Lock request message");
    480                     }
    481                     if (mWakeLock != null) {
    482                         mWakeLock.release();
    483                         if (DEBUG) {
    484                             Log.i(TAG, "  Released Wake Lock by message");
    485                         }
    486                     }
    487                     break;
    488                 case MSG_CHANGE_STATE:
    489                     if (DEBUG) {
    490                         Log.d(TAG, "change state message: newState = " + msg.arg1);
    491                     }
    492                     setState(msg.arg1);
    493                     break;
    494                 case SHUTDOWN:
    495                     /* Ensure to call close from this handler to avoid starting new stuff
    496                        because of pending messages */
    497                     closeService();
    498                     break;
    499                 default:
    500                     break;
    501             }
    502         }
    503     };
    504 
    505     private void setState(int state) {
    506         setState(state, BluetoothSap.RESULT_SUCCESS);
    507     }
    508 
    509     private synchronized void setState(int state, int result) {
    510         if (state != mState) {
    511             if (DEBUG) {
    512                 Log.d(TAG, "Sap state " + mState + " -> " + state + ", result = " + result);
    513             }
    514             if (state == BluetoothProfile.STATE_CONNECTED) {
    515                 MetricsLogger.logProfileConnectionEvent(BluetoothMetricsProto.ProfileId.SAP);
    516             }
    517             int prevState = mState;
    518             mState = state;
    519             Intent intent = new Intent(BluetoothSap.ACTION_CONNECTION_STATE_CHANGED);
    520             intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, prevState);
    521             intent.putExtra(BluetoothProfile.EXTRA_STATE, mState);
    522             intent.putExtra(BluetoothDevice.EXTRA_DEVICE, mRemoteDevice);
    523             sendBroadcast(intent, BLUETOOTH_PERM);
    524         }
    525     }
    526 
    527     public int getState() {
    528         return mState;
    529     }
    530 
    531     public BluetoothDevice getRemoteDevice() {
    532         return mRemoteDevice;
    533     }
    534 
    535     public static String getRemoteDeviceName() {
    536         return sRemoteDeviceName;
    537     }
    538 
    539     public boolean disconnect(BluetoothDevice device) {
    540         boolean result = false;
    541         synchronized (SapService.this) {
    542             if (getRemoteDevice().equals(device)) {
    543                 switch (mState) {
    544                     case BluetoothSap.STATE_CONNECTED:
    545                         closeConnectionSocket();
    546                         setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
    547                         result = true;
    548                         break;
    549                     default:
    550                         break;
    551                 }
    552             }
    553         }
    554         return result;
    555     }
    556 
    557     public List<BluetoothDevice> getConnectedDevices() {
    558         List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
    559         synchronized (this) {
    560             if (mState == BluetoothSap.STATE_CONNECTED && mRemoteDevice != null) {
    561                 devices.add(mRemoteDevice);
    562             }
    563         }
    564         return devices;
    565     }
    566 
    567     public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    568         List<BluetoothDevice> deviceList = new ArrayList<BluetoothDevice>();
    569         Set<BluetoothDevice> bondedDevices = mAdapter.getBondedDevices();
    570         int connectionState;
    571         synchronized (this) {
    572             for (BluetoothDevice device : bondedDevices) {
    573                 ParcelUuid[] featureUuids = device.getUuids();
    574                 if (!BluetoothUuid.containsAnyUuid(featureUuids, SAP_UUIDS)) {
    575                     continue;
    576                 }
    577                 connectionState = getConnectionState(device);
    578                 for (int i = 0; i < states.length; i++) {
    579                     if (connectionState == states[i]) {
    580                         deviceList.add(device);
    581                     }
    582                 }
    583             }
    584         }
    585         return deviceList;
    586     }
    587 
    588     public int getConnectionState(BluetoothDevice device) {
    589         synchronized (this) {
    590             if (getState() == BluetoothSap.STATE_CONNECTED && getRemoteDevice().equals(device)) {
    591                 return BluetoothProfile.STATE_CONNECTED;
    592             } else {
    593                 return BluetoothProfile.STATE_DISCONNECTED;
    594             }
    595         }
    596     }
    597 
    598     public boolean setPriority(BluetoothDevice device, int priority) {
    599         Settings.Global.putInt(getContentResolver(),
    600                 Settings.Global.getBluetoothSapPriorityKey(device.getAddress()), priority);
    601         if (DEBUG) {
    602             Log.d(TAG, "Saved priority " + device + " = " + priority);
    603         }
    604         return true;
    605     }
    606 
    607     public int getPriority(BluetoothDevice device) {
    608         int priority = Settings.Global.getInt(getContentResolver(),
    609                 Settings.Global.getBluetoothSapPriorityKey(device.getAddress()),
    610                 BluetoothProfile.PRIORITY_UNDEFINED);
    611         return priority;
    612     }
    613 
    614     @Override
    615     protected IProfileServiceBinder initBinder() {
    616         return new SapBinder(this);
    617     }
    618 
    619     @Override
    620     protected boolean start() {
    621         Log.v(TAG, "start()");
    622         IntentFilter filter = new IntentFilter();
    623         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
    624         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
    625         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
    626         filter.addAction(USER_CONFIRM_TIMEOUT_ACTION);
    627 
    628         try {
    629             registerReceiver(mSapReceiver, filter);
    630             mIsRegistered = true;
    631         } catch (Exception e) {
    632             Log.w(TAG, "Unable to register sap receiver", e);
    633         }
    634         mInterrupted = false;
    635         mAdapter = BluetoothAdapter.getDefaultAdapter();
    636         // start RFCOMM listener
    637         mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
    638         setSapService(this);
    639         return true;
    640     }
    641 
    642     @Override
    643     protected boolean stop() {
    644         Log.v(TAG, "stop()");
    645         if (!mIsRegistered) {
    646             Log.i(TAG, "Avoid unregister when receiver it is not registered");
    647             return true;
    648         }
    649         setSapService(null);
    650         try {
    651             mIsRegistered = false;
    652             unregisterReceiver(mSapReceiver);
    653         } catch (Exception e) {
    654             Log.w(TAG, "Unable to unregister sap receiver", e);
    655         }
    656         setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
    657         sendShutdownMessage();
    658         return true;
    659     }
    660 
    661     @Override
    662     public void cleanup() {
    663         setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
    664         closeService();
    665         if (mSessionStatusHandler != null) {
    666             mSessionStatusHandler.removeCallbacksAndMessages(null);
    667         }
    668     }
    669 
    670     /**
    671      * Get the current instance of {@link SapService}
    672      *
    673      * @return current instance of {@link SapService}
    674      */
    675     @VisibleForTesting
    676     public static synchronized SapService getSapService() {
    677         if (sSapService == null) {
    678             Log.w(TAG, "getSapService(): service is null");
    679             return null;
    680         }
    681         if (!sSapService.isAvailable()) {
    682             Log.w(TAG, "getSapService(): service is not available");
    683             return null;
    684         }
    685         return sSapService;
    686     }
    687 
    688     private static synchronized void setSapService(SapService instance) {
    689         if (DEBUG) {
    690             Log.d(TAG, "setSapService(): set to: " + instance);
    691         }
    692         sSapService = instance;
    693     }
    694 
    695     private void setUserTimeoutAlarm() {
    696         if (DEBUG) {
    697             Log.d(TAG, "setUserTimeOutAlarm()");
    698         }
    699         cancelUserTimeoutAlarm();
    700         mRemoveTimeoutMsg = true;
    701         Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
    702         PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0);
    703         mAlarmManager.set(AlarmManager.RTC_WAKEUP,
    704                 System.currentTimeMillis() + USER_CONFIRM_TIMEOUT_VALUE, pIntent);
    705     }
    706 
    707     private void cancelUserTimeoutAlarm() {
    708         if (DEBUG) {
    709             Log.d(TAG, "cancelUserTimeOutAlarm()");
    710         }
    711         if (mAlarmManager == null) {
    712             mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
    713         }
    714         if (mRemoveTimeoutMsg) {
    715             Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
    716             PendingIntent sender = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0);
    717             mAlarmManager.cancel(sender);
    718             mRemoveTimeoutMsg = false;
    719         }
    720     }
    721 
    722     private void sendCancelUserConfirmationIntent(BluetoothDevice device) {
    723         Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
    724         intent.setPackage(getString(R.string.pairing_ui_package));
    725         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
    726         intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
    727                 BluetoothDevice.REQUEST_TYPE_SIM_ACCESS);
    728         sendBroadcast(intent, BLUETOOTH_PERM);
    729     }
    730 
    731     private void sendShutdownMessage() {
    732         /* Any pending messages are no longer valid.
    733         To speed up things, simply delete them. */
    734         if (mRemoveTimeoutMsg) {
    735             Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
    736             sendBroadcast(timeoutIntent, BLUETOOTH_PERM);
    737             mIsWaitingAuthorization = false;
    738             cancelUserTimeoutAlarm();
    739         }
    740         removeSdpRecord();
    741         mSessionStatusHandler.removeCallbacksAndMessages(null);
    742         // Request release of all resources
    743         mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget();
    744     }
    745 
    746     private void sendConnectTimeoutMessage() {
    747         if (DEBUG) {
    748             Log.d(TAG, "sendConnectTimeoutMessage()");
    749         }
    750         if (mSessionStatusHandler != null) {
    751             Message msg = mSessionStatusHandler.obtainMessage(USER_TIMEOUT);
    752             msg.sendToTarget();
    753         } // Can only be null during shutdown
    754     }
    755 
    756     private SapBroadcastReceiver mSapReceiver = new SapBroadcastReceiver();
    757 
    758     private class SapBroadcastReceiver extends BroadcastReceiver {
    759         @Override
    760         public void onReceive(Context context, Intent intent) {
    761 
    762             if (VERBOSE) {
    763                 Log.v(TAG, "onReceive");
    764             }
    765             String action = intent.getAction();
    766             if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
    767                 int state =
    768                         intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
    769                 if (state == BluetoothAdapter.STATE_TURNING_OFF) {
    770                     if (DEBUG) {
    771                         Log.d(TAG, "STATE_TURNING_OFF");
    772                     }
    773                     sendShutdownMessage();
    774                 } else if (state == BluetoothAdapter.STATE_ON) {
    775                     if (DEBUG) {
    776                         Log.d(TAG, "STATE_ON");
    777                     }
    778                     // start RFCOMM listener
    779                     mSessionStatusHandler.sendMessage(
    780                             mSessionStatusHandler.obtainMessage(START_LISTENER));
    781                 }
    782                 return;
    783             }
    784 
    785             if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
    786                 Log.v(TAG, " - Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY");
    787                 if (!mIsWaitingAuthorization) {
    788                     // this reply is not for us
    789                     return;
    790                 }
    791 
    792                 mIsWaitingAuthorization = false;
    793 
    794                 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
    795                         BluetoothDevice.CONNECTION_ACCESS_NO)
    796                         == BluetoothDevice.CONNECTION_ACCESS_YES) {
    797                     //bluetooth connection accepted by user
    798                     if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
    799                         boolean result = mRemoteDevice.setSimAccessPermission(
    800                                 BluetoothDevice.ACCESS_ALLOWED);
    801                         if (VERBOSE) {
    802                             Log.v(TAG, "setSimAccessPermission(ACCESS_ALLOWED) result=" + result);
    803                         }
    804                     }
    805                     try {
    806                         if (mConnSocket != null) {
    807                             // start obex server and rfcomm connection
    808                             startSapServerSession();
    809                         } else {
    810                             stopSapServerSession();
    811                         }
    812                     } catch (IOException ex) {
    813                         Log.e(TAG, "Caught the error: ", ex);
    814                     }
    815                 } else {
    816                     if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
    817                         boolean result = mRemoteDevice.setSimAccessPermission(
    818                                 BluetoothDevice.ACCESS_REJECTED);
    819                         if (VERBOSE) {
    820                             Log.v(TAG, "setSimAccessPermission(ACCESS_REJECTED) result=" + result);
    821                         }
    822                     }
    823                     // Ensure proper cleanup, and prepare for new connect.
    824                     mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE);
    825                 }
    826                 return;
    827             }
    828 
    829             if (action.equals(USER_CONFIRM_TIMEOUT_ACTION)) {
    830                 if (DEBUG) {
    831                     Log.d(TAG, "USER_CONFIRM_TIMEOUT_ACTION Received.");
    832                 }
    833                 // send us self a message about the timeout.
    834                 sendConnectTimeoutMessage();
    835                 return;
    836             }
    837 
    838             if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) && mIsWaitingAuthorization) {
    839                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
    840 
    841                 if (mRemoteDevice == null || device == null) {
    842                     Log.i(TAG, "Unexpected error!");
    843                     return;
    844                 }
    845 
    846                 if (DEBUG) {
    847                     Log.d(TAG, "ACL disconnected for " + device);
    848                 }
    849 
    850                 if (mRemoteDevice.equals(device)) {
    851                     if (mRemoveTimeoutMsg) {
    852                         // Send any pending timeout now, as ACL got disconnected.
    853                         mSessionStatusHandler.removeMessages(USER_TIMEOUT);
    854                         mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget();
    855                     }
    856                     setState(BluetoothSap.STATE_DISCONNECTED);
    857                     // Ensure proper cleanup, and prepare for new connect.
    858                     mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE);
    859                 }
    860             }
    861         }
    862     }
    863 
    864     ;
    865 
    866     //Binder object: Must be static class or memory leak may occur
    867 
    868     /**
    869      * This class implements the IBluetoothSap interface - or actually it validates the
    870      * preconditions for calling the actual functionality in the SapService, and calls it.
    871      */
    872     private static class SapBinder extends IBluetoothSap.Stub implements IProfileServiceBinder {
    873         private SapService mService;
    874 
    875         private SapService getService() {
    876             if (!Utils.checkCaller()) {
    877                 Log.w(TAG, "call not allowed for non-active user");
    878                 return null;
    879             }
    880 
    881             if (mService != null && mService.isAvailable()) {
    882                 mService.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
    883                         "Need BLUETOOTH permission");
    884                 return mService;
    885             }
    886             return null;
    887         }
    888 
    889         SapBinder(SapService service) {
    890             Log.v(TAG, "SapBinder()");
    891             mService = service;
    892         }
    893 
    894         @Override
    895         public void cleanup() {
    896             mService = null;
    897         }
    898 
    899         @Override
    900         public int getState() {
    901             Log.v(TAG, "getState()");
    902             SapService service = getService();
    903             if (service == null) {
    904                 return BluetoothSap.STATE_DISCONNECTED;
    905             }
    906             return getService().getState();
    907         }
    908 
    909         @Override
    910         public BluetoothDevice getClient() {
    911             Log.v(TAG, "getClient()");
    912             SapService service = getService();
    913             if (service == null) {
    914                 return null;
    915             }
    916             Log.v(TAG, "getClient() - returning " + service.getRemoteDevice());
    917             return service.getRemoteDevice();
    918         }
    919 
    920         @Override
    921         public boolean isConnected(BluetoothDevice device) {
    922             Log.v(TAG, "isConnected()");
    923             SapService service = getService();
    924             if (service == null) {
    925                 return false;
    926             }
    927             return (service.getState() == BluetoothSap.STATE_CONNECTED && service.getRemoteDevice()
    928                     .equals(device));
    929         }
    930 
    931         @Override
    932         public boolean connect(BluetoothDevice device) {
    933             Log.v(TAG, "connect()");
    934             SapService service = getService();
    935             if (service == null) {
    936                 return false;
    937             }
    938             return false;
    939         }
    940 
    941         @Override
    942         public boolean disconnect(BluetoothDevice device) {
    943             Log.v(TAG, "disconnect()");
    944             SapService service = getService();
    945             if (service == null) {
    946                 return false;
    947             }
    948             return service.disconnect(device);
    949         }
    950 
    951         @Override
    952         public List<BluetoothDevice> getConnectedDevices() {
    953             Log.v(TAG, "getConnectedDevices()");
    954             SapService service = getService();
    955             if (service == null) {
    956                 return new ArrayList<BluetoothDevice>(0);
    957             }
    958             return service.getConnectedDevices();
    959         }
    960 
    961         @Override
    962         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
    963             Log.v(TAG, "getDevicesMatchingConnectionStates()");
    964             SapService service = getService();
    965             if (service == null) {
    966                 return new ArrayList<BluetoothDevice>(0);
    967             }
    968             return service.getDevicesMatchingConnectionStates(states);
    969         }
    970 
    971         @Override
    972         public int getConnectionState(BluetoothDevice device) {
    973             Log.v(TAG, "getConnectionState()");
    974             SapService service = getService();
    975             if (service == null) {
    976                 return BluetoothProfile.STATE_DISCONNECTED;
    977             }
    978             return service.getConnectionState(device);
    979         }
    980 
    981         @Override
    982         public boolean setPriority(BluetoothDevice device, int priority) {
    983             SapService service = getService();
    984             if (service == null) {
    985                 return false;
    986             }
    987             return service.setPriority(device, priority);
    988         }
    989 
    990         @Override
    991         public int getPriority(BluetoothDevice device) {
    992             SapService service = getService();
    993             if (service == null) {
    994                 return BluetoothProfile.PRIORITY_UNDEFINED;
    995             }
    996             return service.getPriority(device);
    997         }
    998     }
    999 }
   1000