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 
SapService()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      */
notifyUpdateWakeLock(Handler messageHandler)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 
removeSdpRecord()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 
startRfcommSocketListener()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 
initSocket()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 
closeServerSocket()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     }
closeConnectionSocket()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 
closeService()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 
startSapServerSession()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 
stopSapServerSession()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
run()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 
shutdown()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 
setState(int state)454     private void setState(int state) {
455         setState(state, BluetoothSap.RESULT_SUCCESS);
456     }
457 
setState(int state, int result)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 
getState()472     public int getState() {
473         return mState;
474     }
475 
getRemoteDevice()476     public BluetoothDevice getRemoteDevice() {
477         return mRemoteDevice;
478     }
479 
getRemoteDeviceName()480     public static String getRemoteDeviceName() {
481         return sRemoteDeviceName;
482     }
483 
disconnect(BluetoothDevice device)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 
getConnectedDevices()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 
getDevicesMatchingConnectionStates(int[] states)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 
getConnectionState(BluetoothDevice device)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 
setPriority(BluetoothDevice device, int priority)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 
getPriority(BluetoothDevice device)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
initBinder()559     protected IProfileServiceBinder initBinder() {
560         return new SapBinder(this);
561     }
562 
563     @Override
start()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
stop()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 
cleanup()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 
setUserTimeoutAlarm()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 
cancelUserTimeoutAlarm()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 
sendCancelUserConfirmationIntent(BluetoothDevice device)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 
sendShutdownMessage()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 
sendConnectTimeoutMessage()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
onReceive(Context context, Intent intent)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 
getService()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 
SapBinder(SapService service)790         SapBinder(SapService service) {
791             Log.v(TAG, "SapBinder()");
792             mService = service;
793         }
794 
cleanup()795         public boolean cleanup()  {
796             mService = null;
797             return true;
798         }
799 
getState()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 
getClient()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 
isConnected(BluetoothDevice device)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 
connect(BluetoothDevice device)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 
disconnect(BluetoothDevice device)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 
getConnectedDevices()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 
getDevicesMatchingConnectionStates(int[] states)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 
getConnectionState(BluetoothDevice device)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 
setPriority(BluetoothDevice device, int priority)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 
getPriority(BluetoothDevice device)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