[Output Switcher] Sets TemporaryAllowList when transfer from local to
remote (or oppsite way) app will create new UMO when transfer between local and remote, and it will produce Exception if app is background, so set it to TemporaryAllowList before start transferring. Bug: 235339353 Test: atest atest MediaOutputAdapterTest MediaOutputControllerTest MediaOutputBaseDialogTest MediaOutputDialogTest MediaOutputGroupAdapterTest MediaOutputGroupDialogTest Change-Id: I32a823919b48fd784d354858c7e3b1070a723e28 Merged-In: I32a823919b48fd784d354858c7e3b1070a723e28
This commit is contained in:
@@ -385,6 +385,12 @@ public class PowerExemptionManager {
|
||||
*/
|
||||
public static final int REASON_ACTIVE_DEVICE_ADMIN = 324;
|
||||
|
||||
/**
|
||||
* Media notification re-generate during transferring.
|
||||
* @hide
|
||||
*/
|
||||
public static final int REASON_MEDIA_NOTIFICATION_TRANSFER = 325;
|
||||
|
||||
/** @hide The app requests out-out. */
|
||||
public static final int REASON_OPT_OUT_REQUESTED = 1000;
|
||||
|
||||
@@ -465,6 +471,7 @@ public class PowerExemptionManager {
|
||||
REASON_DPO_PROTECTED_APP,
|
||||
REASON_DISALLOW_APPS_CONTROL,
|
||||
REASON_ACTIVE_DEVICE_ADMIN,
|
||||
REASON_MEDIA_NOTIFICATION_TRANSFER,
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface ReasonCode {}
|
||||
@@ -830,6 +837,8 @@ public class PowerExemptionManager {
|
||||
return "ACTIVE_DEVICE_ADMIN";
|
||||
case REASON_OPT_OUT_REQUESTED:
|
||||
return "REASON_OPT_OUT_REQUESTED";
|
||||
case REASON_MEDIA_NOTIFICATION_TRANSFER:
|
||||
return "REASON_MEDIA_NOTIFICATION_TRANSFER";
|
||||
default:
|
||||
return "(unknown:" + reasonCode + ")";
|
||||
}
|
||||
|
||||
@@ -60,6 +60,7 @@ import android.net.ConnectivityManager;
|
||||
import android.net.NetworkScoreManager;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.os.BatteryStats;
|
||||
import android.os.PowerExemptionManager;
|
||||
import android.os.PowerManager;
|
||||
import android.os.ServiceManager;
|
||||
import android.os.UserManager;
|
||||
@@ -375,6 +376,13 @@ public class FrameworkServicesModule {
|
||||
return context.getSystemService(PowerManager.class);
|
||||
}
|
||||
|
||||
/** */
|
||||
@Provides
|
||||
@Singleton
|
||||
static PowerExemptionManager providePowerExemptionManager(Context context) {
|
||||
return context.getSystemService(PowerExemptionManager.class);
|
||||
}
|
||||
|
||||
/** */
|
||||
@Provides
|
||||
@Main
|
||||
|
||||
@@ -285,6 +285,7 @@ public class MediaOutputAdapter extends MediaOutputBaseAdapter {
|
||||
Log.d(TAG, "This device is already connected! : " + device.getName());
|
||||
return;
|
||||
}
|
||||
mController.setTemporaryAllowListExceptionIfNeeded(device);
|
||||
mCurrentActivePosition = -1;
|
||||
mController.connectDevice(device);
|
||||
device.setState(MediaDeviceState.STATE_CONNECTING);
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.media.dialog
|
||||
import android.content.Context
|
||||
import android.media.AudioManager
|
||||
import android.media.session.MediaSessionManager
|
||||
import android.os.PowerExemptionManager
|
||||
import android.view.View
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager
|
||||
@@ -43,7 +44,8 @@ class MediaOutputBroadcastDialogFactory @Inject constructor(
|
||||
private val uiEventLogger: UiEventLogger,
|
||||
private val dialogLaunchAnimator: DialogLaunchAnimator,
|
||||
private val nearbyMediaDevicesManagerOptional: Optional<NearbyMediaDevicesManager>,
|
||||
private val audioManager: AudioManager
|
||||
private val audioManager: AudioManager,
|
||||
private val powerExemptionManager: PowerExemptionManager
|
||||
) {
|
||||
var mediaOutputBroadcastDialog: MediaOutputBroadcastDialog? = null
|
||||
|
||||
@@ -54,7 +56,8 @@ class MediaOutputBroadcastDialogFactory @Inject constructor(
|
||||
|
||||
val controller = MediaOutputController(context, packageName,
|
||||
mediaSessionManager, lbm, starter, notifCollection,
|
||||
dialogLaunchAnimator, nearbyMediaDevicesManagerOptional, audioManager)
|
||||
dialogLaunchAnimator, nearbyMediaDevicesManagerOptional, audioManager,
|
||||
powerExemptionManager)
|
||||
val dialog =
|
||||
MediaOutputBroadcastDialog(context, aboveStatusBar, broadcastSender, controller)
|
||||
mediaOutputBroadcastDialog = dialog
|
||||
|
||||
@@ -44,6 +44,7 @@ import android.media.session.MediaController;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.os.IBinder;
|
||||
import android.os.PowerExemptionManager;
|
||||
import android.os.RemoteException;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
@@ -101,6 +102,9 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
|
||||
private static final String PAGE_CONNECTED_DEVICES_KEY =
|
||||
"top_level_connected_devices";
|
||||
private static final long ALLOWLIST_DURATION_MS = 20000;
|
||||
private static final String ALLOWLIST_REASON = "mediaoutput:remote_transfer";
|
||||
|
||||
private final String mPackageName;
|
||||
private final Context mContext;
|
||||
private final MediaSessionManager mMediaSessionManager;
|
||||
@@ -114,6 +118,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
final List<MediaDevice> mMediaDevices = new CopyOnWriteArrayList<>();
|
||||
final List<MediaDevice> mCachedMediaDevices = new CopyOnWriteArrayList<>();
|
||||
private final AudioManager mAudioManager;
|
||||
private final PowerExemptionManager mPowerExemptionManager;
|
||||
private final NearbyMediaDevicesManager mNearbyMediaDevicesManager;
|
||||
private final Map<String, Integer> mNearbyDeviceInfoMap = new ConcurrentHashMap<>();
|
||||
|
||||
@@ -147,7 +152,8 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
CommonNotifCollection notifCollection,
|
||||
DialogLaunchAnimator dialogLaunchAnimator,
|
||||
Optional<NearbyMediaDevicesManager> nearbyMediaDevicesManagerOptional,
|
||||
AudioManager audioManager) {
|
||||
AudioManager audioManager,
|
||||
PowerExemptionManager powerExemptionManager) {
|
||||
mContext = context;
|
||||
mPackageName = packageName;
|
||||
mMediaSessionManager = mediaSessionManager;
|
||||
@@ -155,6 +161,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
mActivityStarter = starter;
|
||||
mNotifCollection = notifCollection;
|
||||
mAudioManager = audioManager;
|
||||
mPowerExemptionManager = powerExemptionManager;
|
||||
InfoMediaManager imm = new InfoMediaManager(mContext, packageName, null, lbm);
|
||||
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
|
||||
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
|
||||
@@ -776,7 +783,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
MediaOutputController controller = new MediaOutputController(mContext, mPackageName,
|
||||
mMediaSessionManager, mLocalBluetoothManager, mActivityStarter,
|
||||
mNotifCollection, mDialogLaunchAnimator, Optional.of(mNearbyMediaDevicesManager),
|
||||
mAudioManager);
|
||||
mAudioManager, mPowerExemptionManager);
|
||||
MediaOutputBroadcastDialog dialog = new MediaOutputBroadcastDialog(mContext, true,
|
||||
broadcastSender, controller);
|
||||
mDialogLaunchAnimator.showFromView(dialog, mediaOutputDialog);
|
||||
@@ -822,6 +829,17 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
|
||||
broadcast.setBroadcastCode(broadcastCode.getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
void setTemporaryAllowListExceptionIfNeeded(MediaDevice targetDevice) {
|
||||
if (mPowerExemptionManager == null || mPackageName == null) {
|
||||
Log.w(TAG, "powerExemptionManager or package name is null");
|
||||
return;
|
||||
}
|
||||
mPowerExemptionManager.addToTemporaryAllowList(mPackageName,
|
||||
PowerExemptionManager.REASON_MEDIA_NOTIFICATION_TRANSFER,
|
||||
ALLOWLIST_REASON,
|
||||
ALLOWLIST_DURATION_MS);
|
||||
}
|
||||
|
||||
String getBroadcastMetadata() {
|
||||
LocalBluetoothLeBroadcast broadcast =
|
||||
mLocalBluetoothManager.getProfileManager().getLeAudioBroadcastProfile();
|
||||
|
||||
@@ -19,6 +19,7 @@ package com.android.systemui.media.dialog
|
||||
import android.content.Context
|
||||
import android.media.AudioManager
|
||||
import android.media.session.MediaSessionManager
|
||||
import android.os.PowerExemptionManager
|
||||
import android.view.View
|
||||
import com.android.internal.logging.UiEventLogger
|
||||
import com.android.settingslib.bluetooth.LocalBluetoothManager
|
||||
@@ -43,7 +44,8 @@ class MediaOutputDialogFactory @Inject constructor(
|
||||
private val uiEventLogger: UiEventLogger,
|
||||
private val dialogLaunchAnimator: DialogLaunchAnimator,
|
||||
private val nearbyMediaDevicesManagerOptional: Optional<NearbyMediaDevicesManager>,
|
||||
private val audioManager: AudioManager
|
||||
private val audioManager: AudioManager,
|
||||
private val powerExemptionManager: PowerExemptionManager
|
||||
) {
|
||||
companion object {
|
||||
var mediaOutputDialog: MediaOutputDialog? = null
|
||||
@@ -54,9 +56,11 @@ class MediaOutputDialogFactory @Inject constructor(
|
||||
// Dismiss the previous dialog, if any.
|
||||
mediaOutputDialog?.dismiss()
|
||||
|
||||
val controller = MediaOutputController(context, packageName,
|
||||
mediaSessionManager, lbm, starter, notifCollection,
|
||||
dialogLaunchAnimator, nearbyMediaDevicesManagerOptional, audioManager)
|
||||
val controller = MediaOutputController(
|
||||
context, packageName,
|
||||
mediaSessionManager, lbm, starter, notifCollection,
|
||||
dialogLaunchAnimator, nearbyMediaDevicesManagerOptional, audioManager,
|
||||
powerExemptionManager)
|
||||
val dialog =
|
||||
MediaOutputDialog(context, aboveStatusBar, broadcastSender, controller, uiEventLogger)
|
||||
mediaOutputDialog = dialog
|
||||
|
||||
@@ -32,6 +32,7 @@ import android.media.session.MediaController;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerExemptionManager;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.View;
|
||||
@@ -86,6 +87,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
|
||||
NearbyMediaDevicesManager.class);
|
||||
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
|
||||
private final AudioManager mAudioManager = mock(AudioManager.class);
|
||||
private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class);
|
||||
|
||||
private List<MediaController> mMediaControllers = new ArrayList<>();
|
||||
private MediaOutputBaseDialogImpl mMediaOutputBaseDialogImpl;
|
||||
@@ -110,7 +112,7 @@ public class MediaOutputBaseDialogTest extends SysuiTestCase {
|
||||
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
|
||||
mMediaSessionManager, mLocalBluetoothManager, mStarter,
|
||||
mNotificationEntryManager, mDialogLaunchAnimator,
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager);
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager);
|
||||
mMediaOutputBaseDialogImpl = new MediaOutputBaseDialogImpl(mContext, mBroadcastSender,
|
||||
mMediaOutputController);
|
||||
mMediaOutputBaseDialogImpl.onCreate(new Bundle());
|
||||
|
||||
@@ -19,6 +19,9 @@ package com.android.systemui.media.dialog;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyLong;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.Mockito.atLeastOnce;
|
||||
import static org.mockito.Mockito.doReturn;
|
||||
import static org.mockito.Mockito.mock;
|
||||
@@ -34,10 +37,12 @@ import android.graphics.drawable.Icon;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaDescription;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.MediaRoute2Info;
|
||||
import android.media.NearbyDevice;
|
||||
import android.media.RoutingSessionInfo;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.os.PowerExemptionManager;
|
||||
import android.os.RemoteException;
|
||||
import android.service.notification.StatusBarNotification;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
@@ -97,6 +102,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
|
||||
private RoutingSessionInfo mRemoteSessionInfo = mock(RoutingSessionInfo.class);
|
||||
private ActivityStarter mStarter = mock(ActivityStarter.class);
|
||||
private AudioManager mAudioManager = mock(AudioManager.class);
|
||||
private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class);
|
||||
private CommonNotifCollection mNotifCollection = mock(CommonNotifCollection.class);
|
||||
private final DialogLaunchAnimator mDialogLaunchAnimator = mock(DialogLaunchAnimator.class);
|
||||
private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
|
||||
@@ -125,7 +131,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
|
||||
mMediaOutputController = new MediaOutputController(mSpyContext, TEST_PACKAGE_NAME,
|
||||
mMediaSessionManager, mLocalBluetoothManager, mStarter,
|
||||
mNotifCollection, mDialogLaunchAnimator,
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager);
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager);
|
||||
mLocalMediaManager = spy(mMediaOutputController.mLocalMediaManager);
|
||||
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
|
||||
MediaDescription.Builder builder = new MediaDescription.Builder();
|
||||
@@ -177,7 +183,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
|
||||
mMediaOutputController = new MediaOutputController(mSpyContext, null,
|
||||
mMediaSessionManager, mLocalBluetoothManager, mStarter,
|
||||
mNotifCollection, mDialogLaunchAnimator,
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager);
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager);
|
||||
|
||||
mMediaOutputController.start(mCb);
|
||||
|
||||
@@ -206,7 +212,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
|
||||
mMediaOutputController = new MediaOutputController(mSpyContext, null,
|
||||
mMediaSessionManager, mLocalBluetoothManager, mStarter,
|
||||
mNotifCollection, mDialogLaunchAnimator,
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager);
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager);
|
||||
|
||||
mMediaOutputController.start(mCb);
|
||||
|
||||
@@ -511,7 +517,7 @@ public class MediaOutputControllerTest extends SysuiTestCase {
|
||||
mMediaOutputController = new MediaOutputController(mSpyContext, null,
|
||||
mMediaSessionManager, mLocalBluetoothManager, mStarter,
|
||||
mNotifCollection, mDialogLaunchAnimator,
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager);
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager);
|
||||
|
||||
assertThat(mMediaOutputController.getNotificationIcon()).isNull();
|
||||
}
|
||||
@@ -591,4 +597,20 @@ public class MediaOutputControllerTest extends SysuiTestCase {
|
||||
|
||||
assertThat(mMediaOutputController.isVolumeControlEnabled(mMediaDevice1)).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void setTemporaryAllowListExceptionIfNeeded_fromRemoteToBluetooth_addsAllowList() {
|
||||
when(mLocalMediaManager.getCurrentConnectedDevice()).thenReturn(mMediaDevice1);
|
||||
when(mMediaDevice1.getDeviceType()).thenReturn(
|
||||
MediaDevice.MediaDeviceType.TYPE_CAST_DEVICE);
|
||||
when(mMediaDevice1.getFeatures()).thenReturn(
|
||||
ImmutableList.of(MediaRoute2Info.FEATURE_REMOTE_AUDIO_PLAYBACK));
|
||||
when(mMediaDevice2.getDeviceType()).thenReturn(
|
||||
MediaDevice.MediaDeviceType.TYPE_BLUETOOTH_DEVICE);
|
||||
|
||||
mMediaOutputController.setTemporaryAllowListExceptionIfNeeded(mMediaDevice2);
|
||||
|
||||
verify(mPowerExemptionManager).addToTemporaryAllowList(anyString(), anyInt(), anyString(),
|
||||
anyLong());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.media.MediaRoute2Info;
|
||||
import android.media.session.MediaController;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.media.session.PlaybackState;
|
||||
import android.os.PowerExemptionManager;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.View;
|
||||
@@ -84,6 +85,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
|
||||
private final NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
|
||||
NearbyMediaDevicesManager.class);
|
||||
private final AudioManager mAudioManager = mock(AudioManager.class);
|
||||
private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class);
|
||||
|
||||
private List<MediaController> mMediaControllers = new ArrayList<>();
|
||||
private MediaOutputDialog mMediaOutputDialog;
|
||||
@@ -103,7 +105,7 @@ public class MediaOutputDialogTest extends SysuiTestCase {
|
||||
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
|
||||
mMediaSessionManager, mLocalBluetoothManager, mStarter,
|
||||
mNotificationEntryManager, mDialogLaunchAnimator,
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager);
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager);
|
||||
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
|
||||
mMediaOutputDialog = new MediaOutputDialog(mContext, false, mBroadcastSender,
|
||||
mMediaOutputController, mUiEventLogger);
|
||||
|
||||
@@ -23,6 +23,7 @@ import static org.mockito.Mockito.when;
|
||||
|
||||
import android.media.AudioManager;
|
||||
import android.media.session.MediaSessionManager;
|
||||
import android.os.PowerExemptionManager;
|
||||
import android.testing.AndroidTestingRunner;
|
||||
import android.testing.TestableLooper;
|
||||
import android.view.View;
|
||||
@@ -70,6 +71,7 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase {
|
||||
private NearbyMediaDevicesManager mNearbyMediaDevicesManager = mock(
|
||||
NearbyMediaDevicesManager.class);
|
||||
private final AudioManager mAudioManager = mock(AudioManager.class);
|
||||
private PowerExemptionManager mPowerExemptionManager = mock(PowerExemptionManager.class);
|
||||
|
||||
private MediaOutputGroupDialog mMediaOutputGroupDialog;
|
||||
private MediaOutputController mMediaOutputController;
|
||||
@@ -80,7 +82,7 @@ public class MediaOutputGroupDialogTest extends SysuiTestCase {
|
||||
mMediaOutputController = new MediaOutputController(mContext, TEST_PACKAGE,
|
||||
mMediaSessionManager, mLocalBluetoothManager, mStarter,
|
||||
mNotificationEntryManager, mDialogLaunchAnimator,
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager);
|
||||
Optional.of(mNearbyMediaDevicesManager), mAudioManager, mPowerExemptionManager);
|
||||
mMediaOutputController.mLocalMediaManager = mLocalMediaManager;
|
||||
mMediaOutputGroupDialog = new MediaOutputGroupDialog(mContext, false, mBroadcastSender,
|
||||
mMediaOutputController);
|
||||
|
||||
Reference in New Issue
Block a user