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 17 package com.android.server.display; 18 19 import android.app.BroadcastOptions; 20 import android.content.BroadcastReceiver; 21 import android.content.Context; 22 import android.content.Intent; 23 import android.content.IntentFilter; 24 import android.content.pm.PackageManager; 25 import android.hardware.display.DisplayManager; 26 import android.hardware.display.WifiDisplay; 27 import android.hardware.display.WifiDisplaySessionInfo; 28 import android.hardware.display.WifiDisplayStatus; 29 import android.media.RemoteDisplay; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.UserHandle; 35 import android.util.Slog; 36 import android.view.Display; 37 import android.view.DisplayAddress; 38 import android.view.DisplayShape; 39 import android.view.Surface; 40 import android.view.SurfaceControl; 41 42 import com.android.internal.util.DumpUtils; 43 import com.android.internal.util.IndentingPrintWriter; 44 45 import java.io.PrintWriter; 46 import java.util.ArrayList; 47 import java.util.Arrays; 48 import java.util.List; 49 import java.util.Objects; 50 51 /** 52 * Connects to Wifi displays that implement the Miracast protocol. 53 * <p> 54 * The Wifi display protocol relies on Wifi direct for discovering and pairing 55 * with the display. Once connected, the Media Server opens an RTSP socket and accepts 56 * a connection from the display. After session negotiation, the Media Server 57 * streams encoded buffers to the display. 58 * </p><p> 59 * This class is responsible for connecting to Wifi displays and mediating 60 * the interactions between Media Server, Surface Flinger and the Display Manager Service. 61 * </p><p> 62 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 63 * </p> 64 */ 65 final class WifiDisplayAdapter extends DisplayAdapter { 66 private static final String TAG = "WifiDisplayAdapter"; 67 68 private static final boolean DEBUG = false; 69 70 private static final int MSG_SEND_STATUS_CHANGE_BROADCAST = 1; 71 72 private static final String ACTION_DISCONNECT = "android.server.display.wfd.DISCONNECT"; 73 74 // Unique id prefix for wifi displays 75 private static final String DISPLAY_NAME_PREFIX = "wifi:"; 76 77 private final WifiDisplayHandler mHandler; 78 private final PersistentDataStore mPersistentDataStore; 79 private final boolean mSupportsProtectedBuffers; 80 81 private WifiDisplayController mDisplayController; 82 private WifiDisplayDevice mDisplayDevice; 83 84 private WifiDisplayStatus mCurrentStatus; 85 private int mFeatureState; 86 private int mScanState; 87 private int mActiveDisplayState; 88 private WifiDisplay mActiveDisplay; 89 private WifiDisplay[] mDisplays = WifiDisplay.EMPTY_ARRAY; 90 private WifiDisplay[] mAvailableDisplays = WifiDisplay.EMPTY_ARRAY; 91 private WifiDisplay[] mRememberedDisplays = WifiDisplay.EMPTY_ARRAY; 92 private WifiDisplaySessionInfo mSessionInfo; 93 94 private boolean mPendingStatusChangeBroadcast; 95 96 // Called with SyncRoot lock held. WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, PersistentDataStore persistentDataStore)97 public WifiDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 98 Context context, Handler handler, Listener listener, 99 PersistentDataStore persistentDataStore) { 100 super(syncRoot, context, handler, listener, TAG); 101 102 if (!context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_DIRECT)) { 103 throw new RuntimeException("WiFi display was requested, " 104 + "but there is no WiFi Direct feature"); 105 } 106 107 mHandler = new WifiDisplayHandler(handler.getLooper()); 108 mPersistentDataStore = persistentDataStore; 109 mSupportsProtectedBuffers = context.getResources().getBoolean( 110 com.android.internal.R.bool.config_wifiDisplaySupportsProtectedBuffers); 111 } 112 113 @Override dumpLocked(PrintWriter pw)114 public void dumpLocked(PrintWriter pw) { 115 super.dumpLocked(pw); 116 117 pw.println("mCurrentStatus=" + getWifiDisplayStatusLocked()); 118 pw.println("mFeatureState=" + mFeatureState); 119 pw.println("mScanState=" + mScanState); 120 pw.println("mActiveDisplayState=" + mActiveDisplayState); 121 pw.println("mActiveDisplay=" + mActiveDisplay); 122 pw.println("mDisplays=" + Arrays.toString(mDisplays)); 123 pw.println("mAvailableDisplays=" + Arrays.toString(mAvailableDisplays)); 124 pw.println("mRememberedDisplays=" + Arrays.toString(mRememberedDisplays)); 125 pw.println("mPendingStatusChangeBroadcast=" + mPendingStatusChangeBroadcast); 126 pw.println("mSupportsProtectedBuffers=" + mSupportsProtectedBuffers); 127 128 // Try to dump the controller state. 129 if (mDisplayController == null) { 130 pw.println("mDisplayController=null"); 131 } else { 132 pw.println("mDisplayController:"); 133 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, " "); 134 ipw.increaseIndent(); 135 DumpUtils.dumpAsync(getHandler(), mDisplayController, ipw, "", 200); 136 } 137 } 138 139 @Override registerLocked()140 public void registerLocked() { 141 super.registerLocked(); 142 143 updateRememberedDisplaysLocked(); 144 145 getHandler().post(new Runnable() { 146 @Override 147 public void run() { 148 mDisplayController = new WifiDisplayController( 149 getContext(), getHandler(), mWifiDisplayListener); 150 151 getContext().registerReceiverAsUser(mBroadcastReceiver, UserHandle.ALL, 152 new IntentFilter(ACTION_DISCONNECT), null, mHandler, 153 Context.RECEIVER_NOT_EXPORTED); 154 } 155 }); 156 } 157 requestStartScanLocked()158 public void requestStartScanLocked() { 159 if (DEBUG) { 160 Slog.d(TAG, "requestStartScanLocked"); 161 } 162 163 getHandler().post(new Runnable() { 164 @Override 165 public void run() { 166 if (mDisplayController != null) { 167 mDisplayController.requestStartScan(); 168 } 169 } 170 }); 171 } 172 requestStopScanLocked()173 public void requestStopScanLocked() { 174 if (DEBUG) { 175 Slog.d(TAG, "requestStopScanLocked"); 176 } 177 178 getHandler().post(new Runnable() { 179 @Override 180 public void run() { 181 if (mDisplayController != null) { 182 mDisplayController.requestStopScan(); 183 } 184 } 185 }); 186 } 187 requestConnectLocked(final String address)188 public void requestConnectLocked(final String address) { 189 if (DEBUG) { 190 Slog.d(TAG, "requestConnectLocked: address=" + address); 191 } 192 193 getHandler().post(new Runnable() { 194 @Override 195 public void run() { 196 if (mDisplayController != null) { 197 mDisplayController.requestConnect(address); 198 } 199 } 200 }); 201 } 202 requestPauseLocked()203 public void requestPauseLocked() { 204 if (DEBUG) { 205 Slog.d(TAG, "requestPauseLocked"); 206 } 207 208 getHandler().post(new Runnable() { 209 @Override 210 public void run() { 211 if (mDisplayController != null) { 212 mDisplayController.requestPause(); 213 } 214 } 215 }); 216 } 217 requestResumeLocked()218 public void requestResumeLocked() { 219 if (DEBUG) { 220 Slog.d(TAG, "requestResumeLocked"); 221 } 222 223 getHandler().post(new Runnable() { 224 @Override 225 public void run() { 226 if (mDisplayController != null) { 227 mDisplayController.requestResume(); 228 } 229 } 230 }); 231 } 232 requestDisconnectLocked()233 public void requestDisconnectLocked() { 234 if (DEBUG) { 235 Slog.d(TAG, "requestDisconnectedLocked"); 236 } 237 238 getHandler().post(new Runnable() { 239 @Override 240 public void run() { 241 if (mDisplayController != null) { 242 mDisplayController.requestDisconnect(); 243 } 244 } 245 }); 246 } 247 requestRenameLocked(String address, String alias)248 public void requestRenameLocked(String address, String alias) { 249 if (DEBUG) { 250 Slog.d(TAG, "requestRenameLocked: address=" + address + ", alias=" + alias); 251 } 252 253 if (alias != null) { 254 alias = alias.trim(); 255 if (alias.isEmpty() || alias.equals(address)) { 256 alias = null; 257 } 258 } 259 260 WifiDisplay display = mPersistentDataStore.getRememberedWifiDisplay(address); 261 if (display != null && !Objects.equals(display.getDeviceAlias(), alias)) { 262 display = new WifiDisplay(address, display.getDeviceName(), alias, 263 false, false, false); 264 if (mPersistentDataStore.rememberWifiDisplay(display)) { 265 mPersistentDataStore.saveIfNeeded(); 266 updateRememberedDisplaysLocked(); 267 scheduleStatusChangedBroadcastLocked(); 268 } 269 } 270 271 if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) { 272 renameDisplayDeviceLocked(mActiveDisplay.getFriendlyDisplayName()); 273 } 274 } 275 requestForgetLocked(String address)276 public void requestForgetLocked(String address) { 277 if (DEBUG) { 278 Slog.d(TAG, "requestForgetLocked: address=" + address); 279 } 280 281 if (mPersistentDataStore.forgetWifiDisplay(address)) { 282 mPersistentDataStore.saveIfNeeded(); 283 updateRememberedDisplaysLocked(); 284 scheduleStatusChangedBroadcastLocked(); 285 } 286 287 if (mActiveDisplay != null && mActiveDisplay.getDeviceAddress().equals(address)) { 288 requestDisconnectLocked(); 289 } 290 } 291 getWifiDisplayStatusLocked()292 public WifiDisplayStatus getWifiDisplayStatusLocked() { 293 if (mCurrentStatus == null) { 294 mCurrentStatus = new WifiDisplayStatus( 295 mFeatureState, mScanState, mActiveDisplayState, 296 mActiveDisplay, mDisplays, mSessionInfo); 297 } 298 299 if (DEBUG) { 300 Slog.d(TAG, "getWifiDisplayStatusLocked: result=" + mCurrentStatus); 301 } 302 return mCurrentStatus; 303 } 304 updateDisplaysLocked()305 private void updateDisplaysLocked() { 306 List<WifiDisplay> displays = new ArrayList<WifiDisplay>( 307 mAvailableDisplays.length + mRememberedDisplays.length); 308 boolean[] remembered = new boolean[mAvailableDisplays.length]; 309 for (WifiDisplay d : mRememberedDisplays) { 310 boolean available = false; 311 for (int i = 0; i < mAvailableDisplays.length; i++) { 312 if (d.equals(mAvailableDisplays[i])) { 313 remembered[i] = available = true; 314 break; 315 } 316 } 317 if (!available) { 318 displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(), 319 d.getDeviceAlias(), false, false, true)); 320 } 321 } 322 for (int i = 0; i < mAvailableDisplays.length; i++) { 323 WifiDisplay d = mAvailableDisplays[i]; 324 displays.add(new WifiDisplay(d.getDeviceAddress(), d.getDeviceName(), 325 d.getDeviceAlias(), true, d.canConnect(), remembered[i])); 326 } 327 mDisplays = displays.toArray(WifiDisplay.EMPTY_ARRAY); 328 } 329 updateRememberedDisplaysLocked()330 private void updateRememberedDisplaysLocked() { 331 mRememberedDisplays = mPersistentDataStore.getRememberedWifiDisplays(); 332 mActiveDisplay = mPersistentDataStore.applyWifiDisplayAlias(mActiveDisplay); 333 mAvailableDisplays = mPersistentDataStore.applyWifiDisplayAliases(mAvailableDisplays); 334 updateDisplaysLocked(); 335 } 336 fixRememberedDisplayNamesFromAvailableDisplaysLocked()337 private void fixRememberedDisplayNamesFromAvailableDisplaysLocked() { 338 // It may happen that a display name has changed since it was remembered. 339 // Consult the list of available displays and update the name if needed. 340 // We don't do anything special for the active display here. The display 341 // controller will send a separate event when it needs to be updates. 342 boolean changed = false; 343 for (int i = 0; i < mRememberedDisplays.length; i++) { 344 WifiDisplay rememberedDisplay = mRememberedDisplays[i]; 345 WifiDisplay availableDisplay = findAvailableDisplayLocked( 346 rememberedDisplay.getDeviceAddress()); 347 if (availableDisplay != null && !rememberedDisplay.equals(availableDisplay)) { 348 if (DEBUG) { 349 Slog.d(TAG, "fixRememberedDisplayNamesFromAvailableDisplaysLocked: " 350 + "updating remembered display to " + availableDisplay); 351 } 352 mRememberedDisplays[i] = availableDisplay; 353 changed |= mPersistentDataStore.rememberWifiDisplay(availableDisplay); 354 } 355 } 356 if (changed) { 357 mPersistentDataStore.saveIfNeeded(); 358 } 359 } 360 findAvailableDisplayLocked(String address)361 private WifiDisplay findAvailableDisplayLocked(String address) { 362 for (WifiDisplay display : mAvailableDisplays) { 363 if (display.getDeviceAddress().equals(address)) { 364 return display; 365 } 366 } 367 return null; 368 } 369 addDisplayDeviceLocked(WifiDisplay display, Surface surface, int width, int height, int flags)370 private void addDisplayDeviceLocked(WifiDisplay display, 371 Surface surface, int width, int height, int flags) { 372 removeDisplayDeviceLocked(); 373 374 if (mPersistentDataStore.rememberWifiDisplay(display)) { 375 mPersistentDataStore.saveIfNeeded(); 376 updateRememberedDisplaysLocked(); 377 scheduleStatusChangedBroadcastLocked(); 378 } 379 380 boolean secure = (flags & RemoteDisplay.DISPLAY_FLAG_SECURE) != 0; 381 int deviceFlags = DisplayDeviceInfo.FLAG_PRESENTATION; 382 if (secure) { 383 deviceFlags |= DisplayDeviceInfo.FLAG_SECURE; 384 if (mSupportsProtectedBuffers) { 385 deviceFlags |= DisplayDeviceInfo.FLAG_SUPPORTS_PROTECTED_BUFFERS; 386 } 387 } 388 389 float refreshRate = 60.0f; // TODO: get this for real 390 391 String name = display.getFriendlyDisplayName(); 392 String address = display.getDeviceAddress(); 393 IBinder displayToken = DisplayControl.createDisplay(name, secure); 394 mDisplayDevice = new WifiDisplayDevice(displayToken, name, width, height, 395 refreshRate, deviceFlags, address, surface); 396 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_ADDED); 397 } 398 removeDisplayDeviceLocked()399 private void removeDisplayDeviceLocked() { 400 if (mDisplayDevice != null) { 401 mDisplayDevice.destroyLocked(); 402 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_REMOVED); 403 mDisplayDevice = null; 404 } 405 } 406 renameDisplayDeviceLocked(String name)407 private void renameDisplayDeviceLocked(String name) { 408 if (mDisplayDevice != null && !mDisplayDevice.getNameLocked().equals(name)) { 409 mDisplayDevice.setNameLocked(name); 410 sendDisplayDeviceEventLocked(mDisplayDevice, DISPLAY_DEVICE_EVENT_CHANGED); 411 } 412 } 413 scheduleStatusChangedBroadcastLocked()414 private void scheduleStatusChangedBroadcastLocked() { 415 mCurrentStatus = null; 416 if (!mPendingStatusChangeBroadcast) { 417 mPendingStatusChangeBroadcast = true; 418 mHandler.sendEmptyMessage(MSG_SEND_STATUS_CHANGE_BROADCAST); 419 } 420 } 421 422 // Runs on the handler. handleSendStatusChangeBroadcast()423 private void handleSendStatusChangeBroadcast() { 424 final Intent intent; 425 final BroadcastOptions options; 426 synchronized (getSyncRoot()) { 427 if (!mPendingStatusChangeBroadcast) { 428 return; 429 } 430 431 mPendingStatusChangeBroadcast = false; 432 intent = new Intent(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED); 433 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 434 intent.putExtra(DisplayManager.EXTRA_WIFI_DISPLAY_STATUS, 435 getWifiDisplayStatusLocked()); 436 437 options = BroadcastOptions.makeBasic(); 438 options.setDeliveryGroupPolicy(BroadcastOptions.DELIVERY_GROUP_POLICY_MOST_RECENT); 439 } 440 441 // Send protected broadcast about wifi display status to registered receivers. 442 getContext().sendBroadcastAsUser(intent, UserHandle.ALL, null, options.toBundle()); 443 } 444 445 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 446 @Override 447 public void onReceive(Context context, Intent intent) { 448 if (intent.getAction().equals(ACTION_DISCONNECT)) { 449 synchronized (getSyncRoot()) { 450 requestDisconnectLocked(); 451 } 452 } 453 } 454 }; 455 456 private final WifiDisplayController.Listener mWifiDisplayListener = 457 new WifiDisplayController.Listener() { 458 @Override 459 public void onFeatureStateChanged(int featureState) { 460 synchronized (getSyncRoot()) { 461 if (mFeatureState != featureState) { 462 mFeatureState = featureState; 463 scheduleStatusChangedBroadcastLocked(); 464 } 465 } 466 } 467 468 @Override 469 public void onScanStarted() { 470 synchronized (getSyncRoot()) { 471 if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) { 472 mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING; 473 scheduleStatusChangedBroadcastLocked(); 474 } 475 } 476 } 477 478 @Override 479 public void onScanResults(WifiDisplay[] availableDisplays) { 480 synchronized (getSyncRoot()) { 481 availableDisplays = mPersistentDataStore.applyWifiDisplayAliases( 482 availableDisplays); 483 484 boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays); 485 486 // Check whether any of the available displays changed canConnect status. 487 for (int i = 0; !changed && i<availableDisplays.length; i++) { 488 changed = availableDisplays[i].canConnect() 489 != mAvailableDisplays[i].canConnect(); 490 } 491 492 if (changed) { 493 mAvailableDisplays = availableDisplays; 494 fixRememberedDisplayNamesFromAvailableDisplaysLocked(); 495 updateDisplaysLocked(); 496 scheduleStatusChangedBroadcastLocked(); 497 } 498 } 499 } 500 501 @Override 502 public void onScanFinished() { 503 synchronized (getSyncRoot()) { 504 if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING) { 505 mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING; 506 scheduleStatusChangedBroadcastLocked(); 507 } 508 } 509 } 510 511 @Override 512 public void onDisplayConnecting(WifiDisplay display) { 513 synchronized (getSyncRoot()) { 514 display = mPersistentDataStore.applyWifiDisplayAlias(display); 515 516 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING 517 || mActiveDisplay == null 518 || !mActiveDisplay.equals(display)) { 519 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING; 520 mActiveDisplay = display; 521 scheduleStatusChangedBroadcastLocked(); 522 } 523 } 524 } 525 526 @Override 527 public void onDisplayConnectionFailed() { 528 synchronized (getSyncRoot()) { 529 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 530 || mActiveDisplay != null) { 531 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 532 mActiveDisplay = null; 533 scheduleStatusChangedBroadcastLocked(); 534 } 535 } 536 } 537 538 @Override 539 public void onDisplayConnected(WifiDisplay display, Surface surface, 540 int width, int height, int flags) { 541 synchronized (getSyncRoot()) { 542 display = mPersistentDataStore.applyWifiDisplayAlias(display); 543 addDisplayDeviceLocked(display, surface, width, height, flags); 544 545 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED 546 || mActiveDisplay == null 547 || !mActiveDisplay.equals(display)) { 548 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED; 549 mActiveDisplay = display; 550 scheduleStatusChangedBroadcastLocked(); 551 } 552 } 553 } 554 555 @Override 556 public void onDisplaySessionInfo(WifiDisplaySessionInfo sessionInfo) { 557 synchronized (getSyncRoot()) { 558 mSessionInfo = sessionInfo; 559 scheduleStatusChangedBroadcastLocked(); 560 } 561 } 562 563 @Override 564 public void onDisplayChanged(WifiDisplay display) { 565 synchronized (getSyncRoot()) { 566 display = mPersistentDataStore.applyWifiDisplayAlias(display); 567 if (mActiveDisplay != null 568 && mActiveDisplay.hasSameAddress(display) 569 && !mActiveDisplay.equals(display)) { 570 mActiveDisplay = display; 571 renameDisplayDeviceLocked(display.getFriendlyDisplayName()); 572 scheduleStatusChangedBroadcastLocked(); 573 } 574 } 575 } 576 577 @Override 578 public void onDisplayDisconnected() { 579 // Stop listening. 580 synchronized (getSyncRoot()) { 581 removeDisplayDeviceLocked(); 582 583 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 584 || mActiveDisplay != null) { 585 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 586 mActiveDisplay = null; 587 scheduleStatusChangedBroadcastLocked(); 588 } 589 } 590 } 591 }; 592 593 private final class WifiDisplayDevice extends DisplayDevice { 594 private String mName; 595 private final int mWidth; 596 private final int mHeight; 597 private final float mRefreshRate; 598 private final int mFlags; 599 private final DisplayAddress mAddress; 600 private final Display.Mode mMode; 601 602 private Surface mSurface; 603 private DisplayDeviceInfo mInfo; 604 WifiDisplayDevice(IBinder displayToken, String name, int width, int height, float refreshRate, int flags, String address, Surface surface)605 public WifiDisplayDevice(IBinder displayToken, String name, 606 int width, int height, float refreshRate, int flags, String address, 607 Surface surface) { 608 super(WifiDisplayAdapter.this, displayToken, DISPLAY_NAME_PREFIX + address, 609 getContext()); 610 mName = name; 611 mWidth = width; 612 mHeight = height; 613 mRefreshRate = refreshRate; 614 mFlags = flags; 615 mAddress = DisplayAddress.fromMacAddress(address); 616 mSurface = surface; 617 mMode = createMode(width, height, refreshRate); 618 } 619 620 @Override hasStableUniqueId()621 public boolean hasStableUniqueId() { 622 return true; 623 } 624 destroyLocked()625 public void destroyLocked() { 626 if (mSurface != null) { 627 mSurface.release(); 628 mSurface = null; 629 } 630 DisplayControl.destroyDisplay(getDisplayTokenLocked()); 631 } 632 setNameLocked(String name)633 public void setNameLocked(String name) { 634 mName = name; 635 mInfo = null; 636 } 637 638 @Override performTraversalLocked(SurfaceControl.Transaction t)639 public void performTraversalLocked(SurfaceControl.Transaction t) { 640 if (mSurface != null) { 641 setSurfaceLocked(t, mSurface); 642 } 643 } 644 645 @Override getDisplayDeviceInfoLocked()646 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 647 if (mInfo == null) { 648 mInfo = new DisplayDeviceInfo(); 649 mInfo.name = mName; 650 mInfo.uniqueId = getUniqueId(); 651 mInfo.width = mWidth; 652 mInfo.height = mHeight; 653 mInfo.modeId = mMode.getModeId(); 654 mInfo.renderFrameRate = mMode.getRefreshRate(); 655 mInfo.defaultModeId = mMode.getModeId(); 656 mInfo.supportedModes = new Display.Mode[] { mMode }; 657 mInfo.presentationDeadlineNanos = 1000000000L / (int) mRefreshRate; // 1 frame 658 mInfo.flags = mFlags; 659 mInfo.type = Display.TYPE_WIFI; 660 mInfo.address = mAddress; 661 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; 662 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight); 663 // The display is trusted since it is created by system. 664 mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED; 665 mInfo.displayShape = 666 DisplayShape.createDefaultDisplayShape(mInfo.width, mInfo.height, false); 667 } 668 return mInfo; 669 } 670 } 671 672 private final class WifiDisplayHandler extends Handler { WifiDisplayHandler(Looper looper)673 public WifiDisplayHandler(Looper looper) { 674 super(looper, null, true /*async*/); 675 } 676 677 @Override handleMessage(Message msg)678 public void handleMessage(Message msg) { 679 switch (msg.what) { 680 case MSG_SEND_STATUS_CHANGE_BROADCAST: 681 handleSendStatusChangeBroadcast(); 682 break; 683 } 684 } 685 } 686 } 687