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