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 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