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