CastControllerImpl.java revision 1e6eb17a22056529601c8e413c2da0541d59d93b
1/* 2 * Copyright (C) 2014 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 17package com.android.systemui.statusbar.policy; 18 19import static android.media.MediaRouter.ROUTE_TYPE_REMOTE_DISPLAY; 20 21import android.content.Context; 22import android.media.MediaRouter; 23import android.media.MediaRouter.RouteInfo; 24import android.util.ArrayMap; 25import android.util.ArraySet; 26import android.util.Log; 27 28import java.io.FileDescriptor; 29import java.io.PrintWriter; 30import java.util.ArrayList; 31import java.util.Set; 32import java.util.UUID; 33 34/** Platform implementation of the cast controller. **/ 35public class CastControllerImpl implements CastController { 36 private static final String TAG = "CastController"; 37 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 38 39 private final Context mContext; 40 private final ArrayList<Callback> mCallbacks = new ArrayList<Callback>(); 41 private final MediaRouter mMediaRouter; 42 private final ArrayMap<String, RouteInfo> mRoutes = new ArrayMap<>(); 43 private final Object mDiscoveringLock = new Object(); 44 45 private boolean mDiscovering; 46 47 public CastControllerImpl(Context context) { 48 mContext = context; 49 mMediaRouter = (MediaRouter) context.getSystemService(Context.MEDIA_ROUTER_SERVICE); 50 if (DEBUG) Log.d(TAG, "new CastController()"); 51 } 52 53 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 54 pw.println("CastController state:"); 55 pw.print(" mDiscovering="); pw.println(mDiscovering); 56 pw.print(" mCallbacks.size="); pw.println(mCallbacks.size()); 57 pw.print(" mRoutes.size="); pw.println(mRoutes.size()); 58 for (int i = 0; i < mRoutes.size(); i++) { 59 final RouteInfo route = mRoutes.valueAt(i); 60 pw.print(" "); pw.println(routeToString(route)); 61 } 62 } 63 64 @Override 65 public void addCallback(Callback callback) { 66 mCallbacks.add(callback); 67 fireOnCastDevicesChanged(callback); 68 } 69 70 @Override 71 public void removeCallback(Callback callback) { 72 mCallbacks.remove(callback); 73 } 74 75 @Override 76 public void setDiscovering(boolean request) { 77 synchronized (mDiscoveringLock) { 78 if (mDiscovering == request) return; 79 mDiscovering = request; 80 if (DEBUG) Log.d(TAG, "setDiscovering: " + request); 81 if (request) { 82 mMediaRouter.addCallback(ROUTE_TYPE_REMOTE_DISPLAY, mMediaCallback, 83 MediaRouter.CALLBACK_FLAG_REQUEST_DISCOVERY); 84 } else { 85 mMediaRouter.removeCallback(mMediaCallback); 86 } 87 } 88 } 89 90 @Override 91 public void setCurrentUserId(int currentUserId) { 92 mMediaRouter.rebindAsUser(currentUserId); 93 } 94 95 @Override 96 public Set<CastDevice> getCastDevices() { 97 final ArraySet<CastDevice> devices = new ArraySet<CastDevice>(); 98 synchronized(mRoutes) { 99 for (RouteInfo route : mRoutes.values()) { 100 final CastDevice device = new CastDevice(); 101 device.id = route.getTag().toString(); 102 final CharSequence name = route.getName(mContext); 103 device.name = name != null ? name.toString() : null; 104 final CharSequence description = route.getDescription(); 105 device.description = description != null ? description.toString() : null; 106 device.state = route.isConnecting() ? CastDevice.STATE_CONNECTING 107 : route.isSelected() ? CastDevice.STATE_CONNECTED 108 : CastDevice.STATE_DISCONNECTED; 109 device.tag = route; 110 devices.add(device); 111 } 112 } 113 return devices; 114 } 115 116 @Override 117 public void startCasting(CastDevice device) { 118 if (device == null || device.tag == null) return; 119 final RouteInfo route = (RouteInfo) device.tag; 120 if (DEBUG) Log.d(TAG, "startCasting: " + routeToString(route)); 121 mMediaRouter.selectRoute(ROUTE_TYPE_REMOTE_DISPLAY, route); 122 } 123 124 @Override 125 public void stopCasting() { 126 if (DEBUG) Log.d(TAG, "stopCasting"); 127 mMediaRouter.getDefaultRoute().select(); 128 } 129 130 private void updateRemoteDisplays() { 131 synchronized(mRoutes) { 132 mRoutes.clear(); 133 final int n = mMediaRouter.getRouteCount(); 134 for (int i = 0; i < n; i++) { 135 final RouteInfo route = mMediaRouter.getRouteAt(i); 136 if (!route.isEnabled()) continue; 137 if (!route.matchesTypes(ROUTE_TYPE_REMOTE_DISPLAY)) continue; 138 ensureTagExists(route); 139 mRoutes.put(route.getTag().toString(), route); 140 } 141 final RouteInfo selected = mMediaRouter.getSelectedRoute(ROUTE_TYPE_REMOTE_DISPLAY); 142 if (selected != null && !selected.isDefault()) { 143 ensureTagExists(selected); 144 mRoutes.put(selected.getTag().toString(), selected); 145 } 146 } 147 fireOnCastDevicesChanged(); 148 } 149 150 private void ensureTagExists(RouteInfo route) { 151 if (route.getTag() == null) { 152 route.setTag(UUID.randomUUID().toString()); 153 } 154 } 155 156 private void fireOnCastDevicesChanged() { 157 for (Callback callback : mCallbacks) { 158 fireOnCastDevicesChanged(callback); 159 } 160 } 161 162 private void fireOnCastDevicesChanged(Callback callback) { 163 callback.onCastDevicesChanged(); 164 } 165 166 private static String routeToString(RouteInfo route) { 167 if (route == null) return null; 168 final StringBuilder sb = new StringBuilder().append(route.getName()).append('/') 169 .append(route.getDescription()).append('@').append(route.getDeviceAddress()) 170 .append(",status=").append(route.getStatus()); 171 if (route.isDefault()) sb.append(",default"); 172 if (route.isEnabled()) sb.append(",enabled"); 173 if (route.isConnecting()) sb.append(",connecting"); 174 if (route.isSelected()) sb.append(",selected"); 175 return sb.append(",id=").append(route.getTag()).toString(); 176 } 177 178 private final MediaRouter.SimpleCallback mMediaCallback = new MediaRouter.SimpleCallback() { 179 @Override 180 public void onRouteAdded(MediaRouter router, RouteInfo route) { 181 if (DEBUG) Log.d(TAG, "onRouteAdded: " + routeToString(route)); 182 updateRemoteDisplays(); 183 } 184 @Override 185 public void onRouteChanged(MediaRouter router, RouteInfo route) { 186 if (DEBUG) Log.d(TAG, "onRouteChanged: " + routeToString(route)); 187 updateRemoteDisplays(); 188 } 189 @Override 190 public void onRouteRemoved(MediaRouter router, RouteInfo route) { 191 if (DEBUG) Log.d(TAG, "onRouteRemoved: " + routeToString(route)); 192 updateRemoteDisplays(); 193 } 194 @Override 195 public void onRouteSelected(MediaRouter router, int type, RouteInfo route) { 196 if (DEBUG) Log.d(TAG, "onRouteSelected(" + type + "): " + routeToString(route)); 197 updateRemoteDisplays(); 198 } 199 @Override 200 public void onRouteUnselected(MediaRouter router, int type, RouteInfo route) { 201 if (DEBUG) Log.d(TAG, "onRouteUnselected(" + type + "): " + routeToString(route)); 202 updateRemoteDisplays(); 203 } 204 }; 205} 206