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