WifiDisplayAdapter.java revision 255dd04271088590fedc46c8e22b2fd4ab142d39
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.R; 20import com.android.internal.util.DumpUtils; 21import com.android.internal.util.IndentingPrintWriter; 22 23import android.app.Notification; 24import android.app.NotificationManager; 25import android.app.PendingIntent; 26import android.content.BroadcastReceiver; 27import android.content.Context; 28import android.content.Intent; 29import android.content.IntentFilter; 30import android.content.res.Resources; 31import android.hardware.display.DisplayManager; 32import android.hardware.display.WifiDisplay; 33import android.hardware.display.WifiDisplaySessionInfo; 34import android.hardware.display.WifiDisplayStatus; 35import android.media.RemoteDisplay; 36import android.os.Handler; 37import android.os.IBinder; 38import android.os.Looper; 39import android.os.Message; 40import android.os.UserHandle; 41import android.provider.Settings; 42import android.util.Slog; 43import android.view.Display; 44import android.view.Surface; 45import android.view.SurfaceControl; 46 47import java.io.PrintWriter; 48import java.util.Arrays; 49import java.util.List; 50import java.util.ArrayList; 51 52import 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 */ 68final 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 .setColor(r.getColor( 506 com.android.internal.R.color.system_notification_accent_color)) 507 .build(); 508 } else { 509 notification = new Notification.Builder(context) 510 .setContentTitle(r.getString( 511 R.string.wifi_display_notification_connected_title)) 512 .setContentText(r.getString( 513 R.string.wifi_display_notification_connected_message, 514 display.getFriendlyDisplayName())) 515 .setContentIntent(mSettingsPendingIntent) 516 .setSmallIcon(R.drawable.ic_notification_cast_on) 517 .setOngoing(true) 518 .addAction(android.R.drawable.ic_menu_close_clear_cancel, 519 r.getString(R.string.wifi_display_notification_disconnect), 520 mDisconnectPendingIntent) 521 .setColor(r.getColor( 522 com.android.internal.R.color.system_notification_accent_color)) 523 .build(); 524 } 525 mNotificationManager.notifyAsUser(null, 526 R.string.wifi_display_notification_disconnect, 527 notification, UserHandle.ALL); 528 } 529 } 530 531 private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() { 532 @Override 533 public void onReceive(Context context, Intent intent) { 534 if (intent.getAction().equals(ACTION_DISCONNECT)) { 535 synchronized (getSyncRoot()) { 536 requestDisconnectLocked(); 537 } 538 } 539 } 540 }; 541 542 private final WifiDisplayController.Listener mWifiDisplayListener = 543 new WifiDisplayController.Listener() { 544 @Override 545 public void onFeatureStateChanged(int featureState) { 546 synchronized (getSyncRoot()) { 547 if (mFeatureState != featureState) { 548 mFeatureState = featureState; 549 scheduleStatusChangedBroadcastLocked(); 550 } 551 } 552 } 553 554 @Override 555 public void onScanStarted() { 556 synchronized (getSyncRoot()) { 557 if (mScanState != WifiDisplayStatus.SCAN_STATE_SCANNING) { 558 mScanState = WifiDisplayStatus.SCAN_STATE_SCANNING; 559 scheduleStatusChangedBroadcastLocked(); 560 } 561 } 562 } 563 564 @Override 565 public void onScanResults(WifiDisplay[] availableDisplays) { 566 synchronized (getSyncRoot()) { 567 availableDisplays = mPersistentDataStore.applyWifiDisplayAliases( 568 availableDisplays); 569 570 boolean changed = !Arrays.equals(mAvailableDisplays, availableDisplays); 571 572 // Check whether any of the available displays changed canConnect status. 573 for (int i = 0; !changed && i<availableDisplays.length; i++) { 574 changed = availableDisplays[i].canConnect() 575 != mAvailableDisplays[i].canConnect(); 576 } 577 578 if (changed) { 579 mAvailableDisplays = availableDisplays; 580 fixRememberedDisplayNamesFromAvailableDisplaysLocked(); 581 updateDisplaysLocked(); 582 scheduleStatusChangedBroadcastLocked(); 583 } 584 } 585 } 586 587 @Override 588 public void onScanFinished() { 589 synchronized (getSyncRoot()) { 590 if (mScanState != WifiDisplayStatus.SCAN_STATE_NOT_SCANNING) { 591 mScanState = WifiDisplayStatus.SCAN_STATE_NOT_SCANNING; 592 scheduleStatusChangedBroadcastLocked(); 593 } 594 } 595 } 596 597 @Override 598 public void onDisplayConnecting(WifiDisplay display) { 599 synchronized (getSyncRoot()) { 600 display = mPersistentDataStore.applyWifiDisplayAlias(display); 601 602 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTING 603 || mActiveDisplay == null 604 || !mActiveDisplay.equals(display)) { 605 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTING; 606 mActiveDisplay = display; 607 scheduleStatusChangedBroadcastLocked(); 608 scheduleUpdateNotificationLocked(); 609 } 610 } 611 } 612 613 @Override 614 public void onDisplayConnectionFailed() { 615 synchronized (getSyncRoot()) { 616 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 617 || mActiveDisplay != null) { 618 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 619 mActiveDisplay = null; 620 scheduleStatusChangedBroadcastLocked(); 621 scheduleUpdateNotificationLocked(); 622 } 623 } 624 } 625 626 @Override 627 public void onDisplayConnected(WifiDisplay display, Surface surface, 628 int width, int height, int flags) { 629 synchronized (getSyncRoot()) { 630 display = mPersistentDataStore.applyWifiDisplayAlias(display); 631 addDisplayDeviceLocked(display, surface, width, height, flags); 632 633 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_CONNECTED 634 || mActiveDisplay == null 635 || !mActiveDisplay.equals(display)) { 636 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_CONNECTED; 637 mActiveDisplay = display; 638 scheduleStatusChangedBroadcastLocked(); 639 scheduleUpdateNotificationLocked(); 640 } 641 } 642 } 643 644 @Override 645 public void onDisplaySessionInfo(WifiDisplaySessionInfo sessionInfo) { 646 synchronized (getSyncRoot()) { 647 mSessionInfo = sessionInfo; 648 scheduleStatusChangedBroadcastLocked(); 649 } 650 } 651 652 @Override 653 public void onDisplayChanged(WifiDisplay display) { 654 synchronized (getSyncRoot()) { 655 display = mPersistentDataStore.applyWifiDisplayAlias(display); 656 if (mActiveDisplay != null 657 && mActiveDisplay.hasSameAddress(display) 658 && !mActiveDisplay.equals(display)) { 659 mActiveDisplay = display; 660 renameDisplayDeviceLocked(display.getFriendlyDisplayName()); 661 scheduleStatusChangedBroadcastLocked(); 662 scheduleUpdateNotificationLocked(); 663 } 664 } 665 } 666 667 @Override 668 public void onDisplayDisconnected() { 669 // Stop listening. 670 synchronized (getSyncRoot()) { 671 removeDisplayDeviceLocked(); 672 673 if (mActiveDisplayState != WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED 674 || mActiveDisplay != null) { 675 mActiveDisplayState = WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED; 676 mActiveDisplay = null; 677 scheduleStatusChangedBroadcastLocked(); 678 scheduleUpdateNotificationLocked(); 679 } 680 } 681 } 682 }; 683 684 private final class WifiDisplayDevice extends DisplayDevice { 685 private String mName; 686 private final int mWidth; 687 private final int mHeight; 688 private final float mRefreshRate; 689 private final int mFlags; 690 private final String mAddress; 691 692 private Surface mSurface; 693 private DisplayDeviceInfo mInfo; 694 695 public WifiDisplayDevice(IBinder displayToken, String name, 696 int width, int height, float refreshRate, int flags, String address, 697 Surface surface) { 698 super(WifiDisplayAdapter.this, displayToken); 699 mName = name; 700 mWidth = width; 701 mHeight = height; 702 mRefreshRate = refreshRate; 703 mFlags = flags; 704 mAddress = address; 705 mSurface = surface; 706 } 707 708 public void destroyLocked() { 709 if (mSurface != null) { 710 mSurface.release(); 711 mSurface = null; 712 } 713 SurfaceControl.destroyDisplay(getDisplayTokenLocked()); 714 } 715 716 public void setNameLocked(String name) { 717 mName = name; 718 mInfo = null; 719 } 720 721 @Override 722 public void performTraversalInTransactionLocked() { 723 if (mSurface != null) { 724 setSurfaceInTransactionLocked(mSurface); 725 } 726 } 727 728 @Override 729 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 730 if (mInfo == null) { 731 mInfo = new DisplayDeviceInfo(); 732 mInfo.name = mName; 733 mInfo.width = mWidth; 734 mInfo.height = mHeight; 735 mInfo.refreshRate = mRefreshRate; 736 mInfo.presentationDeadlineNanos = 1000000000L / (int) mRefreshRate; // 1 frame 737 mInfo.flags = mFlags; 738 mInfo.type = Display.TYPE_WIFI; 739 mInfo.address = mAddress; 740 mInfo.touch = DisplayDeviceInfo.TOUCH_EXTERNAL; 741 mInfo.setAssumedDensityForExternalDisplay(mWidth, mHeight); 742 } 743 return mInfo; 744 } 745 } 746 747 private final class WifiDisplayHandler extends Handler { 748 public WifiDisplayHandler(Looper looper) { 749 super(looper, null, true /*async*/); 750 } 751 752 @Override 753 public void handleMessage(Message msg) { 754 switch (msg.what) { 755 case MSG_SEND_STATUS_CHANGE_BROADCAST: 756 handleSendStatusChangeBroadcast(); 757 break; 758 759 case MSG_UPDATE_NOTIFICATION: 760 handleUpdateNotification(); 761 break; 762 } 763 } 764 } 765} 766