From b58b8f832d06b0ffa8886eba5a4916578a3b8743 Mon Sep 17 00:00:00 2001 From: Dianne Hackborn Date: Mon, 11 Jun 2012 15:08:39 -0700 Subject: Rework media router to be per-context with global process state. Change-Id: Ic1cb6f13e691d22783bcdafae85b7c75aec0a66f --- media/java/android/media/MediaRouter.java | 316 +++++++++++++++--------------- 1 file changed, 158 insertions(+), 158 deletions(-) (limited to 'media/java') diff --git a/media/java/android/media/MediaRouter.java b/media/java/android/media/MediaRouter.java index 8259a5f..5a2389e 100644 --- a/media/java/android/media/MediaRouter.java +++ b/media/java/android/media/MediaRouter.java @@ -21,6 +21,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.content.res.Resources; import android.os.Handler; import android.util.Log; @@ -32,27 +33,60 @@ import java.util.List; * MediaRouter allows applications to control the routing of media channels * and streams from the current device to external speakers and destination devices. * - *

Media routes should only be modified on your application's main thread.

+ *

A MediaRouter is retrieved through {@link Context#getSystemService(String) + * Context.getSystemService()} of a {@link Context#MEDIA_ROUTER_SERVICE + * Context.MEDIA_ROUTER_SERVICE}. + * + *

The media router API is not thread-safe; all interactions with it must be + * done from the main thread of the process.

*/ public class MediaRouter { private static final String TAG = "MediaRouter"; - private Context mAppContext; - private AudioManager mAudioManager; - private Handler mHandler; - private final ArrayList mCallbacks = new ArrayList(); + static class Static { + private final Resources mResources; + private final AudioManager mAudioManager; + private final Handler mHandler; + private final ArrayList mCallbacks = new ArrayList(); + + private final ArrayList mRoutes = new ArrayList(); + private final ArrayList mCategories = new ArrayList(); + + private final RouteCategory mSystemCategory; + private final HeadphoneChangedBroadcastReceiver mHeadphoneChangedReceiver; + + private RouteInfo mDefaultAudio; + private RouteInfo mBluetoothA2dpRoute; + + private RouteInfo mSelectedRoute; - private final ArrayList mRoutes = new ArrayList(); - private final ArrayList mCategories = new ArrayList(); + Static(Context appContext) { + mResources = Resources.getSystem(); + mHandler = new Handler(appContext.getMainLooper()); - private final RouteCategory mSystemCategory; - private RouteInfo mDefaultAudio; - private RouteInfo mBluetoothA2dpRoute; + mAudioManager = (AudioManager)appContext.getSystemService(Context.AUDIO_SERVICE); - private RouteInfo mSelectedRoute; + // XXX this doesn't deal with locale changes! + mSystemCategory = new RouteCategory(mResources.getText( + com.android.internal.R.string.default_audio_route_category_name), + ROUTE_TYPE_LIVE_AUDIO, false); + + final IntentFilter speakerFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); + speakerFilter.addAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG); + speakerFilter.addAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG); + speakerFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG); + mHeadphoneChangedReceiver = new HeadphoneChangedBroadcastReceiver(); + appContext.registerReceiver(mHeadphoneChangedReceiver, speakerFilter); + + mDefaultAudio = new RouteInfo(mSystemCategory); + mDefaultAudio.mName = mResources.getText( + com.android.internal.R.string.default_audio_route_name); + mDefaultAudio.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO; + addRoute(mDefaultAudio); + } + } - // These get removed when an activity dies - final ArrayList mRegisteredReceivers = new ArrayList(); + static Static sStatic; /** * Route type flag for live audio. @@ -79,25 +113,6 @@ public class MediaRouter { // Maps application contexts static final HashMap sRouters = new HashMap(); - /** - * Return a MediaRouter for the application that the specified Context belongs to. - * The behavior or availability of media routing may depend on - * various parameters of the context. - * - * @param context Context for the desired router - * @return Router for the supplied Context - */ - public static MediaRouter forApplication(Context context) { - final Context appContext = context.getApplicationContext(); - if (!sRouters.containsKey(appContext)) { - final MediaRouter r = new MediaRouter(appContext); - sRouters.put(appContext, r); - return r; - } else { - return sRouters.get(appContext); - } - } - static String typesToString(int types) { final StringBuilder result = new StringBuilder(); if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) { @@ -109,52 +124,20 @@ public class MediaRouter { return result.toString(); } - private MediaRouter(Context context) { - mAppContext = context; - mHandler = new Handler(mAppContext.getMainLooper()); - - mAudioManager = (AudioManager) mAppContext.getSystemService(Context.AUDIO_SERVICE); - - mSystemCategory = new RouteCategory(mAppContext.getText( - com.android.internal.R.string.default_audio_route_category_name), - ROUTE_TYPE_LIVE_AUDIO, false); - - registerReceivers(); - createDefaultRoutes(); - } - - private void registerReceivers() { - final IntentFilter speakerFilter = new IntentFilter(Intent.ACTION_HEADSET_PLUG); - speakerFilter.addAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG); - speakerFilter.addAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG); - speakerFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG); - final BroadcastReceiver plugReceiver = new HeadphoneChangedBroadcastReceiver(); - mAppContext.registerReceiver(plugReceiver, speakerFilter); - mRegisteredReceivers.add(plugReceiver); - } - - void unregisterReceivers() { - final int count = mRegisteredReceivers.size(); - for (int i = 0; i < count; i++) { - final BroadcastReceiver r = mRegisteredReceivers.get(i); - mAppContext.unregisterReceiver(r); + /** @hide */ + public MediaRouter(Context context) { + synchronized (Static.class) { + if (sStatic == null) { + sStatic = new Static(context.getApplicationContext()); + } } - mRegisteredReceivers.clear(); - } - - private void createDefaultRoutes() { - mDefaultAudio = new RouteInfo(mSystemCategory); - mDefaultAudio.mName = mAppContext.getText( - com.android.internal.R.string.default_audio_route_name); - mDefaultAudio.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO; - addRoute(mDefaultAudio); } /** * @hide for use by framework routing UI */ public RouteInfo getSystemAudioRoute() { - return mDefaultAudio; + return sStatic.mDefaultAudio; } /** @@ -164,13 +147,15 @@ public class MediaRouter { * @return the selected route */ public RouteInfo getSelectedRoute(int type) { - return mSelectedRoute; + return sStatic.mSelectedRoute; } - void onHeadphonesPlugged(boolean headphonesPresent, String headphonesName) { - mDefaultAudio.mName = headphonesPresent ? headphonesName : mAppContext.getText( - com.android.internal.R.string.default_audio_route_name); - dispatchRouteChanged(mDefaultAudio); + static void onHeadphonesPlugged(boolean headphonesPresent, String headphonesName) { + sStatic.mDefaultAudio.mName = headphonesPresent + ? headphonesName + : sStatic.mResources.getText( + com.android.internal.R.string.default_audio_route_name); + dispatchRouteChanged(sStatic.mDefaultAudio); } /** @@ -182,15 +167,15 @@ public class MediaRouter { * @param cb Callback to add */ public void addCallback(int types, Callback cb) { - final int count = mCallbacks.size(); + final int count = sStatic.mCallbacks.size(); for (int i = 0; i < count; i++) { - final CallbackInfo info = mCallbacks.get(i); + final CallbackInfo info = sStatic.mCallbacks.get(i); if (info.cb == cb) { info.type &= types; return; } } - mCallbacks.add(new CallbackInfo(cb, types)); + sStatic.mCallbacks.add(new CallbackInfo(cb, types, this)); } /** @@ -199,10 +184,10 @@ public class MediaRouter { * @param cb Callback to remove */ public void removeCallback(Callback cb) { - final int count = mCallbacks.size(); + final int count = sStatic.mCallbacks.size(); for (int i = 0; i < count; i++) { - if (mCallbacks.get(i).cb == cb) { - mCallbacks.remove(i); + if (sStatic.mCallbacks.get(i).cb == cb) { + sStatic.mCallbacks.remove(i); return; } } @@ -217,13 +202,18 @@ public class MediaRouter { * @param route Route to select */ public void selectRoute(int types, RouteInfo route) { - if (mSelectedRoute == route) return; + selectRouteStatic(types, route); + } + + static void selectRouteStatic(int types, RouteInfo route) { + if (sStatic.mSelectedRoute == route) return; - if (mSelectedRoute != null) { + if (sStatic.mSelectedRoute != null) { // TODO filter types properly - dispatchRouteUnselected(types & mSelectedRoute.getSupportedTypes(), mSelectedRoute); + dispatchRouteUnselected(types & sStatic.mSelectedRoute.getSupportedTypes(), + sStatic.mSelectedRoute); } - mSelectedRoute = route; + sStatic.mSelectedRoute = route; if (route != null) { // TODO filter types properly dispatchRouteSelected(types & route.getSupportedTypes(), route); @@ -242,16 +232,16 @@ public class MediaRouter { addRoute(info); } - void addRoute(RouteInfo info) { + static void addRoute(RouteInfo info) { final RouteCategory cat = info.getCategory(); - if (!mCategories.contains(cat)) { - mCategories.add(cat); + if (!sStatic.mCategories.contains(cat)) { + sStatic.mCategories.add(cat); } - final boolean onlyRoute = mRoutes.isEmpty(); + final boolean onlyRoute = sStatic.mRoutes.isEmpty(); if (cat.isGroupable() && !(info instanceof RouteGroup)) { // Enforce that any added route in a groupable category must be in a group. final RouteGroup group = new RouteGroup(info.getCategory()); - mRoutes.add(group); + sStatic.mRoutes.add(group); dispatchRouteAdded(group); final int at = group.getRouteCount(); @@ -260,12 +250,12 @@ public class MediaRouter { info = group; } else { - mRoutes.add(info); + sStatic.mRoutes.add(info); dispatchRouteAdded(info); } if (onlyRoute) { - selectRoute(info.getSupportedTypes(), info); + selectRouteStatic(info.getSupportedTypes(), info); } } @@ -285,8 +275,8 @@ public class MediaRouter { * @see #removeUserRoute(UserRouteInfo) */ public void clearUserRoutes() { - for (int i = 0; i < mRoutes.size(); i++) { - final RouteInfo info = mRoutes.get(i); + for (int i = 0; i < sStatic.mRoutes.size(); i++) { + final RouteInfo info = sStatic.mRoutes.get(i); if (info instanceof UserRouteInfo) { removeRouteAt(i); i--; @@ -294,40 +284,40 @@ public class MediaRouter { } } - void removeRoute(RouteInfo info) { - if (mRoutes.remove(info)) { + static void removeRoute(RouteInfo info) { + if (sStatic.mRoutes.remove(info)) { final RouteCategory removingCat = info.getCategory(); - final int count = mRoutes.size(); + final int count = sStatic.mRoutes.size(); boolean found = false; for (int i = 0; i < count; i++) { - final RouteCategory cat = mRoutes.get(i).getCategory(); + final RouteCategory cat = sStatic.mRoutes.get(i).getCategory(); if (removingCat == cat) { found = true; break; } } if (!found) { - mCategories.remove(removingCat); + sStatic.mCategories.remove(removingCat); } dispatchRouteRemoved(info); } } void removeRouteAt(int routeIndex) { - if (routeIndex >= 0 && routeIndex < mRoutes.size()) { - final RouteInfo info = mRoutes.remove(routeIndex); + if (routeIndex >= 0 && routeIndex < sStatic.mRoutes.size()) { + final RouteInfo info = sStatic.mRoutes.remove(routeIndex); final RouteCategory removingCat = info.getCategory(); - final int count = mRoutes.size(); + final int count = sStatic.mRoutes.size(); boolean found = false; for (int i = 0; i < count; i++) { - final RouteCategory cat = mRoutes.get(i).getCategory(); + final RouteCategory cat = sStatic.mRoutes.get(i).getCategory(); if (removingCat == cat) { found = true; break; } } if (!found) { - mCategories.remove(removingCat); + sStatic.mCategories.remove(removingCat); } dispatchRouteRemoved(info); } @@ -340,7 +330,7 @@ public class MediaRouter { * @return the number of unique categories represented by this MediaRouter's known routes */ public int getCategoryCount() { - return mCategories.size(); + return sStatic.mCategories.size(); } /** @@ -351,7 +341,7 @@ public class MediaRouter { * @return the category at index */ public RouteCategory getCategoryAt(int index) { - return mCategories.get(index); + return sStatic.mCategories.get(index); } /** @@ -361,7 +351,7 @@ public class MediaRouter { * @return the number of routes tracked by this router */ public int getRouteCount() { - return mRoutes.size(); + return sStatic.mRoutes.size(); } /** @@ -371,7 +361,15 @@ public class MediaRouter { * @return the route at index */ public RouteInfo getRouteAt(int index) { - return mRoutes.get(index); + return sStatic.mRoutes.get(index); + } + + static int getRouteCountStatic() { + return sStatic.mRoutes.size(); + } + + static RouteInfo getRouteAtStatic(int index) { + return sStatic.mRoutes.get(index); } /** @@ -399,96 +397,96 @@ public class MediaRouter { return new RouteCategory(name, ROUTE_TYPE_USER, isGroupable); } - void updateRoute(final RouteInfo info) { + static void updateRoute(final RouteInfo info) { dispatchRouteChanged(info); } - void dispatchRouteSelected(int type, RouteInfo info) { - final int count = mCallbacks.size(); + static void dispatchRouteSelected(int type, RouteInfo info) { + final int count = sStatic.mCallbacks.size(); for (int i = 0; i < count; i++) { - final CallbackInfo cbi = mCallbacks.get(i); + final CallbackInfo cbi = sStatic.mCallbacks.get(i); if ((cbi.type & type) != 0) { - cbi.cb.onRouteSelected(this, type, info); + cbi.cb.onRouteSelected(cbi.router, type, info); } } } - void dispatchRouteUnselected(int type, RouteInfo info) { - final int count = mCallbacks.size(); + static void dispatchRouteUnselected(int type, RouteInfo info) { + final int count = sStatic.mCallbacks.size(); for (int i = 0; i < count; i++) { - final CallbackInfo cbi = mCallbacks.get(i); + final CallbackInfo cbi = sStatic.mCallbacks.get(i); if ((cbi.type & type) != 0) { - cbi.cb.onRouteUnselected(this, type, info); + cbi.cb.onRouteUnselected(cbi.router, type, info); } } } - void dispatchRouteChanged(RouteInfo info) { - final int count = mCallbacks.size(); + static void dispatchRouteChanged(RouteInfo info) { + final int count = sStatic.mCallbacks.size(); for (int i = 0; i < count; i++) { - final CallbackInfo cbi = mCallbacks.get(i); + final CallbackInfo cbi = sStatic.mCallbacks.get(i); if ((cbi.type & info.mSupportedTypes) != 0) { - cbi.cb.onRouteChanged(this, info); + cbi.cb.onRouteChanged(cbi.router, info); } } } - void dispatchRouteAdded(RouteInfo info) { - final int count = mCallbacks.size(); + static void dispatchRouteAdded(RouteInfo info) { + final int count = sStatic.mCallbacks.size(); for (int i = 0; i < count; i++) { - final CallbackInfo cbi = mCallbacks.get(i); + final CallbackInfo cbi = sStatic.mCallbacks.get(i); if ((cbi.type & info.mSupportedTypes) != 0) { - cbi.cb.onRouteAdded(this, info); + cbi.cb.onRouteAdded(cbi.router, info); } } } - void dispatchRouteRemoved(RouteInfo info) { - final int count = mCallbacks.size(); + static void dispatchRouteRemoved(RouteInfo info) { + final int count = sStatic.mCallbacks.size(); for (int i = 0; i < count; i++) { - final CallbackInfo cbi = mCallbacks.get(i); + final CallbackInfo cbi = sStatic.mCallbacks.get(i); if ((cbi.type & info.mSupportedTypes) != 0) { - cbi.cb.onRouteRemoved(this, info); + cbi.cb.onRouteRemoved(cbi.router, info); } } } - void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) { - final int count = mCallbacks.size(); + static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) { + final int count = sStatic.mCallbacks.size(); for (int i = 0; i < count; i++) { - final CallbackInfo cbi = mCallbacks.get(i); + final CallbackInfo cbi = sStatic.mCallbacks.get(i); if ((cbi.type & group.mSupportedTypes) != 0) { - cbi.cb.onRouteGrouped(this, info, group, index); + cbi.cb.onRouteGrouped(cbi.router, info, group, index); } } } - void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) { - final int count = mCallbacks.size(); + static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) { + final int count = sStatic.mCallbacks.size(); for (int i = 0; i < count; i++) { - final CallbackInfo cbi = mCallbacks.get(i); + final CallbackInfo cbi = sStatic.mCallbacks.get(i); if ((cbi.type & group.mSupportedTypes) != 0) { - cbi.cb.onRouteUngrouped(this, info, group); + cbi.cb.onRouteUngrouped(cbi.router, info, group); } } } - void onA2dpDeviceConnected() { - final RouteInfo info = new RouteInfo(mSystemCategory); + static void onA2dpDeviceConnected() { + final RouteInfo info = new RouteInfo(sStatic.mSystemCategory); info.mName = "Bluetooth"; // TODO Fetch the real name of the device - mBluetoothA2dpRoute = info; - addRoute(mBluetoothA2dpRoute); + sStatic.mBluetoothA2dpRoute = info; + addRoute(sStatic.mBluetoothA2dpRoute); } - void onA2dpDeviceDisconnected() { - removeRoute(mBluetoothA2dpRoute); - mBluetoothA2dpRoute = null; + static void onA2dpDeviceDisconnected() { + removeRoute(sStatic.mBluetoothA2dpRoute); + sStatic.mBluetoothA2dpRoute = null; } /** * Information about a media route. */ - public class RouteInfo { + public static class RouteInfo { CharSequence mName; private CharSequence mStatus; int mSupportedTypes; @@ -565,7 +563,7 @@ public class MediaRouter { * * @see MediaRouter.RouteInfo */ - public class UserRouteInfo extends RouteInfo { + public static class UserRouteInfo extends RouteInfo { UserRouteInfo(RouteCategory category) { super(category); @@ -594,7 +592,7 @@ public class MediaRouter { /** * Information about a route that consists of multiple other routes in a group. */ - public class RouteGroup extends RouteInfo { + public static class RouteGroup extends RouteInfo { final ArrayList mRoutes = new ArrayList(); private boolean mUpdateName; @@ -722,7 +720,7 @@ public class MediaRouter { /** * Definition of a category of routes. All routes belong to a category. */ - public class RouteCategory { + public static class RouteCategory { CharSequence mName; int mTypes; final boolean mGroupable; @@ -760,9 +758,9 @@ public class MediaRouter { out.clear(); } - final int count = getRouteCount(); + final int count = getRouteCountStatic(); for (int i = 0; i < count; i++) { - final RouteInfo route = getRouteAt(i); + final RouteInfo route = getRouteAtStatic(i); if (route.mCategory == this) { out.add(route); } @@ -791,17 +789,19 @@ public class MediaRouter { public String toString() { return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) + - " groupable=" + mGroupable + " routes=" + mRoutes.size() + " }"; + " groupable=" + mGroupable + " routes=" + sStatic.mRoutes.size() + " }"; } } static class CallbackInfo { public int type; - public Callback cb; + public final Callback cb; + public final MediaRouter router; - public CallbackInfo(Callback cb, int type) { + public CallbackInfo(Callback cb, int type, MediaRouter router) { this.cb = cb; this.type = type; + this.router = router; } } @@ -937,24 +937,24 @@ public class MediaRouter { } } - class HeadphoneChangedBroadcastReceiver extends BroadcastReceiver { + static class HeadphoneChangedBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { final String action = intent.getAction(); if (Intent.ACTION_HEADSET_PLUG.equals(action)) { final boolean plugged = intent.getIntExtra("state", 0) != 0; - final String name = mAppContext.getString( + final String name = sStatic.mResources.getString( com.android.internal.R.string.default_audio_route_name_headphones); onHeadphonesPlugged(plugged, name); } else if (Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG.equals(action) || Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG.equals(action)) { final boolean plugged = intent.getIntExtra("state", 0) != 0; - final String name = mAppContext.getString( + final String name = sStatic.mResources.getString( com.android.internal.R.string.default_audio_route_name_dock_speakers); onHeadphonesPlugged(plugged, name); } else if (Intent.ACTION_HDMI_AUDIO_PLUG.equals(action)) { final boolean plugged = intent.getIntExtra("state", 0) != 0; - final String name = mAppContext.getString( + final String name = sStatic.mResources.getString( com.android.internal.R.string.default_audio_route_name_hdmi); onHeadphonesPlugged(plugged, name); } -- cgit v1.1