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.compat.annotation.UnsupportedAppUsage; 22 import android.content.Context; 23 import android.content.pm.ParceledListSlice; 24 import android.content.res.Resources; 25 import android.graphics.ColorSpace; 26 import android.graphics.Point; 27 import android.hardware.display.DisplayManager.DisplayListener; 28 import android.media.projection.IMediaProjection; 29 import android.media.projection.MediaProjection; 30 import android.os.Handler; 31 import android.os.IBinder; 32 import android.os.Looper; 33 import android.os.Message; 34 import android.os.RemoteException; 35 import android.os.ServiceManager; 36 import android.text.TextUtils; 37 import android.util.Log; 38 import android.util.Pair; 39 import android.util.SparseArray; 40 import android.view.Display; 41 import android.view.DisplayAdjustments; 42 import android.view.DisplayInfo; 43 import android.view.Surface; 44 45 import java.util.ArrayList; 46 import java.util.Collections; 47 import java.util.List; 48 49 /** 50 * Manager communication with the display manager service on behalf of 51 * an application process. You're probably looking for {@link DisplayManager}. 52 * 53 * @hide 54 */ 55 public final class DisplayManagerGlobal { 56 private static final String TAG = "DisplayManager"; 57 private static final boolean DEBUG = false; 58 59 // True if display info and display ids should be cached. 60 // 61 // FIXME: The cache is currently disabled because it's unclear whether we have the 62 // necessary guarantees that the caches will always be flushed before clients 63 // attempt to observe their new state. For example, depending on the order 64 // in which the binder transactions take place, we might have a problem where 65 // an application could start processing a configuration change due to a display 66 // orientation change before the display info cache has actually been invalidated. 67 private static final boolean USE_CACHE = false; 68 69 public static final int EVENT_DISPLAY_ADDED = 1; 70 public static final int EVENT_DISPLAY_CHANGED = 2; 71 public static final int EVENT_DISPLAY_REMOVED = 3; 72 73 @UnsupportedAppUsage 74 private static DisplayManagerGlobal sInstance; 75 76 private final Object mLock = new Object(); 77 78 @UnsupportedAppUsage 79 private final IDisplayManager mDm; 80 81 private DisplayManagerCallback mCallback; 82 private final ArrayList<DisplayListenerDelegate> mDisplayListeners = 83 new ArrayList<DisplayListenerDelegate>(); 84 85 private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>(); 86 private final ColorSpace mWideColorSpace; 87 private int[] mDisplayIdCache; 88 89 private int mWifiDisplayScanNestCount; 90 DisplayManagerGlobal(IDisplayManager dm)91 private DisplayManagerGlobal(IDisplayManager dm) { 92 mDm = dm; 93 try { 94 mWideColorSpace = 95 ColorSpace.get( 96 ColorSpace.Named.values()[mDm.getPreferredWideGamutColorSpaceId()]); 97 } catch (RemoteException ex) { 98 throw ex.rethrowFromSystemServer(); 99 } 100 } 101 102 /** 103 * Gets an instance of the display manager global singleton. 104 * 105 * @return The display manager instance, may be null early in system startup 106 * before the display manager has been fully initialized. 107 */ 108 @UnsupportedAppUsage getInstance()109 public static DisplayManagerGlobal getInstance() { 110 synchronized (DisplayManagerGlobal.class) { 111 if (sInstance == null) { 112 IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE); 113 if (b != null) { 114 sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b)); 115 } 116 } 117 return sInstance; 118 } 119 } 120 121 /** 122 * Get information about a particular logical display. 123 * 124 * @param displayId The logical display id. 125 * @return Information about the specified display, or null if it does not exist. 126 * This object belongs to an internal cache and should be treated as if it were immutable. 127 */ 128 @UnsupportedAppUsage getDisplayInfo(int displayId)129 public DisplayInfo getDisplayInfo(int displayId) { 130 try { 131 synchronized (mLock) { 132 DisplayInfo info; 133 if (USE_CACHE) { 134 info = mDisplayInfoCache.get(displayId); 135 if (info != null) { 136 return info; 137 } 138 } 139 140 info = mDm.getDisplayInfo(displayId); 141 if (info == null) { 142 return null; 143 } 144 145 if (USE_CACHE) { 146 mDisplayInfoCache.put(displayId, info); 147 } 148 registerCallbackIfNeededLocked(); 149 150 if (DEBUG) { 151 Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info); 152 } 153 return info; 154 } 155 } catch (RemoteException ex) { 156 throw ex.rethrowFromSystemServer(); 157 } 158 } 159 160 /** 161 * Gets all currently valid logical display ids. 162 * 163 * @return An array containing all display ids. 164 */ 165 @UnsupportedAppUsage getDisplayIds()166 public int[] getDisplayIds() { 167 try { 168 synchronized (mLock) { 169 if (USE_CACHE) { 170 if (mDisplayIdCache != null) { 171 return mDisplayIdCache; 172 } 173 } 174 175 int[] displayIds = mDm.getDisplayIds(); 176 if (USE_CACHE) { 177 mDisplayIdCache = displayIds; 178 } 179 registerCallbackIfNeededLocked(); 180 return displayIds; 181 } 182 } catch (RemoteException ex) { 183 throw ex.rethrowFromSystemServer(); 184 } 185 } 186 187 /** 188 * Check if specified UID's content is present on display and should be granted access to it. 189 * 190 * @param uid UID to be checked. 191 * @param displayId id of the display where presence of the content is checked. 192 * @return {@code true} if UID is present on display, {@code false} otherwise. 193 */ isUidPresentOnDisplay(int uid, int displayId)194 public boolean isUidPresentOnDisplay(int uid, int displayId) { 195 try { 196 return mDm.isUidPresentOnDisplay(uid, displayId); 197 } catch (RemoteException ex) { 198 throw ex.rethrowFromSystemServer(); 199 } 200 } 201 202 /** 203 * Gets information about a logical display. 204 * 205 * The display metrics may be adjusted to provide compatibility 206 * for legacy applications or limited screen areas. 207 * 208 * @param displayId The logical display id. 209 * @param daj The compatibility info and activityToken. 210 * @return The display object, or null if there is no display with the given id. 211 */ getCompatibleDisplay(int displayId, DisplayAdjustments daj)212 public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) { 213 DisplayInfo displayInfo = getDisplayInfo(displayId); 214 if (displayInfo == null) { 215 return null; 216 } 217 return new Display(this, displayId, displayInfo, daj); 218 } 219 220 /** 221 * Gets information about a logical display. 222 * 223 * The display metrics may be adjusted to provide compatibility 224 * for legacy applications or limited screen areas. 225 * 226 * @param displayId The logical display id. 227 * @param resources Resources providing compatibility info. 228 * @return The display object, or null if there is no display with the given id. 229 */ getCompatibleDisplay(int displayId, Resources resources)230 public Display getCompatibleDisplay(int displayId, Resources resources) { 231 DisplayInfo displayInfo = getDisplayInfo(displayId); 232 if (displayInfo == null) { 233 return null; 234 } 235 return new Display(this, displayId, displayInfo, resources); 236 } 237 238 /** 239 * Gets information about a logical display without applying any compatibility metrics. 240 * 241 * @param displayId The logical display id. 242 * @return The display object, or null if there is no display with the given id. 243 */ 244 @UnsupportedAppUsage getRealDisplay(int displayId)245 public Display getRealDisplay(int displayId) { 246 return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); 247 } 248 249 /** 250 * Register a listener for display-related changes. 251 * 252 * @param listener The listener that will be called when display changes occur. 253 * @param handler Handler for the thread that will be receiving the callbacks. May be null. 254 * If null, listener will use the handler for the current thread, and if still null, 255 * the handler for the main thread. 256 * If that is still null, a runtime exception will be thrown. 257 */ registerDisplayListener(@onNull DisplayListener listener, @Nullable Handler handler)258 public void registerDisplayListener(@NonNull DisplayListener listener, 259 @Nullable Handler handler) { 260 if (listener == null) { 261 throw new IllegalArgumentException("listener must not be null"); 262 } 263 264 synchronized (mLock) { 265 int index = findDisplayListenerLocked(listener); 266 if (index < 0) { 267 Looper looper = getLooperForHandler(handler); 268 mDisplayListeners.add(new DisplayListenerDelegate(listener, looper)); 269 registerCallbackIfNeededLocked(); 270 } 271 } 272 } 273 unregisterDisplayListener(DisplayListener listener)274 public void unregisterDisplayListener(DisplayListener listener) { 275 if (listener == null) { 276 throw new IllegalArgumentException("listener must not be null"); 277 } 278 279 synchronized (mLock) { 280 int index = findDisplayListenerLocked(listener); 281 if (index >= 0) { 282 DisplayListenerDelegate d = mDisplayListeners.get(index); 283 d.clearEvents(); 284 mDisplayListeners.remove(index); 285 } 286 } 287 } 288 getLooperForHandler(@ullable Handler handler)289 private static Looper getLooperForHandler(@Nullable Handler handler) { 290 Looper looper = handler != null ? handler.getLooper() : Looper.myLooper(); 291 if (looper == null) { 292 looper = Looper.getMainLooper(); 293 } 294 if (looper == null) { 295 throw new RuntimeException("Could not get Looper for the UI thread."); 296 } 297 return looper; 298 } 299 findDisplayListenerLocked(DisplayListener listener)300 private int findDisplayListenerLocked(DisplayListener listener) { 301 final int numListeners = mDisplayListeners.size(); 302 for (int i = 0; i < numListeners; i++) { 303 if (mDisplayListeners.get(i).mListener == listener) { 304 return i; 305 } 306 } 307 return -1; 308 } 309 registerCallbackIfNeededLocked()310 private void registerCallbackIfNeededLocked() { 311 if (mCallback == null) { 312 mCallback = new DisplayManagerCallback(); 313 try { 314 mDm.registerCallback(mCallback); 315 } catch (RemoteException ex) { 316 throw ex.rethrowFromSystemServer(); 317 } 318 } 319 } 320 handleDisplayEvent(int displayId, int event)321 private void handleDisplayEvent(int displayId, int event) { 322 synchronized (mLock) { 323 if (USE_CACHE) { 324 mDisplayInfoCache.remove(displayId); 325 326 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) { 327 mDisplayIdCache = null; 328 } 329 } 330 331 final int numListeners = mDisplayListeners.size(); 332 for (int i = 0; i < numListeners; i++) { 333 mDisplayListeners.get(i).sendDisplayEvent(displayId, event); 334 } 335 } 336 } 337 startWifiDisplayScan()338 public void startWifiDisplayScan() { 339 synchronized (mLock) { 340 if (mWifiDisplayScanNestCount++ == 0) { 341 registerCallbackIfNeededLocked(); 342 try { 343 mDm.startWifiDisplayScan(); 344 } catch (RemoteException ex) { 345 throw ex.rethrowFromSystemServer(); 346 } 347 } 348 } 349 } 350 stopWifiDisplayScan()351 public void stopWifiDisplayScan() { 352 synchronized (mLock) { 353 if (--mWifiDisplayScanNestCount == 0) { 354 try { 355 mDm.stopWifiDisplayScan(); 356 } catch (RemoteException ex) { 357 throw ex.rethrowFromSystemServer(); 358 } 359 } else if (mWifiDisplayScanNestCount < 0) { 360 Log.wtf(TAG, "Wifi display scan nest count became negative: " 361 + mWifiDisplayScanNestCount); 362 mWifiDisplayScanNestCount = 0; 363 } 364 } 365 } 366 connectWifiDisplay(String deviceAddress)367 public void connectWifiDisplay(String deviceAddress) { 368 if (deviceAddress == null) { 369 throw new IllegalArgumentException("deviceAddress must not be null"); 370 } 371 372 try { 373 mDm.connectWifiDisplay(deviceAddress); 374 } catch (RemoteException ex) { 375 throw ex.rethrowFromSystemServer(); 376 } 377 } 378 pauseWifiDisplay()379 public void pauseWifiDisplay() { 380 try { 381 mDm.pauseWifiDisplay(); 382 } catch (RemoteException ex) { 383 throw ex.rethrowFromSystemServer(); 384 } 385 } 386 resumeWifiDisplay()387 public void resumeWifiDisplay() { 388 try { 389 mDm.resumeWifiDisplay(); 390 } catch (RemoteException ex) { 391 throw ex.rethrowFromSystemServer(); 392 } 393 } 394 395 @UnsupportedAppUsage disconnectWifiDisplay()396 public void disconnectWifiDisplay() { 397 try { 398 mDm.disconnectWifiDisplay(); 399 } catch (RemoteException ex) { 400 throw ex.rethrowFromSystemServer(); 401 } 402 } 403 renameWifiDisplay(String deviceAddress, String alias)404 public void renameWifiDisplay(String deviceAddress, String alias) { 405 if (deviceAddress == null) { 406 throw new IllegalArgumentException("deviceAddress must not be null"); 407 } 408 409 try { 410 mDm.renameWifiDisplay(deviceAddress, alias); 411 } catch (RemoteException ex) { 412 throw ex.rethrowFromSystemServer(); 413 } 414 } 415 forgetWifiDisplay(String deviceAddress)416 public void forgetWifiDisplay(String deviceAddress) { 417 if (deviceAddress == null) { 418 throw new IllegalArgumentException("deviceAddress must not be null"); 419 } 420 421 try { 422 mDm.forgetWifiDisplay(deviceAddress); 423 } catch (RemoteException ex) { 424 throw ex.rethrowFromSystemServer(); 425 } 426 } 427 428 @UnsupportedAppUsage getWifiDisplayStatus()429 public WifiDisplayStatus getWifiDisplayStatus() { 430 try { 431 return mDm.getWifiDisplayStatus(); 432 } catch (RemoteException ex) { 433 throw ex.rethrowFromSystemServer(); 434 } 435 } 436 requestColorMode(int displayId, int colorMode)437 public void requestColorMode(int displayId, int colorMode) { 438 try { 439 mDm.requestColorMode(displayId, colorMode); 440 } catch (RemoteException ex) { 441 throw ex.rethrowFromSystemServer(); 442 } 443 } 444 createVirtualDisplay(Context context, MediaProjection projection, String name, int width, int height, int densityDpi, Surface surface, int flags, VirtualDisplay.Callback callback, Handler handler, String uniqueId)445 public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection, 446 String name, int width, int height, int densityDpi, Surface surface, int flags, 447 VirtualDisplay.Callback callback, Handler handler, String uniqueId) { 448 if (TextUtils.isEmpty(name)) { 449 throw new IllegalArgumentException("name must be non-null and non-empty"); 450 } 451 if (width <= 0 || height <= 0 || densityDpi <= 0) { 452 throw new IllegalArgumentException("width, height, and densityDpi must be " 453 + "greater than 0"); 454 } 455 456 VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler); 457 IMediaProjection projectionToken = projection != null ? projection.getProjection() : null; 458 int displayId; 459 try { 460 displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken, 461 context.getPackageName(), name, width, height, densityDpi, surface, flags, 462 uniqueId); 463 } catch (RemoteException ex) { 464 throw ex.rethrowFromSystemServer(); 465 } 466 if (displayId < 0) { 467 Log.e(TAG, "Could not create virtual display: " + name); 468 return null; 469 } 470 Display display = getRealDisplay(displayId); 471 if (display == null) { 472 Log.wtf(TAG, "Could not obtain display info for newly created " 473 + "virtual display: " + name); 474 try { 475 mDm.releaseVirtualDisplay(callbackWrapper); 476 } catch (RemoteException ex) { 477 throw ex.rethrowFromSystemServer(); 478 } 479 return null; 480 } 481 return new VirtualDisplay(this, display, callbackWrapper, surface); 482 } 483 setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface)484 public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) { 485 try { 486 mDm.setVirtualDisplaySurface(token, surface); 487 setVirtualDisplayState(token, surface != null); 488 } catch (RemoteException ex) { 489 throw ex.rethrowFromSystemServer(); 490 } 491 } 492 resizeVirtualDisplay(IVirtualDisplayCallback token, int width, int height, int densityDpi)493 public void resizeVirtualDisplay(IVirtualDisplayCallback token, 494 int width, int height, int densityDpi) { 495 try { 496 mDm.resizeVirtualDisplay(token, width, height, densityDpi); 497 } catch (RemoteException ex) { 498 throw ex.rethrowFromSystemServer(); 499 } 500 } 501 releaseVirtualDisplay(IVirtualDisplayCallback token)502 public void releaseVirtualDisplay(IVirtualDisplayCallback token) { 503 try { 504 mDm.releaseVirtualDisplay(token); 505 } catch (RemoteException ex) { 506 throw ex.rethrowFromSystemServer(); 507 } 508 } 509 setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn)510 void setVirtualDisplayState(IVirtualDisplayCallback token, boolean isOn) { 511 try { 512 mDm.setVirtualDisplayState(token, isOn); 513 } catch (RemoteException ex) { 514 throw ex.rethrowFromSystemServer(); 515 } 516 } 517 518 /** 519 * Gets the stable device display size, in pixels. 520 */ getStableDisplaySize()521 public Point getStableDisplaySize() { 522 try { 523 return mDm.getStableDisplaySize(); 524 } catch (RemoteException ex) { 525 throw ex.rethrowFromSystemServer(); 526 } 527 } 528 529 /** 530 * Retrieves brightness change events. 531 */ getBrightnessEvents(String callingPackage)532 public List<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) { 533 try { 534 ParceledListSlice<BrightnessChangeEvent> events = 535 mDm.getBrightnessEvents(callingPackage); 536 if (events == null) { 537 return Collections.emptyList(); 538 } 539 return events.getList(); 540 } catch (RemoteException ex) { 541 throw ex.rethrowFromSystemServer(); 542 } 543 } 544 545 /** 546 * Gets the preferred wide gamut color space for all displays. 547 * The wide gamut color space is returned from composition pipeline 548 * based on hardware capability. 549 * 550 * @hide 551 */ getPreferredWideGamutColorSpace()552 public ColorSpace getPreferredWideGamutColorSpace() { 553 return mWideColorSpace; 554 } 555 556 /** 557 * Sets the global brightness configuration for a given user. 558 * 559 * @hide 560 */ setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId, String packageName)561 public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId, 562 String packageName) { 563 try { 564 mDm.setBrightnessConfigurationForUser(c, userId, packageName); 565 } catch (RemoteException ex) { 566 throw ex.rethrowFromSystemServer(); 567 } 568 } 569 570 /** 571 * Gets the global brightness configuration for a given user or null if one hasn't been set. 572 * 573 * @hide 574 */ getBrightnessConfigurationForUser(int userId)575 public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) { 576 try { 577 return mDm.getBrightnessConfigurationForUser(userId); 578 } catch (RemoteException ex) { 579 throw ex.rethrowFromSystemServer(); 580 } 581 } 582 583 /** 584 * Gets the default brightness configuration or null if one hasn't been configured. 585 * 586 * @hide 587 */ getDefaultBrightnessConfiguration()588 public BrightnessConfiguration getDefaultBrightnessConfiguration() { 589 try { 590 return mDm.getDefaultBrightnessConfiguration(); 591 } catch (RemoteException ex) { 592 throw ex.rethrowFromSystemServer(); 593 } 594 } 595 596 /** 597 * Temporarily sets the brightness of the display. 598 * <p> 599 * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission. 600 * </p> 601 * 602 * @param brightness The brightness value from 0 to 255. 603 * 604 * @hide Requires signature permission. 605 */ setTemporaryBrightness(int brightness)606 public void setTemporaryBrightness(int brightness) { 607 try { 608 mDm.setTemporaryBrightness(brightness); 609 } catch (RemoteException ex) { 610 throw ex.rethrowFromSystemServer(); 611 } 612 } 613 614 /** 615 * Temporarily sets the auto brightness adjustment factor. 616 * <p> 617 * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission. 618 * </p> 619 * 620 * @param adjustment The adjustment factor from -1.0 to 1.0. 621 * 622 * @hide Requires signature permission. 623 */ setTemporaryAutoBrightnessAdjustment(float adjustment)624 public void setTemporaryAutoBrightnessAdjustment(float adjustment) { 625 try { 626 mDm.setTemporaryAutoBrightnessAdjustment(adjustment); 627 } catch (RemoteException ex) { 628 throw ex.rethrowFromSystemServer(); 629 } 630 } 631 632 /** 633 * Returns the minimum brightness curve, which guarantess that any brightness curve that dips 634 * below it is rejected by the system. 635 * This prevent auto-brightness from setting the screen so dark as to prevent the user from 636 * resetting or disabling it, and maps lux to the absolute minimum nits that are still readable 637 * in that ambient brightness. 638 * 639 * @return The minimum brightness curve (as lux values and their corresponding nits values). 640 */ getMinimumBrightnessCurve()641 public Pair<float[], float[]> getMinimumBrightnessCurve() { 642 try { 643 Curve curve = mDm.getMinimumBrightnessCurve(); 644 return Pair.create(curve.getX(), curve.getY()); 645 } catch (RemoteException ex) { 646 throw ex.rethrowFromSystemServer(); 647 } 648 } 649 650 /** 651 * Retrieves ambient brightness stats. 652 */ getAmbientBrightnessStats()653 public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() { 654 try { 655 ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats(); 656 if (stats == null) { 657 return Collections.emptyList(); 658 } 659 return stats.getList(); 660 } catch (RemoteException ex) { 661 throw ex.rethrowFromSystemServer(); 662 } 663 } 664 665 private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { 666 @Override onDisplayEvent(int displayId, int event)667 public void onDisplayEvent(int displayId, int event) { 668 if (DEBUG) { 669 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event); 670 } 671 handleDisplayEvent(displayId, event); 672 } 673 } 674 675 private static final class DisplayListenerDelegate extends Handler { 676 public final DisplayListener mListener; 677 DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper)678 DisplayListenerDelegate(DisplayListener listener, @NonNull Looper looper) { 679 super(looper, null, true /*async*/); 680 mListener = listener; 681 } 682 sendDisplayEvent(int displayId, int event)683 public void sendDisplayEvent(int displayId, int event) { 684 Message msg = obtainMessage(event, displayId, 0); 685 sendMessage(msg); 686 } 687 clearEvents()688 public void clearEvents() { 689 removeCallbacksAndMessages(null); 690 } 691 692 @Override handleMessage(Message msg)693 public void handleMessage(Message msg) { 694 switch (msg.what) { 695 case EVENT_DISPLAY_ADDED: 696 mListener.onDisplayAdded(msg.arg1); 697 break; 698 case EVENT_DISPLAY_CHANGED: 699 mListener.onDisplayChanged(msg.arg1); 700 break; 701 case EVENT_DISPLAY_REMOVED: 702 mListener.onDisplayRemoved(msg.arg1); 703 break; 704 } 705 } 706 } 707 708 private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub { 709 private VirtualDisplayCallbackDelegate mDelegate; 710 VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler)711 public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) { 712 if (callback != null) { 713 mDelegate = new VirtualDisplayCallbackDelegate(callback, handler); 714 } 715 } 716 717 @Override // Binder call onPaused()718 public void onPaused() { 719 if (mDelegate != null) { 720 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED); 721 } 722 } 723 724 @Override // Binder call onResumed()725 public void onResumed() { 726 if (mDelegate != null) { 727 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED); 728 } 729 } 730 731 @Override // Binder call onStopped()732 public void onStopped() { 733 if (mDelegate != null) { 734 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED); 735 } 736 } 737 } 738 739 private final static class VirtualDisplayCallbackDelegate extends Handler { 740 public static final int MSG_DISPLAY_PAUSED = 0; 741 public static final int MSG_DISPLAY_RESUMED = 1; 742 public static final int MSG_DISPLAY_STOPPED = 2; 743 744 private final VirtualDisplay.Callback mCallback; 745 VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback, Handler handler)746 public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback, 747 Handler handler) { 748 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); 749 mCallback = callback; 750 } 751 752 @Override handleMessage(Message msg)753 public void handleMessage(Message msg) { 754 switch (msg.what) { 755 case MSG_DISPLAY_PAUSED: 756 mCallback.onPaused(); 757 break; 758 case MSG_DISPLAY_RESUMED: 759 mCallback.onResumed(); 760 break; 761 case MSG_DISPLAY_STOPPED: 762 mCallback.onStopped(); 763 break; 764 } 765 } 766 } 767 } 768