1af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock/* 2af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Copyright (C) 2014 The Android Open Source Project 3af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * 4af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Licensed under the Apache License, Version 2.0 (the "License"); 5af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * you may not use this file except in compliance with the License. 6af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * You may obtain a copy of the License at 7af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * 8af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * http://www.apache.org/licenses/LICENSE-2.0 9af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * 10af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * Unless required by applicable law or agreed to in writing, software 11af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * distributed under the License is distributed on an "AS IS" BASIS, 12af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * See the License for the specific language governing permissions and 14af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock * limitations under the License. 15af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock */ 16af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 17af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockpackage com.android.systemui.statusbar.policy; 18af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 19af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.content.Context; 2078b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlockimport android.content.pm.ApplicationInfo; 2178b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlockimport android.content.pm.PackageManager; 2278b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlockimport android.content.pm.PackageManager.NameNotFoundException; 23af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.media.MediaRouter; 24af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport android.media.MediaRouter.RouteInfo; 2578b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlockimport android.media.projection.MediaProjectionInfo; 2678b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlockimport android.media.projection.MediaProjectionManager; 2778b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlockimport android.os.Handler; 2878b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlockimport android.text.TextUtils; 291e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlockimport android.util.ArrayMap; 301e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlockimport android.util.ArraySet; 311e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlockimport android.util.Log; 32af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 3378b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlockimport com.android.systemui.R; 3478b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock 351e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlockimport java.io.FileDescriptor; 361e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlockimport java.io.PrintWriter; 37af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockimport java.util.ArrayList; 3878b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlockimport java.util.Objects; 391e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlockimport java.util.Set; 401e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlockimport java.util.UUID; 41af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 42c0d7058b14c24cd07912f5629c26b39b7b4673d5Winsonimport static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY; 43c0d7058b14c24cd07912f5629c26b39b7b4673d5Winson 44af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock/** Platform implementation of the cast controller. **/ 45af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlockpublic class CastControllerImpl implements CastController { 461e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock private static final String TAG = "CastController"; 471e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 48af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 491e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock private final Context mContext; 50af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); 51af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock private final MediaRouter mMediaRouter; 521e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock private final ArrayMap<String, RouteInfo> mRoutes = new ArrayMap<>(); 531e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock private final Object mDiscoveringLock = new Object(); 5478b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock private final MediaProjectionManager mProjectionManager; 5578b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock private final Object mProjectionLock = new Object(); 56af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 571e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock private boolean mDiscovering; 587eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk private boolean mCallbackRegistered; 5978b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock private MediaProjectionInfo mProjection; 60ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock 61af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock public CastControllerImpl(Context context) { 621e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock mContext = context; 63af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); 6478b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock mProjectionManager = (MediaProjectionManager) 6578b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock context.getSystemService(Context.MEDIA_PROJECTION_SERVICE); 6678b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock mProjection = mProjectionManager.getActiveProjectionInfo(); 6778b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock mProjectionManager.addCallback(mProjectionCallback, new Handler()); 681e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (DEBUG) Log.d(TAG, "new CastController()"); 691e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock } 701e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock 711e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 721e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock pw.println("CastController state:"); 731e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock pw.print(" mDiscovering="); pw.println(mDiscovering); 747eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk pw.print(" mCallbackRegistered="); pw.println(mCallbackRegistered); 751e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock pw.print(" mCallbacks.size="); pw.println(mCallbacks.size()); 761e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock pw.print(" mRoutes.size="); pw.println(mRoutes.size()); 771e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock for (int i = 0; i < mRoutes.size(); i++) { 781e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock final RouteInfo route = mRoutes.valueAt(i); 791e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock pw.print(" "); pw.println(routeToString(route)); 801e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock } 8178b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock pw.print(" mProjection="); pw.println(mProjection); 82af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 83af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 84af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock @Override 85af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock public void addCallback(Callback callback) { 86af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock mCallbacks.add(callback); 871e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock fireOnCastDevicesChanged(callback); 887eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk synchronized (mDiscoveringLock) { 897eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk handleDiscoveryChangeLocked(); 907eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk } 91af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 92af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 93af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock @Override 94af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock public void removeCallback(Callback callback) { 95af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock mCallbacks.remove(callback); 967eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk synchronized (mDiscoveringLock) { 977eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk handleDiscoveryChangeLocked(); 987eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk } 99af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 100af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 101af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock @Override 102af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock public void setDiscovering(boolean request) { 1031e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock synchronized (mDiscoveringLock) { 1041e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (mDiscovering == request) return; 1051e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock mDiscovering = request; 1061e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (DEBUG) Log.d(TAG, "setDiscovering: " + request); 1077eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk handleDiscoveryChangeLocked(); 1087eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk } 1097eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk } 1107eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk 1117eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk private void handleDiscoveryChangeLocked() { 1127eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk if (mCallbackRegistered) { 1137eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk mMediaRouter.removeCallback(mMediaCallback); 1147eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk mCallbackRegistered = false; 1157eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk } 1167eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk if (mDiscovering) { 1177eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk mMediaRouter.addCallback(ROUTE_TYPE_REMOTE_DISPLAY, mMediaCallback, 1187eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); 1197eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk mCallbackRegistered = true; 1207eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk } else if (mCallbacks.size() != 0) { 1217eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk mMediaRouter.addCallback(ROUTE_TYPE_REMOTE_DISPLAY, mMediaCallback, 1227eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk MediaRouter.CALLBACK_FLAG_PASSIVE_DISCOVERY); 1237eb6202feef939d92b5ea268cc2cfea3cc177cd6Jason Monk mCallbackRegistered = true; 124af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 125af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 126af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 127af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock @Override 128af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock public void setCurrentUserId(int currentUserId) { 129af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock mMediaRouter.rebindAsUser(currentUserId); 130af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 131af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 1321e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock @Override 1331e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock public Set<CastDevice> getCastDevices() { 1341e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock final ArraySet<CastDevice> devices = new ArraySet<CastDevice>(); 13578b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock synchronized (mProjectionLock) { 13678b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock if (mProjection != null) { 13778b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock final CastDevice device = new CastDevice(); 13878b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock device.id = mProjection.getPackageName(); 13978b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock device.name = getAppName(mProjection.getPackageName()); 14078b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock device.description = mContext.getString(R.string.quick_settings_casting); 14178b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock device.state = CastDevice.STATE_CONNECTED; 14278b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock device.tag = mProjection; 14378b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock devices.add(device); 14478b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock return devices; 14578b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 14678b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 1471e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock synchronized(mRoutes) { 1481e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock for (RouteInfo route : mRoutes.values()) { 1491e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock final CastDevice device = new CastDevice(); 1501e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock device.id = route.getTag().toString(); 1511e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock final CharSequence name = route.getName(mContext); 1521e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock device.name = name != null ? name.toString() : null; 1531e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock final CharSequence description = route.getDescription(); 1541e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock device.description = description != null ? description.toString() : null; 1551e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock device.state = route.isConnecting() ? CastDevice.STATE_CONNECTING 1561e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock : route.isSelected() ? CastDevice.STATE_CONNECTED 1571e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock : CastDevice.STATE_DISCONNECTED; 1581e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock device.tag = route; 1591e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock devices.add(device); 1601e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock } 161af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 1621e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock return devices; 1631e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock } 1641e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock 1651e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock @Override 1661e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock public void startCasting(CastDevice device) { 1671e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (device == null || device.tag == null) return; 1681e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock final RouteInfo route = (RouteInfo) device.tag; 1691e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (DEBUG) Log.d(TAG, "startCasting: " + routeToString(route)); 1701e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock mMediaRouter.selectRoute(ROUTE_TYPE_REMOTE_DISPLAY, route); 1711e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock } 172af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 1731e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock @Override 17478b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock public void stopCasting(CastDevice device) { 17578b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock final boolean isProjection = device.tag instanceof MediaProjectionInfo; 17678b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock if (DEBUG) Log.d(TAG, "stopCasting isProjection=" + isProjection); 17778b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock if (isProjection) { 17878b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock final MediaProjectionInfo projection = (MediaProjectionInfo) device.tag; 17978b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock if (Objects.equals(mProjectionManager.getActiveProjectionInfo(), projection)) { 18078b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock mProjectionManager.stopActiveProjection(); 18178b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } else { 18278b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock Log.w(TAG, "Projection is no longer active: " + projection); 18378b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 18478b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } else { 18578b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock mMediaRouter.getDefaultRoute().select(); 18678b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 18778b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 18878b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock 18978b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock private void setProjection(MediaProjectionInfo projection, boolean started) { 19078b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock boolean changed = false; 19178b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock final MediaProjectionInfo oldProjection = mProjection; 19278b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock synchronized (mProjectionLock) { 19378b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock final boolean isCurrent = Objects.equals(projection, mProjection); 19478b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock if (started && !isCurrent) { 19578b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock mProjection = projection; 19678b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock changed = true; 19778b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } else if (!started && isCurrent) { 19878b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock mProjection = null; 19978b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock changed = true; 20078b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 20178b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 20278b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock if (changed) { 20378b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock if (DEBUG) Log.d(TAG, "setProjection: " + oldProjection + " -> " + mProjection); 20478b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock fireOnCastDevicesChanged(); 20578b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 20678b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 20778b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock 20878b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock private String getAppName(String packageName) { 20978b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock final PackageManager pm = mContext.getPackageManager(); 21078b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock try { 21178b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock final ApplicationInfo appInfo = pm.getApplicationInfo(packageName, 0); 21278b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock if (appInfo != null) { 21378b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock final CharSequence label = appInfo.loadLabel(pm); 21478b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock if (!TextUtils.isEmpty(label)) { 21578b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock return label.toString(); 21678b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 21778b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 21878b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock Log.w(TAG, "No label found for package: " + packageName); 21978b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } catch (NameNotFoundException e) { 22078b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock Log.w(TAG, "Error getting appName for package: " + packageName, e); 22178b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 22278b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock return packageName; 2231e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock } 2241e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock 2251e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock private void updateRemoteDisplays() { 2261e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock synchronized(mRoutes) { 2271e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock mRoutes.clear(); 2281e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock final int n = mMediaRouter.getRouteCount(); 2291e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock for (int i = 0; i < n; i++) { 2301e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock final RouteInfo route = mMediaRouter.getRouteAt(i); 2311e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (!route.isEnabled()) continue; 2321e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (!route.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) continue; 2331e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock ensureTagExists(route); 2341e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock mRoutes.put(route.getTag().toString(), route); 2351e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock } 2361e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock final RouteInfo selected = mMediaRouter.getSelectedRoute(ROUTE_TYPE_REMOTE_DISPLAY); 2371e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (selected != null && !selected.isDefault()) { 2381e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock ensureTagExists(selected); 2391e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock mRoutes.put(selected.getTag().toString(), selected); 2401e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock } 241af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 2421e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock fireOnCastDevicesChanged(); 2431e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock } 2441e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock 2451e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock private void ensureTagExists(RouteInfo route) { 2461e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (route.getTag() == null) { 2471e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock route.setTag(UUID.randomUUID().toString()); 248ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock } 249af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 250af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 2511e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock private void fireOnCastDevicesChanged() { 252af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock for (Callback callback : mCallbacks) { 2531e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock fireOnCastDevicesChanged(callback); 254ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock } 255ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock } 256ccb6b9a90f228cc4e31a9442ed28756ff474c080John Spurlock 2571e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock private void fireOnCastDevicesChanged(Callback callback) { 2581e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock callback.onCastDevicesChanged(); 2591e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock } 2601e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock 2611e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock private static String routeToString(RouteInfo route) { 2621e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (route == null) return null; 2631e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock final StringBuilder sb = new StringBuilder().append(route.getName()).append('/') 2641e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock .append(route.getDescription()).append('@').append(route.getDeviceAddress()) 2651e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock .append(",status=").append(route.getStatus()); 2661e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (route.isDefault()) sb.append(",default"); 2671e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (route.isEnabled()) sb.append(",enabled"); 2681e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (route.isConnecting()) sb.append(",connecting"); 2691e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (route.isSelected()) sb.append(",selected"); 2701e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock return sb.append(",id=").append(route.getTag()).toString(); 271af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 272af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock 273af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock private final MediaRouter.SimpleCallback mMediaCallback = new MediaRouter.SimpleCallback() { 274af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock @Override 275af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock public void onRouteAdded(MediaRouter router, RouteInfo route) { 2761e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (DEBUG) Log.d(TAG, "onRouteAdded: " + routeToString(route)); 277af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock updateRemoteDisplays(); 278af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 279af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock @Override 280af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock public void onRouteChanged(MediaRouter router, RouteInfo route) { 2811e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (DEBUG) Log.d(TAG, "onRouteChanged: " + routeToString(route)); 282af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock updateRemoteDisplays(); 283af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 284af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock @Override 285af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock public void onRouteRemoved(MediaRouter router, RouteInfo route) { 2861e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (DEBUG) Log.d(TAG, "onRouteRemoved: " + routeToString(route)); 287af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock updateRemoteDisplays(); 288af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 289af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock @Override 290af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock public void onRouteSelected(MediaRouter router, int type, RouteInfo route) { 2911e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (DEBUG) Log.d(TAG, "onRouteSelected(" + type + "): " + routeToString(route)); 292af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock updateRemoteDisplays(); 293af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 294af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock @Override 295af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) { 2961e6eb17a22056529601c8e413c2da0541d59d93bJohn Spurlock if (DEBUG) Log.d(TAG, "onRouteUnselected(" + type + "): " + routeToString(route)); 297af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock updateRemoteDisplays(); 298af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock } 299af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock }; 30078b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock 30178b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock private final MediaProjectionManager.Callback mProjectionCallback 30278b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock = new MediaProjectionManager.Callback() { 30378b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock @Override 30478b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock public void onStart(MediaProjectionInfo info) { 30578b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock setProjection(info, true); 30678b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 30778b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock 30878b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock @Override 30978b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock public void onStop(MediaProjectionInfo info) { 31078b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock setProjection(info, false); 31178b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock } 31278b8c8fbde12b214314cc2ab2644350b5d5189a5John Spurlock }; 313af8d6c44f06d2f8baac2c5774a9efdae3fc36797John Spurlock} 314