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