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