OverlayDisplayAdapter.java revision e8b1aeb51e1e5da64f1d4fd40f2ee1e815886fe5
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.densityDpi = mDensityDpi;
252                mInfo.xDpi = mDensityDpi;
253                mInfo.yDpi = mDensityDpi;
254                mInfo.presentationDeadlineNanos = mDisplayPresentationDeadlineNanos +
255                        1000000000L / (int) mRefreshRate;   // display's deadline + 1 frame
256                mInfo.flags = DisplayDeviceInfo.FLAG_PRESENTATION;
257                if (mSecure) {
258                    mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
259                }
260                mInfo.type = Display.TYPE_OVERLAY;
261                mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
262                mInfo.state = mState;
263            }
264            return mInfo;
265        }
266    }
267
268    /**
269     * Functions as a handle for overlay display devices which are created and
270     * destroyed asynchronously.
271     *
272     * Guarded by the {@link DisplayManagerService.SyncRoot} lock.
273     */
274    private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener {
275        private final String mName;
276        private final int mWidth;
277        private final int mHeight;
278        private final int mDensityDpi;
279        private final int mGravity;
280        private final boolean mSecure;
281
282        private OverlayDisplayWindow mWindow;
283        private OverlayDisplayDevice mDevice;
284
285        public OverlayDisplayHandle(String name,
286                int width, int height, int densityDpi, int gravity, boolean secure) {
287            mName = name;
288            mWidth = width;
289            mHeight = height;
290            mDensityDpi = densityDpi;
291            mGravity = gravity;
292            mSecure = secure;
293
294            mUiHandler.post(mShowRunnable);
295        }
296
297        public void dismissLocked() {
298            mUiHandler.removeCallbacks(mShowRunnable);
299            mUiHandler.post(mDismissRunnable);
300        }
301
302        // Called on the UI thread.
303        @Override
304        public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate,
305                long presentationDeadlineNanos, int state) {
306            synchronized (getSyncRoot()) {
307                IBinder displayToken = SurfaceControl.createDisplay(mName, mSecure);
308                mDevice = new OverlayDisplayDevice(displayToken, mName,
309                        mWidth, mHeight, refreshRate, presentationDeadlineNanos,
310                        mDensityDpi, mSecure, state, surfaceTexture);
311
312                sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
313            }
314        }
315
316        // Called on the UI thread.
317        @Override
318        public void onWindowDestroyed() {
319            synchronized (getSyncRoot()) {
320                if (mDevice != null) {
321                    mDevice.destroyLocked();
322                    sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
323                }
324            }
325        }
326
327        // Called on the UI thread.
328        @Override
329        public void onStateChanged(int state) {
330            synchronized (getSyncRoot()) {
331                if (mDevice != null) {
332                    mDevice.setStateLocked(state);
333                    sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_CHANGED);
334                }
335            }
336        }
337
338        public void dumpLocked(PrintWriter pw) {
339            pw.println("  " + mName + ":");
340            pw.println("    mWidth=" + mWidth);
341            pw.println("    mHeight=" + mHeight);
342            pw.println("    mDensityDpi=" + mDensityDpi);
343            pw.println("    mGravity=" + mGravity);
344            pw.println("    mSecure=" + mSecure);
345
346            // Try to dump the window state.
347            if (mWindow != null) {
348                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
349                ipw.increaseIndent();
350                DumpUtils.dumpAsync(mUiHandler, mWindow, ipw, 200);
351            }
352        }
353
354        // Runs on the UI thread.
355        private final Runnable mShowRunnable = new Runnable() {
356            @Override
357            public void run() {
358                OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
359                        mName, mWidth, mHeight, mDensityDpi, mGravity, mSecure,
360                        OverlayDisplayHandle.this);
361                window.show();
362
363                synchronized (getSyncRoot()) {
364                    mWindow = window;
365                }
366            }
367        };
368
369        // Runs on the UI thread.
370        private final Runnable mDismissRunnable = new Runnable() {
371            @Override
372            public void run() {
373                OverlayDisplayWindow window;
374                synchronized (getSyncRoot()) {
375                    window = mWindow;
376                    mWindow = null;
377                }
378
379                if (window != null) {
380                    window.dismiss();
381                }
382            }
383        };
384    }
385}
386