• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2017 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 com.android.cts.mockime;
18 
19 import android.inputmethodservice.AbstractInputMethodService;
20 import android.os.Bundle;
21 import android.os.Handler;
22 import android.os.Parcelable;
23 import android.view.View;
24 import android.view.inputmethod.TextSnapshot;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 
29 import java.util.ArrayList;
30 import java.util.List;
31 
32 /**
33  * An immutable object that stores event happened in the {@link MockIme}.
34  */
35 public final class ImeEvent {
36 
37     private enum ReturnType {
38         Null,
39         Unavailable,
40         KnownUnsupportedType,
41         Boolean,
42         Integer,
43         Long,
44         String,
45         CharSequence,
46         Exception,
47         Parcelable,
48         List
49     }
50 
51     /**
52      * A special placeholder object that represents that return value information is not available.
53      */
54     static final Object RETURN_VALUE_UNAVAILABLE = new Object();
55 
getReturnTypeFromObject(@ullable Object object)56     private static ReturnType getReturnTypeFromObject(@Nullable Object object) {
57         if (object == null) {
58             return ReturnType.Null;
59         }
60         if (object == RETURN_VALUE_UNAVAILABLE) {
61             return ReturnType.Unavailable;
62         }
63         if (object instanceof AbstractInputMethodService.AbstractInputMethodImpl) {
64             return ReturnType.KnownUnsupportedType;
65         }
66         if (object instanceof View) {
67             return ReturnType.KnownUnsupportedType;
68         }
69         if (object instanceof Handler) {
70             return ReturnType.KnownUnsupportedType;
71         }
72         if (object instanceof TextSnapshot) {
73             return ReturnType.KnownUnsupportedType;
74         }
75         if (object instanceof Boolean) {
76             return ReturnType.Boolean;
77         }
78         if (object instanceof Integer) {
79             return ReturnType.Integer;
80         }
81         if (object instanceof Long) {
82             return ReturnType.Long;
83         }
84         if (object instanceof String) {
85             return ReturnType.String;
86         }
87         if (object instanceof CharSequence) {
88             return ReturnType.CharSequence;
89         }
90         if (object instanceof Exception) {
91             return ReturnType.Exception;
92         }
93         if (object instanceof Parcelable) {
94             return ReturnType.Parcelable;
95         }
96         if (object instanceof List) {
97             return ReturnType.List;
98         }
99         throw new UnsupportedOperationException("Unsupported return type=" + object);
100     }
101 
ImeEvent(@onNull String eventName, int nestLevel, @NonNull String threadName, int threadId, boolean isMainThread, long enterTimestamp, long exitTimestamp, long enterWallTime, long exitWallTime, @NonNull ImeState enterState, @Nullable ImeState exitState, @NonNull Bundle arguments, @Nullable Object returnValue)102     ImeEvent(@NonNull String eventName, int nestLevel, @NonNull String threadName, int threadId,
103             boolean isMainThread, long enterTimestamp, long exitTimestamp, long enterWallTime,
104             long exitWallTime, @NonNull ImeState enterState, @Nullable ImeState exitState,
105             @NonNull Bundle arguments, @Nullable Object returnValue) {
106         this(eventName, nestLevel, threadName, threadId, isMainThread, enterTimestamp,
107                 exitTimestamp, enterWallTime, exitWallTime, enterState, exitState, arguments,
108                 returnValue, getReturnTypeFromObject(returnValue));
109     }
110 
ImeEvent(@onNull String eventName, int nestLevel, @NonNull String threadName, int threadId, boolean isMainThread, long enterTimestamp, long exitTimestamp, long enterWallTime, long exitWallTime, @NonNull ImeState enterState, @Nullable ImeState exitState, @NonNull Bundle arguments, @Nullable Object returnValue, @NonNull ReturnType returnType)111     private ImeEvent(@NonNull String eventName, int nestLevel, @NonNull String threadName,
112             int threadId, boolean isMainThread, long enterTimestamp, long exitTimestamp,
113             long enterWallTime, long exitWallTime, @NonNull ImeState enterState,
114             @Nullable ImeState exitState, @NonNull Bundle arguments, @Nullable Object returnValue,
115             @NonNull ReturnType returnType) {
116         mEventName = eventName;
117         mNestLevel = nestLevel;
118         mThreadName = threadName;
119         mThreadId = threadId;
120         mIsMainThread = isMainThread;
121         mEnterTimestamp = enterTimestamp;
122         mExitTimestamp = exitTimestamp;
123         mEnterWallTime = enterWallTime;
124         mExitWallTime = exitWallTime;
125         mEnterState = enterState;
126         mExitState = exitState;
127         mArguments = arguments;
128         mReturnValue = returnValue;
129         mReturnType = returnType;
130     }
131 
132     @NonNull
toBundle()133     Bundle toBundle() {
134         final Bundle bundle = new Bundle();
135         bundle.putString("mEventName", mEventName);
136         bundle.putInt("mNestLevel", mNestLevel);
137         bundle.putString("mThreadName", mThreadName);
138         bundle.putInt("mThreadId", mThreadId);
139         bundle.putBoolean("mIsMainThread", mIsMainThread);
140         bundle.putLong("mEnterTimestamp", mEnterTimestamp);
141         bundle.putLong("mExitTimestamp", mExitTimestamp);
142         bundle.putLong("mEnterWallTime", mEnterWallTime);
143         bundle.putLong("mExitWallTime", mExitWallTime);
144         bundle.putBundle("mEnterState", mEnterState.toBundle());
145         bundle.putBundle("mExitState", mExitState != null ? mExitState.toBundle() : null);
146         bundle.putBundle("mArguments", mArguments);
147         bundle.putString("mReturnType", mReturnType.name());
148         switch (mReturnType) {
149             case Null:
150             case Unavailable:
151             case KnownUnsupportedType:
152                 break;
153             case Boolean:
154                 bundle.putBoolean("mReturnValue", getReturnBooleanValue());
155                 break;
156             case Integer:
157                 bundle.putInt("mReturnValue", getReturnIntegerValue());
158                 break;
159             case Long:
160                 bundle.putLong("mReturnValue", getReturnLongValue());
161                 break;
162             case String:
163                 bundle.putString("mReturnValue", getReturnStringValue());
164                 break;
165             case CharSequence:
166                 bundle.putCharSequence("mReturnValue", getReturnCharSequenceValue());
167                 break;
168             case Exception:
169                 bundle.putSerializable("mReturnValue", getReturnExceptionValue());
170                 break;
171             case Parcelable:
172                 bundle.putParcelable("mReturnValue", getReturnParcelableValue());
173                 break;
174             case List:
175                 bundle.putParcelableArrayList("mReturnValue", getReturnParcelableArrayListValue());
176                 break;
177             default:
178                 throw new UnsupportedOperationException("Unsupported type=" + mReturnType);
179         }
180         return bundle;
181     }
182 
183     @NonNull
fromBundle(@onNull Bundle bundle)184     static ImeEvent fromBundle(@NonNull Bundle bundle) {
185         final String eventName = bundle.getString("mEventName");
186         final int nestLevel = bundle.getInt("mNestLevel");
187         final String threadName = bundle.getString("mThreadName");
188         final int threadId = bundle.getInt("mThreadId");
189         final boolean isMainThread = bundle.getBoolean("mIsMainThread");
190         final long enterTimestamp = bundle.getLong("mEnterTimestamp");
191         final long exitTimestamp = bundle.getLong("mExitTimestamp");
192         final long enterWallTime = bundle.getLong("mEnterWallTime");
193         final long exitWallTime = bundle.getLong("mExitWallTime");
194         final ImeState enterState = ImeState.fromBundle(bundle.getBundle("mEnterState"));
195         final ImeState exitState = ImeState.fromBundle(bundle.getBundle("mExitState"));
196         final Bundle arguments = bundle.getBundle("mArguments");
197         final Object result;
198         final ReturnType returnType = ReturnType.valueOf(bundle.getString("mReturnType"));
199         switch (returnType) {
200             case Null:
201             case Unavailable:
202             case KnownUnsupportedType:
203                 result = null;
204                 break;
205             case Boolean:
206                 result = bundle.getBoolean("mReturnValue");
207                 break;
208             case Integer:
209                 result = bundle.getInt("mReturnValue");
210                 break;
211             case Long:
212                 result = bundle.getLong("mReturnValue");
213                 break;
214             case String:
215                 result = bundle.getString("mReturnValue");
216                 break;
217             case CharSequence:
218                 result = bundle.getCharSequence("mReturnValue");
219                 break;
220             case Exception:
221                 result = bundle.getSerializable("mReturnValue");
222                 break;
223             case Parcelable:
224                 result = bundle.getParcelable("mReturnValue");
225                 break;
226             case List:
227                 result = bundle.getParcelableArrayList("mReturnValue");
228                 break;
229             default:
230                 throw new UnsupportedOperationException("Unsupported type=" + returnType);
231         }
232         return new ImeEvent(eventName, nestLevel, threadName,
233                 threadId, isMainThread, enterTimestamp, exitTimestamp, enterWallTime, exitWallTime,
234                 enterState, exitState, arguments, result, returnType);
235     }
236 
237     /**
238      * Returns a string that represents the type of this event.
239      *
240      * <p>Examples: &quot;onCreate&quot;, &quot;onStartInput&quot;, ...</p>
241      *
242      * <p>TODO: Use enum type or something like that instead of raw String type.</p>
243      * @return A string that represents the type of this event.
244      */
245     @NonNull
getEventName()246     public String getEventName() {
247         return mEventName;
248     }
249 
250     /**
251      * Returns the nest level of this event.
252      *
253      * <p>For instance, when &quot;showSoftInput&quot; internally calls
254      * &quot;onStartInputView&quot;, the event for &quot;onStartInputView&quot; has 1 level higher
255      * nest level than &quot;showSoftInput&quot;.</p>
256      */
getNestLevel()257     public int getNestLevel() {
258         return mNestLevel;
259     }
260 
261     /**
262      * @return Name of the thread, where the event was consumed.
263      */
264     @NonNull
getThreadName()265     public String getThreadName() {
266         return mThreadName;
267     }
268 
269     /**
270      * @return Thread ID (TID) of the thread, where the event was consumed.
271      */
getThreadId()272     public int getThreadId() {
273         return mThreadId;
274     }
275 
276     /**
277      * @return {@code true} if the event was being consumed in the main thread.
278      */
isMainThread()279     public boolean isMainThread() {
280         return mIsMainThread;
281     }
282 
283     /**
284      * @return Monotonic time measured by {@link android.os.SystemClock#elapsedRealtimeNanos()} when
285      *         the corresponding event handler was called back.
286      */
getEnterTimestamp()287     public long getEnterTimestamp() {
288         return mEnterTimestamp;
289     }
290 
291     /**
292      * @return Monotonic time measured by {@link android.os.SystemClock#elapsedRealtimeNanos()} when
293      *         the corresponding event handler finished.
294      */
getExitTimestamp()295     public long getExitTimestamp() {
296         return mExitTimestamp;
297     }
298 
299     /**
300      * @return Wall-clock time measured by {@link System#currentTimeMillis()} when the corresponding
301      *         event handler was called back.
302      */
getEnterWallTime()303     public long getEnterWallTime() {
304         return mEnterWallTime;
305     }
306 
307     /**
308      * @return Wall-clock time measured by {@link System#currentTimeMillis()} when the corresponding
309      *         event handler finished.
310      */
getExitWallTime()311     public long getExitWallTime() {
312         return mExitWallTime;
313     }
314 
315     /**
316      * @return IME state snapshot taken when the corresponding event handler was called back.
317      */
318     @NonNull
getEnterState()319     public ImeState getEnterState() {
320         return mEnterState;
321     }
322 
323     /**
324      * @return IME state snapshot taken when the corresponding event handler finished.
325      */
326     @Nullable
getExitState()327     public ImeState getExitState() {
328         return mExitState;
329     }
330 
331     /**
332      * @return {@link Bundle} that stores parameters passed to the corresponding event handler.
333      */
334     @NonNull
getArguments()335     public Bundle getArguments() {
336         return mArguments;
337     }
338 
339     /**
340      * @return result value of this event.
341      * @throws NullPointerException if the return value is {@code null}
342      * @throws ClassCastException if the return value is non-{@code null} object that is different
343      *                            from {@link Boolean}
344      */
getReturnBooleanValue()345     public boolean getReturnBooleanValue() {
346         if (mReturnType == ReturnType.Null) {
347             throw new NullPointerException();
348         }
349         if (mReturnType != ReturnType.Boolean) {
350             throw new ClassCastException();
351         }
352         return (Boolean) mReturnValue;
353     }
354 
355     /**
356      * @return result value of this event.
357      * @throws NullPointerException if the return value is {@code null}
358      * @throws ClassCastException if the return value is non-{@code null} object that is different
359      *                            from {@link Integer}
360      */
getReturnIntegerValue()361     public int getReturnIntegerValue() {
362         if (mReturnType == ReturnType.Null) {
363             throw new NullPointerException();
364         }
365         if (mReturnType != ReturnType.Integer) {
366             throw new ClassCastException();
367         }
368         return (Integer) mReturnValue;
369     }
370 
371     /**
372      * @return result value of this event.
373      * @throws NullPointerException if the return value is {@code null}
374      * @throws ClassCastException if the return value is non-{@code null} object that is different
375      *                            from {@link Long}
376      */
getReturnLongValue()377     public long getReturnLongValue() {
378         if (mReturnType == ReturnType.Null) {
379             throw new NullPointerException();
380         }
381         if (mReturnType != ReturnType.Long) {
382             throw new ClassCastException();
383         }
384         return (Long) mReturnValue;
385     }
386 
387     /**
388      * @return result value of this event.
389      * @throws NullPointerException if the return value is {@code null}
390      * @throws ClassCastException if the return value is non-{@code null} object that does not
391      *                            implement {@link CharSequence}
392      */
getReturnCharSequenceValue()393     public CharSequence getReturnCharSequenceValue() {
394         if (mReturnType == ReturnType.Null) {
395             throw new NullPointerException();
396         }
397         if (mReturnType == ReturnType.CharSequence || mReturnType == ReturnType.String
398                 || mReturnType == ReturnType.Parcelable) {
399             return (CharSequence) mReturnValue;
400         }
401         throw new ClassCastException();
402     }
403 
404     /**
405      * @return result value of this event.
406      * @throws NullPointerException if the return value is {@code null}
407      * @throws ClassCastException if the return value is non-{@code null} object that is different
408      *                            from {@link String}
409      */
getReturnStringValue()410     public String getReturnStringValue() {
411         if (mReturnType == ReturnType.Null) {
412             throw new NullPointerException();
413         }
414         if (mReturnType != ReturnType.String) {
415             throw new ClassCastException();
416         }
417         return (String) mReturnValue;
418     }
419 
420      /**
421       * Retrieves a result that is known to be {@link Exception} or its subclasses.
422       *
423       * @param <T> {@link Exception} or its subclass.
424       * @return {@link Exception} object returned as a result of the command.
425       * @throws NullPointerException if the return value is {@code null}
426       * @throws ClassCastException if the return value is non-{@code null} object that is different
427       *                            from {@link Exception}
428      */
getReturnExceptionValue()429     public <T extends Exception> T getReturnExceptionValue() {
430         if (mReturnType == ReturnType.Null) {
431             throw new NullPointerException();
432         }
433         if (mReturnType != ReturnType.Exception) {
434             throw new ClassCastException();
435         }
436         return (T) mReturnValue;
437     }
438 
439     /**
440      * @return result value of this event.
441      * @throws NullPointerException if the return value is {@code null}
442      * @throws ClassCastException if the return value is non-{@code null} object that is different
443      *                            from {@link Parcelable}
444      */
getReturnParcelableValue()445     public <T extends Parcelable> T getReturnParcelableValue() {
446         if (mReturnType == ReturnType.Null) {
447             throw new NullPointerException();
448         }
449         if (mReturnType != ReturnType.Parcelable) {
450             throw new ClassCastException();
451         }
452         return (T) mReturnValue;
453     }
454 
455     /**
456      * @return result value of this event.
457      * @throws NullPointerException if the return value is {@code null}
458      * @throws ClassCastException if the return value is non-{@code null} object that is different
459      *                            from {@link ArrayList<? extends Parcelable>}
460      */
getReturnParcelableArrayListValue()461     public <T extends Parcelable> ArrayList<T> getReturnParcelableArrayListValue() {
462         if (mReturnType == ReturnType.Null) {
463             throw new NullPointerException();
464         }
465         if (mReturnType != ReturnType.List) {
466             throw new ClassCastException();
467         }
468         return (ArrayList<T>) mReturnValue;
469     }
470 
471     /**
472      * @return {@code true} when the result value is an {@link Exception}.
473      */
isExceptionReturnValue()474     public boolean isExceptionReturnValue() {
475         return mReturnType == ReturnType.Exception;
476     }
477 
478     /**
479      * @return {@code true} when the result value is {@code null}.
480      */
isNullReturnValue()481     public boolean isNullReturnValue() {
482         return mReturnType == ReturnType.Null;
483     }
484 
485     /**
486      * @return {@code true} if the event is issued when the event starts, not when the event
487      * finishes.
488      */
isEnterEvent()489     public boolean isEnterEvent() {
490         return mExitState == null;
491     }
492 
493     @NonNull
494     private final String mEventName;
495     private final int mNestLevel;
496     @NonNull
497     private final String mThreadName;
498     private final int mThreadId;
499     private final boolean mIsMainThread;
500     private final long mEnterTimestamp;
501     private final long mExitTimestamp;
502     private final long mEnterWallTime;
503     private final long mExitWallTime;
504     @NonNull
505     private final ImeState mEnterState;
506     @Nullable
507     private final ImeState mExitState;
508     @NonNull
509     private final Bundle mArguments;
510     @Nullable
511     private final Object mReturnValue;
512     @NonNull
513     private final ReturnType mReturnType;
514 }
515