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.media.projection.MediaProjection;
     22 import android.media.projection.IMediaProjection;
     23 import android.os.Binder;
     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     /**
    196      * Gets information about a logical display without applying any compatibility metrics.
    197      *
    198      * @param displayId The logical display id.
    199      * @param IBinder the activity token for this display.
    200      * @return The display object, or null if there is no display with the given id.
    201      */
    202     public Display getRealDisplay(int displayId, IBinder token) {
    203         return getCompatibleDisplay(displayId, new DisplayAdjustments(token));
    204     }
    205 
    206     public void registerDisplayListener(DisplayListener listener, Handler handler) {
    207         if (listener == null) {
    208             throw new IllegalArgumentException("listener must not be null");
    209         }
    210 
    211         synchronized (mLock) {
    212             int index = findDisplayListenerLocked(listener);
    213             if (index < 0) {
    214                 mDisplayListeners.add(new DisplayListenerDelegate(listener, handler));
    215                 registerCallbackIfNeededLocked();
    216             }
    217         }
    218     }
    219 
    220     public void unregisterDisplayListener(DisplayListener listener) {
    221         if (listener == null) {
    222             throw new IllegalArgumentException("listener must not be null");
    223         }
    224 
    225         synchronized (mLock) {
    226             int index = findDisplayListenerLocked(listener);
    227             if (index >= 0) {
    228                 DisplayListenerDelegate d = mDisplayListeners.get(index);
    229                 d.clearEvents();
    230                 mDisplayListeners.remove(index);
    231             }
    232         }
    233     }
    234 
    235     private int findDisplayListenerLocked(DisplayListener listener) {
    236         final int numListeners = mDisplayListeners.size();
    237         for (int i = 0; i < numListeners; i++) {
    238             if (mDisplayListeners.get(i).mListener == listener) {
    239                 return i;
    240             }
    241         }
    242         return -1;
    243     }
    244 
    245     private void registerCallbackIfNeededLocked() {
    246         if (mCallback == null) {
    247             mCallback = new DisplayManagerCallback();
    248             try {
    249                 mDm.registerCallback(mCallback);
    250             } catch (RemoteException ex) {
    251                 Log.e(TAG, "Failed to register callback with display manager service.", ex);
    252                 mCallback = null;
    253             }
    254         }
    255     }
    256 
    257     private void handleDisplayEvent(int displayId, int event) {
    258         synchronized (mLock) {
    259             if (USE_CACHE) {
    260                 mDisplayInfoCache.remove(displayId);
    261 
    262                 if (event == EVENT_DISPLAY_ADDED || event == EVENT_DISPLAY_REMOVED) {
    263                     mDisplayIdCache = null;
    264                 }
    265             }
    266 
    267             final int numListeners = mDisplayListeners.size();
    268             for (int i = 0; i < numListeners; i++) {
    269                 mDisplayListeners.get(i).sendDisplayEvent(displayId, event);
    270             }
    271         }
    272     }
    273 
    274     public void startWifiDisplayScan() {
    275         synchronized (mLock) {
    276             if (mWifiDisplayScanNestCount++ == 0) {
    277                 registerCallbackIfNeededLocked();
    278                 try {
    279                     mDm.startWifiDisplayScan();
    280                 } catch (RemoteException ex) {
    281                     Log.e(TAG, "Failed to scan for Wifi displays.", ex);
    282                 }
    283             }
    284         }
    285     }
    286 
    287     public void stopWifiDisplayScan() {
    288         synchronized (mLock) {
    289             if (--mWifiDisplayScanNestCount == 0) {
    290                 try {
    291                     mDm.stopWifiDisplayScan();
    292                 } catch (RemoteException ex) {
    293                     Log.e(TAG, "Failed to scan for Wifi displays.", ex);
    294                 }
    295             } else if (mWifiDisplayScanNestCount < 0) {
    296                 Log.wtf(TAG, "Wifi display scan nest count became negative: "
    297                         + mWifiDisplayScanNestCount);
    298                 mWifiDisplayScanNestCount = 0;
    299             }
    300         }
    301     }
    302 
    303     public void connectWifiDisplay(String deviceAddress) {
    304         if (deviceAddress == null) {
    305             throw new IllegalArgumentException("deviceAddress must not be null");
    306         }
    307 
    308         try {
    309             mDm.connectWifiDisplay(deviceAddress);
    310         } catch (RemoteException ex) {
    311             Log.e(TAG, "Failed to connect to Wifi display " + deviceAddress + ".", ex);
    312         }
    313     }
    314 
    315     public void pauseWifiDisplay() {
    316         try {
    317             mDm.pauseWifiDisplay();
    318         } catch (RemoteException ex) {
    319             Log.e(TAG, "Failed to pause Wifi display.", ex);
    320         }
    321     }
    322 
    323     public void resumeWifiDisplay() {
    324         try {
    325             mDm.resumeWifiDisplay();
    326         } catch (RemoteException ex) {
    327             Log.e(TAG, "Failed to resume Wifi display.", ex);
    328         }
    329     }
    330 
    331     public void disconnectWifiDisplay() {
    332         try {
    333             mDm.disconnectWifiDisplay();
    334         } catch (RemoteException ex) {
    335             Log.e(TAG, "Failed to disconnect from Wifi display.", ex);
    336         }
    337     }
    338 
    339     public void renameWifiDisplay(String deviceAddress, String alias) {
    340         if (deviceAddress == null) {
    341             throw new IllegalArgumentException("deviceAddress must not be null");
    342         }
    343 
    344         try {
    345             mDm.renameWifiDisplay(deviceAddress, alias);
    346         } catch (RemoteException ex) {
    347             Log.e(TAG, "Failed to rename Wifi display " + deviceAddress
    348                     + " with alias " + alias + ".", ex);
    349         }
    350     }
    351 
    352     public void forgetWifiDisplay(String deviceAddress) {
    353         if (deviceAddress == null) {
    354             throw new IllegalArgumentException("deviceAddress must not be null");
    355         }
    356 
    357         try {
    358             mDm.forgetWifiDisplay(deviceAddress);
    359         } catch (RemoteException ex) {
    360             Log.e(TAG, "Failed to forget Wifi display.", ex);
    361         }
    362     }
    363 
    364     public WifiDisplayStatus getWifiDisplayStatus() {
    365         try {
    366             return mDm.getWifiDisplayStatus();
    367         } catch (RemoteException ex) {
    368             Log.e(TAG, "Failed to get Wifi display status.", ex);
    369             return new WifiDisplayStatus();
    370         }
    371     }
    372 
    373     public VirtualDisplay createVirtualDisplay(Context context, MediaProjection projection,
    374             String name, int width, int height, int densityDpi, Surface surface, int flags,
    375             VirtualDisplay.Callback callback, Handler handler) {
    376         if (TextUtils.isEmpty(name)) {
    377             throw new IllegalArgumentException("name must be non-null and non-empty");
    378         }
    379         if (width <= 0 || height <= 0 || densityDpi <= 0) {
    380             throw new IllegalArgumentException("width, height, and densityDpi must be "
    381                     + "greater than 0");
    382         }
    383 
    384         VirtualDisplayCallback callbackWrapper = new VirtualDisplayCallback(callback, handler);
    385         IMediaProjection projectionToken = projection != null ? projection.getProjection() : null;
    386         int displayId;
    387         try {
    388             displayId = mDm.createVirtualDisplay(callbackWrapper, projectionToken,
    389                     context.getPackageName(), name, width, height, densityDpi, surface, flags);
    390         } catch (RemoteException ex) {
    391             Log.e(TAG, "Could not create virtual display: " + name, ex);
    392             return null;
    393         }
    394         if (displayId < 0) {
    395             Log.e(TAG, "Could not create virtual display: " + name);
    396             return null;
    397         }
    398         Display display = getRealDisplay(displayId);
    399         if (display == null) {
    400             Log.wtf(TAG, "Could not obtain display info for newly created "
    401                     + "virtual display: " + name);
    402             try {
    403                 mDm.releaseVirtualDisplay(callbackWrapper);
    404             } catch (RemoteException ex) {
    405             }
    406             return null;
    407         }
    408         return new VirtualDisplay(this, display, callbackWrapper, surface);
    409     }
    410 
    411     public void setVirtualDisplaySurface(IVirtualDisplayCallback token, Surface surface) {
    412         try {
    413             mDm.setVirtualDisplaySurface(token, surface);
    414         } catch (RemoteException ex) {
    415             Log.w(TAG, "Failed to set virtual display surface.", ex);
    416         }
    417     }
    418 
    419     public void resizeVirtualDisplay(IVirtualDisplayCallback token,
    420             int width, int height, int densityDpi) {
    421         try {
    422             mDm.resizeVirtualDisplay(token, width, height, densityDpi);
    423         } catch (RemoteException ex) {
    424             Log.w(TAG, "Failed to resize virtual display.", ex);
    425         }
    426     }
    427 
    428     public void releaseVirtualDisplay(IVirtualDisplayCallback token) {
    429         try {
    430             mDm.releaseVirtualDisplay(token);
    431         } catch (RemoteException ex) {
    432             Log.w(TAG, "Failed to release virtual display.", ex);
    433         }
    434     }
    435 
    436     private final class DisplayManagerCallback extends IDisplayManagerCallback.Stub {
    437         @Override
    438         public void onDisplayEvent(int displayId, int event) {
    439             if (DEBUG) {
    440                 Log.d(TAG, "onDisplayEvent: displayId=" + displayId + ", event=" + event);
    441             }
    442             handleDisplayEvent(displayId, event);
    443         }
    444     }
    445 
    446     private static final class DisplayListenerDelegate extends Handler {
    447         public final DisplayListener mListener;
    448 
    449         public DisplayListenerDelegate(DisplayListener listener, Handler handler) {
    450             super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
    451             mListener = listener;
    452         }
    453 
    454         public void sendDisplayEvent(int displayId, int event) {
    455             Message msg = obtainMessage(event, displayId, 0);
    456             sendMessage(msg);
    457         }
    458 
    459         public void clearEvents() {
    460             removeCallbacksAndMessages(null);
    461         }
    462 
    463         @Override
    464         public void handleMessage(Message msg) {
    465             switch (msg.what) {
    466                 case EVENT_DISPLAY_ADDED:
    467                     mListener.onDisplayAdded(msg.arg1);
    468                     break;
    469                 case EVENT_DISPLAY_CHANGED:
    470                     mListener.onDisplayChanged(msg.arg1);
    471                     break;
    472                 case EVENT_DISPLAY_REMOVED:
    473                     mListener.onDisplayRemoved(msg.arg1);
    474                     break;
    475             }
    476         }
    477     }
    478 
    479     private final static class VirtualDisplayCallback extends IVirtualDisplayCallback.Stub {
    480         private VirtualDisplayCallbackDelegate mDelegate;
    481 
    482         public VirtualDisplayCallback(VirtualDisplay.Callback callback, Handler handler) {
    483             if (callback != null) {
    484                 mDelegate = new VirtualDisplayCallbackDelegate(callback, handler);
    485             }
    486         }
    487 
    488         @Override // Binder call
    489         public void onPaused() {
    490             if (mDelegate != null) {
    491                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_PAUSED);
    492             }
    493         }
    494 
    495         @Override // Binder call
    496         public void onResumed() {
    497             if (mDelegate != null) {
    498                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_RESUMED);
    499             }
    500         }
    501 
    502         @Override // Binder call
    503         public void onStopped() {
    504             if (mDelegate != null) {
    505                 mDelegate.sendEmptyMessage(VirtualDisplayCallbackDelegate.MSG_DISPLAY_STOPPED);
    506             }
    507         }
    508     }
    509 
    510     private final static class VirtualDisplayCallbackDelegate extends Handler {
    511         public static final int MSG_DISPLAY_PAUSED = 0;
    512         public static final int MSG_DISPLAY_RESUMED = 1;
    513         public static final int MSG_DISPLAY_STOPPED = 2;
    514 
    515         private final VirtualDisplay.Callback mCallback;
    516 
    517         public VirtualDisplayCallbackDelegate(VirtualDisplay.Callback callback,
    518                 Handler handler) {
    519             super(handler != null ? handler.getLooper() : Looper.myLooper(), null, true /*async*/);
    520             mCallback = callback;
    521         }
    522 
    523         @Override
    524         public void handleMessage(Message msg) {
    525             switch (msg.what) {
    526                 case MSG_DISPLAY_PAUSED:
    527                     mCallback.onPaused();
    528                     break;
    529                 case MSG_DISPLAY_RESUMED:
    530                     mCallback.onResumed();
    531                     break;
    532                 case MSG_DISPLAY_STOPPED:
    533                     mCallback.onStopped();
    534                     break;
    535             }
    536         }
    537     }
    538 }
    539