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