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