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