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