Conduct refactoring in BluetoothRouteProvider.
- BluetoothRouteProvider has been renamed to LegacyBluetoothRouteController - Extracted interface from BluetoothRouteProvider to make granular changes - Provide NoOp implementation to reduce null checks Bug: b/264876378 Test: tested manually Change-Id: Ibb32ba3a0825fff45ded460f747b3c3b2d14c2dd
This commit is contained in:
@@ -0,0 +1,176 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.server.media;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.Nullable;
|
||||
import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.content.Context;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Provides control over bluetooth routes.
|
||||
*/
|
||||
/* package */ interface BluetoothRouteController {
|
||||
|
||||
/**
|
||||
* Returns a new instance of {@link LegacyBluetoothRouteController}.
|
||||
*
|
||||
* <p>It may return {@link NoOpBluetoothRouteController} if Bluetooth is not supported on this
|
||||
* hardware platform.
|
||||
*/
|
||||
@NonNull
|
||||
static BluetoothRouteController createInstance(@NonNull Context context,
|
||||
@NonNull BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
|
||||
Objects.requireNonNull(context);
|
||||
Objects.requireNonNull(listener);
|
||||
|
||||
BluetoothManager bluetoothManager = (BluetoothManager)
|
||||
context.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
BluetoothAdapter btAdapter = bluetoothManager.getAdapter();
|
||||
|
||||
if (btAdapter == null) {
|
||||
return new NoOpBluetoothRouteController();
|
||||
}
|
||||
|
||||
return new LegacyBluetoothRouteController(context, btAdapter, listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes the controller to listen to events from Bluetooth stack.
|
||||
*
|
||||
* @param userHandle is needed to subscribe for broadcasts on user's behalf.
|
||||
*/
|
||||
void start(@NonNull UserHandle userHandle);
|
||||
|
||||
/**
|
||||
* Stops the controller from listening to any Bluetooth events.
|
||||
*/
|
||||
void stop();
|
||||
|
||||
/**
|
||||
* Transfers Bluetooth output to the given route.
|
||||
*
|
||||
* <p>If the route is {@code null} then active route will be deactivated.
|
||||
*
|
||||
* @param routeId to switch to or {@code null} to unset the active device.
|
||||
*/
|
||||
void transferTo(@Nullable String routeId);
|
||||
|
||||
/**
|
||||
* Returns currently selected Bluetooth route.
|
||||
*
|
||||
* @return the selected route or {@code null} if there are no active routes.
|
||||
*/
|
||||
@Nullable
|
||||
MediaRoute2Info getSelectedRoute();
|
||||
|
||||
/**
|
||||
* Returns transferable routes.
|
||||
*
|
||||
* <p>A route is considered to be transferable if the bluetooth device is connected but not
|
||||
* considered as selected.
|
||||
*
|
||||
* @return list of transferable routes or an empty list.
|
||||
*/
|
||||
@NonNull
|
||||
List<MediaRoute2Info> getTransferableRoutes();
|
||||
|
||||
/**
|
||||
* Provides all connected Bluetooth routes.
|
||||
*
|
||||
* @return list of Bluetooth routes or an empty list.
|
||||
*/
|
||||
@NonNull
|
||||
List<MediaRoute2Info> getAllBluetoothRoutes();
|
||||
|
||||
/**
|
||||
* Updates the volume for all Bluetooth devices for the given profile.
|
||||
*
|
||||
* @param devices specifies the profile, may be, {@link android.bluetooth.BluetoothA2dp}, {@link
|
||||
* android.bluetooth.BluetoothLeAudio}, or {@link android.bluetooth.BluetoothHearingAid}
|
||||
* @param volume the specific volume value for the given devices or 0 if unknown.
|
||||
* @return {@code true} if updated successfully and {@code false} otherwise.
|
||||
*/
|
||||
boolean updateVolumeForDevices(int devices, int volume);
|
||||
|
||||
/**
|
||||
* Interface for receiving events about Bluetooth routes changes.
|
||||
*/
|
||||
interface BluetoothRoutesUpdatedListener {
|
||||
|
||||
/**
|
||||
* Called when Bluetooth routes have changed.
|
||||
*
|
||||
* @param routes updated Bluetooth routes list.
|
||||
*/
|
||||
void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* No-op implementation of {@link BluetoothRouteController}.
|
||||
*
|
||||
* <p>Useful if the device does not support Bluetooth.
|
||||
*/
|
||||
class NoOpBluetoothRouteController implements BluetoothRouteController {
|
||||
|
||||
@Override
|
||||
public void start(UserHandle userHandle) {
|
||||
// no op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
// no op
|
||||
}
|
||||
|
||||
@Override
|
||||
public void transferTo(String routeId) {
|
||||
// no op
|
||||
}
|
||||
|
||||
@Override
|
||||
public MediaRoute2Info getSelectedRoute() {
|
||||
// no op
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaRoute2Info> getTransferableRoutes() {
|
||||
// no op
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<MediaRoute2Info> getAllBluetoothRoutes() {
|
||||
// no op
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean updateVolumeForDevices(int devices, int volume) {
|
||||
// no op
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import android.bluetooth.BluetoothAdapter;
|
||||
import android.bluetooth.BluetoothDevice;
|
||||
import android.bluetooth.BluetoothHearingAid;
|
||||
import android.bluetooth.BluetoothLeAudio;
|
||||
import android.bluetooth.BluetoothManager;
|
||||
import android.bluetooth.BluetoothProfile;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@@ -50,10 +49,9 @@ import java.util.HashMap;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
|
||||
class BluetoothRouteProvider {
|
||||
class LegacyBluetoothRouteController implements BluetoothRouteController {
|
||||
private static final String TAG = "BTRouteProvider";
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
|
||||
@@ -69,7 +67,7 @@ class BluetoothRouteProvider {
|
||||
|
||||
private final Context mContext;
|
||||
private final BluetoothAdapter mBluetoothAdapter;
|
||||
private final BluetoothRoutesUpdatedListener mListener;
|
||||
private final BluetoothRouteController.BluetoothRoutesUpdatedListener mListener;
|
||||
private final AudioManager mAudioManager;
|
||||
private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();
|
||||
|
||||
@@ -82,27 +80,8 @@ class BluetoothRouteProvider {
|
||||
private BluetoothHearingAid mHearingAidProfile;
|
||||
private BluetoothLeAudio mLeAudioProfile;
|
||||
|
||||
/**
|
||||
* Create an instance of {@link BluetoothRouteProvider}.
|
||||
* It may return {@code null} if Bluetooth is not supported on this hardware platform.
|
||||
*/
|
||||
@Nullable
|
||||
static BluetoothRouteProvider createInstance(@NonNull Context context,
|
||||
@NonNull BluetoothRoutesUpdatedListener listener) {
|
||||
Objects.requireNonNull(context);
|
||||
Objects.requireNonNull(listener);
|
||||
|
||||
BluetoothManager bluetoothManager = (BluetoothManager)
|
||||
context.getSystemService(Context.BLUETOOTH_SERVICE);
|
||||
BluetoothAdapter btAdapter = bluetoothManager.getAdapter();
|
||||
if (btAdapter == null) {
|
||||
return null;
|
||||
}
|
||||
return new BluetoothRouteProvider(context, btAdapter, listener);
|
||||
}
|
||||
|
||||
private BluetoothRouteProvider(Context context, BluetoothAdapter btAdapter,
|
||||
BluetoothRoutesUpdatedListener listener) {
|
||||
LegacyBluetoothRouteController(Context context, BluetoothAdapter btAdapter,
|
||||
BluetoothRouteController.BluetoothRoutesUpdatedListener listener) {
|
||||
mContext = context;
|
||||
mBluetoothAdapter = btAdapter;
|
||||
mListener = listener;
|
||||
@@ -119,7 +98,8 @@ class BluetoothRouteProvider {
|
||||
*
|
||||
* @param user {@code UserHandle} as which receiver is registered
|
||||
*/
|
||||
void start(UserHandle user) {
|
||||
@Override
|
||||
public void start(UserHandle user) {
|
||||
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
|
||||
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
|
||||
mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.LE_AUDIO);
|
||||
@@ -146,7 +126,8 @@ class BluetoothRouteProvider {
|
||||
deviceStateChangedIntentFilter, null, null);
|
||||
}
|
||||
|
||||
void stop() {
|
||||
@Override
|
||||
public void stop() {
|
||||
mContext.unregisterReceiver(mAdapterStateChangedReceiver);
|
||||
mContext.unregisterReceiver(mDeviceStateChangedReceiver);
|
||||
}
|
||||
@@ -158,7 +139,8 @@ class BluetoothRouteProvider {
|
||||
* @param routeId the id of the Bluetooth device. {@code null} denotes to clear the use of
|
||||
* BT routes.
|
||||
*/
|
||||
void transferTo(@Nullable String routeId) {
|
||||
@Override
|
||||
public void transferTo(@Nullable String routeId) {
|
||||
if (routeId == null) {
|
||||
clearActiveDevices();
|
||||
return;
|
||||
@@ -213,14 +195,16 @@ class BluetoothRouteProvider {
|
||||
}
|
||||
|
||||
@Nullable
|
||||
MediaRoute2Info getSelectedRoute() {
|
||||
@Override
|
||||
public MediaRoute2Info getSelectedRoute() {
|
||||
// For now, active routes can be multiple only when a pair of hearing aid devices is active.
|
||||
// Let the first active device represent them.
|
||||
return (mActiveRoutes.isEmpty() ? null : mActiveRoutes.get(0).mRoute);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
List<MediaRoute2Info> getTransferableRoutes() {
|
||||
@Override
|
||||
public List<MediaRoute2Info> getTransferableRoutes() {
|
||||
List<MediaRoute2Info> routes = getAllBluetoothRoutes();
|
||||
for (BluetoothRouteInfo btRoute : mActiveRoutes) {
|
||||
routes.remove(btRoute.mRoute);
|
||||
@@ -229,7 +213,8 @@ class BluetoothRouteProvider {
|
||||
}
|
||||
|
||||
@NonNull
|
||||
List<MediaRoute2Info> getAllBluetoothRoutes() {
|
||||
@Override
|
||||
public List<MediaRoute2Info> getAllBluetoothRoutes() {
|
||||
List<MediaRoute2Info> routes = new ArrayList<>();
|
||||
List<String> routeIds = new ArrayList<>();
|
||||
|
||||
@@ -255,7 +240,8 @@ class BluetoothRouteProvider {
|
||||
*
|
||||
* @return true if devices can be handled by the provider.
|
||||
*/
|
||||
boolean updateVolumeForDevices(int devices, int volume) {
|
||||
@Override
|
||||
public boolean updateVolumeForDevices(int devices, int volume) {
|
||||
int routeType;
|
||||
if ((devices & (AudioSystem.DEVICE_OUT_HEARING_AID)) != 0) {
|
||||
routeType = MediaRoute2Info.TYPE_HEARING_AID;
|
||||
@@ -415,10 +401,6 @@ class BluetoothRouteProvider {
|
||||
}
|
||||
}
|
||||
|
||||
interface BluetoothRoutesUpdatedListener {
|
||||
void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes);
|
||||
}
|
||||
|
||||
private static class BluetoothRouteInfo {
|
||||
private BluetoothDevice mBtDevice;
|
||||
private MediaRoute2Info mRoute;
|
||||
@@ -76,7 +76,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
private final Handler mHandler;
|
||||
private final Context mContext;
|
||||
private final UserHandle mUser;
|
||||
private final BluetoothRouteProvider mBtRouteProvider;
|
||||
private final BluetoothRouteController mBtRouteProvider;
|
||||
|
||||
private String mSelectedRouteId;
|
||||
// For apps without MODIFYING_AUDIO_ROUTING permission.
|
||||
@@ -108,7 +108,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
|
||||
SystemMediaRoute2Provider(Context context, UserHandle user) {
|
||||
super(COMPONENT_NAME);
|
||||
|
||||
mIsSystemRouteProvider = true;
|
||||
mContext = context;
|
||||
mUser = user;
|
||||
@@ -117,20 +116,23 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
|
||||
mAudioService = IAudioService.Stub.asInterface(
|
||||
ServiceManager.getService(Context.AUDIO_SERVICE));
|
||||
AudioRoutesInfo newAudioRoutes = null;
|
||||
try {
|
||||
newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
updateDeviceRoute(newAudioRoutes);
|
||||
|
||||
// .getInstance returns null if there is no bt adapter available
|
||||
mBtRouteProvider = BluetoothRouteProvider.createInstance(context, (routes) -> {
|
||||
mBtRouteProvider = BluetoothRouteController.createInstance(context, (routes) -> {
|
||||
publishProviderState();
|
||||
if (updateSessionInfosIfNeeded()) {
|
||||
notifySessionInfoUpdated();
|
||||
}
|
||||
});
|
||||
|
||||
AudioRoutesInfo newAudioRoutes = null;
|
||||
try {
|
||||
newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
|
||||
} catch (RemoteException e) {
|
||||
}
|
||||
|
||||
// The methods below should be called after all fields are initialized, as they
|
||||
// access the fields inside.
|
||||
updateDeviceRoute(newAudioRoutes);
|
||||
updateSessionInfosIfNeeded();
|
||||
}
|
||||
|
||||
@@ -140,23 +142,19 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
mContext.registerReceiverAsUser(mAudioReceiver, mUser,
|
||||
intentFilter, null, null);
|
||||
|
||||
if (mBtRouteProvider != null) {
|
||||
mHandler.post(() -> {
|
||||
mBtRouteProvider.start(mUser);
|
||||
notifyProviderState();
|
||||
});
|
||||
}
|
||||
mHandler.post(() -> {
|
||||
mBtRouteProvider.start(mUser);
|
||||
notifyProviderState();
|
||||
});
|
||||
updateVolume();
|
||||
}
|
||||
|
||||
public void stop() {
|
||||
mContext.unregisterReceiver(mAudioReceiver);
|
||||
if (mBtRouteProvider != null) {
|
||||
mHandler.post(() -> {
|
||||
mBtRouteProvider.stop();
|
||||
notifyProviderState();
|
||||
});
|
||||
}
|
||||
mHandler.post(() -> {
|
||||
mBtRouteProvider.stop();
|
||||
notifyProviderState();
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -218,12 +216,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
// The currently selected route is the default route.
|
||||
return;
|
||||
}
|
||||
if (mBtRouteProvider != null) {
|
||||
if (TextUtils.equals(routeId, mDeviceRoute.getId())) {
|
||||
mBtRouteProvider.transferTo(null);
|
||||
} else {
|
||||
mBtRouteProvider.transferTo(routeId);
|
||||
}
|
||||
if (TextUtils.equals(routeId, mDeviceRoute.getId())) {
|
||||
mBtRouteProvider.transferTo(null);
|
||||
} else {
|
||||
mBtRouteProvider.transferTo(routeId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,10 +257,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
RoutingSessionInfo.Builder builder = new RoutingSessionInfo.Builder(
|
||||
SYSTEM_SESSION_ID, packageName).setSystemSession(true);
|
||||
builder.addSelectedRoute(mDeviceRoute.getId());
|
||||
if (mBtRouteProvider != null) {
|
||||
for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
|
||||
builder.addTransferableRoute(route.getId());
|
||||
}
|
||||
for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
|
||||
builder.addTransferableRoute(route.getId());
|
||||
}
|
||||
return builder.setProviderId(mUniqueId).build();
|
||||
}
|
||||
@@ -311,10 +305,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
private void updateProviderState() {
|
||||
MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder();
|
||||
builder.addRoute(mDeviceRoute);
|
||||
if (mBtRouteProvider != null) {
|
||||
for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
|
||||
builder.addRoute(route);
|
||||
}
|
||||
for (MediaRoute2Info route : mBtRouteProvider.getAllBluetoothRoutes()) {
|
||||
builder.addRoute(route);
|
||||
}
|
||||
MediaRoute2ProviderInfo providerInfo = builder.build();
|
||||
setProviderState(providerInfo);
|
||||
@@ -336,12 +328,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
.setSystemSession(true);
|
||||
|
||||
MediaRoute2Info selectedRoute = mDeviceRoute;
|
||||
if (mBtRouteProvider != null) {
|
||||
MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute();
|
||||
if (selectedBtRoute != null) {
|
||||
selectedRoute = selectedBtRoute;
|
||||
builder.addTransferableRoute(mDeviceRoute.getId());
|
||||
}
|
||||
MediaRoute2Info selectedBtRoute = mBtRouteProvider.getSelectedRoute();
|
||||
if (selectedBtRoute != null) {
|
||||
selectedRoute = selectedBtRoute;
|
||||
builder.addTransferableRoute(mDeviceRoute.getId());
|
||||
}
|
||||
mSelectedRouteId = selectedRoute.getId();
|
||||
mDefaultRoute = new MediaRoute2Info.Builder(DEFAULT_ROUTE_ID, selectedRoute)
|
||||
@@ -350,10 +340,8 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
.build();
|
||||
builder.addSelectedRoute(mSelectedRouteId);
|
||||
|
||||
if (mBtRouteProvider != null) {
|
||||
for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
|
||||
builder.addTransferableRoute(route.getId());
|
||||
}
|
||||
for (MediaRoute2Info route : mBtRouteProvider.getTransferableRoutes()) {
|
||||
builder.addTransferableRoute(route.getId());
|
||||
}
|
||||
|
||||
RoutingSessionInfo newSessionInfo = builder.setProviderId(mUniqueId).build();
|
||||
@@ -432,7 +420,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider {
|
||||
.build();
|
||||
}
|
||||
|
||||
if (mBtRouteProvider != null && mBtRouteProvider.updateVolumeForDevices(devices, volume)) {
|
||||
if (mBtRouteProvider.updateVolumeForDevices(devices, volume)) {
|
||||
return;
|
||||
}
|
||||
if (mDeviceVolume != volume) {
|
||||
|
||||
Reference in New Issue
Block a user