• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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.settings.media;
18 
19 import static android.media.AudioManager.STREAM_DEVICES_CHANGED_ACTION;
20 
21 import android.content.BroadcastReceiver;
22 import android.content.Context;
23 import android.content.Intent;
24 import android.content.IntentFilter;
25 import android.media.AudioManager;
26 import android.media.session.MediaController;
27 import android.media.session.MediaSessionManager;
28 import android.net.Uri;
29 import android.text.TextUtils;
30 import android.util.Log;
31 
32 import androidx.annotation.Nullable;
33 
34 import com.android.settings.bluetooth.Utils;
35 import com.android.settings.slices.SliceBackgroundWorker;
36 import com.android.settingslib.bluetooth.BluetoothCallback;
37 import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
38 import com.android.settingslib.bluetooth.LocalBluetoothManager;
39 import com.android.settingslib.media.LocalMediaManager;
40 import com.android.settingslib.media.MediaDevice;
41 import com.android.settingslib.utils.ThreadUtils;
42 
43 import com.google.common.annotations.VisibleForTesting;
44 
45 import java.util.Collection;
46 import java.util.List;
47 import java.util.concurrent.CopyOnWriteArrayList;
48 
49 /**
50  * Listener for background change from {@code BluetoothCallback} to update media output indicator.
51  */
52 public class MediaOutputIndicatorWorker extends SliceBackgroundWorker implements BluetoothCallback,
53         LocalMediaManager.DeviceCallback {
54 
55     private static final String TAG = "MediaOutputIndWorker";
56     private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
57 
58     private final DevicesChangedBroadcastReceiver mReceiver;
59     private final Context mContext;
60     private final Collection<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
61 
62     private LocalBluetoothManager mLocalBluetoothManager;
63     private String mPackageName;
64 
65     @VisibleForTesting
66     LocalMediaManager mLocalMediaManager;
67 
MediaOutputIndicatorWorker(Context context, Uri uri)68     public MediaOutputIndicatorWorker(Context context, Uri uri) {
69         super(context, uri);
70         mReceiver = new DevicesChangedBroadcastReceiver();
71         mContext = context;
72     }
73 
74     @Override
onSlicePinned()75     protected void onSlicePinned() {
76         mMediaDevices.clear();
77         mLocalBluetoothManager = Utils.getLocalBtManager(getContext());
78         if (mLocalBluetoothManager == null) {
79             Log.e(TAG, "Bluetooth is not supported on this device");
80             return;
81         }
82         final IntentFilter intentFilter = new IntentFilter(STREAM_DEVICES_CHANGED_ACTION);
83         mContext.registerReceiver(mReceiver, intentFilter);
84         mLocalBluetoothManager.getEventManager().registerCallback(this);
85 
86         ThreadUtils.postOnBackgroundThread(() -> {
87             final MediaController controller = getActiveLocalMediaController();
88             if (controller == null) {
89                 mPackageName = null;
90             } else {
91                 mPackageName = controller.getPackageName();
92             }
93             if (mLocalMediaManager == null || !TextUtils.equals(mPackageName,
94                     mLocalMediaManager.getPackageName())) {
95                 mLocalMediaManager = new LocalMediaManager(mContext, mPackageName);
96             }
97             mLocalMediaManager.registerCallback(this);
98             mLocalMediaManager.startScan();
99         });
100     }
101 
102     @Override
onSliceUnpinned()103     protected void onSliceUnpinned() {
104         if (mLocalMediaManager != null) {
105             mLocalMediaManager.unregisterCallback(this);
106             mLocalMediaManager.stopScan();
107         }
108 
109         if (mLocalBluetoothManager == null) {
110             Log.e(TAG, "Bluetooth is not supported on this device");
111             return;
112         }
113         mLocalBluetoothManager.getEventManager().unregisterCallback(this);
114         mContext.unregisterReceiver(mReceiver);
115     }
116 
117     @Override
close()118     public void close() {
119         mLocalBluetoothManager = null;
120         mLocalMediaManager = null;
121     }
122 
123     @Override
onAudioModeChanged()124     public void onAudioModeChanged() {
125         notifySliceChange();
126     }
127 
128     @Nullable
getActiveLocalMediaController()129     public MediaController getActiveLocalMediaController() {
130         return MediaOutputUtils.getActiveLocalMediaController(mContext.getSystemService(
131                 MediaSessionManager.class));
132     }
133 
134     @Override
onDeviceListUpdate(List<MediaDevice> devices)135     public void onDeviceListUpdate(List<MediaDevice> devices) {
136         buildMediaDevices(devices);
137         notifySliceChange();
138     }
139 
buildMediaDevices(List<MediaDevice> devices)140     private void buildMediaDevices(List<MediaDevice> devices) {
141         mMediaDevices.clear();
142         mMediaDevices.addAll(devices);
143     }
144 
145     @Override
onSelectedDeviceStateChanged(MediaDevice device, int state)146     public void onSelectedDeviceStateChanged(MediaDevice device, int state) {
147         notifySliceChange();
148     }
149 
150     @Override
onDeviceAttributesChanged()151     public void onDeviceAttributesChanged() {
152         notifySliceChange();
153     }
154 
getMediaDevices()155     Collection<MediaDevice> getMediaDevices() {
156         return mMediaDevices;
157     }
158 
159     @Nullable
getCurrentConnectedMediaDevice()160     public MediaDevice getCurrentConnectedMediaDevice() {
161         if (mLocalMediaManager == null) {
162             return null;
163         }
164         return mLocalMediaManager.getCurrentConnectedDevice();
165     }
166 
getPackageName()167     public String getPackageName() {
168         return mPackageName;
169     }
170 
171     /** Check if this device supports LE Audio Broadcast feature */
isBroadcastSupported()172     public boolean isBroadcastSupported() {
173         if (mLocalBluetoothManager == null) {
174             Log.e(TAG, "isBroadcastSupported: Bluetooth is not supported on this device");
175             return false;
176         }
177         LocalBluetoothLeBroadcast broadcast =
178                 mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
179         return broadcast != null ? true : false;
180     }
181 
isDeviceBroadcasting()182     public boolean isDeviceBroadcasting() {
183         if (mLocalBluetoothManager == null) {
184             Log.e(TAG, "isDeviceBroadcasting: Bluetooth is not supported on this device");
185             return false;
186         }
187         LocalBluetoothLeBroadcast broadcast =
188                 mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
189         if (broadcast == null) {
190             return false;
191         }
192         return broadcast.isEnabled(null);
193     }
194 
195     private class DevicesChangedBroadcastReceiver extends BroadcastReceiver {
196         @Override
onReceive(Context context, Intent intent)197         public void onReceive(Context context, Intent intent) {
198             final String action = intent.getAction();
199             if (TextUtils.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION, action)) {
200                 notifySliceChange();
201             }
202         }
203     }
204 }
205