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