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