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