OSDN Git Service

Add an API to allow a callback to request active scans.
authorJeff Brown <jeffbrown@google.com>
Thu, 9 May 2013 20:40:39 +0000 (13:40 -0700)
committerJeff Brown <jeffbrown@google.com>
Thu, 9 May 2013 21:44:08 +0000 (14:44 -0700)
This API is needed by the support library media router to ensure
that wifi display routes can be discovered while the route
chooser dialog is open.

Bug: 8175766
Change-Id: I3773773d93384aa4a3c009e71a5444ee8ce37caf

api/current.txt
core/java/com/android/internal/app/MediaRouteChooserDialogFragment.java
media/java/android/media/MediaRouter.java

index c1caaf2..1fb398e 100644 (file)
@@ -12202,6 +12202,7 @@ package android.media {
 
   public class MediaRouter {
     method public void addCallback(int, android.media.MediaRouter.Callback);
+    method public void addCallback(int, android.media.MediaRouter.Callback, int);
     method public void addUserRoute(android.media.MediaRouter.UserRouteInfo);
     method public void clearUserRoutes();
     method public android.media.MediaRouter.RouteCategory createRouteCategory(java.lang.CharSequence, boolean);
@@ -12216,6 +12217,8 @@ package android.media {
     method public void removeCallback(android.media.MediaRouter.Callback);
     method public void removeUserRoute(android.media.MediaRouter.UserRouteInfo);
     method public void selectRoute(int, android.media.MediaRouter.RouteInfo);
+    field public static final int CALLBACK_FLAG_ACTIVE_SCAN = 1; // 0x1
+    field public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 2; // 0x2
     field public static final int ROUTE_TYPE_LIVE_AUDIO = 1; // 0x1
     field public static final int ROUTE_TYPE_LIVE_VIDEO = 2; // 0x2
     field public static final int ROUTE_TYPE_USER = 8388608; // 0x800000
index 2bc80ff..cf797bb 100644 (file)
@@ -70,7 +70,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
     };
 
     MediaRouter mRouter;
-    DisplayManager mDisplayService;
     private int mRouteTypes;
 
     private LayoutInflater mInflater;
@@ -98,7 +97,7 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
     public void onAttach(Activity activity) {
         super.onAttach(activity);
         mRouter = (MediaRouter) activity.getSystemService(Context.MEDIA_ROUTER_SERVICE);
-        mDisplayService = (DisplayManager) activity.getSystemService(Context.DISPLAY_SERVICE);
+        mRouter.addCallback(mRouteTypes, mCallback, MediaRouter.CALLBACK_FLAG_ACTIVE_SCAN);
     }
 
     @Override
@@ -121,15 +120,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
 
     public void setRouteTypes(int types) {
         mRouteTypes = types;
-        if ((mRouteTypes & MediaRouter.ROUTE_TYPE_LIVE_VIDEO) != 0 && mDisplayService == null) {
-            final Context activity = getActivity();
-            if (activity != null) {
-                mDisplayService = (DisplayManager) activity.getSystemService(
-                        Context.DISPLAY_SERVICE);
-            }
-        } else {
-            mDisplayService = null;
-        }
     }
 
     void updateVolume() {
@@ -192,7 +182,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
         list.setOnItemClickListener(mAdapter);
 
         mListView = list;
-        mRouter.addCallback(mRouteTypes, mCallback);
 
         mAdapter.scrollToSelectedItem();
 
@@ -204,14 +193,6 @@ public class MediaRouteChooserDialogFragment extends DialogFragment {
         return new RouteChooserDialog(getActivity(), getTheme());
     }
 
-    @Override
-    public void onResume() {
-        super.onResume();
-        if (mDisplayService != null) {
-            mDisplayService.scanWifiDisplays();
-        }
-    }
-
     private static class ViewHolder {
         public TextView text1;
         public TextView text2;
index 9e8aeeb..9168d46 100644 (file)
@@ -53,6 +53,9 @@ public class MediaRouter {
     private static final String TAG = "MediaRouter";
 
     static class Static implements DisplayManager.DisplayListener {
+        // Time between wifi display scans when actively scanning in milliseconds.
+        private static final int WIFI_DISPLAY_SCAN_INTERVAL = 15000;
+
         final Resources mResources;
         final IAudioService mAudioService;
         final DisplayManager mDisplayService;
@@ -73,8 +76,10 @@ public class MediaRouter {
         RouteInfo mSelectedRoute;
 
         WifiDisplayStatus mLastKnownWifiDisplayStatus;
+        boolean mActivelyScanningWifiDisplays;
 
         final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
+            @Override
             public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
                 mHandler.post(new Runnable() {
                     @Override public void run() {
@@ -84,6 +89,16 @@ public class MediaRouter {
             }
         };
 
+        final Runnable mScanWifiDisplays = new Runnable() {
+            @Override
+            public void run() {
+                if (mActivelyScanningWifiDisplays) {
+                    mDisplayService.scanWifiDisplays();
+                    mHandler.postDelayed(this, WIFI_DISPLAY_SCAN_INTERVAL);
+                }
+            }
+        };
+
         Static(Context appContext) {
             mResources = Resources.getSystem();
             mHandler = new Handler(appContext.getMainLooper());
@@ -195,6 +210,32 @@ public class MediaRouter {
             }
         }
 
+        void updateActiveScan() {
+            if (hasActiveScanCallbackOfType(ROUTE_TYPE_LIVE_VIDEO)) {
+                if (!mActivelyScanningWifiDisplays) {
+                    mActivelyScanningWifiDisplays = true;
+                    mHandler.post(mScanWifiDisplays);
+                }
+            } else {
+                if (mActivelyScanningWifiDisplays) {
+                    mActivelyScanningWifiDisplays = false;
+                    mHandler.removeCallbacks(mScanWifiDisplays);
+                }
+            }
+        }
+
+        private boolean hasActiveScanCallbackOfType(int type) {
+            final int count = mCallbacks.size();
+            for (int i = 0; i < count; i++) {
+                CallbackInfo cbi = mCallbacks.get(i);
+                if ((cbi.flags & CALLBACK_FLAG_ACTIVE_SCAN) != 0
+                        && (cbi.type & type) != 0) {
+                    return true;
+                }
+            }
+            return false;
+        }
+
         @Override
         public void onDisplayAdded(int displayId) {
             updatePresentationDisplays(displayId);
@@ -270,6 +311,33 @@ public class MediaRouter {
      */
     public static final int ROUTE_TYPE_USER = 0x00800000;
 
+    /**
+     * Flag for {@link #addCallback}: Actively scan for routes while this callback
+     * is registered.
+     * <p>
+     * When this flag is specified, the media router will actively scan for new
+     * routes.  Certain routes, such as wifi display routes, may not be discoverable
+     * except when actively scanning.  This flag is typically used when the route picker
+     * dialog has been opened by the user to ensure that the route information is
+     * up to date.
+     * </p><p>
+     * Active scanning may consume a significant amount of power and may have intrusive
+     * effects on wireless connectivity.  Therefore it is important that active scanning
+     * only be requested when it is actually needed to satisfy a user request to
+     * discover and select a new route.
+     * </p>
+     */
+    public static final int CALLBACK_FLAG_ACTIVE_SCAN = 1 << 0;
+
+    /**
+     * Flag for {@link #addCallback}: Do not filter route events.
+     * <p>
+     * When this flag is specified, the callback will be invoked for event that affect any
+     * route event if they do not match the callback's associated media route selector.
+     * </p>
+     */
+    public static final int CALLBACK_FLAG_UNFILTERED_EVENTS = 1 << 1;
+
     // Maps application contexts
     static final HashMap<Context, MediaRouter> sRouters = new HashMap<Context, MediaRouter>();
 
@@ -343,20 +411,48 @@ public class MediaRouter {
      * Add a callback to listen to events about specific kinds of media routes.
      * If the specified callback is already registered, its registration will be updated for any
      * additional route types specified.
+     * <p>
+     * This is a convenience method that has the same effect as calling
+     * {@link #addCallback(int, Callback, int)} without flags.
+     * </p>
      *
      * @param types Types of routes this callback is interested in
      * @param cb Callback to add
      */
     public void addCallback(int types, Callback cb) {
-        final int count = sStatic.mCallbacks.size();
-        for (int i = 0; i < count; i++) {
-            final CallbackInfo info = sStatic.mCallbacks.get(i);
-            if (info.cb == cb) {
-                info.type |= types;
-                return;
-            }
+        addCallback(types, cb, 0);
+    }
+
+    /**
+     * Add a callback to listen to events about specific kinds of media routes.
+     * If the specified callback is already registered, its registration will be updated for any
+     * additional route types specified.
+     * <p>
+     * By default, the callback will only be invoked for events that affect routes
+     * that match the specified selector.  The filtering may be disabled by specifying
+     * the {@link #CALLBACK_FLAG_UNFILTERED_EVENTS} flag.
+     * </p>
+     *
+     * @param types Types of routes this callback is interested in
+     * @param cb Callback to add
+     * @param flags Flags to control the behavior of the callback.
+     * May be zero or a combination of {@link #CALLBACK_FLAG_ACTIVE_SCAN} and
+     * {@link #CALLBACK_FLAG_UNFILTERED_EVENTS}.
+     */
+    public void addCallback(int types, Callback cb, int flags) {
+        CallbackInfo info;
+        int index = findCallbackInfo(cb);
+        if (index >= 0) {
+            info = sStatic.mCallbacks.get(index);
+            info.type |= types;
+            info.flags |= flags;
+        } else {
+            info = new CallbackInfo(cb, types, flags, this);
+            sStatic.mCallbacks.add(info);
+        }
+        if ((info.flags & CALLBACK_FLAG_ACTIVE_SCAN) != 0) {
+            sStatic.updateActiveScan();
         }
-        sStatic.mCallbacks.add(new CallbackInfo(cb, types, this));
     }
 
     /**
@@ -365,14 +461,26 @@ public class MediaRouter {
      * @param cb Callback to remove
      */
     public void removeCallback(Callback cb) {
+        int index = findCallbackInfo(cb);
+        if (index >= 0) {
+            CallbackInfo info = sStatic.mCallbacks.remove(index);
+            if ((info.flags & CALLBACK_FLAG_ACTIVE_SCAN) != 0) {
+                sStatic.updateActiveScan();
+            }
+        } else {
+            Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
+        }
+    }
+
+    private int findCallbackInfo(Callback cb) {
         final int count = sStatic.mCallbacks.size();
         for (int i = 0; i < count; i++) {
-            if (sStatic.mCallbacks.get(i).cb == cb) {
-                sStatic.mCallbacks.remove(i);
-                return;
+            final CallbackInfo info = sStatic.mCallbacks.get(i);
+            if (info.cb == cb) {
+                return i;
             }
         }
-        Log.w(TAG, "removeCallback(" + cb + "): callback not registered");
+        return -1;
     }
 
     /**
@@ -431,12 +539,10 @@ public class MediaRouter {
         }
 
         if (oldRoute != null) {
-            // TODO filter types properly
             dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute);
         }
         sStatic.mSelectedRoute = route;
         if (route != null) {
-            // TODO filter types properly
             dispatchRouteSelected(types & route.getSupportedTypes(), route);
         }
     }
@@ -671,7 +777,7 @@ public class MediaRouter {
 
     static void dispatchRouteSelected(int type, RouteInfo info) {
         for (CallbackInfo cbi : sStatic.mCallbacks) {
-            if ((cbi.type & type) != 0) {
+            if (cbi.filterRouteEvent(info)) {
                 cbi.cb.onRouteSelected(cbi.router, type, info);
             }
         }
@@ -679,7 +785,7 @@ public class MediaRouter {
 
     static void dispatchRouteUnselected(int type, RouteInfo info) {
         for (CallbackInfo cbi : sStatic.mCallbacks) {
-            if ((cbi.type & type) != 0) {
+            if (cbi.filterRouteEvent(info)) {
                 cbi.cb.onRouteUnselected(cbi.router, type, info);
             }
         }
@@ -687,7 +793,7 @@ public class MediaRouter {
 
     static void dispatchRouteChanged(RouteInfo info) {
         for (CallbackInfo cbi : sStatic.mCallbacks) {
-            if ((cbi.type & info.mSupportedTypes) != 0) {
+            if (cbi.filterRouteEvent(info)) {
                 cbi.cb.onRouteChanged(cbi.router, info);
             }
         }
@@ -695,7 +801,7 @@ public class MediaRouter {
 
     static void dispatchRouteAdded(RouteInfo info) {
         for (CallbackInfo cbi : sStatic.mCallbacks) {
-            if ((cbi.type & info.mSupportedTypes) != 0) {
+            if (cbi.filterRouteEvent(info)) {
                 cbi.cb.onRouteAdded(cbi.router, info);
             }
         }
@@ -703,7 +809,7 @@ public class MediaRouter {
 
     static void dispatchRouteRemoved(RouteInfo info) {
         for (CallbackInfo cbi : sStatic.mCallbacks) {
-            if ((cbi.type & info.mSupportedTypes) != 0) {
+            if (cbi.filterRouteEvent(info)) {
                 cbi.cb.onRouteRemoved(cbi.router, info);
             }
         }
@@ -711,7 +817,7 @@ public class MediaRouter {
 
     static void dispatchRouteGrouped(RouteInfo info, RouteGroup group, int index) {
         for (CallbackInfo cbi : sStatic.mCallbacks) {
-            if ((cbi.type & group.mSupportedTypes) != 0) {
+            if (cbi.filterRouteEvent(group)) {
                 cbi.cb.onRouteGrouped(cbi.router, info, group, index);
             }
         }
@@ -719,7 +825,7 @@ public class MediaRouter {
 
     static void dispatchRouteUngrouped(RouteInfo info, RouteGroup group) {
         for (CallbackInfo cbi : sStatic.mCallbacks) {
-            if ((cbi.type & group.mSupportedTypes) != 0) {
+            if (cbi.filterRouteEvent(group)) {
                 cbi.cb.onRouteUngrouped(cbi.router, info, group);
             }
         }
@@ -727,7 +833,7 @@ public class MediaRouter {
 
     static void dispatchRouteVolumeChanged(RouteInfo info) {
         for (CallbackInfo cbi : sStatic.mCallbacks) {
-            if ((cbi.type & info.mSupportedTypes) != 0) {
+            if (cbi.filterRouteEvent(info)) {
                 cbi.cb.onRouteVolumeChanged(cbi.router, info);
             }
         }
@@ -735,7 +841,7 @@ public class MediaRouter {
 
     static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
         for (CallbackInfo cbi : sStatic.mCallbacks) {
-            if ((cbi.type & info.mSupportedTypes) != 0) {
+            if (cbi.filterRouteEvent(info)) {
                 cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
             }
         }
@@ -1892,24 +1998,33 @@ public class MediaRouter {
 
     static class CallbackInfo {
         public int type;
+        public int flags;
         public final Callback cb;
         public final MediaRouter router;
 
-        public CallbackInfo(Callback cb, int type, MediaRouter router) {
+        public CallbackInfo(Callback cb, int type, int flags, MediaRouter router) {
             this.cb = cb;
             this.type = type;
+            this.flags = flags;
             this.router = router;
         }
+
+        public boolean filterRouteEvent(RouteInfo route) {
+            return (flags & CALLBACK_FLAG_UNFILTERED_EVENTS) != 0
+                    || (type & route.mSupportedTypes) != 0;
+        }
     }
 
     /**
      * Interface for receiving events about media routing changes.
      * All methods of this interface will be called from the application's main thread.
+     * <p>
+     * A Callback will only receive events relevant to routes that the callback
+     * was registered for unless the {@link MediaRouter#CALLBACK_FLAG_UNFILTERED_EVENTS}
+     * flag was specified in {@link MediaRouter#addCallback(int, Callback, int)}.
+     * </p>
      *
-     * <p>A Callback will only receive events relevant to routes that the callback
-     * was registered for.</p>
-     *
-     * @see MediaRouter#addCallback(int, Callback)
+     * @see MediaRouter#addCallback(int, Callback, int)
      * @see MediaRouter#removeCallback(Callback)
      */
     public static abstract class Callback {