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
diff --git a/packages/SettingsLib/Android.bp b/packages/SettingsLib/Android.bp
index c244ca0..3d35bad 100644
--- a/packages/SettingsLib/Android.bp
+++ b/packages/SettingsLib/Android.bp
@@ -58,6 +58,7 @@
"setupdesign",
"zxing-core-1.7",
"androidx.room_room-runtime",
+ "settingslib_flags_lib",
],
@@ -88,3 +89,16 @@
"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",
+}
diff --git a/packages/SettingsLib/AndroidManifest.xml b/packages/SettingsLib/AndroidManifest.xml
index 13f8a37..322d6cf 100644
--- a/packages/SettingsLib/AndroidManifest.xml
+++ b/packages/SettingsLib/AndroidManifest.xml
@@ -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"
diff --git a/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
new file mode 100644
index 0000000..0b74fa8
--- /dev/null
+++ b/packages/SettingsLib/aconfig/settingslib_media_flag_declarations.aconfig
@@ -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"
+}
\ No newline at end of file
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
index 1ea155a6..bf63f5d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/InfoMediaManager.java
@@ -62,6 +62,7 @@
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 @@
}
}
+ /** 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();
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
index 4fb0487..fe3ef5d 100644
--- a/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
+++ b/packages/SettingsLib/src/com/android/settingslib/media/LocalMediaManager.java
@@ -137,7 +137,7 @@
}
mInfoMediaManager =
- new ManagerInfoMediaManager(
+ InfoMediaManager.createInstance(
context, packageName, notification, mLocalBluetoothManager);
}
diff --git a/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
new file mode 100644
index 0000000..9d578bc
--- /dev/null
+++ b/packages/SettingsLib/src/com/android/settingslib/media/NoOpInfoMediaManager.java
@@ -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();
+ }
+}
diff --git a/packages/SettingsLib/tests/integ/Android.bp b/packages/SettingsLib/tests/integ/Android.bp
index ff3eeec..8970deb 100644
--- a/packages/SettingsLib/tests/integ/Android.bp
+++ b/packages/SettingsLib/tests/integ/Android.bp
@@ -48,11 +48,14 @@
"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"],
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/InfoMediaManagerIntegTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/InfoMediaManagerIntegTest.java
new file mode 100644
index 0000000..c647cbb
--- /dev/null
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/media/InfoMediaManagerIntegTest.java
@@ -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);
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt
index f908481..785a1e8 100644
--- a/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt
+++ b/packages/SystemUI/src/com/android/systemui/media/controls/pipeline/LocalMediaManagerFactory.kt
@@ -18,8 +18,8 @@
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 @@
) {
/** 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) }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
index 83631b0..be42569 100644
--- a/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
+++ b/packages/SystemUI/src/com/android/systemui/media/dialog/MediaOutputController.java
@@ -76,7 +76,6 @@
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 @@
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;