/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.hardware; import android.annotation.FlaggedApi; import android.annotation.IntDef; import android.annotation.IntRange; import android.annotation.LongDef; import android.annotation.NonNull; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.GraphicBuffer; import android.os.BadParcelableException; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.view.SurfaceControl; import dalvik.annotation.optimization.CriticalNative; import dalvik.annotation.optimization.FastNative; import dalvik.system.CloseGuard; import libcore.util.NativeAllocationRegistry; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; /** * HardwareBuffer wraps a native AHardwareBuffer object, which is a low-level object * representing a memory buffer accessible by various hardware units. HardwareBuffer allows sharing * buffers across different application processes. In particular, HardwareBuffers may be mappable * to memory accessible to various hardware systems, such as the GPU, a sensor or context hub, or * other auxiliary processing units. * * For more information, see the NDK documentation for AHardwareBuffer. */ public final class HardwareBuffer implements Parcelable, AutoCloseable { /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = { "RGB", "BLOB", "YCBCR_", "D_", "DS_", "S_" }, value = { RGBA_8888, RGBA_FP16, RGBA_1010102, RGBX_8888, RGB_888, RGB_565, BLOB, YCBCR_420_888, D_16, D_24, DS_24UI8, D_FP32, DS_FP32UI8, S_UI8, YCBCR_P010, R_8, R_16, RG_1616, RGBA_10101010, }) public @interface Format { } @Format /** Format: 8 bits each red, green, blue, alpha */ public static final int RGBA_8888 = 1; /** Format: 8 bits each red, green, blue, alpha, alpha is always 0xFF */ public static final int RGBX_8888 = 2; /** Format: 8 bits each red, green, blue, no alpha */ public static final int RGB_888 = 3; /** Format: 5 bits each red and blue, 6 bits green, no alpha */ public static final int RGB_565 = 4; /** Format: 16 bits each red, green, blue, alpha */ public static final int RGBA_FP16 = 0x16; /** Format: 10 bits each red, green, blue, 2 bits alpha */ public static final int RGBA_1010102 = 0x2b; /** Format: opaque format used for raw data transfer; must have a height of 1 */ public static final int BLOB = 0x21; /** Format: Planar YCbCr 420; must have an even width and height */ public static final int YCBCR_420_888 = 0x23; /** Format: 16 bits depth */ public static final int D_16 = 0x30; /** Format: 24 bits depth */ public static final int D_24 = 0x31; /** Format: 24 bits depth, 8 bits stencil */ public static final int DS_24UI8 = 0x32; /** Format: 32 bits depth */ public static final int D_FP32 = 0x33; /** Format: 32 bits depth, 8 bits stencil */ public static final int DS_FP32UI8 = 0x34; /** Format: 8 bits stencil */ public static final int S_UI8 = 0x35; /** *

Android YUV P010 format.

* * P010 is a 4:2:0 YCbCr semiplanar format comprised of a WxH Y plane * followed by a Wx(H/2) CbCr plane. Each sample is represented by a 16-bit * little-endian value, with the lower 6 bits set to zero. */ public static final int YCBCR_P010 = 0x36; /** Format: 8 bits red */ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V) public static final int R_8 = 0x38; /** * Format: 16 bits red. When sampled on the GPU this is represented as an * unsigned integer instead of implicit unsigned normalize. * For more information see https://www.khronos.org/opengl/wiki/Normalized_Integer */ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V) public static final int R_16 = 0x39; /** * Format: 16 bits each red, green. When sampled on the GPU this is represented * as an unsigned integer instead of implicit unsigned normalize. * For more information see https://www.khronos.org/opengl/wiki/Normalized_Integer */ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V) public static final int RG_1616 = 0x3a; /** Format: 10 bits each red, green, blue, alpha */ @FlaggedApi(com.android.graphics.hwui.flags.Flags.FLAG_REQUESTED_FORMATS_V) public static final int RGBA_10101010 = 0x3b; // Note: do not rename, this field is used by native code @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private long mNativeObject; // Invoked on destruction private Runnable mCleaner; private final CloseGuard mCloseGuard = CloseGuard.get(); /** @hide */ @Retention(RetentionPolicy.SOURCE) @LongDef(flag = true, value = {USAGE_CPU_READ_RARELY, USAGE_CPU_READ_OFTEN, USAGE_CPU_WRITE_RARELY, USAGE_CPU_WRITE_OFTEN, USAGE_GPU_SAMPLED_IMAGE, USAGE_GPU_COLOR_OUTPUT, USAGE_COMPOSER_OVERLAY, USAGE_PROTECTED_CONTENT, USAGE_VIDEO_ENCODE, USAGE_GPU_DATA_BUFFER, USAGE_SENSOR_DIRECT_DATA, USAGE_GPU_CUBE_MAP, USAGE_GPU_MIPMAP_COMPLETE, USAGE_FRONT_BUFFER}) public @interface Usage {}; @Usage /** Usage: The buffer will sometimes be read by the CPU */ public static final long USAGE_CPU_READ_RARELY = 2; /** Usage: The buffer will often be read by the CPU */ public static final long USAGE_CPU_READ_OFTEN = 3; /** Usage: The buffer will sometimes be written to by the CPU */ public static final long USAGE_CPU_WRITE_RARELY = 2 << 4; /** Usage: The buffer will often be written to by the CPU */ public static final long USAGE_CPU_WRITE_OFTEN = 3 << 4; /** Usage: The buffer will be read from by the GPU */ public static final long USAGE_GPU_SAMPLED_IMAGE = 1 << 8; /** Usage: The buffer will be written to by the GPU */ public static final long USAGE_GPU_COLOR_OUTPUT = 1 << 9; /** * The buffer will be used as a hardware composer overlay layer. That is, it will be displayed * using the system compositor via {@link SurfaceControl} * * This flag is currently only needed when using * {@link android.view.SurfaceControl.Transaction#setBuffer(SurfaceControl, HardwareBuffer)} * to set a buffer. In all other cases, the framework adds this flag * internally to buffers that could be presented in a composer overlay. */ public static final long USAGE_COMPOSER_OVERLAY = 1 << 11; /** Usage: The buffer must not be used outside of a protected hardware path */ public static final long USAGE_PROTECTED_CONTENT = 1 << 14; /** Usage: The buffer will be read by a hardware video encoder */ public static final long USAGE_VIDEO_ENCODE = 1 << 16; /** Usage: The buffer will be used for sensor direct data */ public static final long USAGE_SENSOR_DIRECT_DATA = 1 << 23; /** Usage: The buffer will be used as a shader storage or uniform buffer object */ public static final long USAGE_GPU_DATA_BUFFER = 1 << 24; /** Usage: The buffer will be used as a cube map texture */ public static final long USAGE_GPU_CUBE_MAP = 1 << 25; /** Usage: The buffer contains a complete mipmap hierarchy */ public static final long USAGE_GPU_MIPMAP_COMPLETE = 1 << 26; /** Usage: The buffer is used for front-buffer rendering. When front-buffering rendering is * specified, different usages may adjust their behavior as a result. For example, when * used as USAGE_GPU_COLOR_OUTPUT the buffer will behave similar to a single-buffered window. * When used with USAGE_COMPOSER_OVERLAY, the system will try to prioritize the buffer * receiving an overlay plane & avoid caching it in intermediate composition buffers. */ public static final long USAGE_FRONT_BUFFER = 1L << 32; /** * Creates a new HardwareBuffer instance. * *

Calling this method will throw an IllegalStateException if * format is not a supported Format type.

* * @param width The width in pixels of the buffer * @param height The height in pixels of the buffer * @param format The @Format of each pixel * @param layers The number of layers in the buffer * @param usage The @Usage flags describing how the buffer will be used * @return A HardwareBuffer instance if successful, or throws an * IllegalArgumentException if the dimensions passed are invalid (either zero, negative, or * too large to allocate), if the format is not supported, if the requested number of layers * is less than one or not supported, or if the passed usage flags are not a supported set. */ @NonNull public static HardwareBuffer create( @IntRange(from = 1) int width, @IntRange(from = 1) int height, @Format int format, @IntRange(from = 1) int layers, @Usage long usage) { if (width <= 0) { throw new IllegalArgumentException("Invalid width " + width); } if (height <= 0) { throw new IllegalArgumentException("Invalid height " + height); } if (layers <= 0) { throw new IllegalArgumentException("Invalid layer count " + layers); } if (format == BLOB && height != 1) { throw new IllegalArgumentException("Height must be 1 when using the BLOB format"); } long nativeObject = nCreateHardwareBuffer(width, height, format, layers, usage); if (nativeObject == 0) { throw new IllegalArgumentException("Unable to create a HardwareBuffer, either the " + "dimensions passed were too large, too many image layers were requested, " + "or an invalid set of usage flags or invalid format was passed"); } return new HardwareBuffer(nativeObject); } /** * Queries whether the given buffer description is supported by the system. If this returns * true, then the allocation may succeed until resource exhaustion occurs. If this returns * false then this combination will never succeed. * * @param width The width in pixels of the buffer * @param height The height in pixels of the buffer * @param format The @Format of each pixel * @param layers The number of layers in the buffer * @param usage The @Usage flags describing how the buffer will be used * @return True if the combination is supported, false otherwise. */ public static boolean isSupported(@IntRange(from = 1) int width, @IntRange(from = 1) int height, @Format int format, @IntRange(from = 1) int layers, @Usage long usage) { if (width <= 0) { throw new IllegalArgumentException("Invalid width " + width); } if (height <= 0) { throw new IllegalArgumentException("Invalid height " + height); } if (layers <= 0) { throw new IllegalArgumentException("Invalid layer count " + layers); } if (format == BLOB && height != 1) { throw new IllegalArgumentException("Height must be 1 when using the BLOB format"); } return nIsSupported(width, height, format, layers, usage); } /** * @hide * Returns a HardwareBuffer instance from GraphicBuffer * * @param graphicBuffer A GraphicBuffer to be wrapped as HardwareBuffer * @return A HardwareBuffer instance. */ @NonNull public static HardwareBuffer createFromGraphicBuffer(@NonNull GraphicBuffer graphicBuffer) { long nativeObject = nCreateFromGraphicBuffer(graphicBuffer); return new HardwareBuffer(nativeObject); } /** * Private use only. See {@link #create(int, int, int, int, long)}. May also be * called from JNI using an already allocated native HardwareBuffer. */ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023) private HardwareBuffer(long nativeObject) { mNativeObject = nativeObject; long bufferSize = nEstimateSize(nativeObject); ClassLoader loader = HardwareBuffer.class.getClassLoader(); NativeAllocationRegistry registry = new NativeAllocationRegistry( loader, nGetNativeFinalizer(), bufferSize); mCleaner = registry.registerNativeAllocation(this, mNativeObject); mCloseGuard.open("HardwareBuffer.close"); } @Override protected void finalize() throws Throwable { try { mCloseGuard.warnIfOpen(); close(); } finally { super.finalize(); } } /** * Returns the width of this buffer in pixels. */ public int getWidth() { checkClosed("width"); return nGetWidth(mNativeObject); } /** * Returns the height of this buffer in pixels. */ public int getHeight() { checkClosed("height"); return nGetHeight(mNativeObject); } /** * Returns the @Format of this buffer. */ @Format public int getFormat() { checkClosed("format"); return nGetFormat(mNativeObject); } /** * Returns the number of layers in this buffer. */ public int getLayers() { checkClosed("layer count"); return nGetLayers(mNativeObject); } /** * Returns the usage flags of the usage hints set on this buffer. */ public long getUsage() { checkClosed("usage"); return nGetUsage(mNativeObject); } /** * Returns the system-wide unique id for this buffer * * This can be useful as a cache key for associating additional objects with * a given HardwareBuffer, such as associating an imported EGLImage with * the target HardwareBuffer when processing a stream of buffers from * ImageReader. * * This can also be useful for doing cross-process buffer caching. As sending * a HardwareBuffer over Binder is slower than sending a long, this can be * used as reliable cache key after an initial handshake that passes the * HardwareBuffers themselves to later be referred to using only the id. */ public long getId() { checkClosed("id"); return nGetId(mNativeObject); } private void checkClosed(String name) { if (isClosed()) { throw new IllegalStateException("This HardwareBuffer has been closed and its " + name + " cannot be obtained."); } } /** * Destroys this buffer immediately. Calling this method frees up any * underlying native resources. After calling this method, this buffer * must not be used in any way. * * @see #isClosed() */ @Override public void close() { if (!isClosed()) { mCloseGuard.close(); mNativeObject = 0; mCleaner.run(); mCleaner = null; } } /** * Indicates whether this buffer has been closed. A closed buffer cannot * be used in any way: the buffer cannot be written to a parcel, etc. * * @return True if this HardwareBuffer is in a closed state, * false otherwise. * * @see #close() */ public boolean isClosed() { return mNativeObject == 0; } @Override public int describeContents() { return Parcelable.CONTENTS_FILE_DESCRIPTOR; } /** * Flatten this object in to a Parcel. * *

Calling this method will throw an IllegalStateException if * {@link #close()} has been previously called.

* * @param dest The Parcel in which the object should be written. * @param flags Additional flags about how the object should be written. * May be 0 or {@link #PARCELABLE_WRITE_RETURN_VALUE}. */ @Override public void writeToParcel(Parcel dest, int flags) { if (isClosed()) { throw new IllegalStateException("This HardwareBuffer has been closed and cannot be " + "written to a parcel."); } nWriteHardwareBufferToParcel(mNativeObject, dest); } public static final @android.annotation.NonNull Parcelable.Creator CREATOR = new Parcelable.Creator() { public HardwareBuffer createFromParcel(Parcel in) { if (in == null) { throw new NullPointerException("null passed to createFromParcel"); } long nativeObject = nReadHardwareBufferFromParcel(in); if (nativeObject != 0) { return new HardwareBuffer(nativeObject); } throw new BadParcelableException("Failed to read hardware buffer"); } public HardwareBuffer[] newArray(int size) { return new HardwareBuffer[size]; } }; private static native long nCreateHardwareBuffer(int width, int height, int format, int layers, long usage); private static native long nCreateFromGraphicBuffer(GraphicBuffer graphicBuffer); private static native long nGetNativeFinalizer(); private static native void nWriteHardwareBufferToParcel(long nativeObject, Parcel dest); private static native long nReadHardwareBufferFromParcel(Parcel in); @FastNative private static native int nGetWidth(long nativeObject); @FastNative private static native int nGetHeight(long nativeObject); @FastNative private static native int nGetFormat(long nativeObject); @FastNative private static native int nGetLayers(long nativeObject); @FastNative private static native long nGetUsage(long nativeObject); private static native boolean nIsSupported(int width, int height, int format, int layers, long usage); @CriticalNative private static native long nEstimateSize(long nativeObject); @CriticalNative private static native long nGetId(long nativeObject); }