1 /* 2 * Copyright 2022 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.bas; 18 19 import static android.Manifest.permission.BLUETOOTH_CONNECT; 20 21 import static com.android.bluetooth.Utils.enforceBluetoothPrivilegedPermission; 22 23 import android.annotation.RequiresPermission; 24 import android.bluetooth.BluetoothDevice; 25 import android.bluetooth.BluetoothProfile; 26 import android.bluetooth.BluetoothUuid; 27 import android.bluetooth.IBluetoothBattery; 28 import android.content.AttributionSource; 29 import android.content.BroadcastReceiver; 30 import android.content.Context; 31 import android.content.Intent; 32 import android.content.IntentFilter; 33 import android.os.HandlerThread; 34 import android.os.ParcelUuid; 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 import com.android.modules.utils.SynchronousResultReceiver; 44 45 import java.lang.ref.WeakReference; 46 import java.util.ArrayList; 47 import java.util.HashMap; 48 import java.util.List; 49 import java.util.Map; 50 import java.util.Objects; 51 52 /** 53 * A profile service that connects to the Battery service (BAS) of BLE devices 54 */ 55 public class BatteryService extends ProfileService { 56 private static final boolean DBG = false; 57 private static final String TAG = "BatteryService"; 58 59 // Timeout for state machine thread join, to prevent potential ANR. 60 private static final int SM_THREAD_JOIN_TIMEOUT_MS = 1_000; 61 62 private static final int MAX_BATTERY_STATE_MACHINES = 10; 63 private static BatteryService sBatteryService; 64 private AdapterService mAdapterService; 65 private DatabaseManager mDatabaseManager; 66 private HandlerThread mStateMachinesThread; 67 private final Map<BluetoothDevice, BatteryStateMachine> mStateMachines = new HashMap<>(); 68 69 private BroadcastReceiver mBondStateChangedReceiver; 70 isEnabled()71 public static boolean isEnabled() { 72 return BluetoothProperties.isProfileBasClientEnabled().orElse(false); 73 } 74 75 @Override initBinder()76 protected IProfileServiceBinder initBinder() { 77 return new BluetoothBatteryBinder(this); 78 } 79 80 @Override create()81 protected void create() { 82 if (DBG) { 83 Log.d(TAG, "create()"); 84 } 85 } 86 87 @Override start()88 protected boolean start() { 89 if (DBG) { 90 Log.d(TAG, "start()"); 91 } 92 if (sBatteryService != null) { 93 throw new IllegalStateException("start() called twice"); 94 } 95 96 mAdapterService = Objects.requireNonNull(AdapterService.getAdapterService(), 97 "AdapterService cannot be null when BatteryService starts"); 98 mDatabaseManager = Objects.requireNonNull(mAdapterService.getDatabase(), 99 "DatabaseManager cannot be null when BatteryService starts"); 100 101 mStateMachines.clear(); 102 mStateMachinesThread = new HandlerThread("BatteryService.StateMachines"); 103 mStateMachinesThread.start(); 104 105 // Setup broadcast receivers 106 IntentFilter filter = new IntentFilter(); 107 filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 108 filter.addAction(BluetoothDevice.ACTION_BOND_STATE_CHANGED); 109 mBondStateChangedReceiver = new BondStateChangedReceiver(); 110 registerReceiver(mBondStateChangedReceiver, filter); 111 112 setBatteryService(this); 113 114 return true; 115 } 116 117 @Override stop()118 protected boolean stop() { 119 if (DBG) { 120 Log.d(TAG, "stop()"); 121 } 122 if (sBatteryService == null) { 123 Log.w(TAG, "stop() called before start()"); 124 return true; 125 } 126 127 setBatteryService(null); 128 // Unregister broadcast receivers 129 unregisterReceiver(mBondStateChangedReceiver); 130 mBondStateChangedReceiver = null; 131 132 // Destroy state machines and stop handler thread 133 synchronized (mStateMachines) { 134 for (BatteryStateMachine sm : mStateMachines.values()) { 135 sm.doQuit(); 136 sm.cleanup(); 137 } 138 mStateMachines.clear(); 139 } 140 141 142 if (mStateMachinesThread != null) { 143 try { 144 mStateMachinesThread.quitSafely(); 145 mStateMachinesThread.join(SM_THREAD_JOIN_TIMEOUT_MS); 146 mStateMachinesThread = null; 147 } catch (InterruptedException e) { 148 // Do not rethrow as we are shutting down anyway 149 } 150 } 151 152 mAdapterService = null; 153 154 return true; 155 } 156 157 @Override cleanup()158 protected void cleanup() { 159 if (DBG) { 160 Log.d(TAG, "cleanup()"); 161 } 162 } 163 164 /** 165 * Gets the BatteryService instance 166 */ getBatteryService()167 public static synchronized BatteryService getBatteryService() { 168 if (sBatteryService == null) { 169 Log.w(TAG, "getBatteryService(): service is NULL"); 170 return null; 171 } 172 173 if (!sBatteryService.isAvailable()) { 174 Log.w(TAG, "getBatteryService(): service is not available"); 175 return null; 176 } 177 return sBatteryService; 178 } 179 180 /** 181 * Sets the battery service instance. It should be called only for testing purpose. 182 */ 183 @VisibleForTesting setBatteryService(BatteryService instance)184 public static synchronized void setBatteryService(BatteryService instance) { 185 if (DBG) { 186 Log.d(TAG, "setBatteryService(): set to: " + instance); 187 } 188 sBatteryService = instance; 189 } 190 191 /** 192 * Connects to the battery service of the given device. 193 */ 194 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) connect(BluetoothDevice device)195 public boolean connect(BluetoothDevice device) { 196 enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, 197 "Need BLUETOOTH_PRIVILEGED permission"); 198 if (DBG) { 199 Log.d(TAG, "connect(): " + device); 200 } 201 if (device == null) { 202 Log.w(TAG, "Ignore connecting to null device"); 203 return false; 204 } 205 206 if (getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 207 Log.w(TAG, "Cannot connect to " + device + " : policy forbidden"); 208 return false; 209 } 210 ParcelUuid[] featureUuids = mAdapterService.getRemoteUuids(device); 211 if (!Utils.arrayContains(featureUuids, BluetoothUuid.BATTERY)) { 212 Log.e(TAG, "Cannot connect to " + device 213 + " : Remote does not have Battery UUID"); 214 return false; 215 } 216 217 synchronized (mStateMachines) { 218 BatteryStateMachine sm = getOrCreateStateMachine(device); 219 if (sm == null) { 220 Log.e(TAG, "Cannot connect to " + device + " : no state machine"); 221 return false; 222 } 223 sm.sendMessage(BatteryStateMachine.CONNECT); 224 } 225 226 return true; 227 } 228 229 /** 230 * Connects to the battery service of the given device if possible. 231 * If it's impossible, it doesn't try without logging errors. 232 */ connectIfPossible(BluetoothDevice device)233 public boolean connectIfPossible(BluetoothDevice device) { 234 if (device == null 235 || getConnectionPolicy(device) == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN 236 || !Utils.arrayContains( 237 mAdapterService.getRemoteUuids(device), BluetoothUuid.BATTERY)) { 238 return false; 239 } 240 return connect(device); 241 } 242 243 /** 244 * Disconnects from the battery service of the given device. 245 */ 246 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) disconnect(BluetoothDevice device)247 public boolean disconnect(BluetoothDevice device) { 248 enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, 249 "Need BLUETOOTH_PRIVILEGED permission"); 250 if (DBG) { 251 Log.d(TAG, "disconnect(): " + device); 252 } 253 if (device == null) { 254 Log.w(TAG, "Ignore disconnecting to null device"); 255 return false; 256 } 257 synchronized (mStateMachines) { 258 BatteryStateMachine sm = getOrCreateStateMachine(device); 259 if (sm != null) { 260 sm.sendMessage(BatteryStateMachine.DISCONNECT); 261 } 262 } 263 264 return true; 265 } 266 267 /** 268 * Gets devices that battery service is connected. 269 * @return 270 */ 271 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) getConnectedDevices()272 public List<BluetoothDevice> getConnectedDevices() { 273 enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, 274 "Need BLUETOOTH_PRIVILEGED permission"); 275 synchronized (mStateMachines) { 276 List<BluetoothDevice> devices = new ArrayList<>(); 277 for (BatteryStateMachine sm : mStateMachines.values()) { 278 if (sm.isConnected()) { 279 devices.add(sm.getDevice()); 280 } 281 } 282 return devices; 283 } 284 } 285 286 /** 287 * Check whether it can connect to a peer device. 288 * The check considers a number of factors during the evaluation. 289 * 290 * @param device the peer device to connect to 291 * @return true if connection is allowed, otherwise false 292 */ 293 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) canConnect(BluetoothDevice device)294 public boolean canConnect(BluetoothDevice device) { 295 // Check connectionPolicy and accept or reject the connection. 296 int connectionPolicy = getConnectionPolicy(device); 297 int bondState = mAdapterService.getBondState(device); 298 // Allow this connection only if the device is bonded. Any attempt to connect while 299 // bonding would potentially lead to an unauthorized connection. 300 if (bondState != BluetoothDevice.BOND_BONDED) { 301 Log.w(TAG, "canConnect: return false, bondState=" + bondState); 302 return false; 303 } else if (connectionPolicy != BluetoothProfile.CONNECTION_POLICY_UNKNOWN 304 && connectionPolicy != BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 305 // Otherwise, reject the connection if connectionPolicy is not valid. 306 Log.w(TAG, "canConnect: return false, connectionPolicy=" + connectionPolicy); 307 return false; 308 } 309 return true; 310 } 311 312 /** 313 * Called when the connection state of a state machine is changed 314 */ 315 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) handleConnectionStateChanged(BatteryStateMachine sm, int fromState, int toState)316 public void handleConnectionStateChanged(BatteryStateMachine sm, 317 int fromState, int toState) { 318 BluetoothDevice device = sm.getDevice(); 319 if ((sm == null) || (fromState == toState)) { 320 Log.e(TAG, "connectionStateChanged: unexpected invocation. device=" + device 321 + " fromState=" + fromState + " toState=" + toState); 322 return; 323 } 324 325 // Check if the device is disconnected - if unbonded, remove the state machine 326 if (toState == BluetoothProfile.STATE_DISCONNECTED) { 327 int bondState = mAdapterService.getBondState(device); 328 if (bondState == BluetoothDevice.BOND_NONE) { 329 if (DBG) { 330 Log.d(TAG, device + " is unbonded. Remove state machine"); 331 } 332 removeStateMachine(device); 333 } 334 } 335 } 336 337 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) getDevicesMatchingConnectionStates(int[] states)338 List<BluetoothDevice> getDevicesMatchingConnectionStates(int[] states) { 339 enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, 340 "Need BLUETOOTH_PRIVILEGED permission"); 341 ArrayList<BluetoothDevice> devices = new ArrayList<>(); 342 if (states == null) { 343 return devices; 344 } 345 final BluetoothDevice[] bondedDevices = mAdapterService.getBondedDevices(); 346 if (bondedDevices == null) { 347 return devices; 348 } 349 synchronized (mStateMachines) { 350 for (BluetoothDevice device : bondedDevices) { 351 int connectionState = BluetoothProfile.STATE_DISCONNECTED; 352 BatteryStateMachine sm = mStateMachines.get(device); 353 if (sm != null) { 354 connectionState = sm.getConnectionState(); 355 } 356 for (int state : states) { 357 if (connectionState == state) { 358 devices.add(device); 359 break; 360 } 361 } 362 } 363 return devices; 364 } 365 } 366 367 /** 368 * Get the list of devices that have state machines. 369 * 370 * @return the list of devices that have state machines 371 */ 372 @VisibleForTesting getDevices()373 List<BluetoothDevice> getDevices() { 374 List<BluetoothDevice> devices = new ArrayList<>(); 375 synchronized (mStateMachines) { 376 for (BatteryStateMachine sm : mStateMachines.values()) { 377 devices.add(sm.getDevice()); 378 } 379 return devices; 380 } 381 } 382 383 /** 384 * Gets the connection state of the given device's battery service 385 */ 386 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getConnectionState(BluetoothDevice device)387 public int getConnectionState(BluetoothDevice device) { 388 enforceCallingOrSelfPermission(BLUETOOTH_CONNECT, 389 "Need BLUETOOTH_CONNECT permission"); 390 synchronized (mStateMachines) { 391 BatteryStateMachine sm = mStateMachines.get(device); 392 if (sm == null) { 393 return BluetoothProfile.STATE_DISCONNECTED; 394 } 395 return sm.getConnectionState(); 396 } 397 } 398 399 /** 400 * Set connection policy of the profile and connects it if connectionPolicy is 401 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED} or disconnects if connectionPolicy is 402 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN} 403 * 404 * <p> The device should already be paired. 405 * Connection policy can be one of: 406 * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, 407 * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, 408 * {@link BluetoothProfile#CONNECTION_POLICY_UNKNOWN} 409 * 410 * @param device the remote device 411 * @param connectionPolicy is the connection policy to set to for this profile 412 * @return true on success, otherwise false 413 */ 414 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) setConnectionPolicy(BluetoothDevice device, int connectionPolicy)415 public boolean setConnectionPolicy(BluetoothDevice device, int connectionPolicy) { 416 enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, 417 "Need BLUETOOTH_PRIVILEGED permission"); 418 if (DBG) { 419 Log.d(TAG, "Saved connectionPolicy " + device + " = " + connectionPolicy); 420 } 421 mDatabaseManager.setProfileConnectionPolicy(device, BluetoothProfile.BATTERY, 422 connectionPolicy); 423 if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_ALLOWED) { 424 connect(device); 425 } else if (connectionPolicy == BluetoothProfile.CONNECTION_POLICY_FORBIDDEN) { 426 disconnect(device); 427 } 428 return true; 429 } 430 431 /** 432 * Gets the connection policy for the battery service of the given device. 433 */ 434 @RequiresPermission(android.Manifest.permission.BLUETOOTH_PRIVILEGED) getConnectionPolicy(BluetoothDevice device)435 public int getConnectionPolicy(BluetoothDevice device) { 436 enforceCallingOrSelfPermission(BLUETOOTH_PRIVILEGED, 437 "Need BLUETOOTH_PRIVILEGED permission"); 438 return mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.BATTERY); 439 } 440 /** 441 * Called when the battery level of the device is notified. 442 */ 443 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) handleBatteryChanged(BluetoothDevice device, int batteryLevel)444 public void handleBatteryChanged(BluetoothDevice device, int batteryLevel) { 445 mAdapterService.setBatteryLevel(device, batteryLevel); 446 } 447 getOrCreateStateMachine(BluetoothDevice device)448 private BatteryStateMachine getOrCreateStateMachine(BluetoothDevice device) { 449 if (device == null) { 450 Log.e(TAG, "getOrCreateGatt failed: device cannot be null"); 451 return null; 452 } 453 synchronized (mStateMachines) { 454 BatteryStateMachine sm = mStateMachines.get(device); 455 if (sm != null) { 456 return sm; 457 } 458 // Limit the maximum number of state machines to avoid DoS attack 459 if (mStateMachines.size() >= MAX_BATTERY_STATE_MACHINES) { 460 Log.e(TAG, "Maximum number of Battery state machines reached: " 461 + MAX_BATTERY_STATE_MACHINES); 462 return null; 463 } 464 if (DBG) { 465 Log.d(TAG, "Creating a new state machine for " + device); 466 } 467 sm = BatteryStateMachine.make(device, this, mStateMachinesThread.getLooper()); 468 mStateMachines.put(device, sm); 469 return sm; 470 } 471 } 472 473 // Remove state machine if the bonding for a device is removed 474 private class BondStateChangedReceiver extends BroadcastReceiver { 475 @Override onReceive(Context context, Intent intent)476 public void onReceive(Context context, Intent intent) { 477 if (!BluetoothDevice.ACTION_BOND_STATE_CHANGED.equals(intent.getAction())) { 478 return; 479 } 480 int state = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, 481 BluetoothDevice.ERROR); 482 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 483 Objects.requireNonNull(device, "ACTION_BOND_STATE_CHANGED with no EXTRA_DEVICE"); 484 handleBondStateChanged(device, state); 485 } 486 } 487 488 /** 489 * Process a change in the bonding state for a device. 490 * 491 * @param device the device whose bonding state has changed 492 * @param bondState the new bond state for the device. Possible values are: 493 * {@link BluetoothDevice#BOND_NONE}, 494 * {@link BluetoothDevice#BOND_BONDING}, 495 * {@link BluetoothDevice#BOND_BONDED}, 496 * {@link BluetoothDevice#ERROR}. 497 */ 498 @VisibleForTesting handleBondStateChanged(BluetoothDevice device, int bondState)499 void handleBondStateChanged(BluetoothDevice device, int bondState) { 500 if (DBG) { 501 Log.d(TAG, "Bond state changed for device: " + device + " state: " + bondState); 502 } 503 // Remove state machine if the bonding for a device is removed 504 if (bondState != BluetoothDevice.BOND_NONE) { 505 return; 506 } 507 508 synchronized (mStateMachines) { 509 BatteryStateMachine sm = mStateMachines.get(device); 510 if (sm == null) { 511 return; 512 } 513 if (sm.getConnectionState() != BluetoothProfile.STATE_DISCONNECTED) { 514 return; 515 } 516 removeStateMachine(device); 517 } 518 } 519 removeStateMachine(BluetoothDevice device)520 private void removeStateMachine(BluetoothDevice device) { 521 if (device == null) { 522 Log.e(TAG, "removeStateMachine failed: device cannot be null"); 523 return; 524 } 525 synchronized (mStateMachines) { 526 BatteryStateMachine sm = mStateMachines.remove(device); 527 if (sm == null) { 528 Log.w(TAG, "removeStateMachine: device " + device 529 + " does not have a state machine"); 530 return; 531 } 532 Log.i(TAG, "removeGatt: removing bluetooth gatt for device: " + device); 533 sm.doQuit(); 534 sm.cleanup(); 535 } 536 } 537 538 /** 539 * Binder object: must be a static class or memory leak may occur 540 */ 541 @VisibleForTesting 542 static class BluetoothBatteryBinder extends IBluetoothBattery.Stub 543 implements IProfileServiceBinder { 544 private final WeakReference<BatteryService> mServiceRef; 545 546 @RequiresPermission(android.Manifest.permission.BLUETOOTH_CONNECT) getService(AttributionSource source)547 private BatteryService getService(AttributionSource source) { 548 BatteryService service = mServiceRef.get(); 549 if (Utils.isInstrumentationTestMode()) { 550 return service; 551 } 552 553 if (!Utils.checkServiceAvailable(service, TAG) 554 || !Utils.checkCallerIsSystemOrActiveOrManagedUser(service, TAG) 555 || !Utils.checkConnectPermissionForDataDelivery(service, source, TAG)) { 556 return null; 557 } 558 return service; 559 } 560 BluetoothBatteryBinder(BatteryService svc)561 BluetoothBatteryBinder(BatteryService svc) { 562 mServiceRef = new WeakReference<>(svc); 563 } 564 565 @Override cleanup()566 public void cleanup() { 567 mServiceRef.clear(); 568 } 569 570 @Override connect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)571 public void connect(BluetoothDevice device, AttributionSource source, 572 SynchronousResultReceiver receiver) { 573 try { 574 BatteryService service = getService(source); 575 boolean result = false; 576 if (service != null) { 577 result = service.connect(device); 578 } 579 receiver.send(result); 580 } catch (RuntimeException e) { 581 receiver.propagateException(e); 582 } 583 } 584 585 @Override disconnect(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)586 public void disconnect(BluetoothDevice device, AttributionSource source, 587 SynchronousResultReceiver receiver) { 588 try { 589 BatteryService service = getService(source); 590 boolean result = false; 591 if (service != null) { 592 result = service.disconnect(device); 593 } 594 receiver.send(result); 595 } catch (RuntimeException e) { 596 receiver.propagateException(e); 597 } 598 } 599 600 @Override getConnectedDevices(AttributionSource source, SynchronousResultReceiver receiver)601 public void getConnectedDevices(AttributionSource source, 602 SynchronousResultReceiver receiver) { 603 try { 604 BatteryService service = getService(source); 605 List<BluetoothDevice> result = new ArrayList<>(); 606 if (service != null) { 607 enforceBluetoothPrivilegedPermission(service); 608 result = service.getConnectedDevices(); 609 } 610 receiver.send(result); 611 } catch (RuntimeException e) { 612 receiver.propagateException(e); 613 } 614 } 615 616 @Override getDevicesMatchingConnectionStates(int[] states, AttributionSource source, SynchronousResultReceiver receiver)617 public void getDevicesMatchingConnectionStates(int[] states, 618 AttributionSource source, SynchronousResultReceiver receiver) { 619 try { 620 BatteryService service = getService(source); 621 List<BluetoothDevice> result = new ArrayList<>(); 622 if (service != null) { 623 result = service.getDevicesMatchingConnectionStates(states); 624 } 625 receiver.send(result); 626 } catch (RuntimeException e) { 627 receiver.propagateException(e); 628 } 629 } 630 631 @Override getConnectionState(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)632 public void getConnectionState(BluetoothDevice device, AttributionSource source, 633 SynchronousResultReceiver receiver) { 634 try { 635 BatteryService service = getService(source); 636 int result = BluetoothProfile.STATE_DISCONNECTED; 637 if (service != null) { 638 result = service.getConnectionState(device); 639 } 640 receiver.send(result); 641 } catch (RuntimeException e) { 642 receiver.propagateException(e); 643 } 644 } 645 646 @Override setConnectionPolicy(BluetoothDevice device, int connectionPolicy, AttributionSource source, SynchronousResultReceiver receiver)647 public void setConnectionPolicy(BluetoothDevice device, int connectionPolicy, 648 AttributionSource source, SynchronousResultReceiver receiver) { 649 try { 650 BatteryService service = getService(source); 651 boolean result = false; 652 if (service != null) { 653 result = service.setConnectionPolicy(device, connectionPolicy); 654 } 655 receiver.send(result); 656 } catch (RuntimeException e) { 657 receiver.propagateException(e); 658 } 659 } 660 661 @Override getConnectionPolicy(BluetoothDevice device, AttributionSource source, SynchronousResultReceiver receiver)662 public void getConnectionPolicy(BluetoothDevice device, AttributionSource source, 663 SynchronousResultReceiver receiver) { 664 try { 665 BatteryService service = getService(source); 666 int result = BluetoothProfile.CONNECTION_POLICY_UNKNOWN; 667 if (service != null) { 668 result = service.getConnectionPolicy(device); 669 } 670 receiver.send(result); 671 } catch (RuntimeException e) { 672 receiver.propagateException(e); 673 } 674 } 675 } 676 677 @Override dump(StringBuilder sb)678 public void dump(StringBuilder sb) { 679 super.dump(sb); 680 for (BatteryStateMachine sm : mStateMachines.values()) { 681 sm.dump(sb); 682 } 683 } 684 } 685