Home | History | Annotate | Download | only in display
      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 int mWifiDisplayScanNestCount;
     76 
     77     private DisplayManagerGlobal(IDisplayManager dm) {
     78         mDm = dm;
     79     }
     80 
     81     /**
     82      * Gets an instance of the display manager global singleton.
     83      *
     84      * @return The display manager instance, may be null early in system startup
     85      * before the display manager has been fully initialized.
     86      */
     87     public static DisplayManagerGlobal getInstance() {
     88         synchronized (DisplayManagerGlobal.class) {
     89             if (sInstance == null) {
     90                 IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
     91                 if (b != null) {
     92                     sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
     93                 }
     94             }
     95             return sInstance;
     96         }
     97     }
     98 
     99     /**
    100      * Get information about a particular logical display.
    101      *
    102      * @param displayId The logical display id.
    103      * @return Information about the specified display, or null if it does not exist.
    104      * This object belongs to an internal cache and should be treated as if it were immutable.
    105      */
    106     public DisplayInfo getDisplayInfo(int displayId) {
    107         try {
    108             synchronized (mLock) {
    109                 DisplayInfo info;
    110                 if (USE_CACHE) {
    111                     info = mDisplayInfoCache.get(displayId);
    112                     if (info != null) {
    113                         return info;
    114                     }
    115                 }
    116 
    117                 info = mDm.getDisplayInfo(displayId);
    118                 if (info == null) {
    119                     return null;
    120                 }
    121 
    122                 if (USE_CACHE) {
    123                     mDisplayInfoCache.put(displayId, info);
    124                 }
    125                 registerCallbackIfNeededLocked();
    126 
    127                 if (DEBUG) {
    128                     Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
    129                 }
    130                 return info;
    131             }
    132         } catch (RemoteException ex) {
    133             Log.e(TAG, "Could not get display information from display manager.", ex);
    134             return null;
    135         }
    136     }
    137 
    138     /**
    139      * Gets all currently valid logical display ids.
    140      *
    141      * @return An array containing all display ids.
    142      */
    143     public int[] getDisplayIds() {
    144         try {
    145             synchronized (mLock) {
    146                 if (USE_CACHE) {
    147                     if (mDisplayIdCache != null) {
    148                         return mDisplayIdCache;
    149                     }
    150                 }
    151 
    152                 int[] displayIds = mDm.getDisplayIds();
    153                 if (USE_CACHE) {
    154                     mDisplayIdCache = displayIds;
    155                 }
    156                 registerCallbackIfNeededLocked();
    157                 return displayIds;
    158             }
    159         } catch (RemoteException ex) {
    160             Log.e(TAG, "Could not get display ids from display manager.", ex);
    161             return new int[] { Display.DEFAULT_DISPLAY };
    162         }
    163     }
    164 
    165     /**
    166      * Gets information about a logical display.
    167      *
    168      * The display metrics may be adjusted to provide compatibility
    169      * for legacy applications or limited screen areas.
    170      *
    171      * @param displayId The logical display id.
    172      * @param daj The compatibility info and activityToken.
    173      * @return The display object, or null if there is no display with the given id.
    174      */
    175     public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
    176         DisplayInfo displayInfo = getDisplayInfo(displayId);
    177         if (displayInfo == null) {
    178             return null;
    179         }
    180         return new Display(this, displayId, displayInfo, daj);
    181     }
    182 
    183     /**
    184      * Gets information about a logical display without applying any compatibility metrics.
    185      *
    186      * @param displayId The logical display id.
    187      * @return The display object, or null if there is no display with the given id.
    188      */
    189     public Display getRealDisplay(int displayId) {
    190         return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
    191     }
    192 
    193     /**
    194      * Gets information about a logical display without applying any compatibility metrics.
    195      *
    196      * @param displayId The logical display id.
    197      * @param IBinder the activity token for this display.
    198      * @return The display object, or null if there is no display with the given id.
    199      */
    200     public Display getRealDisplay(int displayId, IBinder token) {
    201         return getCompatibleDisplay(displayId, new DisplayAdjustments(token));
    202     }
    203 
    204     public void registerDisplayListener(DisplayListener listener, Handler handler) {
    205         if (listener == null) {
    206             throw new IllegalArgumentException("listener must not be null");
    207         }
    208 
    209         synchronized (mLock) {
    210             int index = findDisplayListenerLocked(listener);
    211             if (index < 0) {
    212                 mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
    213                 registerCallbackIfNeededLocked();
    214             }
    215         }
    216     }
    217 
    218     public void unregisterDisplayListener(DisplayListener listener) {
    219         if (listener == null) {
    220             throw new IllegalArgumentException("listener must not be null");
    221         }
    222 
    223         synchronized (mLock) {
    224             int index = findDisplayListenerLocked(listener);
    225             if (index >= 0) {
    226                 DisplayListenerDelegate d = mDisplayListeners.get(index);
    227                 d.clearEvents();
    228                 mDisplayListeners.remove(index);
    229             }
    230         }
    231     }
    232 
    233     private int findDisplayListenerLocked(DisplayListener listener) {
    234         final int numListeners = mDisplayListeners.size();
    235         for (int i = 0; i < numListeners; i++) {
    236             if (mDisplayListeners.get(i).mListener == listener) {
    237                 return i;
    238             }
    239         }
    240         return -1;
    241     }
    242 
    243     private void registerCallbackIfNeededLocked() {
    244         if (mCallback == null) {
    245             mCallback = new DisplayManagerCallback();
    246             try {
    247                 mDm.registerCallback(mCallback);
    248             } catch (RemoteException ex) {
    249                 Log.e(TAG, "Failed to register callback with display manager service.", ex);
    250                 mCallback = null;
    251             }
    252         }
    253     }
    254 
    255     private void handleDisplayEvent(int displayId, int event) {
    256         synchronized (mLock) {
    257             if (USE_CACHE) {
    258                 mDisplayInfoCache.remove(displayId);
    259 
    260                 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
    261                     mDisplayIdCache = null;
    262                 }
    263             }
    264 
    265             final int numListeners = mDisplayListeners.size();
    266             for (int i = 0; i < numListeners; i++) {
    267                 mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
    268             }
    269         }
    270     }
    271 
    272     public void startWifiDisplayScan() {
    273         synchronized (mLock) {
    274             if (mWifiDisplayScanNestCount++ == 0) {
    275                 registerCallbackIfNeededLocked();
    276                 try {
    277                     mDm.startWifiDisplayScan();
    278                 } catch (RemoteException ex) {
    279                     Log.e(TAG, "Failed to scan for Wifi displays.", ex);
    280                 }
    281             }
    282         }
    283     }
    284 
    285     public void stopWifiDisplayScan() {
    286         synchronized (mLock) {
    287             if (--mWifiDisplayScanNestCount == 0) {
    288                 try {
    289                     mDm.stopWifiDisplayScan();
    290                 } catch (RemoteException ex) {
    291                     Log.e(TAG, "Failed to scan for Wifi displays.", ex);
    292                 }
    293             } else if (mWifiDisplayScanNestCount < 0) {
    294                 Log.wtf(TAG, "Wifi display scan nest count became negative: "
    295                         + mWifiDisplayScanNestCount);
    296                 mWifiDisplayScanNestCount = 0;
    297             }
    298         }
    299     }
    300 
    301     public void connectWifiDisplay(String deviceAddress) {
    302         if (deviceAddress == null) {
    303             throw new IllegalArgumentException("deviceAddress must not be null");
    304         }
    305 
    306         try {
    307             mDm.connectWifiDisplay(deviceAddress);
    308         } catch (RemoteException ex) {
    309             Log.e(TAG, "Failed to connect to Wifi display " + deviceAddress + ".", ex);
    310         }
    311     }
    312 
    313     public void pauseWifiDisplay() {
    314         try {
    315             mDm.pauseWifiDisplay();
    316         } catch (RemoteException ex) {
    317             Log.e(TAG, "Failed to pause Wifi display.", ex);
    318         }
    319     }
    320 
    321     public void resumeWifiDisplay() {
    322         try {
    323             mDm.resumeWifiDisplay();
    324         } catch (RemoteException ex) {
    325             Log.e(TAG, "Failed to resume Wifi display.", ex);
    326         }
    327     }
    328 
    329     public void disconnectWifiDisplay() {
    330         try {
    331             mDm.disconnectWifiDisplay();
    332         } catch (RemoteException ex) {
    333             Log.e(TAG, "Failed to disconnect from Wifi display.", ex);
    334         }
    335     }
    336 
    337     public void renameWifiDisplay(String deviceAddress, String alias) {
    338         if (deviceAddress == null) {
    339             throw new IllegalArgumentException("deviceAddress must not be null");
    340         }
    341 
    342         try {
    343             mDm.renameWifiDisplay(deviceAddress, alias);
    344         } catch (RemoteException ex) {
    345             Log.e(TAG, "Failed to rename Wifi display " + deviceAddress
    346                     + " with alias " + alias + ".", ex);
    347         }
    348     }
    349 
    350     public void forgetWifiDisplay(String deviceAddress) {
    351         if (deviceAddress == null) {
    352             throw new IllegalArgumentException("deviceAddress must not be null");
    353         }
    354 
    355         try {
    356             mDm.forgetWifiDisplay(deviceAddress);
    357         } catch (RemoteException ex) {
    358             Log.e(TAG, "Failed to forget Wifi display.", ex);
    359         }
    360     }
    361 
    362     public WifiDisplayStatus getWifiDisplayStatus() {
    363         try {
    364             return mDm.getWifiDisplayStatus();
    365         } catch (RemoteException ex) {
    366             Log.e(TAG, "Failed to get Wifi display status.", ex);
    367             return new WifiDisplayStatus();
    368         }
    369     }
    370 
    371     public VirtualDisplay createVirtualDisplay(Context context, String name,
    372             int width, int height, int densityDpi, Surface surface, int flags) {
    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         if (surface == null) {
    381             throw new IllegalArgumentException("surface must not be null");
    382         }
    383 
    384         Binder token = new Binder();
    385         int displayId;
    386         try {
    387             displayId = mDm.createVirtualDisplay(token, context.getPackageName(),
    388                     name, width, height, densityDpi, surface, flags);
    389         } catch (RemoteException ex) {
    390             Log.e(TAG, "Could not create virtual display: " + name, ex);
    391             return null;
    392         }
    393         if (displayId < 0) {
    394             Log.e(TAG, "Could not create virtual display: " + name);
    395             return null;
    396         }
    397         Display display = getRealDisplay(displayId);
    398         if (display == null) {
    399             Log.wtf(TAG, "Could not obtain display info for newly created "
    400                     + "virtual display: " + name);
    401             try {
    402                 mDm.releaseVirtualDisplay(token);
    403             } catch (RemoteException ex) {
    404             }
    405             return null;
    406         }
    407         return new VirtualDisplay(this, display, token);
    408     }
    409 
    410     public void releaseVirtualDisplay(IBinder token) {
    411         try {
    412             mDm.releaseVirtualDisplay(token);
    413         } catch (RemoteException ex) {
    414             Log.w(TAG, "Failed to release virtual display.", ex);
    415         }
    416     }
    417 
    418     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
    419         @Override
    420         public void onDisplayEvent(int displayId, int event) {
    421             if (DEBUG) {
    422                 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
    423             }
    424             handleDisplayEvent(displayId, event);
    425         }
    426     }
    427 
    428     private static final class DisplayListenerDelegate extends Handler {
    429         public final DisplayListener mListener;
    430 
    431         public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
    432             super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
    433             mListener = listener;
    434         }
    435 
    436         public void sendDisplayEvent(int displayId, int event) {
    437             Message msg = obtainMessage(event, displayId, 0);
    438             sendMessage(msg);
    439         }
    440 
    441         public void clearEvents() {
    442             removeCallbacksAndMessages(null);
    443         }
    444 
    445         @Override
    446         public void handleMessage(Message msg) {
    447             switch (msg.what) {
    448                 case EVENT_DISPLAY_ADDED:
    449                     mListener.onDisplayAdded(msg.arg1);
    450                     break;
    451                 case EVENT_DISPLAY_CHANGED:
    452                     mListener.onDisplayChanged(msg.arg1);
    453                     break;
    454                 case EVENT_DISPLAY_REMOVED:
    455                     mListener.onDisplayRemoved(msg.arg1);
    456                     break;
    457             }
    458         }
    459     }
    460 }
    461