Loading services/core/java/com/android/server/media/BluetoothRouteProvider.java 0 → 100644 +321 −0 Original line number Diff line number Diff line /* * Copyright 2020 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.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.MediaRoute2Info; import android.text.TextUtils; import android.util.Log; import android.util.SparseBooleanArray; import com.android.internal.R; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; class BluetoothRouteProvider { private static final String TAG = "BTRouteProvider"; private static BluetoothRouteProvider sInstance; @SuppressWarnings("WeakerAccess") /* synthetic access */ final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>(); @SuppressWarnings("WeakerAccess") /* synthetic access */ BluetoothA2dp mA2dpProfile; @SuppressWarnings("WeakerAccess") /* synthetic access */ BluetoothHearingAid mHearingAidProfile; private final Context mContext; private final BluetoothAdapter mBluetoothAdapter; private final BluetoothRoutesUpdatedListener mListener; private final Map<String, BluetoothEventReceiver> mEventReceiverMap = new HashMap<>(); private final IntentFilter mIntentFilter = new IntentFilter(); private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver(); private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener(); private BluetoothDevice mActiveDevice = null; static synchronized BluetoothRouteProvider getInstance(@NonNull Context context, @NonNull BluetoothRoutesUpdatedListener listener) { Objects.requireNonNull(context); Objects.requireNonNull(listener); if (sInstance == null) { BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); if (btAdapter == null) { return null; } sInstance = new BluetoothRouteProvider(context, btAdapter, listener); } return sInstance; } private BluetoothRouteProvider(Context context, BluetoothAdapter btAdapter, BluetoothRoutesUpdatedListener listener) { mContext = context; mBluetoothAdapter = btAdapter; mListener = listener; buildBluetoothRoutes(); mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP); mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID); // Bluetooth on/off broadcasts addEventReceiver(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedReceiver()); // Pairing broadcasts addEventReceiver(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedReceiver()); DeviceStateChangedRecevier deviceStateChangedReceiver = new DeviceStateChangedRecevier(); addEventReceiver(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED, deviceStateChangedReceiver); addEventReceiver(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, deviceStateChangedReceiver); addEventReceiver(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED, deviceStateChangedReceiver); addEventReceiver(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED, deviceStateChangedReceiver); mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, null); } private void addEventReceiver(String action, BluetoothEventReceiver eventReceiver) { mEventReceiverMap.put(action, eventReceiver); mIntentFilter.addAction(action); } private void buildBluetoothRoutes() { mBluetoothRoutes.clear(); for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) { if (device.isConnected()) { BluetoothRouteInfo newBtRoute = createBluetoothRoute(device); mBluetoothRoutes.put(device.getAddress(), newBtRoute); } } } @NonNull List<MediaRoute2Info> getBluetoothRoutes() { ArrayList<MediaRoute2Info> routes = new ArrayList<>(); for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) { routes.add(btRoute.route); } return routes; } private void notifyBluetoothRoutesUpdated() { if (mListener != null) { mListener.onBluetoothRoutesUpdated(getBluetoothRoutes()); } } private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) { BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo(); newBtRoute.btDevice = device; newBtRoute.route = new MediaRoute2Info.Builder(device.getAddress(), device.getName()) .addFeature(SystemMediaRoute2Provider.TYPE_LIVE_AUDIO) .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED) .setDescription(mContext.getResources().getText( R.string.bluetooth_a2dp_audio_route_name).toString()) .build(); newBtRoute.connectedProfiles = new SparseBooleanArray(); return newBtRoute; } private void setRouteConnectionStateForDevice(BluetoothDevice device, @MediaRoute2Info.ConnectionState int state) { if (device == null) { Log.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null"); return; } BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); if (btRoute == null) { Log.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null"); return; } if (btRoute.route.getConnectionState() != state) { btRoute.route = new MediaRoute2Info.Builder(btRoute.route) .setConnectionState(state).build(); } } interface BluetoothRoutesUpdatedListener { void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes); } private class BluetoothRouteInfo { public BluetoothDevice btDevice; public MediaRoute2Info route; public SparseBooleanArray connectedProfiles; } // These callbacks run on the main thread. private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener { public void onServiceConnected(int profile, BluetoothProfile proxy) { switch (profile) { case BluetoothProfile.A2DP: mA2dpProfile = (BluetoothA2dp) proxy; break; case BluetoothProfile.HEARING_AID: mHearingAidProfile = (BluetoothHearingAid) proxy; break; default: return; } for (BluetoothDevice device : proxy.getConnectedDevices()) { BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); if (btRoute == null) { btRoute = createBluetoothRoute(device); mBluetoothRoutes.put(device.getAddress(), btRoute); } btRoute.connectedProfiles.put(profile, true); } } public void onServiceDisconnected(int profile) { switch (profile) { case BluetoothProfile.A2DP: mA2dpProfile = null; break; case BluetoothProfile.HEARING_AID: mHearingAidProfile = null; break; default: return; } } } private class BluetoothBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); BluetoothEventReceiver receiver = mEventReceiverMap.get(action); if (receiver != null) { receiver.onReceive(context, intent, device); } } } private interface BluetoothEventReceiver { void onReceive(Context context, Intent intent, BluetoothDevice device); } private class AdapterStateChangedReceiver implements BluetoothEventReceiver { public void onReceive(Context context, Intent intent, BluetoothDevice device) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); if (state == BluetoothAdapter.STATE_OFF || state == BluetoothAdapter.STATE_TURNING_OFF) { mBluetoothRoutes.clear(); notifyBluetoothRoutesUpdated(); } else if (state == BluetoothAdapter.STATE_ON) { buildBluetoothRoutes(); if (!mBluetoothRoutes.isEmpty()) { notifyBluetoothRoutesUpdated(); } } } } private class BondStateChangedReceiver implements BluetoothEventReceiver { public void onReceive(Context context, Intent intent, BluetoothDevice device) { int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR); BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); if (bondState == BluetoothDevice.BOND_BONDED && btRoute == null) { btRoute = createBluetoothRoute(device); if (mA2dpProfile != null && mA2dpProfile.getConnectedDevices().contains(device)) { btRoute.connectedProfiles.put(BluetoothProfile.A2DP, true); } if (mHearingAidProfile != null && mHearingAidProfile.getConnectedDevices().contains(device)) { btRoute.connectedProfiles.put(BluetoothProfile.HEARING_AID, true); } mBluetoothRoutes.put(device.getAddress(), btRoute); notifyBluetoothRoutesUpdated(); } else if (bondState == BluetoothDevice.BOND_NONE && mBluetoothRoutes.remove(device.getAddress()) != null) { notifyBluetoothRoutesUpdated(); } } } private class DeviceStateChangedRecevier implements BluetoothEventReceiver { @Override public void onReceive(Context context, Intent intent, BluetoothDevice device) { switch (intent.getAction()) { case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED: String prevActiveDeviceAddress = (mActiveDevice == null) ? null : mActiveDevice.getAddress(); String curActiveDeviceAddress = (device == null) ? null : device.getAddress(); if (!TextUtils.equals(prevActiveDeviceAddress, curActiveDeviceAddress)) { if (mActiveDevice != null) { setRouteConnectionStateForDevice(mActiveDevice, MediaRoute2Info.CONNECTION_STATE_DISCONNECTED); } if (device != null) { setRouteConnectionStateForDevice(device, MediaRoute2Info.CONNECTION_STATE_CONNECTED); } notifyBluetoothRoutesUpdated(); mActiveDevice = device; } break; case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device); break; } } private void handleConnectionStateChanged(int profile, Intent intent, BluetoothDevice device) { int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); if (state == BluetoothProfile.STATE_CONNECTED) { if (btRoute == null) { btRoute = createBluetoothRoute(device); mBluetoothRoutes.put(device.getAddress(), btRoute); btRoute.connectedProfiles.put(profile, true); notifyBluetoothRoutesUpdated(); } else { btRoute.connectedProfiles.put(profile, true); } } else if (state == BluetoothProfile.STATE_DISCONNECTING || state == BluetoothProfile.STATE_DISCONNECTED) { btRoute.connectedProfiles.delete(profile); if (btRoute.connectedProfiles.size() == 0) { mBluetoothRoutes.remove(device.getAddress()); if (mActiveDevice != null && TextUtils.equals(mActiveDevice.getAddress(), device.getAddress())) { mActiveDevice = null; } notifyBluetoothRoutesUpdated(); } } } } } services/core/java/com/android/server/media/MediaRoute2Provider.java +9 −1 Original line number Diff line number Diff line Loading @@ -72,7 +72,7 @@ abstract class MediaRoute2Provider { return mSessionInfos; } void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo, void setProviderState(MediaRoute2ProviderInfo providerInfo, List<RoutingSessionInfo> sessionInfos) { if (providerInfo == null) { mProviderInfo = null; Loading @@ -89,12 +89,20 @@ abstract class MediaRoute2Provider { .build()); } mSessionInfos = sessionInfoWithProviderId; } void notifyProviderState() { if (mCallback != null) { mCallback.onProviderStateChanged(this); } } void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo, List<RoutingSessionInfo> sessionInfos) { setProviderState(providerInfo, sessionInfos); notifyProviderState(); } public boolean hasComponentName(String packageName, String className) { return mComponentName.getPackageName().equals(packageName) && mComponentName.getClassName().equals(className); Loading services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +19 −23 Original line number Diff line number Diff line Loading @@ -30,12 +30,12 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; import android.util.Log; import com.android.internal.R; import java.util.Collections; import java.util.List; /** * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers. Loading @@ -55,6 +55,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { private final IAudioService mAudioService; private final Handler mHandler; private final Context mContext; private final BluetoothRouteProvider mBtRouteProvider; private static ComponentName sComponentName = new ComponentName( SystemMediaRoute2Provider.class.getPackageName$(), Loading @@ -62,7 +63,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { //TODO: Clean up these when audio manager support multiple bt devices MediaRoute2Info mDefaultRoute; MediaRoute2Info mBluetoothA2dpRoute; @NonNull List<MediaRoute2Info> mBluetoothRoutes = Collections.EMPTY_LIST; final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { Loading @@ -87,6 +88,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> { mBluetoothRoutes = routes; publishRoutes(); }); initializeRoutes(); } Loading Loading @@ -157,7 +162,15 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { updateAudioRoutes(newAudioRoutes); } publishRoutes(); mBluetoothRoutes = mBtRouteProvider.getBluetoothRoutes(); MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); builder.addRoute(mDefaultRoute); for (MediaRoute2Info route : mBluetoothRoutes) { builder.addRoute(route); } setProviderState(builder.build(), Collections.emptyList()); mHandler.post(() -> notifyProviderState()); } void updateAudioRoutes(AudioRoutesInfo newRoutes) { Loading Loading @@ -185,21 +198,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { .addFeature(TYPE_LIVE_VIDEO) .build(); if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) { mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName; if (mCurAudioRoutesInfo.bluetoothName != null) { //TODO: mark as bluetooth once MediaRoute2Info has device type mBluetoothA2dpRoute = new MediaRoute2Info.Builder(BLUETOOTH_ROUTE_ID, mCurAudioRoutesInfo.bluetoothName) .setDescription(mContext.getResources().getText( R.string.bluetooth_a2dp_audio_route_name).toString()) .addFeature(TYPE_LIVE_AUDIO) .build(); } else { mBluetoothA2dpRoute = null; } } publishRoutes(); } Loading @@ -207,15 +205,13 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { * The first route should be the currently selected system route. * For example, if there are two system routes (BT and device speaker), * BT will be the first route in the list. * * TODO: Support multiple BT devices */ void publishRoutes() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); if (mBluetoothA2dpRoute != null) { builder.addRoute(mBluetoothA2dpRoute); } builder.addRoute(mDefaultRoute); for (MediaRoute2Info route : mBluetoothRoutes) { builder.addRoute(route); } setAndNotifyProviderState(builder.build(), Collections.emptyList()); } } Loading
services/core/java/com/android/server/media/BluetoothRouteProvider.java 0 → 100644 +321 −0 Original line number Diff line number Diff line /* * Copyright 2020 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.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.media.MediaRoute2Info; import android.text.TextUtils; import android.util.Log; import android.util.SparseBooleanArray; import com.android.internal.R; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Objects; class BluetoothRouteProvider { private static final String TAG = "BTRouteProvider"; private static BluetoothRouteProvider sInstance; @SuppressWarnings("WeakerAccess") /* synthetic access */ final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>(); @SuppressWarnings("WeakerAccess") /* synthetic access */ BluetoothA2dp mA2dpProfile; @SuppressWarnings("WeakerAccess") /* synthetic access */ BluetoothHearingAid mHearingAidProfile; private final Context mContext; private final BluetoothAdapter mBluetoothAdapter; private final BluetoothRoutesUpdatedListener mListener; private final Map<String, BluetoothEventReceiver> mEventReceiverMap = new HashMap<>(); private final IntentFilter mIntentFilter = new IntentFilter(); private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver(); private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener(); private BluetoothDevice mActiveDevice = null; static synchronized BluetoothRouteProvider getInstance(@NonNull Context context, @NonNull BluetoothRoutesUpdatedListener listener) { Objects.requireNonNull(context); Objects.requireNonNull(listener); if (sInstance == null) { BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); if (btAdapter == null) { return null; } sInstance = new BluetoothRouteProvider(context, btAdapter, listener); } return sInstance; } private BluetoothRouteProvider(Context context, BluetoothAdapter btAdapter, BluetoothRoutesUpdatedListener listener) { mContext = context; mBluetoothAdapter = btAdapter; mListener = listener; buildBluetoothRoutes(); mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP); mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID); // Bluetooth on/off broadcasts addEventReceiver(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedReceiver()); // Pairing broadcasts addEventReceiver(BluetoothDevice.ACTION_BOND_STATE_CHANGED, new BondStateChangedReceiver()); DeviceStateChangedRecevier deviceStateChangedReceiver = new DeviceStateChangedRecevier(); addEventReceiver(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED, deviceStateChangedReceiver); addEventReceiver(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, deviceStateChangedReceiver); addEventReceiver(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED, deviceStateChangedReceiver); addEventReceiver(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED, deviceStateChangedReceiver); mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, null); } private void addEventReceiver(String action, BluetoothEventReceiver eventReceiver) { mEventReceiverMap.put(action, eventReceiver); mIntentFilter.addAction(action); } private void buildBluetoothRoutes() { mBluetoothRoutes.clear(); for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) { if (device.isConnected()) { BluetoothRouteInfo newBtRoute = createBluetoothRoute(device); mBluetoothRoutes.put(device.getAddress(), newBtRoute); } } } @NonNull List<MediaRoute2Info> getBluetoothRoutes() { ArrayList<MediaRoute2Info> routes = new ArrayList<>(); for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) { routes.add(btRoute.route); } return routes; } private void notifyBluetoothRoutesUpdated() { if (mListener != null) { mListener.onBluetoothRoutesUpdated(getBluetoothRoutes()); } } private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) { BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo(); newBtRoute.btDevice = device; newBtRoute.route = new MediaRoute2Info.Builder(device.getAddress(), device.getName()) .addFeature(SystemMediaRoute2Provider.TYPE_LIVE_AUDIO) .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED) .setDescription(mContext.getResources().getText( R.string.bluetooth_a2dp_audio_route_name).toString()) .build(); newBtRoute.connectedProfiles = new SparseBooleanArray(); return newBtRoute; } private void setRouteConnectionStateForDevice(BluetoothDevice device, @MediaRoute2Info.ConnectionState int state) { if (device == null) { Log.w(TAG, "setRouteConnectionStateForDevice: device shouldn't be null"); return; } BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); if (btRoute == null) { Log.w(TAG, "setRouteConnectionStateForDevice: route shouldn't be null"); return; } if (btRoute.route.getConnectionState() != state) { btRoute.route = new MediaRoute2Info.Builder(btRoute.route) .setConnectionState(state).build(); } } interface BluetoothRoutesUpdatedListener { void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes); } private class BluetoothRouteInfo { public BluetoothDevice btDevice; public MediaRoute2Info route; public SparseBooleanArray connectedProfiles; } // These callbacks run on the main thread. private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener { public void onServiceConnected(int profile, BluetoothProfile proxy) { switch (profile) { case BluetoothProfile.A2DP: mA2dpProfile = (BluetoothA2dp) proxy; break; case BluetoothProfile.HEARING_AID: mHearingAidProfile = (BluetoothHearingAid) proxy; break; default: return; } for (BluetoothDevice device : proxy.getConnectedDevices()) { BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); if (btRoute == null) { btRoute = createBluetoothRoute(device); mBluetoothRoutes.put(device.getAddress(), btRoute); } btRoute.connectedProfiles.put(profile, true); } } public void onServiceDisconnected(int profile) { switch (profile) { case BluetoothProfile.A2DP: mA2dpProfile = null; break; case BluetoothProfile.HEARING_AID: mHearingAidProfile = null; break; default: return; } } } private class BluetoothBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); BluetoothEventReceiver receiver = mEventReceiverMap.get(action); if (receiver != null) { receiver.onReceive(context, intent, device); } } } private interface BluetoothEventReceiver { void onReceive(Context context, Intent intent, BluetoothDevice device); } private class AdapterStateChangedReceiver implements BluetoothEventReceiver { public void onReceive(Context context, Intent intent, BluetoothDevice device) { int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1); if (state == BluetoothAdapter.STATE_OFF || state == BluetoothAdapter.STATE_TURNING_OFF) { mBluetoothRoutes.clear(); notifyBluetoothRoutesUpdated(); } else if (state == BluetoothAdapter.STATE_ON) { buildBluetoothRoutes(); if (!mBluetoothRoutes.isEmpty()) { notifyBluetoothRoutesUpdated(); } } } } private class BondStateChangedReceiver implements BluetoothEventReceiver { public void onReceive(Context context, Intent intent, BluetoothDevice device) { int bondState = intent.getIntExtra(BluetoothDevice.EXTRA_BOND_STATE, BluetoothDevice.ERROR); BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); if (bondState == BluetoothDevice.BOND_BONDED && btRoute == null) { btRoute = createBluetoothRoute(device); if (mA2dpProfile != null && mA2dpProfile.getConnectedDevices().contains(device)) { btRoute.connectedProfiles.put(BluetoothProfile.A2DP, true); } if (mHearingAidProfile != null && mHearingAidProfile.getConnectedDevices().contains(device)) { btRoute.connectedProfiles.put(BluetoothProfile.HEARING_AID, true); } mBluetoothRoutes.put(device.getAddress(), btRoute); notifyBluetoothRoutesUpdated(); } else if (bondState == BluetoothDevice.BOND_NONE && mBluetoothRoutes.remove(device.getAddress()) != null) { notifyBluetoothRoutesUpdated(); } } } private class DeviceStateChangedRecevier implements BluetoothEventReceiver { @Override public void onReceive(Context context, Intent intent, BluetoothDevice device) { switch (intent.getAction()) { case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED: String prevActiveDeviceAddress = (mActiveDevice == null) ? null : mActiveDevice.getAddress(); String curActiveDeviceAddress = (device == null) ? null : device.getAddress(); if (!TextUtils.equals(prevActiveDeviceAddress, curActiveDeviceAddress)) { if (mActiveDevice != null) { setRouteConnectionStateForDevice(mActiveDevice, MediaRoute2Info.CONNECTION_STATE_DISCONNECTED); } if (device != null) { setRouteConnectionStateForDevice(device, MediaRoute2Info.CONNECTION_STATE_CONNECTED); } notifyBluetoothRoutesUpdated(); mActiveDevice = device; } break; case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED: handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device); break; } } private void handleConnectionStateChanged(int profile, Intent intent, BluetoothDevice device) { int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress()); if (state == BluetoothProfile.STATE_CONNECTED) { if (btRoute == null) { btRoute = createBluetoothRoute(device); mBluetoothRoutes.put(device.getAddress(), btRoute); btRoute.connectedProfiles.put(profile, true); notifyBluetoothRoutesUpdated(); } else { btRoute.connectedProfiles.put(profile, true); } } else if (state == BluetoothProfile.STATE_DISCONNECTING || state == BluetoothProfile.STATE_DISCONNECTED) { btRoute.connectedProfiles.delete(profile); if (btRoute.connectedProfiles.size() == 0) { mBluetoothRoutes.remove(device.getAddress()); if (mActiveDevice != null && TextUtils.equals(mActiveDevice.getAddress(), device.getAddress())) { mActiveDevice = null; } notifyBluetoothRoutesUpdated(); } } } } }
services/core/java/com/android/server/media/MediaRoute2Provider.java +9 −1 Original line number Diff line number Diff line Loading @@ -72,7 +72,7 @@ abstract class MediaRoute2Provider { return mSessionInfos; } void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo, void setProviderState(MediaRoute2ProviderInfo providerInfo, List<RoutingSessionInfo> sessionInfos) { if (providerInfo == null) { mProviderInfo = null; Loading @@ -89,12 +89,20 @@ abstract class MediaRoute2Provider { .build()); } mSessionInfos = sessionInfoWithProviderId; } void notifyProviderState() { if (mCallback != null) { mCallback.onProviderStateChanged(this); } } void setAndNotifyProviderState(MediaRoute2ProviderInfo providerInfo, List<RoutingSessionInfo> sessionInfos) { setProviderState(providerInfo, sessionInfos); notifyProviderState(); } public boolean hasComponentName(String packageName, String className) { return mComponentName.getPackageName().equals(packageName) && mComponentName.getClassName().equals(className); Loading
services/core/java/com/android/server/media/SystemMediaRoute2Provider.java +19 −23 Original line number Diff line number Diff line Loading @@ -30,12 +30,12 @@ import android.os.Handler; import android.os.Looper; import android.os.RemoteException; import android.os.ServiceManager; import android.text.TextUtils; import android.util.Log; import com.android.internal.R; import java.util.Collections; import java.util.List; /** * Provides routes for local playbacks such as phone speaker, wired headset, or Bluetooth speakers. Loading @@ -55,6 +55,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { private final IAudioService mAudioService; private final Handler mHandler; private final Context mContext; private final BluetoothRouteProvider mBtRouteProvider; private static ComponentName sComponentName = new ComponentName( SystemMediaRoute2Provider.class.getPackageName$(), Loading @@ -62,7 +63,7 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { //TODO: Clean up these when audio manager support multiple bt devices MediaRoute2Info mDefaultRoute; MediaRoute2Info mBluetoothA2dpRoute; @NonNull List<MediaRoute2Info> mBluetoothRoutes = Collections.EMPTY_LIST; final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo(); final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() { Loading @@ -87,6 +88,10 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { mAudioService = IAudioService.Stub.asInterface( ServiceManager.getService(Context.AUDIO_SERVICE)); mBtRouteProvider = BluetoothRouteProvider.getInstance(context, (routes) -> { mBluetoothRoutes = routes; publishRoutes(); }); initializeRoutes(); } Loading Loading @@ -157,7 +162,15 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { updateAudioRoutes(newAudioRoutes); } publishRoutes(); mBluetoothRoutes = mBtRouteProvider.getBluetoothRoutes(); MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); builder.addRoute(mDefaultRoute); for (MediaRoute2Info route : mBluetoothRoutes) { builder.addRoute(route); } setProviderState(builder.build(), Collections.emptyList()); mHandler.post(() -> notifyProviderState()); } void updateAudioRoutes(AudioRoutesInfo newRoutes) { Loading Loading @@ -185,21 +198,6 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { .addFeature(TYPE_LIVE_VIDEO) .build(); if (!TextUtils.equals(newRoutes.bluetoothName, mCurAudioRoutesInfo.bluetoothName)) { mCurAudioRoutesInfo.bluetoothName = newRoutes.bluetoothName; if (mCurAudioRoutesInfo.bluetoothName != null) { //TODO: mark as bluetooth once MediaRoute2Info has device type mBluetoothA2dpRoute = new MediaRoute2Info.Builder(BLUETOOTH_ROUTE_ID, mCurAudioRoutesInfo.bluetoothName) .setDescription(mContext.getResources().getText( R.string.bluetooth_a2dp_audio_route_name).toString()) .addFeature(TYPE_LIVE_AUDIO) .build(); } else { mBluetoothA2dpRoute = null; } } publishRoutes(); } Loading @@ -207,15 +205,13 @@ class SystemMediaRoute2Provider extends MediaRoute2Provider { * The first route should be the currently selected system route. * For example, if there are two system routes (BT and device speaker), * BT will be the first route in the list. * * TODO: Support multiple BT devices */ void publishRoutes() { MediaRoute2ProviderInfo.Builder builder = new MediaRoute2ProviderInfo.Builder(); if (mBluetoothA2dpRoute != null) { builder.addRoute(mBluetoothA2dpRoute); } builder.addRoute(mDefaultRoute); for (MediaRoute2Info route : mBluetoothRoutes) { builder.addRoute(route); } setAndNotifyProviderState(builder.build(), Collections.emptyList()); } }