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