• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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