1 /* 2 * Copyright (C) 2019 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.uibench.microbenchmark; 18 19 import android.app.ActivityManager; 20 import android.app.HomeVisibilityListener; 21 import android.app.Instrumentation; 22 import android.app.UiAutomation; 23 import android.content.ComponentName; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.os.Bundle; 27 import android.os.SystemClock; 28 import android.platform.helpers.AbstractStandardAppHelper2; 29 import android.util.DisplayMetrics; 30 import android.view.KeyEvent; 31 import android.widget.EditText; 32 import android.widget.ListView; 33 34 import androidx.test.uiautomator.By; 35 import androidx.test.uiautomator.Direction; 36 import androidx.test.uiautomator.UiObject2; 37 import androidx.test.uiautomator.Until; 38 39 import com.android.compatibility.common.util.ShellIdentityUtils; 40 import com.android.compatibility.common.util.TestUtils; 41 42 import junit.framework.Assert; 43 44 import java.util.concurrent.atomic.AtomicBoolean; 45 46 public class UiBenchJankHelper extends AbstractStandardAppHelper2 implements IUiBenchJankHelper { 47 public static final int LONG_TIMEOUT = 5000; 48 public static final int FULL_TEST_DURATION = 25000; 49 public static final int FIND_OBJECT_TIMEOUT = 250; 50 public static final int SHORT_TIMEOUT = 2000; 51 public static final int EXPECTED_FRAMES = 100; 52 public static final int KEY_DELAY = 1000; 53 public static final String EXTRA_BITMAP_UPLOAD = "extra_bitmap_upload"; 54 public static final String EXTRA_SHOW_FAST_LANE = "extra_show_fast_lane"; 55 56 /** 57 * Only to be used for initial-fling tests, or similar cases where perf during brief experience 58 * is important. 59 */ 60 public static final int SHORT_EXPECTED_FRAMES = 30; 61 62 public static final String PACKAGE_NAME = "com.android.test.uibench"; 63 64 public static final String APP_LAUNCHER_NAME = "UiBench"; 65 66 private static final int SLOW_FLING_SPEED = 3000; // compare to UiObject2#DEFAULT_FLING_SPEED 67 68 // Main UiObject2 exercised by the test. 69 private UiObject2 mContents, mNavigation; 70 UiBenchJankHelper(Instrumentation instr)71 public UiBenchJankHelper(Instrumentation instr) { 72 super(instr); 73 } 74 75 /** {@inheritDoc} */ 76 @Override getPackage()77 public String getPackage() { 78 return PACKAGE_NAME; 79 } 80 81 /** {@inheritDoc} */ 82 @Override getLauncherName()83 public String getLauncherName() { 84 return APP_LAUNCHER_NAME; 85 } 86 87 /** {@inheritDoc} */ 88 @Override dismissInitialDialogs()89 public void dismissInitialDialogs() {} 90 91 /** Launch activity using intent */ launchActivity(String activityName, Bundle extras, String verifyText)92 void launchActivity(String activityName, Bundle extras, String verifyText) { 93 ComponentName cn = 94 new ComponentName(PACKAGE_NAME, String.format("%s.%s", PACKAGE_NAME, activityName)); 95 Intent intent = new Intent(Intent.ACTION_MAIN); 96 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 97 intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK); 98 if (extras != null) { 99 intent.putExtras(extras); 100 } 101 intent.setComponent(cn); 102 // Launch the activity 103 mInstrumentation.getContext().startActivity(intent); 104 UiObject2 expectedTextCmp = 105 mDevice.wait(Until.findObject(By.text(verifyText)), LONG_TIMEOUT); 106 Assert.assertNotNull(String.format("Issue in opening %s", activityName), expectedTextCmp); 107 } 108 launchActivity(String activityName, String verifyText)109 void launchActivity(String activityName, String verifyText) { 110 final UiAutomation uiAutomation = mInstrumentation.getUiAutomation(); 111 uiAutomation.adoptShellPermissionIdentity(); 112 try { 113 launchActivity(activityName, null, verifyText); 114 } finally { 115 uiAutomation.dropShellPermissionIdentity(); 116 } 117 } 118 launchActivityAndAssert(String activityName, String verifyText)119 void launchActivityAndAssert(String activityName, String verifyText) { 120 launchActivity(activityName, verifyText); 121 mContents = 122 mDevice.wait(Until.findObject(By.res("android", "content")), FIND_OBJECT_TIMEOUT); 123 Assert.assertNotNull(activityName + " isn't found", mContents); 124 } 125 getEdgeSensitivity()126 int getEdgeSensitivity() { 127 int resId = 128 mInstrumentation 129 .getContext() 130 .getResources() 131 .getIdentifier("config_backGestureInset", "dimen", "android"); 132 return mInstrumentation.getContext().getResources().getDimensionPixelSize(resId) + 1; 133 } 134 135 /** To perform the fling down and up on given content for flingCount number of times */ 136 @Override flingUpDown(int flingCount)137 public void flingUpDown(int flingCount) { 138 flingUpDown(flingCount, false); 139 } 140 141 @Override flingDownUp(int flingCount)142 public void flingDownUp(int flingCount) { 143 flingUpDown(flingCount, true); 144 } 145 flingUpDown(int flingCount, boolean reverse)146 void flingUpDown(int flingCount, boolean reverse) { 147 mContents.setGestureMargin(getEdgeSensitivity()); 148 for (int count = 0; count < flingCount; count++) { 149 SystemClock.sleep(SHORT_TIMEOUT); 150 mContents.fling(reverse ? Direction.UP : Direction.DOWN); 151 SystemClock.sleep(SHORT_TIMEOUT); 152 mContents.fling(reverse ? Direction.DOWN : Direction.UP); 153 } 154 } 155 156 /** To perform the swipe right and left on given content for swipeCount number of times */ 157 @Override swipeRightLeft(int swipeCount)158 public void swipeRightLeft(int swipeCount) { 159 mNavigation = 160 mDevice.wait( 161 Until.findObject(By.desc("Open navigation drawer")), FIND_OBJECT_TIMEOUT); 162 mContents.setGestureMargin(getEdgeSensitivity()); 163 for (int count = 0; count < swipeCount; count++) { 164 SystemClock.sleep(SHORT_TIMEOUT); 165 mNavigation.click(); 166 SystemClock.sleep(SHORT_TIMEOUT); 167 mContents.swipe(Direction.LEFT, 1); 168 } 169 } 170 171 @Override slowSingleFlingDown()172 public void slowSingleFlingDown() { 173 SystemClock.sleep(SHORT_TIMEOUT); 174 Context context = mInstrumentation.getContext(); 175 DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); 176 mContents.setGestureMargin(getEdgeSensitivity()); 177 mContents.fling(Direction.DOWN, (int) (SLOW_FLING_SPEED * displayMetrics.density)); 178 mDevice.waitForIdle(); 179 } 180 181 @Override openDialogList()182 public void openDialogList() { 183 launchActivity("DialogListActivity", "Dialog"); 184 mContents = mDevice.wait(Until.findObject(By.clazz(ListView.class)), FIND_OBJECT_TIMEOUT); 185 Assert.assertNotNull("Dialog List View isn't found", mContents); 186 } 187 188 @Override openFullscreenOverdraw()189 public void openFullscreenOverdraw() { 190 launchActivity("FullscreenOverdrawActivity", "General/Fullscreen Overdraw"); 191 } 192 193 @Override openGLTextureView()194 public void openGLTextureView() { 195 launchActivity("GlTextureViewActivity", "General/GL TextureView"); 196 } 197 198 @Override openInvalidate()199 public void openInvalidate() { 200 launchActivity("InvalidateActivity", "General/Invalidate"); 201 } 202 203 @Override openInvalidateTree()204 public void openInvalidateTree() { 205 launchActivity("InvalidateTreeActivity", "General/Invalidate Tree"); 206 } 207 208 @Override openTrivialAnimation()209 public void openTrivialAnimation() { 210 launchActivity("TrivialAnimationActivity", "General/Trivial Animation"); 211 } 212 213 @Override openTrivialListView()214 public void openTrivialListView() { 215 launchActivityAndAssert("TrivialListActivity", "General/Trivial ListView"); 216 } 217 218 @Override openFadingEdgeListView()219 public void openFadingEdgeListView() { 220 launchActivityAndAssert("FadingEdgeListActivity", "General/Fading Edge ListView"); 221 } 222 223 @Override openSaveLayerInterleaveActivity()224 public void openSaveLayerInterleaveActivity() { 225 launchActivityAndAssert("SaveLayerInterleaveActivity", "General/SaveLayer Animation"); 226 } 227 228 @Override openTrivialRecyclerView()229 public void openTrivialRecyclerView() { 230 launchActivityAndAssert("TrivialRecyclerViewActivity", "General/Trivial RecyclerView"); 231 } 232 233 @Override openSlowBindRecyclerView()234 public void openSlowBindRecyclerView() { 235 launchActivityAndAssert("SlowBindRecyclerViewActivity", "General/Slow Bind RecyclerView"); 236 } 237 238 @Override openSlowNestedRecyclerView()239 public void openSlowNestedRecyclerView() { 240 launchActivityAndAssert( 241 "SlowNestedRecyclerViewActivity", "General/Slow Nested RecyclerView"); 242 } 243 244 @Override openInflatingListView()245 public void openInflatingListView() { 246 launchActivityAndAssert("InflatingListActivity", "Inflation/Inflating ListView"); 247 } 248 249 @Override openInflatingEmojiListView()250 public void openInflatingEmojiListView() { 251 launchActivityAndAssert( 252 "InflatingEmojiListActivity", "Inflation/Inflating ListView with Emoji"); 253 } 254 255 @Override openInflatingHanListView()256 public void openInflatingHanListView() { 257 launchActivityAndAssert( 258 "InflatingHanListActivity", "Inflation/Inflating ListView with Han Characters"); 259 } 260 261 @Override openInflatingLongStringListView()262 public void openInflatingLongStringListView() { 263 launchActivityAndAssert( 264 "InflatingLongStringListActivity", "Inflation/Inflating ListView with long string"); 265 } 266 267 @Override openNavigationDrawerActivity()268 public void openNavigationDrawerActivity() { 269 launchActivityAndAssert("NavigationDrawerActivity", "Navigation Drawer Activity"); 270 mContents.setGestureMargins(0, 0, 10, 0); 271 } 272 273 @Override openNotificationShade()274 public void openNotificationShade() { 275 launchActivityAndAssert("NotificationShadeActivity", "Notification Shade"); 276 } 277 278 @Override openResizeHWLayer()279 public void openResizeHWLayer() { 280 launchActivity("ResizeHWLayerActivity", "General/Resize HW Layer"); 281 } 282 283 @Override openClippedListView()284 public void openClippedListView() { 285 launchActivityAndAssert("ClippedListActivity", "General/Clipped ListView"); 286 } 287 288 @Override openLeanbackActivity( boolean extraBitmapUpload, boolean extraShowFastLane, String activityName, String expectedText)289 public void openLeanbackActivity( 290 boolean extraBitmapUpload, 291 boolean extraShowFastLane, 292 String activityName, 293 String expectedText) { 294 Bundle extrasBundle = new Bundle(); 295 extrasBundle.putBoolean(EXTRA_BITMAP_UPLOAD, extraBitmapUpload); 296 extrasBundle.putBoolean(EXTRA_SHOW_FAST_LANE, extraShowFastLane); 297 launchActivity(activityName, extrasBundle, expectedText); 298 } 299 pressKeyCode(int keyCode)300 void pressKeyCode(int keyCode) { 301 SystemClock.sleep(KEY_DELAY); 302 mDevice.pressKeyCode(keyCode); 303 } 304 305 @Override scrollDownAndUp(int count)306 public void scrollDownAndUp(int count) { 307 for (int i = 0; i < count; i++) { 308 pressKeyCode(KeyEvent.KEYCODE_DPAD_DOWN); 309 } 310 for (int i = 0; i < count; i++) { 311 pressKeyCode(KeyEvent.KEYCODE_DPAD_UP); 312 } 313 } 314 315 // Open Bitmap Upload 316 @Override openBitmapUpload()317 public void openBitmapUpload() { 318 launchActivity("BitmapUploadActivity", "Rendering/Bitmap Upload"); 319 } 320 321 // Open Shadow Grid 322 @Override openRenderingList()323 public void openRenderingList() { 324 launchActivity("ShadowGridActivity", "Rendering/Shadow Grid"); 325 mContents = mDevice.wait(Until.findObject(By.clazz(ListView.class)), FIND_OBJECT_TIMEOUT); 326 Assert.assertNotNull("Shadow Grid list isn't found", mContents); 327 } 328 329 // Open EditText Typing 330 @Override openEditTextTyping()331 public void openEditTextTyping() { 332 launchActivity("EditTextTypeActivity", "Text/EditText Typing"); 333 mContents = mDevice.wait(Until.findObject(By.clazz(EditText.class)), FIND_OBJECT_TIMEOUT); 334 } 335 336 // Open Layout Cache High Hitrate 337 @Override openLayoutCacheHighHitrate()338 public void openLayoutCacheHighHitrate() { 339 launchActivity("TextCacheHighHitrateActivity", "Text/Layout Cache High Hitrate"); 340 mContents = mDevice.wait(Until.findObject(By.clazz(ListView.class)), FIND_OBJECT_TIMEOUT); 341 Assert.assertNotNull("LayoutCacheHighHitrateContents isn't found", mContents); 342 } 343 344 // Open Layout Cache Low Hitrate 345 @Override openLayoutCacheLowHitrate()346 public void openLayoutCacheLowHitrate() { 347 launchActivity("TextCacheLowHitrateActivity", "Text/Layout Cache Low Hitrate"); 348 mContents = mDevice.wait(Until.findObject(By.clazz(ListView.class)), FIND_OBJECT_TIMEOUT); 349 Assert.assertNotNull("LayoutCacheLowHitrateContents isn't found", mContents); 350 } 351 352 // Open Transitions 353 @Override openActivityTransition()354 public void openActivityTransition() { 355 launchActivity("ActivityTransition", "Transitions/Activity Transition"); 356 } 357 358 @Override openWindowInsetsController()359 public void openWindowInsetsController() { 360 launchActivityAndAssert("WindowInsetsControllerActivity", "WindowInsetsControllerActivity"); 361 } 362 363 // Get the image to click 364 @Override clickImage(String imageName)365 public void clickImage(String imageName) { 366 UiObject2 image = 367 mDevice.wait( 368 Until.findObject(By.res(PACKAGE_NAME, imageName)), FIND_OBJECT_TIMEOUT); 369 Assert.assertNotNull(imageName + "Image not found", image); 370 image.clickAndWait(Until.newWindow(), FIND_OBJECT_TIMEOUT); 371 mDevice.pressBack(); 372 } 373 374 // Open Scrollable WebView from WebView test 375 @Override openScrollableWebView()376 public void openScrollableWebView() { 377 launchActivity("ScrollableWebViewActivity", "WebView/Scrollable WebView"); 378 mContents = 379 mDevice.wait(Until.findObject(By.res("android", "content")), FIND_OBJECT_TIMEOUT); 380 } 381 382 // Exit the app and ensure going back to home successfully 383 @Override exit()384 public void exit() { 385 final ActivityManager activityManager = 386 mInstrumentation.getContext().getSystemService(ActivityManager.class); 387 final AtomicBoolean isHomeVisible = new AtomicBoolean(); 388 mDevice.pressHome(); 389 mDevice.waitForIdle(); 390 HomeVisibilityListener homeVisibilityListener = 391 new HomeVisibilityListener() { 392 @Override 393 public void onHomeVisibilityChanged(boolean isHomeActivityVisible) { 394 isHomeVisible.set(isHomeActivityVisible); 395 } 396 }; 397 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( 398 activityManager, 399 (am) -> am.addHomeVisibilityListener(Runnable::run, homeVisibilityListener)); 400 try { 401 TestUtils.waitUntil("Failed to exit the app to launcher", isHomeVisible::get); 402 } catch (Exception e) { 403 throw new RuntimeException(e); 404 } finally { 405 ShellIdentityUtils.invokeMethodWithShellPermissionsNoReturn( 406 activityManager, 407 (am) -> am.removeHomeVisibilityListener(homeVisibilityListener)); 408 } 409 } 410 } 411