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