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