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 android.hardware.display; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.SystemApi; 22 import android.annotation.SystemService; 23 import android.content.Context; 24 import android.graphics.Point; 25 import android.media.projection.MediaProjection; 26 import android.os.Handler; 27 import android.util.SparseArray; 28 import android.view.Display; 29 import android.view.Surface; 30 import android.view.WindowManagerPolicy; 31 32 import java.util.ArrayList; 33 34 /** 35 * Manages the properties of attached displays. 36 */ 37 @SystemService(Context.DISPLAY_SERVICE) 38 public final class DisplayManager { 39 private static final String TAG = "DisplayManager"; 40 private static final boolean DEBUG = false; 41 42 private final Context mContext; 43 private final DisplayManagerGlobal mGlobal; 44 45 private final Object mLock = new Object(); 46 private final SparseArray<Display> mDisplays = new SparseArray<Display>(); 47 48 private final ArrayList<Display> mTempDisplays = new ArrayList<Display>(); 49 50 /** 51 * Broadcast receiver that indicates when the Wifi display status changes. 52 * <p> 53 * The status is provided as a {@link WifiDisplayStatus} object in the 54 * {@link #EXTRA_WIFI_DISPLAY_STATUS} extra. 55 * </p><p> 56 * This broadcast is only sent to registered receivers and can only be sent by the system. 57 * </p> 58 * @hide 59 */ 60 public static final String ACTION_WIFI_DISPLAY_STATUS_CHANGED = 61 "android.hardware.display.action.WIFI_DISPLAY_STATUS_CHANGED"; 62 63 /** 64 * Contains a {@link WifiDisplayStatus} object. 65 * @hide 66 */ 67 public static final String EXTRA_WIFI_DISPLAY_STATUS = 68 "android.hardware.display.extra.WIFI_DISPLAY_STATUS"; 69 70 /** 71 * Display category: Presentation displays. 72 * <p> 73 * This category can be used to identify secondary displays that are suitable for 74 * use as presentation displays such as HDMI or Wireless displays. Applications 75 * may automatically project their content to presentation displays to provide 76 * richer second screen experiences. 77 * </p> 78 * 79 * @see android.app.Presentation 80 * @see Display#FLAG_PRESENTATION 81 * @see #getDisplays(String) 82 */ 83 public static final String DISPLAY_CATEGORY_PRESENTATION = 84 "android.hardware.display.category.PRESENTATION"; 85 86 /** 87 * Virtual display flag: Create a public display. 88 * 89 * <h3>Public virtual displays</h3> 90 * <p> 91 * When this flag is set, the virtual display is public. 92 * </p><p> 93 * A public virtual display behaves just like most any other display that is connected 94 * to the system such as an HDMI or Wireless display. Applications can open 95 * windows on the display and the system may mirror the contents of other displays 96 * onto it. 97 * </p><p> 98 * Creating a public virtual display that isn't restricted to own-content only implicitly 99 * creates an auto-mirroring display. See {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} for 100 * restrictions on who is allowed to create an auto-mirroring display. 101 * </p> 102 * 103 * <h3>Private virtual displays</h3> 104 * <p> 105 * When this flag is not set, the virtual display is private as defined by the 106 * {@link Display#FLAG_PRIVATE} display flag. 107 * </p> 108 * 109 * <p> 110 * A private virtual display belongs to the application that created it. Only the a owner of a 111 * private virtual display and the apps that are already on that display are allowed to place 112 * windows upon it. The private virtual display also does not participate in display mirroring: 113 * it will neither receive mirrored content from another display nor allow its own content to be 114 * mirrored elsewhere. More precisely, the only processes that are allowed to enumerate or 115 * interact with the private display are those that have the same UID as the application that 116 * originally created the private virtual display or as the activities that are already on that 117 * display. 118 * </p> 119 * 120 * @see #createVirtualDisplay 121 * @see #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY 122 * @see #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR 123 */ 124 public static final int VIRTUAL_DISPLAY_FLAG_PUBLIC = 1 << 0; 125 126 /** 127 * Virtual display flag: Create a presentation display. 128 * 129 * <h3>Presentation virtual displays</h3> 130 * <p> 131 * When this flag is set, the virtual display is registered as a presentation 132 * display in the {@link #DISPLAY_CATEGORY_PRESENTATION presentation display category}. 133 * Applications may automatically project their content to presentation displays 134 * to provide richer second screen experiences. 135 * </p> 136 * 137 * <h3>Non-presentation virtual displays</h3> 138 * <p> 139 * When this flag is not set, the virtual display is not registered as a presentation 140 * display. Applications can still project their content on the display but they 141 * will typically not do so automatically. This option is appropriate for 142 * more special-purpose displays. 143 * </p> 144 * 145 * @see android.app.Presentation 146 * @see #createVirtualDisplay 147 * @see #DISPLAY_CATEGORY_PRESENTATION 148 * @see Display#FLAG_PRESENTATION 149 */ 150 public static final int VIRTUAL_DISPLAY_FLAG_PRESENTATION = 1 << 1; 151 152 /** 153 * Virtual display flag: Create a secure display. 154 * 155 * <h3>Secure virtual displays</h3> 156 * <p> 157 * When this flag is set, the virtual display is considered secure as defined 158 * by the {@link Display#FLAG_SECURE} display flag. The caller promises to take 159 * reasonable measures, such as over-the-air encryption, to prevent the contents 160 * of the display from being intercepted or recorded on a persistent medium. 161 * </p><p> 162 * Creating a secure virtual display requires the 163 * {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT} permission. 164 * This permission is reserved for use by system components and is not available to 165 * third-party applications. 166 * </p> 167 * 168 * <h3>Non-secure virtual displays</h3> 169 * <p> 170 * When this flag is not set, the virtual display is considered unsecure. 171 * The content of secure windows will be blanked if shown on this display. 172 * </p> 173 * 174 * @see Display#FLAG_SECURE 175 * @see #createVirtualDisplay 176 */ 177 public static final int VIRTUAL_DISPLAY_FLAG_SECURE = 1 << 2; 178 179 /** 180 * Virtual display flag: Only show this display's own content; do not mirror 181 * the content of another display. 182 * 183 * <p> 184 * This flag is used in conjunction with {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}. 185 * Ordinarily public virtual displays will automatically mirror the content of the 186 * default display if they have no windows of their own. When this flag is 187 * specified, the virtual display will only ever show its own content and 188 * will be blanked instead if it has no windows. 189 * </p> 190 * 191 * <p> 192 * This flag is mutually exclusive with {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. If both 193 * flags are specified then the own-content only behavior will be applied. 194 * </p> 195 * 196 * <p> 197 * This behavior of this flag is implied whenever neither {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC} 198 * nor {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR} have been set. This flag is only required to 199 * override the default behavior when creating a public display. 200 * </p> 201 * 202 * @see #createVirtualDisplay 203 */ 204 public static final int VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY = 1 << 3; 205 206 207 /** 208 * Virtual display flag: Allows content to be mirrored on private displays when no content is 209 * being shown. 210 * 211 * <p> 212 * This flag is mutually exclusive with {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}. 213 * If both flags are specified then the own-content only behavior will be applied. 214 * </p> 215 * 216 * <p> 217 * The behavior of this flag is implied whenever {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC} is set 218 * and {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY} has not been set. This flag is only 219 * required to override the default behavior when creating a private display. 220 * </p> 221 * 222 * <p> 223 * Creating an auto-mirroing virtual display requires the 224 * {@link android.Manifest.permission#CAPTURE_VIDEO_OUTPUT} 225 * or {@link android.Manifest.permission#CAPTURE_SECURE_VIDEO_OUTPUT} permission. 226 * These permissions are reserved for use by system components and are not available to 227 * third-party applications. 228 * 229 * Alternatively, an appropriate {@link MediaProjection} may be used to create an 230 * auto-mirroring virtual display. 231 * </p> 232 * 233 * @see #createVirtualDisplay 234 */ 235 public static final int VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR = 1 << 4; 236 237 /** 238 * Virtual display flag: Allows content to be displayed on private virtual displays when 239 * keyguard is shown but is insecure. 240 * 241 * <p> 242 * This might be used in a case when the content of a virtual display is captured and sent to an 243 * external hardware display that is not visible to the system directly. This flag will allow 244 * the continued display of content while other displays will be covered by a keyguard which 245 * doesn't require providing credentials to unlock. This means that there is either no password 246 * or other authentication method set, or the device is in a trusted state - 247 * {@link android.service.trust.TrustAgentService} has available and active trust agent. 248 * </p><p> 249 * This flag can only be applied to private displays as defined by the 250 * {@link Display#FLAG_PRIVATE} display flag. It is mutually exclusive with 251 * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}. If both flags are specified then this flag's behavior 252 * will not be applied. 253 * </p> 254 * 255 * @see #createVirtualDisplay 256 * @see WindowManagerPolicy#isKeyguardSecure(int) 257 * @see WindowManagerPolicy#isKeyguardTrustedLw() 258 * @hide 259 */ 260 // TODO: Update name and documentation and un-hide the flag. Don't change the value before that. 261 public static final int VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD = 1 << 5; 262 263 /** 264 * Virtual display flag: Specifies that the virtual display can be associated with a 265 * touchpad device that matches its uniqueId. 266 * 267 * @see #createVirtualDisplay 268 * @hide 269 */ 270 public static final int VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH = 1 << 6; 271 272 /** 273 * Virtual display flag: Indicates that the orientation of this display device is coupled to 274 * the rotation of its associated logical display. 275 * 276 * @see #createVirtualDisplay 277 * @hide 278 */ 279 public static final int VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT = 1 << 7; 280 281 /** 282 * Virtual display flag: Indicates that the contents will be destroyed once 283 * the display is removed. 284 * 285 * Public virtual displays without this flag will move their content to main display 286 * stack once they're removed. Private vistual displays will always destroy their 287 * content on removal even without this flag. 288 * 289 * @see #createVirtualDisplay 290 * @hide 291 */ 292 public static final int VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL = 1 << 8; 293 294 /** @hide */ 295 public DisplayManager(Context context) { 296 mContext = context; 297 mGlobal = DisplayManagerGlobal.getInstance(); 298 } 299 300 /** 301 * Gets information about a logical display. 302 * 303 * The display metrics may be adjusted to provide compatibility 304 * for legacy applications. 305 * 306 * @param displayId The logical display id. 307 * @return The display object, or null if there is no valid display with the given id. 308 */ 309 public Display getDisplay(int displayId) { 310 synchronized (mLock) { 311 return getOrCreateDisplayLocked(displayId, false /*assumeValid*/); 312 } 313 } 314 315 /** 316 * Gets all currently valid logical displays. 317 * 318 * @return An array containing all displays. 319 */ 320 public Display[] getDisplays() { 321 return getDisplays(null); 322 } 323 324 /** 325 * Gets all currently valid logical displays of the specified category. 326 * <p> 327 * When there are multiple displays in a category the returned displays are sorted 328 * of preference. For example, if the requested category is 329 * {@link #DISPLAY_CATEGORY_PRESENTATION} and there are multiple presentation displays 330 * then the displays are sorted so that the first display in the returned array 331 * is the most preferred presentation display. The application may simply 332 * use the first display or allow the user to choose. 333 * </p> 334 * 335 * @param category The requested display category or null to return all displays. 336 * @return An array containing all displays sorted by order of preference. 337 * 338 * @see #DISPLAY_CATEGORY_PRESENTATION 339 */ 340 public Display[] getDisplays(String category) { 341 final int[] displayIds = mGlobal.getDisplayIds(); 342 synchronized (mLock) { 343 try { 344 if (category == null) { 345 addAllDisplaysLocked(mTempDisplays, displayIds); 346 } else if (category.equals(DISPLAY_CATEGORY_PRESENTATION)) { 347 addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_WIFI); 348 addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_HDMI); 349 addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_OVERLAY); 350 addPresentationDisplaysLocked(mTempDisplays, displayIds, Display.TYPE_VIRTUAL); 351 } 352 return mTempDisplays.toArray(new Display[mTempDisplays.size()]); 353 } finally { 354 mTempDisplays.clear(); 355 } 356 } 357 } 358 359 private void addAllDisplaysLocked(ArrayList<Display> displays, int[] displayIds) { 360 for (int i = 0; i < displayIds.length; i++) { 361 Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/); 362 if (display != null) { 363 displays.add(display); 364 } 365 } 366 } 367 368 private void addPresentationDisplaysLocked( 369 ArrayList<Display> displays, int[] displayIds, int matchType) { 370 for (int i = 0; i < displayIds.length; i++) { 371 Display display = getOrCreateDisplayLocked(displayIds[i], true /*assumeValid*/); 372 if (display != null 373 && (display.getFlags() & Display.FLAG_PRESENTATION) != 0 374 && display.getType() == matchType) { 375 displays.add(display); 376 } 377 } 378 } 379 380 private Display getOrCreateDisplayLocked(int displayId, boolean assumeValid) { 381 Display display = mDisplays.get(displayId); 382 if (display == null) { 383 // TODO: We cannot currently provide any override configurations for metrics on displays 384 // other than the display the context is associated with. 385 final Context context = mContext.getDisplay().getDisplayId() == displayId 386 ? mContext : mContext.getApplicationContext(); 387 388 display = mGlobal.getCompatibleDisplay(displayId, context.getResources()); 389 if (display != null) { 390 mDisplays.put(displayId, display); 391 } 392 } else if (!assumeValid && !display.isValid()) { 393 display = null; 394 } 395 return display; 396 } 397 398 /** 399 * Registers an display listener to receive notifications about when 400 * displays are added, removed or changed. 401 * 402 * @param listener The listener to register. 403 * @param handler The handler on which the listener should be invoked, or null 404 * if the listener should be invoked on the calling thread's looper. 405 * 406 * @see #unregisterDisplayListener 407 */ 408 public void registerDisplayListener(DisplayListener listener, Handler handler) { 409 mGlobal.registerDisplayListener(listener, handler); 410 } 411 412 /** 413 * Unregisters a display listener. 414 * 415 * @param listener The listener to unregister. 416 * 417 * @see #registerDisplayListener 418 */ 419 public void unregisterDisplayListener(DisplayListener listener) { 420 mGlobal.unregisterDisplayListener(listener); 421 } 422 423 /** 424 * Starts scanning for available Wifi displays. 425 * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. 426 * <p> 427 * Calls to this method nest and must be matched by an equal number of calls to 428 * {@link #stopWifiDisplayScan()}. 429 * </p><p> 430 * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. 431 * </p> 432 * 433 * @hide 434 */ 435 public void startWifiDisplayScan() { 436 mGlobal.startWifiDisplayScan(); 437 } 438 439 /** 440 * Stops scanning for available Wifi displays. 441 * <p> 442 * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. 443 * </p> 444 * 445 * @hide 446 */ 447 public void stopWifiDisplayScan() { 448 mGlobal.stopWifiDisplayScan(); 449 } 450 451 /** 452 * Connects to a Wifi display. 453 * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. 454 * <p> 455 * Automatically remembers the display after a successful connection, if not 456 * already remembered. 457 * </p><p> 458 * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. 459 * </p> 460 * 461 * @param deviceAddress The MAC address of the device to which we should connect. 462 * @hide 463 */ 464 public void connectWifiDisplay(String deviceAddress) { 465 mGlobal.connectWifiDisplay(deviceAddress); 466 } 467 468 /** @hide */ 469 public void pauseWifiDisplay() { 470 mGlobal.pauseWifiDisplay(); 471 } 472 473 /** @hide */ 474 public void resumeWifiDisplay() { 475 mGlobal.resumeWifiDisplay(); 476 } 477 478 /** 479 * Disconnects from the current Wifi display. 480 * The results are sent as a {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED} broadcast. 481 * @hide 482 */ 483 public void disconnectWifiDisplay() { 484 mGlobal.disconnectWifiDisplay(); 485 } 486 487 /** 488 * Renames a Wifi display. 489 * <p> 490 * The display must already be remembered for this call to succeed. In other words, 491 * we must already have successfully connected to the display at least once and then 492 * not forgotten it. 493 * </p><p> 494 * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. 495 * </p> 496 * 497 * @param deviceAddress The MAC address of the device to rename. 498 * @param alias The alias name by which to remember the device, or null 499 * or empty if no alias should be used. 500 * @hide 501 */ 502 public void renameWifiDisplay(String deviceAddress, String alias) { 503 mGlobal.renameWifiDisplay(deviceAddress, alias); 504 } 505 506 /** 507 * Forgets a previously remembered Wifi display. 508 * <p> 509 * Automatically disconnects from the display if currently connected to it. 510 * </p><p> 511 * Requires {@link android.Manifest.permission#CONFIGURE_WIFI_DISPLAY}. 512 * </p> 513 * 514 * @param deviceAddress The MAC address of the device to forget. 515 * @hide 516 */ 517 public void forgetWifiDisplay(String deviceAddress) { 518 mGlobal.forgetWifiDisplay(deviceAddress); 519 } 520 521 /** 522 * Gets the current Wifi display status. 523 * Watch for changes in the status by registering a broadcast receiver for 524 * {@link #ACTION_WIFI_DISPLAY_STATUS_CHANGED}. 525 * 526 * @return The current Wifi display status. 527 * @hide 528 */ 529 public WifiDisplayStatus getWifiDisplayStatus() { 530 return mGlobal.getWifiDisplayStatus(); 531 } 532 533 /** 534 * Creates a virtual display. 535 * 536 * @see #createVirtualDisplay(String, int, int, int, Surface, int, 537 * VirtualDisplay.Callback, Handler) 538 */ 539 public VirtualDisplay createVirtualDisplay(@NonNull String name, 540 int width, int height, int densityDpi, @Nullable Surface surface, int flags) { 541 return createVirtualDisplay(name, width, height, densityDpi, surface, flags, null, null); 542 } 543 544 /** 545 * Creates a virtual display. 546 * <p> 547 * The content of a virtual display is rendered to a {@link Surface} provided 548 * by the application. 549 * </p><p> 550 * The virtual display should be {@link VirtualDisplay#release released} 551 * when no longer needed. Because a virtual display renders to a surface 552 * provided by the application, it will be released automatically when the 553 * process terminates and all remaining windows on it will be forcibly removed. 554 * </p><p> 555 * The behavior of the virtual display depends on the flags that are provided 556 * to this method. By default, virtual displays are created to be private, 557 * non-presentation and unsecure. Permissions may be required to use certain flags. 558 * </p><p> 559 * As of {@link android.os.Build.VERSION_CODES#KITKAT_WATCH}, the surface may 560 * be attached or detached dynamically using {@link VirtualDisplay#setSurface}. 561 * Previously, the surface had to be non-null when {@link #createVirtualDisplay} 562 * was called and could not be changed for the lifetime of the display. 563 * </p><p> 564 * Detaching the surface that backs a virtual display has a similar effect to 565 * turning off the screen. 566 * </p> 567 * 568 * @param name The name of the virtual display, must be non-empty. 569 * @param width The width of the virtual display in pixels, must be greater than 0. 570 * @param height The height of the virtual display in pixels, must be greater than 0. 571 * @param densityDpi The density of the virtual display in dpi, must be greater than 0. 572 * @param surface The surface to which the content of the virtual display should 573 * be rendered, or null if there is none initially. 574 * @param flags A combination of virtual display flags: 575 * {@link #VIRTUAL_DISPLAY_FLAG_PUBLIC}, {@link #VIRTUAL_DISPLAY_FLAG_PRESENTATION}, 576 * {@link #VIRTUAL_DISPLAY_FLAG_SECURE}, {@link #VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY}, 577 * or {@link #VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR}. 578 * @param callback Callback to call when the state of the {@link VirtualDisplay} changes 579 * @param handler The handler on which the listener should be invoked, or null 580 * if the listener should be invoked on the calling thread's looper. 581 * @return The newly created virtual display, or null if the application could 582 * not create the virtual display. 583 * 584 * @throws SecurityException if the caller does not have permission to create 585 * a virtual display with the specified flags. 586 */ 587 public VirtualDisplay createVirtualDisplay(@NonNull String name, 588 int width, int height, int densityDpi, @Nullable Surface surface, int flags, 589 @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler) { 590 return createVirtualDisplay(null /* projection */, name, width, height, densityDpi, surface, 591 flags, callback, handler, null /* uniqueId */); 592 } 593 594 /** @hide */ 595 public VirtualDisplay createVirtualDisplay(@Nullable MediaProjection projection, 596 @NonNull String name, int width, int height, int densityDpi, @Nullable Surface surface, 597 int flags, @Nullable VirtualDisplay.Callback callback, @Nullable Handler handler, 598 @Nullable String uniqueId) { 599 return mGlobal.createVirtualDisplay(mContext, projection, 600 name, width, height, densityDpi, surface, flags, callback, handler, uniqueId); 601 } 602 603 /** 604 * Gets the stable device display size, in pixels. 605 * 606 * This should really only be used for things like server-side filtering of available 607 * applications. Most applications don't need the level of stability guaranteed by this and 608 * should instead query either the size of the display they're currently running on or the 609 * size of the default display. 610 * @hide 611 */ 612 @SystemApi 613 public Point getStableDisplaySize() { 614 return mGlobal.getStableDisplaySize(); 615 } 616 617 /** 618 * Listens for changes in available display devices. 619 */ 620 public interface DisplayListener { 621 /** 622 * Called whenever a logical display has been added to the system. 623 * Use {@link DisplayManager#getDisplay} to get more information about 624 * the display. 625 * 626 * @param displayId The id of the logical display that was added. 627 */ 628 void onDisplayAdded(int displayId); 629 630 /** 631 * Called whenever a logical display has been removed from the system. 632 * 633 * @param displayId The id of the logical display that was removed. 634 */ 635 void onDisplayRemoved(int displayId); 636 637 /** 638 * Called whenever the properties of a logical display have changed. 639 * 640 * @param displayId The id of the logical display that changed. 641 */ 642 void onDisplayChanged(int displayId); 643 } 644 } 645