| /** |
| * Copyright (C) 2022 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.NonNull; |
| import android.media.Image; |
| import android.media.ImageWriter; |
| import android.opengl.EGLDisplay; |
| import android.opengl.EGLSync; |
| import android.os.Parcel; |
| import android.os.ParcelFileDescriptor; |
| import android.os.Parcelable; |
| import android.os.SystemClock; |
| |
| import com.android.window.flags.Flags; |
| |
| import libcore.util.NativeAllocationRegistry; |
| |
| import java.io.FileDescriptor; |
| import java.io.IOException; |
| import java.time.Duration; |
| |
| /** |
| * A SyncFence represents a synchronization primitive which signals when hardware units have |
| * completed work on a particular resource. They initially start in an unsignaled state and make |
| * a one-time transition to either a signaled or error state. SyncFences are created by various |
| * device APIs in response to submitting tasks to the device. They cannot be created nor signaled |
| * by userspace. As a result, this means that a SyncFence will make always make forward progress. |
| * |
| * <p>SyncFence's generally come in one of two varieties. "Presentation fences" refer to |
| * a SyncFence when the writing to a buffer has finished. "Release fences" then refer |
| * to when the reading from a buffer has finished.</p> |
| * |
| * <p>For example, a GPU rendering to a framebuffer may generate a synchronization fence, |
| * e.g., an EGLSync or VkFence, which signals when rendering has completed. Once the fence signals, |
| * then the backing storage for the framebuffer may be safely read from, such as for display or |
| * for media encoding. This would be referred to as a "presentation fence."</p> |
| * |
| * <p>Similarly when using an {@link android.media.ImageWriter} it is possible that an |
| * {@link android.media.Image} returned by {@link ImageWriter#dequeueInputImage()} may already |
| * have a {@link Image#getFence() fence} set on it. This would be what is referred to as either |
| * a "release fence" or an "acqurie fence" and indicates the fence that the writer must wait |
| * on before writing to the underlying buffer. In the case of ImageWriter this is done |
| * automatically when eg {@link Image#getPlanes()} is called, however when using |
| * {@link Image#getHardwareBuffer()} it is the caller's responsibility to ensure the |
| * release fence has signaled before writing to the buffer.</p> |
| * |
| * @see android.opengl.EGLExt#eglDupNativeFenceFDANDROID(EGLDisplay, EGLSync) |
| * @see android.media.Image#getFence() |
| */ |
| public final class SyncFence implements AutoCloseable, Parcelable { |
| |
| /** |
| * An invalid signal time. Represents either the signal time for a SyncFence that isn't valid |
| * (that is, {@link #isValid()} is false), or if an error occurred while attempting to retrieve |
| * the signal time. |
| */ |
| public static final long SIGNAL_TIME_INVALID = -1; |
| |
| /** |
| * A pending signal time. This is equivalent to the max value of a long, representing an |
| * infinitely far point in the future. |
| */ |
| public static final long SIGNAL_TIME_PENDING = Long.MAX_VALUE; |
| |
| private static final NativeAllocationRegistry sRegistry = |
| NativeAllocationRegistry.createNonmalloced(SyncFence.class.getClassLoader(), |
| nGetDestructor(), 4); |
| |
| private long mNativePtr; |
| |
| // The destructor for this object |
| // This is also used as our internal lock object. Although SyncFence doesn't claim to be |
| // thread-safe, the cost of doing so to avoid issues around double-close or similar issues |
| // is well worth making. |
| private final Runnable mCloser; |
| |
| private SyncFence(int fileDescriptor) { |
| mNativePtr = nCreate(fileDescriptor); |
| mCloser = sRegistry.registerNativeAllocation(this, mNativePtr); |
| } |
| |
| private SyncFence(@NonNull Parcel parcel) { |
| boolean valid = parcel.readBoolean(); |
| FileDescriptor fileDescriptor = null; |
| if (valid) { |
| fileDescriptor = parcel.readRawFileDescriptor(); |
| } |
| if (fileDescriptor != null) { |
| mNativePtr = nCreate(fileDescriptor.getInt$()); |
| mCloser = sRegistry.registerNativeAllocation(this, mNativePtr); |
| } else { |
| mCloser = () -> {}; |
| } |
| } |
| |
| /** |
| * Creates a SyncFence from a libui Fence* |
| * DOES NOT TAKE AN ADDITIONAL REFERENCE, the caller must incref if it intends to retain |
| * ownership (eg, when using sp<Fence>) |
| * @hide |
| */ |
| public SyncFence(long nativeFencePtr) { |
| mNativePtr = nativeFencePtr; |
| if (nativeFencePtr != 0) { |
| mCloser = sRegistry.registerNativeAllocation(this, mNativePtr); |
| } else { |
| mCloser = () -> {}; |
| } |
| } |
| |
| /** |
| * Creates a copy of the SyncFence from an existing one. |
| * Both fences must be closed() independently. |
| */ |
| @FlaggedApi(Flags.FLAG_SDK_DESIRED_PRESENT_TIME) |
| public SyncFence(@NonNull SyncFence other) { |
| this(other.mNativePtr); |
| |
| if (mNativePtr != 0) { |
| nIncRef(mNativePtr); |
| } |
| } |
| |
| private SyncFence() { |
| mCloser = () -> {}; |
| } |
| |
| /*** |
| * Create an empty SyncFence |
| * |
| * @return a SyncFence with invalid fence |
| * @hide |
| */ |
| public static @NonNull SyncFence createEmpty() { |
| return new SyncFence(); |
| } |
| |
| /** |
| * Create a new SyncFence wrapped around another {@link ParcelFileDescriptor}. By default, all |
| * method calls are delegated to the wrapped descriptor. This takes ownership of the |
| * {@link ParcelFileDescriptor}. |
| * |
| * @param wrapped The descriptor to be wrapped. |
| * @hide |
| */ |
| public static @NonNull SyncFence create(@NonNull ParcelFileDescriptor wrapped) { |
| return new SyncFence(wrapped.detachFd()); |
| } |
| |
| /** |
| * Create a new SyncFence wrapped around another descriptor. The returned {@link SyncFence} |
| * instance takes ownership of the file descriptor. |
| * |
| * @param fileDescriptor The descriptor to be wrapped. |
| * @hide |
| */ |
| public static @NonNull SyncFence adopt(int fileDescriptor) { |
| return new SyncFence(fileDescriptor); |
| } |
| |
| /** |
| * Return a dup'd ParcelFileDescriptor from the SyncFence ParcelFileDescriptor. |
| * @hide |
| */ |
| public @NonNull ParcelFileDescriptor getFdDup() throws IOException { |
| synchronized (mCloser) { |
| final int fd = mNativePtr != 0 ? nGetFd(mNativePtr) : -1; |
| if (fd == -1) { |
| throw new IllegalStateException("Cannot dup the FD of an invalid SyncFence"); |
| } |
| return ParcelFileDescriptor.fromFd(fd); |
| } |
| } |
| |
| /** |
| * Checks if the SyncFile object is valid. |
| * |
| * @return {@code true} if the file descriptor represents a valid, open file; |
| * {@code false} otherwise. |
| */ |
| public boolean isValid() { |
| synchronized (mCloser) { |
| return mNativePtr != 0 && nIsValid(mNativePtr); |
| } |
| } |
| |
| /** |
| * Waits for a SyncFence to signal for up to the timeout duration. |
| * |
| * An invalid SyncFence, that is if {@link #isValid()} is false, is treated equivalently |
| * to a SyncFence that has already signaled. That is, wait() will immediately return true. |
| * |
| * @param timeout The timeout duration. If the duration is negative, then this waits forever. |
| * @return true if the fence signaled or isn't valid, false otherwise. |
| */ |
| public boolean await(@NonNull Duration timeout) { |
| final long timeoutNanos; |
| if (timeout.isNegative()) { |
| timeoutNanos = -1; |
| } else { |
| timeoutNanos = timeout.toNanos(); |
| } |
| return await(timeoutNanos); |
| } |
| |
| /** |
| * Waits forever for a SyncFence to signal. |
| * |
| * An invalid SyncFence, that is if {@link #isValid()} is false, is treated equivalently |
| * to a SyncFence that has already signaled. That is, wait() will immediately return true. |
| * |
| * @return true if the fence signaled or isn't valid, false otherwise. |
| */ |
| public boolean awaitForever() { |
| return await(-1); |
| } |
| |
| private boolean await(long timeoutNanos) { |
| synchronized (mCloser) { |
| return mNativePtr != 0 && nWait(mNativePtr, timeoutNanos); |
| } |
| } |
| |
| /** |
| * Returns the time in nanoseconds that the fence signaled in the CLOCK_MONOTONIC time domain. |
| * This corresponds to {@link System#nanoTime()} but may also be compared to |
| * {@link SystemClock#uptimeMillis()} after adjusting for milliseconds vs. nanoseconds. |
| * |
| * If the fence isn't valid, that is if {@link #isValid()} is false, then this returns |
| * {@link #SIGNAL_TIME_INVALID}. Similarly, if an error occurs while trying to access the |
| * signal time, then {@link #SIGNAL_TIME_INVALID} is also returned. |
| * |
| * If the fence hasn't yet signaled, then {@link #SIGNAL_TIME_PENDING} is returned. |
| * |
| * @return The time the fence signaled, {@link #SIGNAL_TIME_INVALID} if there's an error, |
| * or {@link #SIGNAL_TIME_PENDING} if the fence hasn't signaled yet. |
| */ |
| public long getSignalTime() { |
| synchronized (mCloser) { |
| return mNativePtr != 0 ? nGetSignalTime(mNativePtr) : SIGNAL_TIME_INVALID; |
| } |
| } |
| |
| /** |
| * Close the SyncFence. This implementation closes the underlying OS resources allocated |
| * this stream. |
| */ |
| @Override |
| public void close() { |
| synchronized (mCloser) { |
| if (mNativePtr == 0) { |
| return; |
| } |
| mNativePtr = 0; |
| mCloser.run(); |
| } |
| } |
| |
| @Override |
| public int describeContents() { |
| return CONTENTS_FILE_DESCRIPTOR; |
| } |
| |
| /** @hide */ |
| public Object getLock() { |
| return mCloser; |
| } |
| |
| /** @hide */ |
| public long getNativeFence() { |
| return mNativePtr; |
| } |
| |
| /** |
| * Flatten this object into a Parcel. |
| * |
| * @param out The Parcel in which the object should be written. |
| * @param flags Additional flags about how the object should be written. |
| * May be {@code 0} or {@link #PARCELABLE_WRITE_RETURN_VALUE} |
| */ |
| @Override |
| public void writeToParcel(@NonNull Parcel out, int flags) { |
| synchronized (mCloser) { |
| final int fd = mNativePtr != 0 ? nGetFd(mNativePtr) : -1; |
| if (fd == -1) { |
| out.writeBoolean(false); |
| } else { |
| out.writeBoolean(true); |
| FileDescriptor temp = new FileDescriptor(); |
| temp.setInt$(fd); |
| out.writeFileDescriptor(temp); |
| } |
| } |
| } |
| |
| public static final @NonNull Parcelable.Creator<SyncFence> CREATOR = |
| new Parcelable.Creator<SyncFence>() { |
| @Override |
| public SyncFence createFromParcel(Parcel in) { |
| return new SyncFence(in); |
| } |
| |
| @Override |
| public SyncFence[] newArray(int size) { |
| return new SyncFence[size]; |
| } |
| }; |
| |
| private static native long nGetDestructor(); |
| private static native long nCreate(int fd); |
| private static native boolean nIsValid(long nPtr); |
| private static native int nGetFd(long nPtr); |
| private static native boolean nWait(long nPtr, long timeout); |
| private static native long nGetSignalTime(long nPtr); |
| private static native void nIncRef(long nPtr); |
| } |