Add flag for MediaRouter2-based InfoMediaManager

InfoMediaManager.createInstance() will gate MediaRouter2-based and
MediaRouter2Manager-based instances based on the new flag.

Also, return a NoOpInfoMediaManager when the target package name does
not exist.

Bug: 192657812
Test: Testmapping + Manual Flag Testing
Change-Id: I8dcdd1c1366ee4246b8a4805f2347a810f51afec
This commit is contained in:
Iván Budnik
2023-07-27 14:47:03 +00:00
parent 5bb1e5e803
commit 6f3bd5f8a0
10 changed files with 302 additions and 7 deletions

View File

@@ -58,6 +58,7 @@ android_library {
"setupdesign",
"zxing-core-1.7",
"androidx.room_room-runtime",
"settingslib_flags_lib",
],
@@ -88,3 +89,16 @@ java_defaults {
"SettingsLib",
],
}
aconfig_declarations {
name: "settingslib_media_flags",
package: "com.android.settingslib.media.flags",
srcs: [
"aconfig/settingslib_media_flag_declarations.aconfig",
],
}
java_aconfig_library {
name: "settingslib_flags_lib",
aconfig_declarations: "settingslib_media_flags",
}

View File

@@ -18,6 +18,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.settingslib">
<uses-permission android:name="android.permission.READ_DEVICE_CONFIG" />
<application>
<activity
android:name="com.android.settingslib.users.AvatarPickerActivity"

View File

@@ -0,0 +1,8 @@
package: "com.android.settingslib.media.flags"
flag {
name: "use_media_router2_for_info_media_manager"
namespace: "placeholder_namespace"
description: "Gates whether to use a MediaRouter2-based implementation of InfoMediaManager, instead of the legacy MediaRouter2Manager-based implementation."
bug: "192657812"
}

View File

@@ -62,6 +62,7 @@ import androidx.annotation.RequiresApi;
import com.android.internal.annotations.VisibleForTesting;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.flags.Flags;
import java.util.ArrayList;
import java.util.Collections;
@@ -105,6 +106,28 @@ public abstract class InfoMediaManager extends MediaManager {
}
}
/** Creates an instance of InfoMediaManager. */
public static InfoMediaManager createInstance(
Context context,
String packageName,
Notification notification,
LocalBluetoothManager localBluetoothManager) {
if (Flags.useMediaRouter2ForInfoMediaManager()) {
try {
return new RouterInfoMediaManager(
context, packageName, notification, localBluetoothManager);
} catch (PackageNotAvailableException ex) {
// TODO: b/293578081 - Propagate this exception to callers for proper handling.
Log.w(TAG, "Returning a no-op InfoMediaManager for package " + packageName);
return new NoOpInfoMediaManager(
context, packageName, notification, localBluetoothManager);
}
} else {
return new ManagerInfoMediaManager(
context, packageName, notification, localBluetoothManager);
}
}
@Override
public void startScan() {
mMediaDevices.clear();

View File

@@ -137,7 +137,7 @@ public class LocalMediaManager implements BluetoothCallback {
}
mInfoMediaManager =
new ManagerInfoMediaManager(
InfoMediaManager.createInstance(
context, packageName, notification, mLocalBluetoothManager);
}

View File

@@ -0,0 +1,154 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.media;
import android.app.Notification;
import android.content.Context;
import android.media.MediaRoute2Info;
import android.media.RouteListingPreference;
import android.media.RoutingSessionInfo;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import java.util.Collections;
import java.util.List;
/**
* No-op implementation of {@link InfoMediaManager}.
*
* <p>This implementation is used when {@link RouterInfoMediaManager} throws a {@link
* InfoMediaManager.PackageNotAvailableException}.
*/
// TODO - b/293578081: Remove once PackageNotAvailableException is propagated to library clients.
/* package */ final class NoOpInfoMediaManager extends InfoMediaManager {
NoOpInfoMediaManager(
Context context,
String packageName,
Notification notification,
LocalBluetoothManager localBluetoothManager) {
super(context, packageName, notification, localBluetoothManager);
}
@Override
public void stopScan() {
// Do nothing.
}
@Override
protected void startScanOnRouter() {
// Do nothing.
}
@Override
protected boolean connectDeviceWithoutPackageName(@NonNull MediaDevice device) {
return false;
}
@Override
protected void transferToRoute(@NonNull MediaRoute2Info route) {
// Do nothing.
}
@Override
protected void selectRoute(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo info) {
// Do nothing.
}
@Override
protected void deselectRoute(@NonNull MediaRoute2Info route, @NonNull RoutingSessionInfo info) {
// Do nothing.
}
@Override
protected void releaseSession(@NonNull RoutingSessionInfo sessionInfo) {
// Do nothing.
}
@NonNull
@Override
protected List<MediaRoute2Info> getSelectableRoutes(@NonNull RoutingSessionInfo info) {
return Collections.emptyList();
}
@NonNull
@Override
protected List<MediaRoute2Info> getDeselectableRoutes(@NonNull RoutingSessionInfo info) {
return Collections.emptyList();
}
@NonNull
@Override
protected List<MediaRoute2Info> getSelectedRoutes(@NonNull RoutingSessionInfo info) {
return Collections.emptyList();
}
@Override
protected void setSessionVolume(@NonNull RoutingSessionInfo info, int volume) {
// Do nothing.
}
@Override
protected void setRouteVolume(@NonNull MediaRoute2Info route, int volume) {
// Do nothing.
}
@Nullable
@Override
protected RouteListingPreference getRouteListingPreference() {
return null;
}
@NonNull
@Override
protected List<RoutingSessionInfo> getRemoteSessions() {
return Collections.emptyList();
}
@NonNull
@Override
protected List<RoutingSessionInfo> getRoutingSessionsForPackage() {
return Collections.emptyList();
}
@Nullable
@Override
protected RoutingSessionInfo getRoutingSessionById(@NonNull String sessionId) {
return null;
}
@NonNull
@Override
protected List<MediaRoute2Info> getAllRoutes() {
return Collections.emptyList();
}
@NonNull
@Override
protected List<MediaRoute2Info> getAvailableRoutesFromRouter() {
return Collections.emptyList();
}
@NonNull
@Override
protected List<MediaRoute2Info> getTransferableRoutes(@NonNull String packageName) {
return Collections.emptyList();
}
}

View File

@@ -48,11 +48,14 @@ android_test {
"androidx.test.core",
"androidx.test.rules",
"androidx.test.espresso.core",
"flag-junit",
"mockito-target-minus-junit4",
"platform-test-annotations",
"truth-prebuilt",
"SettingsLibDeviceStateRotationLock",
"SettingsLibSettingsSpinner",
"SettingsLibUsageProgressBarPreference",
"settingslib_flags_lib",
],
dxflags: ["--multi-dex"],

View File

@@ -0,0 +1,93 @@
/*
* Copyright (C) 2023 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.settingslib.media;
import static com.android.settingslib.media.flags.Flags.FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER;
import static com.google.common.truth.Truth.assertThat;
import android.Manifest;
import android.app.UiAutomation;
import android.content.Context;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.test.platform.app.InstrumentationRegistry;
import androidx.test.runner.AndroidJUnit4;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@RunWith(AndroidJUnit4.class)
public class InfoMediaManagerIntegTest {
private static final String FAKE_PACKAGE = "FAKE_PACKAGE";
private Context mContext;
private UiAutomation mUiAutomation;
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Before
public void setUp() {
mContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
mUiAutomation = InstrumentationRegistry.getInstrumentation().getUiAutomation();
mUiAutomation.adoptShellPermissionIdentity(Manifest.permission.MEDIA_CONTENT_CONTROL);
}
@After
public void tearDown() {
mUiAutomation.dropShellPermissionIdentity();
}
@Test
@RequiresFlagsEnabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOn_returnsRouterInfoMediaManager() {
InfoMediaManager manager =
InfoMediaManager.createInstance(mContext, mContext.getPackageName(), null, null);
assertThat(manager).isInstanceOf(RouterInfoMediaManager.class);
}
@Test
@RequiresFlagsEnabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOn_withFakePackage_returnsNoOpInfoMediaManager() {
InfoMediaManager manager =
InfoMediaManager.createInstance(mContext, FAKE_PACKAGE, null, null);
assertThat(manager).isInstanceOf(NoOpInfoMediaManager.class);
}
@Test
@RequiresFlagsEnabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOn_withNullPackage_returnsRouterInfoMediaManager() {
InfoMediaManager manager = InfoMediaManager.createInstance(mContext, null, null, null);
assertThat(manager).isInstanceOf(RouterInfoMediaManager.class);
}
@Test
@RequiresFlagsDisabled(FLAG_USE_MEDIA_ROUTER2_FOR_INFO_MEDIA_MANAGER)
public void createInstance_withMR2FlagOff_returnsManagerInfoMediaManager() {
InfoMediaManager manager =
InfoMediaManager.createInstance(mContext, mContext.getPackageName(), null, null);
assertThat(manager).isInstanceOf(ManagerInfoMediaManager.class);
}
}

View File

@@ -18,8 +18,8 @@ package com.android.systemui.media.controls.pipeline
import android.content.Context
import com.android.settingslib.bluetooth.LocalBluetoothManager
import com.android.settingslib.media.InfoMediaManager
import com.android.settingslib.media.LocalMediaManager
import com.android.settingslib.media.ManagerInfoMediaManager
import javax.inject.Inject
/** Factory to create [LocalMediaManager] objects. */
@@ -31,8 +31,7 @@ constructor(
) {
/** Creates a [LocalMediaManager] for the given package. */
fun create(packageName: String): LocalMediaManager {
return ManagerInfoMediaManager(context, packageName, null, localBluetoothManager).run {
LocalMediaManager(context, localBluetoothManager, this, packageName)
}
return InfoMediaManager.createInstance(context, packageName, null, localBluetoothManager)
.run { LocalMediaManager(context, localBluetoothManager, this, packageName) }
}
}

View File

@@ -76,7 +76,6 @@ import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastMetadata;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.media.InfoMediaManager;
import com.android.settingslib.media.LocalMediaManager;
import com.android.settingslib.media.ManagerInfoMediaManager;
import com.android.settingslib.media.MediaDevice;
import com.android.settingslib.utils.ThreadUtils;
import com.android.systemui.R;
@@ -194,7 +193,7 @@ public class MediaOutputController implements LocalMediaManager.DeviceCallback,
mKeyGuardManager = keyGuardManager;
mFeatureFlags = featureFlags;
mUserTracker = userTracker;
InfoMediaManager imm = new ManagerInfoMediaManager(mContext, packageName, null, lbm);
InfoMediaManager imm = InfoMediaManager.createInstance(mContext, packageName, null, lbm);
mLocalMediaManager = new LocalMediaManager(mContext, lbm, imm, packageName);
mMetricLogger = new MediaOutputMetricLogger(mContext, mPackageName);
mDialogLaunchAnimator = dialogLaunchAnimator;