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 android.view.inputmethod.cts.util; 18 19 import static org.junit.Assert.fail; 20 import static org.junit.Assume.assumeFalse; 21 import static org.junit.Assume.assumeTrue; 22 23 import android.Manifest; 24 import android.app.ActivityManager; 25 import android.app.ActivityTaskManager; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.content.pm.PackageManager; 29 import android.content.res.Resources; 30 import android.os.SystemClock; 31 import android.platform.test.annotations.AppModeFull; 32 import android.platform.test.annotations.AppModeInstant; 33 34 import androidx.test.platform.app.InstrumentationRegistry; 35 36 import com.android.compatibility.common.util.CtsTouchUtils; 37 import com.android.compatibility.common.util.SystemUtil; 38 39 import org.junit.After; 40 import org.junit.AfterClass; 41 import org.junit.Before; 42 import org.junit.BeforeClass; 43 import org.junit.Rule; 44 import org.junit.rules.TestName; 45 46 import java.lang.reflect.Method; 47 import java.util.List; 48 49 public class EndToEndImeTestBase { 50 51 @Rule 52 public TestName mTestName = new TestName(); 53 54 protected final CtsTouchUtils mCtsTouchUtils = new CtsTouchUtils( 55 InstrumentationRegistry.getInstrumentation().getTargetContext()); 56 57 /** Returns a unique marker based on the concrete class name and elapsed time. */ createUniqueMarker()58 protected String createUniqueMarker() { 59 return getClass().getName() + "/" + SystemClock.elapsedRealtimeNanos(); 60 } 61 62 /** Command to get verbose ImeTracker logging state. */ 63 private static final String GET_VERBOSE_IME_TRACKER_LOGGING_CMD = 64 "getprop persist.debug.imetracker"; 65 66 /** Command to set verbose ImeTracker logging state. */ 67 private static final String SET_VERBOSE_IME_TRACKER_LOGGING_CMD = 68 "setprop persist.debug.imetracker"; 69 70 /** 71 * Whether verbose ImeTracker logging was enabled prior to running the tests, 72 * used to handle reverting the state when the test run ends. 73 */ 74 private static boolean sWasVerboseImeTrackerLoggingEnabled; 75 76 /** 77 * Enters touch mode when instrumenting. 78 * 79 * Making the view focus state in instrumentation process more reliable in case when 80 * {@link android.view.View#clearFocus()} invoked but system may reFocus again when the view 81 * was not in touch mode. (i.e {@link android.view.View#isInTouchMode()} is {@code false}). 82 */ 83 @Before enterTouchMode()84 public final void enterTouchMode() { 85 InstrumentationRegistry.getInstrumentation().setInTouchMode(true); 86 } 87 88 /** 89 * Restore to the default touch mode state after the test. 90 */ 91 @After restoreTouchMode()92 public final void restoreTouchMode() { 93 InstrumentationRegistry.getInstrumentation().resetInTouchMode(); 94 } 95 96 /** 97 * Our own safeguard in case "atest" command is regressed and start running tests with 98 * {@link AppModeInstant} even when {@code --instant} option is not specified. 99 * 100 * <p>Unfortunately this scenario had regressed at least 3 times. That's why we also check 101 * this in our side. See Bug 158617529, Bug 187211725 and Bug 187222205 for examples.</p> 102 */ 103 @Before verifyAppModeConsistency()104 public void verifyAppModeConsistency() { 105 final Class<?> thisClass = this.getClass(); 106 final String testMethodName = mTestName.getMethodName(); 107 final String fullTestMethodName = thisClass.getSimpleName() + "#" + testMethodName; 108 109 final Method testMethod; 110 try { 111 testMethod = thisClass.getMethod(testMethodName); 112 } catch (NoSuchMethodException e) { 113 throw new IllegalStateException("Failed to find " + fullTestMethodName, e); 114 } 115 116 final boolean hasAppModeFull = testMethod.getAnnotation(AppModeFull.class) != null; 117 final boolean hasAppModeInstant = testMethod.getAnnotation(AppModeInstant.class) != null; 118 119 if (hasAppModeFull && hasAppModeInstant) { 120 fail("Both @AppModeFull and @AppModeInstant are found in " + fullTestMethodName 121 + ", which does not make sense. " 122 + "Remove both to make it clear that this test is app-mode agnostic, " 123 + "or specify one of them otherwise."); 124 } 125 126 // We want to explicitly check this condition in case tests are executed with atest 127 // command. See Bug 158617529 for details. 128 if (hasAppModeFull) { 129 assumeFalse("This test should run under and only under the full app mode.", 130 InstrumentationRegistry.getInstrumentation().getTargetContext() 131 .getPackageManager().isInstantApp()); 132 } 133 if (hasAppModeInstant) { 134 assumeTrue("This test should run under and only under the instant app mode.", 135 InstrumentationRegistry.getInstrumentation().getTargetContext() 136 .getPackageManager().isInstantApp()); 137 } 138 } 139 140 @Before showStateInitializeActivity()141 public void showStateInitializeActivity() { 142 // TODO(b/37502066): Move this back to @BeforeClass once b/37502066 is fixed. 143 assumeTrue("MockIme cannot be used for devices that do not support installable IMEs", 144 InstrumentationRegistry.getInstrumentation().getContext().getPackageManager() 145 .hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS)); 146 147 final Intent intent = new Intent() 148 .setAction(Intent.ACTION_MAIN) 149 .setClass(InstrumentationRegistry.getInstrumentation().getTargetContext(), 150 StateInitializeActivity.class) 151 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 152 .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) 153 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); 154 InstrumentationRegistry.getInstrumentation().startActivitySync(intent); 155 } 156 157 @Before clearLaunchParams()158 public void clearLaunchParams() { 159 final Context context = InstrumentationRegistry.getInstrumentation().getContext(); 160 final ActivityTaskManager atm = context.getSystemService(ActivityTaskManager.class); 161 SystemUtil.runWithShellPermissionIdentity(() -> { 162 // Clear launch params for all test packages to make sure each test is run in a clean 163 // state. 164 atm.clearLaunchParamsForPackages(List.of(context.getPackageName())); 165 }, Manifest.permission.MANAGE_ACTIVITY_TASKS); 166 } 167 isPreventImeStartup()168 protected static boolean isPreventImeStartup() { 169 final Context context = InstrumentationRegistry.getInstrumentation().getContext(); 170 try { 171 return context.getResources().getBoolean( 172 android.R.bool.config_preventImeStartupUnlessTextEditor); 173 } catch (Resources.NotFoundException e) { 174 // Assume this is not enabled. 175 return false; 176 } 177 } 178 179 /** 180 * Enables verbose logging in {@link android.view.inputmethod.ImeTracker}. 181 */ 182 @BeforeClass enableVerboseImeTrackerLogging()183 public static void enableVerboseImeTrackerLogging() { 184 sWasVerboseImeTrackerLoggingEnabled = getVerboseImeTrackerLogging(); 185 if (!sWasVerboseImeTrackerLoggingEnabled) { 186 setVerboseImeTrackerLogging(true); 187 } 188 } 189 190 /** 191 * Reverts verbose logging in {@link android.view.inputmethod.ImeTracker} to the previous value. 192 */ 193 @AfterClass revertVerboseImeTrackerLogging()194 public static void revertVerboseImeTrackerLogging() { 195 if (!sWasVerboseImeTrackerLoggingEnabled) { 196 setVerboseImeTrackerLogging(false); 197 } 198 } 199 200 /** 201 * Gets the verbose logging state in {@link android.view.inputmethod.ImeTracker}. 202 * 203 * @return {@code true} iff verbose logging is enabled. 204 */ getVerboseImeTrackerLogging()205 private static boolean getVerboseImeTrackerLogging() { 206 return SystemUtil.runShellCommand(GET_VERBOSE_IME_TRACKER_LOGGING_CMD).trim().equals("1"); 207 } 208 209 /** 210 * Sets verbose logging in {@link android.view.inputmethod.ImeTracker}. 211 * 212 * @param enabled whether to enable or disable verbose logging. 213 * 214 * @implNote This must use {@link ActivityManager#notifySystemPropertiesChanged()} to listen 215 * for changes to the system property for the verbose ImeTracker logging. 216 */ setVerboseImeTrackerLogging(boolean enabled)217 private static void setVerboseImeTrackerLogging(boolean enabled) { 218 final var context = InstrumentationRegistry.getInstrumentation().getContext(); 219 final var am = context.getSystemService(ActivityManager.class); 220 221 SystemUtil.runShellCommand( 222 SET_VERBOSE_IME_TRACKER_LOGGING_CMD + " " + (enabled ? "1" : "0")); 223 am.notifySystemPropertiesChanged(); 224 } 225 } 226