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