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+)");
     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                     if (width >= MIN_WIDTH && width <= MAX_WIDTH
    147                             && height >= MIN_HEIGHT && height <= MAX_HEIGHT
    148                             && densityDpi >= DisplayMetrics.DENSITY_LOW
    149                             && densityDpi <= DisplayMetrics.DENSITY_XXHIGH) {
    150                         int number = ++count;
    151                         String name = getContext().getResources().getString(
    152                                 com.android.internal.R.string.display_manager_overlay_display_name,
    153                                 number);
    154                         int gravity = chooseOverlayGravity(number);
    155 
    156                         Slog.i(TAG, "Showing overlay display device #" + number
    157                                 + ": name=" + name + ", width=" + width + ", height=" + height
    158                                 + ", densityDpi=" + densityDpi);
    159 
    160                         mOverlays.add(new OverlayDisplayHandle(name,
    161                                 width, height, densityDpi, gravity));
    162                         continue;
    163                     }
    164                 } catch (NumberFormatException ex) {
    165                 }
    166             } else if (part.isEmpty()) {
    167                 continue;
    168             }
    169             Slog.w(TAG, "Malformed overlay display devices setting: " + value);
    170         }
    171     }
    172 
    173     private static int chooseOverlayGravity(int overlayNumber) {
    174         switch (overlayNumber) {
    175             case 1:
    176                 return Gravity.TOP | Gravity.LEFT;
    177             case 2:
    178                 return Gravity.BOTTOM | Gravity.RIGHT;
    179             case 3:
    180                 return Gravity.TOP | Gravity.RIGHT;
    181             case 4:
    182             default:
    183                 return Gravity.BOTTOM | Gravity.LEFT;
    184         }
    185     }
    186 
    187     private final class OverlayDisplayDevice extends DisplayDevice {
    188         private final String mName;
    189         private final int mWidth;
    190         private final int mHeight;
    191         private final float mRefreshRate;
    192         private final int mDensityDpi;
    193 
    194         private Surface mSurface;
    195         private SurfaceTexture mSurfaceTexture;
    196         private DisplayDeviceInfo mInfo;
    197 
    198         public OverlayDisplayDevice(IBinder displayToken, String name,
    199                 int width, int height, float refreshRate, int densityDpi,
    200                 SurfaceTexture surfaceTexture) {
    201             super(OverlayDisplayAdapter.this, displayToken);
    202             mName = name;
    203             mWidth = width;
    204             mHeight = height;
    205             mRefreshRate = refreshRate;
    206             mDensityDpi = densityDpi;
    207             mSurfaceTexture = surfaceTexture;
    208         }
    209 
    210         public void clearSurfaceTextureLocked() {
    211             if (mSurfaceTexture != null) {
    212                 mSurfaceTexture = null;
    213             }
    214             sendTraversalRequestLocked();
    215         }
    216 
    217         @Override
    218         public void performTraversalInTransactionLocked() {
    219             if (mSurfaceTexture != null) {
    220                 if (mSurface == null) {
    221                     mSurface = new Surface(mSurfaceTexture);
    222                 }
    223                 setSurfaceInTransactionLocked(mSurface);
    224             } else {
    225                 setSurfaceInTransactionLocked(null);
    226                 if (mSurface != null) {
    227                     mSurface.destroy();
    228                     mSurface = null;
    229                 }
    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 = 0;
    245                 mInfo.type = Display.TYPE_OVERLAY;
    246                 mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
    247             }
    248             return mInfo;
    249         }
    250     }
    251 
    252     /**
    253      * Functions as a handle for overlay display devices which are created and
    254      * destroyed asynchronously.
    255      *
    256      * Guarded by the {@link DisplayManagerService.SyncRoot} lock.
    257      */
    258     private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener {
    259         private final String mName;
    260         private final int mWidth;
    261         private final int mHeight;
    262         private final int mDensityDpi;
    263         private final int mGravity;
    264 
    265         private OverlayDisplayWindow mWindow;
    266         private OverlayDisplayDevice mDevice;
    267 
    268         public OverlayDisplayHandle(String name,
    269                 int width, int height, int densityDpi, int gravity) {
    270             mName = name;
    271             mWidth = width;
    272             mHeight = height;
    273             mDensityDpi = densityDpi;
    274             mGravity = gravity;
    275 
    276             mUiHandler.post(mShowRunnable);
    277         }
    278 
    279         public void dismissLocked() {
    280             mUiHandler.removeCallbacks(mShowRunnable);
    281             mUiHandler.post(mDismissRunnable);
    282         }
    283 
    284         // Called on the UI thread.
    285         @Override
    286         public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate) {
    287             synchronized (getSyncRoot()) {
    288                 IBinder displayToken = SurfaceControl.createDisplay(mName, false);
    289                 mDevice = new OverlayDisplayDevice(displayToken, mName,
    290                         mWidth, mHeight, refreshRate, mDensityDpi, surfaceTexture);
    291 
    292                 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
    293             }
    294         }
    295 
    296         // Called on the UI thread.
    297         @Override
    298         public void onWindowDestroyed() {
    299             synchronized (getSyncRoot()) {
    300                 if (mDevice != null) {
    301                     mDevice.clearSurfaceTextureLocked();
    302                     sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
    303                 }
    304             }
    305         }
    306 
    307         public void dumpLocked(PrintWriter pw) {
    308             pw.println("  " + mName + ":");
    309             pw.println("    mWidth=" + mWidth);
    310             pw.println("    mHeight=" + mHeight);
    311             pw.println("    mDensityDpi=" + mDensityDpi);
    312             pw.println("    mGravity=" + mGravity);
    313 
    314             // Try to dump the window state.
    315             if (mWindow != null) {
    316                 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
    317                 ipw.increaseIndent();
    318                 DumpUtils.dumpAsync(mUiHandler, mWindow, ipw, 200);
    319             }
    320         }
    321 
    322         // Runs on the UI thread.
    323         private final Runnable mShowRunnable = new Runnable() {
    324             @Override
    325             public void run() {
    326                 OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
    327                         mName, mWidth, mHeight, mDensityDpi, mGravity,
    328                         OverlayDisplayHandle.this);
    329                 window.show();
    330 
    331                 synchronized (getSyncRoot()) {
    332                     mWindow = window;
    333                 }
    334             }
    335         };
    336 
    337         // Runs on the UI thread.
    338         private final Runnable mDismissRunnable = new Runnable() {
    339             @Override
    340             public void run() {
    341                 OverlayDisplayWindow window;
    342                 synchronized (getSyncRoot()) {
    343                     window = mWindow;
    344                     mWindow = null;
    345                 }
    346 
    347                 if (window != null) {
    348                     window.dismiss();
    349                 }
    350             }
    351         };
    352     }
    353 }
    354