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