DisplayManagerGlobal.java revision 609bf65668181d93502a57575f6f20281f2494b8
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.res.Configuration; 21import android.content.res.Resources; 22import android.hardware.display.DisplayManager.DisplayListener; 23import android.media.projection.MediaProjection; 24import android.media.projection.IMediaProjection; 25import android.os.Handler; 26import android.os.IBinder; 27import android.os.Looper; 28import android.os.Message; 29import android.os.RemoteException; 30import android.os.ServiceManager; 31import android.text.TextUtils; 32import android.util.Log; 33import android.util.SparseArray; 34import android.view.DisplayAdjustments; 35import android.view.Display; 36import android.view.DisplayInfo; 37import android.view.Surface; 38 39import 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 */ 47public 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) { 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 } catch (RemoteException ex) { 402 throw ex.rethrowFromSystemServer(); 403 } 404 if (displayId < 0) { 405 Log.e(TAG, "Could not create virtual display: " + name); 406 return null; 407 } 408 Display display = getRealDisplay(displayId); 409 if (display == null) { 410 Log.wtf(TAG, "Could not obtain display info for newly created " 411 + "virtual display: " + name); 412 try { 413 mDm.releaseVirtualDisplay(callbackWrapper); 414 } catch (RemoteException ex) { 415 throw ex.rethrowFromSystemServer(); 416 } 417 return null; 418 } 419 return new VirtualDisplay(this, display, callbackWrapper, surface); 420 } 421 422 public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) { 423 try { 424 mDm.setVirtualDisplaySurface(token, surface); 425 } catch (RemoteException ex) { 426 throw ex.rethrowFromSystemServer(); 427 } 428 } 429 430 public void resizeVirtualDisplay(IVirtualDisplayCallback token, 431 int width, int height, int densityDpi) { 432 try { 433 mDm.resizeVirtualDisplay(token, width, height, densityDpi); 434 } catch (RemoteException ex) { 435 throw ex.rethrowFromSystemServer(); 436 } 437 } 438 439 public void releaseVirtualDisplay(IVirtualDisplayCallback token) { 440 try { 441 mDm.releaseVirtualDisplay(token); 442 } catch (RemoteException ex) { 443 throw ex.rethrowFromSystemServer(); 444 } 445 } 446 447 private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { 448 @Override 449 public void onDisplayEvent(int displayId, int event) { 450 if (DEBUG) { 451 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event); 452 } 453 handleDisplayEvent(displayId, event); 454 } 455 } 456 457 private static final class DisplayListenerDelegate extends Handler { 458 public final DisplayListener mListener; 459 460 public DisplayListenerDelegate(DisplayListener listener, Handler handler) { 461 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); 462 mListener = listener; 463 } 464 465 public void sendDisplayEvent(int displayId, int event) { 466 Message msg = obtainMessage(event, displayId, 0); 467 sendMessage(msg); 468 } 469 470 public void clearEvents() { 471 removeCallbacksAndMessages(null); 472 } 473 474 @Override 475 public void handleMessage(Message msg) { 476 switch (msg.what) { 477 case EVENT_DISPLAY_ADDED: 478 mListener.onDisplayAdded(msg.arg1); 479 break; 480 case EVENT_DISPLAY_CHANGED: 481 mListener.onDisplayChanged(msg.arg1); 482 break; 483 case EVENT_DISPLAY_REMOVED: 484 mListener.onDisplayRemoved(msg.arg1); 485 break; 486 } 487 } 488 } 489 490 private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub { 491 private VirtualDisplayCallbackDelegate mDelegate; 492 493 public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) { 494 if (callback != null) { 495 mDelegate = new VirtualDisplayCallbackDelegate(callback, handler); 496 } 497 } 498 499 @Override // Binder call 500 public void onPaused() { 501 if (mDelegate != null) { 502 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED); 503 } 504 } 505 506 @Override // Binder call 507 public void onResumed() { 508 if (mDelegate != null) { 509 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED); 510 } 511 } 512 513 @Override // Binder call 514 public void onStopped() { 515 if (mDelegate != null) { 516 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED); 517 } 518 } 519 } 520 521 private final static class VirtualDisplayCallbackDelegate extends Handler { 522 public static final int MSG_DISPLAY_PAUSED = 0; 523 public static final int MSG_DISPLAY_RESUMED = 1; 524 public static final int MSG_DISPLAY_STOPPED = 2; 525 526 private final VirtualDisplay.Callback mCallback; 527 528 public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback, 529 Handler handler) { 530 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); 531 mCallback = callback; 532 } 533 534 @Override 535 public void handleMessage(Message msg) { 536 switch (msg.what) { 537 case MSG_DISPLAY_PAUSED: 538 mCallback.onPaused(); 539 break; 540 case MSG_DISPLAY_RESUMED: 541 mCallback.onResumed(); 542 break; 543 case MSG_DISPLAY_STOPPED: 544 mCallback.onStopped(); 545 break; 546 } 547 } 548 } 549} 550