1 /*
2  * Copyright (C) 2021 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.hardware.input;
18 
19 import android.annotation.FloatRange;
20 import android.annotation.IntDef;
21 import android.annotation.IntRange;
22 import android.annotation.NonNull;
23 import android.annotation.SystemApi;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.os.SystemClock;
27 import android.view.InputEvent;
28 import android.view.MotionEvent;
29 
30 import java.lang.annotation.Retention;
31 import java.lang.annotation.RetentionPolicy;
32 
33 /**
34  * An event describing a touchscreen interaction originating from a remote device.
35  *
36  * The pointer id, tool type, action, and location are required; pressure and main axis size are
37  * optional.
38  *
39  * Note: A VirtualTouchEvent with ACTION_CANCEL can only be created with TOOL_TYPE_PALM (and vice
40  * versa). Events are injected into the uinput kernel module, which has no concept of cancelling
41  * an action. The only way to state the intention that a pointer should not be handled as a pointer
42  * is to change its tool type to TOOL_TYPE_PALM.
43  *
44  * @hide
45  */
46 @SystemApi
47 public final class VirtualTouchEvent implements Parcelable {
48 
49     /** @hide */
50     public static final int TOOL_TYPE_UNKNOWN = MotionEvent.TOOL_TYPE_UNKNOWN;
51     /** Tool type indicating that the user's finger is the origin of the event. */
52     public static final int TOOL_TYPE_FINGER = MotionEvent.TOOL_TYPE_FINGER;
53     /**
54      * Tool type indicating that a user's palm (or other input mechanism to be rejected) is the
55      * origin of the event.
56      */
57     public static final int TOOL_TYPE_PALM = MotionEvent.TOOL_TYPE_PALM;
58     /** @hide */
59     @IntDef(prefix = { "TOOL_TYPE_" }, value = {
60             TOOL_TYPE_UNKNOWN,
61             TOOL_TYPE_FINGER,
62             TOOL_TYPE_PALM,
63     })
64     @Retention(RetentionPolicy.SOURCE)
65     public @interface ToolType {}
66 
67     /** @hide */
68     public static final int ACTION_UNKNOWN = -1;
69     /** Action indicating the tool has been pressed down to the touchscreen. */
70     public static final int ACTION_DOWN = MotionEvent.ACTION_DOWN;
71     /** Action indicating the tool has been lifted from the touchscreen. */
72     public static final int ACTION_UP = MotionEvent.ACTION_UP;
73     /** Action indicating the tool has been moved along the face of the touchscreen. */
74     public static final int ACTION_MOVE = MotionEvent.ACTION_MOVE;
75     /** Action indicating the tool cancelled the current movement. */
76     public static final int ACTION_CANCEL = MotionEvent.ACTION_CANCEL;
77     /** @hide */
78     @IntDef(prefix = { "ACTION_" }, value = {
79             ACTION_UNKNOWN,
80             ACTION_DOWN,
81             ACTION_UP,
82             ACTION_MOVE,
83             ACTION_CANCEL,
84     })
85     @Retention(RetentionPolicy.SOURCE)
86     public @interface Action {}
87 
88     // The maximum number of pointers that can be touching the screen at once. (See MAX_POINTERS
89     // in frameworks/native/include/input/Input.h)
90     private static final int MAX_POINTERS = 16;
91 
92     private final int mPointerId;
93     private final @ToolType int mToolType;
94     private final @Action int mAction;
95     private final float mX;
96     private final float mY;
97     private final float mPressure;
98     private final float mMajorAxisSize;
99     private final long mEventTimeNanos;
100 
VirtualTouchEvent(int pointerId, @ToolType int toolType, @Action int action, float x, float y, float pressure, float majorAxisSize, long eventTimeNanos)101     private VirtualTouchEvent(int pointerId, @ToolType int toolType, @Action int action,
102             float x, float y, float pressure, float majorAxisSize, long eventTimeNanos) {
103         mPointerId = pointerId;
104         mToolType = toolType;
105         mAction = action;
106         mX = x;
107         mY = y;
108         mPressure = pressure;
109         mMajorAxisSize = majorAxisSize;
110         mEventTimeNanos = eventTimeNanos;
111     }
112 
VirtualTouchEvent(@onNull Parcel parcel)113     private VirtualTouchEvent(@NonNull Parcel parcel) {
114         mPointerId = parcel.readInt();
115         mToolType = parcel.readInt();
116         mAction = parcel.readInt();
117         mX = parcel.readFloat();
118         mY = parcel.readFloat();
119         mPressure = parcel.readFloat();
120         mMajorAxisSize = parcel.readFloat();
121         mEventTimeNanos = parcel.readLong();
122     }
123 
124     @Override
writeToParcel(@onNull Parcel dest, int flags)125     public void writeToParcel(@NonNull Parcel dest, int flags) {
126         dest.writeInt(mPointerId);
127         dest.writeInt(mToolType);
128         dest.writeInt(mAction);
129         dest.writeFloat(mX);
130         dest.writeFloat(mY);
131         dest.writeFloat(mPressure);
132         dest.writeFloat(mMajorAxisSize);
133         dest.writeLong(mEventTimeNanos);
134     }
135 
136     @Override
describeContents()137     public int describeContents() {
138         return 0;
139     }
140 
141     @Override
toString()142     public String toString() {
143         return "VirtualTouchEvent("
144                 + " pointerId=" + mPointerId
145                 + " toolType=" + MotionEvent.toolTypeToString(mToolType)
146                 + " action=" + MotionEvent.actionToString(mAction)
147                 + " x=" + mX
148                 + " y=" + mY
149                 + " pressure=" + mPressure
150                 + " majorAxisSize=" + mMajorAxisSize
151                 + " eventTime(ns)=" + mEventTimeNanos;
152     }
153 
154     /**
155      * Returns the pointer id associated with this event.
156      */
getPointerId()157     public int getPointerId() {
158         return mPointerId;
159     }
160 
161     /**
162      * Returns the tool type associated with this event.
163      */
getToolType()164     public @ToolType int getToolType() {
165         return mToolType;
166     }
167 
168     /**
169      * Returns the action associated with this event.
170      */
getAction()171     public @Action int getAction() {
172         return mAction;
173     }
174 
175     /**
176      * Returns the x-axis location associated with this event.
177      */
getX()178     public float getX() {
179         return mX;
180     }
181 
182     /**
183      * Returns the y-axis location associated with this event.
184      */
getY()185     public float getY() {
186         return mY;
187     }
188 
189     /**
190      * Returns the pressure associated with this event. Returns {@link Float#NaN} if omitted.
191      */
getPressure()192     public float getPressure() {
193         return mPressure;
194     }
195 
196     /**
197      * Returns the major axis size associated with this event. Returns {@link Float#NaN} if omitted.
198      */
getMajorAxisSize()199     public float getMajorAxisSize() {
200         return mMajorAxisSize;
201     }
202 
203     /**
204      * Returns the time this event occurred, in the {@link SystemClock#uptimeMillis()} time base but
205      * with nanosecond (instead of millisecond) precision.
206      *
207      * @see InputEvent#getEventTime()
208      */
getEventTimeNanos()209     public long getEventTimeNanos() {
210         return mEventTimeNanos;
211     }
212 
213     /**
214      * Builder for {@link VirtualTouchEvent}.
215      */
216     public static final class Builder {
217 
218         private @ToolType int mToolType = TOOL_TYPE_UNKNOWN;
219         private int mPointerId = MotionEvent.INVALID_POINTER_ID;
220         private @Action int mAction = ACTION_UNKNOWN;
221         private float mX = Float.NaN;
222         private float mY = Float.NaN;
223         private float mPressure = Float.NaN;
224         private float mMajorAxisSize = Float.NaN;
225         private long mEventTimeNanos = 0L;
226 
227         /**
228          * Creates a {@link VirtualTouchEvent} object with the current builder configuration.
229          *
230          * @throws IllegalArgumentException if one of the required arguments is missing or if
231          * ACTION_CANCEL is not set in combination with TOOL_TYPE_PALM. See
232          * {@link VirtualTouchEvent} for a detailed explanation.
233          */
build()234         public @NonNull VirtualTouchEvent build() {
235             if (mToolType == TOOL_TYPE_UNKNOWN || mPointerId == MotionEvent.INVALID_POINTER_ID
236                     || mAction == ACTION_UNKNOWN || Float.isNaN(mX) || Float.isNaN(mY)) {
237                 throw new IllegalArgumentException(
238                         "Cannot build virtual touch event with unset required fields");
239             }
240             if ((mToolType == TOOL_TYPE_PALM && mAction != ACTION_CANCEL)
241                     || (mAction == ACTION_CANCEL && mToolType != TOOL_TYPE_PALM)) {
242                 throw new IllegalArgumentException(
243                         "ACTION_CANCEL and TOOL_TYPE_PALM must always appear together");
244             }
245             return new VirtualTouchEvent(mPointerId, mToolType, mAction, mX, mY, mPressure,
246                     mMajorAxisSize, mEventTimeNanos);
247         }
248 
249         /**
250          * Sets the pointer id of the event.
251          *
252          * <p>A Valid pointer id need to be in the range of 0 to 15.
253          *
254          * @return this builder, to allow for chaining of calls
255          */
setPointerId( @ntRangefrom = 0, to = MAX_POINTERS - 1) int pointerId)256         public @NonNull Builder setPointerId(
257                 @IntRange(from = 0, to = MAX_POINTERS - 1) int pointerId) {
258             if (pointerId < 0 || pointerId > 15) {
259                 throw new IllegalArgumentException(
260                         "The pointer id must be in the range 0 - " + (MAX_POINTERS - 1)
261                                 + "inclusive, but was: " + pointerId);
262             }
263             mPointerId = pointerId;
264             return this;
265         }
266 
267         /**
268          * Sets the tool type of the event.
269          *
270          * @return this builder, to allow for chaining of calls
271          */
setToolType(@oolType int toolType)272         public @NonNull Builder setToolType(@ToolType int toolType) {
273             if (toolType != TOOL_TYPE_FINGER && toolType != TOOL_TYPE_PALM) {
274                 throw new IllegalArgumentException("Unsupported touch event tool type");
275             }
276             mToolType = toolType;
277             return this;
278         }
279 
280         /**
281          * Sets the action of the event.
282          *
283          * @return this builder, to allow for chaining of calls
284          */
setAction(@ction int action)285         public @NonNull Builder setAction(@Action int action) {
286             if (action != ACTION_DOWN && action != ACTION_UP && action != ACTION_MOVE
287                     && action != ACTION_CANCEL) {
288                 throw new IllegalArgumentException(
289                         "Unsupported touch event action type: " + action);
290             }
291             mAction = action;
292             return this;
293         }
294 
295         /**
296          * Sets the x-axis location of the event.
297          *
298          * @return this builder, to allow for chaining of calls
299          */
setX(float absX)300         public @NonNull Builder setX(float absX) {
301             mX = absX;
302             return this;
303         }
304 
305         /**
306          * Sets the y-axis location of the event.
307          *
308          * @return this builder, to allow for chaining of calls
309          */
setY(float absY)310         public @NonNull Builder setY(float absY) {
311             mY = absY;
312             return this;
313         }
314 
315         /**
316          * Sets the pressure of the event. This field is optional and can be omitted.
317          *
318          * @param pressure The pressure of the touch.
319          *                 Note: The VirtualTouchscreen, consuming VirtualTouchEvents, is
320          *                 configured with a pressure axis range from 0.0 to 255.0. Only the
321          *                 lower end of the range is enforced. You can pass values larger than
322          *                 255.0. With physical input devices this could happen if the
323          *                 calibration is off. Values larger than 255.0 will not be trimmed and
324          *                 passed on as is.
325          *
326          * @throws IllegalArgumentException if the pressure is smaller than 0.
327          *
328          * @return this builder, to allow for chaining of calls
329          */
setPressure(@loatRangefrom = 0f) float pressure)330         public @NonNull Builder setPressure(@FloatRange(from = 0f) float pressure) {
331             if (pressure < 0f) {
332                 throw new IllegalArgumentException("Touch event pressure cannot be negative");
333             }
334             mPressure = pressure;
335             return this;
336         }
337 
338         /**
339          * Sets the major axis size of the event. This field is optional and can be omitted.
340          *
341          * @return this builder, to allow for chaining of calls
342          */
setMajorAxisSize(@loatRangefrom = 0f) float majorAxisSize)343         public @NonNull Builder setMajorAxisSize(@FloatRange(from = 0f) float majorAxisSize) {
344             if (majorAxisSize < 0f) {
345                 throw new IllegalArgumentException(
346                         "Touch event major axis size cannot be negative");
347             }
348             mMajorAxisSize = majorAxisSize;
349             return this;
350         }
351 
352         /**
353          * Sets the time (in nanoseconds) when this specific event was generated. This may be
354          * obtained from {@link SystemClock#uptimeMillis()} (with nanosecond precision instead of
355          * millisecond), but can be different depending on the use case.
356          * This field is optional and can be omitted.
357          *
358          * @return this builder, to allow for chaining of calls
359          * @see InputEvent#getEventTime()
360          */
setEventTimeNanos(long eventTimeNanos)361         public @NonNull Builder setEventTimeNanos(long eventTimeNanos) {
362             if (eventTimeNanos < 0L) {
363                 throw new IllegalArgumentException("Event time cannot be negative");
364             }
365             mEventTimeNanos = eventTimeNanos;
366             return this;
367         }
368     }
369 
370     public static final @NonNull Parcelable.Creator<VirtualTouchEvent> CREATOR =
371             new Parcelable.Creator<VirtualTouchEvent>() {
372         public VirtualTouchEvent createFromParcel(Parcel source) {
373             return new VirtualTouchEvent(source);
374         }
375         public VirtualTouchEvent[] newArray(int size) {
376             return new VirtualTouchEvent[size];
377         }
378     };
379 }
380