1 /* 2 * Copyright (C) 2018 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.server.wm; 18 19 import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 20 import static android.os.SystemClock.sleep; 21 import static android.server.wm.ActivityLauncher.KEY_LAUNCH_ACTIVITY; 22 import static android.server.wm.ActivityLauncher.KEY_TARGET_COMPONENT; 23 import static android.server.wm.WindowManagerState.STATE_STOPPED; 24 import static android.server.wm.app.Components.ENTRY_POINT_ALIAS_ACTIVITY; 25 import static android.server.wm.app.Components.LAUNCHING_ACTIVITY; 26 import static android.server.wm.app.Components.NO_DISPLAY_ACTIVITY; 27 import static android.server.wm.app.Components.REPORT_FULLY_DRAWN_ACTIVITY; 28 import static android.server.wm.app.Components.SINGLE_TASK_ACTIVITY; 29 import static android.server.wm.app.Components.TEST_ACTIVITY; 30 import static android.server.wm.app.Components.TRANSLUCENT_TEST_ACTIVITY; 31 import static android.server.wm.app.Components.TRANSLUCENT_TOP_ACTIVITY; 32 import static android.server.wm.app.Components.TopActivity.EXTRA_FINISH_IN_ON_CREATE; 33 import static android.server.wm.second.Components.SECOND_ACTIVITY; 34 import static android.server.wm.third.Components.THIRD_ACTIVITY; 35 import static android.util.TimeUtils.formatDuration; 36 37 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION; 38 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_CANCELLED; 39 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DELAY_MS; 40 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_DEVICE_UPTIME_SECONDS; 41 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_REPORTED_DRAWN; 42 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_REPORTED_DRAWN_MS; 43 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_STARTING_WINDOW_DELAY_MS; 44 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS; 45 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.FIELD_CLASS_NAME; 46 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_COLD_LAUNCH; 47 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_HOT_LAUNCH; 48 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE; 49 import static com.android.internal.logging.nano.MetricsProto.MetricsEvent.TYPE_TRANSITION_WARM_LAUNCH; 50 51 import static org.hamcrest.MatcherAssert.assertThat; 52 import static org.hamcrest.Matchers.containsString; 53 import static org.hamcrest.Matchers.greaterThanOrEqualTo; 54 import static org.hamcrest.Matchers.lessThanOrEqualTo; 55 import static org.junit.Assert.assertEquals; 56 import static org.junit.Assert.assertNotEquals; 57 import static org.junit.Assert.assertNotNull; 58 import static org.junit.Assert.assertNull; 59 import static org.junit.Assert.fail; 60 61 import android.app.Activity; 62 import android.content.ComponentName; 63 import android.metrics.LogMaker; 64 import android.metrics.MetricsReader; 65 import android.os.SystemClock; 66 import android.platform.test.annotations.Presubmit; 67 import android.server.wm.CommandSession.ActivitySessionClient; 68 import android.support.test.metricshelper.MetricsAsserts; 69 import android.util.EventLog.Event; 70 71 72 import androidx.annotation.NonNull; 73 import androidx.annotation.Nullable; 74 75 import com.android.compatibility.common.util.SystemUtil; 76 77 import org.hamcrest.collection.IsIn; 78 import org.junit.Before; 79 import org.junit.Test; 80 81 import java.util.Arrays; 82 import java.util.List; 83 import java.util.Objects; 84 import java.util.Queue; 85 import java.util.concurrent.TimeUnit; 86 import java.util.function.IntConsumer; 87 88 /** 89 * CTS device tests for {@link com.android.server.wm.ActivityMetricsLogger}. 90 * Build/Install/Run: 91 * atest CtsWindowManagerDeviceTestCases:ActivityMetricsLoggerTests 92 */ 93 @Presubmit 94 public class ActivityMetricsLoggerTests extends ActivityManagerTestBase { 95 private static final String TAG_ATM = "ActivityTaskManager"; 96 private static final String LAUNCH_STATE_COLD = "COLD"; 97 private static final String LAUNCH_STATE_WARM = "WARM"; 98 private static final String LAUNCH_STATE_HOT = "HOT"; 99 private static final String LAUNCH_STATE_RELAUNCH = "RELAUNCH"; 100 private static final int EVENT_WM_ACTIVITY_LAUNCH_TIME = 30009; 101 private final MetricsReader mMetricsReader = new MetricsReader(); 102 private long mPreUptimeMs; 103 private LogSeparator mLogSeparator; 104 105 @Before setUp()106 public void setUp() throws Exception { 107 super.setUp(); 108 mPreUptimeMs = SystemClock.uptimeMillis(); 109 mMetricsReader.checkpoint(); // clear out old logs 110 mLogSeparator = separateLogs(); // add a new separator for logs 111 } 112 113 /** 114 * Launch an app and verify: 115 * - appropriate metrics logs are added 116 * - "Displayed activity ..." log is added to logcat 117 * - am_activity_launch_time event is generated 118 * In all three cases, verify the delay measurements are the same. 119 */ 120 @Test testAppLaunchIsLogged()121 public void testAppLaunchIsLogged() { 122 launchAndWaitForActivity(TEST_ACTIVITY); 123 124 final LogMaker metricsLog = waitForMetricsLog(TEST_ACTIVITY, APP_TRANSITION); 125 final String[] deviceLogs = getDeviceLogsForComponents(mLogSeparator, TAG_ATM); 126 final List<Event> eventLogs = getEventLogsForComponents(mLogSeparator, 127 EVENT_WM_ACTIVITY_LAUNCH_TIME); 128 129 final long postUptimeMs = SystemClock.uptimeMillis(); 130 assertMetricsLogs(TEST_ACTIVITY, APP_TRANSITION, metricsLog, mPreUptimeMs, postUptimeMs); 131 assertTransitionIsStartingWindow(metricsLog); 132 final int windowsDrawnDelayMs = 133 (int) metricsLog.getTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS); 134 final String expectedLog = 135 "Displayed " + TEST_ACTIVITY.flattenToShortString() 136 + ": " + formatDuration(windowsDrawnDelayMs); 137 assertLogsContain(deviceLogs, expectedLog); 138 assertEventLogsContainsLaunchTime(eventLogs, TEST_ACTIVITY, windowsDrawnDelayMs); 139 } 140 assertMetricsLogs(ComponentName componentName, int category, LogMaker log, long preUptimeMs, long postUptimeMs)141 private void assertMetricsLogs(ComponentName componentName, 142 int category, LogMaker log, long preUptimeMs, long postUptimeMs) { 143 assertNotNull("did not find the metrics log for: " + componentName 144 + " category:" + categoryToString(category), log); 145 int startUptimeSec = 146 ((Number) log.getTaggedData(APP_TRANSITION_DEVICE_UPTIME_SECONDS)).intValue(); 147 int preUptimeSec = (int) (TimeUnit.MILLISECONDS.toSeconds(preUptimeMs)); 148 int postUptimeSec = (int) (TimeUnit.MILLISECONDS.toSeconds(postUptimeMs)); 149 long testElapsedTimeMs = postUptimeMs - preUptimeMs; 150 assertThat("must be either cold or warm launch", log.getType(), 151 IsIn.oneOf(TYPE_TRANSITION_COLD_LAUNCH, TYPE_TRANSITION_WARM_LAUNCH)); 152 assertThat("reported uptime should be after the app was started", startUptimeSec, 153 greaterThanOrEqualTo(preUptimeSec)); 154 assertThat("reported uptime should be before assertion time", startUptimeSec, 155 lessThanOrEqualTo(postUptimeSec)); 156 assertNotNull("log should have delay", log.getTaggedData(APP_TRANSITION_DELAY_MS)); 157 assertNotNull("log should have windows drawn delay", 158 log.getTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS)); 159 long windowsDrawnDelayMs = (int) log.getTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS); 160 assertThat("windows drawn delay should be less that total elapsed time", 161 windowsDrawnDelayMs, lessThanOrEqualTo(testElapsedTimeMs)); 162 } 163 assertTransitionIsStartingWindow(LogMaker log)164 private void assertTransitionIsStartingWindow(LogMaker log) { 165 assertEquals("transition should be started because of starting window", 166 1 /* APP_TRANSITION_STARTING_WINDOW */, log.getSubtype()); 167 assertNotNull("log should have starting window delay", 168 log.getTaggedData(APP_TRANSITION_STARTING_WINDOW_DELAY_MS)); 169 } 170 assertEventLogsContainsLaunchTime(List<Event> events, ComponentName componentName, int windowsDrawnDelayMs)171 private void assertEventLogsContainsLaunchTime(List<Event> events, ComponentName componentName, 172 int windowsDrawnDelayMs) { 173 verifyLaunchTimeEventLogs(events, componentName, 174 delay -> assertEquals("Unexpected windows drawn delay for " + componentName, 175 delay, windowsDrawnDelayMs)); 176 } 177 verifyLaunchTimeEventLogs(List<Event> launchTimeEvents, ComponentName componentName, IntConsumer launchTimeVerifier)178 private void verifyLaunchTimeEventLogs(List<Event> launchTimeEvents, 179 ComponentName componentName, IntConsumer launchTimeVerifier) { 180 for (Event event : launchTimeEvents) { 181 final Object[] arr = (Object[]) event.getData(); 182 assertEquals(4, arr.length); 183 final String name = (String) arr[2]; 184 final int launchTime = (int) arr[3]; 185 if (name.equals(componentName.flattenToShortString())) { 186 launchTimeVerifier.accept(launchTime); 187 return; 188 } 189 } 190 fail("Could not find wm_activity_launch_time for " + componentName); 191 } 192 193 /** 194 * Start an activity that reports full drawn and verify: 195 * - fully drawn metrics are added to metrics logs 196 * - "Fully drawn activity ..." log is added to logcat 197 * In both cases verify fully drawn delay measurements are equal. 198 * See {@link Activity#reportFullyDrawn()} 199 */ 200 @Test testAppFullyDrawnReportIsLogged()201 public void testAppFullyDrawnReportIsLogged() { 202 launchAndWaitForActivity(REPORT_FULLY_DRAWN_ACTIVITY); 203 204 // Sleep until activity under test has reported drawn (after 500ms) 205 sleep(1000); 206 207 final LogMaker metricsLog = waitForMetricsLog(REPORT_FULLY_DRAWN_ACTIVITY, 208 APP_TRANSITION_REPORTED_DRAWN); 209 final String[] deviceLogs = getDeviceLogsForComponents(mLogSeparator, TAG_ATM); 210 211 assertNotNull("did not find the metrics log for: " + REPORT_FULLY_DRAWN_ACTIVITY 212 + " category:" + APP_TRANSITION_REPORTED_DRAWN, metricsLog); 213 assertThat("test activity has a 500ms delay before reporting fully drawn", 214 (long) metricsLog.getTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS), 215 greaterThanOrEqualTo(500L)); 216 assertEquals(TYPE_TRANSITION_REPORTED_DRAWN_NO_BUNDLE, metricsLog.getType()); 217 218 final long fullyDrawnDelayMs = 219 (long) metricsLog.getTaggedData(APP_TRANSITION_REPORTED_DRAWN_MS); 220 final String expectedLog = 221 "Fully drawn " + REPORT_FULLY_DRAWN_ACTIVITY.flattenToShortString() 222 + ": " + formatDuration(fullyDrawnDelayMs); 223 assertLogsContain(deviceLogs, expectedLog); 224 } 225 226 /** 227 * Warm launch an activity with wait option and verify that {@link android.app.WaitResult#totalTime} 228 * totalTime is set correctly. Make sure the reported value is consistent with value reported to 229 * metrics logs. Verify we output the correct launch state. 230 */ 231 @Test testAppWarmLaunchSetsWaitResultDelayData()232 public void testAppWarmLaunchSetsWaitResultDelayData() { 233 try (ActivitySessionClient client = createActivitySessionClient()) { 234 client.startActivity(getLaunchActivityBuilder() 235 .setUseInstrumentation() 236 .setTargetActivity(TEST_ACTIVITY) 237 .setWaitForLaunched(true)); 238 separateTestJournal(); 239 // The activity will be finished when closing the session client. 240 } 241 assertActivityDestroyed(TEST_ACTIVITY); 242 mMetricsReader.checkpoint(); // clear out old logs 243 244 // This is warm launch because its process should be alive after the above steps. 245 final String amStartOutput = SystemUtil.runShellCommand( 246 "am start -W " + TEST_ACTIVITY.flattenToShortString()); 247 248 final LogMaker metricsLog = waitForMetricsLog(TEST_ACTIVITY, APP_TRANSITION); 249 final int windowsDrawnDelayMs = 250 (int) metricsLog.getTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS); 251 252 assertEquals("Expected a cold launch.", metricsLog.getType(), TYPE_TRANSITION_WARM_LAUNCH); 253 254 assertLaunchComponentStateAndTime(amStartOutput, TEST_ACTIVITY, LAUNCH_STATE_WARM, 255 windowsDrawnDelayMs); 256 } 257 258 /** 259 * Hot launch an activity with wait option and verify that {@link android.app.WaitResult#totalTime} 260 * totalTime is set correctly. Make sure the reported value is consistent with value reported to 261 * metrics logs. Verify we output the correct launch state. 262 */ 263 @Test testAppHotLaunchSetsWaitResultDelayData()264 public void testAppHotLaunchSetsWaitResultDelayData() { 265 SystemUtil.runShellCommand("am start -S -W " + TEST_ACTIVITY.flattenToShortString()); 266 267 // Test hot launch 268 launchHomeActivityNoWait(); 269 waitAndAssertActivityState(TEST_ACTIVITY, STATE_STOPPED, "Activity should be stopped"); 270 mMetricsReader.checkpoint(); // clear out old logs 271 272 final String amStartOutput = SystemUtil.runShellCommand( 273 "am start -W " + TEST_ACTIVITY.flattenToShortString()); 274 275 final LogMaker metricsLog = waitForMetricsLog(TEST_ACTIVITY, APP_TRANSITION); 276 final int windowsDrawnDelayMs = 277 (int) metricsLog.getTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS); 278 279 assertEquals("Expected a cold launch.", metricsLog.getType(), TYPE_TRANSITION_HOT_LAUNCH); 280 281 assertLaunchComponentStateAndTime(amStartOutput, TEST_ACTIVITY, LAUNCH_STATE_HOT, 282 windowsDrawnDelayMs); 283 } 284 285 /** 286 * Launch an existing background activity after the device configuration is changed and the 287 * activity doesn't declare to handle the change. The state should be RELAUNCH instead of HOT. 288 */ 289 @Test testAppRelaunchSetsWaitResultDelayData()290 public void testAppRelaunchSetsWaitResultDelayData() { 291 final String startTestActivityCmd = "am start -W " + TEST_ACTIVITY.flattenToShortString(); 292 SystemUtil.runShellCommand(startTestActivityCmd); 293 294 // Launch another task and make sure a configuration change triggers relaunch. 295 launchAndWaitForActivity(SECOND_ACTIVITY); 296 separateTestJournal(); 297 298 final FontScaleSession fontScaleSession = createManagedFontScaleSession(); 299 fontScaleSession.set(fontScaleSession.get() + 0.1f); 300 assertActivityLifecycle(SECOND_ACTIVITY, true /* relaunched */); 301 302 // Move the task of test activity to front. 303 final String amStartOutput = SystemUtil.runShellCommand(startTestActivityCmd); 304 assertLaunchComponentState(amStartOutput, TEST_ACTIVITY, LAUNCH_STATE_RELAUNCH); 305 } 306 307 /** 308 * Cold launch an activity with wait option and verify that {@link android.app.WaitResult#totalTime} 309 * totalTime is set correctly. Make sure the reported value is consistent with value reported to 310 * metrics logs. Verify we output the correct launch state. 311 */ 312 @Test testAppColdLaunchSetsWaitResultDelayData()313 public void testAppColdLaunchSetsWaitResultDelayData() { 314 final String amStartOutput = SystemUtil.runShellCommand( 315 "am start -S -W " + TEST_ACTIVITY.flattenToShortString()); 316 317 final LogMaker metricsLog = waitForMetricsLog(TEST_ACTIVITY, APP_TRANSITION); 318 final int windowsDrawnDelayMs = 319 (int) metricsLog.getTaggedData(APP_TRANSITION_WINDOWS_DRAWN_DELAY_MS); 320 321 assertEquals("Expected a cold launch.", metricsLog.getType(), TYPE_TRANSITION_COLD_LAUNCH); 322 323 assertLaunchComponentStateAndTime(amStartOutput, TEST_ACTIVITY, LAUNCH_STATE_COLD, 324 windowsDrawnDelayMs); 325 } 326 327 /** 328 * Launch an app that is already visible and verify we handle cases where we will not 329 * receive a windows drawn message. 330 * see b/117148004 331 */ 332 @Test testLaunchOfVisibleApp()333 public void testLaunchOfVisibleApp() { 334 // Launch an activity. 335 launchAndWaitForActivity(SECOND_ACTIVITY); 336 337 // Launch a translucent activity on top. 338 launchAndWaitForActivity(TRANSLUCENT_TEST_ACTIVITY); 339 340 // Launch the first activity again. This will not trigger a windows drawn message since 341 // its windows were visible before launching. 342 mMetricsReader.checkpoint(); // clear out old logs 343 launchAndWaitForActivity(SECOND_ACTIVITY); 344 345 LogMaker metricsLog = getMetricsLog(SECOND_ACTIVITY, APP_TRANSITION); 346 // Verify transition logs are absent since we cannot measure windows drawn delay. 347 assertNull("transition logs should be reset.", metricsLog); 348 349 // Verify metrics for subsequent launches are generated as expected. 350 mPreUptimeMs = SystemClock.uptimeMillis(); 351 mMetricsReader.checkpoint(); // clear out old logs 352 353 launchAndWaitForActivity(THIRD_ACTIVITY); 354 355 long postUptimeMs = SystemClock.uptimeMillis(); 356 metricsLog = waitForMetricsLog(THIRD_ACTIVITY, APP_TRANSITION); 357 assertMetricsLogs(THIRD_ACTIVITY, APP_TRANSITION, metricsLog, mPreUptimeMs, 358 postUptimeMs); 359 assertTransitionIsStartingWindow(metricsLog); 360 } 361 362 @Test testAppLaunchCancelledSameTask()363 public void testAppLaunchCancelledSameTask() { 364 launchAndWaitForActivity(TEST_ACTIVITY); 365 366 // Start a non-opaque activity in a different process in the same task. 367 getLaunchActivityBuilder() 368 .setUseInstrumentation() 369 .setTargetActivity(TRANSLUCENT_TOP_ACTIVITY) 370 .setIntentExtra(extra -> extra.putBoolean(EXTRA_FINISH_IN_ON_CREATE, true)) 371 .setWaitForLaunched(false) 372 .execute(); 373 374 final LogMaker metricsLog = waitForMetricsLog(TRANSLUCENT_TOP_ACTIVITY, 375 APP_TRANSITION_CANCELLED); 376 377 assertNotNull("Metrics log APP_TRANSITION_CANCELLED not found", metricsLog); 378 } 379 380 /** 381 * Launch a NoDisplay activity and verify it does not affect subsequent activity launch 382 * metrics. NoDisplay activities do not draw any windows and may be incorrectly identified as a 383 * trampoline activity. See b/80380150 (Long warm launch times reported in dev play console) 384 */ 385 @Test testNoDisplayActivityLaunch()386 public void testNoDisplayActivityLaunch() { 387 launchAndWaitForActivity(NO_DISPLAY_ACTIVITY); 388 389 mPreUptimeMs = SystemClock.uptimeMillis(); 390 launchAndWaitForActivity(SECOND_ACTIVITY); 391 392 final LogMaker metricsLog = waitForMetricsLog(SECOND_ACTIVITY, APP_TRANSITION); 393 final long postUptimeMs = SystemClock.uptimeMillis(); 394 assertMetricsLogs(SECOND_ACTIVITY, APP_TRANSITION, metricsLog, mPreUptimeMs, postUptimeMs); 395 assertTransitionIsStartingWindow(metricsLog); 396 } 397 398 /** 399 * Launch an activity with a trampoline activity and verify launch metrics measures the complete 400 * launch sequence from when the trampoline activity is launching to when the target activity 401 * draws on screen. 402 */ 403 @Test testTrampolineActivityLaunch()404 public void testTrampolineActivityLaunch() { 405 // Launch a trampoline activity that will launch single task activity. 406 launchAndWaitForActivity(ENTRY_POINT_ALIAS_ACTIVITY); 407 final LogMaker metricsLog = waitForMetricsLog(SINGLE_TASK_ACTIVITY, APP_TRANSITION); 408 final long postUptimeMs = SystemClock.uptimeMillis(); 409 assertMetricsLogs(SINGLE_TASK_ACTIVITY, APP_TRANSITION, metricsLog, mPreUptimeMs, 410 postUptimeMs); 411 } 412 413 /** 414 * Launch an activity which will start another activity immediately. The reported component 415 * name should be the last one with valid launch state. 416 */ 417 @Test testConsecutiveLaunch()418 public void testConsecutiveLaunch() { 419 final String amStartOutput = executeShellCommand("am start --ez " + KEY_LAUNCH_ACTIVITY 420 + " true --es " + KEY_TARGET_COMPONENT + " " + TEST_ACTIVITY.flattenToShortString() 421 + " -W " + LAUNCHING_ACTIVITY.flattenToShortString()); 422 assertLaunchComponentState(amStartOutput, TEST_ACTIVITY, LAUNCH_STATE_COLD); 423 } 424 425 @Test testLaunchTimeEventLogNonProcessSwitch()426 public void testLaunchTimeEventLogNonProcessSwitch() { 427 launchAndWaitForActivity(SINGLE_TASK_ACTIVITY); 428 mLogSeparator = separateLogs(); 429 430 // Launch another activity in the same process. 431 launchAndWaitForActivity(TEST_ACTIVITY); 432 final List<Event> eventLogs = getEventLogsForComponents(mLogSeparator, 433 EVENT_WM_ACTIVITY_LAUNCH_TIME); 434 verifyLaunchTimeEventLogs(eventLogs, TEST_ACTIVITY, time -> assertNotEquals(0, time)); 435 } 436 launchAndWaitForActivity(ComponentName activity)437 private void launchAndWaitForActivity(ComponentName activity) { 438 getLaunchActivityBuilder() 439 .setUseInstrumentation() 440 .setTargetActivity(activity) 441 .setWindowingMode(WINDOWING_MODE_FULLSCREEN) 442 .setWaitForLaunched(true) 443 .execute(); 444 } 445 446 @NonNull waitForMetricsLog(ComponentName componentName, int category)447 private LogMaker waitForMetricsLog(ComponentName componentName, int category) { 448 final String categoryName = categoryToString(category); 449 final String message = componentName.toShortString() + " with category " + categoryName; 450 final LogMaker metricsLog = Condition.waitForResult(new Condition<LogMaker>(message) 451 .setResultSupplier(() -> getMetricsLog(componentName, category)) 452 .setResultValidator(Objects::nonNull)); 453 if (metricsLog != null) { 454 return metricsLog; 455 } 456 457 // Fallback to check again from raw event log. Not sure if sometimes MetricsAsserts cannot 458 // find the expected log. 459 final LogMaker template = new LogMaker(category); 460 final List<Event> eventLogs = getEventLogsForComponents(mLogSeparator, 461 android.util.EventLog.getTagCode("sysui_multi_action")); 462 for (Event event : eventLogs) { 463 final LogMaker log = new LogMaker((Object[]) event.getData()); 464 if (isComponentMatched(log, componentName) && template.isSubsetOf(log)) { 465 return log; 466 } 467 } 468 469 throw new AssertionError("Log should have " + categoryName + " of " + componentName); 470 } 471 472 @Nullable getMetricsLog(ComponentName componentName, int category)473 private LogMaker getMetricsLog(ComponentName componentName, int category) { 474 final Queue<LogMaker> startLogs = MetricsAsserts.findMatchingLogs(mMetricsReader, 475 new LogMaker(category)); 476 return startLogs.stream().filter(log -> isComponentMatched(log, componentName)) 477 .findFirst().orElse(null); 478 } 479 isComponentMatched(LogMaker log, ComponentName componentName)480 private static boolean isComponentMatched(LogMaker log, ComponentName componentName) { 481 final String actualClassName = (String) log.getTaggedData(FIELD_CLASS_NAME); 482 final String actualPackageName = log.getPackageName(); 483 return componentName.getClassName().equals(actualClassName) && 484 componentName.getPackageName().equals(actualPackageName); 485 } 486 categoryToString(int category)487 private static String categoryToString(int category) { 488 return (category == APP_TRANSITION ? "APP_TRANSITION" 489 : category == APP_TRANSITION_CANCELLED ? "APP_TRANSITION_CANCELLED" 490 : category == APP_TRANSITION_REPORTED_DRAWN ? "APP_TRANSITION_REPORTED_DRAWN" 491 : "Unknown") + "(" + category + ")"; 492 } 493 assertLaunchComponentState(String amStartOutput, ComponentName component, String state)494 private static void assertLaunchComponentState(String amStartOutput, ComponentName component, 495 String state) { 496 assertThat("did not find component in am start output.", amStartOutput, 497 containsString(component.flattenToShortString())); 498 assertThat("did not find launch state in am start output.", amStartOutput, 499 containsString(state)); 500 } 501 assertLaunchComponentStateAndTime(String amStartOutput, ComponentName component, String state, int windowsDrawnDelayMs)502 private static void assertLaunchComponentStateAndTime(String amStartOutput, 503 ComponentName component, String state, int windowsDrawnDelayMs) { 504 assertLaunchComponentState(amStartOutput, component, state); 505 assertThat("did not find windows drawn delay time in am start output.", amStartOutput, 506 containsString(Integer.toString(windowsDrawnDelayMs))); 507 } 508 assertLogsContain(String[] logs, String expectedLog)509 private void assertLogsContain(String[] logs, String expectedLog) { 510 for (String line : logs) { 511 if (line.contains(expectedLog)) { 512 return; 513 } 514 } 515 fail("Expected to find '" + expectedLog + "' in " + Arrays.toString(logs)); 516 } 517 } 518