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.hardware.display.DisplayManager.DisplayListener; 22import android.media.projection.MediaProjection; 23import android.media.projection.IMediaProjection; 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 public void registerDisplayListener(DisplayListener listener, Handler handler) { 196 if (listener == null) { 197 throw new IllegalArgumentException("listener must not be null"); 198 } 199 200 synchronized (mLock) { 201 int index = findDisplayListenerLocked(listener); 202 if (index < 0) { 203 mDisplayListeners.add(new DisplayListenerDelegate(listener, handler)); 204 registerCallbackIfNeededLocked(); 205 } 206 } 207 } 208 209 public void unregisterDisplayListener(DisplayListener listener) { 210 if (listener == null) { 211 throw new IllegalArgumentException("listener must not be null"); 212 } 213 214 synchronized (mLock) { 215 int index = findDisplayListenerLocked(listener); 216 if (index >= 0) { 217 DisplayListenerDelegate d = mDisplayListeners.get(index); 218 d.clearEvents(); 219 mDisplayListeners.remove(index); 220 } 221 } 222 } 223 224 private int findDisplayListenerLocked(DisplayListener listener) { 225 final int numListeners = mDisplayListeners.size(); 226 for (int i = 0; i < numListeners; i++) { 227 if (mDisplayListeners.get(i).mListener == listener) { 228 return i; 229 } 230 } 231 return -1; 232 } 233 234 private void registerCallbackIfNeededLocked() { 235 if (mCallback == null) { 236 mCallback = new DisplayManagerCallback(); 237 try { 238 mDm.registerCallback(mCallback); 239 } catch (RemoteException ex) { 240 Log.e(TAG, "Failed to register callback with display manager service.", ex); 241 mCallback = null; 242 } 243 } 244 } 245 246 private void handleDisplayEvent(int displayId, int event) { 247 synchronized (mLock) { 248 if (USE_CACHE) { 249 mDisplayInfoCache.remove(displayId); 250 251 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) { 252 mDisplayIdCache = null; 253 } 254 } 255 256 final int numListeners = mDisplayListeners.size(); 257 for (int i = 0; i < numListeners; i++) { 258 mDisplayListeners.get(i).sendDisplayEvent(displayId, event); 259 } 260 } 261 } 262 263 public void startWifiDisplayScan() { 264 synchronized (mLock) { 265 if (mWifiDisplayScanNestCount++ == 0) { 266 registerCallbackIfNeededLocked(); 267 try { 268 mDm.startWifiDisplayScan(); 269 } catch (RemoteException ex) { 270 Log.e(TAG, "Failed to scan for Wifi displays.", ex); 271 } 272 } 273 } 274 } 275 276 public void stopWifiDisplayScan() { 277 synchronized (mLock) { 278 if (--mWifiDisplayScanNestCount == 0) { 279 try { 280 mDm.stopWifiDisplayScan(); 281 } catch (RemoteException ex) { 282 Log.e(TAG, "Failed to scan for Wifi displays.", ex); 283 } 284 } else if (mWifiDisplayScanNestCount < 0) { 285 Log.wtf(TAG, "Wifi display scan nest count became negative: " 286 + mWifiDisplayScanNestCount); 287 mWifiDisplayScanNestCount = 0; 288 } 289 } 290 } 291 292 public void connectWifiDisplay(String deviceAddress) { 293 if (deviceAddress == null) { 294 throw new IllegalArgumentException("deviceAddress must not be null"); 295 } 296 297 try { 298 mDm.connectWifiDisplay(deviceAddress); 299 } catch (RemoteException ex) { 300 Log.e(TAG, "Failed to connect to Wifi display " + deviceAddress + ".", ex); 301 } 302 } 303 304 public void pauseWifiDisplay() { 305 try { 306 mDm.pauseWifiDisplay(); 307 } catch (RemoteException ex) { 308 Log.e(TAG, "Failed to pause Wifi display.", ex); 309 } 310 } 311 312 public void resumeWifiDisplay() { 313 try { 314 mDm.resumeWifiDisplay(); 315 } catch (RemoteException ex) { 316 Log.e(TAG, "Failed to resume Wifi display.", ex); 317 } 318 } 319 320 public void disconnectWifiDisplay() { 321 try { 322 mDm.disconnectWifiDisplay(); 323 } catch (RemoteException ex) { 324 Log.e(TAG, "Failed to disconnect from Wifi display.", ex); 325 } 326 } 327 328 public void renameWifiDisplay(String deviceAddress, String alias) { 329 if (deviceAddress == null) { 330 throw new IllegalArgumentException("deviceAddress must not be null"); 331 } 332 333 try { 334 mDm.renameWifiDisplay(deviceAddress, alias); 335 } catch (RemoteException ex) { 336 Log.e(TAG, "Failed to rename Wifi display " + deviceAddress 337 + " with alias " + alias + ".", ex); 338 } 339 } 340 341 public void forgetWifiDisplay(String deviceAddress) { 342 if (deviceAddress == null) { 343 throw new IllegalArgumentException("deviceAddress must not be null"); 344 } 345 346 try { 347 mDm.forgetWifiDisplay(deviceAddress); 348 } catch (RemoteException ex) { 349 Log.e(TAG, "Failed to forget Wifi display.", ex); 350 } 351 } 352 353 public WifiDisplayStatus getWifiDisplayStatus() { 354 try { 355 return mDm.getWifiDisplayStatus(); 356 } catch (RemoteException ex) { 357 Log.e(TAG, "Failed to get Wifi display status.", ex); 358 return new WifiDisplayStatus(); 359 } 360 } 361 362 public void requestColorTransform(int displayId, int colorTransformId) { 363 try { 364 mDm.requestColorTransform(displayId, colorTransformId); 365 } catch (RemoteException ex) { 366 Log.e(TAG, "Failed to request color transform.", ex); 367 } 368 } 369 370 public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection, 371 String name, int width, int height, int densityDpi, Surface surface, int flags, 372 VirtualDisplay.Callback callback, Handler handler) { 373 if (TextUtils.isEmpty(name)) { 374 throw new IllegalArgumentException("name must be non-null and non-empty"); 375 } 376 if (width <= 0 || height <= 0 || densityDpi <= 0) { 377 throw new IllegalArgumentException("width, height, and densityDpi must be " 378 + "greater than 0"); 379 } 380 381 VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler); 382 IMediaProjection projectionToken = projection != null ? projection.getProjection() : null; 383 int displayId; 384 try { 385 displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken, 386 context.getPackageName(), name, width, height, densityDpi, surface, flags); 387 } catch (RemoteException ex) { 388 Log.e(TAG, "Could not create virtual display: " + name, ex); 389 return null; 390 } 391 if (displayId < 0) { 392 Log.e(TAG, "Could not create virtual display: " + name); 393 return null; 394 } 395 Display display = getRealDisplay(displayId); 396 if (display == null) { 397 Log.wtf(TAG, "Could not obtain display info for newly created " 398 + "virtual display: " + name); 399 try { 400 mDm.releaseVirtualDisplay(callbackWrapper); 401 } catch (RemoteException ex) { 402 } 403 return null; 404 } 405 return new VirtualDisplay(this, display, callbackWrapper, surface); 406 } 407 408 public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) { 409 try { 410 mDm.setVirtualDisplaySurface(token, surface); 411 } catch (RemoteException ex) { 412 Log.w(TAG, "Failed to set virtual display surface.", ex); 413 } 414 } 415 416 public void resizeVirtualDisplay(IVirtualDisplayCallback token, 417 int width, int height, int densityDpi) { 418 try { 419 mDm.resizeVirtualDisplay(token, width, height, densityDpi); 420 } catch (RemoteException ex) { 421 Log.w(TAG, "Failed to resize virtual display.", ex); 422 } 423 } 424 425 public void releaseVirtualDisplay(IVirtualDisplayCallback token) { 426 try { 427 mDm.releaseVirtualDisplay(token); 428 } catch (RemoteException ex) { 429 Log.w(TAG, "Failed to release virtual display.", ex); 430 } 431 } 432 433 private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub { 434 @Override 435 public void onDisplayEvent(int displayId, int event) { 436 if (DEBUG) { 437 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event); 438 } 439 handleDisplayEvent(displayId, event); 440 } 441 } 442 443 private static final class DisplayListenerDelegate extends Handler { 444 public final DisplayListener mListener; 445 446 public DisplayListenerDelegate(DisplayListener listener, Handler handler) { 447 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); 448 mListener = listener; 449 } 450 451 public void sendDisplayEvent(int displayId, int event) { 452 Message msg = obtainMessage(event, displayId, 0); 453 sendMessage(msg); 454 } 455 456 public void clearEvents() { 457 removeCallbacksAndMessages(null); 458 } 459 460 @Override 461 public void handleMessage(Message msg) { 462 switch (msg.what) { 463 case EVENT_DISPLAY_ADDED: 464 mListener.onDisplayAdded(msg.arg1); 465 break; 466 case EVENT_DISPLAY_CHANGED: 467 mListener.onDisplayChanged(msg.arg1); 468 break; 469 case EVENT_DISPLAY_REMOVED: 470 mListener.onDisplayRemoved(msg.arg1); 471 break; 472 } 473 } 474 } 475 476 private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub { 477 private VirtualDisplayCallbackDelegate mDelegate; 478 479 public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) { 480 if (callback != null) { 481 mDelegate = new VirtualDisplayCallbackDelegate(callback, handler); 482 } 483 } 484 485 @Override // Binder call 486 public void onPaused() { 487 if (mDelegate != null) { 488 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED); 489 } 490 } 491 492 @Override // Binder call 493 public void onResumed() { 494 if (mDelegate != null) { 495 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED); 496 } 497 } 498 499 @Override // Binder call 500 public void onStopped() { 501 if (mDelegate != null) { 502 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED); 503 } 504 } 505 } 506 507 private final static class VirtualDisplayCallbackDelegate extends Handler { 508 public static final int MSG_DISPLAY_PAUSED = 0; 509 public static final int MSG_DISPLAY_RESUMED = 1; 510 public static final int MSG_DISPLAY_STOPPED = 2; 511 512 private final VirtualDisplay.Callback mCallback; 513 514 public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback, 515 Handler handler) { 516 super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/); 517 mCallback = callback; 518 } 519 520 @Override 521 public void handleMessage(Message msg) { 522 switch (msg.what) { 523 case MSG_DISPLAY_PAUSED: 524 mCallback.onPaused(); 525 break; 526 case MSG_DISPLAY_RESUMED: 527 mCallback.onResumed(); 528 break; 529 case MSG_DISPLAY_STOPPED: 530 mCallback.onStopped(); 531 break; 532 } 533 } 534 } 535} 536