WifiDisplayAdapter.java revision ee4f029ff4905abbdbc1ce47db2cf2c3510949e9
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 static final boolean DEBUG = false; 54 55 private final PersistentDataStore mPersistentDataStore; 56 private final boolean mSupportsProtectedBuffers; 57 58 private WifiDisplayController mDisplayController; 59 private WifiDisplayDevice mDisplayDevice; 60 61 private WifiDisplayStatus mCurrentStatus; 62 private int mFeatureState; 63 private int mScanState; 64 private int mActiveDisplayState; 65 private WifiDisplay mActiveDisplay; 66 private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY; 67 private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY; 68 69 private boolean mPendingStatusChangeBroadcast; 70 71 public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 72 Context context, Handler handler, Listener listener, 73 PersistentDataStore persistentDataStore) { 74 super(syncRoot, context, handler, listener, TAG); 75 mPersistentDataStore = persistentDataStore; 76 mSupportsProtectedBuffers = context.getResources().getBoolean( 77 com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers); 78 } 79 80 @Override 81 public void dumpLocked(PrintWriter pw) { 82 super.dumpLocked(pw); 83 84 pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked()); 85 pw.println("mFeatureState=" + mFeatureState); 86 pw.println("mScanState=" + mScanState); 87 pw.println("mActiveDisplayState=" + mActiveDisplayState); 88 pw.println("mActiveDisplay=" + mActiveDisplay); 89 pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays)); 90 pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays)); 91 pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast); 92 pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers); 93 94 // Try to dump the controller state. 95 if (mDisplayController == null) { 96 pw.println("mDisplayController=null"); 97 } else { 98 pw.println("mDisplayController:"); 99 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 100 ipw.increaseIndent(); 101 DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, 200); 102 } 103 } 104 105 @Override 106 public void registerLocked() { 107 super.registerLocked(); 108 109 updateRememberedDisplaysLocked(); 110 111 getHandler().post(new Runnable() { 112 @Override 113 public void run() { 114 mDisplayController = new WifiDisplayController( 115 getContext(), getHandler(), mWifiDisplayListener); 116 } 117 }); 118 } 119 120 public void requestScanLocked() { 121 if (DEBUG) { 122 Slog.d(TAG, "requestScanLocked"); 123 } 124 125 getHandler().post(new Runnable() { 126 @Override 127 public void run() { 128 if (mDisplayController != null) { 129 mDisplayController.requestScan(); 130 } 131 } 132 }); 133 } 134 135 public void requestConnectLocked(final String address, final boolean trusted) { 136 if (DEBUG) { 137 Slog.d(TAG, "requestConnectLocked: address=" + address + ", trusted=" + trusted); 138 } 139 140 if (!trusted) { 141 synchronized (getSyncRoot()) { 142 if (!isRememberedDisplayLocked(address)) { 143 Slog.w(TAG, "Ignoring request by an untrusted client to connect to " 144 + "an unknown wifi display: " + address); 145 return; 146 } 147 } 148 } 149 150 getHandler().post(new Runnable() { 151 @Override 152 public void run() { 153 if (mDisplayController != null) { 154 mDisplayController.requestConnect(address); 155 } 156 } 157 }); 158 } 159 160 private boolean isRememberedDisplayLocked(String address) { 161 for (WifiDisplay display : mRememberedDisplays) { 162 if (display.getDeviceAddress().equals(address)) { 163 return true; 164 } 165 } 166 return false; 167 } 168 169 public void requestDisconnectLocked() { 170 if (DEBUG) { 171 Slog.d(TAG, "requestDisconnectedLocked"); 172 } 173 174 getHandler().post(new Runnable() { 175 @Override 176 public void run() { 177 if (mDisplayController != null) { 178 mDisplayController.requestDisconnect(); 179 } 180 } 181 }); 182 } 183 184 public void requestRenameLocked(String address, String alias) { 185 if (DEBUG) { 186 Slog.d(TAG, "requestRenameLocked: address=" + address + ", alias=" + alias); 187 } 188 189 if (alias != null) { 190 alias = alias.trim(); 191 if (alias.isEmpty() || alias.equals(address)) { 192 alias = null; 193 } 194 } 195 196 if (mPersistentDataStore.renameWifiDisplay(address, alias)) { 197 mPersistentDataStore.saveIfNeeded(); 198 updateRememberedDisplaysLocked(); 199 scheduleStatusChangedBroadcastLocked(); 200 } 201 202 if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address) 203 && mDisplayDevice != null) { 204 mDisplayDevice.setNameLocked(mActiveDisplay.getFriendlyDisplayName()); 205 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED); 206 } 207 } 208 209 public void requestForgetLocked(String address) { 210 if (DEBUG) { 211 Slog.d(TAG, "requestForgetLocked: address=" + address); 212 } 213 214 if (mPersistentDataStore.forgetWifiDisplay(address)) { 215 mPersistentDataStore.saveIfNeeded(); 216 updateRememberedDisplaysLocked(); 217 scheduleStatusChangedBroadcastLocked(); 218 } 219 220 if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) { 221 requestDisconnectLocked(); 222 } 223 } 224 225 public WifiDisplayStatus getWifiDisplayStatusLocked() { 226 if (mCurrentStatus == null) { 227 mCurrentStatus = new WifiDisplayStatus( 228 mFeatureState, mScanState, mActiveDisplayState, 229 mActiveDisplay, mAvailableDisplays, mRememberedDisplays); 230 } 231 232 if (DEBUG) { 233 Slog.d(TAG, "getWifiDisplayStatusLocked: result=" + mCurrentStatus); 234 } 235 return mCurrentStatus; 236 } 237 238 private void updateRememberedDisplaysLocked() { 239 mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays(); 240 mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay); 241 mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays); 242 } 243 244 private void handleConnectLocked(WifiDisplay display, 245 Surface surface, int width, int height, int flags) { 246 handleDisconnectLocked(); 247 248 if (mPersistentDataStore.rememberWifiDisplay(display)) { 249 mPersistentDataStore.saveIfNeeded(); 250 updateRememberedDisplaysLocked(); 251 scheduleStatusChangedBroadcastLocked(); 252 } 253 254 int deviceFlags = 0; 255 if ((flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0) { 256 deviceFlags |= DisplayDeviceInfo.FLAG_SECURE; 257 } 258 if (mSupportsProtectedBuffers) { 259 deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS; 260 } 261 262 float refreshRate = 60.0f; // TODO: get this for real 263 264 String name = display.getFriendlyDisplayName(); 265 IBinder displayToken = Surface.createDisplay(name); 266 mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height, 267 refreshRate, deviceFlags, surface); 268 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED); 269 } 270 271 private void handleDisconnectLocked() { 272 if (mDisplayDevice != null) { 273 mDisplayDevice.clearSurfaceLocked(); 274 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED); 275 mDisplayDevice = null; 276 } 277 } 278 279 private void scheduleStatusChangedBroadcastLocked() { 280 mCurrentStatus = null; 281 if (!mPendingStatusChangeBroadcast) { 282 mPendingStatusChangeBroadcast = true; 283 getHandler().post(mStatusChangeBroadcast); 284 } 285 } 286 287 private final Runnable mStatusChangeBroadcast = new Runnable() { 288 @Override 289 public void run() { 290 final Intent intent; 291 synchronized (getSyncRoot()) { 292 if (!mPendingStatusChangeBroadcast) { 293 return; 294 } 295 296 mPendingStatusChangeBroadcast = false; 297 intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); 298 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 299 intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS, 300 getWifiDisplayStatusLocked()); 301 } 302 303 // Send protected broadcast about wifi display status to registered receivers. 304 getContext().sendBroadcast(intent); 305 } 306 }; 307 308 private final WifiDisplayController.Listener mWifiDisplayListener = 309 new WifiDisplayController.Listener() { 310 @Override 311 public void onFeatureStateChanged(int featureState) { 312 synchronized (getSyncRoot()) { 313 if (mFeatureState != featureState) { 314 mFeatureState = featureState; 315 scheduleStatusChangedBroadcastLocked(); 316 } 317 } 318 } 319 320 @Override 321 public void onScanStarted() { 322 synchronized (getSyncRoot()) { 323 if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) { 324 mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING; 325 scheduleStatusChangedBroadcastLocked(); 326 } 327 } 328 } 329 330 @Override 331 public void onScanFinished(WifiDisplay[] availableDisplays) { 332 synchronized (getSyncRoot()) { 333 availableDisplays = mPersistentDataStore.applyWifiDisplayAliases( 334 availableDisplays); 335 336 if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING 337 || !Arrays.equals(mAvailableDisplays, availableDisplays)) { 338 mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING; 339 mAvailableDisplays = availableDisplays; 340 scheduleStatusChangedBroadcastLocked(); 341 } 342 } 343 } 344 345 @Override 346 public void onDisplayConnecting(WifiDisplay display) { 347 synchronized (getSyncRoot()) { 348 display = mPersistentDataStore.applyWifiDisplayAlias(display); 349 350 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING 351 || mActiveDisplay == null 352 || !mActiveDisplay.equals(display)) { 353 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING; 354 mActiveDisplay = display; 355 scheduleStatusChangedBroadcastLocked(); 356 } 357 } 358 } 359 360 @Override 361 public void onDisplayConnectionFailed() { 362 synchronized (getSyncRoot()) { 363 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 364 || mActiveDisplay != null) { 365 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 366 mActiveDisplay = null; 367 scheduleStatusChangedBroadcastLocked(); 368 } 369 } 370 } 371 372 @Override 373 public void onDisplayConnected(WifiDisplay display, Surface surface, 374 int width, int height, int flags) { 375 synchronized (getSyncRoot()) { 376 display = mPersistentDataStore.applyWifiDisplayAlias(display); 377 handleConnectLocked(display, surface, width, height, flags); 378 379 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED 380 || mActiveDisplay == null 381 || !mActiveDisplay.equals(display)) { 382 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED; 383 mActiveDisplay = display; 384 scheduleStatusChangedBroadcastLocked(); 385 } 386 } 387 } 388 389 @Override 390 public void onDisplayDisconnected() { 391 // Stop listening. 392 synchronized (getSyncRoot()) { 393 handleDisconnectLocked(); 394 395 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 396 || mActiveDisplay != null) { 397 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 398 mActiveDisplay = null; 399 scheduleStatusChangedBroadcastLocked(); 400 } 401 } 402 } 403 }; 404 405 private final class WifiDisplayDevice extends DisplayDevice { 406 private String mName; 407 private final int mWidth; 408 private final int mHeight; 409 private final float mRefreshRate; 410 private final int mFlags; 411 412 private Surface mSurface; 413 private DisplayDeviceInfo mInfo; 414 415 public WifiDisplayDevice(IBinder displayToken, String name, 416 int width, int height, float refreshRate, int flags, 417 Surface surface) { 418 super(WifiDisplayAdapter.this, displayToken); 419 mName = name; 420 mWidth = width; 421 mHeight = height; 422 mRefreshRate = refreshRate; 423 mFlags = flags; 424 mSurface = surface; 425 } 426 427 public void clearSurfaceLocked() { 428 mSurface = null; 429 sendTraversalRequestLocked(); 430 } 431 432 public void setNameLocked(String name) { 433 mName = name; 434 mInfo = null; 435 } 436 437 @Override 438 public void performTraversalInTransactionLocked() { 439 setSurfaceInTransactionLocked(mSurface); 440 } 441 442 @Override 443 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 444 if (mInfo == null) { 445 mInfo = new DisplayDeviceInfo(); 446 mInfo.name = mName; 447 mInfo.width = mWidth; 448 mInfo.height = mHeight; 449 mInfo.refreshRate = mRefreshRate; 450 mInfo.flags = mFlags; 451 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; 452 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight); 453 } 454 return mInfo; 455 } 456 } 457} 458