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