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