1 /*
2  * Copyright (C) 2015 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 org.chromium.latency.walt;
18 
19 import android.util.Log;
20 import android.view.MotionEvent;
21 
22 import java.lang.reflect.Method;
23 import java.util.Locale;
24 
25 /**
26  * A convenient representation of MotionEvent events
27  * - microsecond accuracy
28  * - no bundling of ACTION_MOVE events
29  */
30 
31 public class UsMotionEvent {
32 
33     public long physicalTime, kernelTime, createTime;
34     public float x, y;
35     public int slot;
36     public int action;
37     public int num;
38     public String metadata;
39     public long baseTime;
40 
41     public boolean isOk = false;
42 
43     /**
44      *
45      * @param event - MotionEvent as received by the handler.
46      * @param baseTime - base time of the last clock sync.
47      */
UsMotionEvent(MotionEvent event, long baseTime)48     public UsMotionEvent(MotionEvent event, long baseTime) {
49         createTime = RemoteClockInfo.microTime() - baseTime;
50         this.baseTime = baseTime;
51         slot = -1;
52         kernelTime = getEventTimeMicro(event) - baseTime;
53         x = event.getX();
54         y = event.getY();
55         action = event.getAction();
56     }
57 
UsMotionEvent(MotionEvent event, long baseTime, int pos)58     public UsMotionEvent(MotionEvent event, long baseTime, int pos) {
59         createTime = RemoteClockInfo.microTime() - baseTime;
60         this.baseTime = baseTime;
61         slot = pos;
62         action = MotionEvent.ACTION_MOVE; // Only MOVE events get bundled with history
63 
64         kernelTime = getHistoricalEventTimeMicro(event, pos) - baseTime;
65         x = event.getHistoricalX(pos);
66         y = event.getHistoricalY(pos);
67     }
68 
getActionString()69     public String getActionString() {
70         return actionToString(action);
71     }
72 
73 
toString()74     public String toString() {
75         return String.format(Locale.US, "%d %f %f",
76                 kernelTime, x, y);
77 
78     }
79 
toStringLong()80     public String toStringLong() {
81         return String.format(Locale.US, "Event: t=%d x=%.1f y=%.1f slot=%d num=%d %s",
82                 kernelTime, x, y, slot, num, actionToString(action));
83 
84     }
85 
86     // The MotionEvent.actionToString is not present before API 19
actionToString(int action)87     public static String actionToString(int action) {
88         switch (action) {
89             case MotionEvent.ACTION_DOWN:
90                 return "ACTION_DOWN";
91             case MotionEvent.ACTION_UP:
92                 return "ACTION_UP";
93             case MotionEvent.ACTION_CANCEL:
94                 return "ACTION_CANCEL";
95             case MotionEvent.ACTION_OUTSIDE:
96                 return "ACTION_OUTSIDE";
97             case MotionEvent.ACTION_MOVE:
98                 return "ACTION_MOVE";
99             case MotionEvent.ACTION_HOVER_MOVE:
100                 return "ACTION_HOVER_MOVE";
101             case MotionEvent.ACTION_SCROLL:
102                 return "ACTION_SCROLL";
103             case MotionEvent.ACTION_HOVER_ENTER:
104                 return "ACTION_HOVER_ENTER";
105             case MotionEvent.ACTION_HOVER_EXIT:
106                 return "ACTION_HOVER_EXIT";
107         }
108         return "UNKNOWN_ACTION";
109     }
110 
111     /**
112      MotionEvent.getEventTime() function only provides millisecond resolution.
113      There is a MotionEvent.getEventTimeNano() function but for some reason it
114      is hidden by @hide which means it can't be called directly.
115      Calling is via reflection.
116 
117      See:
118      http://stackoverflow.com/questions/17035271/what-does-hide-mean-in-the-android-source-code
119      */
getEventTimeMicro(MotionEvent event)120     private long getEventTimeMicro(MotionEvent event) {
121         long t_nanos = -1;
122         try {
123             Class<?> cls = Class.forName("android.view.MotionEvent");
124             Method myTimeGetter = cls.getMethod("getEventTimeNano");
125             t_nanos = (long) myTimeGetter.invoke(event);
126         } catch (Exception e) {
127             Log.i("WALT.MsMotionEvent", e.getMessage());
128         }
129 
130         return t_nanos / 1000;
131     }
132 
getHistoricalEventTimeMicro(MotionEvent event, int pos)133     private long getHistoricalEventTimeMicro(MotionEvent event, int pos) {
134         long t_nanos = -1;
135         try {
136             Class<?> cls = Class.forName("android.view.MotionEvent");
137             Method myTimeGetter = cls.getMethod("getHistoricalEventTimeNano", new Class[] {int.class});
138             t_nanos = (long) myTimeGetter.invoke(event, new Object[]{pos});
139         } catch (Exception e) {
140             Log.i("WALT.MsMotionEvent", e.getMessage());
141         }
142 
143         return t_nanos / 1000;
144     }
145 
146 }
147 
148