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