• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.compat.annotation.UnsupportedAppUsage;
20 import android.os.Build;
21 import android.os.IBinder;
22 import android.os.Looper;
23 import android.os.MessageQueue;
24 import android.os.Trace;
25 import android.util.Log;
26 import android.util.SparseIntArray;
27 
28 import dalvik.system.CloseGuard;
29 
30 import java.io.PrintWriter;
31 import java.lang.ref.Reference;
32 import java.lang.ref.WeakReference;
33 
34 /**
35  * Provides a low-level mechanism for an application to receive input events.
36  * @hide
37  */
38 public abstract class InputEventReceiver {
39     private static final String TAG = "InputEventReceiver";
40 
41     private final CloseGuard mCloseGuard = CloseGuard.get();
42 
43     private long mReceiverPtr;
44 
45     // We keep references to the input channel and message queue objects here so that
46     // they are not GC'd while the native peer of the receiver is using them.
47     private InputChannel mInputChannel;
48     private MessageQueue mMessageQueue;
49 
50     // Map from InputEvent sequence numbers to dispatcher sequence numbers.
51     private final SparseIntArray mSeqMap = new SparseIntArray();
52 
nativeInit(WeakReference<InputEventReceiver> receiver, InputChannel inputChannel, MessageQueue messageQueue)53     private static native long nativeInit(WeakReference<InputEventReceiver> receiver,
54             InputChannel inputChannel, MessageQueue messageQueue);
nativeDispose(long receiverPtr)55     private static native void nativeDispose(long receiverPtr);
nativeFinishInputEvent(long receiverPtr, int seq, boolean handled)56     private static native void nativeFinishInputEvent(long receiverPtr, int seq, boolean handled);
nativeReportTimeline(long receiverPtr, int inputEventId, long gpuCompletedTime, long presentTime)57     private static native void nativeReportTimeline(long receiverPtr, int inputEventId,
58             long gpuCompletedTime, long presentTime);
nativeConsumeBatchedInputEvents(long receiverPtr, long frameTimeNanos)59     private static native boolean nativeConsumeBatchedInputEvents(long receiverPtr,
60             long frameTimeNanos);
nativeDump(long receiverPtr, String prefix)61     private static native String nativeDump(long receiverPtr, String prefix);
62 
63     /**
64      * Creates an input event receiver bound to the specified input channel.
65      *
66      * @param inputChannel The input channel.
67      * @param looper The looper to use when invoking callbacks.
68      */
InputEventReceiver(InputChannel inputChannel, Looper looper)69     public InputEventReceiver(InputChannel inputChannel, Looper looper) {
70         if (inputChannel == null) {
71             throw new IllegalArgumentException("inputChannel must not be null");
72         }
73         if (looper == null) {
74             throw new IllegalArgumentException("looper must not be null");
75         }
76 
77         mInputChannel = inputChannel;
78         mMessageQueue = looper.getQueue();
79         mReceiverPtr = nativeInit(new WeakReference<InputEventReceiver>(this),
80                 inputChannel, mMessageQueue);
81 
82         mCloseGuard.open("dispose");
83     }
84 
85     @Override
finalize()86     protected void finalize() throws Throwable {
87         try {
88             dispose(true);
89         } finally {
90             super.finalize();
91         }
92     }
93 
94     /**
95      * Disposes the receiver.
96      * Must be called on the same Looper thread to which the receiver is attached.
97      */
dispose()98     public void dispose() {
99         dispose(false);
100     }
101 
dispose(boolean finalized)102     private void dispose(boolean finalized) {
103         if (mCloseGuard != null) {
104             if (finalized) {
105                 mCloseGuard.warnIfOpen();
106             }
107             mCloseGuard.close();
108         }
109 
110         if (mReceiverPtr != 0) {
111             nativeDispose(mReceiverPtr);
112             mReceiverPtr = 0;
113         }
114 
115         if (mInputChannel != null) {
116             mInputChannel.dispose();
117             mInputChannel = null;
118         }
119         mMessageQueue = null;
120         Reference.reachabilityFence(this);
121     }
122 
123     /**
124      * Called when an input event is received.
125      * The recipient should process the input event and then call {@link #finishInputEvent}
126      * to indicate whether the event was handled.  No new input events will be received
127      * until {@link #finishInputEvent} is called.
128      *
129      * @param event The input event that was received.
130      */
131     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
onInputEvent(InputEvent event)132     public void onInputEvent(InputEvent event) {
133         finishInputEvent(event, false);
134     }
135 
136     /**
137      * Called when a focus event is received.
138      *
139      * @param hasFocus if true, the window associated with this input channel has just received
140      *                 focus
141      *                 if false, the window associated with this input channel has just lost focus
142      * @param inTouchMode if true, the device is in touch mode
143      *                    if false, the device is not in touch mode
144      */
145     // Called from native code.
onFocusEvent(boolean hasFocus, boolean inTouchMode)146     public void onFocusEvent(boolean hasFocus, boolean inTouchMode) {
147     }
148 
149     /**
150      * Called when a Pointer Capture event is received.
151      *
152      * @param pointerCaptureEnabled if true, the window associated with this input channel has just
153      *                              received Pointer Capture
154      *                              if false, the window associated with this input channel has just
155      *                              lost Pointer Capture
156      * @see View#requestPointerCapture()
157      * @see View#releasePointerCapture()
158      */
159     // Called from native code.
onPointerCaptureEvent(boolean pointerCaptureEnabled)160     public void onPointerCaptureEvent(boolean pointerCaptureEnabled) {
161     }
162 
163     /**
164      * Called when a drag event is received, from native code.
165      *
166      * @param isExiting if false, the window associated with this input channel has just received
167      *                 drag
168      *                 if true, the window associated with this input channel has just lost drag
169      */
onDragEvent(boolean isExiting, float x, float y)170     public void onDragEvent(boolean isExiting, float x, float y) {
171     }
172 
173     /**
174      * Called when a batched input event is pending.
175      *
176      * The batched input event will continue to accumulate additional movement
177      * samples until the recipient calls {@link #consumeBatchedInputEvents} or
178      * an event is received that ends the batch and causes it to be consumed
179      * immediately (such as a pointer up event).
180      * @param source The source of the batched event.
181      */
onBatchedInputEventPending(int source)182     public void onBatchedInputEventPending(int source) {
183         consumeBatchedInputEvents(-1);
184     }
185 
186     /**
187      * Finishes an input event and indicates whether it was handled.
188      * Must be called on the same Looper thread to which the receiver is attached.
189      *
190      * @param event The input event that was finished.
191      * @param handled True if the event was handled.
192      */
finishInputEvent(InputEvent event, boolean handled)193     public final void finishInputEvent(InputEvent event, boolean handled) {
194         if (event == null) {
195             throw new IllegalArgumentException("event must not be null");
196         }
197         if (mReceiverPtr == 0) {
198             Log.w(TAG, "Attempted to finish an input event but the input event "
199                     + "receiver has already been disposed.");
200         } else {
201             int index = mSeqMap.indexOfKey(event.getSequenceNumber());
202             if (index < 0) {
203                 Log.w(TAG, "Attempted to finish an input event that is not in progress.");
204             } else {
205                 int seq = mSeqMap.valueAt(index);
206                 mSeqMap.removeAt(index);
207                 nativeFinishInputEvent(mReceiverPtr, seq, handled);
208             }
209         }
210         event.recycleIfNeededAfterDispatch();
211     }
212 
213     /**
214      * Report the timing / latency information for a specific input event.
215      */
reportTimeline(int inputEventId, long gpuCompletedTime, long presentTime)216     public final void reportTimeline(int inputEventId, long gpuCompletedTime, long presentTime) {
217         Trace.traceBegin(Trace.TRACE_TAG_INPUT, "reportTimeline");
218         nativeReportTimeline(mReceiverPtr, inputEventId, gpuCompletedTime, presentTime);
219         Trace.traceEnd(Trace.TRACE_TAG_INPUT);
220     }
221 
222     /**
223      * Consumes all pending batched input events.
224      * Must be called on the same Looper thread to which the receiver is attached.
225      *
226      * This method forces all batched input events to be delivered immediately.
227      * Should be called just before animating or drawing a new frame in the UI.
228      *
229      * @param frameTimeNanos The time in the {@link System#nanoTime()} time base
230      * when the current display frame started rendering, or -1 if unknown.
231      *
232      * @return Whether a batch was consumed
233      */
consumeBatchedInputEvents(long frameTimeNanos)234     public final boolean consumeBatchedInputEvents(long frameTimeNanos) {
235         if (mReceiverPtr == 0) {
236             Log.w(TAG, "Attempted to consume batched input events but the input event "
237                     + "receiver has already been disposed.");
238         } else {
239             return nativeConsumeBatchedInputEvents(mReceiverPtr, frameTimeNanos);
240         }
241         return false;
242     }
243 
244     /**
245      * @return Returns a token to identify the input channel.
246      */
getToken()247     public IBinder getToken() {
248         if (mInputChannel == null) {
249             return null;
250         }
251         return mInputChannel.getToken();
252     }
253 
254     // Called from native code.
255     @SuppressWarnings("unused")
256     @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.R, trackingBug = 170729553)
dispatchInputEvent(int seq, InputEvent event)257     private void dispatchInputEvent(int seq, InputEvent event) {
258         mSeqMap.put(event.getSequenceNumber(), seq);
259         onInputEvent(event);
260     }
261 
262     /**
263      * Dump the state of this InputEventReceiver to the writer.
264      * @param prefix the prefix (typically whitespace padding) to append in front of each line
265      * @param writer the writer where the dump should be written
266      */
dump(String prefix, PrintWriter writer)267     public void dump(String prefix, PrintWriter writer) {
268         writer.println(prefix + getClass().getName());
269         writer.println(prefix + " mInputChannel: " + mInputChannel);
270         writer.println(prefix + " mSeqMap: " + mSeqMap);
271         writer.println(prefix + " mReceiverPtr:\n" + nativeDump(mReceiverPtr, prefix + "  "));
272     }
273 
274     /**
275      * Factory for InputEventReceiver
276      */
277     public interface Factory {
278         /**
279          * Create a new InputReceiver for a given inputChannel
280          */
createInputEventReceiver(InputChannel inputChannel, Looper looper)281         InputEventReceiver createInputEventReceiver(InputChannel inputChannel, Looper looper);
282     }
283 }
284