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