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