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.text.TextUtils;
24 import android.util.Log;
25 
26 import com.android.bluetooth.BluetoothMetricsProto;
27 import com.android.bluetooth.R;
28 import com.android.bluetooth.Utils;
29 import com.android.bluetooth.btservice.AdapterService;
30 import com.android.bluetooth.btservice.MetricsLogger;
31 import com.android.bluetooth.btservice.ProfileService;
32 import com.android.bluetooth.sdp.SdpManager;
33 import com.android.internal.annotations.VisibleForTesting;
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 (mRemoteDevice != null && mRemoteDevice.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 
598     /**
599      * Set connection policy of the profile and disconnects it if connectionPolicy is
600      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}
601      *
602      * <p> The device should already be paired.
603      * Connection policy can be one of:
604      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
605      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
606      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
607      *
608      * @param device Paired bluetooth device
609      * @param connectionPolicy is the connection policy to set to for this profile
610      * @return true if connectionPolicy is set, false on error
611      */
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)612     public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
613         if (DEBUG) {
614             Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy);
615         }
616         enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
617                 "Need BLUETOOTH_PRIVILEGED permission");
618         AdapterService.getAdapterService().getDatabase()
619                 .setProfileConnectionPolicy(device, BluetoothProfile.SAP, connectionPolicy);
620         if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) {
621             disconnect(device);
622         }
623         return true;
624     }
625 
626     /**
627      * Get the connection policy of the profile.
628      *
629      * <p> The connection policy can be any of:
630      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED},
631      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN},
632      * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN}
633      *
634      * @param device Bluetooth device
635      * @return connection policy of the device
636      * @hide
637      */
getConnectionPolicy(BluetoothDevice device)638     public int getConnectionPolicy(BluetoothDevice device) {
639         enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED,
640                 "Need BLUETOOTH_PRIVILEGED permission");
641         return AdapterService.getAdapterService().getDatabase()
642                 .getProfileConnectionPolicy(device, BluetoothProfile.SAP);
643     }
644 
645     @Override
initBinder()646     protected IProfileServiceBinder initBinder() {
647         return new SapBinder(this);
648     }
649 
650     @Override
start()651     protected boolean start() {
652         Log.v(TAG, "start()");
653         IntentFilter filter = new IntentFilter();
654         filter.addAction(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY);
655         filter.addAction(BluetoothAdapter.ACTION_STATE_CHANGED);
656         filter.addAction(BluetoothDevice.ACTION_ACL_DISCONNECTED);
657         filter.addAction(USER_CONFIRM_TIMEOUT_ACTION);
658 
659         try {
660             registerReceiver(mSapReceiver, filter);
661             mIsRegistered = true;
662         } catch (Exception e) {
663             Log.w(TAG, "Unable to register sap receiver", e);
664         }
665         mInterrupted = false;
666         mAdapter = BluetoothAdapter.getDefaultAdapter();
667         // start RFCOMM listener
668         mSessionStatusHandler.sendMessage(mSessionStatusHandler.obtainMessage(START_LISTENER));
669         setSapService(this);
670         return true;
671     }
672 
673     @Override
stop()674     protected boolean stop() {
675         Log.v(TAG, "stop()");
676         if (!mIsRegistered) {
677             Log.i(TAG, "Avoid unregister when receiver it is not registered");
678             return true;
679         }
680         setSapService(null);
681         try {
682             mIsRegistered = false;
683             unregisterReceiver(mSapReceiver);
684         } catch (Exception e) {
685             Log.w(TAG, "Unable to unregister sap receiver", e);
686         }
687         setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
688         sendShutdownMessage();
689         return true;
690     }
691 
692     @Override
cleanup()693     public void cleanup() {
694         setState(BluetoothSap.STATE_DISCONNECTED, BluetoothSap.RESULT_CANCELED);
695         closeService();
696         if (mSessionStatusHandler != null) {
697             mSessionStatusHandler.removeCallbacksAndMessages(null);
698         }
699     }
700 
701     /**
702      * Get the current instance of {@link SapService}
703      *
704      * @return current instance of {@link SapService}
705      */
706     @VisibleForTesting
getSapService()707     public static synchronized SapService getSapService() {
708         if (sSapService == null) {
709             Log.w(TAG, "getSapService(): service is null");
710             return null;
711         }
712         if (!sSapService.isAvailable()) {
713             Log.w(TAG, "getSapService(): service is not available");
714             return null;
715         }
716         return sSapService;
717     }
718 
setSapService(SapService instance)719     private static synchronized void setSapService(SapService instance) {
720         if (DEBUG) {
721             Log.d(TAG, "setSapService(): set to: " + instance);
722         }
723         sSapService = instance;
724     }
725 
setUserTimeoutAlarm()726     private void setUserTimeoutAlarm() {
727         if (DEBUG) {
728             Log.d(TAG, "setUserTimeOutAlarm()");
729         }
730         cancelUserTimeoutAlarm();
731         mRemoveTimeoutMsg = true;
732         Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
733         PendingIntent pIntent = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0);
734         mAlarmManager.set(AlarmManager.RTC_WAKEUP,
735                 System.currentTimeMillis() + USER_CONFIRM_TIMEOUT_VALUE, pIntent);
736     }
737 
cancelUserTimeoutAlarm()738     private void cancelUserTimeoutAlarm() {
739         if (DEBUG) {
740             Log.d(TAG, "cancelUserTimeOutAlarm()");
741         }
742         if (mAlarmManager == null) {
743             mAlarmManager = (AlarmManager) this.getSystemService(Context.ALARM_SERVICE);
744         }
745         if (mRemoveTimeoutMsg) {
746             Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
747             PendingIntent sender = PendingIntent.getBroadcast(this, 0, timeoutIntent, 0);
748             mAlarmManager.cancel(sender);
749             mRemoveTimeoutMsg = false;
750         }
751     }
752 
sendCancelUserConfirmationIntent(BluetoothDevice device)753     private void sendCancelUserConfirmationIntent(BluetoothDevice device) {
754         Intent intent = new Intent(BluetoothDevice.ACTION_CONNECTION_ACCESS_CANCEL);
755         intent.setPackage(getString(R.string.pairing_ui_package));
756         intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device);
757         intent.putExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE,
758                 BluetoothDevice.REQUEST_TYPE_SIM_ACCESS);
759         sendBroadcast(intent, BLUETOOTH_PERM);
760     }
761 
sendShutdownMessage()762     private void sendShutdownMessage() {
763         /* Any pending messages are no longer valid.
764         To speed up things, simply delete them. */
765         if (mRemoveTimeoutMsg) {
766             Intent timeoutIntent = new Intent(USER_CONFIRM_TIMEOUT_ACTION);
767             sendBroadcast(timeoutIntent, BLUETOOTH_PERM);
768             mIsWaitingAuthorization = false;
769             cancelUserTimeoutAlarm();
770         }
771         removeSdpRecord();
772         mSessionStatusHandler.removeCallbacksAndMessages(null);
773         // Request release of all resources
774         mSessionStatusHandler.obtainMessage(SHUTDOWN).sendToTarget();
775     }
776 
sendConnectTimeoutMessage()777     private void sendConnectTimeoutMessage() {
778         if (DEBUG) {
779             Log.d(TAG, "sendConnectTimeoutMessage()");
780         }
781         if (mSessionStatusHandler != null) {
782             Message msg = mSessionStatusHandler.obtainMessage(USER_TIMEOUT);
783             msg.sendToTarget();
784         } // Can only be null during shutdown
785     }
786 
787     private SapBroadcastReceiver mSapReceiver = new SapBroadcastReceiver();
788 
789     private class SapBroadcastReceiver extends BroadcastReceiver {
790         @Override
onReceive(Context context, Intent intent)791         public void onReceive(Context context, Intent intent) {
792 
793             if (VERBOSE) {
794                 Log.v(TAG, "onReceive");
795             }
796             String action = intent.getAction();
797             if (action.equals(BluetoothAdapter.ACTION_STATE_CHANGED)) {
798                 int state =
799                         intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR);
800                 if (state == BluetoothAdapter.STATE_TURNING_OFF) {
801                     if (DEBUG) {
802                         Log.d(TAG, "STATE_TURNING_OFF");
803                     }
804                     sendShutdownMessage();
805                 } else if (state == BluetoothAdapter.STATE_ON) {
806                     if (DEBUG) {
807                         Log.d(TAG, "STATE_ON");
808                     }
809                     // start RFCOMM listener
810                     mSessionStatusHandler.sendMessage(
811                             mSessionStatusHandler.obtainMessage(START_LISTENER));
812                 }
813                 return;
814             }
815 
816             if (action.equals(BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY)) {
817                 Log.v(TAG, " - Received BluetoothDevice.ACTION_CONNECTION_ACCESS_REPLY");
818 
819                 int requestType = intent.getIntExtra(BluetoothDevice.EXTRA_ACCESS_REQUEST_TYPE, -1);
820                 if (requestType != BluetoothDevice.REQUEST_TYPE_SIM_ACCESS) {
821                     return;
822                 }
823 
824                 mIsWaitingAuthorization = false;
825 
826                 if (intent.getIntExtra(BluetoothDevice.EXTRA_CONNECTION_ACCESS_RESULT,
827                         BluetoothDevice.CONNECTION_ACCESS_NO)
828                         == BluetoothDevice.CONNECTION_ACCESS_YES) {
829                     // bluetooth connection accepted by user
830                     if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
831                         boolean result = mRemoteDevice.setSimAccessPermission(
832                                 BluetoothDevice.ACCESS_ALLOWED);
833                         if (VERBOSE) {
834                             Log.v(TAG, "setSimAccessPermission(ACCESS_ALLOWED) result=" + result);
835                         }
836                     }
837                     boolean result = setConnectionPolicy(mRemoteDevice,
838                             BluetoothProfile.CONNECTION_POLICY_ALLOWED);
839                     Log.d(TAG, "setConnectionPolicy ALLOWED, result = " + result);
840 
841                     try {
842                         if (mConnSocket != null) {
843                             // start obex server and rfcomm connection
844                             startSapServerSession();
845                         } else {
846                             stopSapServerSession();
847                         }
848                     } catch (IOException ex) {
849                         Log.e(TAG, "Caught the error: ", ex);
850                     }
851                 } else {
852                     if (intent.getBooleanExtra(BluetoothDevice.EXTRA_ALWAYS_ALLOWED, false)) {
853                         boolean result = mRemoteDevice.setSimAccessPermission(
854                                 BluetoothDevice.ACCESS_REJECTED);
855                         if (VERBOSE) {
856                             Log.v(TAG, "setSimAccessPermission(ACCESS_REJECTED) result=" + result);
857                         }
858                     }
859                     boolean result = setConnectionPolicy(mRemoteDevice,
860                             BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
861                     Log.d(TAG, "setConnectionPolicy FORBIDDEN, result = " + result);
862                     // Ensure proper cleanup, and prepare for new connect.
863                     mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE);
864                 }
865                 return;
866             }
867 
868             if (action.equals(USER_CONFIRM_TIMEOUT_ACTION)) {
869                 if (DEBUG) {
870                     Log.d(TAG, "USER_CONFIRM_TIMEOUT_ACTION Received.");
871                 }
872                 // send us self a message about the timeout.
873                 sendConnectTimeoutMessage();
874                 return;
875             }
876 
877             if (action.equals(BluetoothDevice.ACTION_ACL_DISCONNECTED) && mIsWaitingAuthorization) {
878                 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
879 
880                 if (mRemoteDevice == null || device == null) {
881                     Log.i(TAG, "Unexpected error!");
882                     return;
883                 }
884 
885                 if (DEBUG) {
886                     Log.d(TAG, "ACL disconnected for " + device);
887                 }
888 
889                 if (mRemoteDevice.equals(device)) {
890                     if (mRemoveTimeoutMsg) {
891                         // Send any pending timeout now, as ACL got disconnected.
892                         mSessionStatusHandler.removeMessages(USER_TIMEOUT);
893                         mSessionStatusHandler.obtainMessage(USER_TIMEOUT).sendToTarget();
894                     }
895                     setState(BluetoothSap.STATE_DISCONNECTED);
896                     // Ensure proper cleanup, and prepare for new connect.
897                     mSessionStatusHandler.sendEmptyMessage(MSG_SERVERSESSION_CLOSE);
898                 }
899             }
900         }
901     }
902 
903     ;
904 
905     //Binder object: Must be static class or memory leak may occur
906 
907     /**
908      * This class implements the IBluetoothSap interface - or actually it validates the
909      * preconditions for calling the actual functionality in the SapService, and calls it.
910      */
911     private static class SapBinder extends IBluetoothSap.Stub implements IProfileServiceBinder {
912         private SapService mService;
913 
getService()914         private SapService getService() {
915             if (!Utils.checkCaller()) {
916                 Log.w(TAG, "call not allowed for non-active user");
917                 return null;
918             }
919 
920             if (mService != null && mService.isAvailable()) {
921                 mService.enforceCallingOrSelfPermission(BLUETOOTH_PERM,
922                         "Need BLUETOOTH permission");
923                 return mService;
924             }
925             return null;
926         }
927 
SapBinder(SapService service)928         SapBinder(SapService service) {
929             Log.v(TAG, "SapBinder()");
930             mService = service;
931         }
932 
933         @Override
cleanup()934         public void cleanup() {
935             mService = null;
936         }
937 
938         @Override
getState()939         public int getState() {
940             Log.v(TAG, "getState()");
941             SapService service = getService();
942             if (service == null) {
943                 return BluetoothSap.STATE_DISCONNECTED;
944             }
945             return getService().getState();
946         }
947 
948         @Override
getClient()949         public BluetoothDevice getClient() {
950             Log.v(TAG, "getClient()");
951             SapService service = getService();
952             if (service == null) {
953                 return null;
954             }
955             Log.v(TAG, "getClient() - returning " + service.getRemoteDevice());
956             return service.getRemoteDevice();
957         }
958 
959         @Override
isConnected(BluetoothDevice device)960         public boolean isConnected(BluetoothDevice device) {
961             Log.v(TAG, "isConnected()");
962             SapService service = getService();
963             if (service == null) {
964                 return false;
965             }
966             return (service.getState() == BluetoothSap.STATE_CONNECTED && service.getRemoteDevice()
967                     .equals(device));
968         }
969 
970         @Override
connect(BluetoothDevice device)971         public boolean connect(BluetoothDevice device) {
972             Log.v(TAG, "connect()");
973             SapService service = getService();
974             if (service == null) {
975                 return false;
976             }
977             return false;
978         }
979 
980         @Override
disconnect(BluetoothDevice device)981         public boolean disconnect(BluetoothDevice device) {
982             Log.v(TAG, "disconnect()");
983             SapService service = getService();
984             if (service == null) {
985                 return false;
986             }
987             return service.disconnect(device);
988         }
989 
990         @Override
getConnectedDevices()991         public List<BluetoothDevice> getConnectedDevices() {
992             Log.v(TAG, "getConnectedDevices()");
993             SapService service = getService();
994             if (service == null) {
995                 return new ArrayList<BluetoothDevice>(0);
996             }
997             return service.getConnectedDevices();
998         }
999 
1000         @Override
getDevicesMatchingConnectionStates(int[] states)1001         public List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) {
1002             Log.v(TAG, "getDevicesMatchingConnectionStates()");
1003             SapService service = getService();
1004             if (service == null) {
1005                 return new ArrayList<BluetoothDevice>(0);
1006             }
1007             return service.getDevicesMatchingConnectionStates(states);
1008         }
1009 
1010         @Override
getConnectionState(BluetoothDevice device)1011         public int getConnectionState(BluetoothDevice device) {
1012             Log.v(TAG, "getConnectionState()");
1013             SapService service = getService();
1014             if (service == null) {
1015                 return BluetoothProfile.STATE_DISCONNECTED;
1016             }
1017             return service.getConnectionState(device);
1018         }
1019 
1020         @Override
setConnectionPolicy(BluetoothDevice device, int connectionPolicy)1021         public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) {
1022             SapService service = getService();
1023             if (service == null) {
1024                 return false;
1025             }
1026             return service.setConnectionPolicy(device, connectionPolicy);
1027         }
1028 
1029         @Override
getConnectionPolicy(BluetoothDevice device)1030         public int getConnectionPolicy(BluetoothDevice device) {
1031             SapService service = getService();
1032             if (service == null) {
1033                 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN;
1034             }
1035             return service.getConnectionPolicy(device);
1036         }
1037     }
1038 }
1039