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 
SapService()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      */
notifyUpdateWakeLock(Handler messageHandler)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 
removeSdpRecord()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 
startRfcommSocketListener()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 
initSocket()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 
closeServerSocket()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     }
closeConnectionSocket()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 
closeService()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 
startSapServerSession()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 
stopSapServerSession()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
run()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 
shutdown()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 
setState(int state)458     private void setState(int state) {
459         setState(state, BluetoothSap.RESULT_SUCCESS);
460     }
461 
setState(int state, int result)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 
getState()481     public int getState() {
482         return mState;
483     }
484 
getRemoteDevice()485     public BluetoothDevice getRemoteDevice() {
486         return mRemoteDevice;
487     }
488 
getRemoteDeviceName()489     public static String getRemoteDeviceName() {
490         return sRemoteDeviceName;
491     }
492 
disconnect(BluetoothDevice device)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 
getConnectedDevices()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 
getDevicesMatchingConnectionStates(int[] states)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 
getConnectionState(BluetoothDevice device)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 
setPriority(BluetoothDevice device, int priority)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 
getPriority(BluetoothDevice device)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
initBinder()568     protected IProfileServiceBinder initBinder() {
569         return new SapBinder(this);
570     }
571 
572     @Override
start()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
stop()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 
cleanup()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 
setUserTimeoutAlarm()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 
cancelUserTimeoutAlarm()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 
sendCancelUserConfirmationIntent(BluetoothDevice device)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 
sendShutdownMessage()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 
sendConnectTimeoutMessage()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
onReceive(Context context, Intent intent)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 
getService()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 
SapBinder(SapService service)793         SapBinder(SapService service) {
794             Log.v(TAG, "SapBinder()");
795             mService = service;
796         }
797 
cleanup()798         public boolean cleanup()  {
799             mService = null;
800             return true;
801         }
802 
getState()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 
getClient()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 
isConnected(BluetoothDevice device)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 
connect(BluetoothDevice device)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 
disconnect(BluetoothDevice device)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 
getConnectedDevices()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 
getDevicesMatchingConnectionStates(int[] states)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 
getConnectionState(BluetoothDevice device)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 
setPriority(BluetoothDevice device, int priority)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 
getPriority(BluetoothDevice device)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