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