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