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 static com.android.settings.media.MediaOutputSlice.MEDIA_PACKAGE_NAME; 22 23 import android.content.BroadcastReceiver; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.IntentFilter; 27 import android.media.AudioManager; 28 import android.media.RoutingSessionInfo; 29 import android.net.Uri; 30 import android.os.UserHandle; 31 import android.os.UserManager; 32 import android.text.TextUtils; 33 34 import androidx.annotation.VisibleForTesting; 35 36 import com.android.settings.slices.SliceBackgroundWorker; 37 import com.android.settingslib.RestrictedLockUtilsInternal; 38 import com.android.settingslib.Utils; 39 import com.android.settingslib.media.LocalMediaManager; 40 import com.android.settingslib.media.MediaDevice; 41 import com.android.settingslib.utils.ThreadUtils; 42 43 import java.util.ArrayList; 44 import java.util.Collection; 45 import java.util.List; 46 import java.util.concurrent.CopyOnWriteArrayList; 47 48 /** 49 * SliceBackgroundWorker for get MediaDevice list and handle MediaDevice state change event. 50 */ 51 public class MediaDeviceUpdateWorker extends SliceBackgroundWorker 52 implements LocalMediaManager.DeviceCallback { 53 54 protected final Context mContext; 55 protected final Collection<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>(); 56 private final DevicesChangedBroadcastReceiver mReceiver; 57 private final String mPackageName; 58 59 private boolean mIsTouched; 60 private MediaDevice mTopDevice; 61 62 @VisibleForTesting 63 LocalMediaManager mLocalMediaManager; 64 MediaDeviceUpdateWorker(Context context, Uri uri)65 public MediaDeviceUpdateWorker(Context context, Uri uri) { 66 super(context, uri); 67 mContext = context; 68 mPackageName = uri.getQueryParameter(MEDIA_PACKAGE_NAME); 69 mReceiver = new DevicesChangedBroadcastReceiver(); 70 } 71 72 @Override onSlicePinned()73 protected void onSlicePinned() { 74 mMediaDevices.clear(); 75 mIsTouched = false; 76 if (mLocalMediaManager == null || !TextUtils.equals(mPackageName, 77 mLocalMediaManager.getPackageName())) { 78 mLocalMediaManager = new LocalMediaManager(mContext, mPackageName, null); 79 } 80 81 mLocalMediaManager.registerCallback(this); 82 final IntentFilter intentFilter = new IntentFilter(STREAM_DEVICES_CHANGED_ACTION); 83 mContext.registerReceiver(mReceiver, intentFilter); 84 mLocalMediaManager.startScan(); 85 } 86 87 @Override onSliceUnpinned()88 protected void onSliceUnpinned() { 89 mLocalMediaManager.unregisterCallback(this); 90 mContext.unregisterReceiver(mReceiver); 91 mLocalMediaManager.stopScan(); 92 } 93 94 @Override close()95 public void close() { 96 mLocalMediaManager = null; 97 } 98 99 @Override onDeviceListUpdate(List<MediaDevice> devices)100 public void onDeviceListUpdate(List<MediaDevice> devices) { 101 buildMediaDevices(devices); 102 notifySliceChange(); 103 } 104 buildMediaDevices(List<MediaDevice> devices)105 private void buildMediaDevices(List<MediaDevice> devices) { 106 mMediaDevices.clear(); 107 mMediaDevices.addAll(devices); 108 } 109 110 @Override onSelectedDeviceStateChanged(MediaDevice device, int state)111 public void onSelectedDeviceStateChanged(MediaDevice device, int state) { 112 notifySliceChange(); 113 } 114 115 @Override onDeviceAttributesChanged()116 public void onDeviceAttributesChanged() { 117 notifySliceChange(); 118 } 119 120 @Override onRequestFailed(int reason)121 public void onRequestFailed(int reason) { 122 notifySliceChange(); 123 } 124 getMediaDevices()125 public Collection<MediaDevice> getMediaDevices() { 126 return mMediaDevices; 127 } 128 connectDevice(MediaDevice device)129 public void connectDevice(MediaDevice device) { 130 ThreadUtils.postOnBackgroundThread(() -> { 131 if (mLocalMediaManager.connectDevice(device)) { 132 ThreadUtils.postOnMainThread(() -> { 133 notifySliceChange(); 134 }); 135 } 136 }); 137 } 138 getMediaDeviceById(String id)139 public MediaDevice getMediaDeviceById(String id) { 140 return mLocalMediaManager.getMediaDeviceById(new ArrayList<>(mMediaDevices), id); 141 } 142 getCurrentConnectedMediaDevice()143 public MediaDevice getCurrentConnectedMediaDevice() { 144 return mLocalMediaManager.getCurrentConnectedDevice(); 145 } 146 setIsTouched(boolean isTouched)147 void setIsTouched(boolean isTouched) { 148 mIsTouched = isTouched; 149 } 150 getIsTouched()151 boolean getIsTouched() { 152 return mIsTouched; 153 } 154 setTopDevice(MediaDevice device)155 void setTopDevice(MediaDevice device) { 156 mTopDevice = device; 157 } 158 getTopDevice()159 MediaDevice getTopDevice() { 160 return getMediaDeviceById(mTopDevice.getId()); 161 } 162 addDeviceToPlayMedia(MediaDevice device)163 boolean addDeviceToPlayMedia(MediaDevice device) { 164 return mLocalMediaManager.addDeviceToPlayMedia(device); 165 } 166 removeDeviceFromPlayMedia(MediaDevice device)167 boolean removeDeviceFromPlayMedia(MediaDevice device) { 168 return mLocalMediaManager.removeDeviceFromPlayMedia(device); 169 } 170 getSelectableMediaDevice()171 List<MediaDevice> getSelectableMediaDevice() { 172 return mLocalMediaManager.getSelectableMediaDevice(); 173 } 174 getSelectedMediaDevice()175 List<MediaDevice> getSelectedMediaDevice() { 176 return mLocalMediaManager.getSelectedMediaDevice(); 177 } 178 getDeselectableMediaDevice()179 List<MediaDevice> getDeselectableMediaDevice() { 180 return mLocalMediaManager.getDeselectableMediaDevice(); 181 } 182 isDeviceIncluded(Collection<MediaDevice> deviceCollection, MediaDevice targetDevice)183 boolean isDeviceIncluded(Collection<MediaDevice> deviceCollection, MediaDevice targetDevice) { 184 for (MediaDevice device : deviceCollection) { 185 if (TextUtils.equals(device.getId(), targetDevice.getId())) { 186 return true; 187 } 188 } 189 return false; 190 } 191 adjustSessionVolume(String sessionId, int volume)192 void adjustSessionVolume(String sessionId, int volume) { 193 mLocalMediaManager.adjustSessionVolume(sessionId, volume); 194 } 195 adjustSessionVolume(int volume)196 void adjustSessionVolume(int volume) { 197 mLocalMediaManager.adjustSessionVolume(volume); 198 } 199 getSessionVolumeMax()200 int getSessionVolumeMax() { 201 return mLocalMediaManager.getSessionVolumeMax(); 202 } 203 getSessionVolume()204 int getSessionVolume() { 205 return mLocalMediaManager.getSessionVolume(); 206 } 207 getSessionName()208 CharSequence getSessionName() { 209 return mLocalMediaManager.getSessionName(); 210 } 211 getActiveRemoteMediaDevice()212 List<RoutingSessionInfo> getActiveRemoteMediaDevice() { 213 final List<RoutingSessionInfo> sessionInfos = new ArrayList<>(); 214 for (RoutingSessionInfo info : mLocalMediaManager.getActiveMediaSession()) { 215 if (!info.isSystemSession()) { 216 sessionInfos.add(info); 217 } 218 } 219 return sessionInfos; 220 } 221 222 /** 223 * Request to set volume. 224 * 225 * @param device for the targeted device. 226 * @param volume for the new value. 227 * 228 */ adjustVolume(MediaDevice device, int volume)229 public void adjustVolume(MediaDevice device, int volume) { 230 ThreadUtils.postOnBackgroundThread(() -> { 231 device.requestSetVolume(volume); 232 }); 233 } 234 getPackageName()235 String getPackageName() { 236 return mPackageName; 237 } 238 hasAdjustVolumeUserRestriction()239 boolean hasAdjustVolumeUserRestriction() { 240 if (RestrictedLockUtilsInternal.checkIfRestrictionEnforced( 241 mContext, UserManager.DISALLOW_ADJUST_VOLUME, UserHandle.myUserId()) != null) { 242 return true; 243 } 244 final UserManager um = (UserManager) mContext.getSystemService(Context.USER_SERVICE); 245 return um.hasBaseUserRestriction(UserManager.DISALLOW_ADJUST_VOLUME, 246 UserHandle.of(UserHandle.myUserId())); 247 248 } 249 250 private class DevicesChangedBroadcastReceiver extends BroadcastReceiver { 251 @Override onReceive(Context context, Intent intent)252 public void onReceive(Context context, Intent intent) { 253 final String action = intent.getAction(); 254 if (TextUtils.equals(AudioManager.STREAM_DEVICES_CHANGED_ACTION, action) 255 && Utils.isAudioModeOngoingCall(mContext)) { 256 notifySliceChange(); 257 } 258 } 259 } 260 } 261