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