WifiDisplayAdapter.java revision e08ae388d63c4db8f9d9a7ecd634f9a51f6e91b9
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.util.Slog; 31import android.view.Surface; 32 33import java.io.PrintWriter; 34import java.util.Arrays; 35 36/** 37 * Connects to Wifi displays that implement the Miracast protocol. 38 * <p> 39 * The Wifi display protocol relies on Wifi direct for discovering and pairing 40 * with the display. Once connected, the Media Server opens an RTSP socket and accepts 41 * a connection from the display. After session negotiation, the Media Server 42 * streams encoded buffers to the display. 43 * </p><p> 44 * This class is responsible for connecting to Wifi displays and mediating 45 * the interactions between Media Server, Surface Flinger and the Display Manager Service. 46 * </p><p> 47 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 48 * </p> 49 */ 50final class WifiDisplayAdapter extends DisplayAdapter { 51 private static final String TAG = "WifiDisplayAdapter"; 52 53 private WifiDisplayHandle mDisplayHandle; 54 private WifiDisplayController mDisplayController; 55 56 private WifiDisplayStatus mCurrentStatus; 57 private boolean mEnabled; 58 private WifiDisplay mConnectedDisplay; 59 private WifiDisplay[] mKnownDisplays = WifiDisplay.EMPTY_ARRAY; 60 private boolean mScanInProgress; 61 private boolean mConnectionInProgress; 62 63 private boolean mPendingStatusChangeBroadcast; 64 65 public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 66 Context context, Handler handler, Listener listener) { 67 super(syncRoot, context, handler, listener, TAG); 68 } 69 70 @Override 71 public void dumpLocked(PrintWriter pw) { 72 super.dumpLocked(pw); 73 74 if (mDisplayHandle == null) { 75 pw.println("mDisplayHandle=null"); 76 } else { 77 pw.println("mDisplayHandle:"); 78 mDisplayHandle.dumpLocked(pw); 79 } 80 81 pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked()); 82 pw.println("mEnabled=" + mEnabled); 83 pw.println("mConnectedDisplay=" + mConnectedDisplay); 84 pw.println("mKnownDisplays=" + Arrays.toString(mKnownDisplays)); 85 pw.println("mScanInProgress=" + mScanInProgress); 86 pw.println("mConnectionInProgress=" + mConnectionInProgress); 87 pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast); 88 89 // Try to dump the controller state. 90 if (mDisplayController == null) { 91 pw.println("mDisplayController=null"); 92 } else { 93 pw.println("mDisplayController:"); 94 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 95 ipw.increaseIndent(); 96 DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200); 97 } 98 } 99 100 @Override 101 public void registerLocked() { 102 super.registerLocked(); 103 104 getHandler().post(new Runnable() { 105 @Override 106 public void run() { 107 mDisplayController = new WifiDisplayController( 108 getContext(), getHandler(), mWifiDisplayListener); 109 } 110 }); 111 } 112 113 public void requestScanLocked() { 114 getHandler().post(new Runnable() { 115 @Override 116 public void run() { 117 if (mDisplayController != null) { 118 mDisplayController.requestScan(); 119 } 120 } 121 }); 122 } 123 124 public void requestConnectLocked(final String address) { 125 getHandler().post(new Runnable() { 126 @Override 127 public void run() { 128 if (mDisplayController != null) { 129 mDisplayController.requestConnect(address); 130 } 131 } 132 }); 133 } 134 135 public void requestDisconnectLocked() { 136 getHandler().post(new Runnable() { 137 @Override 138 public void run() { 139 if (mDisplayController != null) { 140 mDisplayController.requestDisconnect(); 141 } 142 } 143 }); 144 } 145 146 public WifiDisplayStatus getWifiDisplayStatusLocked() { 147 if (mCurrentStatus == null) { 148 mCurrentStatus = new WifiDisplayStatus(mEnabled, 149 mConnectedDisplay, mKnownDisplays, 150 mScanInProgress, mConnectionInProgress); 151 } 152 return mCurrentStatus; 153 } 154 155 private void handleConnectLocked(WifiDisplay display, String iface) { 156 handleDisconnectLocked(); 157 158 mDisplayHandle = new WifiDisplayHandle(display.getDeviceName(), iface); 159 } 160 161 private void handleDisconnectLocked() { 162 if (mDisplayHandle != null) { 163 mDisplayHandle.disposeLocked(); 164 mDisplayHandle = null; 165 } 166 } 167 168 private void scheduleStatusChangedBroadcastLocked() { 169 if (!mPendingStatusChangeBroadcast) { 170 mPendingStatusChangeBroadcast = true; 171 getHandler().post(mStatusChangeBroadcast); 172 } 173 } 174 175 private final Runnable mStatusChangeBroadcast = new Runnable() { 176 @Override 177 public void run() { 178 final Intent intent; 179 synchronized (getSyncRoot()) { 180 if (!mPendingStatusChangeBroadcast) { 181 return; 182 } 183 184 mPendingStatusChangeBroadcast = false; 185 intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); 186 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 187 intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS, 188 getWifiDisplayStatusLocked()); 189 } 190 191 // Send protected broadcast about wifi display status to receivers that 192 // have the required permission. 193 getContext().sendBroadcast(intent, 194 android.Manifest.permission.CONFIGURE_WIFI_DISPLAY); 195 } 196 }; 197 198 private final WifiDisplayController.Listener mWifiDisplayListener = 199 new WifiDisplayController.Listener() { 200 @Override 201 public void onEnablementChanged(boolean enabled) { 202 synchronized (getSyncRoot()) { 203 if (mEnabled != enabled) { 204 mCurrentStatus = null; 205 mEnabled = enabled; 206 scheduleStatusChangedBroadcastLocked(); 207 } 208 } 209 } 210 211 @Override 212 public void onScanStarted() { 213 synchronized (getSyncRoot()) { 214 if (!mScanInProgress) { 215 mCurrentStatus = null; 216 mScanInProgress = true; 217 scheduleStatusChangedBroadcastLocked(); 218 } 219 } 220 } 221 222 public void onScanFinished(WifiDisplay[] knownDisplays) { 223 synchronized (getSyncRoot()) { 224 if (!Arrays.equals(mKnownDisplays, knownDisplays) || mScanInProgress) { 225 mCurrentStatus = null; 226 mKnownDisplays = knownDisplays; 227 mScanInProgress = false; 228 scheduleStatusChangedBroadcastLocked(); 229 } 230 } 231 } 232 233 @Override 234 public void onDisplayConnecting(WifiDisplay display) { 235 synchronized (getSyncRoot()) { 236 if (!mConnectionInProgress) { 237 mCurrentStatus = null; 238 mConnectionInProgress = true; 239 scheduleStatusChangedBroadcastLocked(); 240 } 241 } 242 } 243 244 @Override 245 public void onDisplayConnectionFailed() { 246 synchronized (getSyncRoot()) { 247 if (mConnectionInProgress) { 248 mCurrentStatus = null; 249 mConnectionInProgress = false; 250 scheduleStatusChangedBroadcastLocked(); 251 } 252 } 253 } 254 255 @Override 256 public void onDisplayConnected(WifiDisplay display, String iface) { 257 synchronized (getSyncRoot()) { 258 handleConnectLocked(display, iface); 259 260 if (mConnectedDisplay == null || !mConnectedDisplay.equals(display) 261 || mConnectionInProgress) { 262 mCurrentStatus = null; 263 mConnectedDisplay = display; 264 mConnectionInProgress = false; 265 scheduleStatusChangedBroadcastLocked(); 266 } 267 } 268 } 269 270 @Override 271 public void onDisplayDisconnected() { 272 // Stop listening. 273 synchronized (getSyncRoot()) { 274 handleDisconnectLocked(); 275 276 if (mConnectedDisplay != null || mConnectionInProgress) { 277 mCurrentStatus = null; 278 mConnectedDisplay = null; 279 mConnectionInProgress = false; 280 scheduleStatusChangedBroadcastLocked(); 281 } 282 } 283 } 284 }; 285 286 private final class WifiDisplayDevice extends DisplayDevice { 287 private final String mName; 288 private final int mWidth; 289 private final int mHeight; 290 private final float mRefreshRate; 291 private final int mFlags; 292 293 private Surface mSurface; 294 private DisplayDeviceInfo mInfo; 295 296 public WifiDisplayDevice(IBinder displayToken, String name, 297 int width, int height, float refreshRate, int flags, 298 Surface surface) { 299 super(WifiDisplayAdapter.this, displayToken); 300 mName = name; 301 mWidth = width; 302 mHeight = height; 303 mRefreshRate = refreshRate; 304 mFlags = flags; 305 mSurface = surface; 306 } 307 308 public void clearSurfaceLocked() { 309 mSurface = null; 310 sendTraversalRequestLocked(); 311 } 312 313 @Override 314 public void performTraversalInTransactionLocked() { 315 setSurfaceInTransactionLocked(mSurface); 316 } 317 318 @Override 319 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 320 if (mInfo == null) { 321 mInfo = new DisplayDeviceInfo(); 322 mInfo.name = mName; 323 mInfo.width = mWidth; 324 mInfo.height = mHeight; 325 mInfo.refreshRate = mRefreshRate; 326 mInfo.flags = mFlags; 327 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight); 328 } 329 return mInfo; 330 } 331 } 332 333 private final class WifiDisplayHandle implements RemoteDisplay.Listener { 334 private final String mName; 335 private final String mIface; 336 private final RemoteDisplay mRemoteDisplay; 337 338 private WifiDisplayDevice mDevice; 339 private int mLastError; 340 341 public WifiDisplayHandle(String name, String iface) { 342 mName = name; 343 mIface = iface; 344 mRemoteDisplay = RemoteDisplay.listen(iface, this, getHandler()); 345 346 Slog.i(TAG, "Listening for Wifi display connections on " + iface 347 + " from " + mName); 348 } 349 350 public void disposeLocked() { 351 Slog.i(TAG, "Stopped listening for Wifi display connections on " + mIface 352 + " from " + mName); 353 354 removeDisplayLocked(); 355 mRemoteDisplay.dispose(); 356 } 357 358 public void dumpLocked(PrintWriter pw) { 359 pw.println(" " + mName + ": " + (mDevice != null ? "connected" : "disconnected")); 360 pw.println(" mIface=" + mIface); 361 pw.println(" mLastError=" + mLastError); 362 } 363 364 // Called on the handler thread. 365 @Override 366 public void onDisplayConnected(Surface surface, int width, int height, int flags) { 367 synchronized (getSyncRoot()) { 368 mLastError = 0; 369 removeDisplayLocked(); 370 addDisplayLocked(surface, width, height, flags); 371 372 Slog.i(TAG, "Wifi display connected: " + mName); 373 } 374 } 375 376 // Called on the handler thread. 377 @Override 378 public void onDisplayDisconnected() { 379 synchronized (getSyncRoot()) { 380 mLastError = 0; 381 removeDisplayLocked(); 382 383 Slog.i(TAG, "Wifi display disconnected: " + mName); 384 } 385 } 386 387 // Called on the handler thread. 388 @Override 389 public void onDisplayError(int error) { 390 synchronized (getSyncRoot()) { 391 mLastError = error; 392 removeDisplayLocked(); 393 394 Slog.i(TAG, "Wifi display disconnected due to error " + error + ": " + mName); 395 } 396 } 397 398 private void addDisplayLocked(Surface surface, int width, int height, int flags) { 399 int deviceFlags = 0; 400 if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) { 401 deviceFlags |= DisplayDeviceInfo.FLAG_SECURE; 402 } 403 404 float refreshRate = 60.0f; // TODO: get this for real 405 406 IBinder displayToken = Surface.createDisplay(mName); 407 mDevice = new WifiDisplayDevice(displayToken, mName, width, height, 408 refreshRate, deviceFlags, surface); 409 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED); 410 } 411 412 private void removeDisplayLocked() { 413 if (mDevice != null) { 414 mDevice.clearSurfaceLocked(); 415 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED); 416 mDevice = null; 417 } 418 } 419 } 420} 421