DisplayManagerGlobal.java revision c39d47a8e7c74bd539104b0efab898ef6fc43ddf
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.hardware.display.DisplayManager.DisplayListener; 21import android.media.projection.MediaProjection; 22import android.media.projection.IMediaProjection; 23import android.os.Binder; 24import android.os.Handler; 25import android.os.IBinder; 26import android.os.Looper; 27import android.os.Message; 28import android.os.RemoteException; 29import android.os.ServiceManager; 30import android.text.TextUtils; 31import android.util.Log; 32import android.util.SparseArray; 33import android.view.DisplayAdjustments; 34import android.view.Display; 35import android.view.DisplayInfo; 36import android.view.Surface; 37 38import 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 */ 46public 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 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 */ 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 */ 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 Log.e(TAG, "Could not get display information from display manager.", ex); 136 return null; 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 Log.e(TAG, "Could not get display ids from display manager.", ex); 163 return new int[] { Display.DEFAULT_DISPLAY }; 164 } 165 } 166 167 /** 168 * Gets information about a logical display. 169 * 170 * The display metrics may be adjusted to provide compatibility 171 * for legacy applications or limited screen areas. 172 * 173 * @param displayId The logical display id. 174 * @param daj The compatibility info and activityToken. 175 * @return The display object, or null if there is no display with the given id. 176 */ 177 public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) { 178 DisplayInfo displayInfo = getDisplayInfo(displayId); 179 if (displayInfo == null) { 180 return null; 181 } 182 return new Display(this, displayId, displayInfo, daj); 183 } 184 185 /** 186 * Gets information about a logical display without applying any compatibility metrics. 187 * 188 * @param displayId The logical display id. 189 * @return The display object, or null if there is no display with the given id. 190 */ 191 public Display getRealDisplay(int displayId) { 192 return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS); 193 } 194 195 /** 196 * Gets information about a logical display without applying any compatibility metrics. 197 * 198 * @param displayId The logical display id. 199 * @param IBinder the activity token for this display. 200 * @return The display object, or null if there is no display with the given id. 201 */ 202 public Display getRealDisplay(int displayId, IBinder token) { 203 return getCompatibleDisplay(displayId, new DisplayAdjustments(token)); 204 } 205 206 public void registerDisplayListener(DisplayListener listener, Handler handler) { 207 if (listener == null) { 208 throw new IllegalArgumentException("listener must not be null"); 209 } 210 211 synchronized (mLock) { 212 int index = findDisplayListenerLocked(listener); 213 if (index < 0) { 214 mDisplayListeners.add(new DisplayListenerDelegate(listener, handler)); 215 registerCallbackIfNeededLocked(); 216 } 217 } 218 } 219 220 public void unregisterDisplayListener(DisplayListener listener) { 221 if (listener == null) { 222 throw new IllegalArgumentException("listener must not be null"); 223 } 224 225 synchronized (mLock) { 226 int index = findDisplayListenerLocked(listener); 227 if (index >= 0) { 228 DisplayListenerDelegate d = mDisplayListeners.get(index); 229 d.clearEvents(); 230 mDisplayListeners.remove(index); 231 } 232 } 233 } 234 235 private int findDisplayListenerLocked(DisplayListener listener) { 236 final int numListeners = mDisplayListeners.size(); 237 for (int i = 0; i < numListeners; i++) { 238 if (mDisplayListeners.get(i).mListener == listener) { 239 return i; 240 } 241 } 242 return -1; 243 } 244 245 private void registerCallbackIfNeededLocked() { 246 if (mCallback == null) { 247 mCallback = new DisplayManagerCallback(); 248 try { 249 mDm.registerCallback(mCallback); 250 } catch (RemoteException ex) { 251 Log.e(TAG, "Failed to register callback with display manager service.", ex); 252 mCallback = null; 253 } 254 } 255 } 256 257 private void handleDisplayEvent(int displayId, int event) { 258 synchronized (mLock) { 259 if (USE_CACHE) { 260 mDisplayInfoCache.remove(displayId); 261 262 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) { 263 mDisplayIdCache = null; 264 } 265 } 266 267 final int numListeners = mDisplayListeners.size(); 268 for (int i = 0; i < numListeners; i++) { 269 mDisplayListeners.get(i).sendDisplayEvent(displayId, event); 270 } 271 } 272 } 273 274 public void startWifiDisplayScan() { 275 synchronized (mLock) { 276 if (mWifiDisplayScanNestCount++ == 0) { 277 registerCallbackIfNeededLocked(); 278 try { 279 mDm.startWifiDisplayScan(); 280 } catch (RemoteException ex) { 281 Log.e(TAG, "Failed to scan for Wifi displays.", ex); 282 } 283 } 284 } 285 } 286 287 public void stopWifiDisplayScan() { 288 synchronized (mLock) { 289 if (--mWifiDisplayScanNestCount == 0) { 290 try { 291 mDm.stopWifiDisplayScan(); 292 } catch (RemoteException ex) { 293 Log.e(TAG, "Failed to scan for Wifi displays.", ex); 294 } 295 } else if (mWifiDisplayScanNestCount < 0) { 296 Log.wtf(TAG, "Wifi display scan nest count became negative: " 297 + mWifiDisplayScanNestCount); 298 mWifiDisplayScanNestCount = 0; 299 } 300 } 301 } 302 303 public void connectWifiDisplay(String deviceAddress) { 304 if (deviceAddress == null) { 305 throw new IllegalArgumentException("deviceAddress must not be null"); 306 } 307 308 try { 309 mDm.connectWifiDisplay(deviceAddress); 310 } catch (RemoteException ex) { 311 Log.e(TAG, "Failed to connect to Wifi display " + deviceAddress + ".", ex); 312 } 313 } 314 315 public void pauseWifiDisplay() { 316 try { 317 mDm.pauseWifiDisplay(); 318 } catch (RemoteException ex) { 319 Log.e(TAG, "Failed to pause Wifi display.", ex); 320 } 321 } 322 323 public void resumeWifiDisplay() { 324 try { 325 mDm.resumeWifiDisplay(); 326 } catch (RemoteException ex) { 327 Log.e(TAG, "Failed to resume Wifi display.", ex); 328 } 329 } 330 331 public void disconnectWifiDisplay() { 332 try { 333 mDm.disconnectWifiDisplay(); 334 } catch (RemoteException ex) { 335 Log.e(TAG, "Failed to disconnect from Wifi display.", ex); 336 } 337 } 338 339 public void renameWifiDisplay(String deviceAddress, String alias) { 340 if (deviceAddress == null) { 341 throw new IllegalArgumentException("deviceAddress must not be null"); 342 } 343 344 try { 345 mDm.renameWifiDisplay(deviceAddress, alias); 346 } catch (RemoteException ex) { 347 Log.e(TAG, "Failed to rename Wifi display " + deviceAddress 348 + " with alias " + alias + ".", ex); 349 } 350 } 351 352 public void forgetWifiDisplay(String deviceAddress) { 353 if (deviceAddress == null) { 354 throw new IllegalArgumentException("deviceAddress must not be null"); 355 } 356 357 try { 358 mDm.forgetWifiDisplay(deviceAddress); 359 } catch (RemoteException ex) { 360 Log.e(TAG, "Failed to forget Wifi display.", ex); 361 } 362 } 363 364 public WifiDisplayStatus getWifiDisplayStatus() { 365 try { 366 return mDm.getWifiDisplayStatus(); 367 } catch (RemoteException ex) { 368 Log.e(TAG, "Failed to get Wifi display status.", ex); 369 return new WifiDisplayStatus(); 370 } 371 } 372 373 public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection, 374 String name, int width, int height, int densityDpi, Surface surface, int flags, 375 VirtualDisplay.Callbacks callbacks, Handler handler) { 376 if (TextUtils.isEmpty(name)) { 377 throw new IllegalArgumentException("name must be non-null and non-empty"); 378 } 379 if (width <= 0 || height <= 0 || densityDpi <= 0) { 380 throw new IllegalArgumentException("width, height, and densityDpi must be " 381 + "greater than 0"); 382 } 383 384 VirtualDisplayCallbacks callbackWrapper = new VirtualDisplayCallbacks(callbacks, handler); 385 IMediaProjection projectionToken = projection != null ? projection.getProjection() : null; 386 int displayId; 387 try { 388 displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken, 389 context.getPackageName(), name, width, height, densityDpi, surface, flags); 390 } catch (RemoteException ex) { 391 Log.e(TAG, "Could not create virtual display: " + name, ex); 392 return null; 393 } 394 if (displayId < 0) { 395 Log.e(TAG, "Could not create virtual display: " + name); 396 return null; 397 } 398 Display display = getRealDisplay(displayId); 399 if (display == null) { 400 Log.wtf(TAG, "Could not obtain display info for newly created " 401 + "virtual display: " + name); 402 try { 403 mDm.releaseVirtualDisplay(callbackWrapper); 404 } catch (RemoteException ex) { 405 } 406 return null; 407 } 408 return new VirtualDisplay(this, display, callbackWrapper, surface); 409 } 410 411 public void setVirtualDisplaySurface(IVirtualDisplayCallbacks token, Surface surface) { 412 try { 413 mDm.setVirtualDisplaySurface(token, surface); 414 } catch (RemoteException ex) { 415 Log.w(TAG, "Failed to set virtual display surface.", ex); 416 } 417 } 418 419 public void releaseVirtualDisplay(IVirtualDisplayCallbacks token) { 420 try { 421 mDm.releaseVirtualDisplay(token); 422 } catch (RemoteException ex) { 423 Log.w(TAG, "Failed to release virtual display.", ex); 424 } 425 } 426 427 private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { 428 @Override 429 public void onDisplayEvent(int displayId, int event) { 430 if (DEBUG) { 431 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event); 432 } 433 handleDisplayEvent(displayId, event); 434 } 435 } 436 437 private static final class DisplayListenerDelegate extends Handler { 438 public final DisplayListener mListener; 439 440 public DisplayListenerDelegate(DisplayListener listener, Handler handler) { 441 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); 442 mListener = listener; 443 } 444 445 public void sendDisplayEvent(int displayId, int event) { 446 Message msg = obtainMessage(event, displayId, 0); 447 sendMessage(msg); 448 } 449 450 public void clearEvents() { 451 removeCallbacksAndMessages(null); 452 } 453 454 @Override 455 public void handleMessage(Message msg) { 456 switch (msg.what) { 457 case EVENT_DISPLAY_ADDED: 458 mListener.onDisplayAdded(msg.arg1); 459 break; 460 case EVENT_DISPLAY_CHANGED: 461 mListener.onDisplayChanged(msg.arg1); 462 break; 463 case EVENT_DISPLAY_REMOVED: 464 mListener.onDisplayRemoved(msg.arg1); 465 break; 466 } 467 } 468 } 469 470 private final static class VirtualDisplayCallbacks extends IVirtualDisplayCallbacks.Stub { 471 private VirtualDisplayCallbacksDelegate mDelegate; 472 473 public VirtualDisplayCallbacks(VirtualDisplay.Callbacks callbacks, Handler handler) { 474 mDelegate = new VirtualDisplayCallbacksDelegate(callbacks, handler); 475 } 476 477 @Override // Binder call 478 public void onDisplayPaused() { 479 mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_PAUSED); 480 } 481 482 @Override // Binder call 483 public void onDisplayResumed() { 484 mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_RESUMED); 485 } 486 487 @Override // Binder call 488 public void onDisplayStopped() { 489 mDelegate.sendEmptyMessage(VirtualDisplayCallbacksDelegate.MSG_DISPLAY_STOPPED); 490 } 491 } 492 493 private final static class VirtualDisplayCallbacksDelegate extends Handler { 494 public static final int MSG_DISPLAY_PAUSED = 0; 495 public static final int MSG_DISPLAY_RESUMED = 1; 496 public static final int MSG_DISPLAY_STOPPED = 2; 497 498 private final VirtualDisplay.Callbacks mCallbacks; 499 500 public VirtualDisplayCallbacksDelegate(VirtualDisplay.Callbacks callbacks, 501 Handler handler) { 502 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); 503 mCallbacks = callbacks; 504 } 505 506 @Override 507 public void handleMessage(Message msg) { 508 if (mCallbacks == null) { 509 return; 510 } 511 switch (msg.what) { 512 case MSG_DISPLAY_PAUSED: 513 mCallbacks.onDisplayPaused(); 514 break; 515 case MSG_DISPLAY_RESUMED: 516 mCallbacks.onDisplayResumed(); 517 break; 518 case MSG_DISPLAY_STOPPED: 519 mCallbacks.onDisplayStopped(); 520 break; 521 } 522 } 523 } 524} 525