WifiDisplayAdapter.java revision f8f0eddd07d22ab815d97dd32ae6ed52dc31a80c
1/* 2 * Copyright (C) 2012 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.server.display; 18 19import com.android.internal.util.DumpUtils; 20import com.android.internal.util.IndentingPrintWriter; 21 22import android.content.Context; 23import android.content.Intent; 24import android.hardware.display.DisplayManager; 25import android.hardware.display.WifiDisplay; 26import android.hardware.display.WifiDisplayStatus; 27import android.media.RemoteDisplay; 28import android.os.Handler; 29import android.os.IBinder; 30import android.view.Surface; 31 32import java.io.PrintWriter; 33import java.util.Arrays; 34 35/** 36 * Connects to Wifi displays that implement the Miracast protocol. 37 * <p> 38 * The Wifi display protocol relies on Wifi direct for discovering and pairing 39 * with the display. Once connected, the Media Server opens an RTSP socket and accepts 40 * a connection from the display. After session negotiation, the Media Server 41 * streams encoded buffers to the display. 42 * </p><p> 43 * This class is responsible for connecting to Wifi displays and mediating 44 * the interactions between Media Server, Surface Flinger and the Display Manager Service. 45 * </p><p> 46 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 47 * </p> 48 */ 49final class WifiDisplayAdapter extends DisplayAdapter { 50 private static final String TAG = "WifiDisplayAdapter"; 51 52 private WifiDisplayController mDisplayController; 53 private WifiDisplayDevice mDisplayDevice; 54 55 private WifiDisplayStatus mCurrentStatus; 56 private boolean mEnabled; 57 private int mScanState; 58 private int mActiveDisplayState; 59 private WifiDisplay mActiveDisplay; 60 private WifiDisplay[] mKnownDisplays = WifiDisplay.EMPTY_ARRAY; 61 62 private boolean mPendingStatusChangeBroadcast; 63 64 public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 65 Context context, Handler handler, Listener listener) { 66 super(syncRoot, context, handler, listener, TAG); 67 } 68 69 @Override 70 public void dumpLocked(PrintWriter pw) { 71 super.dumpLocked(pw); 72 73 pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked()); 74 pw.println("mEnabled=" + mEnabled); 75 pw.println("mScanState=" + mScanState); 76 pw.println("mActiveDisplayState=" + mActiveDisplayState); 77 pw.println("mActiveDisplay=" + mActiveDisplay); 78 pw.println("mKnownDisplays=" + Arrays.toString(mKnownDisplays)); 79 pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast); 80 81 // Try to dump the controller state. 82 if (mDisplayController == null) { 83 pw.println("mDisplayController=null"); 84 } else { 85 pw.println("mDisplayController:"); 86 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 87 ipw.increaseIndent(); 88 DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200); 89 } 90 } 91 92 @Override 93 public void registerLocked() { 94 super.registerLocked(); 95 96 getHandler().post(new Runnable() { 97 @Override 98 public void run() { 99 mDisplayController = new WifiDisplayController( 100 getContext(), getHandler(), mWifiDisplayListener); 101 } 102 }); 103 } 104 105 public void requestScanLocked() { 106 getHandler().post(new Runnable() { 107 @Override 108 public void run() { 109 if (mDisplayController != null) { 110 mDisplayController.requestScan(); 111 } 112 } 113 }); 114 } 115 116 public void requestConnectLocked(final String address) { 117 getHandler().post(new Runnable() { 118 @Override 119 public void run() { 120 if (mDisplayController != null) { 121 mDisplayController.requestConnect(address); 122 } 123 } 124 }); 125 } 126 127 public void requestDisconnectLocked() { 128 getHandler().post(new Runnable() { 129 @Override 130 public void run() { 131 if (mDisplayController != null) { 132 mDisplayController.requestDisconnect(); 133 } 134 } 135 }); 136 } 137 138 public WifiDisplayStatus getWifiDisplayStatusLocked() { 139 if (mCurrentStatus == null) { 140 mCurrentStatus = new WifiDisplayStatus(mEnabled, mScanState, mActiveDisplayState, 141 mActiveDisplay, mKnownDisplays); 142 } 143 return mCurrentStatus; 144 } 145 146 private void handleConnectLocked(WifiDisplay display, 147 Surface surface, int width, int height, int flags) { 148 handleDisconnectLocked(); 149 150 int deviceFlags = 0; 151 if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) { 152 deviceFlags |= DisplayDeviceInfo.FLAG_SECURE; 153 } 154 155 float refreshRate = 60.0f; // TODO: get this for real 156 157 String name = display.getDeviceName(); 158 IBinder displayToken = Surface.createDisplay(name); 159 mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height, 160 refreshRate, deviceFlags, surface); 161 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED); 162 } 163 164 private void handleDisconnectLocked() { 165 if (mDisplayDevice != null) { 166 mDisplayDevice.clearSurfaceLocked(); 167 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED); 168 mDisplayDevice = null; 169 } 170 } 171 172 private void scheduleStatusChangedBroadcastLocked() { 173 if (!mPendingStatusChangeBroadcast) { 174 mPendingStatusChangeBroadcast = true; 175 getHandler().post(mStatusChangeBroadcast); 176 } 177 } 178 179 private final Runnable mStatusChangeBroadcast = new Runnable() { 180 @Override 181 public void run() { 182 final Intent intent; 183 synchronized (getSyncRoot()) { 184 if (!mPendingStatusChangeBroadcast) { 185 return; 186 } 187 188 mPendingStatusChangeBroadcast = false; 189 intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); 190 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 191 intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS, 192 getWifiDisplayStatusLocked()); 193 } 194 195 // Send protected broadcast about wifi display status to receivers that 196 // have the required permission. 197 getContext().sendBroadcast(intent, 198 android.Manifest.permission.CONFIGURE_WIFI_DISPLAY); 199 } 200 }; 201 202 private final WifiDisplayController.Listener mWifiDisplayListener = 203 new WifiDisplayController.Listener() { 204 @Override 205 public void onEnablementChanged(boolean enabled) { 206 synchronized (getSyncRoot()) { 207 if (mEnabled != enabled) { 208 mCurrentStatus = null; 209 mEnabled = enabled; 210 scheduleStatusChangedBroadcastLocked(); 211 } 212 } 213 } 214 215 @Override 216 public void onScanStarted() { 217 synchronized (getSyncRoot()) { 218 if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) { 219 mCurrentStatus = null; 220 mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING; 221 scheduleStatusChangedBroadcastLocked(); 222 } 223 } 224 } 225 226 public void onScanFinished(WifiDisplay[] knownDisplays) { 227 synchronized (getSyncRoot()) { 228 if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING 229 || !Arrays.equals(mKnownDisplays, knownDisplays)) { 230 mCurrentStatus = null; 231 mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING; 232 mKnownDisplays = knownDisplays; 233 scheduleStatusChangedBroadcastLocked(); 234 } 235 } 236 } 237 238 @Override 239 public void onDisplayConnecting(WifiDisplay display) { 240 synchronized (getSyncRoot()) { 241 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING 242 || mActiveDisplay == null 243 || !mActiveDisplay.equals(display)) { 244 mCurrentStatus = null; 245 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING; 246 mActiveDisplay = display; 247 scheduleStatusChangedBroadcastLocked(); 248 } 249 } 250 } 251 252 @Override 253 public void onDisplayConnectionFailed() { 254 synchronized (getSyncRoot()) { 255 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 256 || mActiveDisplay != null) { 257 mCurrentStatus = null; 258 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 259 mActiveDisplay = null; 260 scheduleStatusChangedBroadcastLocked(); 261 } 262 } 263 } 264 265 @Override 266 public void onDisplayConnected(WifiDisplay display, Surface surface, 267 int width, int height, int flags) { 268 synchronized (getSyncRoot()) { 269 handleConnectLocked(display, surface, width, height, flags); 270 271 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED 272 || mActiveDisplay == null 273 || !mActiveDisplay.equals(display)) { 274 mCurrentStatus = null; 275 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED; 276 mActiveDisplay = display; 277 scheduleStatusChangedBroadcastLocked(); 278 } 279 } 280 } 281 282 @Override 283 public void onDisplayDisconnected() { 284 // Stop listening. 285 synchronized (getSyncRoot()) { 286 handleDisconnectLocked(); 287 288 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 289 || mActiveDisplay != null) { 290 mCurrentStatus = null; 291 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 292 mActiveDisplay = null; 293 scheduleStatusChangedBroadcastLocked(); 294 } 295 } 296 } 297 }; 298 299 private final class WifiDisplayDevice extends DisplayDevice { 300 private final String mName; 301 private final int mWidth; 302 private final int mHeight; 303 private final float mRefreshRate; 304 private final int mFlags; 305 306 private Surface mSurface; 307 private DisplayDeviceInfo mInfo; 308 309 public WifiDisplayDevice(IBinder displayToken, String name, 310 int width, int height, float refreshRate, int flags, 311 Surface surface) { 312 super(WifiDisplayAdapter.this, displayToken); 313 mName = name; 314 mWidth = width; 315 mHeight = height; 316 mRefreshRate = refreshRate; 317 mFlags = flags; 318 mSurface = surface; 319 } 320 321 public void clearSurfaceLocked() { 322 mSurface = null; 323 sendTraversalRequestLocked(); 324 } 325 326 @Override 327 public void performTraversalInTransactionLocked() { 328 setSurfaceInTransactionLocked(mSurface); 329 } 330 331 @Override 332 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 333 if (mInfo == null) { 334 mInfo = new DisplayDeviceInfo(); 335 mInfo.name = mName; 336 mInfo.width = mWidth; 337 mInfo.height = mHeight; 338 mInfo.refreshRate = mRefreshRate; 339 mInfo.flags = mFlags; 340 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; 341 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight); 342 } 343 return mInfo; 344 } 345 } 346} 347