• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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