/** * 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. * *

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.

* *

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."

* *

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.

* * @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) * @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 CREATOR = new Parcelable.Creator() { @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); }