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_ALWAYS_UNLOCKED; 20 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR; 21 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; 22 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL; 23 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP; 24 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP; 25 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_FOCUS; 26 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PRESENTATION; 27 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC; 28 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT; 29 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SECURE; 30 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; 31 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED; 32 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH; 33 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TOUCH_FEEDBACK_DISABLED; 34 import static android.hardware.display.DisplayManager.VIRTUAL_DISPLAY_FLAG_TRUSTED; 35 36 import android.annotation.Nullable; 37 import android.content.Context; 38 import android.graphics.Point; 39 import android.hardware.display.IBrightnessListener; 40 import android.hardware.display.IVirtualDisplayCallback; 41 import android.hardware.display.VirtualDisplayConfig; 42 import android.media.projection.IMediaProjection; 43 import android.media.projection.IMediaProjectionCallback; 44 import android.os.Handler; 45 import android.os.IBinder; 46 import android.os.IBinder.DeathRecipient; 47 import android.os.Message; 48 import android.os.PowerManager; 49 import android.os.RemoteException; 50 import android.os.SystemProperties; 51 import android.util.ArrayMap; 52 import android.util.Slog; 53 import android.util.SparseIntArray; 54 import android.view.Display; 55 import android.view.DisplayCutout; 56 import android.view.DisplayShape; 57 import android.view.Surface; 58 import android.view.SurfaceControl; 59 60 import com.android.internal.R; 61 import com.android.internal.annotations.VisibleForTesting; 62 import com.android.server.display.brightness.BrightnessUtils; 63 import com.android.server.display.feature.DisplayManagerFlags; 64 65 import java.io.PrintWriter; 66 import java.util.concurrent.atomic.AtomicInteger; 67 68 /** 69 * A display adapter that provides virtual displays on behalf of applications. 70 * <p> 71 * Display adapters are guarded by the {@link DisplayManagerService.SyncRoot} lock. 72 * </p> 73 */ 74 @VisibleForTesting(visibility = VisibleForTesting.Visibility.PACKAGE) 75 public class VirtualDisplayAdapter extends DisplayAdapter { 76 static final String TAG = "VirtualDisplayAdapter"; 77 78 // Unique id prefix for virtual displays 79 @VisibleForTesting 80 static final String UNIQUE_ID_PREFIX = "virtual:"; 81 82 // Unique id suffix for virtual displays 83 private static final AtomicInteger sNextUniqueIndex = new AtomicInteger(0); 84 85 private final ArrayMap<IBinder, VirtualDisplayDevice> mVirtualDisplayDevices = new ArrayMap<>(); 86 87 // When a virtual display is created, the mapping (appToken -> ownerUid) is stored here. That 88 // way, when the display is released later, we can retrieve the ownerUid and decrement 89 // the number of virtual displays that exist for that ownerUid. We can't use 90 // Binder.getCallingUid() because the display might be released by the system process and not 91 // the process that created the display. 92 private final ArrayMap<IBinder, Integer> mOwnerUids = new ArrayMap<>(); 93 94 private final int mMaxDevices; 95 private final int mMaxDevicesPerPackage; 96 private final SparseIntArray mNoOfDevicesPerPackage = new SparseIntArray(); 97 98 private final Handler mHandler; 99 private final SurfaceControlDisplayFactory mSurfaceControlDisplayFactory; 100 101 // Called with SyncRoot lock held. VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, DisplayManagerFlags featureFlags)102 public VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 103 Context context, Handler handler, Listener listener, DisplayManagerFlags featureFlags) { 104 this(syncRoot, context, handler, listener, new SurfaceControlDisplayFactory() { 105 @Override 106 public IBinder createDisplay(String name, boolean secure, boolean optimizeForPower, 107 String uniqueId, float requestedRefreshRate) { 108 return DisplayControl.createVirtualDisplay(name, secure, optimizeForPower, uniqueId, 109 requestedRefreshRate); 110 } 111 112 @Override 113 public void destroyDisplay(IBinder displayToken) { 114 DisplayControl.destroyVirtualDisplay(displayToken); 115 } 116 117 @Override 118 public void setDisplayPowerMode(IBinder displayToken, int mode) { 119 SurfaceControl.setDisplayPowerMode(displayToken, mode); 120 } 121 }, featureFlags); 122 } 123 124 @VisibleForTesting VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, Context context, Handler handler, Listener listener, SurfaceControlDisplayFactory surfaceControlDisplayFactory, DisplayManagerFlags featureFlags)125 VirtualDisplayAdapter(DisplayManagerService.SyncRoot syncRoot, 126 Context context, Handler handler, Listener listener, 127 SurfaceControlDisplayFactory surfaceControlDisplayFactory, 128 DisplayManagerFlags featureFlags) { 129 super(syncRoot, context, handler, listener, TAG, featureFlags); 130 mHandler = handler; 131 mSurfaceControlDisplayFactory = surfaceControlDisplayFactory; 132 133 mMaxDevices = context.getResources().getInteger(R.integer.config_virtualDisplayLimit); 134 if (mMaxDevices < 1) { 135 throw new IllegalArgumentException("The limit of virtual displays must be >= 1"); 136 } 137 mMaxDevicesPerPackage = 138 context.getResources().getInteger(R.integer.config_virtualDisplayLimitPerPackage); 139 if (mMaxDevicesPerPackage < 1) { 140 throw new IllegalArgumentException( 141 "The limit of virtual displays per package must be >= 1"); 142 } 143 } 144 145 /** 146 * Create a virtual display 147 * @param callback The callback 148 * @param projection The media projection 149 * @param ownerUid The UID of the package creating a display 150 * @param ownerPackageName The name of the package creating a display 151 * @param uniqueId The unique ID of the display device 152 * @param surface The surface 153 * @param flags The flags 154 * @param virtualDisplayConfig The config 155 * @return The display device created 156 */ createVirtualDisplayLocked(IVirtualDisplayCallback callback, IMediaProjection projection, int ownerUid, String ownerPackageName, String uniqueId, Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig)157 public DisplayDevice createVirtualDisplayLocked(IVirtualDisplayCallback callback, 158 IMediaProjection projection, int ownerUid, String ownerPackageName, String uniqueId, 159 Surface surface, int flags, VirtualDisplayConfig virtualDisplayConfig) { 160 IBinder appToken = callback.asBinder(); 161 if (mVirtualDisplayDevices.containsKey(appToken)) { 162 Slog.wtfStack(TAG, 163 "Can't create virtual display, display with same appToken already exists"); 164 return null; 165 } 166 167 if (getFeatureFlags().isVirtualDisplayLimitEnabled() 168 && mVirtualDisplayDevices.size() >= mMaxDevices) { 169 Slog.w(TAG, "Rejecting request to create private virtual display because " 170 + mMaxDevices + " devices already exist."); 171 return null; 172 } 173 174 int noOfDevices = mNoOfDevicesPerPackage.get(ownerUid, /* valueIfKeyNotFound= */ 0); 175 if (getFeatureFlags().isVirtualDisplayLimitEnabled() 176 && noOfDevices >= mMaxDevicesPerPackage) { 177 Slog.w(TAG, "Rejecting request to create private virtual display because " 178 + mMaxDevicesPerPackage + " devices already exist for package " 179 + ownerPackageName + "."); 180 return null; 181 } 182 183 String name = virtualDisplayConfig.getName(); 184 boolean secure = (flags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0; 185 boolean neverBlank = isNeverBlank(flags); 186 187 // Never-blank displays are considered to be dependent on another display to be rendered. 188 // As a result, such displays should optimize for power instead of performance when it is 189 // powered on. 190 IBinder displayToken = mSurfaceControlDisplayFactory.createDisplay(name, secure, neverBlank, 191 uniqueId, virtualDisplayConfig.getRequestedRefreshRate()); 192 MediaProjectionCallback mediaProjectionCallback = null; 193 if (projection != null) { 194 mediaProjectionCallback = new MediaProjectionCallback(appToken); 195 } 196 197 Callback callbackDelegate = new Callback( 198 callback, virtualDisplayConfig.getBrightnessListener(), mHandler); 199 VirtualDisplayDevice device = new VirtualDisplayDevice(displayToken, appToken, 200 ownerUid, ownerPackageName, surface, flags, callbackDelegate, 201 projection, mediaProjectionCallback, uniqueId, virtualDisplayConfig); 202 203 mVirtualDisplayDevices.put(appToken, device); 204 if (getFeatureFlags().isVirtualDisplayLimitEnabled()) { 205 mNoOfDevicesPerPackage.put(ownerUid, noOfDevices + 1); 206 mOwnerUids.put(appToken, ownerUid); 207 } 208 209 try { 210 if (projection != null) { 211 projection.registerCallback(mediaProjectionCallback); 212 Slog.d(TAG, "Virtual Display: registered media projection callback for new " 213 + "VirtualDisplayDevice"); 214 } 215 appToken.linkToDeath(device, 0); 216 } catch (RemoteException ex) { 217 Slog.e(TAG, "Virtual Display: error while setting up VirtualDisplayDevice", ex); 218 removeVirtualDisplayDeviceLocked(appToken); 219 device.destroyLocked(false); 220 return null; 221 } 222 223 // Return the display device without actually sending the event indicating 224 // that it was added. The caller will handle it. 225 return device; 226 } 227 resizeVirtualDisplayLocked(IBinder appToken, int width, int height, int densityDpi)228 public void resizeVirtualDisplayLocked(IBinder appToken, 229 int width, int height, int densityDpi) { 230 VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); 231 if (device != null) { 232 Slog.v(TAG, "Resize VirtualDisplay " + device.mName + " to " + width 233 + " " + height); 234 device.resizeLocked(width, height, densityDpi); 235 } 236 } 237 238 @VisibleForTesting getVirtualDisplaySurfaceLocked(IBinder appToken)239 Surface getVirtualDisplaySurfaceLocked(IBinder appToken) { 240 VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); 241 if (device != null) { 242 return device.getSurfaceLocked(); 243 } 244 return null; 245 } 246 setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface)247 public void setVirtualDisplaySurfaceLocked(IBinder appToken, Surface surface) { 248 VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); 249 if (device != null) { 250 Slog.v(TAG, "Update surface for VirtualDisplay " + device.mName); 251 device.setSurfaceLocked(surface); 252 } 253 } 254 setDisplayIdToMirror(IBinder appToken, int displayId)255 void setDisplayIdToMirror(IBinder appToken, int displayId) { 256 VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); 257 if (device != null) { 258 device.setDisplayIdToMirror(displayId); 259 } 260 } 261 262 /** 263 * Release a virtual display that was previously created 264 * @param appToken The token to identify the display 265 * @return The display device that has been removed 266 */ releaseVirtualDisplayLocked(IBinder appToken)267 public DisplayDevice releaseVirtualDisplayLocked(IBinder appToken) { 268 VirtualDisplayDevice device = removeVirtualDisplayDeviceLocked(appToken); 269 if (device != null) { 270 Slog.v(TAG, "Release VirtualDisplay " + device.mName); 271 device.destroyLocked(true); 272 appToken.unlinkToDeath(device, 0); 273 } 274 275 // Return the display device that was removed without actually sending the 276 // event indicating that it was removed. The caller will handle it. 277 return device; 278 } 279 getDisplayDevice(IBinder appToken)280 DisplayDevice getDisplayDevice(IBinder appToken) { 281 return mVirtualDisplayDevices.get(appToken); 282 } 283 284 /** 285 * Generates a virtual display's unique identifier. 286 * 287 * <p>It is always prefixed with "virtual:package-name". If the provided config explicitly 288 * specifies a unique ID, then it's simply appended. Otherwise, the UID, display name and a 289 * unique index are appended.</p> 290 * 291 * <p>The unique index is incremented for every virtual display unique ID generation and serves 292 * for differentiating between displays with the same name created by the same owner.</p> 293 */ generateDisplayUniqueId(String packageName, int uid, VirtualDisplayConfig config)294 static String generateDisplayUniqueId(String packageName, int uid, 295 VirtualDisplayConfig config) { 296 return UNIQUE_ID_PREFIX + packageName + ((config.getUniqueId() != null) 297 ? (":" + config.getUniqueId()) 298 : ("," + uid + "," + config.getName() + "," + sNextUniqueIndex.getAndIncrement())); 299 } 300 handleMediaProjectionStoppedLocked(IBinder appToken)301 private void handleMediaProjectionStoppedLocked(IBinder appToken) { 302 VirtualDisplayDevice device = mVirtualDisplayDevices.get(appToken); 303 if (device != null) { 304 Slog.i(TAG, "Virtual display device released because media projection stopped: " 305 + device.mName); 306 device.stopLocked(); 307 } 308 } 309 removeVirtualDisplayDeviceLocked(IBinder appToken)310 private VirtualDisplayDevice removeVirtualDisplayDeviceLocked(IBinder appToken) { 311 if (getFeatureFlags().isVirtualDisplayLimitEnabled()) { 312 Integer ownerUid = mOwnerUids.remove(appToken); 313 if (ownerUid != null) { 314 int noOfDevices = mNoOfDevicesPerPackage.get(ownerUid, /* valueIfKeyNotFound= */ 0); 315 if (noOfDevices <= 1) { 316 mNoOfDevicesPerPackage.delete(ownerUid); 317 } else { 318 mNoOfDevicesPerPackage.put(ownerUid, noOfDevices - 1); 319 } 320 } 321 } 322 return mVirtualDisplayDevices.remove(appToken); 323 } 324 isNeverBlank(int flags)325 private static boolean isNeverBlank(int flags) { 326 // Private non-mirror displays are never blank and always on. 327 return (flags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0 328 && (flags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0; 329 } 330 331 private final class VirtualDisplayDevice extends DisplayDevice implements DeathRecipient { 332 private static final int PENDING_SURFACE_CHANGE = 0x01; 333 private static final int PENDING_RESIZE = 0x02; 334 335 private static final float REFRESH_RATE = 60.0f; 336 337 private final IBinder mAppToken; 338 private final int mOwnerUid; 339 final String mOwnerPackageName; 340 final String mName; 341 private final int mFlags; 342 private final Callback mCallback; 343 @Nullable private final IMediaProjection mProjection; 344 @Nullable private final IMediaProjectionCallback mMediaProjectionCallback; 345 346 private int mWidth; 347 private int mHeight; 348 private int mDensityDpi; 349 private final float mRequestedRefreshRate; 350 private Surface mSurface; 351 private DisplayDeviceInfo mInfo; 352 private int mDisplayState; 353 private boolean mStopped; 354 private int mPendingChanges; 355 private Display.Mode mMode; 356 private int mDisplayIdToMirror; 357 private boolean mIsWindowManagerMirroring; 358 private final boolean mNeverBlank; 359 private final DisplayCutout mDisplayCutout; 360 private final float mDefaultBrightness; 361 private final float mDimBrightness; 362 private float mCurrentBrightness; 363 private final IBrightnessListener mBrightnessListener; 364 VirtualDisplayDevice(IBinder displayToken, IBinder appToken, int ownerUid, String ownerPackageName, Surface surface, int flags, Callback callback, IMediaProjection projection, IMediaProjectionCallback mediaProjectionCallback, String uniqueId, VirtualDisplayConfig virtualDisplayConfig)365 public VirtualDisplayDevice(IBinder displayToken, IBinder appToken, 366 int ownerUid, String ownerPackageName, Surface surface, int flags, 367 Callback callback, IMediaProjection projection, 368 IMediaProjectionCallback mediaProjectionCallback, String uniqueId, 369 VirtualDisplayConfig virtualDisplayConfig) { 370 super(VirtualDisplayAdapter.this, displayToken, uniqueId, getContext()); 371 mAppToken = appToken; 372 mOwnerUid = ownerUid; 373 mOwnerPackageName = ownerPackageName; 374 mName = virtualDisplayConfig.getName(); 375 mWidth = virtualDisplayConfig.getWidth(); 376 mHeight = virtualDisplayConfig.getHeight(); 377 mDensityDpi = virtualDisplayConfig.getDensityDpi(); 378 mRequestedRefreshRate = virtualDisplayConfig.getRequestedRefreshRate(); 379 mDisplayCutout = virtualDisplayConfig.getDisplayCutout(); 380 mDefaultBrightness = virtualDisplayConfig.getDefaultBrightness(); 381 mDimBrightness = virtualDisplayConfig.getDimBrightness(); 382 mCurrentBrightness = PowerManager.BRIGHTNESS_INVALID; 383 mBrightnessListener = virtualDisplayConfig.getBrightnessListener(); 384 mMode = createMode(mWidth, mHeight, getRefreshRate()); 385 mSurface = surface; 386 mFlags = flags; 387 mCallback = callback; 388 mProjection = projection; 389 mMediaProjectionCallback = mediaProjectionCallback; 390 mNeverBlank = isNeverBlank(flags); 391 if (android.companion.virtualdevice.flags.Flags.correctVirtualDisplayPowerState() 392 && !mNeverBlank) { 393 // The display's power state depends on the power state of the state of its 394 // display / power group, which we don't know here. Initializing to UNKNOWN allows 395 // the first call to requestDisplayStateLocked() to set the correct state. 396 // This also triggers VirtualDisplay.Callback to tell the owner the initial state. 397 mDisplayState = Display.STATE_UNKNOWN; 398 } else { 399 mDisplayState = Display.STATE_ON; 400 } 401 mPendingChanges |= PENDING_SURFACE_CHANGE; 402 mDisplayIdToMirror = virtualDisplayConfig.getDisplayIdToMirror(); 403 mIsWindowManagerMirroring = virtualDisplayConfig.isWindowManagerMirroringEnabled(); 404 } 405 406 @Override binderDied()407 public void binderDied() { 408 synchronized (getSyncRoot()) { 409 removeVirtualDisplayDeviceLocked(mAppToken); 410 Slog.i(TAG, "Virtual display device released because application token died: " 411 + mOwnerPackageName); 412 destroyLocked(false); 413 if (mProjection != null && mMediaProjectionCallback != null) { 414 try { 415 mProjection.unregisterCallback(mMediaProjectionCallback); 416 } catch (RemoteException e) { 417 Slog.w(TAG, "Failed to unregister callback in binderDied", e); 418 } 419 } 420 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_REMOVED); 421 } 422 } 423 destroyLocked(boolean binderAlive)424 public void destroyLocked(boolean binderAlive) { 425 if (mSurface != null) { 426 mSurface.release(); 427 mSurface = null; 428 } 429 mSurfaceControlDisplayFactory.destroyDisplay(getDisplayTokenLocked()); 430 if (mProjection != null && mMediaProjectionCallback != null) { 431 try { 432 mProjection.unregisterCallback(mMediaProjectionCallback); 433 } catch (RemoteException e) { 434 Slog.w(TAG, "Failed to unregister callback in destroy", e); 435 } 436 } 437 if (binderAlive) { 438 mCallback.dispatchDisplayStopped(); 439 } 440 } 441 442 @Override getDisplayIdToMirrorLocked()443 public int getDisplayIdToMirrorLocked() { 444 return mDisplayIdToMirror; 445 } 446 setDisplayIdToMirror(int displayIdToMirror)447 void setDisplayIdToMirror(int displayIdToMirror) { 448 if (mDisplayIdToMirror != displayIdToMirror) { 449 mDisplayIdToMirror = displayIdToMirror; 450 mInfo = null; 451 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 452 sendTraversalRequestLocked(); 453 } 454 } 455 456 @Override isWindowManagerMirroringLocked()457 public boolean isWindowManagerMirroringLocked() { 458 return mIsWindowManagerMirroring; 459 } 460 461 @Override setWindowManagerMirroringLocked(boolean mirroring)462 public void setWindowManagerMirroringLocked(boolean mirroring) { 463 if (mIsWindowManagerMirroring != mirroring) { 464 mIsWindowManagerMirroring = mirroring; 465 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 466 sendTraversalRequestLocked(); 467 } 468 } 469 470 @Override getDisplaySurfaceDefaultSizeLocked()471 public Point getDisplaySurfaceDefaultSizeLocked() { 472 if (mSurface == null) { 473 return null; 474 } 475 final Point surfaceSize = mSurface.getDefaultSize(); 476 return isRotatedLocked() ? new Point(surfaceSize.y, surfaceSize.x) : surfaceSize; 477 } 478 479 @VisibleForTesting getSurfaceLocked()480 Surface getSurfaceLocked() { 481 return mSurface; 482 } 483 484 @Override hasStableUniqueId()485 public boolean hasStableUniqueId() { 486 return false; 487 } 488 489 @Override requestDisplayStateLocked(int state, float brightnessState, float sdrBrightnessState, DisplayOffloadSessionImpl displayOffloadSession)490 public Runnable requestDisplayStateLocked(int state, float brightnessState, 491 float sdrBrightnessState, DisplayOffloadSessionImpl displayOffloadSession) { 492 Runnable runnable = null; 493 if (state != mDisplayState) { 494 Slog.d(TAG, "Changing state of virtual display " + mName + " from " 495 + Display.stateToString(mDisplayState) + " to " 496 + Display.stateToString(state)); 497 if (state != Display.STATE_ON && state != Display.STATE_OFF) { 498 Slog.wtf(TAG, "Unexpected display state for Virtual Display: " 499 + Display.stateToString(state)); 500 } 501 mDisplayState = state; 502 mInfo = null; 503 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 504 if (state == Display.STATE_OFF) { 505 mCallback.dispatchDisplayPaused(); 506 } else { 507 mCallback.dispatchDisplayResumed(); 508 } 509 510 if (android.companion.virtualdevice.flags.Flags.correctVirtualDisplayPowerState()) { 511 final IBinder token = getDisplayTokenLocked(); 512 runnable = () -> { 513 final int mode = getPowerModeForState(state); 514 Slog.d(TAG, "Requesting power mode for display " + mName + " to " + mode); 515 mSurfaceControlDisplayFactory.setDisplayPowerMode(token, mode); 516 }; 517 } 518 } 519 if (android.companion.virtualdevice.flags.Flags.deviceAwareDisplayPower() 520 && mBrightnessListener != null 521 && BrightnessUtils.isValidBrightnessValue(brightnessState) 522 && brightnessState != mCurrentBrightness) { 523 mCurrentBrightness = brightnessState; 524 mCallback.dispatchRequestedBrightnessChanged(mCurrentBrightness); 525 } 526 return runnable; 527 } 528 529 @Override configureSurfaceLocked(SurfaceControl.Transaction t)530 public void configureSurfaceLocked(SurfaceControl.Transaction t) { 531 if ((mPendingChanges & PENDING_SURFACE_CHANGE) != 0) { 532 setSurfaceLocked(t, mSurface); 533 mPendingChanges &= ~PENDING_SURFACE_CHANGE; 534 } 535 } 536 537 @Override configureDisplaySizeLocked(SurfaceControl.Transaction t)538 public void configureDisplaySizeLocked(SurfaceControl.Transaction t) { 539 if ((mPendingChanges & PENDING_RESIZE) != 0) { 540 setDisplaySizeLocked(t, mWidth, mHeight); 541 mPendingChanges &= ~PENDING_RESIZE; 542 } 543 } 544 545 @Override shouldOnlyMirror()546 public boolean shouldOnlyMirror() { 547 return mProjection != null || ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) != 0); 548 } 549 setSurfaceLocked(Surface surface)550 public void setSurfaceLocked(Surface surface) { 551 if (!mStopped && mSurface != surface) { 552 if (mDisplayState == Display.STATE_ON 553 && ((mSurface == null) != (surface == null))) { 554 mInfo = null; 555 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 556 } 557 sendTraversalRequestLocked(); 558 mSurface = surface; 559 mPendingChanges |= PENDING_SURFACE_CHANGE; 560 } 561 } 562 resizeLocked(int width, int height, int densityDpi)563 public void resizeLocked(int width, int height, int densityDpi) { 564 if (mWidth != width || mHeight != height || mDensityDpi != densityDpi) { 565 sendDisplayDeviceEventLocked(this, DISPLAY_DEVICE_EVENT_CHANGED); 566 sendTraversalRequestLocked(); 567 mWidth = width; 568 mHeight = height; 569 mMode = createMode(width, height, getRefreshRate()); 570 mDensityDpi = densityDpi; 571 mInfo = null; 572 mPendingChanges |= PENDING_RESIZE; 573 } 574 } 575 stopLocked()576 public void stopLocked() { 577 Slog.d(TAG, "Virtual Display: stopping device " + mName); 578 setSurfaceLocked(null); 579 mStopped = true; 580 } 581 582 @Override dumpLocked(PrintWriter pw)583 public void dumpLocked(PrintWriter pw) { 584 super.dumpLocked(pw); 585 pw.println("mFlags=" + mFlags); 586 pw.println("mDisplayState=" + Display.stateToString(mDisplayState)); 587 pw.println("mStopped=" + mStopped); 588 pw.println("mDisplayIdToMirror=" + mDisplayIdToMirror); 589 pw.println("mWindowManagerMirroring=" + mIsWindowManagerMirroring); 590 pw.println("mRequestedRefreshRate=" + mRequestedRefreshRate); 591 } 592 593 @Override getDisplayDeviceInfoLocked()594 public DisplayDeviceInfo getDisplayDeviceInfoLocked() { 595 if (mInfo == null) { 596 mInfo = new DisplayDeviceInfo(); 597 mInfo.name = mName; 598 mInfo.uniqueId = getUniqueId(); 599 mInfo.width = mWidth; 600 mInfo.height = mHeight; 601 mInfo.modeId = mMode.getModeId(); 602 mInfo.renderFrameRate = mMode.getRefreshRate(); 603 mInfo.defaultModeId = mMode.getModeId(); 604 mInfo.supportedModes = new Display.Mode[] { mMode }; 605 mInfo.densityDpi = mDensityDpi; 606 mInfo.xDpi = mDensityDpi; 607 mInfo.yDpi = mDensityDpi; 608 mInfo.presentationDeadlineNanos = 1000000000L / (int) getRefreshRate(); // 1 frame 609 mInfo.flags = 0; 610 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) == 0) { 611 mInfo.flags |= DisplayDeviceInfo.FLAG_PRIVATE; 612 } 613 if ((mFlags & VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR) == 0) { 614 mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_CONTENT_ONLY; 615 } 616 if (mNeverBlank) { 617 mInfo.flags |= DisplayDeviceInfo.FLAG_NEVER_BLANK; 618 } 619 if ((mFlags & VIRTUAL_DISPLAY_FLAG_OWN_DISPLAY_GROUP) != 0) { 620 mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_DISPLAY_GROUP; 621 } 622 if ((mFlags & VIRTUAL_DISPLAY_FLAG_DEVICE_DISPLAY_GROUP) != 0) { 623 mInfo.flags |= DisplayDeviceInfo.FLAG_DEVICE_DISPLAY_GROUP; 624 } 625 if ((mFlags & VIRTUAL_DISPLAY_FLAG_SECURE) != 0) { 626 mInfo.flags |= DisplayDeviceInfo.FLAG_SECURE; 627 } 628 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PRESENTATION) != 0) { 629 mInfo.flags |= DisplayDeviceInfo.FLAG_PRESENTATION; 630 631 if ((mFlags & VIRTUAL_DISPLAY_FLAG_PUBLIC) != 0) { 632 // For demonstration purposes, allow rotation of the external display. 633 // In the future we might allow the user to configure this directly. 634 if ("portrait".equals(SystemProperties.get( 635 "persist.demo.remoterotation"))) { 636 mInfo.rotation = Surface.ROTATION_270; 637 } 638 } 639 } 640 if ((mFlags & VIRTUAL_DISPLAY_FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD) != 0) { 641 mInfo.flags |= DisplayDeviceInfo.FLAG_CAN_SHOW_WITH_INSECURE_KEYGUARD; 642 } 643 if ((mFlags & VIRTUAL_DISPLAY_FLAG_ROTATES_WITH_CONTENT) != 0) { 644 mInfo.flags |= DisplayDeviceInfo.FLAG_ROTATES_WITH_CONTENT; 645 } 646 if ((mFlags & VIRTUAL_DISPLAY_FLAG_DESTROY_CONTENT_ON_REMOVAL) != 0) { 647 mInfo.flags |= DisplayDeviceInfo.FLAG_DESTROY_CONTENT_ON_REMOVAL; 648 } 649 if ((mFlags & VIRTUAL_DISPLAY_FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS) != 0) { 650 mInfo.flags |= DisplayDeviceInfo.FLAG_SHOULD_SHOW_SYSTEM_DECORATIONS; 651 } 652 if ((mFlags & VIRTUAL_DISPLAY_FLAG_TRUSTED) != 0) { 653 mInfo.flags |= DisplayDeviceInfo.FLAG_TRUSTED; 654 } 655 if ((mFlags & VIRTUAL_DISPLAY_FLAG_ALWAYS_UNLOCKED) != 0) { 656 mInfo.flags |= DisplayDeviceInfo.FLAG_ALWAYS_UNLOCKED; 657 } 658 if ((mFlags & VIRTUAL_DISPLAY_FLAG_TOUCH_FEEDBACK_DISABLED) != 0) { 659 mInfo.flags |= DisplayDeviceInfo.FLAG_TOUCH_FEEDBACK_DISABLED; 660 } 661 if ((mFlags & VIRTUAL_DISPLAY_FLAG_OWN_FOCUS) != 0) { 662 mInfo.flags |= DisplayDeviceInfo.FLAG_OWN_FOCUS; 663 } 664 if ((mFlags & VIRTUAL_DISPLAY_FLAG_STEAL_TOP_FOCUS_DISABLED) != 0) { 665 mInfo.flags |= DisplayDeviceInfo.FLAG_STEAL_TOP_FOCUS_DISABLED; 666 } 667 668 mInfo.type = Display.TYPE_VIRTUAL; 669 mInfo.touch = ((mFlags & VIRTUAL_DISPLAY_FLAG_SUPPORTS_TOUCH) == 0) ? 670 DisplayDeviceInfo.TOUCH_NONE : DisplayDeviceInfo.TOUCH_VIRTUAL; 671 672 if (mSurface == null) { 673 mInfo.state = Display.STATE_OFF; 674 } else { 675 mInfo.state = mDisplayState; 676 } 677 678 mInfo.brightnessMinimum = PowerManager.BRIGHTNESS_MIN; 679 mInfo.brightnessMaximum = PowerManager.BRIGHTNESS_MAX; 680 mInfo.brightnessDefault = mDefaultBrightness; 681 mInfo.brightnessDim = mDimBrightness; 682 683 mInfo.ownerUid = mOwnerUid; 684 mInfo.ownerPackageName = mOwnerPackageName; 685 686 mInfo.displayShape = 687 DisplayShape.createDefaultDisplayShape(mInfo.width, mInfo.height, false); 688 mInfo.displayCutout = mDisplayCutout; 689 } 690 return mInfo; 691 } 692 getRefreshRate()693 private float getRefreshRate() { 694 return (mRequestedRefreshRate != 0.0f) ? mRequestedRefreshRate : REFRESH_RATE; 695 } 696 } 697 698 private static class Callback extends Handler { 699 private static final int MSG_ON_DISPLAY_PAUSED = 0; 700 private static final int MSG_ON_DISPLAY_RESUMED = 1; 701 private static final int MSG_ON_DISPLAY_STOPPED = 2; 702 private static final int MSG_ON_REQUESTED_BRIGHTNESS_CHANGED = 3; 703 704 private final IVirtualDisplayCallback mCallback; 705 private final IBrightnessListener mBrightnessListener; 706 Callback(IVirtualDisplayCallback callback, IBrightnessListener brightnessListener, Handler handler)707 Callback(IVirtualDisplayCallback callback, IBrightnessListener brightnessListener, 708 Handler handler) { 709 super(handler.getLooper()); 710 mCallback = callback; 711 mBrightnessListener = brightnessListener; 712 } 713 714 @Override handleMessage(Message msg)715 public void handleMessage(Message msg) { 716 try { 717 switch (msg.what) { 718 case MSG_ON_DISPLAY_PAUSED: 719 mCallback.onPaused(); 720 break; 721 case MSG_ON_DISPLAY_RESUMED: 722 mCallback.onResumed(); 723 break; 724 case MSG_ON_DISPLAY_STOPPED: 725 mCallback.onStopped(); 726 break; 727 case MSG_ON_REQUESTED_BRIGHTNESS_CHANGED: 728 if (mBrightnessListener != null) { 729 mBrightnessListener.onBrightnessChanged((Float) msg.obj); 730 } 731 break; 732 } 733 } catch (RemoteException e) { 734 Slog.w(TAG, "Failed to notify listener of virtual display event.", e); 735 } 736 } 737 dispatchDisplayPaused()738 public void dispatchDisplayPaused() { 739 sendEmptyMessage(MSG_ON_DISPLAY_PAUSED); 740 } 741 dispatchDisplayResumed()742 public void dispatchDisplayResumed() { 743 sendEmptyMessage(MSG_ON_DISPLAY_RESUMED); 744 } 745 dispatchRequestedBrightnessChanged(float brightness)746 public void dispatchRequestedBrightnessChanged(float brightness) { 747 Message msg = obtainMessage(MSG_ON_REQUESTED_BRIGHTNESS_CHANGED, brightness); 748 sendMessage(msg); 749 } 750 dispatchDisplayStopped()751 public void dispatchDisplayStopped() { 752 sendEmptyMessage(MSG_ON_DISPLAY_STOPPED); 753 } 754 } 755 756 private final class MediaProjectionCallback extends IMediaProjectionCallback.Stub { 757 private IBinder mAppToken; MediaProjectionCallback(IBinder appToken)758 public MediaProjectionCallback(IBinder appToken) { 759 mAppToken = appToken; 760 } 761 762 @Override onStop()763 public void onStop() { 764 synchronized (getSyncRoot()) { 765 handleMediaProjectionStoppedLocked(mAppToken); 766 } 767 } 768 769 @Override onCapturedContentResize(int width, int height)770 public void onCapturedContentResize(int width, int height) { 771 // Do nothing when we tell the client that the content is resized - it is up to them 772 // to decide to update the VirtualDisplay and Surface. 773 // We could only update the VirtualDisplay size, anyway (which the client wouldn't 774 // expect), and there will still be letterboxing on the output content since the 775 // Surface and VirtualDisplay would then have different aspect ratios. 776 } 777 778 @Override onCapturedContentVisibilityChanged(boolean isVisible)779 public void onCapturedContentVisibilityChanged(boolean isVisible) { 780 // Do nothing when we tell the client that the content has a visibility change - it is 781 // up to them to decide to pause recording, and update their own UI, depending on their 782 // use case. 783 } 784 } 785 786 @VisibleForTesting 787 public interface SurfaceControlDisplayFactory { 788 /** 789 * Create a virtual display in SurfaceFlinger. 790 * 791 * @param name The name of the display. 792 * @param secure Whether this display is secure. 793 * @param optimizeForPower Whether SurfaceFlinger should optimize for power (instead of 794 * performance). Such displays will depend on another display for 795 * it to be shown and rendered, and that display will optimize for 796 * performance when it is on. 797 * @param uniqueId The unique ID for the display. 798 * @param requestedRefreshRate 799 * The refresh rate, frames per second, to request on the virtual display. 800 * It should be a divisor of refresh rate of the leader physical display 801 * that drives VSYNC, e.g. 30/60fps on 120fps display. If an arbitrary refresh 802 * rate is specified, SurfaceFlinger rounds up or down to match a divisor of 803 * the refresh rate of the leader physical display. 804 * @return The token reference for the display in SurfaceFlinger. 805 */ createDisplay(String name, boolean secure, boolean optimizeForPower, String uniqueId, float requestedRefreshRate)806 IBinder createDisplay(String name, boolean secure, boolean optimizeForPower, 807 String uniqueId, float requestedRefreshRate); 808 809 /** 810 * Destroy a display in SurfaceFlinger. 811 * 812 * @param displayToken The display token for the display to be destroyed. 813 */ destroyDisplay(IBinder displayToken)814 void destroyDisplay(IBinder displayToken); 815 816 /** 817 * Set the display power mode in SurfaceFlinger. 818 * 819 * @param displayToken The display token for the display. 820 * @param mode the SurfaceControl power mode, e.g. {@link SurfaceControl#POWER_MODE_OFF}. 821 */ setDisplayPowerMode(IBinder displayToken, int mode)822 void setDisplayPowerMode(IBinder displayToken, int mode); 823 } 824 } 825