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 com.android.server.display;
     18 
     19 import com.android.internal.util.DumpUtils;
     20 import com.android.internal.util.IndentingPrintWriter;
     21 
     22 import android.content.Context;
     23 import android.database.ContentObserver;
     24 import android.graphics.SurfaceTexture;
     25 import android.os.Handler;
     26 import android.os.IBinder;
     27 import android.provider.Settings;
     28 import android.util.DisplayMetrics;
     29 import android.util.Slog;
     30 import android.view.Display;
     31 import android.view.Gravity;
     32 import android.view.Surface;
     33 import android.view.SurfaceControl;
     34 
     35 import java.io.PrintWriter;
     36 import java.util.ArrayList;
     37 import java.util.regex.Matcher;
     38 import java.util.regex.Pattern;
     39 
     40 /**
     41  * A display adapter that uses overlay windows to simulate secondary displays
     42  * for development purposes.  Use Development Settings to enable one or more
     43  * overlay displays.
     44  * <p>
     45  * This object has two different handlers (which may be the same) which must not
     46  * get confused.  The main handler is used to posting messages to the display manager
     47  * service as usual.  The UI handler is only used by the {@link OverlayDisplayWindow}.
     48  * </p><p>
     49  * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
     50  * </p>
     51  */
     52 final class OverlayDisplayAdapter extends DisplayAdapter {
     53     static final String TAG = "OverlayDisplayAdapter";
     54     static final boolean DEBUG = false;
     55 
     56     private static final int MIN_WIDTH = 100;
     57     private static final int MIN_HEIGHT = 100;
     58     private static final int MAX_WIDTH = 4096;
     59     private static final int MAX_HEIGHT = 4096;
     60 
     61     private static final Pattern SETTING_PATTERN =
     62             Pattern.compile("(\\d+)x(\\d+)/(\\d+)(,[a-z]+)*");
     63 
     64     private final Handler mUiHandler;
     65     private final ArrayList<OverlayDisplayHandle> mOverlays =
     66             new ArrayList<OverlayDisplayHandle>();
     67     private String mCurrentOverlaySetting = "";
     68 
     69     // Called with SyncRoot lock held.
     70     public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
     71             Context context, Handler handler, Listener listener, Handler uiHandler) {
     72         super(syncRoot, context, handler, listener, TAG);
     73         mUiHandler = uiHandler;
     74     }
     75 
     76     @Override
     77     public void dumpLocked(PrintWriter pw) {
     78         super.dumpLocked(pw);
     79 
     80         pw.println("mCurrentOverlaySetting=" + mCurrentOverlaySetting);
     81         pw.println("mOverlays: size=" + mOverlays.size());
     82         for (OverlayDisplayHandle overlay : mOverlays) {
     83             overlay.dumpLocked(pw);
     84         }
     85     }
     86 
     87     @Override
     88     public void registerLocked() {
     89         super.registerLocked();
     90 
     91         getHandler().post(new Runnable() {
     92             @Override
     93             public void run() {
     94                 getContext().getContentResolver().registerContentObserver(
     95                         Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
     96                         true, new ContentObserver(getHandler()) {
     97                             @Override
     98                             public void onChange(boolean selfChange) {
     99                                 updateOverlayDisplayDevices();
    100                             }
    101                         });
    102 
    103                 updateOverlayDisplayDevices();
    104             }
    105         });
    106     }
    107 
    108     private void updateOverlayDisplayDevices() {
    109         synchronized (getSyncRoot()) {
    110             updateOverlayDisplayDevicesLocked();
    111         }
    112     }
    113 
    114     private void updateOverlayDisplayDevicesLocked() {
    115         String value = Settings.Global.getString(getContext().getContentResolver(),
    116                 Settings.Global.OVERLAY_DISPLAY_DEVICES);
    117         if (value == null) {
    118             value = "";
    119         }
    120 
    121         if (value.equals(mCurrentOverlaySetting)) {
    122             return;
    123         }
    124         mCurrentOverlaySetting = value;
    125 
    126         if (!mOverlays.isEmpty()) {
    127             Slog.i(TAG, "Dismissing all overlay display devices.");
    128             for (OverlayDisplayHandle overlay : mOverlays) {
    129                 overlay.dismissLocked();
    130             }
    131             mOverlays.clear();
    132         }
    133 
    134         int count = 0;
    135         for (String part : value.split(";")) {
    136             Matcher matcher = SETTING_PATTERN.matcher(part);
    137             if (matcher.matches()) {
    138                 if (count >= 4) {
    139                     Slog.w(TAG, "Too many overlay display devices specified: " + value);
    140                     break;
    141                 }
    142                 try {
    143                     int width = Integer.parseInt(matcher.group(1), 10);
    144                     int height = Integer.parseInt(matcher.group(2), 10);
    145                     int densityDpi = Integer.parseInt(matcher.group(3), 10);
    146                     String flagString = matcher.group(4);
    147                     if (width >= MIN_WIDTH && width <= MAX_WIDTH
    148                             && height >= MIN_HEIGHT && height <= MAX_HEIGHT
    149                             && densityDpi >= DisplayMetrics.DENSITY_LOW
    150                             && densityDpi <= DisplayMetrics.DENSITY_XXHIGH) {
    151                         int number = ++count;
    152                         String name = getContext().getResources().getString(
    153                                 com.android.internal.R.string.display_manager_overlay_display_name,
    154                                 number);
    155                         int gravity = chooseOverlayGravity(number);
    156                         boolean secure = flagString != null && flagString.contains(",secure");
    157 
    158                         Slog.i(TAG, "Showing overlay display device #" + number
    159                                 + ": name=" + name + ", width=" + width + ", height=" + height
    160                                 + ", densityDpi=" + densityDpi + ", secure=" + secure);
    161 
    162                         mOverlays.add(new OverlayDisplayHandle(name,
    163                                 width, height, densityDpi, gravity, secure));
    164                         continue;
    165                     }
    166                 } catch (NumberFormatException ex) {
    167                 }
    168             } else if (part.isEmpty()) {
    169                 continue;
    170             }
    171             Slog.w(TAG, "Malformed overlay display devices setting: " + value);
    172         }
    173     }
    174 
    175     private static int chooseOverlayGravity(int overlayNumber) {
    176         switch (overlayNumber) {
    177             case 1:
    178                 return Gravity.TOP | Gravity.LEFT;
    179             case 2:
    180                 return Gravity.BOTTOM | Gravity.RIGHT;
    181             case 3:
    182                 return Gravity.TOP | Gravity.RIGHT;
    183             case 4:
    184             default:
    185                 return Gravity.BOTTOM | Gravity.LEFT;
    186         }
    187     }
    188 
    189     private final class OverlayDisplayDevice extends DisplayDevice {
    190         private final String mName;
    191         private final int mWidth;
    192         private final int mHeight;
    193         private final float mRefreshRate;
    194         private final int mDensityDpi;
    195         private final boolean mSecure;
    196 
    197         private Surface mSurface;
    198         private SurfaceTexture mSurfaceTexture;
    199         private DisplayDeviceInfo mInfo;
    200 
    201         public OverlayDisplayDevice(IBinder displayToken, String name,
    202                 int width, int height, float refreshRate, int densityDpi, boolean secure,
    203                 SurfaceTexture surfaceTexture) {
    204             super(OverlayDisplayAdapter.this, displayToken);
    205             mName = name;
    206             mWidth = width;
    207             mHeight = height;
    208             mRefreshRate = refreshRate;
    209             mDensityDpi = densityDpi;
    210             mSecure = secure;
    211             mSurfaceTexture = surfaceTexture;
    212         }
    213 
    214         public void destroyLocked() {
    215             mSurfaceTexture = null;
    216             if (mSurface != null) {
    217                 mSurface.release();
    218                 mSurface = null;
    219             }
    220             SurfaceControl.destroyDisplay(getDisplayTokenLocked());
    221         }
    222 
    223         @Override
    224         public void performTraversalInTransactionLocked() {
    225             if (mSurfaceTexture != null) {
    226                 if (mSurface == null) {
    227                     mSurface = new Surface(mSurfaceTexture);
    228                 }
    229                 setSurfaceInTransactionLocked(mSurface);
    230             }
    231         }
    232 
    233         @Override
    234         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
    235             if (mInfo == null) {
    236                 mInfo = new DisplayDeviceInfo();
    237                 mInfo.name = mName;
    238                 mInfo.width = mWidth;
    239                 mInfo.height = mHeight;
    240                 mInfo.refreshRate = mRefreshRate;
    241                 mInfo.densityDpi = mDensityDpi;
    242                 mInfo.xDpi = mDensityDpi;
    243                 mInfo.yDpi = mDensityDpi;
    244                 mInfo.flags = DisplayDeviceInfo.FLAG_PRESENTATION;
    245                 if (mSecure) {
    246                     mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
    247                 }
    248                 mInfo.type = Display.TYPE_OVERLAY;
    249                 mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
    250             }
    251             return mInfo;
    252         }
    253     }
    254 
    255     /**
    256      * Functions as a handle for overlay display devices which are created and
    257      * destroyed asynchronously.
    258      *
    259      * Guarded by the {@link DisplayManagerService.SyncRoot} lock.
    260      */
    261     private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener {
    262         private final String mName;
    263         private final int mWidth;
    264         private final int mHeight;
    265         private final int mDensityDpi;
    266         private final int mGravity;
    267         private final boolean mSecure;
    268 
    269         private OverlayDisplayWindow mWindow;
    270         private OverlayDisplayDevice mDevice;
    271 
    272         public OverlayDisplayHandle(String name,
    273                 int width, int height, int densityDpi, int gravity, boolean secure) {
    274             mName = name;
    275             mWidth = width;
    276             mHeight = height;
    277             mDensityDpi = densityDpi;
    278             mGravity = gravity;
    279             mSecure = secure;
    280 
    281             mUiHandler.post(mShowRunnable);
    282         }
    283 
    284         public void dismissLocked() {
    285             mUiHandler.removeCallbacks(mShowRunnable);
    286             mUiHandler.post(mDismissRunnable);
    287         }
    288 
    289         // Called on the UI thread.
    290         @Override
    291         public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate) {
    292             synchronized (getSyncRoot()) {
    293                 IBinder displayToken = SurfaceControl.createDisplay(mName, mSecure);
    294                 mDevice = new OverlayDisplayDevice(displayToken, mName,
    295                         mWidth, mHeight, refreshRate, mDensityDpi, mSecure, surfaceTexture);
    296 
    297                 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
    298             }
    299         }
    300 
    301         // Called on the UI thread.
    302         @Override
    303         public void onWindowDestroyed() {
    304             synchronized (getSyncRoot()) {
    305                 if (mDevice != null) {
    306                     mDevice.destroyLocked();
    307                     sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
    308                 }
    309             }
    310         }
    311 
    312         public void dumpLocked(PrintWriter pw) {
    313             pw.println("  " + mName + ":");
    314             pw.println("    mWidth=" + mWidth);
    315             pw.println("    mHeight=" + mHeight);
    316             pw.println("    mDensityDpi=" + mDensityDpi);
    317             pw.println("    mGravity=" + mGravity);
    318             pw.println("    mSecure=" + mSecure);
    319 
    320             // Try to dump the window state.
    321             if (mWindow != null) {
    322                 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
    323                 ipw.increaseIndent();
    324                 DumpUtils.dumpAsync(mUiHandler, mWindow, ipw, 200);
    325             }
    326         }
    327 
    328         // Runs on the UI thread.
    329         private final Runnable mShowRunnable = new Runnable() {
    330             @Override
    331             public void run() {
    332                 OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
    333                         mName, mWidth, mHeight, mDensityDpi, mGravity, mSecure,
    334                         OverlayDisplayHandle.this);
    335                 window.show();
    336 
    337                 synchronized (getSyncRoot()) {
    338                     mWindow = window;
    339                 }
    340             }
    341         };
    342 
    343         // Runs on the UI thread.
    344         private final Runnable mDismissRunnable = new Runnable() {
    345             @Override
    346             public void run() {
    347                 OverlayDisplayWindow window;
    348                 synchronized (getSyncRoot()) {
    349                     window = mWindow;
    350                     mWindow = null;
    351                 }
    352 
    353                 if (window != null) {
    354                     window.dismiss();
    355                 }
    356             }
    357         };
    358     }
    359 }
    360