1 /*
2  * Copyright (C) 2013 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 android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR;
20 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
21 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL;
22 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP;
23 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION;
24 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC;
25 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT;
26 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE;
27 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
28 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH;
29 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED;
30 
31 import static com.android.server.display.DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP;
32 import static com.android.server.display.DisplayDeviceInfo.FLAG_TRUSTED;
33 
34 import android.annotation.Nullable;
35 import android.content.Context;
36 import android.graphics.Point;
37 import android.hardware.display.IVirtualDisplayCallback;
38 import android.hardware.display.VirtualDisplayConfig;
39 import android.media.projection.IMediaProjection;
40 import android.media.projection.IMediaProjectionCallback;
41 import android.os.Handler;
42 import android.os.IBinder;
43 import android.os.IBinder.DeathRecipient;
44 import android.os.Message;
45 import android.os.RemoteException;
46 import android.os.SystemProperties;
47 import android.util.ArrayMap;
48 import android.util.Slog;
49 import android.view.Display;
50 import android.view.Surface;
51 import android.view.SurfaceControl;
52 
53 import com.android.internal.annotations.VisibleForTesting;
54 
55 import java.io.PrintWriter;
56 import java.util.Iterator;
57 
58 /**
59  * A display adapter that provides virtual displays on behalf of applications.
60  * <p>
61  * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock.
62  * </p>
63  */
64 @VisibleForTesting
65 public class VirtualDisplayAdapter extends DisplayAdapter {
66     static final String TAG = "VirtualDisplayAdapter";
67     static final boolean DEBUG = false;
68 
69     // Unique id prefix for virtual displays
70     @VisibleForTesting
71     static final String UNIQUE_ID_PREFIX = "virtual:";
72 
73     private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices =
74             new ArrayMap<IBinder, VirtualDisplayDevice>();
75     private final Handler mHandler;
76     private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory;
77 
78     // Called with SyncRoot lock held.
VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener)79     public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
80             Context context, Handler handler, Listener listener) {
81         this(syncRoot, context, handler, listener,
82                 (String name, boolean secure) -> SurfaceControl.createDisplay(name, secure));
83     }
84 
85     @VisibleForTesting
VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, SurfaceControlDisplayFactory surfaceControlDisplayFactory)86     VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot,
87             Context context, Handler handler, Listener listener,
88             SurfaceControlDisplayFactory surfaceControlDisplayFactory) {
89         super(syncRoot, context, handler, listener, TAG);
90         mHandler = handler;
91         mSurfaceControlDisplayFactory = surfaceControlDisplayFactory;
92     }
93 
createVirtualDisplayLocked(IVirtualDisplayCallback callback, IMediaProjection projection, int ownerUid, String ownerPackageName, Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig)94     public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback,
95             IMediaProjection projection, int ownerUid, String ownerPackageName, Surface surface,
96             int flags, VirtualDisplayConfig virtualDisplayConfig) {
97         String name = virtualDisplayConfig.getName();
98         boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0;
99         IBinder appToken = callback.asBinder();
100         IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure);
101         final String baseUniqueId =
102                 UNIQUE_ID_PREFIX + ownerPackageName + "," + ownerUid + "," + name + ",";
103         final int uniqueIndex = getNextUniqueIndex(baseUniqueId);
104         String uniqueId = virtualDisplayConfig.getUniqueId();
105         if (uniqueId == null) {
106             uniqueId = baseUniqueId + uniqueIndex;
107         } else {
108             uniqueId = UNIQUE_ID_PREFIX + ownerPackageName + ":" + uniqueId;
109         }
110         VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken,
111                 ownerUid, ownerPackageName, surface, flags, new Callback(callback, mHandler),
112                 uniqueId, uniqueIndex, virtualDisplayConfig);
113 
114         mVirtualDisplayDevices.put(appToken, device);
115 
116         try {
117             if (projection != null) {
118                 projection.registerCallback(new MediaProjectionCallback(appToken));
119             }
120             appToken.linkToDeath(device, 0);
121         } catch (RemoteException ex) {
122             mVirtualDisplayDevices.remove(appToken);
123             device.destroyLocked(false);
124             return null;
125         }
126 
127         // Return the display device without actually sending the event indicating
128         // that it was added.  The caller will handle it.
129         return device;
130     }
131 
resizeVirtualDisplayLocked(IBinder appToken, int width, int height, int densityDpi)132     public void resizeVirtualDisplayLocked(IBinder appToken,
133             int width, int height, int densityDpi) {
134         VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
135         if (device != null) {
136             device.resizeLocked(width, height, densityDpi);
137         }
138     }
139 
140     @VisibleForTesting
getVirtualDisplaySurfaceLocked(IBinder appToken)141     Surface getVirtualDisplaySurfaceLocked(IBinder appToken) {
142         VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
143         if (device != null) {
144             return device.getSurfaceLocked();
145         }
146         return null;
147     }
148 
setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface)149     public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) {
150         VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
151         if (device != null) {
152             device.setSurfaceLocked(surface);
153         }
154     }
155 
releaseVirtualDisplayLocked(IBinder appToken)156     public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
157         VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
158         if (device != null) {
159             device.destroyLocked(true);
160             appToken.unlinkToDeath(device, 0);
161         }
162 
163         // Return the display device that was removed without actually sending the
164         // event indicating that it was removed.  The caller will handle it.
165         return device;
166     }
167 
setVirtualDisplayStateLocked(IBinder appToken, boolean isOn)168     void setVirtualDisplayStateLocked(IBinder appToken, boolean isOn) {
169         VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
170         if (device != null) {
171             device.setDisplayState(isOn);
172         }
173     }
174 
175     /**
176      * Returns the next unique index for the uniqueIdPrefix
177      */
getNextUniqueIndex(String uniqueIdPrefix)178     private int getNextUniqueIndex(String uniqueIdPrefix) {
179         if (mVirtualDisplayDevices.isEmpty()) {
180             return 0;
181         }
182 
183         int nextUniqueIndex = 0;
184         Iterator<VirtualDisplayDevice> it = mVirtualDisplayDevices.values().iterator();
185         while (it.hasNext()) {
186             VirtualDisplayDevice device = it.next();
187             if (device.getUniqueId().startsWith(uniqueIdPrefix)
188                     && device.mUniqueIndex >= nextUniqueIndex) {
189                 // Increment the next unique index to be greater than ones we have already ran
190                 // across for displays that have the same unique Id prefix.
191                 nextUniqueIndex = device.mUniqueIndex + 1;
192             }
193         }
194 
195         return nextUniqueIndex;
196     }
197 
handleBinderDiedLocked(IBinder appToken)198     private void handleBinderDiedLocked(IBinder appToken) {
199         mVirtualDisplayDevices.remove(appToken);
200     }
201 
handleMediaProjectionStoppedLocked(IBinder appToken)202     private void handleMediaProjectionStoppedLocked(IBinder appToken) {
203         VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
204         if (device != null) {
205             Slog.i(TAG, "Virtual display device released because media projection stopped: "
206                     + device.mName);
207             device.stopLocked();
208         }
209     }
210 
211     private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient {
212         private static final int PENDING_SURFACE_CHANGE = 0x01;
213         private static final int PENDING_RESIZE = 0x02;
214 
215         private static final float REFRESH_RATE = 60.0f;
216 
217         private final IBinder mAppToken;
218         private final int mOwnerUid;
219         final String mOwnerPackageName;
220         final String mName;
221         private final int mFlags;
222         private final Callback mCallback;
223 
224         private int mWidth;
225         private int mHeight;
226         private int mDensityDpi;
227         private Surface mSurface;
228         private DisplayDeviceInfo mInfo;
229         private int mDisplayState;
230         private boolean mStopped;
231         private int mPendingChanges;
232         private int mUniqueIndex;
233         private Display.Mode mMode;
234         private boolean mIsDisplayOn;
235         private int mDisplayIdToMirror;
236         private IBinder mWindowTokenClientToMirror;
237 
VirtualDisplayDevice(IBinder displayToken, IBinder appToken, int ownerUid, String ownerPackageName, Surface surface, int flags, Callback callback, String uniqueId, int uniqueIndex, VirtualDisplayConfig virtualDisplayConfig)238         public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
239                 int ownerUid, String ownerPackageName, Surface surface, int flags,
240                 Callback callback, String uniqueId, int uniqueIndex,
241                 VirtualDisplayConfig virtualDisplayConfig) {
242             super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext());
243             mAppToken = appToken;
244             mOwnerUid = ownerUid;
245             mOwnerPackageName = ownerPackageName;
246             mName = virtualDisplayConfig.getName();
247             mWidth = virtualDisplayConfig.getWidth();
248             mHeight = virtualDisplayConfig.getHeight();
249             mMode = createMode(mWidth, mHeight, REFRESH_RATE);
250             mDensityDpi = virtualDisplayConfig.getDensityDpi();
251             mSurface = surface;
252             mFlags = flags;
253             mCallback = callback;
254             mDisplayState = Display.STATE_UNKNOWN;
255             mPendingChanges |= PENDING_SURFACE_CHANGE;
256             mUniqueIndex = uniqueIndex;
257             mIsDisplayOn = surface != null;
258             mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror();
259             mWindowTokenClientToMirror = virtualDisplayConfig.getWindowTokenClientToMirror();
260         }
261 
262         @Override
binderDied()263         public void binderDied() {
264             synchronized (getSyncRoot()) {
265                 handleBinderDiedLocked(mAppToken);
266                 Slog.i(TAG, "Virtual display device released because application token died: "
267                     + mOwnerPackageName);
268                 destroyLocked(false);
269                 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_REMOVED);
270             }
271         }
272 
destroyLocked(boolean binderAlive)273         public void destroyLocked(boolean binderAlive) {
274             if (mSurface != null) {
275                 mSurface.release();
276                 mSurface = null;
277             }
278             SurfaceControl.destroyDisplay(getDisplayTokenLocked());
279             if (binderAlive) {
280                 mCallback.dispatchDisplayStopped();
281             }
282         }
283 
284         @Override
getDisplayIdToMirrorLocked()285         public int getDisplayIdToMirrorLocked() {
286             return mDisplayIdToMirror;
287         }
288 
289         @Override
290         @Nullable
getWindowTokenClientToMirrorLocked()291         public IBinder getWindowTokenClientToMirrorLocked() {
292             return mWindowTokenClientToMirror;
293         }
294 
295         @Override
setWindowTokenClientToMirrorLocked(IBinder windowToken)296         public void setWindowTokenClientToMirrorLocked(IBinder windowToken) {
297             if (mWindowTokenClientToMirror != windowToken) {
298                 mWindowTokenClientToMirror = windowToken;
299                 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
300                 sendTraversalRequestLocked();
301             }
302         }
303 
304         @Override
getDisplaySurfaceDefaultSize()305         public Point getDisplaySurfaceDefaultSize() {
306             if (mSurface == null) {
307                 return null;
308             }
309             return mSurface.getDefaultSize();
310         }
311 
312         @VisibleForTesting
getSurfaceLocked()313         Surface getSurfaceLocked() {
314             return mSurface;
315         }
316 
317         @Override
hasStableUniqueId()318         public boolean hasStableUniqueId() {
319             return false;
320         }
321 
322         @Override
requestDisplayStateLocked(int state, float brightnessState, float sdrBrightnessState)323         public Runnable requestDisplayStateLocked(int state, float brightnessState,
324                 float sdrBrightnessState) {
325             if (state != mDisplayState) {
326                 mDisplayState = state;
327                 if (state == Display.STATE_OFF) {
328                     mCallback.dispatchDisplayPaused();
329                 } else {
330                     mCallback.dispatchDisplayResumed();
331                 }
332             }
333             return null;
334         }
335 
336         @Override
performTraversalLocked(SurfaceControl.Transaction t)337         public void performTraversalLocked(SurfaceControl.Transaction t) {
338             if ((mPendingChanges & PENDING_RESIZE) != 0) {
339                 t.setDisplaySize(getDisplayTokenLocked(), mWidth, mHeight);
340             }
341             if ((mPendingChanges & PENDING_SURFACE_CHANGE) != 0) {
342                 setSurfaceLocked(t, mSurface);
343             }
344             mPendingChanges = 0;
345         }
346 
setSurfaceLocked(Surface surface)347         public void setSurfaceLocked(Surface surface) {
348             if (!mStopped && mSurface != surface) {
349                 if ((mSurface != null) != (surface != null)) {
350                     sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
351                 }
352                 sendTraversalRequestLocked();
353                 mSurface = surface;
354                 mInfo = null;
355                 mPendingChanges |= PENDING_SURFACE_CHANGE;
356             }
357         }
358 
resizeLocked(int width, int height, int densityDpi)359         public void resizeLocked(int width, int height, int densityDpi) {
360             if (mWidth != width || mHeight != height || mDensityDpi != densityDpi) {
361                 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
362                 sendTraversalRequestLocked();
363                 mWidth = width;
364                 mHeight = height;
365                 mMode = createMode(width, height, REFRESH_RATE);
366                 mDensityDpi = densityDpi;
367                 mInfo = null;
368                 mPendingChanges |= PENDING_RESIZE;
369             }
370         }
371 
setDisplayState(boolean isOn)372         void setDisplayState(boolean isOn) {
373             if (mIsDisplayOn != isOn) {
374                 mIsDisplayOn = isOn;
375                 mInfo = null;
376                 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
377             }
378         }
379 
stopLocked()380         public void stopLocked() {
381             setSurfaceLocked(null);
382             mStopped = true;
383         }
384 
385         @Override
dumpLocked(PrintWriter pw)386         public void dumpLocked(PrintWriter pw) {
387             super.dumpLocked(pw);
388             pw.println("mFlags=" + mFlags);
389             pw.println("mDisplayState=" + Display.stateToString(mDisplayState));
390             pw.println("mStopped=" + mStopped);
391             pw.println("mDisplayIdToMirror=" + mDisplayIdToMirror);
392             pw.println("mWindowTokenClientToMirror=" + mWindowTokenClientToMirror);
393         }
394 
395 
396         @Override
getDisplayDeviceInfoLocked()397         public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
398             if (mInfo == null) {
399                 mInfo = new DisplayDeviceInfo();
400                 mInfo.name = mName;
401                 mInfo.uniqueId = getUniqueId();
402                 mInfo.width = mWidth;
403                 mInfo.height = mHeight;
404                 mInfo.modeId = mMode.getModeId();
405                 mInfo.defaultModeId = mMode.getModeId();
406                 mInfo.supportedModes = new Display.Mode[] { mMode };
407                 mInfo.densityDpi = mDensityDpi;
408                 mInfo.xDpi = mDensityDpi;
409                 mInfo.yDpi = mDensityDpi;
410                 mInfo.presentationDeadlineNanos = 1000000000L / (int) REFRESH_RATE; // 1 frame
411                 mInfo.flags = 0;
412                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
413                     mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE
414                             | DisplayDeviceInfo.FLAG_NEVER_BLANK;
415                 }
416                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0) {
417                     mInfo.flags &= ~DisplayDeviceInfo.FLAG_NEVER_BLANK;
418                 } else {
419                     mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
420 
421                     if ((mFlags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) {
422                         mInfo.flags |= FLAG_OWN_DISPLAY_GROUP;
423                     }
424                 }
425 
426                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
427                     mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
428                 }
429                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) {
430                     mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
431 
432                     if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) {
433                         // For demonstration purposes, allow rotation of the external display.
434                         // In the future we might allow the user to configure this directly.
435                         if ("portrait".equals(SystemProperties.get(
436                                 "persist.demo.remoterotation"))) {
437                             mInfo.rotation = Surface.ROTATION_270;
438                         }
439                     }
440                 }
441                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) {
442                     mInfo.flags |= DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD;
443                 }
444                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT) != 0) {
445                     mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT;
446                 }
447                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) {
448                     mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL;
449                 }
450                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) {
451                     mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS;
452                 }
453                 if ((mFlags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) {
454                     mInfo.flags |= FLAG_TRUSTED;
455                 }
456 
457                 mInfo.type = Display.TYPE_VIRTUAL;
458                 mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ?
459                         DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL;
460 
461                 mInfo.state = mIsDisplayOn ? Display.STATE_ON : Display.STATE_OFF;
462 
463                 mInfo.ownerUid = mOwnerUid;
464                 mInfo.ownerPackageName = mOwnerPackageName;
465             }
466             return mInfo;
467         }
468     }
469 
470     private static class Callback extends Handler {
471         private static final int MSG_ON_DISPLAY_PAUSED = 0;
472         private static final int MSG_ON_DISPLAY_RESUMED = 1;
473         private static final int MSG_ON_DISPLAY_STOPPED = 2;
474 
475         private final IVirtualDisplayCallback mCallback;
476 
Callback(IVirtualDisplayCallback callback, Handler handler)477         public Callback(IVirtualDisplayCallback callback, Handler handler) {
478             super(handler.getLooper());
479             mCallback = callback;
480         }
481 
482         @Override
handleMessage(Message msg)483         public void handleMessage(Message msg) {
484             try {
485                 switch (msg.what) {
486                     case MSG_ON_DISPLAY_PAUSED:
487                         mCallback.onPaused();
488                         break;
489                     case MSG_ON_DISPLAY_RESUMED:
490                         mCallback.onResumed();
491                         break;
492                     case MSG_ON_DISPLAY_STOPPED:
493                         mCallback.onStopped();
494                         break;
495                 }
496             } catch (RemoteException e) {
497                 Slog.w(TAG, "Failed to notify listener of virtual display event.", e);
498             }
499         }
500 
dispatchDisplayPaused()501         public void dispatchDisplayPaused() {
502             sendEmptyMessage(MSG_ON_DISPLAY_PAUSED);
503         }
504 
dispatchDisplayResumed()505         public void dispatchDisplayResumed() {
506             sendEmptyMessage(MSG_ON_DISPLAY_RESUMED);
507         }
508 
dispatchDisplayStopped()509         public void dispatchDisplayStopped() {
510             sendEmptyMessage(MSG_ON_DISPLAY_STOPPED);
511         }
512     }
513 
514     private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub {
515         private IBinder mAppToken;
MediaProjectionCallback(IBinder appToken)516         public MediaProjectionCallback(IBinder appToken) {
517             mAppToken = appToken;
518         }
519 
520         @Override
onStop()521         public void onStop() {
522             synchronized (getSyncRoot()) {
523                 handleMediaProjectionStoppedLocked(mAppToken);
524             }
525         }
526     }
527 
528     @VisibleForTesting
529     public interface SurfaceControlDisplayFactory {
createDisplay(String name, boolean secure)530         public IBinder createDisplay(String name, boolean secure);
531     }
532 }
533