Replace feature flags declaration from broadcast receiver to meta data
By using meta data tag declared in the AndroidManifest.xml it is
possible to eliminate binder calls associated with querying the system
whether there is a Broadcast Receiver that can resolve the intent.
Bug: b/296297648
Test: tested manually
Change-Id: Ice6c52c0fabb0478ac0c91b87e020b12864cf756
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java
index e407e5a..d63ebf3 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/GlobalMediaRouter.java
@@ -88,6 +88,7 @@
MediaRouter.PrepareTransferNotifier mTransferNotifier;
private final Context mApplicationContext;
+ private final MediaFeatureFlagsRetriever mMediaFeatureFlagsRetriever;
private final ArrayList<WeakReference<MediaRouter>> mRouters = new ArrayList<>();
private final ArrayList<MediaRouter.RouteInfo> mRoutes = new ArrayList<>();
private final Map<Pair<String, String>, String> mUniqueIdMap = new HashMap<>();
@@ -135,16 +136,27 @@
/* package */ GlobalMediaRouter(Context applicationContext) {
mApplicationContext = applicationContext;
+ mMediaFeatureFlagsRetriever = MediaFeatureFlagsRetriever.fromContext(applicationContext);
mLowRam =
ActivityManagerCompat.isLowRamDevice(
(ActivityManager)
applicationContext.getSystemService(Context.ACTIVITY_SERVICE));
- mTransferReceiverDeclared =
- Build.VERSION.SDK_INT >= Build.VERSION_CODES.R
- && MediaTransferReceiver.isDeclared(mApplicationContext);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ if (mMediaFeatureFlagsRetriever.isFlagDeclared(
+ MediaFeatureFlagsRetriever.FEATURE_FLAG_MEDIA_TRANSFER_ENABLED)) {
+ mTransferReceiverDeclared =
+ mMediaFeatureFlagsRetriever.getBoolean(
+ MediaFeatureFlagsRetriever.FEATURE_FLAG_MEDIA_TRANSFER_ENABLED);
+ } else {
+ mTransferReceiverDeclared = MediaTransferReceiver.isDeclared(mApplicationContext);
+ }
+ }
+
mUseMediaRouter2ForSystemRouting =
- SystemRoutingUsingMediaRouter2Receiver.isDeclared(mApplicationContext);
+ mMediaFeatureFlagsRetriever.getBoolean(
+ MediaFeatureFlagsRetriever
+ .FEATURE_FLAG_SYSTEM_ROUTING_USING_MEDIA_ROUTER2);
if (DEBUG && mUseMediaRouter2ForSystemRouting) {
// This is only added to skip the presubmit check for UnusedVariable
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaFeatureFlagsRetriever.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaFeatureFlagsRetriever.java
new file mode 100644
index 0000000..8b58a10
--- /dev/null
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaFeatureFlagsRetriever.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 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 androidx.mediarouter.media;
+
+import static androidx.annotation.RestrictTo.Scope.LIBRARY;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.RestrictTo;
+import androidx.annotation.StringDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Class for fetching feature flags declared in the AndroidManifest.xml.
+ *
+ * <p>To override the default value of a feature, media apps should declare meta-data in their
+ * manifests with an override value. For example:
+ *
+ * <pre class="prettyprint">{@code
+ * <application>
+ * <meta-data
+ * android:name="androidx.mediarouter.media.media_transfer_enabled"
+ * android:value="true" />
+ * </application>
+ * }</pre>
+ */
+@RestrictTo(LIBRARY)
+/* package */ final class MediaFeatureFlagsRetriever {
+
+ /**
+ * Media transfer is a feature that media routing can be controlled via system UI. By using
+ * this, media app users can re-route the media without opening the app activity again. Also,
+ * the media can be transferred from one device to another device seamlessly, depending on the
+ * devices. This feature is supported from Android 11.
+ */
+ /* package */ static final String FEATURE_FLAG_MEDIA_TRANSFER_ENABLED =
+ "androidx.mediarouter.media.media_transfer_enabled";
+
+ /** Gets SystemRoutes using {@link android.media.MediaRouter2}. */
+ /* package */ static final String FEATURE_FLAG_SYSTEM_ROUTING_USING_MEDIA_ROUTER2 =
+ "androidx.mediarouter.media.system_routing_using_media_router2";
+
+ @StringDef({
+ FEATURE_FLAG_MEDIA_TRANSFER_ENABLED,
+ FEATURE_FLAG_SYSTEM_ROUTING_USING_MEDIA_ROUTER2
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ /* package */ @interface FeatureFlag {}
+
+ @NonNull private final Bundle mMetaData;
+
+ /* package */ MediaFeatureFlagsRetriever(@NonNull Bundle metaData) {
+ this.mMetaData = metaData;
+ }
+
+ /* package */ static MediaFeatureFlagsRetriever fromContext(@NonNull Context context) {
+ try {
+ ApplicationInfo app = context.getPackageManager().getApplicationInfo(
+ context.getPackageName(), PackageManager.GET_META_DATA);
+ Bundle metaData = app.metaData;
+ if (metaData == null) {
+ metaData = Bundle.EMPTY;
+ }
+ return new MediaFeatureFlagsRetriever(metaData);
+ } catch (PackageManager.NameNotFoundException e) {
+ return new MediaFeatureFlagsRetriever(Bundle.EMPTY);
+ }
+ }
+
+ /* package */ boolean isFlagDeclared(@FeatureFlag String featureFlag) {
+ return mMetaData.containsKey(featureFlag);
+ }
+
+ /* package */ boolean getBoolean(@FeatureFlag String featureFlag) {
+ return mMetaData.getBoolean(featureFlag);
+ }
+}
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaTransferReceiver.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaTransferReceiver.java
index 8ed2a5f..39ac63b 100644
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaTransferReceiver.java
+++ b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/MediaTransferReceiver.java
@@ -32,39 +32,40 @@
/**
* A {@link BroadcastReceiver} class for enabling Media transfer feature.
- * <p>
- * Media transfer is a feature that media routing can be controlled via system UI. By using this,
+ *
+ * <p>Media transfer is a feature that media routing can be controlled via system UI. By using this,
* media app users can re-route the media without opening the app activity again. Also, the media
- * can be transferred from one device to another device seamlessly, depending on the devices.
- * This feature is supported from Android 11.
- * <p>
- * To enable the media transfer feature, media apps should declare this receiver in the app's
+ * can be transferred from one device to another device seamlessly, depending on the devices. This
+ * feature is supported from Android 11.
+ *
+ * <p>To enable the media transfer feature, media apps should declare this receiver in the app's
* manifest. For example:
+ *
* <pre class="prettyprint">{@code
* <application>
* <receiver android:name="androidx.mediarouter.media.MediaTransferReceiver" />
* </application>
* }</pre>
- * <p>
- * Media apps that enable this feature should implement the {@link MediaRouter.Callback} properly.
- * Specifically:
+ *
+ * <p>Media apps that enable this feature should implement the {@link MediaRouter.Callback}
+ * properly. Specifically:
+ *
* <ul>
- * <li>Apps should be able to get events even when the app is in background. This means
- * that the callback should not be removed in {@link Activity#onStop()}. (See
- * {@link MediaRouter#addCallback(MediaRouteSelector, MediaRouter.Callback, int)} for
- * how to add callback.</li>
- * <li>Apps should handle the case where the media routing is changed from the outside of the
- * app. The callback's
- * {@link MediaRouter.Callback#onRouteSelected(MediaRouter, MediaRouter.RouteInfo, int)
- * onRouteSelected} method should be able to handle the cases.</li>
- * <li>In order to enable transferring media from remote to local (e.g. from TV to phone),
- * media apps should {@link MediaRouterParams.Builder#setTransferToLocalEnabled(boolean)
- * enable 'transfer to local' feature}. Otherwise, the local devices won't be shown as a
- * transfer target while playing on a remote device.
+ * <li>Apps should be able to get events even when the app is in background. This means that the
+ * callback should not be removed in {@link Activity#onStop()}. (See {@link
+ * MediaRouter#addCallback(MediaRouteSelector, MediaRouter.Callback, int)} for how to add
+ * callback.
+ * <li>Apps should handle the case where the media routing is changed from the outside of the app.
+ * The callback's {@link MediaRouter.Callback#onRouteSelected(MediaRouter,
+ * MediaRouter.RouteInfo, int) onRouteSelected} method should be able to handle the cases.
+ * <li>In order to enable transferring media from remote to local (e.g. from TV to phone), media
+ * apps should {@link MediaRouterParams.Builder#setTransferToLocalEnabled(boolean) enable
+ * 'transfer to local' feature}. Otherwise, the local devices won't be shown as a transfer
+ * target while playing on a remote device.
* </ul>
*/
-// TODO: Mention that devs should implement onPrepareTransfer() - after the API is ready.
-public final class MediaTransferReceiver extends BroadcastReceiver {
+// TODO(b/311679859): deprecate the API after 1.7.0 is released.
+public final class MediaTransferReceiver extends BroadcastReceiver {
@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
// Do nothing for now.
diff --git a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/SystemRoutingUsingMediaRouter2Receiver.java b/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/SystemRoutingUsingMediaRouter2Receiver.java
deleted file mode 100644
index ecc6df6..0000000
--- a/mediarouter/mediarouter/src/main/java/androidx/mediarouter/media/SystemRoutingUsingMediaRouter2Receiver.java
+++ /dev/null
@@ -1,57 +0,0 @@
-/*
- * Copyright 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 androidx.mediarouter.media;
-
-import static androidx.annotation.RestrictTo.Scope.LIBRARY;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-
-import androidx.annotation.NonNull;
-import androidx.annotation.RestrictTo;
-
-import java.util.List;
-
-/**
- * A {@link BroadcastReceiver} class for enabling apps to get SystemRoutes using
- * {@link android.media.MediaRouter2}.
- */
-@RestrictTo(LIBRARY)
-final class SystemRoutingUsingMediaRouter2Receiver extends BroadcastReceiver {
- @Override
- public void onReceive(@NonNull Context context, @NonNull Intent intent) {
- // Do nothing for now.
- }
-
- /**
- * Checks whether the {@link SystemRoutingUsingMediaRouter2Receiver} is declared in the app's
- * manifest.
- */
- @RestrictTo(LIBRARY)
- public static boolean isDeclared(@NonNull Context applicationContext) {
- Intent queryIntent = new Intent(applicationContext,
- SystemRoutingUsingMediaRouter2Receiver.class);
- queryIntent.setPackage(applicationContext.getPackageName());
- PackageManager pm = applicationContext.getPackageManager();
- List<ResolveInfo> resolveInfos = pm.queryBroadcastReceivers(queryIntent, 0);
-
- return resolveInfos.size() > 0;
- }
-}
diff --git a/samples/MediaRoutingDemo/src/main/AndroidManifest.xml b/samples/MediaRoutingDemo/src/main/AndroidManifest.xml
index a7eca17..6562ac1 100644
--- a/samples/MediaRoutingDemo/src/main/AndroidManifest.xml
+++ b/samples/MediaRoutingDemo/src/main/AndroidManifest.xml
@@ -19,7 +19,8 @@
supplies a unique name for the application. When writing your
own application, the package name must be changed from "com.example.*"
to come from a domain that you own or have control over. -->
-<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+<manifest xmlns:tools="http://schemas.android.com/tools"
+ xmlns:android="http://schemas.android.com/apk/res/android">
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.BLUETOOTH"
@@ -84,12 +85,6 @@
<activity android:name=".activities.systemrouting.SystemRoutingActivity"
android:exported="false" />
- <receiver android:name="androidx.mediarouter.media.MediaTransferReceiver"
- android:exported="true" />
-
- <receiver android:name="androidx.mediarouter.media.SystemRoutingUsingMediaRouter2Receiver"
- android:exported="false" />
-
<service
android:name=".services.SampleMediaRouteProviderService"
android:exported="true"
@@ -113,6 +108,16 @@
</intent-filter>
</service>
+ <meta-data
+ android:name="androidx.mediarouter.media.media_transfer_enabled"
+ android:value="true"
+ tools:ignore="MetadataTagInsideApplicationTag" />
+
+ <meta-data
+ android:name="androidx.mediarouter.media.system_routing_using_media_router2"
+ android:value="true"
+ tools:ignore="MetadataTagInsideApplicationTag" />
+
</application>
<!-- The smallest screen this app works on is a phone. The app will