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