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 android.view; 18 19 import dalvik.system.CloseGuard; 20 import android.graphics.Bitmap; 21 import android.graphics.Rect; 22 import android.graphics.Region; 23 import android.view.Surface; 24 import android.os.IBinder; 25 import android.os.SystemProperties; 26 import android.util.Log; 27 import android.view.Surface.OutOfResourcesException; 28 29 /** 30 * SurfaceControl 31 * @hide 32 */ 33 public class SurfaceControl { 34 private static final String TAG = "SurfaceControl"; 35 nativeCreate(SurfaceSession session, String name, int w, int h, int format, int flags)36 private static native int nativeCreate(SurfaceSession session, String name, 37 int w, int h, int format, int flags) 38 throws OutOfResourcesException; nativeRelease(int nativeObject)39 private static native void nativeRelease(int nativeObject); nativeDestroy(int nativeObject)40 private static native void nativeDestroy(int nativeObject); 41 nativeScreenshot(IBinder displayToken, int width, int height, int minLayer, int maxLayer, boolean allLayers)42 private static native Bitmap nativeScreenshot(IBinder displayToken, 43 int width, int height, int minLayer, int maxLayer, boolean allLayers); nativeScreenshot(IBinder displayToken, Surface consumer, int width, int height, int minLayer, int maxLayer, boolean allLayers)44 private static native void nativeScreenshot(IBinder displayToken, Surface consumer, 45 int width, int height, int minLayer, int maxLayer, boolean allLayers); 46 nativeOpenTransaction()47 private static native void nativeOpenTransaction(); nativeCloseTransaction()48 private static native void nativeCloseTransaction(); nativeSetAnimationTransaction()49 private static native void nativeSetAnimationTransaction(); 50 nativeSetLayer(int nativeObject, int zorder)51 private static native void nativeSetLayer(int nativeObject, int zorder); nativeSetPosition(int nativeObject, float x, float y)52 private static native void nativeSetPosition(int nativeObject, float x, float y); nativeSetSize(int nativeObject, int w, int h)53 private static native void nativeSetSize(int nativeObject, int w, int h); nativeSetTransparentRegionHint(int nativeObject, Region region)54 private static native void nativeSetTransparentRegionHint(int nativeObject, Region region); nativeSetAlpha(int nativeObject, float alpha)55 private static native void nativeSetAlpha(int nativeObject, float alpha); nativeSetMatrix(int nativeObject, float dsdx, float dtdx, float dsdy, float dtdy)56 private static native void nativeSetMatrix(int nativeObject, float dsdx, float dtdx, float dsdy, float dtdy); nativeSetFlags(int nativeObject, int flags, int mask)57 private static native void nativeSetFlags(int nativeObject, int flags, int mask); nativeSetWindowCrop(int nativeObject, int l, int t, int r, int b)58 private static native void nativeSetWindowCrop(int nativeObject, int l, int t, int r, int b); nativeSetLayerStack(int nativeObject, int layerStack)59 private static native void nativeSetLayerStack(int nativeObject, int layerStack); 60 nativeGetBuiltInDisplay(int physicalDisplayId)61 private static native IBinder nativeGetBuiltInDisplay(int physicalDisplayId); nativeCreateDisplay(String name, boolean secure)62 private static native IBinder nativeCreateDisplay(String name, boolean secure); nativeDestroyDisplay(IBinder displayToken)63 private static native void nativeDestroyDisplay(IBinder displayToken); nativeSetDisplaySurface( IBinder displayToken, int nativeSurfaceObject)64 private static native void nativeSetDisplaySurface( 65 IBinder displayToken, int nativeSurfaceObject); nativeSetDisplayLayerStack( IBinder displayToken, int layerStack)66 private static native void nativeSetDisplayLayerStack( 67 IBinder displayToken, int layerStack); nativeSetDisplayProjection( IBinder displayToken, int orientation, int l, int t, int r, int b, int L, int T, int R, int B)68 private static native void nativeSetDisplayProjection( 69 IBinder displayToken, int orientation, 70 int l, int t, int r, int b, 71 int L, int T, int R, int B); nativeGetDisplayInfo( IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo)72 private static native boolean nativeGetDisplayInfo( 73 IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo); nativeBlankDisplay(IBinder displayToken)74 private static native void nativeBlankDisplay(IBinder displayToken); nativeUnblankDisplay(IBinder displayToken)75 private static native void nativeUnblankDisplay(IBinder displayToken); 76 77 78 private final CloseGuard mCloseGuard = CloseGuard.get(); 79 private final String mName; 80 int mNativeObject; // package visibility only for Surface.java access 81 82 private static final boolean HEADLESS = "1".equals( 83 SystemProperties.get("ro.config.headless", "0")); 84 85 /* flags used in constructor (keep in sync with ISurfaceComposerClient.h) */ 86 87 /** 88 * Surface creation flag: Surface is created hidden 89 */ 90 public static final int HIDDEN = 0x00000004; 91 92 /** 93 * Surface creation flag: The surface contains secure content, special 94 * measures will be taken to disallow the surface's content to be copied 95 * from another process. In particular, screenshots and VNC servers will 96 * be disabled, but other measures can take place, for instance the 97 * surface might not be hardware accelerated. 98 * 99 */ 100 public static final int SECURE = 0x00000080; 101 102 /** 103 * Surface creation flag: Creates a surface where color components are interpreted 104 * as "non pre-multiplied" by their alpha channel. Of course this flag is 105 * meaningless for surfaces without an alpha channel. By default 106 * surfaces are pre-multiplied, which means that each color component is 107 * already multiplied by its alpha value. In this case the blending 108 * equation used is: 109 * 110 * DEST = SRC + DEST * (1-SRC_ALPHA) 111 * 112 * By contrast, non pre-multiplied surfaces use the following equation: 113 * 114 * DEST = SRC * SRC_ALPHA * DEST * (1-SRC_ALPHA) 115 * 116 * pre-multiplied surfaces must always be used if transparent pixels are 117 * composited on top of each-other into the surface. A pre-multiplied 118 * surface can never lower the value of the alpha component of a given 119 * pixel. 120 * 121 * In some rare situations, a non pre-multiplied surface is preferable. 122 * 123 */ 124 public static final int NON_PREMULTIPLIED = 0x00000100; 125 126 /** 127 * Surface creation flag: Indicates that the surface must be considered opaque, 128 * even if its pixel format is set to translucent. This can be useful if an 129 * application needs full RGBA 8888 support for instance but will 130 * still draw every pixel opaque. 131 * 132 */ 133 public static final int OPAQUE = 0x00000400; 134 135 /** 136 * Surface creation flag: Application requires a hardware-protected path to an 137 * external display sink. If a hardware-protected path is not available, 138 * then this surface will not be displayed on the external sink. 139 * 140 */ 141 public static final int PROTECTED_APP = 0x00000800; 142 143 // 0x1000 is reserved for an independent DRM protected flag in framework 144 145 /** 146 * Surface creation flag: Creates a normal surface. 147 * This is the default. 148 * 149 */ 150 public static final int FX_SURFACE_NORMAL = 0x00000000; 151 152 /** 153 * Surface creation flag: Creates a Dim surface. 154 * Everything behind this surface is dimmed by the amount specified 155 * in {@link #setAlpha}. It is an error to lock a Dim surface, since it 156 * doesn't have a backing store. 157 * 158 */ 159 public static final int FX_SURFACE_DIM = 0x00020000; 160 161 /** 162 * Mask used for FX values above. 163 * 164 */ 165 public static final int FX_SURFACE_MASK = 0x000F0000; 166 167 /* flags used with setFlags() (keep in sync with ISurfaceComposer.h) */ 168 169 /** 170 * Surface flag: Hide the surface. 171 * Equivalent to calling hide(). 172 */ 173 public static final int SURFACE_HIDDEN = 0x01; 174 175 176 /* built-in physical display ids (keep in sync with ISurfaceComposer.h) 177 * these are different from the logical display ids used elsewhere in the framework */ 178 179 /** 180 * Built-in physical display id: Main display. 181 * Use only with {@link SurfaceControl#getBuiltInDisplay()}. 182 */ 183 public static final int BUILT_IN_DISPLAY_ID_MAIN = 0; 184 185 /** 186 * Built-in physical display id: Attached HDMI display. 187 * Use only with {@link SurfaceControl#getBuiltInDisplay()}. 188 */ 189 public static final int BUILT_IN_DISPLAY_ID_HDMI = 1; 190 191 192 193 /** 194 * Create a surface with a name. 195 * 196 * The surface creation flags specify what kind of surface to create and 197 * certain options such as whether the surface can be assumed to be opaque 198 * and whether it should be initially hidden. Surfaces should always be 199 * created with the {@link #HIDDEN} flag set to ensure that they are not 200 * made visible prematurely before all of the surface's properties have been 201 * configured. 202 * 203 * Good practice is to first create the surface with the {@link #HIDDEN} flag 204 * specified, open a transaction, set the surface layer, layer stack, alpha, 205 * and position, call {@link #show} if appropriate, and close the transaction. 206 * 207 * @param session The surface session, must not be null. 208 * @param name The surface name, must not be null. 209 * @param w The surface initial width. 210 * @param h The surface initial height. 211 * @param flags The surface creation flags. Should always include {@link #HIDDEN} 212 * in the creation flags. 213 * 214 * @throws throws OutOfResourcesException If the SurfaceControl cannot be created. 215 */ SurfaceControl(SurfaceSession session, String name, int w, int h, int format, int flags)216 public SurfaceControl(SurfaceSession session, 217 String name, int w, int h, int format, int flags) 218 throws OutOfResourcesException { 219 if (session == null) { 220 throw new IllegalArgumentException("session must not be null"); 221 } 222 if (name == null) { 223 throw new IllegalArgumentException("name must not be null"); 224 } 225 226 if ((flags & SurfaceControl.HIDDEN) == 0) { 227 Log.w(TAG, "Surfaces should always be created with the HIDDEN flag set " 228 + "to ensure that they are not made visible prematurely before " 229 + "all of the surface's properties have been configured. " 230 + "Set the other properties and make the surface visible within " 231 + "a transaction. New surface name: " + name, 232 new Throwable()); 233 } 234 235 checkHeadless(); 236 237 mName = name; 238 mNativeObject = nativeCreate(session, name, w, h, format, flags); 239 if (mNativeObject == 0) { 240 throw new OutOfResourcesException( 241 "Couldn't allocate SurfaceControl native object"); 242 } 243 244 mCloseGuard.open("release"); 245 } 246 247 @Override finalize()248 protected void finalize() throws Throwable { 249 try { 250 if (mCloseGuard != null) { 251 mCloseGuard.warnIfOpen(); 252 } 253 if (mNativeObject != 0) { 254 nativeRelease(mNativeObject); 255 } 256 } finally { 257 super.finalize(); 258 } 259 } 260 261 @Override toString()262 public String toString() { 263 return "Surface(name=" + mName + ")"; 264 } 265 266 /** 267 * Release the local reference to the server-side surface. 268 * Always call release() when you're done with a Surface. 269 * This will make the surface invalid. 270 */ release()271 public void release() { 272 if (mNativeObject != 0) { 273 nativeRelease(mNativeObject); 274 mNativeObject = 0; 275 } 276 mCloseGuard.close(); 277 } 278 279 /** 280 * Free all server-side state associated with this surface and 281 * release this object's reference. This method can only be 282 * called from the process that created the service. 283 */ destroy()284 public void destroy() { 285 if (mNativeObject != 0) { 286 nativeDestroy(mNativeObject); 287 mNativeObject = 0; 288 } 289 mCloseGuard.close(); 290 } 291 checkNotReleased()292 private void checkNotReleased() { 293 if (mNativeObject == 0) throw new NullPointerException( 294 "mNativeObject is null. Have you called release() already?"); 295 } 296 297 /* 298 * set surface parameters. 299 * needs to be inside open/closeTransaction block 300 */ 301 302 /** start a transaction */ openTransaction()303 public static void openTransaction() { 304 nativeOpenTransaction(); 305 } 306 307 /** end a transaction */ closeTransaction()308 public static void closeTransaction() { 309 nativeCloseTransaction(); 310 } 311 312 /** flag the transaction as an animation */ setAnimationTransaction()313 public static void setAnimationTransaction() { 314 nativeSetAnimationTransaction(); 315 } 316 setLayer(int zorder)317 public void setLayer(int zorder) { 318 checkNotReleased(); 319 nativeSetLayer(mNativeObject, zorder); 320 } 321 setPosition(float x, float y)322 public void setPosition(float x, float y) { 323 checkNotReleased(); 324 nativeSetPosition(mNativeObject, x, y); 325 } 326 setSize(int w, int h)327 public void setSize(int w, int h) { 328 checkNotReleased(); 329 nativeSetSize(mNativeObject, w, h); 330 } 331 hide()332 public void hide() { 333 checkNotReleased(); 334 nativeSetFlags(mNativeObject, SURFACE_HIDDEN, SURFACE_HIDDEN); 335 } 336 show()337 public void show() { 338 checkNotReleased(); 339 nativeSetFlags(mNativeObject, 0, SURFACE_HIDDEN); 340 } 341 setTransparentRegionHint(Region region)342 public void setTransparentRegionHint(Region region) { 343 checkNotReleased(); 344 nativeSetTransparentRegionHint(mNativeObject, region); 345 } 346 setAlpha(float alpha)347 public void setAlpha(float alpha) { 348 checkNotReleased(); 349 nativeSetAlpha(mNativeObject, alpha); 350 } 351 setMatrix(float dsdx, float dtdx, float dsdy, float dtdy)352 public void setMatrix(float dsdx, float dtdx, float dsdy, float dtdy) { 353 checkNotReleased(); 354 nativeSetMatrix(mNativeObject, dsdx, dtdx, dsdy, dtdy); 355 } 356 setFlags(int flags, int mask)357 public void setFlags(int flags, int mask) { 358 checkNotReleased(); 359 nativeSetFlags(mNativeObject, flags, mask); 360 } 361 setWindowCrop(Rect crop)362 public void setWindowCrop(Rect crop) { 363 checkNotReleased(); 364 if (crop != null) { 365 nativeSetWindowCrop(mNativeObject, 366 crop.left, crop.top, crop.right, crop.bottom); 367 } else { 368 nativeSetWindowCrop(mNativeObject, 0, 0, 0, 0); 369 } 370 } 371 setLayerStack(int layerStack)372 public void setLayerStack(int layerStack) { 373 checkNotReleased(); 374 nativeSetLayerStack(mNativeObject, layerStack); 375 } 376 377 /* 378 * set display parameters. 379 * needs to be inside open/closeTransaction block 380 */ 381 382 /** 383 * Describes the properties of a physical display known to surface flinger. 384 */ 385 public static final class PhysicalDisplayInfo { 386 public int width; 387 public int height; 388 public float refreshRate; 389 public float density; 390 public float xDpi; 391 public float yDpi; 392 public boolean secure; 393 PhysicalDisplayInfo()394 public PhysicalDisplayInfo() { 395 } 396 PhysicalDisplayInfo(PhysicalDisplayInfo other)397 public PhysicalDisplayInfo(PhysicalDisplayInfo other) { 398 copyFrom(other); 399 } 400 401 @Override equals(Object o)402 public boolean equals(Object o) { 403 return o instanceof PhysicalDisplayInfo && equals((PhysicalDisplayInfo)o); 404 } 405 equals(PhysicalDisplayInfo other)406 public boolean equals(PhysicalDisplayInfo other) { 407 return other != null 408 && width == other.width 409 && height == other.height 410 && refreshRate == other.refreshRate 411 && density == other.density 412 && xDpi == other.xDpi 413 && yDpi == other.yDpi 414 && secure == other.secure; 415 } 416 417 @Override hashCode()418 public int hashCode() { 419 return 0; // don't care 420 } 421 copyFrom(PhysicalDisplayInfo other)422 public void copyFrom(PhysicalDisplayInfo other) { 423 width = other.width; 424 height = other.height; 425 refreshRate = other.refreshRate; 426 density = other.density; 427 xDpi = other.xDpi; 428 yDpi = other.yDpi; 429 secure = other.secure; 430 } 431 432 // For debugging purposes 433 @Override toString()434 public String toString() { 435 return "PhysicalDisplayInfo{" + width + " x " + height + ", " + refreshRate + " fps, " 436 + "density " + density + ", " + xDpi + " x " + yDpi + " dpi, secure " + secure 437 + "}"; 438 } 439 } 440 unblankDisplay(IBinder displayToken)441 public static void unblankDisplay(IBinder displayToken) { 442 if (displayToken == null) { 443 throw new IllegalArgumentException("displayToken must not be null"); 444 } 445 nativeUnblankDisplay(displayToken); 446 } 447 blankDisplay(IBinder displayToken)448 public static void blankDisplay(IBinder displayToken) { 449 if (displayToken == null) { 450 throw new IllegalArgumentException("displayToken must not be null"); 451 } 452 nativeBlankDisplay(displayToken); 453 } 454 getDisplayInfo(IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo)455 public static boolean getDisplayInfo(IBinder displayToken, SurfaceControl.PhysicalDisplayInfo outInfo) { 456 if (displayToken == null) { 457 throw new IllegalArgumentException("displayToken must not be null"); 458 } 459 if (outInfo == null) { 460 throw new IllegalArgumentException("outInfo must not be null"); 461 } 462 return nativeGetDisplayInfo(displayToken, outInfo); 463 } 464 setDisplayProjection(IBinder displayToken, int orientation, Rect layerStackRect, Rect displayRect)465 public static void setDisplayProjection(IBinder displayToken, 466 int orientation, Rect layerStackRect, Rect displayRect) { 467 if (displayToken == null) { 468 throw new IllegalArgumentException("displayToken must not be null"); 469 } 470 if (layerStackRect == null) { 471 throw new IllegalArgumentException("layerStackRect must not be null"); 472 } 473 if (displayRect == null) { 474 throw new IllegalArgumentException("displayRect must not be null"); 475 } 476 nativeSetDisplayProjection(displayToken, orientation, 477 layerStackRect.left, layerStackRect.top, layerStackRect.right, layerStackRect.bottom, 478 displayRect.left, displayRect.top, displayRect.right, displayRect.bottom); 479 } 480 setDisplayLayerStack(IBinder displayToken, int layerStack)481 public static void setDisplayLayerStack(IBinder displayToken, int layerStack) { 482 if (displayToken == null) { 483 throw new IllegalArgumentException("displayToken must not be null"); 484 } 485 nativeSetDisplayLayerStack(displayToken, layerStack); 486 } 487 setDisplaySurface(IBinder displayToken, Surface surface)488 public static void setDisplaySurface(IBinder displayToken, Surface surface) { 489 if (displayToken == null) { 490 throw new IllegalArgumentException("displayToken must not be null"); 491 } 492 493 if (surface != null) { 494 synchronized (surface.mLock) { 495 nativeSetDisplaySurface(displayToken, surface.mNativeObject); 496 } 497 } else { 498 nativeSetDisplaySurface(displayToken, 0); 499 } 500 } 501 createDisplay(String name, boolean secure)502 public static IBinder createDisplay(String name, boolean secure) { 503 if (name == null) { 504 throw new IllegalArgumentException("name must not be null"); 505 } 506 return nativeCreateDisplay(name, secure); 507 } 508 destroyDisplay(IBinder displayToken)509 public static void destroyDisplay(IBinder displayToken) { 510 if (displayToken == null) { 511 throw new IllegalArgumentException("displayToken must not be null"); 512 } 513 nativeDestroyDisplay(displayToken); 514 } 515 getBuiltInDisplay(int builtInDisplayId)516 public static IBinder getBuiltInDisplay(int builtInDisplayId) { 517 return nativeGetBuiltInDisplay(builtInDisplayId); 518 } 519 520 521 /** 522 * Copy the current screen contents into the provided {@link Surface} 523 * 524 * @param display The display to take the screenshot of. 525 * @param consumer The {@link Surface} to take the screenshot into. 526 * @param width The desired width of the returned bitmap; the raw 527 * screen will be scaled down to this size. 528 * @param height The desired height of the returned bitmap; the raw 529 * screen will be scaled down to this size. 530 * @param minLayer The lowest (bottom-most Z order) surface layer to 531 * include in the screenshot. 532 * @param maxLayer The highest (top-most Z order) surface layer to 533 * include in the screenshot. 534 */ screenshot(IBinder display, Surface consumer, int width, int height, int minLayer, int maxLayer)535 public static void screenshot(IBinder display, Surface consumer, 536 int width, int height, int minLayer, int maxLayer) { 537 screenshot(display, consumer, width, height, minLayer, maxLayer, false); 538 } 539 540 /** 541 * Copy the current screen contents into the provided {@link Surface} 542 * 543 * @param display The display to take the screenshot of. 544 * @param consumer The {@link Surface} to take the screenshot into. 545 * @param width The desired width of the returned bitmap; the raw 546 * screen will be scaled down to this size. 547 * @param height The desired height of the returned bitmap; the raw 548 * screen will be scaled down to this size. 549 */ screenshot(IBinder display, Surface consumer, int width, int height)550 public static void screenshot(IBinder display, Surface consumer, 551 int width, int height) { 552 screenshot(display, consumer, width, height, 0, 0, true); 553 } 554 555 /** 556 * Copy the current screen contents into the provided {@link Surface} 557 * 558 * @param display The display to take the screenshot of. 559 * @param consumer The {@link Surface} to take the screenshot into. 560 */ screenshot(IBinder display, Surface consumer)561 public static void screenshot(IBinder display, Surface consumer) { 562 screenshot(display, consumer, 0, 0, 0, 0, true); 563 } 564 565 566 /** 567 * Copy the current screen contents into a bitmap and return it. 568 * 569 * CAVEAT: Versions of screenshot that return a {@link Bitmap} can 570 * be extremely slow; avoid use unless absolutely necessary; prefer 571 * the versions that use a {@link Surface} instead, such as 572 * {@link SurfaceControl#screenshot(IBinder, Surface)}. 573 * 574 * @param width The desired width of the returned bitmap; the raw 575 * screen will be scaled down to this size. 576 * @param height The desired height of the returned bitmap; the raw 577 * screen will be scaled down to this size. 578 * @param minLayer The lowest (bottom-most Z order) surface layer to 579 * include in the screenshot. 580 * @param maxLayer The highest (top-most Z order) surface layer to 581 * include in the screenshot. 582 * @return Returns a Bitmap containing the screen contents, or null 583 * if an error occurs. Make sure to call Bitmap.recycle() as soon as 584 * possible, once its content is not needed anymore. 585 */ screenshot(int width, int height, int minLayer, int maxLayer)586 public static Bitmap screenshot(int width, int height, int minLayer, int maxLayer) { 587 // TODO: should take the display as a parameter 588 IBinder displayToken = SurfaceControl.getBuiltInDisplay( 589 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); 590 return nativeScreenshot(displayToken, width, height, minLayer, maxLayer, false); 591 } 592 593 /** 594 * Like {@link SurfaceControl#screenshot(int, int, int, int)} but includes all 595 * Surfaces in the screenshot. 596 * 597 * @param width The desired width of the returned bitmap; the raw 598 * screen will be scaled down to this size. 599 * @param height The desired height of the returned bitmap; the raw 600 * screen will be scaled down to this size. 601 * @return Returns a Bitmap containing the screen contents, or null 602 * if an error occurs. Make sure to call Bitmap.recycle() as soon as 603 * possible, once its content is not needed anymore. 604 */ screenshot(int width, int height)605 public static Bitmap screenshot(int width, int height) { 606 // TODO: should take the display as a parameter 607 IBinder displayToken = SurfaceControl.getBuiltInDisplay( 608 SurfaceControl.BUILT_IN_DISPLAY_ID_MAIN); 609 return nativeScreenshot(displayToken, width, height, 0, 0, true); 610 } 611 screenshot(IBinder display, Surface consumer, int width, int height, int minLayer, int maxLayer, boolean allLayers)612 private static void screenshot(IBinder display, Surface consumer, 613 int width, int height, int minLayer, int maxLayer, boolean allLayers) { 614 if (display == null) { 615 throw new IllegalArgumentException("displayToken must not be null"); 616 } 617 if (consumer == null) { 618 throw new IllegalArgumentException("consumer must not be null"); 619 } 620 nativeScreenshot(display, consumer, width, height, minLayer, maxLayer, allLayers); 621 } 622 checkHeadless()623 private static void checkHeadless() { 624 if (HEADLESS) { 625 throw new UnsupportedOperationException("Device is headless"); 626 } 627 } 628 } 629