VirtualDisplayAdapter.java revision c39d47a8e7c74bd539104b0efab898ef6fc43ddf
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.IVirtualDisplayCallbacks;
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(IVirtualDisplayCallbacks callbacks,
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 = callbacks.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 Callbacks(callbacks, mHandler));
67
68        mVirtualDisplayDevices.put(appToken, device);
69
70        try {
71            if (projection != null) {
72                projection.addCallback(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 setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) {
87        VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken);
88        if (device != null) {
89            device.setSurfaceLocked(surface);
90        }
91    }
92
93    public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) {
94        VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
95        if (device != null) {
96            device.destroyLocked();
97            appToken.unlinkToDeath(device, 0);
98        }
99
100        // Return the display device that was removed without actually sending the
101        // event indicating that it was removed.  The caller will handle it.
102        return device;
103    }
104
105    private void handleBinderDiedLocked(IBinder appToken) {
106        VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
107        if (device != null) {
108            Slog.i(TAG, "Virtual display device released because application token died: "
109                    + device.mOwnerPackageName);
110            device.destroyLocked();
111            sendDisplayDeviceEventLocked(device, DISPLAY_DEVICE_EVENT_REMOVED);
112        }
113    }
114
115    private void handleMediaProjectionStoppedLocked(IBinder appToken) {
116        VirtualDisplayDevice device = mVirtualDisplayDevices.remove(appToken);
117        if (device != null) {
118            Slog.i(TAG, "Virtual display device released because media projection stopped: "
119                    + device.mName);
120            device.stopLocked();
121        }
122    }
123
124    private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient {
125        private final IBinder mAppToken;
126        private final int mOwnerUid;
127        final String mOwnerPackageName;
128        final String mName;
129        private final int mWidth;
130        private final int mHeight;
131        private final int mDensityDpi;
132        private final int mFlags;
133        private final Callbacks mCallbacks;
134
135        private Surface mSurface;
136        private DisplayDeviceInfo mInfo;
137        private int mState;
138        private boolean mStopped;
139
140        public VirtualDisplayDevice(IBinder displayToken, IBinder appToken,
141                int ownerUid, String ownerPackageName,
142                String name, int width, int height, int densityDpi, Surface surface, int flags,
143                Callbacks callbacks) {
144            super(VirtualDisplayAdapter.this, displayToken);
145            mAppToken = appToken;
146            mOwnerUid = ownerUid;
147            mOwnerPackageName = ownerPackageName;
148            mName = name;
149            mWidth = width;
150            mHeight = height;
151            mDensityDpi = densityDpi;
152            mSurface = surface;
153            mFlags = flags;
154            mCallbacks = callbacks;
155            mState = Display.STATE_UNKNOWN;
156        }
157
158        @Override
159        public void binderDied() {
160            synchronized (getSyncRoot()) {
161                if (mSurface != null) {
162                    handleBinderDiedLocked(mAppToken);
163                }
164            }
165        }
166
167        public void destroyLocked() {
168            if (mSurface != null) {
169                mSurface.release();
170                mSurface = null;
171            }
172            SurfaceControl.destroyDisplay(getDisplayTokenLocked());
173            mCallbacks.dispatchDisplayStopped();
174        }
175
176        @Override
177        public void requestDisplayStateLocked(int state) {
178            if (state != mState) {
179                mState = state;
180                if (state == Display.STATE_OFF) {
181                    mCallbacks.dispatchDisplayPaused();
182                } else {
183                    mCallbacks.dispatchDisplayResumed();
184                }
185            }
186        }
187
188        @Override
189        public void performTraversalInTransactionLocked() {
190            setSurfaceInTransactionLocked(mSurface);
191        }
192
193        public void setSurfaceLocked(Surface surface) {
194            if (!mStopped && mSurface != surface) {
195                if ((mSurface != null) != (surface != null)) {
196                    sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED);
197                }
198                sendTraversalRequestLocked();
199                mSurface = surface;
200                mInfo = null;
201            }
202        }
203
204        public void stopLocked() {
205            setSurfaceLocked(null);
206            mStopped = true;
207        }
208
209        @Override
210        public void dumpLocked(PrintWriter pw) {
211            super.dumpLocked(pw);
212            pw.println("mFlags=" + mFlags);
213            pw.println("mState=" + Display.stateToString(mState));
214            pw.println("mStopped=" + mStopped);
215        }
216
217
218        @Override
219        public DisplayDeviceInfo getDisplayDeviceInfoLocked() {
220            if (mInfo == null) {
221                mInfo = new DisplayDeviceInfo();
222                mInfo.name = mName;
223                mInfo.width = mWidth;
224                mInfo.height = mHeight;
225                mInfo.refreshRate = 60;
226                mInfo.densityDpi = mDensityDpi;
227                mInfo.xDpi = mDensityDpi;
228                mInfo.yDpi = mDensityDpi;
229                mInfo.presentationDeadlineNanos = 1000000000L / (int) mInfo.refreshRate; // 1 frame
230                mInfo.flags = 0;
231                if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) {
232                    mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE;
233                    if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SCREEN_SHARE) == 0) {
234                        mInfo.flags |=  DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY
235                                | DisplayDeviceInfo.FLAG_NEVER_BLANK;
236                    }
237                } else if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY) != 0) {
238                    mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY;
239                }
240                if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE) != 0) {
241                    mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE;
242                }
243                if ((mFlags & DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) {
244                    mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION;
245                }
246                mInfo.type = Display.TYPE_VIRTUAL;
247                mInfo.touch = DisplayDeviceInfo.TOUCH_NONE;
248                mInfo.state = mSurface != null ? Display.STATE_ON : Display.STATE_OFF;
249                mInfo.ownerUid = mOwnerUid;
250                mInfo.ownerPackageName = mOwnerPackageName;
251            }
252            return mInfo;
253        }
254    }
255
256    private static class Callbacks extends Handler {
257        private static final int MSG_ON_DISPLAY_PAUSED = 0;
258        private static final int MSG_ON_DISPLAY_RESUMED = 1;
259        private static final int MSG_ON_DISPLAY_STOPPED = 2;
260
261        private final IVirtualDisplayCallbacks mCallbacks;
262
263        public Callbacks(IVirtualDisplayCallbacks callbacks, Handler handler) {
264            super(handler.getLooper());
265            mCallbacks = callbacks;
266        }
267
268        @Override
269        public void handleMessage(Message msg) {
270            try {
271                switch (msg.what) {
272                    case MSG_ON_DISPLAY_PAUSED:
273                        mCallbacks.onDisplayPaused();
274                        break;
275                    case MSG_ON_DISPLAY_RESUMED:
276                        mCallbacks.onDisplayResumed();
277                        break;
278                    case MSG_ON_DISPLAY_STOPPED:
279                        mCallbacks.onDisplayStopped();
280                        break;
281                }
282            } catch (RemoteException e) {
283                Slog.w(TAG, "Failed to notify listener of virtual display event.", e);
284            }
285        }
286
287        public void dispatchDisplayPaused() {
288            sendEmptyMessage(MSG_ON_DISPLAY_PAUSED);
289        }
290
291        public void dispatchDisplayResumed() {
292            sendEmptyMessage(MSG_ON_DISPLAY_RESUMED);
293        }
294
295        public void dispatchDisplayStopped() {
296            sendEmptyMessage(MSG_ON_DISPLAY_STOPPED);
297        }
298    }
299
300    private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub {
301        private IBinder mAppToken;
302        public MediaProjectionCallback(IBinder appToken) {
303            mAppToken = appToken;
304        }
305
306        @Override
307        public void onStop() {
308            synchronized (getSyncRoot()) {
309                handleMediaProjectionStoppedLocked(mAppToken);
310            }
311        }
312    }
313}
314