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