1 /* 2 * Copyright (C) 2020 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 package android.view.contentcapture; 17 18 import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_CUSTOM_VIEWS; 19 import static android.view.contentcapture.CustomTestActivity.INTENT_EXTRA_LAYOUT_ID; 20 21 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; 22 23 import static com.android.compatibility.common.util.ShellUtils.runShellCommand; 24 25 import android.app.Activity; 26 import android.app.Application; 27 import android.app.Instrumentation; 28 import android.content.ContentCaptureOptions; 29 import android.content.Context; 30 import android.content.Intent; 31 import android.os.BatteryManager; 32 import android.os.UserHandle; 33 import android.perftests.utils.PerfStatusReporter; 34 import android.perftests.utils.PerfTestActivity; 35 import android.provider.Settings; 36 import android.util.Log; 37 38 import androidx.annotation.NonNull; 39 40 import com.android.compatibility.common.util.ActivitiesWatcher; 41 import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher; 42 import com.android.perftests.contentcapture.R; 43 44 import org.junit.After; 45 import org.junit.AfterClass; 46 import org.junit.Before; 47 import org.junit.BeforeClass; 48 import org.junit.Rule; 49 import org.junit.rules.TestRule; 50 import org.junit.runners.model.Statement; 51 52 /** 53 * Base class for all content capture tests. 54 */ 55 public abstract class AbstractContentCapturePerfTestCase { 56 57 private static final String TAG = AbstractContentCapturePerfTestCase.class.getSimpleName(); 58 protected static final long GENERIC_TIMEOUT_MS = 5_000; 59 60 private static int sOriginalStayOnWhilePluggedIn; 61 protected static final Instrumentation sInstrumentation = getInstrumentation(); 62 protected static final Context sContext = sInstrumentation.getTargetContext(); 63 64 protected ActivitiesWatcher mActivitiesWatcher; 65 66 /** A simple activity as the task root to reduce the noise of pause and animation time. */ 67 protected Activity mEntryActivity; 68 69 private MyContentCaptureService.ServiceWatcher mServiceWatcher; 70 71 @Rule 72 public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); 73 74 @Rule 75 public TestRule mServiceDisablerRule = (base, description) -> { 76 return new Statement() { 77 @Override 78 public void evaluate() throws Throwable { 79 try { 80 base.evaluate(); 81 } finally { 82 Log.v(TAG, "@mServiceDisablerRule: safelyDisableService()"); 83 safelyDisableService(); 84 } 85 } 86 }; 87 }; 88 safelyDisableService()89 private void safelyDisableService() { 90 try { 91 resetService(); 92 MyContentCaptureService.resetStaticState(); 93 94 if (mServiceWatcher != null) { 95 mServiceWatcher.waitOnDestroy(); 96 } 97 } catch (Throwable t) { 98 Log.e(TAG, "error disabling service", t); 99 } 100 } 101 102 /** 103 * Sets the content capture service. 104 */ setService(@onNull String service)105 private static void setService(@NonNull String service) { 106 final int userId = getCurrentUserId(); 107 Log.d(TAG, "Setting service for user " + userId + " to " + service); 108 // TODO(b/123540602): use @TestingAPI to get max duration constant 109 runShellCommand("cmd content_capture set temporary-service %d %s 119000", userId, service); 110 } 111 112 /** 113 * Resets the content capture service. 114 */ resetService()115 private static void resetService() { 116 final int userId = getCurrentUserId(); 117 Log.d(TAG, "Resetting back user " + userId + " to default service"); 118 runShellCommand("cmd content_capture set temporary-service %d", userId); 119 } 120 getCurrentUserId()121 private static int getCurrentUserId() { 122 return UserHandle.myUserId(); 123 } 124 125 @BeforeClass setStayAwake()126 public static void setStayAwake() { 127 Log.v(TAG, "@BeforeClass: setStayAwake()"); 128 // Some test cases will restart the activity, and stay awake is necessary to ensure that 129 // the test will not screen off during the test. 130 // Keeping the activity screen on is not enough, screen off may occur between the activity 131 // finished and the next start 132 final int stayOnWhilePluggedIn = Settings.Global.getInt(sContext.getContentResolver(), 133 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0); 134 sOriginalStayOnWhilePluggedIn = -1; 135 if (stayOnWhilePluggedIn != BatteryManager.BATTERY_PLUGGED_ANY) { 136 sOriginalStayOnWhilePluggedIn = stayOnWhilePluggedIn; 137 // Keep the device awake during testing. 138 setStayOnWhilePluggedIn(BatteryManager.BATTERY_PLUGGED_ANY); 139 } 140 } 141 142 @AfterClass resetStayAwake()143 public static void resetStayAwake() { 144 Log.v(TAG, "@AfterClass: resetStayAwake()"); 145 if (sOriginalStayOnWhilePluggedIn != -1) { 146 setStayOnWhilePluggedIn(sOriginalStayOnWhilePluggedIn); 147 } 148 } 149 setStayOnWhilePluggedIn(int value)150 private static void setStayOnWhilePluggedIn(int value) { 151 runShellCommand(String.format("settings put global %s %d", 152 Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value)); 153 } 154 155 @BeforeClass setAllowSelf()156 public static void setAllowSelf() { 157 final ContentCaptureOptions options = new ContentCaptureOptions(null); 158 Log.v(TAG, "@BeforeClass: setAllowSelf(): options=" + options); 159 sContext.getApplicationContext().setContentCaptureOptions(options); 160 } 161 162 @AfterClass unsetAllowSelf()163 public static void unsetAllowSelf() { 164 Log.v(TAG, "@AfterClass: unsetAllowSelf()"); 165 clearOptions(); 166 } 167 clearOptions()168 protected static void clearOptions() { 169 sContext.getApplicationContext().setContentCaptureOptions(null); 170 } 171 172 @BeforeClass disableDefaultService()173 public static void disableDefaultService() { 174 Log.v(TAG, "@BeforeClass: disableDefaultService()"); 175 setDefaultServiceEnabled(false); 176 } 177 178 @AfterClass enableDefaultService()179 public static void enableDefaultService() { 180 Log.v(TAG, "@AfterClass: enableDefaultService()"); 181 setDefaultServiceEnabled(true); 182 } 183 184 /** 185 * Enables / disables the default service. 186 */ setDefaultServiceEnabled(boolean enabled)187 private static void setDefaultServiceEnabled(boolean enabled) { 188 final int userId = getCurrentUserId(); 189 Log.d(TAG, "setDefaultServiceEnabled(user=" + userId + ", enabled= " + enabled + ")"); 190 runShellCommand("cmd content_capture set default-service-enabled %d %s", userId, 191 Boolean.toString(enabled)); 192 } 193 194 @Before prepareDevice()195 public void prepareDevice() throws Exception { 196 Log.v(TAG, "@Before: prepareDevice()"); 197 198 // Unlock screen. 199 runShellCommand("input keyevent KEYCODE_WAKEUP"); 200 201 // Dismiss keyguard, in case it's set as "Swipe to unlock". 202 runShellCommand("wm dismiss-keyguard"); 203 204 // Collapse notifications. 205 runShellCommand("cmd statusbar collapse"); 206 } 207 208 @Before registerLifecycleCallback()209 public void registerLifecycleCallback() { 210 Log.v(TAG, "@Before: Registering lifecycle callback"); 211 final Application app = (Application) sContext.getApplicationContext(); 212 mActivitiesWatcher = new ActivitiesWatcher(GENERIC_TIMEOUT_MS); 213 app.registerActivityLifecycleCallbacks(mActivitiesWatcher); 214 } 215 216 @After unregisterLifecycleCallback()217 public void unregisterLifecycleCallback() { 218 Log.d(TAG, "@After: Unregistering lifecycle callback: " + mActivitiesWatcher); 219 if (mActivitiesWatcher != null) { 220 final Application app = (Application) sContext.getApplicationContext(); 221 app.unregisterActivityLifecycleCallbacks(mActivitiesWatcher); 222 } 223 } 224 225 @Before setUp()226 public void setUp() { 227 mEntryActivity = sInstrumentation.startActivitySync( 228 PerfTestActivity.createLaunchIntent(sInstrumentation.getContext())); 229 } 230 231 @After tearDown()232 public void tearDown() { 233 mEntryActivity.finishAndRemoveTask(); 234 } 235 236 /** 237 * Sets {@link MyContentCaptureService} as the service for the current user and waits until 238 * its created, then add the perf test package into allow list. 239 */ enableService()240 public MyContentCaptureService enableService() throws InterruptedException { 241 if (mServiceWatcher != null) { 242 throw new IllegalStateException("There Can Be Only One!"); 243 } 244 245 mServiceWatcher = MyContentCaptureService.setServiceWatcher(); 246 setService(MyContentCaptureService.SERVICE_NAME); 247 mServiceWatcher.setAllowSelf(); 248 return mServiceWatcher.waitOnCreate(); 249 } 250 251 /** Wait for session paused. */ waitForSessionPaused()252 public void waitForSessionPaused() throws InterruptedException { 253 mServiceWatcher.waitSessionPaused(); 254 } 255 256 @NonNull startWatcher()257 protected ActivityWatcher startWatcher() { 258 return mActivitiesWatcher.watch(CustomTestActivity.class); 259 } 260 261 /** 262 * Launch test activity with default login layout 263 */ launchActivity()264 protected CustomTestActivity launchActivity() { 265 return launchActivity(R.layout.test_login_activity, 0); 266 } 267 268 /** 269 * Returns the intent which will launch CustomTestActivity. 270 */ getLaunchIntent(int layoutId, int numViews)271 protected Intent getLaunchIntent(int layoutId, int numViews) { 272 final Intent intent = new Intent(sContext, CustomTestActivity.class) 273 // Use NEW_TASK because the context is not activity. It is still in the same task 274 // of PerfTestActivity because of the same task affinity. Use NO_ANIMATION because 275 // this test focuses on launch time instead of animation duration. 276 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION); 277 intent.putExtra(INTENT_EXTRA_LAYOUT_ID, layoutId); 278 intent.putExtra(INTENT_EXTRA_CUSTOM_VIEWS, numViews); 279 return intent; 280 } 281 282 /** 283 * Launch test activity with give layout and parameter 284 */ launchActivity(int layoutId, int numViews)285 protected CustomTestActivity launchActivity(int layoutId, int numViews) { 286 final Intent intent = getLaunchIntent(layoutId, numViews); 287 return (CustomTestActivity) sInstrumentation.startActivitySync(intent); 288 } 289 } 290