OverlayDisplayAdapter.java revision 037c33eae74bee2774897d969d48947f9abe254f
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.Display;
31import android.view.Gravity;
32import android.view.Surface;
33import android.view.SurfaceControl;
34
35import java.io.PrintWriter;
36import java.util.ArrayList;
37import java.util.regex.Matcher;
38import 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 */
52final 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 int mState;
198        private SurfaceTexture mSurfaceTexture;
199        private Surface mSurface;
200        private DisplayDeviceInfo mInfo;
201
202        public OverlayDisplayDevice(IBinder displayToken, String name,
203                int width, int height, float refreshRate,
204                int densityDpi, boolean secure, int state,
205                SurfaceTexture surfaceTexture) {
206            super(OverlayDisplayAdapter.this, displayToken);
207            mName = name;
208            mWidth = width;
209            mHeight = height;
210            mRefreshRate = refreshRate;
211            mDensityDpi = densityDpi;
212            mSecure = secure;
213            mState = state;
214            mSurfaceTexture = surfaceTexture;
215        }
216
217        public void destroyLocked() {
218            mSurfaceTexture = null;
219            if (mSurface != null) {
220                mSurface.release();
221                mSurface = null;
222            }
223            SurfaceControl.destroyDisplay(getDisplayTokenLocked());
224        }
225
226        @Override
227        public void performTraversalInTransactionLocked() {
228            if (mSurfaceTexture != null) {
229                if (mSurface == null) {
230                    mSurface = new Surface(mSurfaceTexture);
231                }
232                setSurfaceInTransactionLocked(mSurface);
233            }
234        }
235
236        public void setStateLocked(int state) {
237            mState = state;
238            mInfo = null;
239        }
240
241        @Override
242        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
243            if (mInfo == null) {
244                mInfo = new DisplayDeviceInfo();
245                mInfo.name = mName;
246                mInfo.width = mWidth;
247                mInfo.height = mHeight;
248                mInfo.refreshRate = mRefreshRate;
249                mInfo.densityDpi = mDensityDpi;
250                mInfo.xDpi = mDensityDpi;
251                mInfo.yDpi = mDensityDpi;
252                mInfo.flags = DisplayDeviceInfo.FLAG_PRESENTATION;
253                if (mSecure) {
254                    mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
255                }
256                mInfo.type = Display.TYPE_OVERLAY;
257                mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
258                mInfo.state = mState;
259            }
260            return mInfo;
261        }
262    }
263
264    /**
265     * Functions as a handle for overlay display devices which are created and
266     * destroyed asynchronously.
267     *
268     * Guarded by the {@link DisplayManagerService.SyncRoot} lock.
269     */
270    private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener {
271        private final String mName;
272        private final int mWidth;
273        private final int mHeight;
274        private final int mDensityDpi;
275        private final int mGravity;
276        private final boolean mSecure;
277
278        private OverlayDisplayWindow mWindow;
279        private OverlayDisplayDevice mDevice;
280
281        public OverlayDisplayHandle(String name,
282                int width, int height, int densityDpi, int gravity, boolean secure) {
283            mName = name;
284            mWidth = width;
285            mHeight = height;
286            mDensityDpi = densityDpi;
287            mGravity = gravity;
288            mSecure = secure;
289
290            mUiHandler.post(mShowRunnable);
291        }
292
293        public void dismissLocked() {
294            mUiHandler.removeCallbacks(mShowRunnable);
295            mUiHandler.post(mDismissRunnable);
296        }
297
298        // Called on the UI thread.
299        @Override
300        public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate, int state) {
301            synchronized (getSyncRoot()) {
302                IBinder displayToken = SurfaceControl.createDisplay(mName, mSecure);
303                mDevice = new OverlayDisplayDevice(displayToken, mName,
304                        mWidth, mHeight, refreshRate, mDensityDpi, mSecure,
305                        state, surfaceTexture);
306
307                sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
308            }
309        }
310
311        // Called on the UI thread.
312        @Override
313        public void onWindowDestroyed() {
314            synchronized (getSyncRoot()) {
315                if (mDevice != null) {
316                    mDevice.destroyLocked();
317                    sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
318                }
319            }
320        }
321
322        // Called on the UI thread.
323        @Override
324        public void onStateChanged(int state) {
325            synchronized (getSyncRoot()) {
326                if (mDevice != null) {
327                    mDevice.setStateLocked(state);
328                    sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_CHANGED);
329                }
330            }
331        }
332
333        public void dumpLocked(PrintWriter pw) {
334            pw.println("  " + mName + ":");
335            pw.println("    mWidth=" + mWidth);
336            pw.println("    mHeight=" + mHeight);
337            pw.println("    mDensityDpi=" + mDensityDpi);
338            pw.println("    mGravity=" + mGravity);
339            pw.println("    mSecure=" + mSecure);
340
341            // Try to dump the window state.
342            if (mWindow != null) {
343                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
344                ipw.increaseIndent();
345                DumpUtils.dumpAsync(mUiHandler, mWindow, ipw, 200);
346            }
347        }
348
349        // Runs on the UI thread.
350        private final Runnable mShowRunnable = new Runnable() {
351            @Override
352            public void run() {
353                OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
354                        mName, mWidth, mHeight, mDensityDpi, mGravity, mSecure,
355                        OverlayDisplayHandle.this);
356                window.show();
357
358                synchronized (getSyncRoot()) {
359                    mWindow = window;
360                }
361            }
362        };
363
364        // Runs on the UI thread.
365        private final Runnable mDismissRunnable = new Runnable() {
366            @Override
367            public void run() {
368                OverlayDisplayWindow window;
369                synchronized (getSyncRoot()) {
370                    window = mWindow;
371                    mWindow = null;
372                }
373
374                if (window != null) {
375                    window.dismiss();
376                }
377            }
378        };
379    }
380}
381