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