Merge "Crystalball : Collect AppTransitionMetrics and support for hot launch."
diff --git a/libraries/collectors-helper/statsd/src/com/android/helpers/AppStartupHelper.java b/libraries/collectors-helper/statsd/src/com/android/helpers/AppStartupHelper.java
index b2f027b..5d1be27 100644
--- a/libraries/collectors-helper/statsd/src/com/android/helpers/AppStartupHelper.java
+++ b/libraries/collectors-helper/statsd/src/com/android/helpers/AppStartupHelper.java
@@ -54,6 +54,7 @@
private static final String PROCESS_START = "process_start";
private static final String PROCESS_START_DELAY = "process_start_delay";
+ private static final String TRANSITION_DELAY_MILLIS = "transition_delay_millis";
private boolean isProcStartDetailsDisabled;
private StatsdHelper mStatsdHelper = new StatsdHelper();
@@ -87,11 +88,13 @@
String pkgName = appStartAtom.getPkgName();
String transitionType = appStartAtom.getType().toString();
int windowsDrawnMillis = appStartAtom.getWindowsDrawnDelayMillis();
+ int transitionDelayMillis = appStartAtom.getTransitionDelayMillis();
Log.i(LOG_TAG, String.format("Pkg Name: %s, Transition Type: %s, "
- + "WindowDrawnDelayMillis: %s",
- pkgName, transitionType, windowsDrawnMillis));
+ + "WindowDrawnDelayMillis: %s, TransitionDelayMillis: %s",
+ pkgName, transitionType, windowsDrawnMillis, transitionDelayMillis));
- String metricKey = "";
+ String metricTypeKey = "";
+ String metricTransitionKey = "";
// To track number of startups per type per package.
String metricCountKey = "";
// To track total number of startups per type.
@@ -111,13 +114,20 @@
break;
}
if (!typeKey.isEmpty()) {
- metricKey = MetricUtility.constructKey(typeKey, pkgName);
+ metricTypeKey = MetricUtility.constructKey(typeKey, pkgName);
metricCountKey = MetricUtility.constructKey(typeKey, COUNT, pkgName);
totalCountKey = MetricUtility.constructKey(typeKey, TOTAL_COUNT);
- // Update the metrics.
- MetricUtility.addMetric(metricKey, windowsDrawnMillis, appStartResultMap);
+
+ // Update the windows drawn delay metrics.
+ MetricUtility.addMetric(metricTypeKey, windowsDrawnMillis, appStartResultMap);
MetricUtility.addMetric(metricCountKey, appStartCountMap);
MetricUtility.addMetric(totalCountKey, appStartCountMap);
+
+ // Update the transition delay metrics.
+ metricTransitionKey = MetricUtility.constructKey(typeKey,
+ TRANSITION_DELAY_MILLIS, pkgName);
+ MetricUtility.addMetric(metricTransitionKey, transitionDelayMillis,
+ appStartResultMap);
}
}
if (atom.hasAppStartFullyDrawn()) {
@@ -188,7 +198,7 @@
totalCountKey = MetricUtility.constructKey(typeKey, PROCESS_START,
TOTAL_COUNT);
// Update the metrics
- if(isProcStartDetailsDisabled) {
+ if (isProcStartDetailsDisabled) {
MetricUtility.addMetric(metricCountKey, tempResultCountMap);
} else {
MetricUtility.addMetric(metricKey, processStartDelayMillis,
diff --git a/libraries/collectors-helper/statsd/test/src/com/android/helpers/AppStartupHelperTest.java b/libraries/collectors-helper/statsd/test/src/com/android/helpers/AppStartupHelperTest.java
index 6565fca..824f371 100644
--- a/libraries/collectors-helper/statsd/test/src/com/android/helpers/AppStartupHelperTest.java
+++ b/libraries/collectors-helper/statsd/test/src/com/android/helpers/AppStartupHelperTest.java
@@ -53,6 +53,13 @@
private static final String COLD_LAUNCH_KEY_TEMPLATE = "cold_startup_%s";
private static final String COLD_LAUNCH_PROCESSS_FG_KEY_TEMPLATE =
"cold_startup_process_start_delay_%s_fg";
+ private static final String COLD_LAUNCH_TRANSITION_DELAY_MILLIS_KEY_TEMPLATE =
+ "cold_startup_transition_delay_millis_%s";
+ private static final String WARM_LAUNCH_TRANSITION_DELAY_MILLIS_KEY_TEMPLATE =
+ "warm_startup_transition_delay_millis_%s";
+ private static final String HOT_LAUNCH_TRANSITION_DELAY_MILLIS_KEY_TEMPLATE =
+ "hot_startup_transition_delay_millis_%s";
+
private static final String COLD_LAUNCH_COUNT_PKG_KEY_TEMPLATE = "cold_startup_count_%s";
private static final String COLD_LAUNCH_PROCESS_COUNT_FG_KEY_TEMPLATE =
"cold_startup_process_start_count_%s_fg";
@@ -118,6 +125,14 @@
assertEquals(1, Integer.parseInt(appLaunchMetrics.get(COLD_LAUNCH_TOTAL_COUNT_KEY_TEMPLATE)
.toString()));
+ // Verify transition metrics.
+ String coldLaunchTransitionMetricKey = String.format(
+ COLD_LAUNCH_TRANSITION_DELAY_MILLIS_KEY_TEMPLATE,
+ CALENDAR_PKG_NAME);
+ assertTrue(appLaunchMetrics.keySet().contains(coldLaunchTransitionMetricKey));
+ assertEquals(1,
+ appLaunchMetrics.get(coldLaunchTransitionMetricKey).toString().split(",").length);
+
// Verify process start values.
String coldLaunchProcessMetricKey = String.format(COLD_LAUNCH_PROCESSS_FG_KEY_TEMPLATE,
CALENDAR_PKG_NAME);
@@ -223,6 +238,20 @@
assertEquals(2, Integer.parseInt(appLaunchMetrics.get(COLD_LAUNCH_TOTAL_COUNT_KEY_TEMPLATE)
.toString()));
+ // Verify transition metrics.
+ String coldLaunchTransCalMetricKey = String.format(
+ COLD_LAUNCH_TRANSITION_DELAY_MILLIS_KEY_TEMPLATE,
+ CALENDAR_PKG_NAME);
+ String coldLaunchTransSetMetricKey = String.format(
+ COLD_LAUNCH_TRANSITION_DELAY_MILLIS_KEY_TEMPLATE,
+ SETTINGS_PKG_NAME);
+ assertTrue(appLaunchMetrics.keySet().contains(coldLaunchTransCalMetricKey));
+ assertTrue(appLaunchMetrics.keySet().contains(coldLaunchTransSetMetricKey));
+ assertEquals(1,
+ appLaunchMetrics.get(coldLaunchTransCalMetricKey).toString().split(",").length);
+ assertEquals(1,
+ appLaunchMetrics.get(coldLaunchTransSetMetricKey).toString().split(",").length);
+
// Verify process start values.
String coldLaunchProcessMetricKey = String.format(COLD_LAUNCH_PROCESSS_FG_KEY_TEMPLATE,
CALENDAR_PKG_NAME);
@@ -277,6 +306,15 @@
assertTrue(appLaunchMetrics.keySet().contains(calendarWarmLaunchKey));
assertEquals(1, appLaunchMetrics.get(calendarWarmLaunchKey).toString().split(",").length);
assertTrue(mAppStartupHelper.stopCollecting());
+
+ // Verify transition metrics.
+ String warmLaunchTransitionMetricKey = String.format(
+ WARM_LAUNCH_TRANSITION_DELAY_MILLIS_KEY_TEMPLATE,
+ CALENDAR_PKG_NAME);
+ assertTrue(appLaunchMetrics.keySet().contains(warmLaunchTransitionMetricKey));
+ assertEquals(1,
+ appLaunchMetrics.get(warmLaunchTransitionMetricKey).toString().split(",").length);
+
mHelper.get().exit();
}
@@ -303,6 +341,15 @@
SystemClock.sleep(HelperTestUtility.ACTION_DELAY);
HelperTestUtility.clearApp(String.format(KILL_TEST_APP_CMD_TEMPLATE, SETTINGS_PKG_NAME));
SystemClock.sleep(HelperTestUtility.ACTION_DELAY);
+
+ // Verify transition metrics.
+ String hotLaunchTransitionMetricKey = String.format(
+ HOT_LAUNCH_TRANSITION_DELAY_MILLIS_KEY_TEMPLATE,
+ SETTINGS_PKG_NAME);
+ assertTrue(appLaunchMetrics.keySet().contains(hotLaunchTransitionMetricKey));
+ assertEquals(1,
+ appLaunchMetrics.get(hotLaunchTransitionMetricKey).toString().split(",").length);
+
}
/**
diff --git a/libraries/rule/src/android/platform/test/rule/KillAppsRule.java b/libraries/rule/src/android/platform/test/rule/KillAppsRule.java
index d14ca17..6217836 100644
--- a/libraries/rule/src/android/platform/test/rule/KillAppsRule.java
+++ b/libraries/rule/src/android/platform/test/rule/KillAppsRule.java
@@ -15,6 +15,7 @@
*/
package android.platform.test.rule;
+import androidx.annotation.VisibleForTesting;
import org.junit.runner.Description;
import org.junit.runners.model.InitializationError;
@@ -24,6 +25,10 @@
public class KillAppsRule extends TestWatcher {
private final String[] mApplications;
+ @VisibleForTesting
+ static final String KILL_APP = "kill-app";
+ private static boolean mKillApp;
+
public KillAppsRule() throws InitializationError {
throw new InitializationError("Must supply an application to kill.");
}
@@ -34,9 +39,16 @@
@Override
protected void starting(Description description) {
- // Force stop each application in sequence.
+ // Check if killing the app after launch is selected or not.
+ mKillApp = Boolean.parseBoolean(getArguments().getString(KILL_APP, "true"));
+ if (!mKillApp) {
+ return;
+ }
+
+ // Force stop each application in sequence if the kill app option is selected.
for (String app : mApplications) {
executeShellCommand(String.format("am force-stop %s", app));
}
+
}
}
diff --git a/libraries/rule/src/android/platform/test/rule/PressHomeRule.java b/libraries/rule/src/android/platform/test/rule/PressHomeRule.java
new file mode 100644
index 0000000..61a0cd1
--- /dev/null
+++ b/libraries/rule/src/android/platform/test/rule/PressHomeRule.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.rule;
+
+import android.os.SystemClock;
+
+import org.junit.runner.Description;
+
+/**
+ * This rule will navigate to home at the end of each test method.
+ * TODO: Revisit this after b/132100677 is fixed.
+ */
+public class PressHomeRule extends TestWatcher {
+
+ static final String GO_HOME = "press-home";
+ private static boolean mGoHome;
+
+ @Override
+ protected void finished(Description description) {
+ // Navigate to home after the test method is executed.
+ mGoHome = Boolean.parseBoolean(getArguments().getString(GO_HOME, "true"));
+ if (!mGoHome) {
+ return;
+ }
+
+ getUiDevice().pressHome();
+ // Sleep for statsd to update the metrics.
+ SystemClock.sleep(3000);
+
+ }
+}
diff --git a/libraries/rule/tests/src/android/platform/test/rule/KillAppsRuleTest.java b/libraries/rule/tests/src/android/platform/test/rule/KillAppsRuleTest.java
index 2f82a05..89240b1 100644
--- a/libraries/rule/tests/src/android/platform/test/rule/KillAppsRuleTest.java
+++ b/libraries/rule/tests/src/android/platform/test/rule/KillAppsRuleTest.java
@@ -18,6 +18,8 @@
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.fail;
+import android.os.Bundle;
+
import org.junit.Test;
import org.junit.runner.Description;
import org.junit.runner.RunWith;
@@ -51,12 +53,12 @@
*/
@Test
public void testOneAppToKill() throws Throwable {
- TestableKillAppsRule rule = new TestableKillAppsRule("example.package.name");
+ TestableKillAppsRule rule = new TestableKillAppsRule(new Bundle(), "example.package.name");
rule.apply(rule.getTestStatement(), Description.createTestDescription("clzz", "mthd"))
- .evaluate();
+ .evaluate();
assertThat(rule.getOperations()).containsExactly(
"am force-stop example.package.name", "test")
- .inOrder();
+ .inOrder();
}
/**
@@ -64,29 +66,48 @@
*/
@Test
public void testMultipleAppsToKill() throws Throwable {
- TestableKillAppsRule rule = new TestableKillAppsRule(
+ TestableKillAppsRule rule = new TestableKillAppsRule(new Bundle(),
"package.name1",
"package.name2",
"package.name3");
rule.apply(rule.getTestStatement(), Description.createTestDescription("clzz", "mthd"))
- .evaluate();
+ .evaluate();
assertThat(rule.getOperations()).containsExactly(
"am force-stop package.name1",
"am force-stop package.name2",
"am force-stop package.name3",
"test")
- .inOrder();
+ .inOrder();
+ }
+
+ /**
+ * Tests apps are not killed if kill-app flag is set to false.
+ */
+ @Test
+ public void testDisableKillsAppsRuleOption() throws Throwable {
+ Bundle noKillAppsBundle = new Bundle();
+ noKillAppsBundle.putString(KillAppsRule.KILL_APP, "false");
+ TestableKillAppsRule rule = new TestableKillAppsRule(noKillAppsBundle,
+ "example.package.name");
+
+ rule.apply(rule.getTestStatement(), Description.createTestDescription("clzz", "mthd"))
+ .evaluate();
+ assertThat(rule.getOperations()).containsExactly("test")
+ .inOrder();
}
private static class TestableKillAppsRule extends KillAppsRule {
private List<String> mOperations = new ArrayList<>();
+ private Bundle mBundle;
- public TestableKillAppsRule(String app) {
+ public TestableKillAppsRule(Bundle bundle, String app) {
super(app);
+ mBundle = bundle;
}
- public TestableKillAppsRule(String... apps) {
+ public TestableKillAppsRule(Bundle bundle, String... apps) {
super(apps);
+ mBundle = bundle;
}
@Override
@@ -95,6 +116,11 @@
return "";
}
+ @Override
+ protected Bundle getArguments() {
+ return mBundle;
+ }
+
public List<String> getOperations() {
return mOperations;
}
diff --git a/libraries/rule/tests/src/android/platform/test/rule/PressHomeRuleTest.java b/libraries/rule/tests/src/android/platform/test/rule/PressHomeRuleTest.java
new file mode 100644
index 0000000..3e836fa
--- /dev/null
+++ b/libraries/rule/tests/src/android/platform/test/rule/PressHomeRuleTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2019 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package android.platform.test.rule;
+
+import android.os.Bundle;
+
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.support.test.uiautomator.UiDevice;
+
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+import org.junit.runners.model.Statement;
+
+import org.mockito.Mockito;
+
+/**
+ * Unit test the logic for {@link PressHomeRule}
+ */
+@RunWith(JUnit4.class)
+public class PressHomeRuleTest {
+ /**
+ * Tests that press home happens at the end of the test method.
+ */
+ @Test
+ public void testPressHomeEnabled() throws Throwable {
+
+ Bundle pressHomeBundle = new Bundle();
+ pressHomeBundle.putString(PressHomeRule.GO_HOME, "true");
+ TestablePressHomeRule rule = new TestablePressHomeRule(pressHomeBundle);
+
+ Statement testStatement = new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ // Assert that navigate to press home is not initiated.
+ verify(rule.getUiDevice(), never()).pressHome();
+ }
+ };
+ rule.apply(testStatement, Description.createTestDescription("clzz", "mthd")).evaluate();
+ // Assert that the home button is pressed after the test method.
+ verify(rule.getUiDevice(), times(1)).pressHome();
+ }
+
+ /**
+ * Tests that did not press home if the option is disabled
+ */
+ @Test
+ public void testPressHomeDisabled() throws Throwable {
+
+ Bundle pressHomeBundle = new Bundle();
+ pressHomeBundle.putString(PressHomeRule.GO_HOME, "false");
+ TestablePressHomeRule rule = new TestablePressHomeRule(pressHomeBundle);
+
+ Statement testStatement = new Statement() {
+ @Override
+ public void evaluate() throws Throwable {
+ // Assert that navigate to press home is not initiated.
+ verify(rule.getUiDevice(), never()).pressHome();
+ }
+ };
+ rule.apply(testStatement, Description.createTestDescription("clzz", "mthd")).evaluate();
+ // Assert that the home button is not pressed after the test method.
+ verify(rule.getUiDevice(), times(0)).pressHome();
+ }
+
+ private static class TestablePressHomeRule extends PressHomeRule {
+ private UiDevice mUiDevice;
+ private Bundle mBundle;
+
+ public TestablePressHomeRule(Bundle bundle) {
+ mUiDevice = Mockito.mock(UiDevice.class);
+ mBundle = bundle;
+ }
+
+ @Override
+ protected Bundle getArguments() {
+ return mBundle;
+ }
+
+ @Override
+ protected UiDevice getUiDevice() {
+ return mUiDevice;
+ }
+ }
+}