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

* Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h */ 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. *

* Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h */ public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1; /** * Specifies to generate mode changed events from Surface Flinger. *

* Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h */ public static final int EVENT_REGISTRATION_MODE_CHANGED_FLAG = 0x1; /** * Specifies to generate frame rate override events from Surface Flinger. *

* Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h */ 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 static native long nativeInit(WeakReference receiver, MessageQueue messageQueue, int vsyncSource, int eventRegistration); private static native void nativeDispose(long receiverPtr); @FastNative private static native void nativeScheduleVsync(long receiverPtr); /** * Creates a display event receiver. * * @param looper The looper to use when invoking callbacks. */ @UnsupportedAppUsage public DisplayEventReceiver(Looper looper) { this(looper, VSYNC_SOURCE_APP, 0); } /** * 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. */ public DisplayEventReceiver(Looper looper, int vsyncSource, int eventRegistration) { if (looper == null) { throw new IllegalArgumentException("looper must not be null"); } mMessageQueue = looper.getQueue(); mReceiverPtr = nativeInit(new WeakReference(this), mMessageQueue, vsyncSource, eventRegistration); } @Override protected void finalize() throws Throwable { try { dispose(true); } finally { super.finalize(); } } /** * Disposes the receiver. */ public void dispose() { dispose(false); } private void dispose(boolean finalized) { if (mReceiverPtr != 0) { nativeDispose(mReceiverPtr); mReceiverPtr = 0; } mMessageQueue = null; } static final class VsyncEventData { // The frame timeline vsync id, used to correlate a frame // produced by HWUI with the timeline data stored in Surface Flinger. public final long id; // The frame deadline timestamp in {@link System#nanoTime()} timebase that it is // allotted for the frame to be completed. public final long frameDeadline; /** * 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 final long frameInterval; VsyncEventData(long id, long frameDeadline, long frameInterval) { this.id = id; this.frameDeadline = frameDeadline; this.frameInterval = frameInterval; } VsyncEventData() { this.id = FrameInfo.INVALID_VSYNC_ID; this.frameDeadline = Long.MAX_VALUE; this.frameInterval = -1; } } /** * 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 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 */ public void onModeChanged(long timestampNanos, long physicalDisplayId, int modeId) { } /** * 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); } } // Called from native code. @SuppressWarnings("unused") private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame, long frameTimelineVsyncId, long frameDeadline, long frameInterval) { onVsync(timestampNanos, physicalDisplayId, frame, new VsyncEventData(frameTimelineVsyncId, frameDeadline, frameInterval)); } // 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); } // Called from native code. @SuppressWarnings("unused") private void dispatchModeChanged(long timestampNanos, long physicalDisplayId, int modeId) { onModeChanged(timestampNanos, physicalDisplayId, modeId); } // Called from native code. @SuppressWarnings("unused") private void dispatchFrameRateOverrides(long timestampNanos, long physicalDisplayId, FrameRateOverride[] overrides) { onFrameRateOverridesChanged(timestampNanos, physicalDisplayId, overrides); } }