Merge "Merge errors." into jb-dev
diff --git a/api/16.txt b/api/16.txt
index bcbccda..c95de36 100644
--- a/api/16.txt
+++ b/api/16.txt
@@ -5336,6 +5336,7 @@
     field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
     field public static final java.lang.String LAYOUT_INFLATER_SERVICE = "layout_inflater";
     field public static final java.lang.String LOCATION_SERVICE = "location";
+    field public static final java.lang.String MEDIA_ROUTER_SERVICE = "media_router";
     field public static final int MODE_APPEND = 32768; // 0x8000
     field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
     field public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -11511,7 +11512,6 @@
     method public void clearUserRoutes();
     method public android.media.MediaRouter.RouteCategory createRouteCategory(java.lang.CharSequence, boolean);
     method public android.media.MediaRouter.UserRouteInfo createUserRoute(android.media.MediaRouter.RouteCategory);
-    method public static android.media.MediaRouter forApplication(android.content.Context);
     method public android.media.MediaRouter.RouteCategory getCategoryAt(int);
     method public int getCategoryCount();
     method public android.media.MediaRouter.RouteInfo getRouteAt(int);
@@ -11534,14 +11534,14 @@
     method public abstract void onRouteUnselected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo);
   }
 
-  public class MediaRouter.RouteCategory {
+  public static class MediaRouter.RouteCategory {
     method public java.lang.CharSequence getName();
     method public java.util.List<android.media.MediaRouter.RouteInfo> getRoutes(java.util.List<android.media.MediaRouter.RouteInfo>);
     method public int getSupportedTypes();
     method public boolean isGroupable();
   }
 
-  public class MediaRouter.RouteGroup extends android.media.MediaRouter.RouteInfo {
+  public static class MediaRouter.RouteGroup extends android.media.MediaRouter.RouteInfo {
     method public void addRoute(android.media.MediaRouter.RouteInfo);
     method public void addRoute(android.media.MediaRouter.RouteInfo, int);
     method public android.media.MediaRouter.RouteInfo getRouteAt(int);
@@ -11550,7 +11550,7 @@
     method public void removeRoute(int);
   }
 
-  public class MediaRouter.RouteInfo {
+  public static class MediaRouter.RouteInfo {
     method public android.media.MediaRouter.RouteCategory getCategory();
     method public android.media.MediaRouter.RouteGroup getGroup();
     method public java.lang.CharSequence getName();
@@ -11569,7 +11569,7 @@
     method public void onRouteUnselected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo);
   }
 
-  public class MediaRouter.UserRouteInfo extends android.media.MediaRouter.RouteInfo {
+  public static class MediaRouter.UserRouteInfo extends android.media.MediaRouter.RouteInfo {
     method public void setName(java.lang.CharSequence);
     method public void setStatus(java.lang.CharSequence);
   }
diff --git a/api/current.txt b/api/current.txt
index bcbccda..c95de36 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -5336,6 +5336,7 @@
     field public static final java.lang.String KEYGUARD_SERVICE = "keyguard";
     field public static final java.lang.String LAYOUT_INFLATER_SERVICE = "layout_inflater";
     field public static final java.lang.String LOCATION_SERVICE = "location";
+    field public static final java.lang.String MEDIA_ROUTER_SERVICE = "media_router";
     field public static final int MODE_APPEND = 32768; // 0x8000
     field public static final int MODE_ENABLE_WRITE_AHEAD_LOGGING = 8; // 0x8
     field public static final int MODE_MULTI_PROCESS = 4; // 0x4
@@ -11511,7 +11512,6 @@
     method public void clearUserRoutes();
     method public android.media.MediaRouter.RouteCategory createRouteCategory(java.lang.CharSequence, boolean);
     method public android.media.MediaRouter.UserRouteInfo createUserRoute(android.media.MediaRouter.RouteCategory);
-    method public static android.media.MediaRouter forApplication(android.content.Context);
     method public android.media.MediaRouter.RouteCategory getCategoryAt(int);
     method public int getCategoryCount();
     method public android.media.MediaRouter.RouteInfo getRouteAt(int);
@@ -11534,14 +11534,14 @@
     method public abstract void onRouteUnselected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo);
   }
 
-  public class MediaRouter.RouteCategory {
+  public static class MediaRouter.RouteCategory {
     method public java.lang.CharSequence getName();
     method public java.util.List<android.media.MediaRouter.RouteInfo> getRoutes(java.util.List<android.media.MediaRouter.RouteInfo>);
     method public int getSupportedTypes();
     method public boolean isGroupable();
   }
 
-  public class MediaRouter.RouteGroup extends android.media.MediaRouter.RouteInfo {
+  public static class MediaRouter.RouteGroup extends android.media.MediaRouter.RouteInfo {
     method public void addRoute(android.media.MediaRouter.RouteInfo);
     method public void addRoute(android.media.MediaRouter.RouteInfo, int);
     method public android.media.MediaRouter.RouteInfo getRouteAt(int);
@@ -11550,7 +11550,7 @@
     method public void removeRoute(int);
   }
 
-  public class MediaRouter.RouteInfo {
+  public static class MediaRouter.RouteInfo {
     method public android.media.MediaRouter.RouteCategory getCategory();
     method public android.media.MediaRouter.RouteGroup getGroup();
     method public java.lang.CharSequence getName();
@@ -11569,7 +11569,7 @@
     method public void onRouteUnselected(android.media.MediaRouter, int, android.media.MediaRouter.RouteInfo);
   }
 
-  public class MediaRouter.UserRouteInfo extends android.media.MediaRouter.RouteInfo {
+  public static class MediaRouter.UserRouteInfo extends android.media.MediaRouter.RouteInfo {
     method public void setName(java.lang.CharSequence);
     method public void setStatus(java.lang.CharSequence);
   }
diff --git a/core/java/android/app/ContextImpl.java b/core/java/android/app/ContextImpl.java
index 4c35a8c..b902550 100644
--- a/core/java/android/app/ContextImpl.java
+++ b/core/java/android/app/ContextImpl.java
@@ -55,6 +55,7 @@
 import android.location.ILocationManager;
 import android.location.LocationManager;
 import android.media.AudioManager;
+import android.media.MediaRouter;
 import android.net.ConnectivityManager;
 import android.net.IConnectivityManager;
 import android.net.INetworkPolicyManager;
@@ -288,6 +289,11 @@
                     return new AudioManager(ctx);
                 }});
 
+        registerService(MEDIA_ROUTER_SERVICE, new ServiceFetcher() {
+                public Object createService(ContextImpl ctx) {
+                    return new MediaRouter(ctx);
+                }});
+
         registerService(CLIPBOARD_SERVICE, new ServiceFetcher() {
                 public Object createService(ContextImpl ctx) {
                     return new ClipboardManager(ctx.getOuterContext(),
diff --git a/core/java/android/app/MediaRouteActionProvider.java b/core/java/android/app/MediaRouteActionProvider.java
index 9b9e14f..7764ac6 100644
--- a/core/java/android/app/MediaRouteActionProvider.java
+++ b/core/java/android/app/MediaRouteActionProvider.java
@@ -37,7 +37,7 @@
     public MediaRouteActionProvider(Context context) {
         super(context);
         mContext = context;
-        mRouter = MediaRouter.forApplication(context);
+        mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
 
         // Start with live audio by default.
         // TODO Update this when new route types are added; segment by API level
diff --git a/core/java/android/app/MediaRouteButton.java b/core/java/android/app/MediaRouteButton.java
index 10c0793..8f9379a 100644
--- a/core/java/android/app/MediaRouteButton.java
+++ b/core/java/android/app/MediaRouteButton.java
@@ -58,7 +58,7 @@
     public MediaRouteButton(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
 
-        mRouter = MediaRouter.forApplication(context);
+        mRouter = (MediaRouter)context.getSystemService(Context.MEDIA_ROUTER_SERVICE);
 
         TypedArray a = context.obtainStyledAttributes(attrs,
                 com.android.internal.R.styleable.MediaRouteButton, defStyleAttr, 0);
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 4c169d38..7588cda 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1528,6 +1528,8 @@
      * @see android.net.wifi.WifiManager
      * @see #AUDIO_SERVICE
      * @see android.media.AudioManager
+     * @see #MEDIA_ROUTER_SERVICE
+     * @see android.media.MediaRouter
      * @see #TELEPHONY_SERVICE
      * @see android.telephony.TelephonyManager
      * @see #INPUT_METHOD_SERVICE
@@ -1778,7 +1780,6 @@
      */
     public static final String NSD_SERVICE = "servicediscovery";
 
-
     /**
      * Use with {@link #getSystemService} to retrieve a
      * {@link android.media.AudioManager} for handling management of volume,
@@ -1791,6 +1792,16 @@
 
     /**
      * Use with {@link #getSystemService} to retrieve a
+     * {@link android.media.MediaRouter} for controlling and managing
+     * routing of media.
+     *
+     * @see #getSystemService
+     * @see android.media.MediaRouter
+     */
+    public static final String MEDIA_ROUTER_SERVICE = "media_router";
+
+    /**
+     * Use with {@link #getSystemService} to retrieve a
      * {@link android.telephony.TelephonyManager} for handling management the
      * telephony features of the device.
      *
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.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 @@
  * MediaRouter allows applications to control the routing of media channels
  * and streams from the current device to external speakers and destination devices.
  *
- * <p>Media routes should only be modified on your application's main thread.</p>
+ * <p>A MediaRouter is retrieved through {@link Context#getSystemService(String)
+ * Context.getSystemService()} of a {@link Context#MEDIA_ROUTER_SERVICE
+ * Context.MEDIA_ROUTER_SERVICE}.
+ *
+ * <p>The media router API is not thread-safe; all interactions with it must be
+ * done from the main thread of the process.</p>
  */
 public class MediaRouter {
     private static final String TAG = "MediaRouter";
 
-    private Context mAppContext;
-    private AudioManager mAudioManager;
-    private Handler mHandler;
-    private final ArrayList<CallbackInfo> mCallbacks = new ArrayList<CallbackInfo>();
+    static class Static {
+        private final Resources mResources;
+        private final AudioManager mAudioManager;
+        private final Handler mHandler;
+        private final ArrayList<CallbackInfo> mCallbacks = new ArrayList<CallbackInfo>();
 
-    private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
-    private final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
+        private final ArrayList<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
+        private final ArrayList<RouteCategory> mCategories = new ArrayList<RouteCategory>();
 
-    private final RouteCategory mSystemCategory;
-    private RouteInfo mDefaultAudio;
-    private RouteInfo mBluetoothA2dpRoute;
+        private final RouteCategory mSystemCategory;
+        private final HeadphoneChangedBroadcastReceiver mHeadphoneChangedReceiver;
 
-    private RouteInfo mSelectedRoute;
+        private RouteInfo mDefaultAudio;
+        private RouteInfo mBluetoothA2dpRoute;
 
-    // These get removed when an activity dies
-    final ArrayList<BroadcastReceiver> mRegisteredReceivers = new ArrayList<BroadcastReceiver>();
+        private RouteInfo mSelectedRoute;
+
+        Static(Context appContext) {
+            mResources = Resources.getSystem();
+            mHandler = new Handler(appContext.getMainLooper());
+
+            mAudioManager = (AudioManager)appContext.getSystemService(Context.AUDIO_SERVICE);
+
+            // 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);
+        }
+    }
+
+    static Static sStatic;
 
     /**
      * Route type flag for live audio.
@@ -79,25 +113,6 @@
     // Maps application contexts
     static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
 
-    /**
-     * 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 @@
         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 @@
      * @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 @@
      * @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 @@
      * @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 @@
      * @param route Route to select
      */
     public void selectRoute(int types, RouteInfo route) {
-        if (mSelectedRoute == route) return;
+        selectRouteStatic(types, route);
+    }
 
-        if (mSelectedRoute != null) {
+    static void selectRouteStatic(int types, RouteInfo route) {
+        if (sStatic.mSelectedRoute == route) return;
+
+        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 @@
         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 @@
 
             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 @@
      * @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 @@
         }
     }
 
-    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 @@
      * @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 @@
      * @return the category at index
      */
     public RouteCategory getCategoryAt(int index) {
-        return mCategories.get(index);
+        return sStatic.mCategories.get(index);
     }
 
     /**
@@ -361,7 +351,7 @@
      * @return the number of routes tracked by this router
      */
     public int getRouteCount() {
-        return mRoutes.size();
+        return sStatic.mRoutes.size();
     }
 
     /**
@@ -371,7 +361,15 @@
      * @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 @@
         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 @@
      *
      * @see MediaRouter.RouteInfo
      */
-    public class UserRouteInfo extends RouteInfo {
+    public static class UserRouteInfo extends RouteInfo {
 
         UserRouteInfo(RouteCategory category) {
             super(category);
@@ -594,7 +592,7 @@
     /**
      * 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<RouteInfo> mRoutes = new ArrayList<RouteInfo>();
         private boolean mUpdateName;
 
@@ -722,7 +720,7 @@
     /**
      * 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 @@
                 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 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 @@
         }
     }
 
-    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);
             }