1 /*
2  * Copyright (C) 2023 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.server.media;
18 
19 import android.annotation.NonNull;
20 import android.annotation.Nullable;
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothManager;
23 import android.content.Context;
24 import android.media.MediaRoute2Info;
25 import android.os.UserHandle;
26 
27 import java.util.Collections;
28 import java.util.List;
29 import java.util.Objects;
30 
31 /**
32  * Provides control over bluetooth routes.
33  */
34 /* package */ interface BluetoothRouteController {
35 
36     /**
37      * Returns a new instance of {@link LegacyBluetoothRouteController}.
38      *
39      * <p>It may return {@link NoOpBluetoothRouteController} if Bluetooth is not supported on this
40      * hardware platform.
41      */
42     @NonNull
createInstance(@onNull Context context, @NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener)43     static BluetoothRouteController createInstance(@NonNull Context context,
44             @NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
45         Objects.requireNonNull(context);
46         Objects.requireNonNull(listener);
47 
48         BluetoothManager bluetoothManager = (BluetoothManager)
49                 context.getSystemService(Context.BLUETOOTH_SERVICE);
50         BluetoothAdapter btAdapter = bluetoothManager.getAdapter();
51 
52         if (btAdapter == null) {
53             return new NoOpBluetoothRouteController();
54         }
55 
56         MediaFeatureFlagManager flagManager = MediaFeatureFlagManager.getInstance();
57         boolean isUsingLegacyController = flagManager.getBoolean(
58                 MediaFeatureFlagManager.FEATURE_AUDIO_STRATEGIES_IS_USING_LEGACY_CONTROLLER,
59                 true);
60 
61         if (isUsingLegacyController) {
62             return new LegacyBluetoothRouteController(context, btAdapter, listener);
63         } else {
64             return new AudioPoliciesBluetoothRouteController(context, btAdapter, listener);
65         }
66     }
67 
68     /**
69      * Makes the controller to listen to events from Bluetooth stack.
70      *
71      * @param userHandle is needed to subscribe for broadcasts on user's behalf.
72      */
start(@onNull UserHandle userHandle)73     void start(@NonNull UserHandle userHandle);
74 
75     /**
76      * Stops the controller from listening to any Bluetooth events.
77      */
stop()78     void stop();
79 
80 
81     /**
82      * Selects the route with the given {@code deviceAddress}.
83      *
84      * @param deviceAddress The physical address of the device to select. May be null to unselect
85      *                      the currently selected device.
86      * @return Whether the selection succeeds. If the selection fails, the state of the instance
87      * remains unaltered.
88      */
selectRoute(@ullable String deviceAddress)89     boolean selectRoute(@Nullable String deviceAddress);
90 
91     /**
92      * Transfers Bluetooth output to the given route.
93      *
94      * <p>If the route is {@code null} then active route will be deactivated.
95      *
96      * @param routeId to switch to or {@code null} to unset the active device.
97      */
transferTo(@ullable String routeId)98     void transferTo(@Nullable String routeId);
99 
100     /**
101      * Returns currently selected Bluetooth route.
102      *
103      * @return the selected route or {@code null} if there are no active routes.
104      */
105     @Nullable
getSelectedRoute()106     MediaRoute2Info getSelectedRoute();
107 
108     /**
109      * Returns transferable routes.
110      *
111      * <p>A route is considered to be transferable if the bluetooth device is connected but not
112      * considered as selected.
113      *
114      * @return list of transferable routes or an empty list.
115      */
116     @NonNull
getTransferableRoutes()117     List<MediaRoute2Info> getTransferableRoutes();
118 
119     /**
120      * Provides all connected Bluetooth routes.
121      *
122      * @return list of Bluetooth routes or an empty list.
123      */
124     @NonNull
getAllBluetoothRoutes()125     List<MediaRoute2Info> getAllBluetoothRoutes();
126 
127     /**
128      * Updates the volume for all Bluetooth devices for the given profile.
129      *
130      * @param devices specifies the profile, may be, {@link android.bluetooth.BluetoothA2dp}, {@link
131      * android.bluetooth.BluetoothLeAudio}, or {@link android.bluetooth.BluetoothHearingAid}
132      * @param volume the specific volume value for the given devices or 0 if unknown.
133      * @return {@code true} if updated successfully and {@code false} otherwise.
134      */
updateVolumeForDevices(int devices, int volume)135     boolean updateVolumeForDevices(int devices, int volume);
136 
137     /**
138      * Interface for receiving events about Bluetooth routes changes.
139      */
140     interface BluetoothRoutesUpdatedListener {
141 
142         /**
143          * Called when Bluetooth routes have changed.
144          *
145          * @param routes updated Bluetooth routes list.
146          */
onBluetoothRoutesUpdated(@onNull List<MediaRoute2Info> routes)147         void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes);
148     }
149 
150     /**
151      * No-op implementation of {@link BluetoothRouteController}.
152      *
153      * <p>Useful if the device does not support Bluetooth.
154      */
155     class NoOpBluetoothRouteController implements BluetoothRouteController {
156 
157         @Override
start(UserHandle userHandle)158         public void start(UserHandle userHandle) {
159             // no op
160         }
161 
162         @Override
stop()163         public void stop() {
164             // no op
165         }
166 
167         @Override
selectRoute(String deviceAddress)168         public boolean selectRoute(String deviceAddress) {
169             // no op
170             return false;
171         }
172 
173         @Override
transferTo(String routeId)174         public void transferTo(String routeId) {
175             // no op
176         }
177 
178         @Override
getSelectedRoute()179         public MediaRoute2Info getSelectedRoute() {
180             // no op
181             return null;
182         }
183 
184         @Override
getTransferableRoutes()185         public List<MediaRoute2Info> getTransferableRoutes() {
186             // no op
187             return Collections.emptyList();
188         }
189 
190         @Override
getAllBluetoothRoutes()191         public List<MediaRoute2Info> getAllBluetoothRoutes() {
192             // no op
193             return Collections.emptyList();
194         }
195 
196         @Override
updateVolumeForDevices(int devices, int volume)197         public boolean updateVolumeForDevices(int devices, int volume) {
198             // no op
199             return false;
200         }
201     }
202 }
203