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 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 */ 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 */ 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 */ 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 */ 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 */ 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 */ 208 public Display getRealDisplay(int displayId) { 209 return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); 210 } 211 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 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 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 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 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 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 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 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 320 public void pauseWifiDisplay() { 321 try { 322 mDm.pauseWifiDisplay(); 323 } catch (RemoteException ex) { 324 throw ex.rethrowFromSystemServer(); 325 } 326 } 327 328 public void resumeWifiDisplay() { 329 try { 330 mDm.resumeWifiDisplay(); 331 } catch (RemoteException ex) { 332 throw ex.rethrowFromSystemServer(); 333 } 334 } 335 336 public void disconnectWifiDisplay() { 337 try { 338 mDm.disconnectWifiDisplay(); 339 } catch (RemoteException ex) { 340 throw ex.rethrowFromSystemServer(); 341 } 342 } 343 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 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 368 public WifiDisplayStatus getWifiDisplayStatus() { 369 try { 370 return mDm.getWifiDisplayStatus(); 371 } catch (RemoteException ex) { 372 throw ex.rethrowFromSystemServer(); 373 } 374 } 375 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 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 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 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 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 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 461 public DisplayListenerDelegate(DisplayListener listener, Handler handler) { 462 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); 463 mListener = listener; 464 } 465 466 public void sendDisplayEvent(int displayId, int event) { 467 Message msg = obtainMessage(event, displayId, 0); 468 sendMessage(msg); 469 } 470 471 public void clearEvents() { 472 removeCallbacksAndMessages(null); 473 } 474 475 @Override 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 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 501 public void onPaused() { 502 if (mDelegate != null) { 503 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED); 504 } 505 } 506 507 @Override // Binder call 508 public void onResumed() { 509 if (mDelegate != null) { 510 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED); 511 } 512 } 513 514 @Override // Binder call 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 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 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