1 /*
2  * Copyright (C) 2011 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.view;
18 
19 import android.annotation.UnsupportedAppUsage;
20 import android.os.Looper;
21 import android.os.MessageQueue;
22 import android.util.Log;
23 
24 import dalvik.annotation.optimization.FastNative;
25 import dalvik.system.CloseGuard;
26 
27 import java.lang.ref.WeakReference;
28 
29 /**
30  * Provides a low-level mechanism for an application to receive display events
31  * such as vertical sync.
32  *
33  * The display event receive is NOT thread safe.  Moreover, its methods must only
34  * be called on the Looper thread to which it is attached.
35  *
36  * @hide
37  */
38 public abstract class DisplayEventReceiver {
39 
40     /**
41      * When retrieving vsync events, this specifies that the vsync event should happen at the normal
42      * vsync-app tick.
43      * <p>
44      * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h
45      */
46     public static final int VSYNC_SOURCE_APP = 0;
47 
48     /**
49      * When retrieving vsync events, this specifies that the vsync event should happen whenever
50      * Surface Flinger is processing a frame.
51      * <p>
52      * Needs to be kept in sync with frameworks/native/include/gui/ISurfaceComposer.h
53      */
54     public static final int VSYNC_SOURCE_SURFACE_FLINGER = 1;
55 
56     private static final String TAG = "DisplayEventReceiver";
57 
58     private final CloseGuard mCloseGuard = CloseGuard.get();
59 
60     @UnsupportedAppUsage
61     private long mReceiverPtr;
62 
63     // We keep a reference message queue object here so that it is not
64     // GC'd while the native peer of the receiver is using them.
65     private MessageQueue mMessageQueue;
66 
nativeInit(WeakReference<DisplayEventReceiver> receiver, MessageQueue messageQueue, int vsyncSource)67     private static native long nativeInit(WeakReference<DisplayEventReceiver> receiver,
68             MessageQueue messageQueue, int vsyncSource);
nativeDispose(long receiverPtr)69     private static native void nativeDispose(long receiverPtr);
70     @FastNative
nativeScheduleVsync(long receiverPtr)71     private static native void nativeScheduleVsync(long receiverPtr);
72 
73     /**
74      * Creates a display event receiver.
75      *
76      * @param looper The looper to use when invoking callbacks.
77      */
78     @UnsupportedAppUsage
DisplayEventReceiver(Looper looper)79     public DisplayEventReceiver(Looper looper) {
80         this(looper, VSYNC_SOURCE_APP);
81     }
82 
83     /**
84      * Creates a display event receiver.
85      *
86      * @param looper The looper to use when invoking callbacks.
87      * @param vsyncSource The source of the vsync tick. Must be on of the VSYNC_SOURCE_* values.
88      */
DisplayEventReceiver(Looper looper, int vsyncSource)89     public DisplayEventReceiver(Looper looper, int vsyncSource) {
90         if (looper == null) {
91             throw new IllegalArgumentException("looper must not be null");
92         }
93 
94         mMessageQueue = looper.getQueue();
95         mReceiverPtr = nativeInit(new WeakReference<DisplayEventReceiver>(this), mMessageQueue,
96                 vsyncSource);
97 
98         mCloseGuard.open("dispose");
99     }
100 
101     @Override
finalize()102     protected void finalize() throws Throwable {
103         try {
104             dispose(true);
105         } finally {
106             super.finalize();
107         }
108     }
109 
110     /**
111      * Disposes the receiver.
112      */
dispose()113     public void dispose() {
114         dispose(false);
115     }
116 
dispose(boolean finalized)117     private void dispose(boolean finalized) {
118         if (mCloseGuard != null) {
119             if (finalized) {
120                 mCloseGuard.warnIfOpen();
121             }
122             mCloseGuard.close();
123         }
124 
125         if (mReceiverPtr != 0) {
126             nativeDispose(mReceiverPtr);
127             mReceiverPtr = 0;
128         }
129         mMessageQueue = null;
130     }
131 
132     /**
133      * Called when a vertical sync pulse is received.
134      * The recipient should render a frame and then call {@link #scheduleVsync}
135      * to schedule the next vertical sync pulse.
136      *
137      * @param timestampNanos The timestamp of the pulse, in the {@link System#nanoTime()}
138      * timebase.
139      * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
140      * @param frame The frame number.  Increases by one for each vertical sync interval.
141      */
142     @UnsupportedAppUsage
onVsync(long timestampNanos, long physicalDisplayId, int frame)143     public void onVsync(long timestampNanos, long physicalDisplayId, int frame) {
144     }
145 
146     /**
147      * Called when a display hotplug event is received.
148      *
149      * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
150      * timebase.
151      * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
152      * @param connected True if the display is connected, false if it disconnected.
153      */
154     @UnsupportedAppUsage
onHotplug(long timestampNanos, long physicalDisplayId, boolean connected)155     public void onHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
156     }
157 
158     /**
159      * Called when a display config changed event is received.
160      *
161      * @param timestampNanos The timestamp of the event, in the {@link System#nanoTime()}
162      * timebase.
163      * @param physicalDisplayId Stable display ID that uniquely describes a (display, port) pair.
164      * @param configId The new config Id
165      */
onConfigChanged(long timestampNanos, long physicalDisplayId, int configId)166     public void onConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
167     }
168 
169     /**
170      * Schedules a single vertical sync pulse to be delivered when the next
171      * display frame begins.
172      */
173     @UnsupportedAppUsage
scheduleVsync()174     public void scheduleVsync() {
175         if (mReceiverPtr == 0) {
176             Log.w(TAG, "Attempted to schedule a vertical sync pulse but the display event "
177                     + "receiver has already been disposed.");
178         } else {
179             nativeScheduleVsync(mReceiverPtr);
180         }
181     }
182 
183     // Called from native code.
184     @SuppressWarnings("unused")
185     @UnsupportedAppUsage
dispatchVsync(long timestampNanos, long physicalDisplayId, int frame)186     private void dispatchVsync(long timestampNanos, long physicalDisplayId, int frame) {
187         onVsync(timestampNanos, physicalDisplayId, frame);
188     }
189 
190     // Called from native code.
191     @SuppressWarnings("unused")
192     @UnsupportedAppUsage
dispatchHotplug(long timestampNanos, long physicalDisplayId, boolean connected)193     private void dispatchHotplug(long timestampNanos, long physicalDisplayId, boolean connected) {
194         onHotplug(timestampNanos, physicalDisplayId, connected);
195     }
196 
197     // Called from native code.
198     @SuppressWarnings("unused")
dispatchConfigChanged(long timestampNanos, long physicalDisplayId, int configId)199     private void dispatchConfigChanged(long timestampNanos, long physicalDisplayId, int configId) {
200         onConfigChanged(timestampNanos, physicalDisplayId, configId);
201     }
202 
203 }
204