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