blob: 4def7db76bc5f0a7292233065830ed452bd9debd [file] [log] [blame]
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +09001/*
2 * Copyright 2020 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
17package com.android.server.media;
18
Kyunglyul Hyune77fbcb2020-05-29 15:18:11 +090019import static android.bluetooth.BluetoothAdapter.ACTIVE_DEVICE_AUDIO;
20
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090021import android.annotation.NonNull;
Hyundo Moon67c41fd2020-01-17 14:22:42 +090022import android.annotation.Nullable;
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090023import android.bluetooth.BluetoothA2dp;
24import android.bluetooth.BluetoothAdapter;
25import android.bluetooth.BluetoothDevice;
26import android.bluetooth.BluetoothHearingAid;
27import android.bluetooth.BluetoothProfile;
28import android.content.BroadcastReceiver;
29import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
Hyundo Moon717ef722020-02-06 18:44:13 +090032import android.media.AudioManager;
Kyunglyul Hyunf8f170c2020-05-13 21:31:26 +090033import android.media.AudioSystem;
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090034import android.media.MediaRoute2Info;
Sungsoo Lim2f19fd12020-05-07 09:53:09 +090035import android.text.TextUtils;
Kyunglyul Hyun581fc982020-01-21 16:30:28 +090036import android.util.Slog;
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090037import android.util.SparseBooleanArray;
Kyunglyul Hyunf8f170c2020-05-13 21:31:26 +090038import android.util.SparseIntArray;
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090039
40import com.android.internal.R;
41
42import java.util.ArrayList;
Kyunglyul Hyun581fc982020-01-21 16:30:28 +090043import java.util.Collections;
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090044import java.util.HashMap;
45import java.util.List;
46import java.util.Map;
47import java.util.Objects;
48
49class BluetoothRouteProvider {
50 private static final String TAG = "BTRouteProvider";
Kyunglyul Hyunf6d5b3c2020-06-11 22:23:11 +090051 private static final String HEARING_AID_ROUTE_ID_PREFIX = "HEARING_AID_";
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090052 private static BluetoothRouteProvider sInstance;
53
54 @SuppressWarnings("WeakerAccess") /* synthetic access */
Kyunglyul Hyuna8f2a062020-06-17 15:46:38 +090055 // Maps hardware address to BluetoothRouteInfo
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090056 final Map<String, BluetoothRouteInfo> mBluetoothRoutes = new HashMap<>();
57 @SuppressWarnings("WeakerAccess") /* synthetic access */
Sungsoo Limcfa7abc2020-02-12 09:32:05 +090058 BluetoothRouteInfo mSelectedRoute = null;
59 @SuppressWarnings("WeakerAccess") /* synthetic access */
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090060 BluetoothA2dp mA2dpProfile;
61 @SuppressWarnings("WeakerAccess") /* synthetic access */
62 BluetoothHearingAid mHearingAidProfile;
63
Kyunglyul Hyunf8f170c2020-05-13 21:31:26 +090064 // Route type -> volume map
65 private final SparseIntArray mVolumeMap = new SparseIntArray();
66
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090067 private final Context mContext;
68 private final BluetoothAdapter mBluetoothAdapter;
69 private final BluetoothRoutesUpdatedListener mListener;
Hyundo Moon717ef722020-02-06 18:44:13 +090070 private final AudioManager mAudioManager;
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090071 private final Map<String, BluetoothEventReceiver> mEventReceiverMap = new HashMap<>();
72 private final IntentFilter mIntentFilter = new IntentFilter();
73 private final BroadcastReceiver mBroadcastReceiver = new BluetoothBroadcastReceiver();
74 private final BluetoothProfileListener mProfileListener = new BluetoothProfileListener();
75
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090076 static synchronized BluetoothRouteProvider getInstance(@NonNull Context context,
77 @NonNull BluetoothRoutesUpdatedListener listener) {
78 Objects.requireNonNull(context);
79 Objects.requireNonNull(listener);
80
81 if (sInstance == null) {
82 BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter();
83 if (btAdapter == null) {
84 return null;
85 }
86 sInstance = new BluetoothRouteProvider(context, btAdapter, listener);
87 }
88 return sInstance;
89 }
90
91 private BluetoothRouteProvider(Context context, BluetoothAdapter btAdapter,
92 BluetoothRoutesUpdatedListener listener) {
93 mContext = context;
94 mBluetoothAdapter = btAdapter;
95 mListener = listener;
Hyundo Moon717ef722020-02-06 18:44:13 +090096 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090097 buildBluetoothRoutes();
Sungsoo Lim613a77a2020-03-16 15:44:51 +090098 }
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +090099
Sungsoo Lim613a77a2020-03-16 15:44:51 +0900100 public void start() {
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900101 mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.A2DP);
102 mBluetoothAdapter.getProfileProxy(mContext, mProfileListener, BluetoothProfile.HEARING_AID);
103
104 // Bluetooth on/off broadcasts
105 addEventReceiver(BluetoothAdapter.ACTION_STATE_CHANGED, new AdapterStateChangedReceiver());
106
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900107 DeviceStateChangedRecevier deviceStateChangedReceiver = new DeviceStateChangedRecevier();
108 addEventReceiver(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED, deviceStateChangedReceiver);
109 addEventReceiver(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED, deviceStateChangedReceiver);
110 addEventReceiver(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED,
111 deviceStateChangedReceiver);
112 addEventReceiver(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED,
113 deviceStateChangedReceiver);
114
115 mContext.registerReceiver(mBroadcastReceiver, mIntentFilter, null, null);
116 }
117
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900118 /**
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900119 * Transfers to a given bluetooth route.
120 * The dedicated BT device with the route would be activated.
121 *
122 * @param routeId the id of the Bluetooth device. {@code null} denotes to clear the use of
123 * BT routes.
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900124 */
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900125 public void transferTo(@Nullable String routeId) {
126 if (routeId == null) {
127 clearActiveDevices();
128 return;
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900129 }
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900130
Kyunglyul Hyuna8f2a062020-06-17 15:46:38 +0900131 BluetoothRouteInfo btRouteInfo = findBluetoothRouteWithRouteId(routeId);
132
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900133 if (btRouteInfo == null) {
Kyunglyul Hyuna8f2a062020-06-17 15:46:38 +0900134 Slog.w(TAG, "transferTo: Unknown route. ID=" + routeId);
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900135 return;
136 }
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900137
Kyunglyul Hyune77fbcb2020-05-29 15:18:11 +0900138 if (mBluetoothAdapter != null) {
139 mBluetoothAdapter.setActiveDevice(btRouteInfo.btDevice, ACTIVE_DEVICE_AUDIO);
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900140 }
141 }
142
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900143 /**
144 * Clears the active device for all known profiles.
145 */
146 private void clearActiveDevices() {
Kyunglyul Hyune77fbcb2020-05-29 15:18:11 +0900147 if (mBluetoothAdapter != null) {
148 mBluetoothAdapter.removeActiveDevice(ACTIVE_DEVICE_AUDIO);
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900149 }
150 }
151
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900152 private void addEventReceiver(String action, BluetoothEventReceiver eventReceiver) {
153 mEventReceiverMap.put(action, eventReceiver);
154 mIntentFilter.addAction(action);
155 }
156
157 private void buildBluetoothRoutes() {
158 mBluetoothRoutes.clear();
159 for (BluetoothDevice device : mBluetoothAdapter.getBondedDevices()) {
160 if (device.isConnected()) {
161 BluetoothRouteInfo newBtRoute = createBluetoothRoute(device);
Kyunglyul Hyun6eefb5f2020-05-15 16:21:20 +0900162 if (newBtRoute.connectedProfiles.size() > 0) {
163 mBluetoothRoutes.put(device.getAddress(), newBtRoute);
164 }
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900165 }
166 }
167 }
168
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900169 @Nullable
170 MediaRoute2Info getSelectedRoute() {
171 return (mSelectedRoute == null) ? null : mSelectedRoute.route;
172 }
173
174 @NonNull
175 List<MediaRoute2Info> getTransferableRoutes() {
176 List<MediaRoute2Info> routes = getAllBluetoothRoutes();
177 if (mSelectedRoute != null) {
178 routes.remove(mSelectedRoute.route);
179 }
180 return routes;
181 }
182
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900183 @NonNull
184 List<MediaRoute2Info> getAllBluetoothRoutes() {
Kyunglyul Hyunf6d5b3c2020-06-11 22:23:11 +0900185 List<MediaRoute2Info> routes = new ArrayList<>();
186 List<String> routeIds = new ArrayList<>();
187
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900188 for (BluetoothRouteInfo btRoute : mBluetoothRoutes.values()) {
Kyunglyul Hyunf6d5b3c2020-06-11 22:23:11 +0900189 // A pair of hearing aid devices or the same hardware address
190 if (routeIds.contains(btRoute.route.getId())) {
191 continue;
192 }
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900193 routes.add(btRoute.route);
Kyunglyul Hyunf6d5b3c2020-06-11 22:23:11 +0900194 routeIds.add(btRoute.route.getId());
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900195 }
196 return routes;
197 }
198
Kyunglyul Hyuna8f2a062020-06-17 15:46:38 +0900199 BluetoothRouteInfo findBluetoothRouteWithRouteId(String routeId) {
200 if (routeId == null) {
201 return null;
202 }
203 for (BluetoothRouteInfo btRouteInfo : mBluetoothRoutes.values()) {
204 if (TextUtils.equals(btRouteInfo.route.getId(), routeId)) {
205 return btRouteInfo;
206 }
207 }
208 return null;
209 }
210
Kyunglyul Hyunf8f170c2020-05-13 21:31:26 +0900211 /**
212 * Updates the volume for {@link AudioManager#getDevicesForStream(int) devices}.
213 *
214 * @return true if devices can be handled by the provider.
215 */
216 public boolean updateVolumeForDevices(int devices, int volume) {
217 int routeType;
218 if ((devices & (AudioSystem.DEVICE_OUT_HEARING_AID)) != 0) {
219 routeType = MediaRoute2Info.TYPE_HEARING_AID;
220 } else if ((devices & (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP
221 | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES
222 | AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
223 routeType = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
224 } else {
225 return false;
226 }
227 mVolumeMap.put(routeType, volume);
228 if (mSelectedRoute == null || mSelectedRoute.route.getType() != routeType) {
229 return true;
230 }
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900231 mSelectedRoute.route = new MediaRoute2Info.Builder(mSelectedRoute.route)
232 .setVolume(volume)
233 .build();
Kyunglyul Hyunf8f170c2020-05-13 21:31:26 +0900234 notifyBluetoothRoutesUpdated();
Sungsoo Limea1eaf72020-02-12 11:00:06 +0900235 return true;
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900236 }
237
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900238 private void notifyBluetoothRoutesUpdated() {
239 if (mListener != null) {
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900240 mListener.onBluetoothRoutesUpdated(getAllBluetoothRoutes());
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900241 }
242 }
243
244 private BluetoothRouteInfo createBluetoothRoute(BluetoothDevice device) {
245 BluetoothRouteInfo newBtRoute = new BluetoothRouteInfo();
246 newBtRoute.btDevice = device;
Kyunglyul Hyunf6d5b3c2020-06-11 22:23:11 +0900247
248 String routeId = device.getAddress();
Sungsoo Lim2f19fd12020-05-07 09:53:09 +0900249 String deviceName = device.getName();
250 if (TextUtils.isEmpty(deviceName)) {
251 deviceName = mContext.getResources().getText(R.string.unknownName).toString();
252 }
Kyunglyul Hyun6eefb5f2020-05-15 16:21:20 +0900253 int type = MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
254 newBtRoute.connectedProfiles = new SparseBooleanArray();
255 if (mA2dpProfile != null && mA2dpProfile.getConnectedDevices().contains(device)) {
256 newBtRoute.connectedProfiles.put(BluetoothProfile.A2DP, true);
257 }
258 if (mHearingAidProfile != null
259 && mHearingAidProfile.getConnectedDevices().contains(device)) {
260 newBtRoute.connectedProfiles.put(BluetoothProfile.HEARING_AID, true);
Kyunglyul Hyunf6d5b3c2020-06-11 22:23:11 +0900261 // Intentionally assign the same ID for a pair of devices to publish only one of them.
262 routeId = HEARING_AID_ROUTE_ID_PREFIX + mHearingAidProfile.getHiSyncId(device);
Kyunglyul Hyun6eefb5f2020-05-15 16:21:20 +0900263 type = MediaRoute2Info.TYPE_HEARING_AID;
264 }
265
Kyunglyul Hyunf6d5b3c2020-06-11 22:23:11 +0900266 // Current volume will be set when connected.
267 newBtRoute.route = new MediaRoute2Info.Builder(routeId, deviceName)
Hyundo Moon42bef142020-01-14 14:16:30 +0900268 .addFeature(MediaRoute2Info.FEATURE_LIVE_AUDIO)
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900269 .setConnectionState(MediaRoute2Info.CONNECTION_STATE_DISCONNECTED)
270 .setDescription(mContext.getResources().getText(
271 R.string.bluetooth_a2dp_audio_route_name).toString())
Kyunglyul Hyun6eefb5f2020-05-15 16:21:20 +0900272 .setType(type)
Hyundo Moon717ef722020-02-06 18:44:13 +0900273 .setVolumeHandling(MediaRoute2Info.PLAYBACK_VOLUME_VARIABLE)
Kyunglyul Hyunf8f170c2020-05-13 21:31:26 +0900274 .setVolumeMax(mAudioManager.getStreamMaxVolume(AudioManager.STREAM_MUSIC))
Kyunglyul Hyun1ea19b12020-06-10 20:04:15 +0900275 .setAddress(device.getAddress())
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900276 .build();
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900277 return newBtRoute;
278 }
279
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900280 private void setRouteConnectionState(@NonNull BluetoothRouteInfo btRoute,
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900281 @MediaRoute2Info.ConnectionState int state) {
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900282 if (btRoute == null) {
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900283 Slog.w(TAG, "setRouteConnectionState: route shouldn't be null");
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900284 return;
285 }
Hyundo Moon717ef722020-02-06 18:44:13 +0900286 if (btRoute.route.getConnectionState() == state) {
287 return;
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900288 }
Hyundo Moon717ef722020-02-06 18:44:13 +0900289
Hyundo Moon717ef722020-02-06 18:44:13 +0900290 MediaRoute2Info.Builder builder = new MediaRoute2Info.Builder(btRoute.route)
291 .setConnectionState(state);
Kyunglyul Hyunf8f170c2020-05-13 21:31:26 +0900292 builder.setType(btRoute.getRouteType());
Hyundo Moon717ef722020-02-06 18:44:13 +0900293
294 if (state == MediaRoute2Info.CONNECTION_STATE_CONNECTED) {
Kyunglyul Hyunf8f170c2020-05-13 21:31:26 +0900295 builder.setVolume(mVolumeMap.get(btRoute.getRouteType(), 0));
Hyundo Moon717ef722020-02-06 18:44:13 +0900296 }
297 btRoute.route = builder.build();
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900298 }
299
300 interface BluetoothRoutesUpdatedListener {
301 void onBluetoothRoutesUpdated(@NonNull List<MediaRoute2Info> routes);
302 }
303
304 private class BluetoothRouteInfo {
305 public BluetoothDevice btDevice;
306 public MediaRoute2Info route;
307 public SparseBooleanArray connectedProfiles;
Kyunglyul Hyunf8f170c2020-05-13 21:31:26 +0900308
309 @MediaRoute2Info.Type
310 int getRouteType() {
311 // Let hearing aid profile have a priority.
312 if (connectedProfiles.get(BluetoothProfile.HEARING_AID, false)) {
313 return MediaRoute2Info.TYPE_HEARING_AID;
314 }
315 return MediaRoute2Info.TYPE_BLUETOOTH_A2DP;
316 }
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900317 }
318
319 // These callbacks run on the main thread.
320 private final class BluetoothProfileListener implements BluetoothProfile.ServiceListener {
321 public void onServiceConnected(int profile, BluetoothProfile proxy) {
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900322 List<BluetoothDevice> activeDevices;
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900323 switch (profile) {
324 case BluetoothProfile.A2DP:
325 mA2dpProfile = (BluetoothA2dp) proxy;
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900326 // It may contain null.
327 activeDevices = Collections.singletonList(mA2dpProfile.getActiveDevice());
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900328 break;
329 case BluetoothProfile.HEARING_AID:
330 mHearingAidProfile = (BluetoothHearingAid) proxy;
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900331 activeDevices = mHearingAidProfile.getActiveDevices();
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900332 break;
333 default:
334 return;
335 }
Kyunglyul Hyune77fbcb2020-05-29 15:18:11 +0900336 //TODO(b/157708273): Handle two active devices in the binaural case.
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900337 for (BluetoothDevice device : proxy.getConnectedDevices()) {
338 BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
339 if (btRoute == null) {
340 btRoute = createBluetoothRoute(device);
341 mBluetoothRoutes.put(device.getAddress(), btRoute);
342 }
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900343 if (activeDevices.contains(device)) {
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900344 mSelectedRoute = btRoute;
345 setRouteConnectionState(mSelectedRoute,
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900346 MediaRoute2Info.CONNECTION_STATE_CONNECTED);
347 }
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900348 }
Kyunglyul Hyun581fc982020-01-21 16:30:28 +0900349 notifyBluetoothRoutesUpdated();
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900350 }
351
352 public void onServiceDisconnected(int profile) {
353 switch (profile) {
354 case BluetoothProfile.A2DP:
355 mA2dpProfile = null;
356 break;
357 case BluetoothProfile.HEARING_AID:
358 mHearingAidProfile = null;
359 break;
360 default:
361 return;
362 }
363 }
364 }
365 private class BluetoothBroadcastReceiver extends BroadcastReceiver {
366 @Override
367 public void onReceive(Context context, Intent intent) {
368 String action = intent.getAction();
369 BluetoothDevice device = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
370
371 BluetoothEventReceiver receiver = mEventReceiverMap.get(action);
372 if (receiver != null) {
373 receiver.onReceive(context, intent, device);
374 }
375 }
376 }
377
378 private interface BluetoothEventReceiver {
379 void onReceive(Context context, Intent intent, BluetoothDevice device);
380 }
381
382 private class AdapterStateChangedReceiver implements BluetoothEventReceiver {
383 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
384 int state = intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, -1);
385 if (state == BluetoothAdapter.STATE_OFF
386 || state == BluetoothAdapter.STATE_TURNING_OFF) {
387 mBluetoothRoutes.clear();
388 notifyBluetoothRoutesUpdated();
389 } else if (state == BluetoothAdapter.STATE_ON) {
390 buildBluetoothRoutes();
391 if (!mBluetoothRoutes.isEmpty()) {
392 notifyBluetoothRoutesUpdated();
393 }
394 }
395 }
396 }
397
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900398 private class DeviceStateChangedRecevier implements BluetoothEventReceiver {
399 @Override
400 public void onReceive(Context context, Intent intent, BluetoothDevice device) {
401 switch (intent.getAction()) {
402 case BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED:
Kyunglyul Hyune77fbcb2020-05-29 15:18:11 +0900403 case BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED:
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900404 if (mSelectedRoute == null
405 || !mSelectedRoute.btDevice.equals(device)) {
406 if (mSelectedRoute != null) {
407 setRouteConnectionState(mSelectedRoute,
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900408 MediaRoute2Info.CONNECTION_STATE_DISCONNECTED);
409 }
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900410 mSelectedRoute = (device == null) ? null
411 : mBluetoothRoutes.get(device.getAddress());
412 if (mSelectedRoute != null) {
413 setRouteConnectionState(mSelectedRoute,
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900414 MediaRoute2Info.CONNECTION_STATE_CONNECTED);
415 }
Hyundo Moon67c41fd2020-01-17 14:22:42 +0900416 notifyBluetoothRoutesUpdated();
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900417 }
418 break;
419 case BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED:
420 handleConnectionStateChanged(BluetoothProfile.A2DP, intent, device);
421 break;
Kyunglyul Hyune77fbcb2020-05-29 15:18:11 +0900422 case BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED:
423 handleConnectionStateChanged(BluetoothProfile.HEARING_AID, intent, device);
424 break;
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900425 }
426 }
427
428 private void handleConnectionStateChanged(int profile, Intent intent,
429 BluetoothDevice device) {
430 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
431 BluetoothRouteInfo btRoute = mBluetoothRoutes.get(device.getAddress());
432 if (state == BluetoothProfile.STATE_CONNECTED) {
433 if (btRoute == null) {
434 btRoute = createBluetoothRoute(device);
Kyunglyul Hyun6eefb5f2020-05-15 16:21:20 +0900435 if (btRoute.connectedProfiles.size() > 0) {
436 mBluetoothRoutes.put(device.getAddress(), btRoute);
437 notifyBluetoothRoutesUpdated();
438 }
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900439 } else {
440 btRoute.connectedProfiles.put(profile, true);
441 }
442 } else if (state == BluetoothProfile.STATE_DISCONNECTING
443 || state == BluetoothProfile.STATE_DISCONNECTED) {
Kyunglyul Hyundb74f3d2020-01-15 11:12:14 +0900444 if (btRoute != null) {
445 btRoute.connectedProfiles.delete(profile);
446 if (btRoute.connectedProfiles.size() == 0) {
447 mBluetoothRoutes.remove(device.getAddress());
Sungsoo Limcfa7abc2020-02-12 09:32:05 +0900448 if (mSelectedRoute != null && mSelectedRoute.btDevice.equals(device)) {
449 mSelectedRoute = null;
Kyunglyul Hyundb74f3d2020-01-15 11:12:14 +0900450 }
451 notifyBluetoothRoutesUpdated();
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900452 }
Sungsoo Limbb3ee6e2019-12-23 17:47:24 +0900453 }
454 }
455 }
456 }
457}