OverlayDisplayAdapter.java revision d49359631bc2642be73dc162a8a73207df1e0baf
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.BroadcastReceiver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.database.ContentObserver;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.UserHandle;
30import android.provider.Settings;
31import android.util.DisplayMetrics;
32import android.util.Slog;
33import android.view.Gravity;
34import android.view.Surface;
35
36import java.io.PrintWriter;
37import java.util.ArrayList;
38import java.util.regex.Matcher;
39import java.util.regex.Pattern;
40
41/**
42 * A display adapter that uses overlay windows to simulate secondary displays
43 * for development purposes.  Use Development Settings to enable one or more
44 * overlay displays.
45 * <p>
46 * This object has two different handlers (which may be the same) which must not
47 * get confused.  The main handler is used to posting messages to the display manager
48 * service as usual.  The UI handler is only used by the {@link OverlayDisplayWindow}.
49 * </p><p>
50 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
51 * </p>
52 */
53final class OverlayDisplayAdapter extends DisplayAdapter {
54    static final String TAG = "OverlayDisplayAdapter";
55    static final boolean DEBUG = false;
56
57    private static final int MIN_WIDTH = 100;
58    private static final int MIN_HEIGHT = 100;
59    private static final int MAX_WIDTH = 4096;
60    private static final int MAX_HEIGHT = 4096;
61
62    private static final Pattern SETTING_PATTERN =
63            Pattern.compile("(\\d+)x(\\d+)/(\\d+)");
64
65    private final Handler mUiHandler;
66    private final ArrayList<OverlayDisplayHandle> mOverlays =
67            new ArrayList<OverlayDisplayHandle>();
68    private String mCurrentOverlaySetting = "";
69
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                    if (width >= MIN_WIDTH && width <= MAX_WIDTH
147                            && height >= MIN_HEIGHT && height <= MAX_HEIGHT
148                            && densityDpi >= DisplayMetrics.DENSITY_LOW
149                            && densityDpi <= DisplayMetrics.DENSITY_XXHIGH) {
150                        int number = ++count;
151                        String name = getContext().getResources().getString(
152                                com.android.internal.R.string.display_manager_overlay_display_name,
153                                number);
154                        int gravity = chooseOverlayGravity(number);
155
156                        Slog.i(TAG, "Showing overlay display device #" + number
157                                + ": name=" + name + ", width=" + width + ", height=" + height
158                                + ", densityDpi=" + densityDpi);
159
160                        mOverlays.add(new OverlayDisplayHandle(name,
161                                width, height, densityDpi, gravity));
162                        continue;
163                    }
164                } catch (NumberFormatException ex) {
165                }
166            } else if (part.isEmpty()) {
167                continue;
168            }
169            Slog.w(TAG, "Malformed overlay display devices setting: " + value);
170        }
171    }
172
173    private static int chooseOverlayGravity(int overlayNumber) {
174        switch (overlayNumber) {
175            case 1:
176                return Gravity.TOP | Gravity.LEFT;
177            case 2:
178                return Gravity.BOTTOM | Gravity.RIGHT;
179            case 3:
180                return Gravity.TOP | Gravity.RIGHT;
181            case 4:
182            default:
183                return Gravity.BOTTOM | Gravity.LEFT;
184        }
185    }
186
187    private final class OverlayDisplayDevice extends DisplayDevice {
188        private final String mName;
189        private final int mWidth;
190        private final int mHeight;
191        private final float mRefreshRate;
192        private final int mDensityDpi;
193
194        private Surface mSurface;
195        private DisplayDeviceInfo mInfo;
196
197        public OverlayDisplayDevice(IBinder displayToken, String name,
198                int width, int height, float refreshRate, int densityDpi,
199                Surface surface) {
200            super(OverlayDisplayAdapter.this, displayToken);
201            mName = name;
202            mWidth = width;
203            mHeight = height;
204            mRefreshRate = refreshRate;
205            mDensityDpi = densityDpi;
206            mSurface = surface;
207        }
208
209        public void clearSurfaceLocked() {
210            mSurface = null;
211            sendTraversalRequestLocked();
212        }
213
214        @Override
215        public void performTraversalInTransactionLocked() {
216            setSurfaceInTransactionLocked(mSurface);
217        }
218
219        @Override
220        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
221            if (mInfo == null) {
222                mInfo = new DisplayDeviceInfo();
223                mInfo.name = mName;
224                mInfo.width = mWidth;
225                mInfo.height = mHeight;
226                mInfo.refreshRate = mRefreshRate;
227                mInfo.densityDpi = mDensityDpi;
228                mInfo.xDpi = mDensityDpi;
229                mInfo.yDpi = mDensityDpi;
230                mInfo.flags = DisplayDeviceInfo.FLAG_SUPPORTS_SECURE_VIDEO_OUTPUT;
231                mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
232            }
233            return mInfo;
234        }
235    }
236
237    /**
238     * Functions as a handle for overlay display devices which are created and
239     * destroyed asynchronously.
240     *
241     * Guarded by the {@link DisplayManagerService.SyncRoot} lock.
242     */
243    private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener {
244        private final String mName;
245        private final int mWidth;
246        private final int mHeight;
247        private final int mDensityDpi;
248        private final int mGravity;
249
250        private OverlayDisplayWindow mWindow;
251        private OverlayDisplayDevice mDevice;
252
253        public OverlayDisplayHandle(String name,
254                int width, int height, int densityDpi, int gravity) {
255            mName = name;
256            mWidth = width;
257            mHeight = height;
258            mDensityDpi = densityDpi;
259            mGravity = gravity;
260
261            mUiHandler.post(mShowRunnable);
262        }
263
264        public void dismissLocked() {
265            mUiHandler.removeCallbacks(mShowRunnable);
266            mUiHandler.post(mDismissRunnable);
267        }
268
269        // Called on the UI thread.
270        @Override
271        public void onWindowCreated(Surface surface, float refreshRate) {
272            synchronized (getSyncRoot()) {
273                IBinder displayToken = Surface.createDisplay(mName);
274                mDevice = new OverlayDisplayDevice(displayToken, mName,
275                        mWidth, mHeight, refreshRate, mDensityDpi, surface);
276
277                sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
278            }
279        }
280
281        // Called on the UI thread.
282        @Override
283        public void onWindowDestroyed() {
284            synchronized (getSyncRoot()) {
285                if (mDevice != null) {
286                    mDevice.clearSurfaceLocked();
287                    sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
288                }
289            }
290        }
291
292        public void dumpLocked(PrintWriter pw) {
293            pw.println("  " + mName + ":");
294            pw.println("    mWidth=" + mWidth);
295            pw.println("    mHeight=" + mHeight);
296            pw.println("    mDensityDpi=" + mDensityDpi);
297            pw.println("    mGravity=" + mGravity);
298
299            // Try to dump the window state.
300            if (mWindow != null) {
301                final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
302                ipw.increaseIndent();
303                DumpUtils.dumpAsync(mUiHandler, mWindow, ipw, 200);
304            }
305        }
306
307        // Runs on the UI thread.
308        private final Runnable mShowRunnable = new Runnable() {
309            @Override
310            public void run() {
311                OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
312                        mName, mWidth, mHeight, mDensityDpi, mGravity,
313                        OverlayDisplayHandle.this);
314                window.show();
315
316                synchronized (getSyncRoot()) {
317                    mWindow = window;
318                }
319            }
320        };
321
322        // Runs on the UI thread.
323        private final Runnable mDismissRunnable = new Runnable() {
324            @Override
325            public void run() {
326                OverlayDisplayWindow window;
327                synchronized (getSyncRoot()) {
328                    window = mWindow;
329                    mWindow = null;
330                }
331
332                if (window != null) {
333                    window.dismiss();
334                }
335            }
336        };
337    }
338}
339