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.annotation.NonNull; 35 import androidx.test.platform.app.InstrumentationRegistry; 36 37 import com.android.bedstead.harrier.DeviceState; 38 import com.android.bedstead.harrier.annotations.AfterClass; 39 import com.android.bedstead.harrier.annotations.BeforeClass; 40 import com.android.compatibility.common.util.FeatureUtil; 41 import com.android.compatibility.common.util.SystemUtil; 42 43 import org.junit.After; 44 import org.junit.Before; 45 import org.junit.ClassRule; 46 import org.junit.Rule; 47 import org.junit.rules.TestName; 48 49 import java.lang.reflect.Method; 50 import java.util.List; 51 52 public abstract class EndToEndImeTestBase { 53 54 // Required for Bedstead annotations to take effect. 55 @ClassRule 56 @Rule 57 public static final DeviceState sDeviceState = new DeviceState(); 58 59 @Rule 60 public TestName mTestName = new TestName(); 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 /** Tag for the single EditText in the test case. */ 77 protected static final String EDIT_TEXT_TAG = "EditText"; 78 /** Tag for the initially focused EditText. */ 79 protected static final String FOCUSED_EDIT_TEXT_TAG = "focused-EditText"; 80 /** Tag for the initially unfocused EditText. */ 81 protected static final String NON_FOCUSED_EDIT_TEXT_TAG = "non-focused-EditText"; 82 /** Tag for the first EditText. */ 83 protected static final String FIRST_EDIT_TEXT_TAG = "first-EditText"; 84 /** Tag for the second EditText. */ 85 protected static final String SECOND_EDIT_TEXT_TAG = "second-EditText"; 86 87 /** 88 * Skip test executions for know broken platforms. 89 */ 90 @Before checkSupportedPlatforms()91 public final void checkSupportedPlatforms() { 92 // STOPSHIP(b/288952673): Re-enable tests for wear once it becomes stable enough. 93 assumeFalse(FeatureUtil.isWatch()); 94 } 95 96 /** 97 * Enters touch mode when instrumenting. 98 * 99 * Making the view focus state in instrumentation process more reliable in case when 100 * {@link android.view.View#clearFocus()} invoked but system may reFocus again when the view 101 * was not in touch mode. (i.e {@link android.view.View#isInTouchMode()} is {@code false}). 102 */ 103 @Before enterTouchMode()104 public final void enterTouchMode() { 105 InstrumentationRegistry.getInstrumentation().setInTouchMode(true); 106 } 107 108 /** 109 * Restore to the default touch mode state after the test. 110 */ 111 @After restoreTouchMode()112 public final void restoreTouchMode() { 113 InstrumentationRegistry.getInstrumentation().resetInTouchMode(); 114 } 115 116 /** 117 * Our own safeguard in case "atest" command is regressed and start running tests with 118 * {@link AppModeInstant} even when {@code --instant} option is not specified. 119 * 120 * <p>Unfortunately this scenario had regressed at least 3 times. That's why we also check 121 * this in our side. See Bug 158617529, Bug 187211725 and Bug 187222205 for examples.</p> 122 */ 123 @Before verifyAppModeConsistency()124 public void verifyAppModeConsistency() { 125 final Class<?> thisClass = this.getClass(); 126 final String testMethodName = mTestName.getMethodName(); 127 final String fullTestMethodName = thisClass.getSimpleName() + "#" + testMethodName; 128 129 final Method testMethod; 130 try { 131 testMethod = thisClass.getMethod(testMethodName); 132 } catch (NoSuchMethodException e) { 133 throw new IllegalStateException("Failed to find " + fullTestMethodName, e); 134 } 135 136 final boolean hasAppModeFull = testMethod.getAnnotation(AppModeFull.class) != null; 137 final boolean hasAppModeInstant = testMethod.getAnnotation(AppModeInstant.class) != null; 138 139 if (hasAppModeFull && hasAppModeInstant) { 140 fail("Both @AppModeFull and @AppModeInstant are found in " + fullTestMethodName 141 + ", which does not make sense. " 142 + "Remove both to make it clear that this test is app-mode agnostic, " 143 + "or specify one of them otherwise."); 144 } 145 146 // We want to explicitly check this condition in case tests are executed with atest 147 // command. See Bug 158617529 for details. 148 if (hasAppModeFull) { 149 assumeFalse("This test should run under and only under the full app mode.", 150 InstrumentationRegistry.getInstrumentation().getTargetContext() 151 .getPackageManager().isInstantApp()); 152 } 153 if (hasAppModeInstant) { 154 assumeTrue("This test should run under and only under the instant app mode.", 155 InstrumentationRegistry.getInstrumentation().getTargetContext() 156 .getPackageManager().isInstantApp()); 157 } 158 } 159 160 @Before showStateInitializeActivity()161 public void showStateInitializeActivity() { 162 // TODO(b/37502066): Move this back to @BeforeClass once b/37502066 is fixed. 163 assumeTrue("MockIme cannot be used for devices that do not support installable IMEs", 164 InstrumentationRegistry.getInstrumentation().getContext().getPackageManager() 165 .hasSystemFeature(PackageManager.FEATURE_INPUT_METHODS)); 166 167 final Intent intent = new Intent() 168 .setAction(Intent.ACTION_MAIN) 169 .setClass(InstrumentationRegistry.getInstrumentation().getTargetContext(), 170 StateInitializeActivity.class) 171 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) 172 .addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION) 173 .addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); 174 InstrumentationRegistry.getInstrumentation().startActivitySync(intent); 175 } 176 177 @Before clearLaunchParams()178 public void clearLaunchParams() { 179 final Context context = InstrumentationRegistry.getInstrumentation().getContext(); 180 final ActivityTaskManager atm = context.getSystemService(ActivityTaskManager.class); 181 SystemUtil.runWithShellPermissionIdentity(() -> { 182 // Clear launch params for all test packages to make sure each test is run in a clean 183 // state. 184 atm.clearLaunchParamsForPackages(List.of(context.getPackageName())); 185 }, Manifest.permission.MANAGE_ACTIVITY_TASKS); 186 } 187 isPreventImeStartup()188 protected static boolean isPreventImeStartup() { 189 final Context context = InstrumentationRegistry.getInstrumentation().getContext(); 190 try { 191 return context.getResources().getBoolean( 192 android.R.bool.config_preventImeStartupUnlessTextEditor); 193 } catch (Resources.NotFoundException e) { 194 // Assume this is not enabled. 195 return false; 196 } 197 } 198 199 /** 200 * Enables verbose logging in {@link android.view.inputmethod.ImeTracker}. 201 */ 202 @BeforeClass enableVerboseImeTrackerLogging()203 public static void enableVerboseImeTrackerLogging() { 204 sWasVerboseImeTrackerLoggingEnabled = getVerboseImeTrackerLogging(); 205 if (!sWasVerboseImeTrackerLoggingEnabled) { 206 setVerboseImeTrackerLogging(true); 207 } 208 } 209 210 /** 211 * Reverts verbose logging in {@link android.view.inputmethod.ImeTracker} to the previous value. 212 */ 213 @AfterClass revertVerboseImeTrackerLogging()214 public static void revertVerboseImeTrackerLogging() { 215 if (!sWasVerboseImeTrackerLoggingEnabled) { 216 setVerboseImeTrackerLogging(false); 217 } 218 } 219 220 /** 221 * Returns a unique test marker based on the concrete class name, given tag and elapsed time. 222 * 223 * @param tag a tag describing the marker (e.g. EditText, Fence). 224 */ 225 @NonNull getTestMarker(@onNull String tag)226 protected String getTestMarker(@NonNull String tag) { 227 return getClass().getName() + "/" + tag + "/" + SystemClock.elapsedRealtimeNanos(); 228 } 229 230 /** Returns a unique test marker for an EditText. */ getTestMarker()231 protected String getTestMarker() { 232 return getTestMarker(EDIT_TEXT_TAG); 233 } 234 235 /** 236 * Gets the verbose logging state in {@link android.view.inputmethod.ImeTracker}. 237 * 238 * @return {@code true} iff verbose logging is enabled. 239 */ getVerboseImeTrackerLogging()240 private static boolean getVerboseImeTrackerLogging() { 241 return SystemUtil.runShellCommand(GET_VERBOSE_IME_TRACKER_LOGGING_CMD).trim().equals("1"); 242 } 243 244 /** 245 * Sets verbose logging in {@link android.view.inputmethod.ImeTracker}. 246 * 247 * @param enabled whether to enable or disable verbose logging. 248 * 249 * @implNote This must use {@link ActivityManager#notifySystemPropertiesChanged()} to listen 250 * for changes to the system property for the verbose ImeTracker logging. 251 */ setVerboseImeTrackerLogging(boolean enabled)252 private static void setVerboseImeTrackerLogging(boolean enabled) { 253 final var context = InstrumentationRegistry.getInstrumentation().getContext(); 254 final var am = context.getSystemService(ActivityManager.class); 255 256 SystemUtil.runShellCommand( 257 SET_VERBOSE_IME_TRACKER_LOGGING_CMD + " " + (enabled ? "1" : "0")); 258 am.notifySystemPropertiesChanged(); 259 } 260 } 261