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.pm.ParceledListSlice;
     21 import android.content.res.Resources;
     22 import android.graphics.Point;
     23 import android.hardware.display.DisplayManager.DisplayListener;
     24 import android.media.projection.IMediaProjection;
     25 import android.media.projection.MediaProjection;
     26 import android.os.Handler;
     27 import android.os.IBinder;
     28 import android.os.Looper;
     29 import android.os.Message;
     30 import android.os.RemoteException;
     31 import android.os.ServiceManager;
     32 import android.text.TextUtils;
     33 import android.util.Log;
     34 import android.util.Pair;
     35 import android.util.SparseArray;
     36 import android.view.Display;
     37 import android.view.DisplayAdjustments;
     38 import android.view.DisplayInfo;
     39 import android.view.Surface;
     40 
     41 import java.util.ArrayList;
     42 import java.util.Collections;
     43 import java.util.List;
     44 
     45 /**
     46  * Manager communication with the display manager service on behalf of
     47  * an application process.  You're probably looking for {@link DisplayManager}.
     48  *
     49  * @hide
     50  */
     51 public final class DisplayManagerGlobal {
     52     private static final String TAG = "DisplayManager";
     53     private static final boolean DEBUG = false;
     54 
     55     // True if display info and display ids should be cached.
     56     //
     57     // FIXME: The cache is currently disabled because it's unclear whether we have the
     58     // necessary guarantees that the caches will always be flushed before clients
     59     // attempt to observe their new state.  For example, depending on the order
     60     // in which the binder transactions take place, we might have a problem where
     61     // an application could start processing a configuration change due to a display
     62     // orientation change before the display info cache has actually been invalidated.
     63     private static final boolean USE_CACHE = false;
     64 
     65     public static final int EVENT_DISPLAY_ADDED = 1;
     66     public static final int EVENT_DISPLAY_CHANGED = 2;
     67     public static final int EVENT_DISPLAY_REMOVED = 3;
     68 
     69     private static DisplayManagerGlobal sInstance;
     70 
     71     private final Object mLock = new Object();
     72 
     73     private final IDisplayManager mDm;
     74 
     75     private DisplayManagerCallback mCallback;
     76     private final ArrayList<DisplayListenerDelegate> mDisplayListeners =
     77             new ArrayList<DisplayListenerDelegate>();
     78 
     79     private final SparseArray<DisplayInfo> mDisplayInfoCache = new SparseArray<DisplayInfo>();
     80     private int[] mDisplayIdCache;
     81 
     82     private int mWifiDisplayScanNestCount;
     83 
     84     private DisplayManagerGlobal(IDisplayManager dm) {
     85         mDm = dm;
     86     }
     87 
     88     /**
     89      * Gets an instance of the display manager global singleton.
     90      *
     91      * @return The display manager instance, may be null early in system startup
     92      * before the display manager has been fully initialized.
     93      */
     94     public static DisplayManagerGlobal getInstance() {
     95         synchronized (DisplayManagerGlobal.class) {
     96             if (sInstance == null) {
     97                 IBinder b = ServiceManager.getService(Context.DISPLAY_SERVICE);
     98                 if (b != null) {
     99                     sInstance = new DisplayManagerGlobal(IDisplayManager.Stub.asInterface(b));
    100                 }
    101             }
    102             return sInstance;
    103         }
    104     }
    105 
    106     /**
    107      * Get information about a particular logical display.
    108      *
    109      * @param displayId The logical display id.
    110      * @return Information about the specified display, or null if it does not exist.
    111      * This object belongs to an internal cache and should be treated as if it were immutable.
    112      */
    113     public DisplayInfo getDisplayInfo(int displayId) {
    114         try {
    115             synchronized (mLock) {
    116                 DisplayInfo info;
    117                 if (USE_CACHE) {
    118                     info = mDisplayInfoCache.get(displayId);
    119                     if (info != null) {
    120                         return info;
    121                     }
    122                 }
    123 
    124                 info = mDm.getDisplayInfo(displayId);
    125                 if (info == null) {
    126                     return null;
    127                 }
    128 
    129                 if (USE_CACHE) {
    130                     mDisplayInfoCache.put(displayId, info);
    131                 }
    132                 registerCallbackIfNeededLocked();
    133 
    134                 if (DEBUG) {
    135                     Log.d(TAG, "getDisplayInfo: displayId=" + displayId + ", info=" + info);
    136                 }
    137                 return info;
    138             }
    139         } catch (RemoteException ex) {
    140             throw ex.rethrowFromSystemServer();
    141         }
    142     }
    143 
    144     /**
    145      * Gets all currently valid logical display ids.
    146      *
    147      * @return An array containing all display ids.
    148      */
    149     public int[] getDisplayIds() {
    150         try {
    151             synchronized (mLock) {
    152                 if (USE_CACHE) {
    153                     if (mDisplayIdCache != null) {
    154                         return mDisplayIdCache;
    155                     }
    156                 }
    157 
    158                 int[] displayIds = mDm.getDisplayIds();
    159                 if (USE_CACHE) {
    160                     mDisplayIdCache = displayIds;
    161                 }
    162                 registerCallbackIfNeededLocked();
    163                 return displayIds;
    164             }
    165         } catch (RemoteException ex) {
    166             throw ex.rethrowFromSystemServer();
    167         }
    168     }
    169 
    170     /**
    171      * Gets information about a logical display.
    172      *
    173      * The display metrics may be adjusted to provide compatibility
    174      * for legacy applications or limited screen areas.
    175      *
    176      * @param displayId The logical display id.
    177      * @param daj The compatibility info and activityToken.
    178      * @return The display object, or null if there is no display with the given id.
    179      */
    180     public Display getCompatibleDisplay(int displayId, DisplayAdjustments daj) {
    181         DisplayInfo displayInfo = getDisplayInfo(displayId);
    182         if (displayInfo == null) {
    183             return null;
    184         }
    185         return new Display(this, displayId, displayInfo, daj);
    186     }
    187 
    188     /**
    189      * Gets information about a logical display.
    190      *
    191      * The display metrics may be adjusted to provide compatibility
    192      * for legacy applications or limited screen areas.
    193      *
    194      * @param displayId The logical display id.
    195      * @param resources Resources providing compatibility info.
    196      * @return The display object, or null if there is no display with the given id.
    197      */
    198     public Display getCompatibleDisplay(int displayId, Resources resources) {
    199         DisplayInfo displayInfo = getDisplayInfo(displayId);
    200         if (displayInfo == null) {
    201             return null;
    202         }
    203         return new Display(this, displayId, displayInfo, resources);
    204     }
    205 
    206     /**
    207      * Gets information about a logical display without applying any compatibility metrics.
    208      *
    209      * @param displayId The logical display id.
    210      * @return The display object, or null if there is no display with the given id.
    211      */
    212     public Display getRealDisplay(int displayId) {
    213         return getCompatibleDisplay(displayId, DisplayAdjustments.DEFAULT_DISPLAY_ADJUSTMENTS);
    214     }
    215 
    216     public void registerDisplayListener(DisplayListener listener, Handler handler) {
    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                 mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
    225                 registerCallbackIfNeededLocked();
    226             }
    227         }
    228     }
    229 
    230     public void unregisterDisplayListener(DisplayListener listener) {
    231         if (listener == null) {
    232             throw new IllegalArgumentException("listener must not be null");
    233         }
    234 
    235         synchronized (mLock) {
    236             int index = findDisplayListenerLocked(listener);
    237             if (index >= 0) {
    238                 DisplayListenerDelegate d = mDisplayListeners.get(index);
    239                 d.clearEvents();
    240                 mDisplayListeners.remove(index);
    241             }
    242         }
    243     }
    244 
    245     private int findDisplayListenerLocked(DisplayListener listener) {
    246         final int numListeners = mDisplayListeners.size();
    247         for (int i = 0; i < numListeners; i++) {
    248             if (mDisplayListeners.get(i).mListener == listener) {
    249                 return i;
    250             }
    251         }
    252         return -1;
    253     }
    254 
    255     private void registerCallbackIfNeededLocked() {
    256         if (mCallback == null) {
    257             mCallback = new DisplayManagerCallback();
    258             try {
    259                 mDm.registerCallback(mCallback);
    260             } catch (RemoteException ex) {
    261                 throw ex.rethrowFromSystemServer();
    262             }
    263         }
    264     }
    265 
    266     private void handleDisplayEvent(int displayId, int event) {
    267         synchronized (mLock) {
    268             if (USE_CACHE) {
    269                 mDisplayInfoCache.remove(displayId);
    270 
    271                 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
    272                     mDisplayIdCache = null;
    273                 }
    274             }
    275 
    276             final int numListeners = mDisplayListeners.size();
    277             for (int i = 0; i < numListeners; i++) {
    278                 mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
    279             }
    280         }
    281     }
    282 
    283     public void startWifiDisplayScan() {
    284         synchronized (mLock) {
    285             if (mWifiDisplayScanNestCount++ == 0) {
    286                 registerCallbackIfNeededLocked();
    287                 try {
    288                     mDm.startWifiDisplayScan();
    289                 } catch (RemoteException ex) {
    290                     throw ex.rethrowFromSystemServer();
    291                 }
    292             }
    293         }
    294     }
    295 
    296     public void stopWifiDisplayScan() {
    297         synchronized (mLock) {
    298             if (--mWifiDisplayScanNestCount == 0) {
    299                 try {
    300                     mDm.stopWifiDisplayScan();
    301                 } catch (RemoteException ex) {
    302                     throw ex.rethrowFromSystemServer();
    303                 }
    304             } else if (mWifiDisplayScanNestCount < 0) {
    305                 Log.wtf(TAG, "Wifi display scan nest count became negative: "
    306                         + mWifiDisplayScanNestCount);
    307                 mWifiDisplayScanNestCount = 0;
    308             }
    309         }
    310     }
    311 
    312     public void connectWifiDisplay(String deviceAddress) {
    313         if (deviceAddress == null) {
    314             throw new IllegalArgumentException("deviceAddress must not be null");
    315         }
    316 
    317         try {
    318             mDm.connectWifiDisplay(deviceAddress);
    319         } catch (RemoteException ex) {
    320             throw ex.rethrowFromSystemServer();
    321         }
    322     }
    323 
    324     public void pauseWifiDisplay() {
    325         try {
    326             mDm.pauseWifiDisplay();
    327         } catch (RemoteException ex) {
    328             throw ex.rethrowFromSystemServer();
    329         }
    330     }
    331 
    332     public void resumeWifiDisplay() {
    333         try {
    334             mDm.resumeWifiDisplay();
    335         } catch (RemoteException ex) {
    336             throw ex.rethrowFromSystemServer();
    337         }
    338     }
    339 
    340     public void disconnectWifiDisplay() {
    341         try {
    342             mDm.disconnectWifiDisplay();
    343         } catch (RemoteException ex) {
    344             throw ex.rethrowFromSystemServer();
    345         }
    346     }
    347 
    348     public void renameWifiDisplay(String deviceAddress, String alias) {
    349         if (deviceAddress == null) {
    350             throw new IllegalArgumentException("deviceAddress must not be null");
    351         }
    352 
    353         try {
    354             mDm.renameWifiDisplay(deviceAddress, alias);
    355         } catch (RemoteException ex) {
    356             throw ex.rethrowFromSystemServer();
    357         }
    358     }
    359 
    360     public void forgetWifiDisplay(String deviceAddress) {
    361         if (deviceAddress == null) {
    362             throw new IllegalArgumentException("deviceAddress must not be null");
    363         }
    364 
    365         try {
    366             mDm.forgetWifiDisplay(deviceAddress);
    367         } catch (RemoteException ex) {
    368             throw ex.rethrowFromSystemServer();
    369         }
    370     }
    371 
    372     public WifiDisplayStatus getWifiDisplayStatus() {
    373         try {
    374             return mDm.getWifiDisplayStatus();
    375         } catch (RemoteException ex) {
    376             throw ex.rethrowFromSystemServer();
    377         }
    378     }
    379 
    380     public void requestColorMode(int displayId, int colorMode) {
    381         try {
    382             mDm.requestColorMode(displayId, colorMode);
    383         } catch (RemoteException ex) {
    384             throw ex.rethrowFromSystemServer();
    385         }
    386     }
    387 
    388     /**
    389      * Set the level of color saturation to apply to the display.
    390      */
    391     public void setSaturationLevel(float level) {
    392         try {
    393             mDm.setSaturationLevel(level);
    394         } catch (RemoteException ex) {
    395             throw ex.rethrowFromSystemServer();
    396         }
    397     }
    398 
    399     public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
    400             String name, int width, int height, int densityDpi, Surface surface, int flags,
    401             VirtualDisplay.Callback callback, Handler handler, String uniqueId) {
    402         if (TextUtils.isEmpty(name)) {
    403             throw new IllegalArgumentException("name must be non-null and non-empty");
    404         }
    405         if (width <= 0 || height <= 0 || densityDpi <= 0) {
    406             throw new IllegalArgumentException("width, height, and densityDpi must be "
    407                     + "greater than 0");
    408         }
    409 
    410         VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
    411         IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
    412         int displayId;
    413         try {
    414             displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken,
    415                     context.getPackageName(), name, width, height, densityDpi, surface, flags,
    416                     uniqueId);
    417         } catch (RemoteException ex) {
    418             throw ex.rethrowFromSystemServer();
    419         }
    420         if (displayId < 0) {
    421             Log.e(TAG, "Could not create virtual display: " + name);
    422             return null;
    423         }
    424         Display display = getRealDisplay(displayId);
    425         if (display == null) {
    426             Log.wtf(TAG, "Could not obtain display info for newly created "
    427                     + "virtual display: " + name);
    428             try {
    429                 mDm.releaseVirtualDisplay(callbackWrapper);
    430             } catch (RemoteException ex) {
    431                 throw ex.rethrowFromSystemServer();
    432             }
    433             return null;
    434         }
    435         return new VirtualDisplay(this, display, callbackWrapper, surface);
    436     }
    437 
    438     public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
    439         try {
    440             mDm.setVirtualDisplaySurface(token, surface);
    441         } catch (RemoteException ex) {
    442             throw ex.rethrowFromSystemServer();
    443         }
    444     }
    445 
    446     public void resizeVirtualDisplay(IVirtualDisplayCallback token,
    447             int width, int height, int densityDpi) {
    448         try {
    449             mDm.resizeVirtualDisplay(token, width, height, densityDpi);
    450         } catch (RemoteException ex) {
    451             throw ex.rethrowFromSystemServer();
    452         }
    453     }
    454 
    455     public void releaseVirtualDisplay(IVirtualDisplayCallback token) {
    456         try {
    457             mDm.releaseVirtualDisplay(token);
    458         } catch (RemoteException ex) {
    459             throw ex.rethrowFromSystemServer();
    460         }
    461     }
    462 
    463     /**
    464      * Gets the stable device display size, in pixels.
    465      */
    466     public Point getStableDisplaySize() {
    467         try {
    468             return mDm.getStableDisplaySize();
    469         } catch (RemoteException ex) {
    470             throw ex.rethrowFromSystemServer();
    471         }
    472     }
    473 
    474     /**
    475      * Retrieves brightness change events.
    476      */
    477     public List<BrightnessChangeEvent> getBrightnessEvents(String callingPackage) {
    478         try {
    479             ParceledListSlice<BrightnessChangeEvent> events =
    480                     mDm.getBrightnessEvents(callingPackage);
    481             if (events == null) {
    482                 return Collections.emptyList();
    483             }
    484             return events.getList();
    485         } catch (RemoteException ex) {
    486             throw ex.rethrowFromSystemServer();
    487         }
    488     }
    489 
    490     /**
    491      * Sets the global brightness configuration for a given user.
    492      *
    493      * @hide
    494      */
    495     public void setBrightnessConfigurationForUser(BrightnessConfiguration c, int userId,
    496             String packageName) {
    497         try {
    498             mDm.setBrightnessConfigurationForUser(c, userId, packageName);
    499         } catch (RemoteException ex) {
    500             throw ex.rethrowFromSystemServer();
    501         }
    502     }
    503 
    504     /**
    505      * Gets the global brightness configuration for a given user or null if one hasn't been set.
    506      *
    507      * @hide
    508      */
    509     public BrightnessConfiguration getBrightnessConfigurationForUser(int userId) {
    510         try {
    511             return mDm.getBrightnessConfigurationForUser(userId);
    512         } catch (RemoteException ex) {
    513             throw ex.rethrowFromSystemServer();
    514         }
    515     }
    516 
    517     /**
    518      * Gets the default brightness configuration or null if one hasn't been configured.
    519      *
    520      * @hide
    521      */
    522     public BrightnessConfiguration getDefaultBrightnessConfiguration() {
    523         try {
    524             return mDm.getDefaultBrightnessConfiguration();
    525         } catch (RemoteException ex) {
    526             throw ex.rethrowFromSystemServer();
    527         }
    528     }
    529 
    530     /**
    531      * Temporarily sets the brightness of the display.
    532      * <p>
    533      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
    534      * </p>
    535      *
    536      * @param brightness The brightness value from 0 to 255.
    537      *
    538      * @hide Requires signature permission.
    539      */
    540     public void setTemporaryBrightness(int brightness) {
    541         try {
    542             mDm.setTemporaryBrightness(brightness);
    543         } catch (RemoteException ex) {
    544             throw ex.rethrowFromSystemServer();
    545         }
    546     }
    547 
    548     /**
    549      * Temporarily sets the auto brightness adjustment factor.
    550      * <p>
    551      * Requires the {@link android.Manifest.permission#CONTROL_DISPLAY_BRIGHTNESS} permission.
    552      * </p>
    553      *
    554      * @param adjustment The adjustment factor from -1.0 to 1.0.
    555      *
    556      * @hide Requires signature permission.
    557      */
    558     public void setTemporaryAutoBrightnessAdjustment(float adjustment) {
    559         try {
    560             mDm.setTemporaryAutoBrightnessAdjustment(adjustment);
    561         } catch (RemoteException ex) {
    562             throw ex.rethrowFromSystemServer();
    563         }
    564     }
    565 
    566     /**
    567      * Returns the minimum brightness curve, which guarantess that any brightness curve that dips
    568      * below it is rejected by the system.
    569      * This prevent auto-brightness from setting the screen so dark as to prevent the user from
    570      * resetting or disabling it, and maps lux to the absolute minimum nits that are still readable
    571      * in that ambient brightness.
    572      *
    573      * @return The minimum brightness curve (as lux values and their corresponding nits values).
    574      */
    575     public Pair<float[], float[]> getMinimumBrightnessCurve() {
    576         try {
    577             Curve curve = mDm.getMinimumBrightnessCurve();
    578             return Pair.create(curve.getX(), curve.getY());
    579         } catch (RemoteException ex) {
    580             throw ex.rethrowFromSystemServer();
    581         }
    582     }
    583 
    584     /**
    585      * Retrieves ambient brightness stats.
    586      */
    587     public List<AmbientBrightnessDayStats> getAmbientBrightnessStats() {
    588         try {
    589             ParceledListSlice<AmbientBrightnessDayStats> stats = mDm.getAmbientBrightnessStats();
    590             if (stats == null) {
    591                 return Collections.emptyList();
    592             }
    593             return stats.getList();
    594         } catch (RemoteException ex) {
    595             throw ex.rethrowFromSystemServer();
    596         }
    597     }
    598 
    599     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
    600         @Override
    601         public void onDisplayEvent(int displayId, int event) {
    602             if (DEBUG) {
    603                 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
    604             }
    605             handleDisplayEvent(displayId, event);
    606         }
    607     }
    608 
    609     private static final class DisplayListenerDelegate extends Handler {
    610         public final DisplayListener mListener;
    611 
    612         public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
    613             super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
    614             mListener = listener;
    615         }
    616 
    617         public void sendDisplayEvent(int displayId, int event) {
    618             Message msg = obtainMessage(event, displayId, 0);
    619             sendMessage(msg);
    620         }
    621 
    622         public void clearEvents() {
    623             removeCallbacksAndMessages(null);
    624         }
    625 
    626         @Override
    627         public void handleMessage(Message msg) {
    628             switch (msg.what) {
    629                 case EVENT_DISPLAY_ADDED:
    630                     mListener.onDisplayAdded(msg.arg1);
    631                     break;
    632                 case EVENT_DISPLAY_CHANGED:
    633                     mListener.onDisplayChanged(msg.arg1);
    634                     break;
    635                 case EVENT_DISPLAY_REMOVED:
    636                     mListener.onDisplayRemoved(msg.arg1);
    637                     break;
    638             }
    639         }
    640     }
    641 
    642     private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
    643         private VirtualDisplayCallbackDelegate mDelegate;
    644 
    645         public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) {
    646             if (callback != null) {
    647                 mDelegate = new VirtualDisplayCallbackDelegate(callback, handler);
    648             }
    649         }
    650 
    651         @Override // Binder call
    652         public void onPaused() {
    653             if (mDelegate != null) {
    654                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED);
    655             }
    656         }
    657 
    658         @Override // Binder call
    659         public void onResumed() {
    660             if (mDelegate != null) {
    661                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED);
    662             }
    663         }
    664 
    665         @Override // Binder call
    666         public void onStopped() {
    667             if (mDelegate != null) {
    668                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED);
    669             }
    670         }
    671     }
    672 
    673     private final static class VirtualDisplayCallbackDelegate extends Handler {
    674         public static final int MSG_DISPLAY_PAUSED = 0;
    675         public static final int MSG_DISPLAY_RESUMED = 1;
    676         public static final int MSG_DISPLAY_STOPPED = 2;
    677 
    678         private final VirtualDisplay.Callback mCallback;
    679 
    680         public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback,
    681                 Handler handler) {
    682             super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
    683             mCallback = callback;
    684         }
    685 
    686         @Override
    687         public void handleMessage(Message msg) {
    688             switch (msg.what) {
    689                 case MSG_DISPLAY_PAUSED:
    690                     mCallback.onPaused();
    691                     break;
    692                 case MSG_DISPLAY_RESUMED:
    693                     mCallback.onResumed();
    694                     break;
    695                 case MSG_DISPLAY_STOPPED:
    696                     mCallback.onStopped();
    697                     break;
    698             }
    699         }
    700     }
    701 }
    702