1 /* 2 * Copyright (C) 2016 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.mapclient; 18 19 import android.Manifest; 20 import android.annotation.RequiresPermission; 21 import android.app.PendingIntent; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.BluetoothProfile; 25 import android.bluetooth.BluetoothUuid; 26 import android.bluetooth.IBluetoothMapClient; 27 import android.bluetooth.SdpMasRecord; 28 import android.content.AttributionSource; 29 import android.content.Context; 30 import android.net.Uri; 31 import android.os.Handler; 32 import android.os.Looper; 33 import android.os.ParcelUuid; 34 import android.os.Parcelable; 35 import android.sysprop.BluetoothProperties; 36 import android.util.Log; 37 38 import com.android.bluetooth.Utils; 39 import com.android.bluetooth.btservice.AdapterService; 40 import com.android.bluetooth.btservice.ProfileService; 41 import com.android.bluetooth.btservice.storage.DatabaseManager; 42 import com.android.internal.annotations.VisibleForTesting; 43 44 import java.util.ArrayList; 45 import java.util.Arrays; 46 import java.util.Collections; 47 import java.util.Iterator; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.Objects; 51 import java.util.concurrent.ConcurrentHashMap; 52 53 public class MapClientService extends ProfileService { 54 private static final String TAG = MapClientService.class.getSimpleName(); 55 56 static final int MAXIMUM_CONNECTED_DEVICES = 4; 57 58 private final Map<BluetoothDevice, MceStateMachine> mMapInstanceMap = 59 new ConcurrentHashMap<>(1); 60 private MnsService mMnsServer; 61 62 private AdapterService mAdapterService; 63 private DatabaseManager mDatabaseManager; 64 private static MapClientService sMapClientService; 65 @VisibleForTesting private Handler mHandler; 66 67 private Looper mSmLooper; 68 MapClientService(Context ctx)69 public MapClientService(Context ctx) { 70 super(ctx); 71 } 72 73 @VisibleForTesting MapClientService(Context ctx, Looper looper, MnsService mnsServer)74 MapClientService(Context ctx, Looper looper, MnsService mnsServer) { 75 this(ctx); 76 mSmLooper = looper; 77 mMnsServer = mnsServer; 78 } 79 isEnabled()80 public static boolean isEnabled() { 81 return BluetoothProperties.isProfileMapClientEnabled().orElse(false); 82 } 83 getMapClientService()84 public static synchronized MapClientService getMapClientService() { 85 if (sMapClientService == null) { 86 Log.w(TAG, "getMapClientService(): service is null"); 87 return null; 88 } 89 if (!sMapClientService.isAvailable()) { 90 Log.w(TAG, "getMapClientService(): service is not available "); 91 return null; 92 } 93 return sMapClientService; 94 } 95 96 @VisibleForTesting setMapClientService(MapClientService instance)97 static synchronized void setMapClientService(MapClientService instance) { 98 Log.d(TAG, "setMapClientService(): set to: " + instance); 99 sMapClientService = instance; 100 } 101 102 @VisibleForTesting getInstanceMap()103 Map<BluetoothDevice, MceStateMachine> getInstanceMap() { 104 return mMapInstanceMap; 105 } 106 107 /** 108 * Connect the given Bluetooth device. 109 * 110 * @return true if connection is successful, false otherwise. 111 */ 112 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) connect(BluetoothDevice device)113 public synchronized boolean connect(BluetoothDevice device) { 114 enforceCallingOrSelfPermission( 115 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); 116 if (device == null) { 117 throw new IllegalArgumentException("Null device"); 118 } 119 Log.d(TAG, "connect(device= " + device + "): devices=" + mMapInstanceMap.keySet()); 120 if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 121 Log.w( 122 TAG, 123 "Connection not allowed: <" 124 + device.getAddress() 125 + "> is CONNECTION_POLICY_FORBIDDEN"); 126 return false; 127 } 128 MceStateMachine mapStateMachine = mMapInstanceMap.get(device); 129 if (mapStateMachine == null) { 130 // a map state machine instance doesn't exist yet, create a new one if we can. 131 if (mMapInstanceMap.size() < MAXIMUM_CONNECTED_DEVICES) { 132 addDeviceToMapAndConnect(device); 133 return true; 134 } else { 135 // Maxed out on the number of allowed connections. 136 // see if some of the current connections can be cleaned-up, to make room. 137 removeUncleanAccounts(); 138 if (mMapInstanceMap.size() < MAXIMUM_CONNECTED_DEVICES) { 139 addDeviceToMapAndConnect(device); 140 return true; 141 } else { 142 Log.e( 143 TAG, 144 "Maxed out on the number of allowed MAP connections. " 145 + "Connect request rejected on " 146 + device); 147 return false; 148 } 149 } 150 } 151 152 // statemachine already exists in the map. 153 int state = getConnectionState(device); 154 if (state == BluetoothProfile.STATE_CONNECTED 155 || state == BluetoothProfile.STATE_CONNECTING) { 156 Log.w(TAG, "Received connect request while already connecting/connected."); 157 return true; 158 } 159 160 // Statemachine exists but not in connecting or connected state! it should 161 // have been removed form the map. lets get rid of it and add a new one. 162 Log.d(TAG, "Statemachine exists for a device in unexpected state: " + state); 163 mMapInstanceMap.remove(device); 164 mapStateMachine.doQuit(); 165 166 addDeviceToMapAndConnect(device); 167 Log.d(TAG, "connect(device= " + device + "): end devices=" + mMapInstanceMap.keySet()); 168 return true; 169 } 170 addDeviceToMapAndConnect(BluetoothDevice device)171 private synchronized void addDeviceToMapAndConnect(BluetoothDevice device) { 172 // When creating a new statemachine, its state is set to CONNECTING - which will trigger 173 // connect. 174 MceStateMachine mapStateMachine; 175 if (mSmLooper != null) mapStateMachine = new MceStateMachine(this, device, mSmLooper); 176 else mapStateMachine = new MceStateMachine(this, device); 177 mMapInstanceMap.put(device, mapStateMachine); 178 } 179 180 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) disconnect(BluetoothDevice device)181 public synchronized boolean disconnect(BluetoothDevice device) { 182 enforceCallingOrSelfPermission( 183 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); 184 Log.d(TAG, "disconnect(device= " + device + "): devices=" + mMapInstanceMap.keySet()); 185 MceStateMachine mapStateMachine = mMapInstanceMap.get(device); 186 // a map state machine instance doesn't exist. maybe it is already gone? 187 if (mapStateMachine == null) { 188 return false; 189 } 190 int connectionState = mapStateMachine.getState(); 191 if (connectionState != BluetoothProfile.STATE_CONNECTED 192 && connectionState != BluetoothProfile.STATE_CONNECTING) { 193 return false; 194 } 195 mapStateMachine.disconnect(); 196 Log.d(TAG, "disconnect(device= " + device + "): end devices=" + mMapInstanceMap.keySet()); 197 return true; 198 } 199 getConnectedDevices()200 public List<BluetoothDevice> getConnectedDevices() { 201 return getDevicesMatchingConnectionStates(new int[] {BluetoothAdapter.STATE_CONNECTED}); 202 } 203 getMceStateMachineForDevice(BluetoothDevice device)204 MceStateMachine getMceStateMachineForDevice(BluetoothDevice device) { 205 return mMapInstanceMap.get(device); 206 } 207 getDevicesMatchingConnectionStates(int[] states)208 public synchronized List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 209 Log.d(TAG, "getDevicesMatchingConnectionStates" + Arrays.toString(states)); 210 List<BluetoothDevice> deviceList = new ArrayList<>(); 211 BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 212 int connectionState; 213 for (BluetoothDevice device : bondedDevices) { 214 connectionState = getConnectionState(device); 215 Log.d(TAG, "Device: " + device + "State: " + connectionState); 216 for (int i = 0; i < states.length; i++) { 217 if (connectionState == states[i]) { 218 deviceList.add(device); 219 } 220 } 221 } 222 Log.d(TAG, deviceList.toString()); 223 return deviceList; 224 } 225 getConnectionState(BluetoothDevice device)226 public synchronized int getConnectionState(BluetoothDevice device) { 227 MceStateMachine mapStateMachine = mMapInstanceMap.get(device); 228 // a map state machine instance doesn't exist yet, create a new one if we can. 229 return (mapStateMachine == null) 230 ? BluetoothProfile.STATE_DISCONNECTED 231 : mapStateMachine.getState(); 232 } 233 234 /** 235 * Set connection policy of the profile and connects it if connectionPolicy is {@link 236 * BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is {@link 237 * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 238 * 239 * <p>The device should already be paired. Connection policy can be one of: {@link 240 * BluetoothProfile#CONNECTION_POLICY_ALLOWED}, {@link 241 * BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link 242 * BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 243 * 244 * @param device Paired bluetooth device 245 * @param connectionPolicy is the connection policy to set to for this profile 246 * @return true if connectionPolicy is set, false on error 247 */ 248 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) setConnectionPolicy(BluetoothDevice device, int connectionPolicy)249 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 250 Log.v(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy); 251 enforceCallingOrSelfPermission( 252 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); 253 254 if (!mDatabaseManager.setProfileConnectionPolicy( 255 device, BluetoothProfile.MAP_CLIENT, connectionPolicy)) { 256 return false; 257 } 258 if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 259 connect(device); 260 } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 261 disconnect(device); 262 } 263 return true; 264 } 265 266 /** 267 * Get the connection policy of the profile. 268 * 269 * <p>The connection policy can be any of: {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 270 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, {@link 271 * BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 272 * 273 * @param device Bluetooth device 274 * @return connection policy of the device 275 */ 276 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) getConnectionPolicy(BluetoothDevice device)277 public int getConnectionPolicy(BluetoothDevice device) { 278 enforceCallingOrSelfPermission( 279 BLUETOOTH_PRIVILEGED, "Need BLUETOOTH_PRIVILEGED permission"); 280 return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.MAP_CLIENT); 281 } 282 sendMessage( BluetoothDevice device, Uri[] contacts, String message, PendingIntent sentIntent, PendingIntent deliveredIntent)283 public synchronized boolean sendMessage( 284 BluetoothDevice device, 285 Uri[] contacts, 286 String message, 287 PendingIntent sentIntent, 288 PendingIntent deliveredIntent) { 289 MceStateMachine mapStateMachine = mMapInstanceMap.get(device); 290 return mapStateMachine != null 291 && mapStateMachine.sendMapMessage(contacts, message, sentIntent, deliveredIntent); 292 } 293 294 @Override initBinder()295 public IProfileServiceBinder initBinder() { 296 return new Binder(this); 297 } 298 299 @Override start()300 public synchronized void start() { 301 Log.d(TAG, "start()"); 302 303 mAdapterService = AdapterService.getAdapterService(); 304 mDatabaseManager = 305 Objects.requireNonNull( 306 AdapterService.getAdapterService().getDatabase(), 307 "DatabaseManager cannot be null when MapClientService starts"); 308 309 mHandler = new Handler(Looper.getMainLooper()); 310 311 if (mMnsServer == null) { 312 mMnsServer = new MnsService(this); 313 } 314 315 removeUncleanAccounts(); 316 MapClientContent.clearAllContent(this); 317 setMapClientService(this); 318 } 319 320 @Override stop()321 public synchronized void stop() { 322 Log.d(TAG, "stop()"); 323 324 if (mMnsServer != null) { 325 mMnsServer.stop(); 326 } 327 for (MceStateMachine stateMachine : mMapInstanceMap.values()) { 328 if (stateMachine.getState() == BluetoothAdapter.STATE_CONNECTED) { 329 stateMachine.disconnect(); 330 } 331 stateMachine.doQuit(); 332 } 333 mMapInstanceMap.clear(); 334 335 // Unregister Handler and stop all queued messages. 336 if (mHandler != null) { 337 mHandler.removeCallbacksAndMessages(null); 338 mHandler = null; 339 } 340 } 341 342 @Override cleanup()343 public void cleanup() { 344 Log.d(TAG, "cleanup"); 345 removeUncleanAccounts(); 346 // TODO(b/72948646): should be moved to stop() 347 setMapClientService(null); 348 } 349 350 /** 351 * cleanupDevice removes the associated state machine from the instance map 352 * 353 * @param device BluetoothDevice address of remote device 354 * @param sm the state machine to clean up or {@code null} to clean up any state machine. 355 */ 356 @VisibleForTesting cleanupDevice(BluetoothDevice device, MceStateMachine sm)357 public void cleanupDevice(BluetoothDevice device, MceStateMachine sm) { 358 Log.d(TAG, "cleanup(device= " + device + "): devices=" + mMapInstanceMap.keySet()); 359 synchronized (mMapInstanceMap) { 360 MceStateMachine stateMachine = mMapInstanceMap.get(device); 361 if (stateMachine != null) { 362 if (sm == null || stateMachine == sm) { 363 mMapInstanceMap.remove(device); 364 stateMachine.doQuit(); 365 } else { 366 Log.w(TAG, "Trying to clean up wrong state machine"); 367 } 368 } 369 } 370 Log.d(TAG, "cleanup(device= " + device + "): end devices=" + mMapInstanceMap.keySet()); 371 } 372 373 @VisibleForTesting removeUncleanAccounts()374 void removeUncleanAccounts() { 375 Log.d(TAG, "removeUncleanAccounts(): devices=" + mMapInstanceMap.keySet()); 376 Iterator iterator = mMapInstanceMap.entrySet().iterator(); 377 while (iterator.hasNext()) { 378 Map.Entry<BluetoothDevice, MceStateMachine> profileConnection = 379 (Map.Entry) iterator.next(); 380 if (profileConnection.getValue().getState() == BluetoothProfile.STATE_DISCONNECTED) { 381 iterator.remove(); 382 } 383 } 384 Log.d(TAG, "removeUncleanAccounts(): end devices=" + mMapInstanceMap.keySet()); 385 } 386 getUnreadMessages(BluetoothDevice device)387 public synchronized boolean getUnreadMessages(BluetoothDevice device) { 388 MceStateMachine mapStateMachine = mMapInstanceMap.get(device); 389 if (mapStateMachine == null) { 390 return false; 391 } 392 return mapStateMachine.getUnreadMessages(); 393 } 394 395 /** 396 * Returns the SDP record's MapSupportedFeatures field (see Bluetooth MAP 1.4 spec, page 114). 397 * 398 * @param device The Bluetooth device to get this value for. 399 * @return the SDP record's MapSupportedFeatures field. 400 */ getSupportedFeatures(BluetoothDevice device)401 public synchronized int getSupportedFeatures(BluetoothDevice device) { 402 MceStateMachine mapStateMachine = mMapInstanceMap.get(device); 403 if (mapStateMachine == null) { 404 Log.d(TAG, "in getSupportedFeatures, returning 0"); 405 return 0; 406 } 407 return mapStateMachine.getSupportedFeatures(); 408 } 409 setMessageStatus( BluetoothDevice device, String handle, int status)410 public synchronized boolean setMessageStatus( 411 BluetoothDevice device, String handle, int status) { 412 MceStateMachine mapStateMachine = mMapInstanceMap.get(device); 413 if (mapStateMachine == null) { 414 return false; 415 } 416 return mapStateMachine.setMessageStatus(handle, status); 417 } 418 419 @Override dump(StringBuilder sb)420 public void dump(StringBuilder sb) { 421 super.dump(sb); 422 for (MceStateMachine stateMachine : mMapInstanceMap.values()) { 423 stateMachine.dump(sb); 424 } 425 } 426 427 // Binder object: Must be static class or memory leak may occur 428 429 /** 430 * This class implements the IClient interface - or actually it validates the preconditions for 431 * calling the actual functionality in the MapClientService, and calls it. 432 */ 433 @VisibleForTesting 434 static class Binder extends IBluetoothMapClient.Stub implements IProfileServiceBinder { 435 private MapClientService mService; 436 Binder(MapClientService service)437 Binder(MapClientService service) { 438 Log.v(TAG, "Binder()"); 439 mService = service; 440 } 441 442 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getService(AttributionSource source)443 private MapClientService getService(AttributionSource source) { 444 if (Utils.isInstrumentationTestMode()) { 445 return mService; 446 } 447 if (!Utils.checkServiceAvailable(mService, TAG) 448 || !(getCallingUserHandle().isSystem() 449 || Utils.checkCallerIsSystemOrActiveOrManagedUser(mService, TAG)) 450 || !Utils.checkConnectPermissionForDataDelivery(mService, source, TAG)) { 451 return null; 452 } 453 return mService; 454 } 455 456 @Override cleanup()457 public void cleanup() { 458 mService = null; 459 } 460 461 @Override isConnected(BluetoothDevice device, AttributionSource source)462 public boolean isConnected(BluetoothDevice device, AttributionSource source) { 463 Log.v(TAG, "isConnected()"); 464 465 MapClientService service = getService(source); 466 if (service == null) { 467 return false; 468 } 469 470 return service.getConnectionState(device) == BluetoothProfile.STATE_CONNECTED; 471 } 472 473 @Override connect(BluetoothDevice device, AttributionSource source)474 public boolean connect(BluetoothDevice device, AttributionSource source) { 475 Log.v(TAG, "connect()"); 476 477 MapClientService service = getService(source); 478 if (service == null) { 479 return false; 480 } 481 482 return service.connect(device); 483 } 484 485 @Override disconnect(BluetoothDevice device, AttributionSource source)486 public boolean disconnect(BluetoothDevice device, AttributionSource source) { 487 Log.v(TAG, "disconnect()"); 488 489 MapClientService service = getService(source); 490 if (service == null) { 491 return false; 492 } 493 494 return service.disconnect(device); 495 } 496 497 @Override getConnectedDevices(AttributionSource source)498 public List<BluetoothDevice> getConnectedDevices(AttributionSource source) { 499 Log.v(TAG, "getConnectedDevices()"); 500 501 MapClientService service = getService(source); 502 if (service == null) { 503 return Collections.emptyList(); 504 } 505 506 return service.getConnectedDevices(); 507 } 508 509 @Override getDevicesMatchingConnectionStates( int[] states, AttributionSource source)510 public List<BluetoothDevice> getDevicesMatchingConnectionStates( 511 int[] states, AttributionSource source) { 512 Log.v(TAG, "getDevicesMatchingConnectionStates()"); 513 514 MapClientService service = getService(source); 515 if (service == null) { 516 return Collections.emptyList(); 517 } 518 return service.getDevicesMatchingConnectionStates(states); 519 } 520 521 @Override getConnectionState(BluetoothDevice device, AttributionSource source)522 public int getConnectionState(BluetoothDevice device, AttributionSource source) { 523 Log.v(TAG, "getConnectionState()"); 524 525 MapClientService service = getService(source); 526 if (service == null) { 527 return BluetoothProfile.STATE_DISCONNECTED; 528 } 529 530 return service.getConnectionState(device); 531 } 532 533 @Override setConnectionPolicy( BluetoothDevice device, int connectionPolicy, AttributionSource source)534 public boolean setConnectionPolicy( 535 BluetoothDevice device, int connectionPolicy, AttributionSource source) { 536 Log.v(TAG, "setConnectionPolicy()"); 537 538 MapClientService service = getService(source); 539 if (service == null) { 540 return false; 541 } 542 543 return service.setConnectionPolicy(device, connectionPolicy); 544 } 545 546 @Override getConnectionPolicy(BluetoothDevice device, AttributionSource source)547 public int getConnectionPolicy(BluetoothDevice device, AttributionSource source) { 548 Log.v(TAG, "getConnectionPolicy()"); 549 550 MapClientService service = getService(source); 551 if (service == null) { 552 return BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 553 } 554 555 return service.getConnectionPolicy(device); 556 } 557 558 @Override sendMessage( BluetoothDevice device, Uri[] contacts, String message, PendingIntent sentIntent, PendingIntent deliveredIntent, AttributionSource source)559 public boolean sendMessage( 560 BluetoothDevice device, 561 Uri[] contacts, 562 String message, 563 PendingIntent sentIntent, 564 PendingIntent deliveredIntent, 565 AttributionSource source) { 566 Log.v(TAG, "sendMessage()"); 567 568 MapClientService service = getService(source); 569 if (service == null) { 570 return false; 571 } 572 573 Log.d(TAG, "Checking Permission of sendMessage"); 574 mService.enforceCallingOrSelfPermission( 575 Manifest.permission.SEND_SMS, "Need SEND_SMS permission"); 576 577 return service.sendMessage(device, contacts, message, sentIntent, deliveredIntent); 578 } 579 580 @Override getUnreadMessages(BluetoothDevice device, AttributionSource source)581 public boolean getUnreadMessages(BluetoothDevice device, AttributionSource source) { 582 Log.v(TAG, "getUnreadMessages()"); 583 584 MapClientService service = getService(source); 585 if (service == null) { 586 return false; 587 } 588 589 mService.enforceCallingOrSelfPermission( 590 Manifest.permission.READ_SMS, "Need READ_SMS permission"); 591 return service.getUnreadMessages(device); 592 } 593 594 @Override getSupportedFeatures(BluetoothDevice device, AttributionSource source)595 public int getSupportedFeatures(BluetoothDevice device, AttributionSource source) { 596 Log.v(TAG, "getSupportedFeatures()"); 597 598 MapClientService service = getService(source); 599 if (service == null) { 600 Log.d(TAG, "in MapClientService getSupportedFeatures stub, returning 0"); 601 return 0; 602 } 603 return service.getSupportedFeatures(device); 604 } 605 606 @Override setMessageStatus( BluetoothDevice device, String handle, int status, AttributionSource source)607 public boolean setMessageStatus( 608 BluetoothDevice device, String handle, int status, AttributionSource source) { 609 Log.v(TAG, "setMessageStatus()"); 610 611 MapClientService service = getService(source); 612 if (service == null) { 613 return false; 614 } 615 mService.enforceCallingOrSelfPermission( 616 Manifest.permission.READ_SMS, "Need READ_SMS permission"); 617 return service.setMessageStatus(device, handle, status); 618 } 619 } 620 aclDisconnected(BluetoothDevice device, int transport)621 public void aclDisconnected(BluetoothDevice device, int transport) { 622 mHandler.post(() -> handleAclDisconnected(device, transport)); 623 } 624 handleAclDisconnected(BluetoothDevice device, int transport)625 private void handleAclDisconnected(BluetoothDevice device, int transport) { 626 MceStateMachine stateMachine = mMapInstanceMap.get(device); 627 if (stateMachine == null) { 628 Log.e(TAG, "No Statemachine found for the device=" + device.toString()); 629 return; 630 } 631 632 Log.i( 633 TAG, 634 "Received ACL disconnection event, device=" 635 + device.toString() 636 + ", transport=" 637 + transport); 638 639 if (transport != BluetoothDevice.TRANSPORT_BREDR) { 640 return; 641 } 642 643 if (stateMachine.getState() == BluetoothProfile.STATE_CONNECTED) { 644 stateMachine.disconnect(); 645 } 646 } 647 receiveSdpSearchRecord( BluetoothDevice device, int status, Parcelable record, ParcelUuid uuid)648 public void receiveSdpSearchRecord( 649 BluetoothDevice device, int status, Parcelable record, ParcelUuid uuid) { 650 mHandler.post(() -> handleSdpSearchRecordReceived(device, status, record, uuid)); 651 } 652 handleSdpSearchRecordReceived( BluetoothDevice device, int status, Parcelable record, ParcelUuid uuid)653 private void handleSdpSearchRecordReceived( 654 BluetoothDevice device, int status, Parcelable record, ParcelUuid uuid) { 655 MceStateMachine stateMachine = mMapInstanceMap.get(device); 656 Log.d(TAG, "Received SDP Record, device=" + device.toString() + ", uuid=" + uuid); 657 if (stateMachine == null) { 658 Log.e(TAG, "No Statemachine found for the device=" + device.toString()); 659 return; 660 } 661 if (uuid.equals(BluetoothUuid.MAS)) { 662 // Check if we have a valid SDP record. 663 SdpMasRecord masRecord = (SdpMasRecord) record; 664 Log.d(TAG, "SDP complete, status: " + status + ", record:" + masRecord); 665 stateMachine.sendSdpResult(status, masRecord); 666 } 667 } 668 } 669