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