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