• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.server.display;
18 
19 import android.content.Context;
20 import android.database.ContentObserver;
21 import android.graphics.SurfaceTexture;
22 import android.os.Handler;
23 import android.os.IBinder;
24 import android.provider.Settings;
25 import android.util.DisplayMetrics;
26 import android.util.Slog;
27 import android.view.Display;
28 import android.view.Gravity;
29 import android.view.Surface;
30 import android.view.SurfaceControl;
31 
32 import com.android.internal.util.DumpUtils;
33 import com.android.internal.util.IndentingPrintWriter;
34 
35 import java.io.PrintWriter;
36 import java.util.ArrayList;
37 import java.util.Arrays;
38 import java.util.List;
39 import java.util.regex.Matcher;
40 import java.util.regex.Pattern;
41 
42 /**
43  * A display adapter that uses overlay windows to simulate secondary displays
44  * for development purposes.  Use Development Settings to enable one or more
45  * overlay displays.
46  * <p>
47  * This object has two different handlers (which may be the same) which must not
48  * get confused.  The main handler is used to posting messages to the display manager
49  * service as usual.  The UI handler is only used by the {@link OverlayDisplayWindow}.
50  * </p><p>
51  * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
52  * </p><p>
53  * This adapter is configured via the
54  * {@link android.provider.Settings.Global#OVERLAY_DISPLAY_DEVICES} setting. This setting should be
55  * formatted as follows:
56  * <pre>
57  * [mode1]|[mode2]|...,[flag1],[flag2],...
58  * </pre>
59  * with each mode specified as:
60  * <pre>
61  * [width]x[height]/[densityDpi]
62  * </pre>
63  * Supported flags:
64  * <ul>
65  * <li><pre>secure</pre>: creates a secure display</li>
66  * </ul>
67  * </p>
68  */
69 final class OverlayDisplayAdapter extends DisplayAdapter {
70     static final String TAG = "OverlayDisplayAdapter";
71     static final boolean DEBUG = false;
72 
73     private static final int MIN_WIDTH = 100;
74     private static final int MIN_HEIGHT = 100;
75     private static final int MAX_WIDTH = 4096;
76     private static final int MAX_HEIGHT = 4096;
77 
78     private static final Pattern DISPLAY_PATTERN =
79             Pattern.compile("([^,]+)(,[a-z]+)*");
80     private static final Pattern MODE_PATTERN =
81             Pattern.compile("(\\d+)x(\\d+)/(\\d+)");
82 
83     // Unique id prefix for overlay displays.
84     private static final String UNIQUE_ID_PREFIX = "overlay:";
85 
86     private final Handler mUiHandler;
87     private final ArrayList<OverlayDisplayHandle> mOverlays =
88             new ArrayList<OverlayDisplayHandle>();
89     private String mCurrentOverlaySetting = "";
90 
91     // Called with SyncRoot lock held.
OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, Handler uiHandler)92     public OverlayDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
93             Context context, Handler handler, Listener listener, Handler uiHandler) {
94         super(syncRoot, context, handler, listener, TAG);
95         mUiHandler = uiHandler;
96     }
97 
98     @Override
dumpLocked(PrintWriter pw)99     public void dumpLocked(PrintWriter pw) {
100         super.dumpLocked(pw);
101 
102         pw.println("mCurrentOverlaySetting=" + mCurrentOverlaySetting);
103         pw.println("mOverlays: size=" + mOverlays.size());
104         for (OverlayDisplayHandle overlay : mOverlays) {
105             overlay.dumpLocked(pw);
106         }
107     }
108 
109     @Override
registerLocked()110     public void registerLocked() {
111         super.registerLocked();
112 
113         getHandler().post(new Runnable() {
114             @Override
115             public void run() {
116                 getContext().getContentResolver().registerContentObserver(
117                         Settings.Global.getUriFor(Settings.Global.OVERLAY_DISPLAY_DEVICES),
118                         true, new ContentObserver(getHandler()) {
119                             @Override
120                             public void onChange(boolean selfChange) {
121                                 updateOverlayDisplayDevices();
122                             }
123                         });
124 
125                 updateOverlayDisplayDevices();
126             }
127         });
128     }
129 
updateOverlayDisplayDevices()130     private void updateOverlayDisplayDevices() {
131         synchronized (getSyncRoot()) {
132             updateOverlayDisplayDevicesLocked();
133         }
134     }
135 
updateOverlayDisplayDevicesLocked()136     private void updateOverlayDisplayDevicesLocked() {
137         String value = Settings.Global.getString(getContext().getContentResolver(),
138                 Settings.Global.OVERLAY_DISPLAY_DEVICES);
139         if (value == null) {
140             value = "";
141         }
142 
143         if (value.equals(mCurrentOverlaySetting)) {
144             return;
145         }
146         mCurrentOverlaySetting = value;
147 
148         if (!mOverlays.isEmpty()) {
149             Slog.i(TAG, "Dismissing all overlay display devices.");
150             for (OverlayDisplayHandle overlay : mOverlays) {
151                 overlay.dismissLocked();
152             }
153             mOverlays.clear();
154         }
155 
156         int count = 0;
157         for (String part : value.split(";")) {
158             Matcher displayMatcher = DISPLAY_PATTERN.matcher(part);
159             if (displayMatcher.matches()) {
160                 if (count >= 4) {
161                     Slog.w(TAG, "Too many overlay display devices specified: " + value);
162                     break;
163                 }
164                 String modeString = displayMatcher.group(1);
165                 String flagString = displayMatcher.group(2);
166                 ArrayList<OverlayMode> modes = new ArrayList<>();
167                 for (String mode : modeString.split("\\|")) {
168                     Matcher modeMatcher = MODE_PATTERN.matcher(mode);
169                     if (modeMatcher.matches()) {
170                         try {
171                             int width = Integer.parseInt(modeMatcher.group(1), 10);
172                             int height = Integer.parseInt(modeMatcher.group(2), 10);
173                             int densityDpi = Integer.parseInt(modeMatcher.group(3), 10);
174                             if (width >= MIN_WIDTH && width <= MAX_WIDTH
175                                     && height >= MIN_HEIGHT && height <= MAX_HEIGHT
176                                     && densityDpi >= DisplayMetrics.DENSITY_LOW
177                                     && densityDpi <= DisplayMetrics.DENSITY_XXXHIGH) {
178                                 modes.add(new OverlayMode(width, height, densityDpi));
179                                 continue;
180                             } else {
181                                 Slog.w(TAG, "Ignoring out-of-range overlay display mode: " + mode);
182                             }
183                         } catch (NumberFormatException ex) {
184                         }
185                     } else if (mode.isEmpty()) {
186                         continue;
187                     }
188                 }
189                 if (!modes.isEmpty()) {
190                     int number = ++count;
191                     String name = getContext().getResources().getString(
192                             com.android.internal.R.string.display_manager_overlay_display_name,
193                             number);
194                     int gravity = chooseOverlayGravity(number);
195                     boolean secure = flagString != null && flagString.contains(",secure");
196 
197                     Slog.i(TAG, "Showing overlay display device #" + number
198                             + ": name=" + name + ", modes=" + Arrays.toString(modes.toArray()));
199 
200                     mOverlays.add(new OverlayDisplayHandle(name, modes, gravity, secure, number));
201                     continue;
202                 }
203             }
204             Slog.w(TAG, "Malformed overlay display devices setting: " + value);
205         }
206     }
207 
chooseOverlayGravity(int overlayNumber)208     private static int chooseOverlayGravity(int overlayNumber) {
209         switch (overlayNumber) {
210             case 1:
211                 return Gravity.TOP | Gravity.LEFT;
212             case 2:
213                 return Gravity.BOTTOM | Gravity.RIGHT;
214             case 3:
215                 return Gravity.TOP | Gravity.RIGHT;
216             case 4:
217             default:
218                 return Gravity.BOTTOM | Gravity.LEFT;
219         }
220     }
221 
222     private abstract class OverlayDisplayDevice extends DisplayDevice {
223         private final String mName;
224         private final float mRefreshRate;
225         private final long mDisplayPresentationDeadlineNanos;
226         private final boolean mSecure;
227         private final List<OverlayMode> mRawModes;
228         private final Display.Mode[] mModes;
229         private final int mDefaultMode;
230 
231         private int mState;
232         private SurfaceTexture mSurfaceTexture;
233         private Surface mSurface;
234         private DisplayDeviceInfo mInfo;
235         private int mActiveMode;
236 
OverlayDisplayDevice(IBinder displayToken, String name, List<OverlayMode> modes, int activeMode, int defaultMode, float refreshRate, long presentationDeadlineNanos, boolean secure, int state, SurfaceTexture surfaceTexture, int number)237         public OverlayDisplayDevice(IBinder displayToken, String name,
238                 List<OverlayMode> modes, int activeMode, int defaultMode,
239                 float refreshRate, long presentationDeadlineNanos,
240                 boolean secure, int state,
241                 SurfaceTexture surfaceTexture, int number) {
242             super(OverlayDisplayAdapter.this, displayToken, UNIQUE_ID_PREFIX + number);
243             mName = name;
244             mRefreshRate = refreshRate;
245             mDisplayPresentationDeadlineNanos = presentationDeadlineNanos;
246             mSecure = secure;
247             mState = state;
248             mSurfaceTexture = surfaceTexture;
249             mRawModes = modes;
250             mModes = new Display.Mode[modes.size()];
251             for (int i = 0; i < modes.size(); i++) {
252                 OverlayMode mode = modes.get(i);
253                 mModes[i] = createMode(mode.mWidth, mode.mHeight, refreshRate);
254             }
255             mActiveMode = activeMode;
256             mDefaultMode = defaultMode;
257         }
258 
destroyLocked()259         public void destroyLocked() {
260             mSurfaceTexture = null;
261             if (mSurface != null) {
262                 mSurface.release();
263                 mSurface = null;
264             }
265             SurfaceControl.destroyDisplay(getDisplayTokenLocked());
266         }
267 
268         @Override
hasStableUniqueId()269         public boolean hasStableUniqueId() {
270             return false;
271         }
272 
273         @Override
performTraversalLocked(SurfaceControl.Transaction t)274         public void performTraversalLocked(SurfaceControl.Transaction t) {
275             if (mSurfaceTexture != null) {
276                 if (mSurface == null) {
277                     mSurface = new Surface(mSurfaceTexture);
278                 }
279                 setSurfaceLocked(t, mSurface);
280             }
281         }
282 
setStateLocked(int state)283         public void setStateLocked(int state) {
284             mState = state;
285             mInfo = null;
286         }
287 
288         @Override
getDisplayDeviceInfoLocked()289         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
290             if (mInfo == null) {
291                 Display.Mode mode = mModes[mActiveMode];
292                 OverlayMode rawMode = mRawModes.get(mActiveMode);
293                 mInfo = new DisplayDeviceInfo();
294                 mInfo.name = mName;
295                 mInfo.uniqueId = getUniqueId();
296                 mInfo.width = mode.getPhysicalWidth();
297                 mInfo.height = mode.getPhysicalHeight();
298                 mInfo.modeId = mode.getModeId();
299                 mInfo.defaultModeId = mModes[0].getModeId();
300                 mInfo.supportedModes = mModes;
301                 mInfo.densityDpi = rawMode.mDensityDpi;
302                 mInfo.xDpi = rawMode.mDensityDpi;
303                 mInfo.yDpi = rawMode.mDensityDpi;
304                 mInfo.presentationDeadlineNanos = mDisplayPresentationDeadlineNanos +
305                         1000000000L / (int) mRefreshRate;   // display's deadline + 1 frame
306                 mInfo.flags = DisplayDeviceInfo.FLAG_PRESENTATION;
307                 if (mSecure) {
308                     mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
309                 }
310                 mInfo.type = Display.TYPE_OVERLAY;
311                 mInfo.touch = DisplayDeviceInfo.TOUCH_VIRTUAL;
312                 mInfo.state = mState;
313             }
314             return mInfo;
315         }
316 
317         @Override
setAllowedDisplayModesLocked(int[] modes)318         public void setAllowedDisplayModesLocked(int[] modes) {
319             final int id;
320             if (modes.length > 0) {
321                 // The allowed modes should be ordered by preference, so just use the first mode
322                 // here.
323                 id = modes[0];
324             } else {
325                 // If we don't have any allowed modes, just use the default mode.
326                 id = 0;
327             }
328             int index = -1;
329             if (id == 0) {
330                 // Use the default.
331                 index = 0;
332             } else {
333                 for (int i = 0; i < mModes.length; i++) {
334                     if (mModes[i].getModeId() == id) {
335                         index = i;
336                         break;
337                     }
338                 }
339             }
340             if (index == -1) {
341                 Slog.w(TAG, "Unable to locate mode " + id + ", reverting to default.");
342                 index = mDefaultMode;
343             }
344             if (mActiveMode == index) {
345                 return;
346             }
347             mActiveMode = index;
348             mInfo = null;
349             sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
350             onModeChangedLocked(index);
351         }
352 
353         /**
354          * Called when the device switched to a new mode.
355          *
356          * @param index index of the mode in the list of modes
357          */
onModeChangedLocked(int index)358         public abstract void onModeChangedLocked(int index);
359     }
360 
361     /**
362      * Functions as a handle for overlay display devices which are created and
363      * destroyed asynchronously.
364      *
365      * Guarded by the {@link DisplayManagerService.SyncRoot} lock.
366      */
367     private final class OverlayDisplayHandle implements OverlayDisplayWindow.Listener {
368         private static final int DEFAULT_MODE_INDEX = 0;
369 
370         private final String mName;
371         private final List<OverlayMode> mModes;
372         private final int mGravity;
373         private final boolean mSecure;
374         private final int mNumber;
375 
376         private OverlayDisplayWindow mWindow;
377         private OverlayDisplayDevice mDevice;
378         private int mActiveMode;
379 
OverlayDisplayHandle(String name, List<OverlayMode> modes, int gravity, boolean secure, int number)380         public OverlayDisplayHandle(String name, List<OverlayMode> modes, int gravity,
381                 boolean secure, int number) {
382             mName = name;
383             mModes = modes;
384             mGravity = gravity;
385             mSecure = secure;
386             mNumber = number;
387 
388             mActiveMode = 0;
389 
390             showLocked();
391         }
392 
showLocked()393         private void showLocked() {
394             mUiHandler.post(mShowRunnable);
395         }
396 
dismissLocked()397         public void dismissLocked() {
398             mUiHandler.removeCallbacks(mShowRunnable);
399             mUiHandler.post(mDismissRunnable);
400         }
401 
onActiveModeChangedLocked(int index)402         private void onActiveModeChangedLocked(int index) {
403             mUiHandler.removeCallbacks(mResizeRunnable);
404             mActiveMode = index;
405             if (mWindow != null) {
406                 mUiHandler.post(mResizeRunnable);
407             }
408         }
409 
410         // Called on the UI thread.
411         @Override
onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate, long presentationDeadlineNanos, int state)412         public void onWindowCreated(SurfaceTexture surfaceTexture, float refreshRate,
413                 long presentationDeadlineNanos, int state) {
414             synchronized (getSyncRoot()) {
415                 IBinder displayToken = SurfaceControl.createDisplay(mName, mSecure);
416                 mDevice = new OverlayDisplayDevice(displayToken, mName, mModes, mActiveMode,
417                         DEFAULT_MODE_INDEX, refreshRate, presentationDeadlineNanos,
418                         mSecure, state, surfaceTexture, mNumber) {
419                     @Override
420                     public void onModeChangedLocked(int index) {
421                         onActiveModeChangedLocked(index);
422                     }
423                 };
424 
425                 sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_ADDED);
426             }
427         }
428 
429         // Called on the UI thread.
430         @Override
onWindowDestroyed()431         public void onWindowDestroyed() {
432             synchronized (getSyncRoot()) {
433                 if (mDevice != null) {
434                     mDevice.destroyLocked();
435                     sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_REMOVED);
436                 }
437             }
438         }
439 
440         // Called on the UI thread.
441         @Override
onStateChanged(int state)442         public void onStateChanged(int state) {
443             synchronized (getSyncRoot()) {
444                 if (mDevice != null) {
445                     mDevice.setStateLocked(state);
446                     sendDisplayDeviceEventLocked(mDevice, DISPLAY_DEVICE_EVENT_CHANGED);
447                 }
448             }
449         }
450 
dumpLocked(PrintWriter pw)451         public void dumpLocked(PrintWriter pw) {
452             pw.println("  " + mName + ":");
453             pw.println("    mModes=" + Arrays.toString(mModes.toArray()));
454             pw.println("    mActiveMode=" + mActiveMode);
455             pw.println("    mGravity=" + mGravity);
456             pw.println("    mSecure=" + mSecure);
457             pw.println("    mNumber=" + mNumber);
458 
459             // Try to dump the window state.
460             if (mWindow != null) {
461                 final IndentingPrintWriter ipw = new IndentingPrintWriter(pw, "    ");
462                 ipw.increaseIndent();
463                 DumpUtils.dumpAsync(mUiHandler, mWindow, ipw, "", 200);
464             }
465         }
466 
467         // Runs on the UI thread.
468         private final Runnable mShowRunnable = new Runnable() {
469             @Override
470             public void run() {
471                 OverlayMode mode = mModes.get(mActiveMode);
472                 OverlayDisplayWindow window = new OverlayDisplayWindow(getContext(),
473                         mName, mode.mWidth, mode.mHeight, mode.mDensityDpi, mGravity, mSecure,
474                         OverlayDisplayHandle.this);
475                 window.show();
476 
477                 synchronized (getSyncRoot()) {
478                     mWindow = window;
479                 }
480             }
481         };
482 
483         // Runs on the UI thread.
484         private final Runnable mDismissRunnable = new Runnable() {
485             @Override
486             public void run() {
487                 OverlayDisplayWindow window;
488                 synchronized (getSyncRoot()) {
489                     window = mWindow;
490                     mWindow = null;
491                 }
492 
493                 if (window != null) {
494                     window.dismiss();
495                 }
496             }
497         };
498 
499         // Runs on the UI thread.
500         private final Runnable mResizeRunnable = new Runnable() {
501             @Override
502             public void run() {
503                 OverlayMode mode;
504                 OverlayDisplayWindow window;
505                 synchronized (getSyncRoot()) {
506                     if (mWindow == null) {
507                         return;
508                     }
509                     mode = mModes.get(mActiveMode);
510                     window = mWindow;
511                 }
512                 window.resize(mode.mWidth, mode.mHeight, mode.mDensityDpi);
513             }
514         };
515     }
516 
517     /**
518      * A display mode for an overlay display.
519      */
520     private static final class OverlayMode {
521         final int mWidth;
522         final int mHeight;
523         final int mDensityDpi;
524 
OverlayMode(int width, int height, int densityDpi)525         OverlayMode(int width, int height, int densityDpi) {
526             mWidth = width;
527             mHeight = height;
528             mDensityDpi = densityDpi;
529         }
530 
531         @Override
toString()532         public String toString() {
533             return new StringBuilder("{")
534                     .append("width=").append(mWidth)
535                     .append(", height=").append(mHeight)
536                     .append(", densityDpi=").append(mDensityDpi)
537                     .append("}")
538                     .toString();
539         }
540     }
541 }
542