• 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                         null /* notification */);
97             }
98             mLocalMediaManager.registerCallback(this);
99             mLocalMediaManager.startScan();
100         });
101     }
102 
103     @Override
onSliceUnpinned()104     protected void onSliceUnpinned() {
105         if (mLocalMediaManager != null) {
106             mLocalMediaManager.unregisterCallback(this);
107             mLocalMediaManager.stopScan();
108         }
109 
110         if (mLocalBluetoothManager == null) {
111             Log.e(TAG, "Bluetooth is not supported on this device");
112             return;
113         }
114         mLocalBluetoothManager.getEventManager().unregisterCallback(this);
115         mContext.unregisterReceiver(mReceiver);
116     }
117 
118     @Override
close()119     public void close() {
120         mLocalBluetoothManager = null;
121         mLocalMediaManager = null;
122     }
123 
124     @Override
onAudioModeChanged()125     public void onAudioModeChanged() {
126         notifySliceChange();
127     }
128 
129     @Nullable
getActiveLocalMediaController()130     public MediaController getActiveLocalMediaController() {
131         return MediaOutputUtils.getActiveLocalMediaController(mContext.getSystemService(
132                 MediaSessionManager.class));
133     }
134 
135     @Override
onDeviceListUpdate(List<MediaDevice> devices)136     public void onDeviceListUpdate(List<MediaDevice> devices) {
137         buildMediaDevices(devices);
138         notifySliceChange();
139     }
140 
buildMediaDevices(List<MediaDevice> devices)141     private void buildMediaDevices(List<MediaDevice> devices) {
142         mMediaDevices.clear();
143         mMediaDevices.addAll(devices);
144     }
145 
146     @Override
onSelectedDeviceStateChanged(MediaDevice device, int state)147     public void onSelectedDeviceStateChanged(MediaDevice device, int state) {
148         notifySliceChange();
149     }
150 
151     @Override
onDeviceAttributesChanged()152     public void onDeviceAttributesChanged() {
153         notifySliceChange();
154     }
155 
getMediaDevices()156     Collection<MediaDevice> getMediaDevices() {
157         return mMediaDevices;
158     }
159 
getCurrentConnectedMediaDevice()160     public MediaDevice getCurrentConnectedMediaDevice() {
161         return mLocalMediaManager.getCurrentConnectedDevice();
162     }
163 
getPackageName()164     public String getPackageName() {
165         return mPackageName;
166     }
167 
168     /** Check if this device supports LE Audio Broadcast feature */
isBroadcastSupported()169     public boolean isBroadcastSupported() {
170         if (mLocalBluetoothManager == null) {
171             Log.e(TAG, "isBroadcastSupported: Bluetooth is not supported on this device");
172             return false;
173         }
174         LocalBluetoothLeBroadcast broadcast =
175                 mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
176         return broadcast != null ? true : false;
177     }
178 
isDeviceBroadcasting()179     public boolean isDeviceBroadcasting() {
180         if (mLocalBluetoothManager == null) {
181             Log.e(TAG, "isDeviceBroadcasting: Bluetooth is not supported on this device");
182             return false;
183         }
184         LocalBluetoothLeBroadcast broadcast =
185                 mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
186         if (broadcast == null) {
187             return false;
188         }
189         return broadcast.isEnabled(null);
190     }
191 
192     private class DevicesChangedBroadcastReceiver extends BroadcastReceiver {
193         @Override
onReceive(Context context, Intent intent)194         public void onReceive(Context context, Intent intent) {
195             final String action = intent.getAction();
196             if (TextUtils.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION, action)) {
197                 notifySliceChange();
198             }
199         }
200     }
201 }
202