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