/* * Copyright (C) 2011 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.view; import android.compat.annotation.UnsupportedAppUsage; import android.graphics.FrameInfo; import android.os.Build; import android.os.Looper; import android.os.MessageQueue; import android.util.Log; import com.android.internal.annotations.VisibleForTesting; import dalvik.annotation.optimization.FastNative; import libcore.util.NativeAllocationRegistry; import java.lang.ref.WeakReference; /** * Provides a low-level mechanism for an application to receive display events * such as vertical sync. * * The display event receive is NOT thread safe. Moreover, its methods must only * be called on the Looper thread to which it is attached. * * @hide */ public abstract class DisplayEventReceiver { /** * When retrieving vsync events, this specifies that the vsync event should happen at the normal * vsync-app tick. *

* Keep in sync with frameworks/native/libs/gui/aidl/android/gui/ISurfaceComposer.aidl */ public static final int VSYNC_SOURCE_APP = 0; /** * When retrieving vsync events, this specifies that the vsync event should happen whenever * Surface Flinger is processing a frame. *

* Keep in sync with frameworks/native/libs/gui/aidl/android/gui/ISurfaceComposer.aidl */ public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1; /** * Specifies to generate mode changed events from Surface Flinger. *

* Keep in sync with frameworks/native/libs/gui/aidl/android/gui/ISurfaceComposer.aidl */ public static final int EVENT_REGISTRATION_MODE_CHANGED_FLAG = 0x1; /** * Specifies to generate frame rate override events from Surface Flinger. *

* Keep in sync with frameworks/native/libs/gui/aidl/android/gui/ISurfaceComposer.aidl */ public static final int EVENT_REGISTRATION_FRAME_RATE_OVERRIDE_FLAG = 0x2; private static final String TAG = "DisplayEventReceiver"; @UnsupportedAppUsage private long mReceiverPtr; // We keep a reference message queue object here so that it is not // GC'd while the native peer of the receiver is using them. private MessageQueue mMessageQueue; private final VsyncEventData mVsyncEventData = new VsyncEventData(); private static native long nativeInit(WeakReference receiver, WeakReference vsyncEventData, MessageQueue messageQueue, int vsyncSource, int eventRegistration, long layerHandle); private static native long nativeGetDisplayEventReceiverFinalizer(); @FastNative private static native void nativeScheduleVsync(long receiverPtr); private static native VsyncEventData nativeGetLatestVsyncEventData(long receiverPtr); private static final NativeAllocationRegistry sNativeAllocationRegistry = NativeAllocationRegistry.createMalloced( DisplayEventReceiver.class.getClassLoader(), nativeGetDisplayEventReceiverFinalizer()); private Runnable mFreeNativeResources; /** * Creates a display event receiver. * * @param looper The looper to use when invoking callbacks. */ @UnsupportedAppUsage public DisplayEventReceiver(Looper looper) { this(looper, VSYNC_SOURCE_APP, /* eventRegistration */ 0, /* layerHandle */ 0L); } public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) { this(looper, vsyncSource, eventRegistration, /* layerHandle */ 0L); } /** * Creates a display event receiver. * * @param looper The looper to use when invoking callbacks. * @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values. * @param eventRegistration Which events to dispatch. Must be a bitfield consist of the * EVENT_REGISTRATION_*_FLAG values. * @param layerHandle Layer to which the current instance is attached to */ public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration, long layerHandle) { if (looper == null) { throw new IllegalArgumentException("looper must not be null"); } mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference(this), new WeakReference(mVsyncEventData), mMessageQueue, vsyncSource, eventRegistration, layerHandle); mFreeNativeResources = sNativeAllocationRegistry.registerNativeAllocation(this, mReceiverPtr); } /** * Disposes the receiver. */ public void dispose() { if (mReceiverPtr != 0) { mFreeNativeResources.run(); mReceiverPtr = 0; } mMessageQueue = null; } /** * Class to capture all inputs required for syncing events data. * * @hide */ public static final class VsyncEventData { // The max capacity of frame timeline choices. // Must be in sync with VsyncEventData::kFrameTimelinesCapacity in // frameworks/native/libs/gui/include/gui/VsyncEventData.h static final int FRAME_TIMELINES_CAPACITY = 7; public static class FrameTimeline { FrameTimeline() { // Some reasonable values (+10 ms) for default timestamps. deadline = System.nanoTime() + 10_000_000; expectedPresentationTime = deadline + 10_000_000; } // Called from native code. @SuppressWarnings("unused") FrameTimeline(long vsyncId, long expectedPresentationTime, long deadline) { this.vsyncId = vsyncId; this.expectedPresentationTime = expectedPresentationTime; this.deadline = deadline; } void copyFrom(FrameTimeline other) { vsyncId = other.vsyncId; expectedPresentationTime = other.expectedPresentationTime; deadline = other.deadline; } // The frame timeline vsync id, used to correlate a frame // produced by HWUI with the timeline data stored in Surface Flinger. public long vsyncId = FrameInfo.INVALID_VSYNC_ID; // The frame timestamp for when the frame is expected to be presented. public long expectedPresentationTime; // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is // allotted for the frame to be completed. public long deadline; } /** * The current interval between frames in ns. This will be used to align * {@link FrameInfo#VSYNC} to the current vsync in case Choreographer callback was heavily * delayed by the app. */ public long frameInterval = -1; public final FrameTimeline[] frameTimelines; public int preferredFrameTimelineIndex = 0; // The default FrameTimeline is a placeholder populated with invalid vsync ID and some // reasonable timestamps. public int frameTimelinesLength = 1; VsyncEventData() { frameTimelines = new FrameTimeline[FRAME_TIMELINES_CAPACITY]; for (int i = 0; i < frameTimelines.length; i++) { frameTimelines[i] = new FrameTimeline(); } } // Called from native code. @SuppressWarnings("unused") VsyncEventData(FrameTimeline[] frameTimelines, int preferredFrameTimelineIndex, int frameTimelinesLength, long frameInterval) { this.frameTimelines = frameTimelines; this.preferredFrameTimelineIndex = preferredFrameTimelineIndex; this.frameTimelinesLength = frameTimelinesLength; this.frameInterval = frameInterval; } void copyFrom(VsyncEventData other) { preferredFrameTimelineIndex = other.preferredFrameTimelineIndex; frameTimelinesLength = other.frameTimelinesLength; frameInterval = other.frameInterval; for (int i = 0; i < frameTimelines.length; i++) { frameTimelines[i].copyFrom(other.frameTimelines[i]); } } public FrameTimeline preferredFrameTimeline() { return frameTimelines[preferredFrameTimelineIndex]; } } /** * Called when a vertical sync pulse is received. * The recipient should render a frame and then call {@link #scheduleVsync} * to schedule the next vertical sync pulse. * * @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()} * timebase. * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair. * @param frame The frame number. Increases by one for each vertical sync interval. * @param vsyncEventData The vsync event data. */ public void onVsync(long timestampNanos, long physicalDisplayId, int frame, VsyncEventData vsyncEventData) { } /** * Called when a display hotplug event is received. * * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} * timebase. * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair. * @param connected True if the display is connected, false if it disconnected. */ @UnsupportedAppUsage public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { } /** * Called when a display hotplug event with connection error is received. * * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} * timebase. * @param connectionError the hotplug connection error code. */ public void onHotplugConnectionError(long timestampNanos, int connectionError) { } /** * Called when a display mode changed event is received. * * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} * timebase. * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair. * @param modeId The new mode Id * @param renderPeriod The render frame period, which is a multiple of the mode's vsync period */ public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId, long renderPeriod) { } /** * Called when a display hdcp levels change event is received. * * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair. * @param connectedLevel the new connected HDCP level * @param maxLevel the maximum HDCP level */ public void onHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel) { } /** * Represents a mapping between a UID and an override frame rate */ public static class FrameRateOverride { // The application uid public final int uid; // The frame rate that this application runs at public final float frameRateHz; @VisibleForTesting public FrameRateOverride(int uid, float frameRateHz) { this.uid = uid; this.frameRateHz = frameRateHz; } @Override public String toString() { return "{uid=" + uid + " frameRateHz=" + frameRateHz + "}"; } } /** * Called when frame rate override event is received. * * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()} * timebase. * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair. * @param overrides The mappings from uid to frame rates */ public void onFrameRateOverridesChanged(long timestampNanos, long physicalDisplayId, FrameRateOverride[] overrides) { } /** * Schedules a single vertical sync pulse to be delivered when the next * display frame begins. */ @UnsupportedAppUsage public void scheduleVsync() { if (mReceiverPtr == 0) { Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event " + "receiver has already been disposed."); } else { nativeScheduleVsync(mReceiverPtr); } } /** * Gets the latest vsync event data from surface flinger. */ VsyncEventData getLatestVsyncEventData() { return nativeGetLatestVsyncEventData(mReceiverPtr); } // Called from native code. @SuppressWarnings("unused") private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) { onVsync(timestampNanos, physicalDisplayId, frame, mVsyncEventData); } // Called from native code. @SuppressWarnings("unused") @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553) private void dispatchHotplug(long timestampNanos, long physicalDisplayId, boolean connected) { onHotplug(timestampNanos, physicalDisplayId, connected); } @SuppressWarnings("unused") private void dispatchHotplugConnectionError(long timestampNanos, int connectionError) { onHotplugConnectionError(timestampNanos, connectionError); } // Called from native code. @SuppressWarnings("unused") private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId, long renderPeriod) { onModeChanged(timestampNanos, physicalDisplayId, modeId, renderPeriod); } // Called from native code. @SuppressWarnings("unused") private void dispatchFrameRateOverrides(long timestampNanos, long physicalDisplayId, FrameRateOverride[] overrides) { onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides); } // Called from native code. @SuppressWarnings("unused") private void dispatchHdcpLevelsChanged(long physicalDisplayId, int connectedLevel, int maxLevel) { onHdcpLevelsChanged(physicalDisplayId, connectedLevel, maxLevel); } }