Home | History | Annotate | Download | only in media
      1 /*
      2  * Copyright (C) 2013 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package androidx.mediarouter.media;
     18 
     19 import android.content.Context;
     20 import android.hardware.display.DisplayManager;
     21 import android.os.Build;
     22 import android.os.Handler;
     23 import android.util.Log;
     24 import android.view.Display;
     25 
     26 import androidx.annotation.RequiresApi;
     27 
     28 import java.lang.reflect.Field;
     29 import java.lang.reflect.InvocationTargetException;
     30 import java.lang.reflect.Method;
     31 
     32 @RequiresApi(17)
     33 final class MediaRouterJellybeanMr1 {
     34     private static final String TAG = "MediaRouterJellybeanMr1";
     35 
     36     public static Object createCallback(Callback callback) {
     37         return new CallbackProxy<Callback>(callback);
     38     }
     39 
     40     public static final class RouteInfo {
     41         public static boolean isEnabled(Object routeObj) {
     42             return ((android.media.MediaRouter.RouteInfo)routeObj).isEnabled();
     43         }
     44 
     45         public static Display getPresentationDisplay(Object routeObj) {
     46             // android.media.MediaRouter.RouteInfo.getPresentationDisplay() was
     47             // added in API 17. However, some factory releases of JB MR1 missed it.
     48             try {
     49                 return ((android.media.MediaRouter.RouteInfo)routeObj).getPresentationDisplay();
     50             } catch (NoSuchMethodError ex) {
     51                 Log.w(TAG, "Cannot get presentation display for the route.", ex);
     52             }
     53             return null;
     54         }
     55 
     56         private RouteInfo() {
     57         }
     58     }
     59 
     60     public static interface Callback extends MediaRouterJellybean.Callback {
     61         public void onRoutePresentationDisplayChanged(Object routeObj);
     62     }
     63 
     64     /**
     65      * Workaround the fact that the version of MediaRouter.addCallback() that accepts a
     66      * flag to perform an active scan does not exist in JB MR1 so we need to force
     67      * wifi display scans directly through the DisplayManager.
     68      * Do not use on JB MR2 and above.
     69      */
     70     public static final class ActiveScanWorkaround implements Runnable {
     71         // Time between wifi display scans when actively scanning in milliseconds.
     72         private static final int WIFI_DISPLAY_SCAN_INTERVAL = 15000;
     73 
     74         private final DisplayManager mDisplayManager;
     75         private final Handler mHandler;
     76         private Method mScanWifiDisplaysMethod;
     77 
     78         private boolean mActivelyScanningWifiDisplays;
     79 
     80         public ActiveScanWorkaround(Context context, Handler handler) {
     81             if (Build.VERSION.SDK_INT != 17) {
     82                 throw new UnsupportedOperationException();
     83             }
     84 
     85             mDisplayManager = (DisplayManager) context.getSystemService(Context.DISPLAY_SERVICE);
     86             mHandler = handler;
     87             try {
     88                 mScanWifiDisplaysMethod = DisplayManager.class.getMethod("scanWifiDisplays");
     89             } catch (NoSuchMethodException ex) {
     90             }
     91         }
     92 
     93         public void setActiveScanRouteTypes(int routeTypes) {
     94             // On JB MR1, there is no API to scan wifi display routes.
     95             // Instead we must make a direct call into the DisplayManager to scan
     96             // wifi displays on this version but only when live video routes are requested.
     97             // See also the JellybeanMr2Impl implementation of this method.
     98             // This was fixed in JB MR2 by adding a new overload of addCallback() to
     99             // enable active scanning on request.
    100             if ((routeTypes & MediaRouterJellybean.ROUTE_TYPE_LIVE_VIDEO) != 0) {
    101                 if (!mActivelyScanningWifiDisplays) {
    102                     if (mScanWifiDisplaysMethod != null) {
    103                         mActivelyScanningWifiDisplays = true;
    104                         mHandler.post(this);
    105                     } else {
    106                         Log.w(TAG, "Cannot scan for wifi displays because the "
    107                                 + "DisplayManager.scanWifiDisplays() method is "
    108                                 + "not available on this device.");
    109                     }
    110                 }
    111             } else {
    112                 if (mActivelyScanningWifiDisplays) {
    113                     mActivelyScanningWifiDisplays = false;
    114                     mHandler.removeCallbacks(this);
    115                 }
    116             }
    117         }
    118 
    119         @Override
    120         public void run() {
    121             if (mActivelyScanningWifiDisplays) {
    122                 try {
    123                     mScanWifiDisplaysMethod.invoke(mDisplayManager);
    124                 } catch (IllegalAccessException ex) {
    125                     Log.w(TAG, "Cannot scan for wifi displays.", ex);
    126                 } catch (InvocationTargetException ex) {
    127                     Log.w(TAG, "Cannot scan for wifi displays.", ex);
    128                 }
    129                 mHandler.postDelayed(this, WIFI_DISPLAY_SCAN_INTERVAL);
    130             }
    131         }
    132     }
    133 
    134     /**
    135      * Workaround the fact that the isConnecting() method does not exist in JB MR1.
    136      * Do not use on JB MR2 and above.
    137      */
    138     public static final class IsConnectingWorkaround {
    139         private Method mGetStatusCodeMethod;
    140         private int mStatusConnecting;
    141 
    142         public IsConnectingWorkaround() {
    143             if (Build.VERSION.SDK_INT != 17) {
    144                 throw new UnsupportedOperationException();
    145             }
    146 
    147             try {
    148                 Field statusConnectingField =
    149                         android.media.MediaRouter.RouteInfo.class.getField("STATUS_CONNECTING");
    150                 mStatusConnecting = statusConnectingField.getInt(null);
    151                 mGetStatusCodeMethod =
    152                         android.media.MediaRouter.RouteInfo.class.getMethod("getStatusCode");
    153             } catch (NoSuchFieldException ex) {
    154             } catch (NoSuchMethodException ex) {
    155             } catch (IllegalAccessException ex) {
    156             }
    157         }
    158 
    159         public boolean isConnecting(Object routeObj) {
    160             android.media.MediaRouter.RouteInfo route =
    161                     (android.media.MediaRouter.RouteInfo)routeObj;
    162 
    163             if (mGetStatusCodeMethod != null) {
    164                 try {
    165                     int statusCode = (Integer)mGetStatusCodeMethod.invoke(route);
    166                     return statusCode == mStatusConnecting;
    167                 } catch (IllegalAccessException ex) {
    168                 } catch (InvocationTargetException ex) {
    169                 }
    170             }
    171 
    172             // Assume not connecting.
    173             return false;
    174         }
    175     }
    176 
    177     static class CallbackProxy<T extends Callback>
    178             extends MediaRouterJellybean.CallbackProxy<T> {
    179         public CallbackProxy(T callback) {
    180             super(callback);
    181         }
    182 
    183         @Override
    184         public void onRoutePresentationDisplayChanged(android.media.MediaRouter router,
    185                 android.media.MediaRouter.RouteInfo route) {
    186             mCallback.onRoutePresentationDisplayChanged(route);
    187         }
    188     }
    189 
    190     private MediaRouterJellybeanMr1() {
    191     }
    192 }
    193