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