1 /* 2 * Copyright (C) 2022 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 android.window; 18 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.graphics.Bitmap; 22 import android.graphics.ColorSpace; 23 import android.graphics.Gainmap; 24 import android.graphics.PixelFormat; 25 import android.graphics.Rect; 26 import android.hardware.HardwareBuffer; 27 import android.os.Build; 28 import android.os.IBinder; 29 import android.os.Parcel; 30 import android.os.Parcelable; 31 import android.util.Log; 32 import android.view.SurfaceControl; 33 34 import com.android.window.flags.Flags; 35 36 import libcore.util.NativeAllocationRegistry; 37 38 import java.util.concurrent.CountDownLatch; 39 import java.util.concurrent.TimeUnit; 40 import java.util.function.ObjIntConsumer; 41 42 /** 43 * Handles display and layer captures for the system. 44 * 45 * @hide 46 */ 47 public class ScreenCapture { 48 private static final String TAG = "ScreenCapture"; 49 private static final int SCREENSHOT_WAIT_TIME_S = 4 * Build.HW_TIMEOUT_MULTIPLIER; 50 nativeCaptureDisplay(DisplayCaptureArgs captureArgs, long captureListener)51 private static native int nativeCaptureDisplay(DisplayCaptureArgs captureArgs, 52 long captureListener); nativeCaptureLayers(LayerCaptureArgs captureArgs, long captureListener, boolean sync)53 private static native int nativeCaptureLayers(LayerCaptureArgs captureArgs, 54 long captureListener, boolean sync); nativeCreateScreenCaptureListener( ObjIntConsumer<ScreenshotHardwareBuffer> consumer)55 private static native long nativeCreateScreenCaptureListener( 56 ObjIntConsumer<ScreenshotHardwareBuffer> consumer); nativeWriteListenerToParcel(long nativeObject, Parcel out)57 private static native void nativeWriteListenerToParcel(long nativeObject, Parcel out); nativeReadListenerFromParcel(Parcel in)58 private static native long nativeReadListenerFromParcel(Parcel in); getNativeListenerFinalizer()59 private static native long getNativeListenerFinalizer(); 60 61 /** 62 * @param captureArgs Arguments about how to take the screenshot 63 * @param captureListener A listener to receive the screenshot callback 64 * @hide 65 */ captureDisplay(@onNull DisplayCaptureArgs captureArgs, @NonNull ScreenCaptureListener captureListener)66 public static int captureDisplay(@NonNull DisplayCaptureArgs captureArgs, 67 @NonNull ScreenCaptureListener captureListener) { 68 return nativeCaptureDisplay(captureArgs, captureListener.mNativeObject); 69 } 70 71 /** 72 * Captures all the surfaces in a display and returns a {@link ScreenshotHardwareBuffer} with 73 * the content. 74 * 75 * @hide 76 */ captureDisplay( DisplayCaptureArgs captureArgs)77 public static ScreenshotHardwareBuffer captureDisplay( 78 DisplayCaptureArgs captureArgs) { 79 SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener(); 80 int status = captureDisplay(captureArgs, syncScreenCapture); 81 if (status != 0) { 82 return null; 83 } 84 85 try { 86 return syncScreenCapture.getBuffer(); 87 } catch (Exception e) { 88 return null; 89 } 90 } 91 92 /** 93 * Captures a layer and its children and returns a {@link HardwareBuffer} with the content. 94 * 95 * @param layer The root layer to capture. 96 * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new 97 * Rect()' or null if no cropping is desired. If the root layer does not 98 * have a buffer or a crop set, then a non-empty source crop must be 99 * specified. 100 * @param frameScale The desired scale of the returned buffer; the raw screen will be scaled 101 * up/down. 102 * @return Returns a HardwareBuffer that contains the layer capture. 103 * @hide 104 */ captureLayers(SurfaceControl layer, Rect sourceCrop, float frameScale)105 public static ScreenshotHardwareBuffer captureLayers(SurfaceControl layer, Rect sourceCrop, 106 float frameScale) { 107 return captureLayers(layer, sourceCrop, frameScale, PixelFormat.RGBA_8888); 108 } 109 110 /** 111 * Captures a layer and its children and returns a {@link HardwareBuffer} with the content. 112 * 113 * @param layer The root layer to capture. 114 * @param sourceCrop The portion of the root surface to capture; caller may pass in 'new 115 * Rect()' or null if no cropping is desired. If the root layer does not 116 * have a buffer or a crop set, then a non-empty source crop must be 117 * specified. 118 * @param frameScale The desired scale of the returned buffer; the raw screen will be scaled 119 * up/down. 120 * @param format The desired pixel format of the returned buffer. 121 * @return Returns a HardwareBuffer that contains the layer capture. 122 * @hide 123 */ captureLayers(@onNull SurfaceControl layer, @Nullable Rect sourceCrop, float frameScale, int format)124 public static ScreenshotHardwareBuffer captureLayers(@NonNull SurfaceControl layer, 125 @Nullable Rect sourceCrop, float frameScale, int format) { 126 LayerCaptureArgs captureArgs = new LayerCaptureArgs.Builder(layer) 127 .setSourceCrop(sourceCrop) 128 .setFrameScale(frameScale) 129 .setPixelFormat(format) 130 .build(); 131 132 return captureLayers(captureArgs); 133 } 134 135 /** 136 * @hide 137 */ captureLayers(LayerCaptureArgs captureArgs)138 public static ScreenshotHardwareBuffer captureLayers(LayerCaptureArgs captureArgs) { 139 SynchronousScreenCaptureListener syncScreenCapture = createSyncCaptureListener(); 140 int status = nativeCaptureLayers(captureArgs, syncScreenCapture.mNativeObject, 141 Flags.syncScreenCapture()); 142 if (status != 0) { 143 return null; 144 } 145 146 try { 147 return syncScreenCapture.getBuffer(); 148 } catch (Exception e) { 149 return null; 150 } 151 } 152 153 /** 154 * Like {@link #captureLayers(SurfaceControl, Rect, float, int)} but with an array of layer 155 * handles to exclude. 156 * 157 * @hide 158 */ captureLayersExcluding(SurfaceControl layer, Rect sourceCrop, float frameScale, int format, SurfaceControl[] exclude)159 public static ScreenshotHardwareBuffer captureLayersExcluding(SurfaceControl layer, 160 Rect sourceCrop, float frameScale, int format, SurfaceControl[] exclude) { 161 LayerCaptureArgs captureArgs = new LayerCaptureArgs.Builder(layer) 162 .setSourceCrop(sourceCrop) 163 .setFrameScale(frameScale) 164 .setPixelFormat(format) 165 .setExcludeLayers(exclude) 166 .build(); 167 168 return captureLayers(captureArgs); 169 } 170 171 /** 172 * @param captureArgs Arguments about how to take the screenshot 173 * @param captureListener A listener to receive the screenshot callback 174 * @hide 175 */ captureLayers(@onNull LayerCaptureArgs captureArgs, @NonNull ScreenCaptureListener captureListener)176 public static int captureLayers(@NonNull LayerCaptureArgs captureArgs, 177 @NonNull ScreenCaptureListener captureListener) { 178 return nativeCaptureLayers(captureArgs, captureListener.mNativeObject, false /* sync */); 179 } 180 181 /** 182 * A wrapper around HardwareBuffer that contains extra information about how to 183 * interpret the screenshot HardwareBuffer. 184 * 185 * @hide 186 */ 187 public static class ScreenshotHardwareBuffer { 188 private static final float EPSILON = 1.0f / 64.0f; 189 190 private final HardwareBuffer mHardwareBuffer; 191 private final ColorSpace mColorSpace; 192 private final boolean mContainsSecureLayers; 193 private final boolean mContainsHdrLayers; 194 private final HardwareBuffer mGainmap; 195 private final float mHdrSdrRatio; 196 ScreenshotHardwareBuffer(HardwareBuffer hardwareBuffer, ColorSpace colorSpace, boolean containsSecureLayers, boolean containsHdrLayers)197 public ScreenshotHardwareBuffer(HardwareBuffer hardwareBuffer, ColorSpace colorSpace, 198 boolean containsSecureLayers, boolean containsHdrLayers) { 199 this(hardwareBuffer, colorSpace, containsSecureLayers, containsHdrLayers, null, 1.0f); 200 } 201 ScreenshotHardwareBuffer(HardwareBuffer hardwareBuffer, ColorSpace colorSpace, boolean containsSecureLayers, boolean containsHdrLayers, HardwareBuffer gainmap, float hdrSdrRatio)202 public ScreenshotHardwareBuffer(HardwareBuffer hardwareBuffer, ColorSpace colorSpace, 203 boolean containsSecureLayers, boolean containsHdrLayers, HardwareBuffer gainmap, 204 float hdrSdrRatio) { 205 mHardwareBuffer = hardwareBuffer; 206 mColorSpace = colorSpace; 207 mContainsSecureLayers = containsSecureLayers; 208 mContainsHdrLayers = containsHdrLayers; 209 mGainmap = gainmap; 210 mHdrSdrRatio = hdrSdrRatio; 211 } 212 213 /** 214 * Create ScreenshotHardwareBuffer from an existing HardwareBuffer object. 215 * 216 * @param hardwareBuffer The existing HardwareBuffer object 217 * @param dataspace Dataspace describing the content. 218 * {@see android.hardware.DataSpace} 219 * @param containsSecureLayers Indicates whether this graphic buffer contains captured 220 * contents of secure layers, in which case the screenshot 221 * should not be persisted. 222 * @param containsHdrLayers Indicates whether this graphic buffer contains HDR content. 223 */ createFromNative(HardwareBuffer hardwareBuffer, int dataspace, boolean containsSecureLayers, boolean containsHdrLayers, HardwareBuffer gainmap, float hdrSdrRatio)224 private static ScreenshotHardwareBuffer createFromNative(HardwareBuffer hardwareBuffer, 225 int dataspace, boolean containsSecureLayers, boolean containsHdrLayers, 226 HardwareBuffer gainmap, float hdrSdrRatio) { 227 ColorSpace colorSpace = ColorSpace.getFromDataSpace(dataspace); 228 return new ScreenshotHardwareBuffer(hardwareBuffer, 229 colorSpace != null ? colorSpace : ColorSpace.get(ColorSpace.Named.SRGB), 230 containsSecureLayers, containsHdrLayers, gainmap, hdrSdrRatio); 231 } 232 getColorSpace()233 public ColorSpace getColorSpace() { 234 return mColorSpace; 235 } 236 getHardwareBuffer()237 public HardwareBuffer getHardwareBuffer() { 238 return mHardwareBuffer; 239 } 240 241 /** 242 * Whether this screenshot contains secure layers 243 */ containsSecureLayers()244 public boolean containsSecureLayers() { 245 return mContainsSecureLayers; 246 } 247 248 /** 249 * Returns whether the screenshot contains at least one HDR layer. 250 * This information may be useful for informing the display whether this screenshot 251 * is allowed to be dimmed to SDR white. 252 */ containsHdrLayers()253 public boolean containsHdrLayers() { 254 return mContainsHdrLayers; 255 } 256 257 /** 258 * Copy content of ScreenshotHardwareBuffer into a hardware bitmap and return it. 259 * Note: If you want to modify the Bitmap in software, you will need to copy the Bitmap 260 * into 261 * a software Bitmap using {@link Bitmap#copy(Bitmap.Config, boolean)} 262 * <p> 263 * CAVEAT: This can be extremely slow; avoid use unless absolutely necessary; prefer to 264 * directly 265 * use the {@link HardwareBuffer} directly. 266 * 267 * @return Bitmap generated from the {@link HardwareBuffer} 268 */ asBitmap()269 public Bitmap asBitmap() { 270 if (mHardwareBuffer == null) { 271 Log.w(TAG, "Failed to take screenshot. Null screenshot object"); 272 return null; 273 } 274 275 Bitmap bitmap = Bitmap.wrapHardwareBuffer(mHardwareBuffer, mColorSpace); 276 if (mGainmap != null) { 277 Bitmap gainmapBitmap = Bitmap.wrapHardwareBuffer(mGainmap, null); 278 Gainmap gainmap = new Gainmap(gainmapBitmap); 279 gainmap.setRatioMin(1.0f, 1.0f, 1.0f); 280 gainmap.setRatioMax(mHdrSdrRatio, mHdrSdrRatio, mHdrSdrRatio); 281 gainmap.setGamma(1.0f, 1.0f, 1.0f); 282 gainmap.setEpsilonSdr(EPSILON, EPSILON, EPSILON); 283 gainmap.setEpsilonHdr(EPSILON, EPSILON, EPSILON); 284 gainmap.setMinDisplayRatioForHdrTransition(1.0f); 285 gainmap.setDisplayRatioForFullHdr(mHdrSdrRatio); 286 bitmap.setGainmap(gainmap); 287 } 288 289 return bitmap; 290 } 291 } 292 293 /** 294 * A common arguments class used for various screenshot requests. This contains arguments that 295 * are shared between {@link DisplayCaptureArgs} and {@link LayerCaptureArgs} 296 * 297 * @hide 298 */ 299 public static class CaptureArgs implements Parcelable { 300 public final int mPixelFormat; 301 public final Rect mSourceCrop = new Rect(); 302 public final float mFrameScaleX; 303 public final float mFrameScaleY; 304 public final boolean mCaptureSecureLayers; 305 public final boolean mAllowProtected; 306 public final long mUid; 307 public final boolean mGrayscale; 308 final SurfaceControl[] mExcludeLayers; 309 public final boolean mHintForSeamlessTransition; 310 CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder)311 private CaptureArgs(CaptureArgs.Builder<? extends CaptureArgs.Builder<?>> builder) { 312 mPixelFormat = builder.mPixelFormat; 313 mSourceCrop.set(builder.mSourceCrop); 314 mFrameScaleX = builder.mFrameScaleX; 315 mFrameScaleY = builder.mFrameScaleY; 316 mCaptureSecureLayers = builder.mCaptureSecureLayers; 317 mAllowProtected = builder.mAllowProtected; 318 mUid = builder.mUid; 319 mGrayscale = builder.mGrayscale; 320 mExcludeLayers = builder.mExcludeLayers; 321 mHintForSeamlessTransition = builder.mHintForSeamlessTransition; 322 } 323 CaptureArgs(Parcel in)324 private CaptureArgs(Parcel in) { 325 mPixelFormat = in.readInt(); 326 mSourceCrop.readFromParcel(in); 327 mFrameScaleX = in.readFloat(); 328 mFrameScaleY = in.readFloat(); 329 mCaptureSecureLayers = in.readBoolean(); 330 mAllowProtected = in.readBoolean(); 331 mUid = in.readLong(); 332 mGrayscale = in.readBoolean(); 333 334 int excludeLayersLength = in.readInt(); 335 if (excludeLayersLength > 0) { 336 mExcludeLayers = new SurfaceControl[excludeLayersLength]; 337 for (int index = 0; index < excludeLayersLength; index++) { 338 mExcludeLayers[index] = SurfaceControl.CREATOR.createFromParcel(in); 339 } 340 } else { 341 mExcludeLayers = null; 342 } 343 mHintForSeamlessTransition = in.readBoolean(); 344 } 345 346 /** Release any layers if set using {@link Builder#setExcludeLayers(SurfaceControl[])}. */ release()347 public void release() { 348 if (mExcludeLayers == null || mExcludeLayers.length == 0) { 349 return; 350 } 351 352 for (SurfaceControl surfaceControl : mExcludeLayers) { 353 if (surfaceControl != null) { 354 surfaceControl.release(); 355 } 356 } 357 } 358 359 /** 360 * Returns an array of {@link SurfaceControl#mNativeObject} corresponding to 361 * {@link #mExcludeLayers}. Used only in native code. 362 */ getNativeExcludeLayers()363 private long[] getNativeExcludeLayers() { 364 if (mExcludeLayers == null || mExcludeLayers.length == 0) { 365 return new long[0]; 366 } 367 368 long[] nativeExcludeLayers = new long[mExcludeLayers.length]; 369 for (int index = 0; index < mExcludeLayers.length; index++) { 370 nativeExcludeLayers[index] = mExcludeLayers[index].mNativeObject; 371 } 372 373 return nativeExcludeLayers; 374 } 375 376 /** 377 * The Builder class used to construct {@link CaptureArgs} 378 * 379 * @param <T> A builder that extends {@link CaptureArgs.Builder} 380 */ 381 public static class Builder<T extends CaptureArgs.Builder<T>> { 382 private int mPixelFormat = PixelFormat.RGBA_8888; 383 private final Rect mSourceCrop = new Rect(); 384 private float mFrameScaleX = 1; 385 private float mFrameScaleY = 1; 386 private boolean mCaptureSecureLayers; 387 private boolean mAllowProtected; 388 private long mUid = -1; 389 private boolean mGrayscale; 390 private SurfaceControl[] mExcludeLayers; 391 private boolean mHintForSeamlessTransition; 392 393 /** 394 * Construct a new {@link CaptureArgs} with the set parameters. The builder remains 395 * valid. 396 */ build()397 public CaptureArgs build() { 398 return new CaptureArgs(this); 399 } 400 401 /** 402 * The desired pixel format of the returned buffer. 403 */ setPixelFormat(int pixelFormat)404 public T setPixelFormat(int pixelFormat) { 405 mPixelFormat = pixelFormat; 406 return getThis(); 407 } 408 409 /** 410 * The portion of the screen to capture into the buffer. Caller may pass in 411 * 'new Rect()' or null if no cropping is desired. 412 */ setSourceCrop(@ullable Rect sourceCrop)413 public T setSourceCrop(@Nullable Rect sourceCrop) { 414 if (sourceCrop == null) { 415 mSourceCrop.setEmpty(); 416 } else { 417 mSourceCrop.set(sourceCrop); 418 } 419 return getThis(); 420 } 421 422 /** 423 * The desired scale of the returned buffer. The raw screen will be scaled up/down. 424 */ setFrameScale(float frameScale)425 public T setFrameScale(float frameScale) { 426 mFrameScaleX = frameScale; 427 mFrameScaleY = frameScale; 428 return getThis(); 429 } 430 431 /** 432 * The desired scale of the returned buffer, allowing separate values for x and y scale. 433 * The raw screen will be scaled up/down. 434 */ setFrameScale(float frameScaleX, float frameScaleY)435 public T setFrameScale(float frameScaleX, float frameScaleY) { 436 mFrameScaleX = frameScaleX; 437 mFrameScaleY = frameScaleY; 438 return getThis(); 439 } 440 441 /** 442 * Whether to allow the screenshot of secure layers. Warning: This should only be done 443 * if the content will be placed in a secure SurfaceControl. 444 * 445 * @see ScreenshotHardwareBuffer#containsSecureLayers() 446 */ setCaptureSecureLayers(boolean captureSecureLayers)447 public T setCaptureSecureLayers(boolean captureSecureLayers) { 448 mCaptureSecureLayers = captureSecureLayers; 449 return getThis(); 450 } 451 452 /** 453 * Whether to allow the screenshot of protected (DRM) content. Warning: The screenshot 454 * cannot be read in unprotected space. 455 * 456 * @see HardwareBuffer#USAGE_PROTECTED_CONTENT 457 */ setAllowProtected(boolean allowProtected)458 public T setAllowProtected(boolean allowProtected) { 459 mAllowProtected = allowProtected; 460 return getThis(); 461 } 462 463 /** 464 * Set the uid of the content that should be screenshot. The code will skip any surfaces 465 * that don't belong to the specified uid. 466 */ setUid(long uid)467 public T setUid(long uid) { 468 mUid = uid; 469 return getThis(); 470 } 471 472 /** 473 * Set whether the screenshot should use grayscale or not. 474 */ setGrayscale(boolean grayscale)475 public T setGrayscale(boolean grayscale) { 476 mGrayscale = grayscale; 477 return getThis(); 478 } 479 480 /** 481 * An array of {@link SurfaceControl} layer handles to exclude. 482 */ setExcludeLayers(@ullable SurfaceControl[] excludeLayers)483 public T setExcludeLayers(@Nullable SurfaceControl[] excludeLayers) { 484 mExcludeLayers = excludeLayers; 485 return getThis(); 486 } 487 488 /** 489 * Set whether the screenshot will be used in a system animation. 490 * This hint is used for picking the "best" colorspace for the screenshot, in particular 491 * for mixing HDR and SDR content. 492 * E.g., hintForSeamlessTransition is false, then a colorspace suitable for file 493 * encoding, such as BT2100, may be chosen. Otherwise, then the display's color space 494 * would be chosen, with the possibility of having an extended brightness range. This 495 * is important for screenshots that are directly re-routed to a SurfaceControl in 496 * order to preserve accurate colors. 497 */ setHintForSeamlessTransition(boolean hintForSeamlessTransition)498 public T setHintForSeamlessTransition(boolean hintForSeamlessTransition) { 499 mHintForSeamlessTransition = hintForSeamlessTransition; 500 return getThis(); 501 } 502 503 /** 504 * Each sub class should return itself to allow the builder to chain properly 505 */ getThis()506 T getThis() { 507 return (T) this; 508 } 509 } 510 511 @Override describeContents()512 public int describeContents() { 513 return 0; 514 } 515 516 @Override writeToParcel(@onNull Parcel dest, int flags)517 public void writeToParcel(@NonNull Parcel dest, int flags) { 518 dest.writeInt(mPixelFormat); 519 mSourceCrop.writeToParcel(dest, flags); 520 dest.writeFloat(mFrameScaleX); 521 dest.writeFloat(mFrameScaleY); 522 dest.writeBoolean(mCaptureSecureLayers); 523 dest.writeBoolean(mAllowProtected); 524 dest.writeLong(mUid); 525 dest.writeBoolean(mGrayscale); 526 if (mExcludeLayers != null) { 527 dest.writeInt(mExcludeLayers.length); 528 for (SurfaceControl excludeLayer : mExcludeLayers) { 529 excludeLayer.writeToParcel(dest, flags); 530 } 531 } else { 532 dest.writeInt(0); 533 } 534 dest.writeBoolean(mHintForSeamlessTransition); 535 } 536 537 public static final Parcelable.Creator<CaptureArgs> CREATOR = 538 new Parcelable.Creator<CaptureArgs>() { 539 @Override 540 public CaptureArgs createFromParcel(Parcel in) { 541 return new CaptureArgs(in); 542 } 543 544 @Override 545 public CaptureArgs[] newArray(int size) { 546 return new CaptureArgs[size]; 547 } 548 }; 549 } 550 551 /** 552 * The arguments class used to make display capture requests. 553 * 554 * @hide 555 * @see #nativeCaptureDisplay(DisplayCaptureArgs, long) 556 */ 557 public static class DisplayCaptureArgs extends CaptureArgs { 558 private final IBinder mDisplayToken; 559 private final int mWidth; 560 private final int mHeight; 561 DisplayCaptureArgs(Builder builder)562 private DisplayCaptureArgs(Builder builder) { 563 super(builder); 564 mDisplayToken = builder.mDisplayToken; 565 mWidth = builder.mWidth; 566 mHeight = builder.mHeight; 567 } 568 569 /** 570 * The Builder class used to construct {@link DisplayCaptureArgs} 571 */ 572 public static class Builder extends CaptureArgs.Builder<Builder> { 573 private IBinder mDisplayToken; 574 private int mWidth; 575 private int mHeight; 576 577 /** 578 * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder 579 * remains valid. 580 */ build()581 public DisplayCaptureArgs build() { 582 if (mDisplayToken == null) { 583 throw new IllegalStateException( 584 "Can't take screenshot with null display token"); 585 } 586 return new DisplayCaptureArgs(this); 587 } 588 Builder(IBinder displayToken)589 public Builder(IBinder displayToken) { 590 setDisplayToken(displayToken); 591 } 592 593 /** 594 * The display to take the screenshot of. 595 */ setDisplayToken(IBinder displayToken)596 public Builder setDisplayToken(IBinder displayToken) { 597 mDisplayToken = displayToken; 598 return this; 599 } 600 601 /** 602 * Set the desired size of the returned buffer. The raw screen will be scaled down to 603 * this size 604 * 605 * @param width The desired width of the returned buffer. Caller may pass in 0 if no 606 * scaling is desired. 607 * @param height The desired height of the returned buffer. Caller may pass in 0 if no 608 * scaling is desired. 609 */ setSize(int width, int height)610 public Builder setSize(int width, int height) { 611 mWidth = width; 612 mHeight = height; 613 return this; 614 } 615 616 @Override getThis()617 Builder getThis() { 618 return this; 619 } 620 } 621 } 622 623 /** 624 * The arguments class used to make layer capture requests. 625 * 626 * @hide 627 * @see #nativeCaptureLayers(LayerCaptureArgs, long) 628 */ 629 public static class LayerCaptureArgs extends CaptureArgs { 630 private final long mNativeLayer; 631 private final boolean mChildrenOnly; 632 LayerCaptureArgs(Builder builder)633 private LayerCaptureArgs(Builder builder) { 634 super(builder); 635 mChildrenOnly = builder.mChildrenOnly; 636 mNativeLayer = builder.mLayer.mNativeObject; 637 } 638 639 /** 640 * The Builder class used to construct {@link LayerCaptureArgs} 641 */ 642 public static class Builder extends CaptureArgs.Builder<Builder> { 643 private SurfaceControl mLayer; 644 private boolean mChildrenOnly = true; 645 646 /** 647 * Construct a new {@link LayerCaptureArgs} with the set parameters. The builder 648 * remains valid. 649 */ build()650 public LayerCaptureArgs build() { 651 if (mLayer == null) { 652 throw new IllegalStateException( 653 "Can't take screenshot with null layer"); 654 } 655 return new LayerCaptureArgs(this); 656 } 657 Builder(SurfaceControl layer, CaptureArgs args)658 public Builder(SurfaceControl layer, CaptureArgs args) { 659 setLayer(layer); 660 setPixelFormat(args.mPixelFormat); 661 setSourceCrop(args.mSourceCrop); 662 setFrameScale(args.mFrameScaleX, args.mFrameScaleY); 663 setCaptureSecureLayers(args.mCaptureSecureLayers); 664 setAllowProtected(args.mAllowProtected); 665 setUid(args.mUid); 666 setGrayscale(args.mGrayscale); 667 setExcludeLayers(args.mExcludeLayers); 668 setHintForSeamlessTransition(args.mHintForSeamlessTransition); 669 } 670 Builder(SurfaceControl layer)671 public Builder(SurfaceControl layer) { 672 setLayer(layer); 673 } 674 675 /** 676 * The root layer to capture. 677 */ setLayer(SurfaceControl layer)678 public Builder setLayer(SurfaceControl layer) { 679 mLayer = layer; 680 return this; 681 } 682 683 /** 684 * Whether to include the layer itself in the screenshot or just the children and their 685 * descendants. 686 */ setChildrenOnly(boolean childrenOnly)687 public Builder setChildrenOnly(boolean childrenOnly) { 688 mChildrenOnly = childrenOnly; 689 return this; 690 } 691 692 @Override getThis()693 Builder getThis() { 694 return this; 695 } 696 } 697 } 698 699 /** 700 * The object used to receive the results when invoking screen capture requests via 701 * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)} or 702 * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)} 703 * 704 * This listener can only be used for a single call to capture content call. 705 */ 706 public static class ScreenCaptureListener implements Parcelable { 707 final long mNativeObject; 708 private static final NativeAllocationRegistry sRegistry = 709 NativeAllocationRegistry.createMalloced( 710 ScreenCaptureListener.class.getClassLoader(), getNativeListenerFinalizer()); 711 712 /** 713 * @param consumer The callback invoked when the screen capture is complete. 714 */ ScreenCaptureListener(ObjIntConsumer<ScreenshotHardwareBuffer> consumer)715 public ScreenCaptureListener(ObjIntConsumer<ScreenshotHardwareBuffer> consumer) { 716 mNativeObject = nativeCreateScreenCaptureListener(consumer); 717 sRegistry.registerNativeAllocation(this, mNativeObject); 718 } 719 ScreenCaptureListener(Parcel in)720 private ScreenCaptureListener(Parcel in) { 721 if (in.readBoolean()) { 722 mNativeObject = nativeReadListenerFromParcel(in); 723 sRegistry.registerNativeAllocation(this, mNativeObject); 724 } else { 725 mNativeObject = 0; 726 } 727 } 728 729 @Override describeContents()730 public int describeContents() { 731 return 0; 732 } 733 734 @Override writeToParcel(@onNull Parcel dest, int flags)735 public void writeToParcel(@NonNull Parcel dest, int flags) { 736 if (mNativeObject == 0) { 737 dest.writeBoolean(false); 738 } else { 739 dest.writeBoolean(true); 740 nativeWriteListenerToParcel(mNativeObject, dest); 741 } 742 } 743 744 public static final Parcelable.Creator<ScreenCaptureListener> CREATOR = 745 new Parcelable.Creator<ScreenCaptureListener>() { 746 @Override 747 public ScreenCaptureListener createFromParcel(Parcel in) { 748 return new ScreenCaptureListener(in); 749 } 750 751 @Override 752 public ScreenCaptureListener[] newArray(int size) { 753 return new ScreenCaptureListener[0]; 754 } 755 }; 756 } 757 758 /** 759 * A helper method to handle the async screencapture callbacks synchronously. This should only 760 * be used if the screencapture caller doesn't care that it blocks waiting for a screenshot. 761 * 762 * @return a {@link SynchronousScreenCaptureListener} that should be used for capture 763 * calls into SurfaceFlinger. 764 */ createSyncCaptureListener()765 public static SynchronousScreenCaptureListener createSyncCaptureListener() { 766 ScreenshotHardwareBuffer[] bufferRef = new ScreenshotHardwareBuffer[1]; 767 CountDownLatch latch = new CountDownLatch(1); 768 ObjIntConsumer<ScreenshotHardwareBuffer> consumer = (buffer, status) -> { 769 if (status != 0) { 770 bufferRef[0] = null; 771 Log.e(TAG, "Failed to generate screen capture. Error code: " + status); 772 } else { 773 bufferRef[0] = buffer; 774 } 775 latch.countDown(); 776 }; 777 778 return new SynchronousScreenCaptureListener(consumer) { 779 // In order to avoid requiring two GC cycles to clean up the consumer and the buffer 780 // it references, the underlying JNI listener holds a weak reference to the consumer. 781 // This property exists to ensure the consumer stays alive during the listener's 782 // lifetime. 783 private ObjIntConsumer<ScreenshotHardwareBuffer> mConsumer = consumer; 784 785 @Override 786 public ScreenshotHardwareBuffer getBuffer() { 787 try { 788 if (!latch.await(SCREENSHOT_WAIT_TIME_S, TimeUnit.SECONDS)) { 789 Log.e(TAG, "Timed out waiting for screenshot results"); 790 return null; 791 } 792 return bufferRef[0]; 793 } catch (Exception e) { 794 Log.e(TAG, "Failed to wait for screen capture result", e); 795 return null; 796 } 797 } 798 }; 799 } 800 801 /** 802 * Helper class to synchronously get the {@link ScreenshotHardwareBuffer} when calling 803 * {@link #captureLayers(LayerCaptureArgs, ScreenCaptureListener)} or 804 * {@link #captureDisplay(DisplayCaptureArgs, ScreenCaptureListener)} 805 */ 806 public abstract static class SynchronousScreenCaptureListener extends ScreenCaptureListener { SynchronousScreenCaptureListener(ObjIntConsumer<ScreenshotHardwareBuffer> consumer)807 SynchronousScreenCaptureListener(ObjIntConsumer<ScreenshotHardwareBuffer> consumer) { 808 super(consumer); 809 } 810 811 /** 812 * Get the {@link ScreenshotHardwareBuffer} synchronously. This can be null if the 813 * screenshot failed or if there was no callback in {@link #SCREENSHOT_WAIT_TIME_S} seconds. 814 */ 815 @Nullable getBuffer()816 public abstract ScreenshotHardwareBuffer getBuffer(); 817 } 818 } 819