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