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 getReturnTypeFromObject(@ullable Object object)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 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)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 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)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 toBundle()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 fromBundle(@onNull Bundle bundle)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: "onCreate", "onStartInput", ...</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 getEventName()209 public String getEventName() { 210 return mEventName; 211 } 212 213 /** 214 * Returns the nest level of this event. 215 * 216 * <p>For instance, when "showSoftInput" internally calls 217 * "onStartInputView", the event for "onStartInputView" has 1 level higher 218 * nest level than "showSoftInput".</p> 219 */ getNestLevel()220 public int getNestLevel() { 221 return mNestLevel; 222 } 223 224 /** 225 * @return Name of the thread, where the event was consumed. 226 */ 227 @NonNull getThreadName()228 public String getThreadName() { 229 return mThreadName; 230 } 231 232 /** 233 * @return Thread ID (TID) of the thread, where the event was consumed. 234 */ getThreadId()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 */ isMainThread()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 */ getEnterTimestamp()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 */ getExitTimestamp()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 */ getEnterWallTime()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 */ getExitWallTime()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 getEnterState()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 getExitState()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 getArguments()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 */ getReturnBooleanValue()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 */ getReturnIntegerValue()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 */ getReturnCharSequenceValue()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 */ getReturnStringValue()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 */ getReturnParcelableValue()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 */ isNullReturnValue()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 */ isEnterEvent()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