summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSzuWei Lin <szuweilin@google.com>2020-09-01 18:47:59 +0800
committerSzuWei Lin <szuweilin@google.com>2020-09-01 18:47:59 +0800
commit8b1aefc99131638f1dd1d0259f9de1663918935d (patch)
tree24b7192ab08575b8753cbc1e433552377021c19f
parentbed0db716e3ba7d27b3f15ae7f4673a290c52dba (diff)
parent66327d18426083a910176ef02d9a386c33983f14 (diff)
downloadplatform_testing-8b1aefc99131638f1dd1d0259f9de1663918935d.tar.gz
Merge branch android10-qpr3-releaseandroid10-gsi
Change-Id: Iec1447649335e2b4f12ce5a0e28b733e67c4ed82
-rw-r--r--build/tasks/tests/platform_test_list.mk4
-rw-r--r--libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGenericAppHelper.java42
-rw-r--r--libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java13
-rw-r--r--libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterHelper.java86
-rw-r--r--libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterMinimizeControlBarHelper.java62
-rw-r--r--libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterNowPlayingHelper.java62
-rw-r--r--libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java20
-rw-r--r--libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoNotificationHelper.java34
-rw-r--r--libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoNotificationMockingHelper.java3
-rw-r--r--libraries/app-helpers/interfaces/auto/src/android/platform/helpers/utility/Scrollable.java83
-rw-r--r--libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java14
-rw-r--r--libraries/collectors-helper/jank/src/com/android/helpers/SfStatsCollectionHelper.java251
-rw-r--r--libraries/collectors-helper/jank/test/src/com/android/helpers/SfStatsCollectionHelperTest.java268
-rw-r--r--libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java20
-rw-r--r--libraries/collectors-helper/memory/src/com/android/helpers/RssSnapshotHelper.java323
-rw-r--r--libraries/collectors-helper/memory/test/src/com/android/helpers/tests/FreeMemHelperTest.java1
-rw-r--r--libraries/collectors-helper/memory/test/src/com/android/helpers/tests/RssSnapshotHelperTest.java156
-rw-r--r--libraries/collectors-helper/perfetto/src/com/android/helpers/PerfettoHelper.java16
-rw-r--r--libraries/collectors-helper/perfetto/test/src/com/android/helpers/tests/PerfettoHelperTest.java29
-rw-r--r--libraries/device-collectors/src/main/java/android/device/collectors/PerfettoListener.java16
-rw-r--r--libraries/device-collectors/src/main/java/android/device/collectors/RssSnapshotListener.java99
-rw-r--r--libraries/device-collectors/src/main/java/android/device/collectors/SfStatsListener.java37
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/README.md3
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/full-battery-capacity-run-level.pbbin0 -> 261 bytes
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/full-battery-capacity-test-level.pbbin0 -> 260 bytes
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/README.md3
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/greenday-power-smaller-run-level.pbbin0 -> 454 bytes
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/greenday-power-smaller-test-level.pbbin0 -> 456 bytes
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/README.md4
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/on-device-power-measurement-run-level.pb3
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/on-device-power-measurement-test-level.pb3
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/README.md2
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/remaining-battery-capacity-with-uid-run-level.pbbin0 -> 263 bytes
-rw-r--r--libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/remaining-battery-capacity-with-uid-test-level.pbbin0 -> 262 bytes
-rw-r--r--libraries/device-collectors/src/test/java/android/device/collectors/PerfettoListenerTest.java62
-rw-r--r--libraries/device-collectors/src/test/java/android/device/collectors/RssSnapshotListenerTest.java91
-rw-r--r--libraries/device-collectors/src/test/java/android/device/collectors/SfStatsListenerTest.java58
-rw-r--r--libraries/health/runners/longevity/platform/src/android/platform/test/longevity/ScheduledScenarioRunner.java23
-rw-r--r--libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ProfileSuiteTest.java3
-rw-r--r--libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ScheduledScenarioRunnerTest.java26
-rw-r--r--libraries/launcher-helper/src/android/support/test/launcherhelper/AutoLauncherStrategy.java36
-rw-r--r--libraries/launcher-helper/src/android/support/test/launcherhelper/IAutoLauncherStrategy.java13
-rw-r--r--libraries/launcher-helper/src/android/support/test/launcherhelper/LauncherStrategyFactory.java1
-rw-r--r--libraries/launcher-helper/src/android/support/test/launcherhelper/VolvoLauncherStrategy.java154
-rw-r--r--scripts/perfetto-setup/Android.mk48
-rw-r--r--tests/health/scenarios/src/android/platform/test/scenario/sleep/Idle.java5
-rw-r--r--tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java5
47 files changed, 2111 insertions, 71 deletions
diff --git a/build/tasks/tests/platform_test_list.mk b/build/tasks/tests/platform_test_list.mk
index e7a5a18df..d1e484fa8 100644
--- a/build/tasks/tests/platform_test_list.mk
+++ b/build/tasks/tests/platform_test_list.mk
@@ -56,6 +56,7 @@ platform_tests += \
FrameworksUtilTests \
InternalLocTestApp \
JankMicroBenchmarkTests \
+ long_trace_config.textproto \
LauncherRotationStressTest \
MemoryUsage \
MultiDexLegacyTestApp \
@@ -92,6 +93,9 @@ platform_tests += \
SmokeTestApp \
SysAppJankTestsWear \
TouchLatencyJankTestWear \
+ trace_config.textproto \
+ trace_config_detailed.textproto \
+ trace_config_experimental.textproto \
UbSystemUiJankTests \
UbWebViewJankTests \
UiBench \
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGenericAppHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGenericAppHelper.java
new file mode 100644
index 000000000..060004ba5
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGenericAppHelper.java
@@ -0,0 +1,42 @@
+/*
+ * 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.helpers;
+
+import java.util.Map;
+
+/**
+ * An interface to open any applications. One of the member methods must be invoked after creating
+ * an instance of this class.
+ */
+public interface IAutoGenericAppHelper extends IAppHelper, Scrollable {
+ /**
+ * Set the package to open. The application will be opened using the info activity or launcher
+ * activity of the package that has been injected here.
+ */
+ void setPackage(String pkg);
+
+ /**
+ * Set the launch activity. The application will be opened directly using the provided activity.
+ */
+ void setLaunchActivity(String pkg);
+
+ /**
+ * Set the launch action. The application will be opened using the provided launch action with
+ * given extra arguments.
+ */
+ void setLaunchAction(String action, Map<String, String> extraArgs);
+}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java
index c80360790..d3330db57 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoGooglePlayHelper.java
@@ -35,28 +35,29 @@ public interface IAutoGooglePlayHelper extends IAppHelper, Scrollable {
/**
* Setup expectations: Google Play app is open.
*
- * This method is used to install a app.
+ * <p>This method is used to install a app.
*/
void installApp();
/**
* Setup expectations: Google Play app is open.
*
- * This method is used to cancel a download.
+ * <p>This method is used to cancel a download.
*/
void cancelDownload();
/**
* Setup expectations: Google Play app is open.
*
- * This method is used to open a installed app.
+ * <p>This method is used to return back to Google Play main page
*/
- void openApp();
+ void returnToMainPage();
/**
* Setup expectations: Google Play app is open.
*
- * This method is used to return back to Google Play main page
+ * <p>This method is used to open a installed app.
*/
- void returnToMainPage();
+ @Deprecated
+ void openApp();
}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterHelper.java
new file mode 100644
index 000000000..52caa07e5
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterHelper.java
@@ -0,0 +1,86 @@
+/*
+ * 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.helpers;
+
+public interface IAutoMediaCenterHelper extends IAppHelper {
+
+ /**
+ * Setup expectations: Media test app is open.
+ *
+ * This method is used to set account type.
+ */
+ void setAccountType(String accountType);
+
+ /**
+ * Setup expectations: Media test app is open.
+ *
+ * This method is used to set root node type.
+ */
+ void setRootNodeType(String rootNodeType);
+
+ /**
+ * Setup expectations: Media test app is open.
+ *
+ * This method is used to set root reply delay.
+ */
+ void setRootReplyDelay(String rootReplyDelay);
+
+ /**
+ * Setup expectations: media test app is open.
+ *
+ * This method is used to open Folder Menu with menuOptions.
+ * Example - openMenu->Folder->Mediafilename->trackName
+ * openMenuWith(Folder,mediafilename,trackName);
+ *
+ * @param - menuOptions used to pass multiple level of menu options in one go.
+ */
+ void selectMediaTrack(String... menuOptions);
+
+ /**
+ * Setup expectations: media test app is open.
+ *
+ * This method is used to open settings.
+ */
+ void openSettings();
+
+ /**
+ * Setup expectations: media test app is open.
+ *
+ * This method is used to close settings.
+ */
+ void closeSettings();
+
+ /**
+ * Setup expectations: media test app is open.
+ *
+ * @param - tabName used to select tab name.
+ */
+ void selectTab(String tabName);
+
+ /**
+ * Setup expectations: media test app is open.
+ *
+ * @param - title used search media track,artist.
+ */
+ void search(String title);
+
+ /**
+ * This method is used to check if media is currently playing Returns true if media is playing
+ * else returns false
+ */
+ boolean isPlaying();
+}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterMinimizeControlBarHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterMinimizeControlBarHelper.java
new file mode 100644
index 000000000..f90c0ae88
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterMinimizeControlBarHelper.java
@@ -0,0 +1,62 @@
+/*
+ * 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.helpers;
+
+public interface IAutoMediaCenterMinimizeControlBarHelper extends IAppHelper {
+
+ /**
+ * Setup expectations: media test app is open and Minimize control bar present.
+ *
+ * This method is used to play media.
+ */
+ void playMedia();
+
+ /**
+ * Setup expectations: media test app is open and Minimize control bar present.
+ *
+ * This method is used to pause media.
+ */
+ void pauseMedia();
+
+ /**
+ * Setup expectations: media test app is open and Minimize control bar present.
+ *
+ * This method is used to select next track.
+ */
+ void clickNextTrack();
+
+ /**
+ * Setup expectations: media test app is open and Minimize control bar present.
+ *
+ * This method is used to select previous track.
+ */
+ void clickPreviousTrack();
+
+ /**
+ * Setup expectations: media test app is open and Minimize control bar present.
+ *
+ * @return to get current playing track name from home screen.
+ */
+ String getTrackName();
+
+ /**
+ * Setup expectations: media test app is open and Minimize control bar present.
+ *
+ * This method is used to maximize the play back screen.
+ */
+ void maximizeNowPlaying();
+}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterNowPlayingHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterNowPlayingHelper.java
new file mode 100644
index 000000000..bbe0e2bc7
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaCenterNowPlayingHelper.java
@@ -0,0 +1,62 @@
+/*
+ * 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.helpers;
+
+public interface IAutoMediaCenterNowPlayingHelper extends IAppHelper {
+
+ /**
+ * Setup expectations: Now Playing is open.
+ *
+ * This method is used to play media.
+ */
+ void playMedia();
+
+ /**
+ * Setup expectations: Now Playing is open.
+ *
+ * This method is used to pause media.
+ */
+ void pauseMedia();
+
+ /**
+ * Setup expectations: Now Playing is open.
+ *
+ * This method is used to select next track.
+ */
+ void clickNextTrack();
+
+ /**
+ * Setup expectations: Now Playing is open.
+ *
+ * This method is used to select previous track.
+ */
+ void clickPreviousTrack();
+
+ /**
+ * Setup expectations: Now Playing is open.
+ *
+ * @return to get current playing track name from home screen.
+ */
+ String getTrackName();
+
+ /**
+ * Setup expectations: Now Playing is open.
+ *
+ * This method is used to minimize now playing.
+ */
+ void minimizeNowPlaying();
+} \ No newline at end of file
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java
index 035d68fad..ada56e72f 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoMediaHelper.java
@@ -93,4 +93,24 @@ public interface IAutoMediaHelper extends IAppHelper {
* @return to get current playing track name.
*/
String getMediaTrackName();
+
+ /**
+ * Setup expectations: on home screen.
+ *
+ * @return to get current playing track name from home screen.
+ */
+ String getMediaTrackNameFromHomeScreen();
+
+ /**
+ * Setup expectations: Media app is open. User navigates to sub-page of the Media Player
+ *
+ * <p>This method is to go back to the Media Player main page from any sub-page.
+ */
+ void goBackToMediaHomePage();
+
+ /**
+ * This method is used to check if media is currently playing Returns true if media is playing
+ * else returns false
+ */
+ boolean isPlaying();
}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoNotificationHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoNotificationHelper.java
index ac18ada0d..b5fd0f72d 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoNotificationHelper.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoNotificationHelper.java
@@ -16,4 +16,36 @@
package android.platform.helpers;
-public interface IAutoNotificationHelper extends INotificationHelper, Scrollable {}
+public interface IAutoNotificationHelper extends INotificationHelper, Scrollable, IAppHelper {
+ /**
+ * Setup expectations: Notification app is open and scrolled to the bottom.
+ *
+ * <p>Tap clear all button if present.
+ */
+ void tapClearAllBtn();
+
+ /**
+ * Setup expectations: A notification is received.
+ *
+ * <p>Check whether notification has been posted.
+ *
+ * @param title of the notification to be checked.
+ */
+ boolean checkNotificationExists(String title);
+
+ /**
+ * Setup expectations: A notification is received.
+ *
+ * <p>Swipe away a received notification.
+ *
+ * @param title of the notification to be swiped.
+ */
+ void removeNotification(String title);
+
+ /**
+ * Setup expectations: None.
+ *
+ * <p>Swipe down from status bar to open notifications.
+ */
+ void openNotification();
+}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoNotificationMockingHelper.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoNotificationMockingHelper.java
new file mode 100644
index 000000000..9903832ba
--- /dev/null
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/IAutoNotificationMockingHelper.java
@@ -0,0 +1,3 @@
+package android.platform.helpers;
+
+public interface IAutoNotificationMockingHelper extends INotificationHelper, IAppHelper {}
diff --git a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/utility/Scrollable.java b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/utility/Scrollable.java
index 7c8bd714b..c61dc2525 100644
--- a/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/utility/Scrollable.java
+++ b/libraries/app-helpers/interfaces/auto/src/android/platform/helpers/utility/Scrollable.java
@@ -27,6 +27,8 @@ import androidx.test.InstrumentationRegistry;
* This interface is intended to be inherited by AppHelper classes to add scrolling functionlity.
*/
public interface Scrollable {
+ int DEFAULT_MARGIN = 5;
+
/**
* Setup expectations: None
*
@@ -89,6 +91,81 @@ public interface Scrollable {
}
/**
+ * Setup expectations: None.
+ *
+ * <p>This method can be implemented optionally if customized margin is required.
+ *
+ * @return the gesture margin for scrolling.
+ */
+ public default Margin getScrollableMargin() {
+ return new Margin(DEFAULT_MARGIN);
+ }
+
+ /**
+ * Setup expectations: None.
+ *
+ * <p>This method can be implemented optionally if customized margin is required. It sets the
+ * gesture margin returned by <code>getScrollableMargin()</code>.
+ *
+ * @param margin Left, top, right and bottom margins will all be set this this value.
+ */
+ public default void setScrollableMargin(int margin) {
+ throw new UnsupportedOperationException("setScrollableMargin method not implemeneted.");
+ }
+
+ /**
+ * Setup expectations: None.
+ *
+ * <p>This method can be implemented optionally if customized margin is required. It sets the
+ * gesture margin returned by <code>getScrollableMargin()</code>.
+ *
+ * @param left The value to which to set the left margin for scrollling.
+ * @param top The value to which to set the top margin for scrollling.
+ * @param right The value to which to set the right margin for scrollling.
+ * @param bottom The value to which to set the bottom margin for scrollling.
+ */
+ public default void setScrollableMargin(int left, int top, int right, int bottom) {
+ throw new UnsupportedOperationException("setScrollableMargin method not implemeneted.");
+ }
+
+ public class Margin {
+ private int mLeft;
+ private int mTop;
+ private int mRight;
+ private int mBottom;
+
+ public Margin(int margin) {
+ mLeft = margin;
+ mTop = margin;
+ mRight = margin;
+ mBottom = margin;
+ }
+
+ public Margin(int left, int top, int right, int bottom) {
+ mLeft = left;
+ mTop = top;
+ mRight = right;
+ mBottom = bottom;
+ }
+
+ public int getLeft() {
+ return mLeft;
+ }
+
+ public int getTop() {
+ return mTop;
+ }
+
+ public int getRight() {
+ return mRight;
+ }
+
+ public int getBottom() {
+ return mBottom;
+ }
+ }
+
+ /**
* This is not part of the public interface. For internal use only.
*
* <p>Scroll in <code>direction</code> direction by <code>percent</code> percent of the whole
@@ -105,6 +182,12 @@ public interface Scrollable {
UiObject2 scrollable = device.findObject(By.scrollable(true));
if (scrollable != null) {
+ Margin margin = getScrollableMargin();
+ scrollable.setGestureMargins(
+ margin.getLeft(),
+ margin.getTop(),
+ margin.getRight(),
+ margin.getBottom());
int scrollSpeed = calcScrollSpeed(scrollable, durationMs);
scrollable.scroll(direction, percent / 100, scrollSpeed);
} else {
diff --git a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java
index 85b243d15..15684fdd4 100644
--- a/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java
+++ b/libraries/app-helpers/interfaces/handheld/src/android/platform/helpers/IPhotosHelper.java
@@ -112,22 +112,22 @@ public interface IPhotosHelper extends IAppHelper {
public void openPicture(int index);
/**
- * Setup expectations: Photos is open and a picture album is open.
+ * Setup expectations: Photos is open and a picture is open.
*
- * This method will scroll the picture album in the specified direction.
+ * <p>This method will scroll to next or previous picture in the specified direction.
*
* @param direction The direction to scroll, must be LEFT or RIGHT.
- * @return Returns whether album can be still scrolled in the given direction
+ * @return Returns whether picture can be still scrolled in the given direction
*/
- public boolean scrollAlbum(Direction direction);
+ public boolean scrollPicture(Direction direction);
/**
- * Setup expectations: Photos is open and a picture folder is open.
+ * Setup expectations: Photos is open and a page contains pictures or albums is open.
*
- * This method will scroll the Photos grid view in the specified direction.
+ * <p>This method will scroll the page in the specified direction.
*
* @param direction The direction of the scroll, must be UP or DOWN.
* @return Returns whether the object can still scroll in the given direction
*/
- public boolean scrollGridView(Direction direction);
+ public boolean scrollPage(Direction direction);
}
diff --git a/libraries/collectors-helper/jank/src/com/android/helpers/SfStatsCollectionHelper.java b/libraries/collectors-helper/jank/src/com/android/helpers/SfStatsCollectionHelper.java
new file mode 100644
index 000000000..b2c09480f
--- /dev/null
+++ b/libraries/collectors-helper/jank/src/com/android/helpers/SfStatsCollectionHelper.java
@@ -0,0 +1,251 @@
+/*
+ * 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 com.android.helpers;
+
+import static com.android.helpers.MetricUtility.constructKey;
+
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+import androidx.test.InstrumentationRegistry;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.stream.Stream;
+
+/**
+ * An {@link ICollectorHelper} for collecting SurfaceFlinger time stats.
+ *
+ * <p>This parses the output of {@code dumpsys SurfaceFlinger --timestats} and returns a collection
+ * of both global metrics and metrics tracked for each layer.
+ */
+public class SfStatsCollectionHelper implements ICollectorHelper<Double> {
+
+ private static final String LOG_TAG = SfStatsCollectionHelper.class.getSimpleName();
+
+ private static final Pattern KEY_VALUE_PATTERN =
+ Pattern.compile("^(\\w+)\\s+=\\s+(\\d+\\.?\\d*|.*).*");
+ private static final Pattern HISTOGRAM_PATTERN =
+ Pattern.compile("([^\\n]+)\\n((\\d+ms=\\d+\\s+)+)");
+
+ private static final String FRAME_DURATION_KEY = "frameDuration histogram is as below:";
+ private static final String RENDER_ENGINE_KEY = "renderEngineTiming histogram is as below:";
+
+ @VisibleForTesting static final String SFSTATS_METRICS_PREFIX = "SFSTATS";
+
+ @VisibleForTesting static final String SFSTATS_COMMAND = "dumpsys SurfaceFlinger --timestats ";
+
+ @VisibleForTesting
+ static final String SFSTATS_COMMAND_ENABLE_AND_CLEAR = SFSTATS_COMMAND + "-enable -clear";
+
+ @VisibleForTesting static final String SFSTATS_COMMAND_DUMP = SFSTATS_COMMAND + "-dump";
+
+ @VisibleForTesting
+ static final String SFSTATS_COMMAND_DISABLE_AND_CLEAR = SFSTATS_COMMAND + "-disable -clear";
+
+ private UiDevice mDevice;
+
+ @Override
+ public boolean startCollecting() {
+ try {
+ getDevice().executeShellCommand(SFSTATS_COMMAND_ENABLE_AND_CLEAR);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Encountered exception enabling dumpsys SurfaceFlinger.", e);
+ throw new RuntimeException(e);
+ }
+ return true;
+ }
+
+ @Override
+ public Map<String, Double> getMetrics() {
+ Map<String, Double> results = new HashMap<>();
+ String output;
+ try {
+ output = getDevice().executeShellCommand(SFSTATS_COMMAND_DUMP);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Encountered exception calling dumpsys SurfaceFlinger.", e);
+ throw new RuntimeException(e);
+ }
+ String[] blocks = output.split("\n\n");
+
+ HashMap<String, String> globalPairs = getStatPairs(blocks[0]);
+ Map<String, Histogram> histogramPairs = getHistogramPairs(blocks[0]);
+
+ for (String key : globalPairs.keySet()) {
+ String metricKey = constructKey(SFSTATS_METRICS_PREFIX, "GLOBAL", key.toUpperCase());
+ results.put(metricKey, Double.valueOf(globalPairs.get(key)));
+ }
+
+ if (histogramPairs.containsKey(FRAME_DURATION_KEY)) {
+ results.put(
+ constructKey(SFSTATS_METRICS_PREFIX, "GLOBAL", "FRAME_CPU_DURATION_AVG"),
+ histogramPairs.get(FRAME_DURATION_KEY).mean());
+ }
+
+ if (histogramPairs.containsKey(RENDER_ENGINE_KEY)) {
+ results.put(
+ constructKey(SFSTATS_METRICS_PREFIX, "GLOBAL", "RENDER_ENGINE_DURATION_AVG"),
+ histogramPairs.get(RENDER_ENGINE_KEY).mean());
+ }
+
+ for (int i = 1; i < blocks.length; i++) {
+ HashMap<String, String> layerPairs = getStatPairs(blocks[i]);
+ String layerName = layerPairs.get("layerName");
+ String totalFrames = layerPairs.get("totalFrames");
+ String droppedFrames = layerPairs.get("droppedFrames");
+ String averageFPS = layerPairs.get("averageFPS");
+ results.put(
+ constructKey(SFSTATS_METRICS_PREFIX, layerName, "TOTAL_FRAMES"),
+ Double.valueOf(totalFrames));
+ results.put(
+ constructKey(SFSTATS_METRICS_PREFIX, layerName, "DROPPED_FRAMES"),
+ Double.valueOf(droppedFrames));
+ results.put(
+ constructKey(SFSTATS_METRICS_PREFIX, layerName, "AVERAGE_FPS"),
+ Double.valueOf(averageFPS));
+ }
+
+ return results;
+ }
+
+ @Override
+ public boolean stopCollecting() {
+ try {
+ getDevice().executeShellCommand(SFSTATS_COMMAND_DISABLE_AND_CLEAR);
+ } catch (Exception e) {
+ Log.e(LOG_TAG, "Encountered exception disabling dumpsys SurfaceFlinger.", e);
+ throw new RuntimeException(e);
+ }
+ return true;
+ }
+
+ /** Returns the {@link UiDevice} under test. */
+ @VisibleForTesting
+ protected UiDevice getDevice() {
+ if (mDevice == null) {
+ mDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ }
+ return mDevice;
+ }
+
+ /**
+ * Returns a map of key-value pairs for every line of timestats for each layer handled by
+ * SurfaceFlinger as well as some global SurfaceFlinger stats. An output line like {@code
+ * totalFrames = 42} would get parsed and be accessable as {@code pairs.get("totalFrames") =>
+ * "42"}
+ */
+ private HashMap<String, String> getStatPairs(String block) {
+ HashMap<String, String> pairs = new HashMap<>();
+ String[] lines = block.split("\n");
+ for (int j = 0; j < lines.length; j++) {
+ Matcher keyValueMatcher = KEY_VALUE_PATTERN.matcher(lines[j].trim());
+ if (keyValueMatcher.matches()) {
+ pairs.put(keyValueMatcher.group(1), keyValueMatcher.group(2));
+ }
+ }
+ return pairs;
+ }
+
+ /**
+ * Returns a map of {@link Histogram} instances emitted by SurfaceFlinger stats.
+ *
+ * <p>Input must be of the format defined by the {@link HISTOGRAM_PATTERN} regex. Example input
+ * may include:
+ *
+ * <pre>{@code
+ * Sample key:
+ * 0ms=0 1ms=1 2ms=4 3ms=9 4ms=16
+ * }</pre>
+ *
+ * <p>The corresponding output would include "Sample key:" as the key for a {@link Histogram}
+ * instance constructed from the string {@code 0ms=0 1ms=1 2ms=4 3ms=9 4ms=16}.
+ */
+ private Map<String, Histogram> getHistogramPairs(String block) {
+ Map<String, Histogram> pairs = new HashMap<>();
+ Matcher histogramMatcher = HISTOGRAM_PATTERN.matcher(block);
+ while (histogramMatcher.find()) {
+ String key = histogramMatcher.group(1);
+ String histogramString = histogramMatcher.group(2);
+ Histogram histogram = new Histogram();
+ Stream.of(histogramString.split("\\s+"))
+ .forEach(
+ bucket ->
+ histogram.put(
+ Integer.valueOf(
+ bucket.substring(0, bucket.indexOf("ms"))),
+ Long.valueOf(
+ bucket.substring(bucket.indexOf("=") + 1))));
+ pairs.put(key, histogram);
+ }
+ return pairs;
+ }
+
+ /** Representation for a statistical histogram */
+ private static final class Histogram {
+ private final Map<Integer, Long> internalMap;
+
+ /** Constructs a histogram instance. */
+ Histogram() {
+ internalMap = new HashMap<>();
+ }
+
+ /**
+ * Puts a (key, value) pair in the histogram.
+ *
+ * <p>The key would represent the particular bucket that the value is inserted into.
+ */
+ Histogram put(Integer key, Long value) {
+ internalMap.put(key, value);
+ return this;
+ }
+
+ /**
+ * Computes the mean of the histogram
+ *
+ * @return 0 if the histogram is empty, the true mean otherwise.
+ */
+ double mean() {
+ long count = internalMap.values().stream().mapToLong(v -> v).sum();
+ if (count <= 0) {
+ return 0.0;
+ }
+ long numerator =
+ internalMap
+ .entrySet()
+ .stream()
+ .mapToLong(entry -> entry.getKey() * entry.getValue())
+ .sum();
+ return (double) numerator / count;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (!(obj instanceof Histogram)) {
+ return false;
+ }
+
+ Histogram other = (Histogram) obj;
+
+ return internalMap.equals(other.internalMap);
+ }
+
+ @Override
+ public int hashCode() {
+ return internalMap.hashCode();
+ }
+ }
+}
diff --git a/libraries/collectors-helper/jank/test/src/com/android/helpers/SfStatsCollectionHelperTest.java b/libraries/collectors-helper/jank/test/src/com/android/helpers/SfStatsCollectionHelperTest.java
new file mode 100644
index 000000000..c986a6750
--- /dev/null
+++ b/libraries/collectors-helper/jank/test/src/com/android/helpers/SfStatsCollectionHelperTest.java
@@ -0,0 +1,268 @@
+/*
+ * 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 com.android.helpers;
+
+import static com.android.helpers.MetricUtility.constructKey;
+import static com.android.helpers.SfStatsCollectionHelper.SFSTATS_COMMAND_DISABLE_AND_CLEAR;
+import static com.android.helpers.SfStatsCollectionHelper.SFSTATS_COMMAND_DUMP;
+import static com.android.helpers.SfStatsCollectionHelper.SFSTATS_COMMAND_ENABLE_AND_CLEAR;
+import static com.android.helpers.SfStatsCollectionHelper.SFSTATS_METRICS_PREFIX;
+import static com.google.common.truth.Truth.assertThat;
+import static org.mockito.Mockito.when;
+
+import android.support.test.uiautomator.UiDevice;
+import java.io.IOException;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+public class SfStatsCollectionHelperTest {
+ private static final String SFSTATS_DUMP =
+ "SurfaceFlinger TimeStats:\n"
+ + "statsStart = 1566336030\n"
+ + "statsEnd = 1566338516\n"
+ + "totalFrames = 5791\n"
+ + "missedFrames = 82\n"
+ + "clientCompositionFrames = 0\n"
+ + "displayOnTime = 2485421 ms\n"
+ + "displayConfigStats is as below:\n"
+ + "90fps=3700ms 60fps=297492ms\n"
+ + "totalP2PTime = 2674034 ms\n"
+ + "frameDuration histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=50 6ms=50 7ms=0 8ms=0 9ms=0\n"
+ + "renderEngineTiming histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=50 7ms=50 8ms=0 9ms=0\n"
+ + "presentToPresent histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=5791 9ms=0\n"
+ + "\n"
+ + "layerName = com.breel.wallpapers19.DoodleWallpaperV1#0\n"
+ + "packageName = com.breel.wallpapers19.DoodleWallpaperV1\n"
+ + "totalFrames = 5448\n"
+ + "droppedFrames = 4\n"
+ + "averageFPS = 2.038\n"
+ + "present2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=5448 9ms=0\n"
+ + "latch2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=5448 9ms=0\n"
+ + "desired2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=5448 9ms=0\n"
+ + "acquire2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=5448 9ms=0\n"
+ + "post2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=5448 9ms=0\n"
+ + "post2acquire histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=5448 9ms=0\n"
+ + "\n"
+ + "layerName = com.google.android.nexuslauncher.NexusLauncherActivity#0\n"
+ + "packageName = com.google.android.nexuslauncher\n"
+ + "totalFrames = 264\n"
+ + "droppedFrames = 7\n"
+ + "averageFPS = 84.318\n"
+ + "present2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=264 9ms=0\n"
+ + "latch2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=264 9ms=0\n"
+ + "desired2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=264 9ms=0\n"
+ + "acquire2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=264 9ms=0\n"
+ + "post2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=264 9ms=0\n"
+ + "post2acquire histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=264 9ms=0\n"
+ + "\n"
+ + "layerName = SurfaceView - com.mxtech.videoplayer.ad/com.mxtech.videoplayer.ad.ActivityScreen#0\n"
+ + "packageName = \n"
+ + "totalFrames = 2352\n"
+ + "droppedFrames = 0\n"
+ + "averageFPS = 59.999\n"
+ + "present2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0\n"
+ + "latch2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0\n"
+ + "desired2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0\n"
+ + "acquire2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0\n"
+ + "post2present histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0\n"
+ + "post2acquire histogram is as below:\n"
+ + "0ms=0 1ms=0 2ms=0 3ms=0 4ms=0 5ms=0 6ms=0 7ms=0 8ms=2352 9ms=0";
+
+ private static final String LOG_TAG = SfStatsCollectionHelperTest.class.getSimpleName();
+
+ private @Mock UiDevice mUiDevice;
+ private SfStatsCollectionHelper mHelper;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mHelper = Mockito.spy(new SfStatsCollectionHelper());
+ when(mHelper.getDevice()).thenReturn(mUiDevice);
+ }
+
+ /** Test track a single, valid package. */
+ @Test
+ public void testCollect_valuesMatch() throws Exception {
+ mockDumpCommand();
+ mockEnableAndClearCommand();
+ mockDisableAndClearCommand();
+ mHelper.startCollecting();
+ Map<String, Double> metrics = mHelper.getMetrics();
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "GLOBAL",
+ "statsStart".toUpperCase())))
+ .isEqualTo(Double.valueOf(1566336030));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "GLOBAL",
+ "statsEnd".toUpperCase())))
+ .isEqualTo(Double.valueOf(1566338516));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "GLOBAL",
+ "totalFrames".toUpperCase())))
+ .isEqualTo(Double.valueOf(5791));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "GLOBAL",
+ "missedFrames".toUpperCase())))
+ .isEqualTo(Double.valueOf(82));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "GLOBAL",
+ "clientCompositionFrames".toUpperCase())))
+ .isEqualTo(Double.valueOf(0));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "GLOBAL",
+ "displayOnTime".toUpperCase())))
+ .isEqualTo(Double.valueOf(2485421));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "GLOBAL",
+ "totalP2PTime".toUpperCase())))
+ .isEqualTo(Double.valueOf(2674034));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "GLOBAL",
+ "FRAME_CPU_DURATION_AVG")))
+ .isEqualTo(Double.valueOf(5.5));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "GLOBAL",
+ "RENDER_ENGINE_DURATION_AVG")))
+ .isEqualTo(Double.valueOf(6.5));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "com.breel.wallpapers19.DoodleWallpaperV1#0",
+ "TOTAL_FRAMES")))
+ .isEqualTo(Double.valueOf(5448));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "com.breel.wallpapers19.DoodleWallpaperV1#0",
+ "DROPPED_FRAMES")))
+ .isEqualTo(Double.valueOf(4));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "com.breel.wallpapers19.DoodleWallpaperV1#0",
+ "AVERAGE_FPS")))
+ .isEqualTo(2.038);
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "com.google.android.nexuslauncher.NexusLauncherActivity#0",
+ "TOTAL_FRAMES")))
+ .isEqualTo(Double.valueOf(264));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "com.google.android.nexuslauncher.NexusLauncherActivity#0",
+ "DROPPED_FRAMES")))
+ .isEqualTo(Double.valueOf(7));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "com.google.android.nexuslauncher.NexusLauncherActivity#0",
+ "AVERAGE_FPS")))
+ .isEqualTo(84.318);
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "SurfaceView - com.mxtech.videoplayer.ad/com.mxtech.videoplayer.ad.ActivityScreen#0",
+ "TOTAL_FRAMES")))
+ .isEqualTo(Double.valueOf(2352));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "SurfaceView - com.mxtech.videoplayer.ad/com.mxtech.videoplayer.ad.ActivityScreen#0",
+ "DROPPED_FRAMES")))
+ .isEqualTo(Double.valueOf(0));
+ assertThat(
+ metrics.get(
+ constructKey(
+ SFSTATS_METRICS_PREFIX,
+ "SurfaceView - com.mxtech.videoplayer.ad/com.mxtech.videoplayer.ad.ActivityScreen#0",
+ "AVERAGE_FPS")))
+ .isEqualTo(59.999);
+ mHelper.stopCollecting();
+ }
+
+ private void mockEnableAndClearCommand() throws IOException {
+ when(mUiDevice.executeShellCommand(SFSTATS_COMMAND_ENABLE_AND_CLEAR)).thenReturn("");
+ }
+
+ private void mockDumpCommand() throws IOException {
+ when(mUiDevice.executeShellCommand(SFSTATS_COMMAND_DUMP)).thenReturn(SFSTATS_DUMP);
+ }
+
+ private void mockDisableAndClearCommand() throws IOException {
+ when(mUiDevice.executeShellCommand(SFSTATS_COMMAND_DISABLE_AND_CLEAR)).thenReturn("");
+ }
+}
diff --git a/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java b/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java
index da0ff2c26..b2f1f8cb9 100644
--- a/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java
+++ b/libraries/collectors-helper/memory/src/com/android/helpers/FreeMemHelper.java
@@ -50,6 +50,7 @@ public class FreeMemHelper implements ICollectorHelper<Long> {
private static final String PROC_MEMINFO = "cat /proc/meminfo";
private static final String LINE_SEPARATOR = "\\n";
private static final String MEM_AVAILABLE_PATTERN = "^MemAvailable.*";
+ private static final String MEM_FREE_PATTERN = "^MemFree.*";
private static final Pattern CACHE_PROC_START_PATTERN = Pattern.compile(".*: Cached$");
private static final Pattern PID_PATTERN = Pattern.compile("^.*pid(?<processid> [0-9]*).*$");
private static final String DUMPSYS_PROCESS = "dumpsys meminfo %s";
@@ -57,6 +58,7 @@ public class FreeMemHelper implements ICollectorHelper<Long> {
private static final String PROCESS_ID = "processid";
public static final String MEM_AVAILABLE_CACHE_PROC_DIRTY = "MemAvailable_CacheProcDirty_bytes";
public static final String PROC_MEMINFO_MEM_AVAILABLE= "proc_meminfo_memavailable_bytes";
+ public static final String PROC_MEMINFO_MEM_FREE= "proc_meminfo_memfree_bytes";
public static final String DUMPSYS_CACHED_PROC_MEMORY= "dumpsys_cached_procs_memory_bytes";
private UiDevice mUiDevice;
@@ -77,27 +79,35 @@ public class FreeMemHelper implements ICollectorHelper<Long> {
String memInfo;
try {
memInfo = mUiDevice.executeShellCommand(PROC_MEMINFO);
+ Log.i(TAG, "cat proc/meminfo :" + memInfo);
} catch (IOException ioe) {
Log.e(TAG, "Failed to read " + PROC_MEMINFO + ".", ioe);
return null;
}
Pattern memAvailablePattern = Pattern.compile(MEM_AVAILABLE_PATTERN, Pattern.MULTILINE);
+ Pattern memFreePattern = Pattern.compile(MEM_FREE_PATTERN, Pattern.MULTILINE);
Matcher memAvailableMatcher = memAvailablePattern.matcher(memInfo);
+ Matcher memFreeMatcher = memFreePattern.matcher(memInfo);
String[] memAvailable = null;
- if (memAvailableMatcher.find()) {
+ String[] memFree = null;
+ if (memAvailableMatcher.find() && memFreeMatcher.find()) {
memAvailable = memAvailableMatcher.group(0).split(SEPARATOR);
+ memFree = memFreeMatcher.group(0).split(SEPARATOR);
}
- if (memAvailable == null) {
- Log.e(TAG, "MemAvailable is null.");
+ if (memAvailable == null || memFree == null) {
+ Log.e(TAG, "MemAvailable or MemFree is null.");
return null;
}
Map<String, Long> results = new HashMap<>();
long memAvailableProc = Long.parseLong(memAvailable[1]);
results.put(PROC_MEMINFO_MEM_AVAILABLE, (memAvailableProc * 1024));
+ long memFreeProc = Long.parseLong(memFree[1]);
+ results.put(PROC_MEMINFO_MEM_FREE, (memFreeProc * 1024));
+
long cacheProcDirty = memAvailableProc;
byte[] dumpsysMemInfoBytes = MetricUtility.executeCommandBlocking(DUMPSYS_MEMIFNO,
InstrumentationRegistry.getInstrumentation());
@@ -107,7 +117,7 @@ public class FreeMemHelper implements ICollectorHelper<Long> {
for (String process : cachedProcList) {
Log.i(TAG, "Cached Process" + process);
Matcher match;
- if (((match = matches(PID_PATTERN, process))) != null) {
+ if ((match = matches(PID_PATTERN, process)) != null) {
String processId = match.group(PROCESS_ID);
String processDumpSysMemInfo = String.format(DUMPSYS_PROCESS, processId);
String processInfoStr;
@@ -173,7 +183,7 @@ public class FreeMemHelper implements ICollectorHelper<Long> {
Log.i(TAG, currLine);
Matcher match;
if (!isCacheProcSection
- && ((match = matches(CACHE_PROC_START_PATTERN, currLine))) == null) {
+ && (match = matches(CACHE_PROC_START_PATTERN, currLine)) == null) {
// Continue untill the start of cache proc section.
continue;
} else {
diff --git a/libraries/collectors-helper/memory/src/com/android/helpers/RssSnapshotHelper.java b/libraries/collectors-helper/memory/src/com/android/helpers/RssSnapshotHelper.java
new file mode 100644
index 000000000..fc947a75c
--- /dev/null
+++ b/libraries/collectors-helper/memory/src/com/android/helpers/RssSnapshotHelper.java
@@ -0,0 +1,323 @@
+/*
+ * 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 com.android.helpers;
+
+import static com.android.helpers.MetricUtility.constructKey;
+
+import android.support.test.uiautomator.UiDevice;
+import android.util.Log;
+import androidx.test.InstrumentationRegistry;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.InputMismatchException;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Scanner;
+import java.util.Set;
+import java.util.UUID;
+
+/**
+ * Helper to collect rss snapshot for a list of processes.
+ */
+public class RssSnapshotHelper implements ICollectorHelper<String> {
+ private static final String TAG = RssSnapshotHelper.class.getSimpleName();
+
+ private static final String DROP_CACHES_CMD = "echo %d > /proc/sys/vm/drop_caches";
+ private static final String PIDOF_CMD = "pidof %s";
+ public static final String ALL_PROCESSES_CMD = "ps -A";
+ private static final String SHOWMAP_CMD = "showmap -v %d";
+
+ public static final String RSS_METRIC_PREFIX = "showmap_rss_bytes";
+ public static final String OUTPUT_FILE_PATH_KEY = "showmap_output_file";
+ public static final String RSS_PROCESS_COUNT = "rss_process_count";
+
+ private String[] mProcessNames = null;
+ private String mTestOutputDir = null;
+ private String mTestOutputFile = null;
+
+ private int mDropCacheOption;
+ private boolean mCollectForAllProcesses = false;
+ private UiDevice mUiDevice;
+
+ // Map to maintain per-process rss.
+ private Map<String, String> mRssMap = new HashMap<>();
+
+ public void setUp(String testOutputDir, String... processNames) {
+ mProcessNames = processNames;
+ mTestOutputDir = testOutputDir;
+ mDropCacheOption = 0;
+ mUiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ }
+
+ @Override
+ public boolean startCollecting() {
+ if (mTestOutputDir == null) {
+ Log.e(TAG, String.format("Invalid test setup"));
+ return false;
+ }
+
+ File directory = new File(mTestOutputDir);
+ String filePath =
+ String.format("%s/rss_snapshot%d.txt", mTestOutputDir, UUID.randomUUID().hashCode());
+ File file = new File(filePath);
+
+ // Make sure directory exists and file does not
+ if (directory.exists()) {
+ if (file.exists() && !file.delete()) {
+ Log.e(TAG, String.format("Failed to delete result output file %s", filePath));
+ return false;
+ }
+ } else {
+ if (!directory.mkdirs()) {
+ Log.e(TAG, String.format("Failed to create result output directory %s", mTestOutputDir));
+ return false;
+ }
+ }
+
+ // Create an empty file to fail early in case there are no write permissions
+ try {
+ if (!file.createNewFile()) {
+ // This should not happen unless someone created the file right after we deleted it
+ Log.e(TAG, String.format("Race with another user of result output file %s", filePath));
+ return false;
+ }
+ } catch (IOException e) {
+ Log.e(TAG, String.format("Failed to create result output file %s", filePath), e);
+ return false;
+ }
+
+ mTestOutputFile = filePath;
+ return true;
+ }
+
+ @Override
+ public Map<String, String> getMetrics() {
+ try {
+ // Drop cache if requested
+ if (mDropCacheOption > 0) {
+ dropCache(mDropCacheOption);
+ }
+
+ if (mCollectForAllProcesses) {
+ Log.i(TAG, "Collecting RSS metrics for all processes.");
+ mProcessNames = getAllProcessNames();
+ } else if (mProcessNames.length > 0) {
+ Log.i(TAG, "Collecting RSS only for given list of process");
+ } else if (mProcessNames.length == 0) {
+ // No processes specified, just return empty map
+ return mRssMap;
+ }
+
+ FileWriter writer = new FileWriter(new File(mTestOutputFile), true);
+ for (String processName : mProcessNames) {
+ List<Integer> pids = new ArrayList<>();
+
+ long totalrss = 0;
+ // Collect required data
+ try {
+ pids = getPids(processName);
+
+ for (Integer pid: pids) {
+ String showmapOutput = execShowMap(processName, pid);
+ long rss = extractTotalRss(processName, showmapOutput);
+ // Track the total rss for the processes with the same process name.
+ totalrss += rss;
+ // Store showmap output into file. If there are more than one process
+ // with same name write the individual showmap associated with pid.
+ storeToFile(mTestOutputFile, processName, pid, showmapOutput, writer);
+ }
+ } catch (RuntimeException e) {
+ Log.e(TAG, e.getMessage(), e.getCause());
+ // Skip this process and continue with the next one
+ continue;
+ }
+
+ // Store metrics
+ mRssMap.put(constructKey(RSS_METRIC_PREFIX, processName), Long.toString(totalrss * 1024));
+ // Store the unique process count.
+ mRssMap.put(RSS_PROCESS_COUNT, Integer.toString(mProcessNames.length));
+ }
+ writer.close();
+ mRssMap.put(OUTPUT_FILE_PATH_KEY, mTestOutputFile);
+ } catch (RuntimeException e) {
+ Log.e(TAG, e.getMessage(), e.getCause());
+ } catch (IOException e) {
+ Log.e(TAG, String.format("Failed to write output file %s", mTestOutputFile), e);
+ }
+
+ return mRssMap;
+ }
+
+ @Override
+ public boolean stopCollecting() {
+ return true;
+ }
+
+ /**
+ * Set drop cache option.
+ *
+ * @param dropCacheOption drop pagecache (1), slab (2) or all (3) cache
+ * @return true on success, false if input option is invalid
+ */
+ public boolean setDropCacheOption(int dropCacheOption) {
+ // Valid values are 1..3
+ if (dropCacheOption < 1 || dropCacheOption > 3) {
+ return false;
+ }
+
+ mDropCacheOption = dropCacheOption;
+ return true;
+ }
+
+ /**
+ * Drops kernel memory cache.
+ *
+ * @param cacheOption drop pagecache (1), slab (2) or all (3) caches
+ */
+ private void dropCache(int cacheOption) throws RuntimeException {
+ try {
+ mUiDevice.executeShellCommand(String.format(DROP_CACHES_CMD, cacheOption));
+ } catch (IOException e) {
+ throw new RuntimeException("Unable to drop caches", e);
+ }
+ }
+
+ /**
+ * Get pid's of the process with {@code processName} name.
+ *
+ * @param processName name of the process to get pid
+ * @return pid's of the specified process
+ */
+ private List<Integer> getPids(String processName) throws RuntimeException {
+ try {
+ String pidofOutput = mUiDevice.executeShellCommand(String.format(PIDOF_CMD, processName));
+
+ // Sample output for the process with more than 1 pid.
+ // Sample command : "pidof init"
+ // Sample output : 1 559
+ String[] pids = pidofOutput.split("\\s+");
+ List<Integer> pidList = new ArrayList<>();
+ for (String pid: pids) {
+ pidList.add(Integer.parseInt(pid.trim()));
+ }
+ return pidList;
+ } catch (IOException e) {
+ throw new RuntimeException(String.format("Unable to get pid of %s ", processName), e);
+ }
+ }
+
+ /**
+ * Executes showmap command for the process with {@code processName} name and {@code pid} pid.
+ *
+ * @param processName name of the process to run showmap for
+ * @param pid pid of the process to run showmap for
+ * @return the output of showmap command
+ */
+ private String execShowMap(String processName, long pid) throws IOException {
+ try {
+ return mUiDevice.executeShellCommand(String.format(SHOWMAP_CMD, pid));
+ } catch (IOException e) {
+ throw new RuntimeException(
+ String.format("Unable to execute showmap command for %s ", processName), e);
+ }
+ }
+
+ /**
+ * Extract total RSS from showmap command output for the process with {@code processName} name.
+ *
+ * @param processName name of the process to extract RSS for
+ * @param showmapOutput showmap command output
+ * @return total RSS of the process
+ */
+ private long extractTotalRss(String processName, String showmapOutput) throws RuntimeException {
+ try {
+ int pos = showmapOutput.lastIndexOf("----");
+ Scanner sc = new Scanner(showmapOutput.substring(pos));
+ sc.next();
+ sc.nextLong();
+ return sc.nextLong();
+ } catch (IndexOutOfBoundsException | InputMismatchException e) {
+ throw new RuntimeException(
+ String.format("Unexpected showmap format for %s ", processName), e);
+ }
+ }
+
+ /**
+ * Store test results for one process into file.
+ *
+ * @param fileName name of the file being written
+ * @param processName name of the process
+ * @param pid pid of the process
+ * @param showmapOutput showmap command output
+ * @param writer file writer to write the data
+ */
+ private void storeToFile(String fileName, String processName, long pid, String showmapOutput,
+ FileWriter writer) throws RuntimeException {
+ try {
+ writer.write(String.format(">>> %s (%d) <<<\n", processName, pid));
+ writer.write(showmapOutput);
+ writer.write('\n');
+ } catch (IOException e) {
+ throw new RuntimeException(String.format("Unable to write file %s ", fileName), e);
+ }
+ }
+
+ /**
+ * Enables RSS collection for all processes.
+ */
+ public void setAllProcesses() {
+ mCollectForAllProcesses = true;
+ }
+
+ /**
+ * Get all process names running in the system.
+ */
+ private String[] getAllProcessNames() {
+ Set<String> allProcessNames = new LinkedHashSet<>();
+ try {
+ String psOutput = mUiDevice.executeShellCommand(ALL_PROCESSES_CMD);
+ // Split the lines
+ String allProcesses[] = psOutput.split("\\n");
+ for (String invidualProcessDetails : allProcesses) {
+ Log.i(TAG, String.format("Process detail: %s", invidualProcessDetails));
+ // Sample process detail line
+ // system 603 1 41532 5396 SyS_epoll+ 0 S servicemanager
+ String processSplit[] = invidualProcessDetails.split("\\s+");
+ // Parse process name
+ String processName = processSplit[processSplit.length - 1].trim();
+ // Include the process name which are not enclosed in [].
+ if (!processName.startsWith("[") && !processName.endsWith("]")) {
+ // Skip the first (i.e header) line from "ps -A" output.
+ if (processName.equalsIgnoreCase("NAME")) {
+ continue;
+ }
+ Log.i(TAG, String.format("Including the process %s", processName));
+ allProcessNames.add(processName);
+ }
+ }
+ } catch (IOException ioe) {
+ throw new RuntimeException(
+ String.format("Unable execute all processes command %s ", ALL_PROCESSES_CMD),
+ ioe);
+ }
+ return allProcessNames.toArray(new String[0]);
+ }
+}
diff --git a/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/FreeMemHelperTest.java b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/FreeMemHelperTest.java
index 283ab3620..9770908f1 100644
--- a/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/FreeMemHelperTest.java
+++ b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/FreeMemHelperTest.java
@@ -54,6 +54,7 @@ public class FreeMemHelperTest {
assertTrue(freeMemMetrics.containsKey(FreeMemHelper.DUMPSYS_CACHED_PROC_MEMORY));
assertTrue(freeMemMetrics.get(FreeMemHelper.MEM_AVAILABLE_CACHE_PROC_DIRTY) > 0);
assertTrue(freeMemMetrics.get(FreeMemHelper.PROC_MEMINFO_MEM_AVAILABLE) > 0);
+ assertTrue(freeMemMetrics.get(FreeMemHelper.PROC_MEMINFO_MEM_FREE) > 0);
assertTrue(freeMemMetrics.get(FreeMemHelper.DUMPSYS_CACHED_PROC_MEMORY) > 0);
}
}
diff --git a/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/RssSnapshotHelperTest.java b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/RssSnapshotHelperTest.java
new file mode 100644
index 000000000..6a1e17922
--- /dev/null
+++ b/libraries/collectors-helper/memory/test/src/com/android/helpers/tests/RssSnapshotHelperTest.java
@@ -0,0 +1,156 @@
+/*
+ * 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 com.android.helpers.tests;
+
+import static com.android.helpers.MetricUtility.constructKey;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import androidx.test.runner.AndroidJUnit4;
+import com.android.helpers.RssSnapshotHelper;
+import java.util.Map;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+/**
+ * Android Unit tests for {@link RssSnapshotHelper}.
+ *
+ * To run:
+ * atest CollectorsHelperTest:com.android.helpers.tests.RssSnapshotHelperTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RssSnapshotHelperTest {
+ private static final String TAG = RssSnapshotHelperTest.class.getSimpleName();
+
+ // Valid output file
+ private static final String VALID_OUTPUT_DIR = "/sdcard/test_results";
+ // Invalid output file (no permissions to write)
+ private static final String INVALID_OUTPUT_DIR = "/data/local/tmp";
+
+ // Lists of process names
+ private static final String[] EMPTY_PROCESS_LIST = {};
+ private static final String[] ONE_PROCESS_LIST = {"com.android.systemui"};
+ private static final String[] TWO_PROCESS_LIST = {"com.android.systemui", "system_server"};
+ private static final String[] NO_PROCESS_LIST = {null};
+
+ private RssSnapshotHelper mRssSnapshotHelper;
+
+ @Before
+ public void setUp() {
+ mRssSnapshotHelper = new RssSnapshotHelper();
+ }
+
+ /**
+ * Test start collecting returns false if the helper has not been properly set up.
+ */
+ @Test
+ public void testSetUpNotCalled() {
+ assertFalse(mRssSnapshotHelper.startCollecting());
+ }
+
+ /**
+ * Test invalid options for drop cache flag.
+ */
+ @Test
+ public void testInvalidDropCacheOptions() {
+ assertFalse(mRssSnapshotHelper.setDropCacheOption(-1));
+ assertFalse(mRssSnapshotHelper.setDropCacheOption(0));
+ assertFalse(mRssSnapshotHelper.setDropCacheOption(4));
+ }
+
+ /**
+ * Test invalid options for drop cache flag.
+ */
+ @Test
+ public void testValidDropCacheOptions() {
+ assertTrue(mRssSnapshotHelper.setDropCacheOption(1));
+ assertTrue(mRssSnapshotHelper.setDropCacheOption(2));
+ assertTrue(mRssSnapshotHelper.setDropCacheOption(3));
+ }
+
+ /**
+ * Test no metrics are sampled if process name is empty.
+ */
+ @Test
+ public void testEmptyProcessName() {
+ mRssSnapshotHelper.setUp(VALID_OUTPUT_DIR, EMPTY_PROCESS_LIST);
+ Map<String, String> metrics = mRssSnapshotHelper.getMetrics();
+ assertTrue(metrics.isEmpty());
+ }
+
+ /**
+ * Test sampling on a valid and running process.
+ */
+ @Test
+ public void testValidFile() {
+ mRssSnapshotHelper.setUp(VALID_OUTPUT_DIR, ONE_PROCESS_LIST);
+ assertTrue(mRssSnapshotHelper.startCollecting());
+ }
+
+ /**
+ * Test sampling on using an invalid output file.
+ */
+ @Test
+ public void testInvalidFile() {
+ mRssSnapshotHelper.setUp(INVALID_OUTPUT_DIR, ONE_PROCESS_LIST);
+ assertFalse(mRssSnapshotHelper.startCollecting());
+ }
+
+ /**
+ * Test getting metrics from one process.
+ */
+ @Test
+ public void testGetMetrics_OneProcess() {
+ testProcessList(ONE_PROCESS_LIST);
+ }
+
+ /**
+ * Test getting metrics from multiple processes process.
+ */
+ @Test
+ public void testGetMetrics_MultipleProcesses() {
+ testProcessList(TWO_PROCESS_LIST);
+ }
+
+ /**
+ * Test all process flag return more than 2 processes metrics atleast.
+ */
+ @Test
+ public void testGetMetrics_AllProcess() {
+ mRssSnapshotHelper.setUp(VALID_OUTPUT_DIR, NO_PROCESS_LIST);
+ mRssSnapshotHelper.setAllProcesses();
+ assertTrue(mRssSnapshotHelper.startCollecting());
+ Map<String, String> metrics = mRssSnapshotHelper.getMetrics();
+ assertTrue(metrics.size() > 2);
+ assertTrue(metrics.containsKey(RssSnapshotHelper.OUTPUT_FILE_PATH_KEY));
+
+ }
+
+
+ private void testProcessList(String... processNames) {
+ mRssSnapshotHelper.setUp(VALID_OUTPUT_DIR, processNames);
+ assertTrue(mRssSnapshotHelper.startCollecting());
+ Map<String, String> metrics = mRssSnapshotHelper.getMetrics();
+ assertFalse(metrics.isEmpty());
+ for (String processName : processNames) {
+ assertTrue(
+ metrics.containsKey(constructKey(RssSnapshotHelper.RSS_METRIC_PREFIX, processName)));
+ }
+ assertTrue(metrics.containsKey(RssSnapshotHelper.OUTPUT_FILE_PATH_KEY));
+ }
+}
diff --git a/libraries/collectors-helper/perfetto/src/com/android/helpers/PerfettoHelper.java b/libraries/collectors-helper/perfetto/src/com/android/helpers/PerfettoHelper.java
index c1d7aeef8..abf92e25c 100644
--- a/libraries/collectors-helper/perfetto/src/com/android/helpers/PerfettoHelper.java
+++ b/libraries/collectors-helper/perfetto/src/com/android/helpers/PerfettoHelper.java
@@ -40,6 +40,8 @@ public class PerfettoHelper {
private static final String PERFETTO_START_CMD = "perfetto --background -c %s%s -o %s";
private static final String PERFETTO_TMP_OUTPUT_FILE =
"/data/misc/perfetto-traces/trace_output.pb";
+ // Additional arg to indicate that the perfetto config file is text format.
+ private static final String PERFETTO_TXT_PROTO_ARG = " --txt";
// Command to stop (i.e kill) the perfetto tracing.
private static final String PERFETTO_STOP_CMD = "pkill -INT perfetto";
// Command to check the perfetto process id.
@@ -62,9 +64,10 @@ public class PerfettoHelper {
* /data/misc/perfetto-traces/ folder in the device.
*
* @param configFileName used for collecting the perfetto trace.
+ * @param isTextProtoConfig true if the config file is textproto format otherwise false.
* @return true if trace collection started successfully otherwise return false.
*/
- public boolean startCollecting(String configFileName) {
+ public boolean startCollecting(String configFileName, boolean isTextProtoConfig) {
mUIDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
if (configFileName == null || configFileName.isEmpty()) {
Log.e(LOG_TAG, "Perfetto config file name is null or empty.");
@@ -79,15 +82,22 @@ public class PerfettoHelper {
return false;
}
}
+
// Remove already existing temporary output trace file if any.
String output = mUIDevice.executeShellCommand(String.format(REMOVE_CMD,
PERFETTO_TMP_OUTPUT_FILE));
Log.i(LOG_TAG, String.format("Perfetto output file cleanup - %s", output));
+ String perfettoCmd = String.format(PERFETTO_START_CMD,
+ PERFETTO_ROOT_DIR, configFileName, PERFETTO_TMP_OUTPUT_FILE);
+
+ if(isTextProtoConfig) {
+ perfettoCmd = perfettoCmd + PERFETTO_TXT_PROTO_ARG;
+ }
+
// Start perfetto tracing.
Log.i(LOG_TAG, "Starting perfetto tracing.");
- String startOutput = mUIDevice.executeShellCommand(String.format(PERFETTO_START_CMD,
- PERFETTO_ROOT_DIR, configFileName, PERFETTO_TMP_OUTPUT_FILE));
+ String startOutput = mUIDevice.executeShellCommand(perfettoCmd);
Log.i(LOG_TAG, String.format("Perfetto start command output - %s", startOutput));
// TODO : Once the output status is available use that for additional validation.
if (!isPerfettoRunning()) {
diff --git a/libraries/collectors-helper/perfetto/test/src/com/android/helpers/tests/PerfettoHelperTest.java b/libraries/collectors-helper/perfetto/test/src/com/android/helpers/tests/PerfettoHelperTest.java
index 66b4f3bfe..a81499d01 100644
--- a/libraries/collectors-helper/perfetto/test/src/com/android/helpers/tests/PerfettoHelperTest.java
+++ b/libraries/collectors-helper/perfetto/test/src/com/android/helpers/tests/PerfettoHelperTest.java
@@ -37,6 +37,7 @@ import static org.junit.Assert.assertTrue;
*
* To run:
* Have a valid perfetto config under /data/misc/perfetto-traces/valid_config.pb
+ * Have a valid text perfetto config under /data/misc/perfetto-traces/valid_text_config.textproto
* TODO: b/119020380 to keep track of automating the above step.
* atest CollectorsHelperTest:com.android.helpers.tests.PerfettoHelperTest
*/
@@ -64,7 +65,7 @@ public class PerfettoHelperTest {
*/
@Test
public void testNullConfigName() throws Exception {
- assertFalse(perfettoHelper.startCollecting(null));
+ assertFalse(perfettoHelper.startCollecting(null, false));
}
/**
@@ -72,7 +73,7 @@ public class PerfettoHelperTest {
*/
@Test
public void testEmptyConfigName() throws Exception {
- assertFalse(perfettoHelper.startCollecting(""));
+ assertFalse(perfettoHelper.startCollecting("", false));
}
/**
@@ -80,7 +81,7 @@ public class PerfettoHelperTest {
*/
@Test
public void testNoConfigFile() throws Exception {
- assertFalse(perfettoHelper.startCollecting("no_config.pb"));
+ assertFalse(perfettoHelper.startCollecting("no_config.pb", false));
}
/**
@@ -88,7 +89,7 @@ public class PerfettoHelperTest {
*/
@Test
public void testPerfettoStartSuccess() throws Exception {
- assertTrue(perfettoHelper.startCollecting("valid_config.pb"));
+ assertTrue(perfettoHelper.startCollecting("valid_config.pb", false));
}
/**
@@ -96,7 +97,7 @@ public class PerfettoHelperTest {
*/
@Test
public void testPerfettoValidOutputPath() throws Exception {
- assertTrue(perfettoHelper.startCollecting("valid_config.pb"));
+ assertTrue(perfettoHelper.startCollecting("valid_config.pb", false));
assertTrue(perfettoHelper.stopCollecting(1000, "data/local/tmp/out.pb"));
}
@@ -105,7 +106,7 @@ public class PerfettoHelperTest {
*/
@Test
public void testPerfettoInvalidOutputPath() throws Exception {
- assertTrue(perfettoHelper.startCollecting("valid_config.pb"));
+ assertTrue(perfettoHelper.startCollecting("valid_config.pb", false));
// Don't have permission to create new folder under /data
assertFalse(perfettoHelper.stopCollecting(1000, "/data/dummy/xyz/out.pb"));
}
@@ -116,7 +117,7 @@ public class PerfettoHelperTest {
*/
@Test
public void testPerfettoSuccess() throws Exception {
- assertTrue(perfettoHelper.startCollecting("valid_config.pb"));
+ assertTrue(perfettoHelper.startCollecting("valid_config.pb", false));
assertTrue(perfettoHelper.stopCollecting(1000, "/data/local/tmp/out.pb"));
UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
String[] fileStats = uiDevice.executeShellCommand(String.format(
@@ -125,4 +126,18 @@ public class PerfettoHelperTest {
assertTrue(fileSize > 0);
}
+ /**
+ * Test perfetto collection returns true and output file size greater than zero
+ * if the valid perfetto config file used.
+ */
+ @Test
+ public void testTextProtoConfigSuccess() throws Exception {
+ assertTrue(perfettoHelper.startCollecting("valid_text_config.textproto", true));
+ assertTrue(perfettoHelper.stopCollecting(1000, "/data/local/tmp/out.pb"));
+ UiDevice uiDevice = UiDevice.getInstance(InstrumentationRegistry.getInstrumentation());
+ String[] fileStats = uiDevice.executeShellCommand(String.format(
+ FILE_SIZE_IN_BYTES, "/data/local/tmp/out.pb")).split(" ");
+ int fileSize = Integer.parseInt(fileStats[0].trim());
+ assertTrue(fileSize > 0);
+ }
}
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/PerfettoListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/PerfettoListener.java
index 8c0891e5f..9ba0e8b9d 100644
--- a/libraries/device-collectors/src/main/java/android/device/collectors/PerfettoListener.java
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/PerfettoListener.java
@@ -46,6 +46,8 @@ public class PerfettoListener extends BaseMetricListener {
private static final String DEFAULT_WAIT_TIME_MSECS = "3000";
// Default output folder to store the perfetto output traces.
private static final String DEFAULT_OUTPUT_ROOT = "/sdcard/test_results";
+ // Argument to indicate the perfetto config file is text proto file.
+ public static final String PERFETTO_CONFIG_TEXT_PROTO = "perfetto_config_text_proto";
// Argument to get custom config file name for collecting the trace.
private static final String PERFETTO_CONFIG_FILE_ARG = "perfetto_config_file";
// Argument to get custom time in millisecs to wait before dumping the trace.
@@ -75,6 +77,7 @@ public class PerfettoListener extends BaseMetricListener {
// Store the method name and invocation count to create unique file name for each trace.
private Map<String, Integer> mTestIdInvocationCount = new HashMap<>();
private boolean mPerfettoStartSuccess = false;
+ private boolean mIsConfigTextProto = false;
private boolean mIsCollectPerRun;
private PerfettoHelper mPerfettoHelper = new PerfettoHelper();
@@ -123,10 +126,14 @@ public class PerfettoListener extends BaseMetricListener {
// Whether to collect the for the entire test run or per test.
mIsCollectPerRun = Boolean.parseBoolean(args.getString(COLLECT_PER_RUN));
+ // Whether the config is text proto or not. By default set to false.
+ mIsConfigTextProto = Boolean.parseBoolean(args.getString(PERFETTO_CONFIG_TEXT_PROTO));
+
// Perfetto config file has to be under /data/misc/perfetto-traces/
// defaulted to trace_config.pb is perfetto_config_file is not passed.
mConfigFileName = args.getString(PERFETTO_CONFIG_FILE_ARG, DEFAULT_CONFIG_FILE);
+
// Whether to hold wakelocks on all Prefetto tracing functions. You may want to enable
// this if your device is not USB connected. This option prevents the device from
// going into suspend mode while this listener is running intensive tasks.
@@ -288,16 +295,22 @@ public class PerfettoListener extends BaseMetricListener {
@VisibleForTesting
public void acquireWakelock(WakeLock wakelock) {
if (wakelock != null) {
+ Log.d(getTag(), "wakelock.isHeld: " + wakelock.isHeld());
Log.d(getTag(), "acquiring wakelock.");
wakelock.acquire();
+ Log.d(getTag(), "wakelock acquired.");
+ Log.d(getTag(), "wakelock.isHeld: " + wakelock.isHeld());
}
}
@VisibleForTesting
public void releaseWakelock(WakeLock wakelock) {
if (wakelock != null) {
+ Log.d(getTag(), "wakelock.isHeld: " + wakelock.isHeld());
Log.d(getTag(), "releasing wakelock.");
wakelock.release();
+ Log.d(getTag(), "wakelock released.");
+ Log.d(getTag(), "wakelock.isHeld: " + wakelock.isHeld());
}
}
@@ -313,7 +326,8 @@ public class PerfettoListener extends BaseMetricListener {
* Start perfetto tracing using the given config file.
*/
private void startPerfettoTracing() {
- mPerfettoStartSuccess = mPerfettoHelper.startCollecting(mConfigFileName);
+ mPerfettoStartSuccess = mPerfettoHelper.startCollecting(mConfigFileName,
+ mIsConfigTextProto);
if (!mPerfettoStartSuccess) {
Log.e(getTag(), "Perfetto did not start successfully.");
}
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/RssSnapshotListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/RssSnapshotListener.java
new file mode 100644
index 000000000..fc82512c9
--- /dev/null
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/RssSnapshotListener.java
@@ -0,0 +1,99 @@
+/*
+ * 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.device.collectors;
+
+import android.device.collectors.annotations.OptionClass;
+import android.os.Bundle;
+import android.util.Log;
+import androidx.annotation.VisibleForTesting;
+import com.android.helpers.RssSnapshotHelper;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A {@link RssSnapshotListener} that takes a snapshot of Rss sizes for the list of
+ * specified processes.
+ *
+ * Options:
+ * -e process-names [processNames] : a comma-separated list of processes
+ * -e drop-cache [pagecache | slab | all] : drop cache flag
+ * -e test-output-dir [path] : path to the output directory
+ */
+@OptionClass(alias = "rsssnapshot-collector")
+public class RssSnapshotListener extends BaseCollectionListener<String> {
+ private static final String TAG = RssSnapshotListener.class.getSimpleName();
+ private static final String DEFAULT_OUTPUT_DIR = "/sdcard/test_results";
+
+ @VisibleForTesting static final String PROCESS_SEPARATOR = ",";
+ @VisibleForTesting static final String PROCESS_NAMES_KEY = "process-names";
+ @VisibleForTesting static final String DROP_CACHE_KEY = "drop-cache";
+ @VisibleForTesting static final String OUTPUT_DIR_KEY = "test-output-dir";
+
+ private RssSnapshotHelper mRssSnapshotHelper = new RssSnapshotHelper();
+ private final Map<String, Integer> dropCacheValues = new HashMap<String, Integer>() {
+ {
+ put("pagecache", 1);
+ put("slab", 2);
+ put("all", 3);
+ }
+ };
+
+ public RssSnapshotListener() {
+ createHelperInstance(mRssSnapshotHelper);
+ }
+
+ /**
+ * Constructor to simulate receiving the instrumentation arguments. Should not be used except
+ * for testing.
+ */
+ @VisibleForTesting
+ public RssSnapshotListener(Bundle args, RssSnapshotHelper helper) {
+ super(args, helper);
+ mRssSnapshotHelper = helper;
+ createHelperInstance(mRssSnapshotHelper);
+ }
+
+ /**
+ * Adds the options for rss snapshot collector.
+ */
+ @Override
+ public void setupAdditionalArgs() {
+ Bundle args = getArgsBundle();
+ String testOutputDir = args.getString(OUTPUT_DIR_KEY, DEFAULT_OUTPUT_DIR);
+ // Collect for all processes if process list is empty or null.
+ String procsString = args.getString(PROCESS_NAMES_KEY);
+
+ String[] procs = null;
+ if (procsString == null || procsString.isEmpty()) {
+ mRssSnapshotHelper.setAllProcesses();
+ } else {
+ procs = procsString.split(PROCESS_SEPARATOR);
+ }
+
+ mRssSnapshotHelper.setUp(testOutputDir, procs);
+
+ String dropCacheValue = args.getString(DROP_CACHE_KEY);
+ if (dropCacheValue != null) {
+ if (dropCacheValues.containsKey(dropCacheValue)) {
+ mRssSnapshotHelper.setDropCacheOption(dropCacheValues.get(dropCacheValue));
+ } else {
+ Log.e(TAG, "Value for \"" + DROP_CACHE_KEY + "\" parameter is invalid");
+ return;
+ }
+ }
+ }
+}
diff --git a/libraries/device-collectors/src/main/java/android/device/collectors/SfStatsListener.java b/libraries/device-collectors/src/main/java/android/device/collectors/SfStatsListener.java
new file mode 100644
index 000000000..fc03f0b38
--- /dev/null
+++ b/libraries/device-collectors/src/main/java/android/device/collectors/SfStatsListener.java
@@ -0,0 +1,37 @@
+/*
+ * 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.device.collectors;
+
+import android.device.collectors.annotations.OptionClass;
+import android.os.Bundle;
+import androidx.annotation.VisibleForTesting;
+
+import com.android.helpers.SfStatsCollectionHelper;
+
+@OptionClass(alias = "sfstats-listener")
+public class SfStatsListener extends BaseCollectionListener<Double> {
+ private static final String LOG_TAG = SfStatsListener.class.getSimpleName();
+
+ public SfStatsListener() {
+ createHelperInstance(new SfStatsCollectionHelper());
+ }
+
+ @VisibleForTesting
+ public SfStatsListener(Bundle args, SfStatsCollectionHelper helper) {
+ super(args, helper);
+ }
+}
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/README.md b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/README.md
new file mode 100644
index 000000000..e780cfd81
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/README.md
@@ -0,0 +1,3 @@
+# Full Battery Capacity Configs
+
+These statsd configs collects FullBatteryCapacity metrics.
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/full-battery-capacity-run-level.pb b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/full-battery-capacity-run-level.pb
new file mode 100644
index 000000000..16a19a37f
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/full-battery-capacity-run-level.pb
Binary files differ
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/full-battery-capacity-test-level.pb b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/full-battery-capacity-test-level.pb
new file mode 100644
index 000000000..4018b1851
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/full-battery-capacity/full-battery-capacity-test-level.pb
Binary files differ
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/README.md b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/README.md
new file mode 100644
index 000000000..c496110ae
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/README.md
@@ -0,0 +1,3 @@
+# Greenday Power Configs for Run Level and Test Level Metrics
+
+Configs for a smaller set of power metrics : CPUTimePerFreq, MobileBytesTransfer, RemainingBatteryCapacity and WifiBytesTransfer. \ No newline at end of file
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/greenday-power-smaller-run-level.pb b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/greenday-power-smaller-run-level.pb
new file mode 100644
index 000000000..dd2698b46
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/greenday-power-smaller-run-level.pb
Binary files differ
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/greenday-power-smaller-test-level.pb b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/greenday-power-smaller-test-level.pb
new file mode 100644
index 000000000..43dc7c6d6
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/greenday-power-smaller/greenday-power-smaller-test-level.pb
Binary files differ
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/README.md b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/README.md
new file mode 100644
index 000000000..96dc0d661
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/README.md
@@ -0,0 +1,4 @@
+# On Device Power Measurement Configs
+
+These configs are used to pull the on-device power measurement before and after a test and test run as defined in the
+OnDevicePowerMeasurement atom.
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/on-device-power-measurement-run-level.pb b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/on-device-power-measurement-run-level.pb
new file mode 100644
index 000000000..803e7bbbb
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/on-device-power-measurement-run-level.pb
@@ -0,0 +1,3 @@
+ò‘ÊÏÉŸí”ß*,±‚䃆܈š‹ûâǶÁóÑžë0èHXÐ`€Óž¶¤¢Ÿ„2:ûâǶÁóÑžë¶N:ꃂ÷œÇ”«/((:‚›Úï­§âÛ6/((:#€Óž¶¤¢Ÿ„2‚›Úï­§âÛ6ꃂ÷œÇ”«b AID_GRAPHICSb AID_INCIDENTDb
+AID_STATSDb AID_RADIOb
+AID_SYSTEMbAID_ROOTb AID_BLUETOOTHÀ> \ No newline at end of file
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/on-device-power-measurement-test-level.pb b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/on-device-power-measurement-test-level.pb
new file mode 100644
index 000000000..8387eee16
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/on-device-power-measurement/on-device-power-measurement-test-level.pb
@@ -0,0 +1,3 @@
+µÔ¸îÂãø¿1*+Ÿ‹Ôÿÿùï[ûâǶÁóÑžë0èHXÐ`¾Ò°Þö¤¨ºi:ûâǶÁóÑžë¶N:§óæëξ£²³/( (:ŸÓœÎ‚°¨µ„/( (:$¾Ò°Þö¤¨ºiŸÓœÎ‚°¨µ„§óæëξ£²³b AID_GRAPHICSb AID_INCIDENTDb
+AID_STATSDb AID_RADIOb
+AID_SYSTEMbAID_ROOTb AID_BLUETOOTHÀ> \ No newline at end of file
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/README.md b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/README.md
index 558107aae..e3f313952 100644
--- a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/README.md
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/README.md
@@ -1,4 +1,4 @@
# Remaining Battery Capacity Configs
These configs are used to collect the remaining battery capacity on the device as defined in the
-RemainingBatteryCapacity (Colomb counter) atom.
+RemainingBatteryCapacity (Colomb counter) atom. Also includes configs with uid to package name mapping
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/remaining-battery-capacity-with-uid-run-level.pb b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/remaining-battery-capacity-with-uid-run-level.pb
new file mode 100644
index 000000000..f4a1d0a55
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/remaining-battery-capacity-with-uid-run-level.pb
Binary files differ
diff --git a/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/remaining-battery-capacity-with-uid-test-level.pb b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/remaining-battery-capacity-with-uid-test-level.pb
new file mode 100644
index 000000000..4f534e1e8
--- /dev/null
+++ b/libraries/device-collectors/src/main/platform-collectors/res/statsd-configs/remaining-battery-capacity/remaining-battery-capacity-with-uid-test-level.pb
Binary files differ
diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/PerfettoListenerTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/PerfettoListenerTest.java
index c51ded8cf..20ae4d63c 100644
--- a/libraries/device-collectors/src/test/java/android/device/collectors/PerfettoListenerTest.java
+++ b/libraries/device-collectors/src/test/java/android/device/collectors/PerfettoListenerTest.java
@@ -18,6 +18,7 @@ package android.device.collectors;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.doAnswer;
@@ -113,14 +114,14 @@ public class PerfettoListenerTest {
public void testPerfettoPerTestSuccessFlow() throws Exception {
Bundle b = new Bundle();
mListener = initListener(b);
- doReturn(true).when(mPerfettoHelper).startCollecting(anyString());
+ doReturn(true).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
doReturn(true).when(mPerfettoHelper).stopCollecting(anyLong(), anyString());
// Test run start behavior
mListener.testRunStarted(mRunDesc);
// Test test start behavior
mListener.testStarted(mTest1Desc);
- verify(mPerfettoHelper, times(1)).startCollecting(anyString());
+ verify(mPerfettoHelper, times(1)).startCollecting(anyString(), anyBoolean());
mListener.onTestEnd(mDataRecord, mTest1Desc);
verify(mPerfettoHelper, times(1)).stopCollecting(anyLong(), anyString());
@@ -135,14 +136,14 @@ public class PerfettoListenerTest {
Bundle b = new Bundle();
b.putString(PerfettoListener.COLLECT_PER_RUN, "true");
mListener = initListener(b);
- doReturn(true).when(mPerfettoHelper).startCollecting(anyString());
+ doReturn(true).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
doReturn(true).when(mPerfettoHelper).stopCollecting(anyLong(), anyString());
// Test run start behavior
mListener.onTestRunStart(mListener.createDataRecord(), FAKE_DESCRIPTION);
- verify(mPerfettoHelper, times(1)).startCollecting(anyString());
+ verify(mPerfettoHelper, times(1)).startCollecting(anyString(), anyBoolean());
mListener.testStarted(mTest1Desc);
- verify(mPerfettoHelper, times(1)).startCollecting(anyString());
+ verify(mPerfettoHelper, times(1)).startCollecting(anyString(), anyBoolean());
mListener.onTestEnd(mDataRecord, mTest1Desc);
verify(mPerfettoHelper, times(0)).stopCollecting(anyLong(), anyString());
mListener.onTestRunEnd(mListener.createDataRecord(), new Result());
@@ -185,7 +186,7 @@ public class PerfettoListenerTest {
b.putString(PerfettoListener.COLLECT_PER_RUN, "true");
mListener = initListener(b);
- doReturn(true).when(mPerfettoHelper).startCollecting(anyString());
+ doReturn(true).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
doReturn(true).when(mPerfettoHelper).stopCollecting(anyLong(), anyString());
// Test run start behavior
@@ -204,7 +205,7 @@ public class PerfettoListenerTest {
Bundle b = new Bundle();
mListener = initListener(b);
- doReturn(true).when(mPerfettoHelper).startCollecting(anyString());
+ doReturn(true).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
doReturn(true).when(mPerfettoHelper).stopCollecting(anyLong(), anyString());
// Test run start behavior
@@ -225,7 +226,7 @@ public class PerfettoListenerTest {
b.putString(PerfettoListener.COLLECT_PER_RUN, "true");
mListener = initListener(b);
- doReturn(true).when(mPerfettoHelper).startCollecting(anyString());
+ doReturn(true).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
doReturn(true).when(mPerfettoHelper).stopCollecting(anyLong(), anyString());
// Test run start behavior
@@ -243,7 +244,7 @@ public class PerfettoListenerTest {
b.putString(PerfettoListener.HOLD_WAKELOCK_WHILE_COLLECTING, "true");
mListener = initListener(b);
- doReturn(true).when(mPerfettoHelper).startCollecting(anyString());
+ doReturn(true).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
doReturn(true).when(mPerfettoHelper).stopCollecting(anyLong(), anyString());
// Test run start behavior
@@ -264,7 +265,7 @@ public class PerfettoListenerTest {
b.putString(PerfettoListener.HOLD_WAKELOCK_WHILE_COLLECTING, "true");
mListener = initListener(b);
- doReturn(true).when(mPerfettoHelper).startCollecting(anyString());
+ doReturn(true).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
doReturn(true).when(mPerfettoHelper).stopCollecting(anyLong(), anyString());
// Test run start behavior
@@ -290,7 +291,7 @@ public class PerfettoListenerTest {
b.putString(PerfettoListener.COLLECT_PER_RUN, "true");
mListener = initListener(b);
- doReturn(true).when(mPerfettoHelper).startCollecting(anyString());
+ doReturn(true).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
doReturn(true).when(mPerfettoHelper).stopCollecting(anyLong(), anyString());
// Test run start behavior
@@ -315,11 +316,11 @@ public class PerfettoListenerTest {
Bundle b = new Bundle();
b.putString(PerfettoListener.COLLECT_PER_RUN, "true");
mListener = initListener(b);
- doReturn(false).when(mPerfettoHelper).startCollecting(anyString());
+ doReturn(false).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
// Test run start behavior
mListener.onTestRunStart(mListener.createDataRecord(), FAKE_DESCRIPTION);
- verify(mPerfettoHelper, times(1)).startCollecting(anyString());
+ verify(mPerfettoHelper, times(1)).startCollecting(anyString(), anyBoolean());
mListener.onTestRunEnd(mListener.createDataRecord(), new Result());
verify(mPerfettoHelper, times(0)).stopCollecting(anyLong(), anyString());
}
@@ -331,14 +332,14 @@ public class PerfettoListenerTest {
public void testPerfettoStartFailureFlow() throws Exception {
Bundle b = new Bundle();
mListener = initListener(b);
- doReturn(false).when(mPerfettoHelper).startCollecting(anyString());
+ doReturn(false).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
// Test run start behavior
mListener.testRunStarted(mRunDesc);
// Test test start behavior
mListener.testStarted(mTest1Desc);
- verify(mPerfettoHelper, times(1)).startCollecting(anyString());
+ verify(mPerfettoHelper, times(1)).startCollecting(anyString(), anyBoolean());
mListener.onTestEnd(mDataRecord, mTest1Desc);
verify(mPerfettoHelper, times(0)).stopCollecting(anyLong(), anyString());
}
@@ -351,7 +352,7 @@ public class PerfettoListenerTest {
public void testPerfettoInvocationCount() throws Exception {
Bundle b = new Bundle();
mListener = initListener(b);
- doReturn(true).when(mPerfettoHelper).startCollecting(anyString());
+ doReturn(true).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
doReturn(true).when(mPerfettoHelper).stopCollecting(anyLong(), anyString());
// Test run start behavior
@@ -359,19 +360,19 @@ public class PerfettoListenerTest {
// Test1 invocation 1 start behavior
mListener.testStarted(mTest1Desc);
- verify(mPerfettoHelper, times(1)).startCollecting(anyString());
+ verify(mPerfettoHelper, times(1)).startCollecting(anyString(), anyBoolean());
mListener.onTestEnd(mDataRecord, mTest1Desc);
verify(mPerfettoHelper, times(1)).stopCollecting(anyLong(), anyString());
// Test1 invocation 2 start behaviour
mListener.testStarted(mTest1Desc);
- verify(mPerfettoHelper, times(2)).startCollecting(anyString());
+ verify(mPerfettoHelper, times(2)).startCollecting(anyString(), anyBoolean());
mListener.onTestEnd(mDataRecord, mTest1Desc);
verify(mPerfettoHelper, times(2)).stopCollecting(anyLong(), anyString());
// Test2 invocation 1 start behaviour
mListener.testStarted(mTest2Desc);
- verify(mPerfettoHelper, times(3)).startCollecting(anyString());
+ verify(mPerfettoHelper, times(3)).startCollecting(anyString(), anyBoolean());
mDataRecord = mListener.createDataRecord();
mListener.onTestEnd(mDataRecord, mTest2Desc);
verify(mPerfettoHelper, times(3)).stopCollecting(anyLong(), anyString());
@@ -381,4 +382,27 @@ public class PerfettoListenerTest {
assertEquals(1, (int) mInvocationCount.get(mListener.getTestFileName(mTest2Desc)));
}
+
+ /*
+ * Verify perfetto start and stop collection methods called when the text
+ * proto config option is enabled
+ */
+ @Test
+ public void testPerfettoSuccessFlowWithTextConfig() throws Exception {
+ Bundle b = new Bundle();
+ b.putString(PerfettoListener.PERFETTO_CONFIG_TEXT_PROTO, "true");
+ mListener = initListener(b);
+ doReturn(true).when(mPerfettoHelper).startCollecting(anyString(), anyBoolean());
+ doReturn(true).when(mPerfettoHelper).stopCollecting(anyLong(), anyString());
+ // Test run start behavior
+ mListener.testRunStarted(mRunDesc);
+
+ // Test test start behavior
+ mListener.testStarted(mTest1Desc);
+ verify(mPerfettoHelper, times(1)).startCollecting(anyString(), anyBoolean());
+ mListener.onTestEnd(mDataRecord, mTest1Desc);
+ verify(mPerfettoHelper, times(1)).stopCollecting(anyLong(), anyString());
+
+ }
+
}
diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/RssSnapshotListenerTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/RssSnapshotListenerTest.java
new file mode 100644
index 000000000..883a0588b
--- /dev/null
+++ b/libraries/device-collectors/src/test/java/android/device/collectors/RssSnapshotListenerTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2018 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.device.collectors;
+
+import static android.device.collectors.RssSnapshotListener.DROP_CACHE_KEY;
+import static android.device.collectors.RssSnapshotListener.OUTPUT_DIR_KEY;
+import static android.device.collectors.RssSnapshotListener.PROCESS_NAMES_KEY;
+import static android.device.collectors.RssSnapshotListener.PROCESS_SEPARATOR;
+import static org.mockito.Mockito.verify;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+import androidx.test.runner.AndroidJUnit4;
+import com.android.helpers.RssSnapshotHelper;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Android Unit tests for {@link RssSnapshotListener}.
+ *
+ * To run:
+ * atest CollectorDeviceLibTest:android.device.collectors.RssSnapshotListenerTest
+ */
+@RunWith(AndroidJUnit4.class)
+public class RssSnapshotListenerTest {
+ @Mock private Instrumentation mInstrumentation;
+ @Mock private RssSnapshotHelper mRssSnapshotHelper;
+
+ private RssSnapshotListener mListener;
+ private Description mRunDesc;
+
+ private static final String VALID_OUTPUT_DIR = "/data/local/tmp";
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mRunDesc = Description.createSuiteDescription("run");
+ }
+
+ private RssSnapshotListener initListener(Bundle b) {
+ RssSnapshotListener listener = new RssSnapshotListener(b, mRssSnapshotHelper);
+ listener.setInstrumentation(mInstrumentation);
+ return listener;
+ }
+
+ @Test
+ public void testHelperReceivesProcessNames() throws Exception {
+ Bundle b = new Bundle();
+ b.putString(PROCESS_NAMES_KEY, "process1" + PROCESS_SEPARATOR + "process2");
+ b.putString(OUTPUT_DIR_KEY, VALID_OUTPUT_DIR);
+
+ mListener = initListener(b);
+
+ mListener.testRunStarted(mRunDesc);
+
+ verify(mRssSnapshotHelper).setUp(VALID_OUTPUT_DIR, "process1", "process2");
+ }
+
+ @Test
+ public void testAdditionalOptions() throws Exception {
+ Bundle b = new Bundle();
+ b.putString(PROCESS_NAMES_KEY, "process1");
+ b.putString(OUTPUT_DIR_KEY, VALID_OUTPUT_DIR);
+ b.putString(DROP_CACHE_KEY, "all");
+ mListener = initListener(b);
+
+ mListener.testRunStarted(mRunDesc);
+
+ verify(mRssSnapshotHelper).setUp(VALID_OUTPUT_DIR, "process1");
+ // DROP_CACHE_KEY values: "pagecache" = 1, "slab" = 2, "all" = 3
+ verify(mRssSnapshotHelper).setDropCacheOption(3);
+ }
+}
diff --git a/libraries/device-collectors/src/test/java/android/device/collectors/SfStatsListenerTest.java b/libraries/device-collectors/src/test/java/android/device/collectors/SfStatsListenerTest.java
new file mode 100644
index 000000000..68769f48a
--- /dev/null
+++ b/libraries/device-collectors/src/test/java/android/device/collectors/SfStatsListenerTest.java
@@ -0,0 +1,58 @@
+/*
+ * 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.device.collectors;
+
+import android.app.Instrumentation;
+import android.os.Bundle;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.helpers.SfStatsCollectionHelper;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.Description;
+import org.junit.runner.Result;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/** Unit tests for {@link SfStatsListener} specific behavior. */
+@RunWith(AndroidJUnit4.class)
+public final class SfStatsListenerTest {
+
+ // A {@code Description} to pass when faking a test run start call.
+ private static final Description RUN_DESCRIPTION = Description.createSuiteDescription("run");
+ private static final Description TEST_DESCRIPTION =
+ Description.createTestDescription("run", "test");
+
+ @Mock private SfStatsCollectionHelper mHelper;
+ @Mock private Instrumentation mInstrumentation;
+
+ @Before
+ public void setUp() throws Exception {
+ MockitoAnnotations.initMocks(this);
+ }
+
+ @Test
+ public void testCollect_allProcesses() throws Exception {
+ SfStatsListener collector = new SfStatsListener(new Bundle(), mHelper);
+ collector.setInstrumentation(mInstrumentation);
+ collector.testRunStarted(RUN_DESCRIPTION);
+ collector.testStarted(TEST_DESCRIPTION);
+ collector.testFinished(TEST_DESCRIPTION);
+ collector.testRunFinished(new Result());
+ }
+}
diff --git a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/ScheduledScenarioRunner.java b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/ScheduledScenarioRunner.java
index d54d24128..ffd54fee4 100644
--- a/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/ScheduledScenarioRunner.java
+++ b/libraries/health/runners/longevity/platform/src/android/platform/test/longevity/ScheduledScenarioRunner.java
@@ -57,7 +57,10 @@ public class ScheduledScenarioRunner extends LongevityClassRunner {
// Please note that in most cases (when the CUJ does not time out) the actual cushion for
// teardown is double the value below, as a cushion needs to be created inside the timeout
// rule and also outside of it.
- @VisibleForTesting static final long TEARDOWN_LEEWAY_MS = 2000;
+ // This parameter is configurable via the command line as the teardown time varies across CUJs.
+ @VisibleForTesting static final String TEARDOWN_LEEWAY_OPTION = "teardown-window_ms";
+ @VisibleForTesting static final long TEARDOWN_LEEWAY_DEFAULT = 3000L;
+ private long mTeardownLeewayMs = TEARDOWN_LEEWAY_DEFAULT;
private static final String LOG_TAG = ScheduledScenarioRunner.class.getSimpleName();
@@ -86,9 +89,13 @@ public class ScheduledScenarioRunner extends LongevityClassRunner {
mTotalTimeoutMs = max(timeout, 0);
// Ensure that the enforced timeout is non-negative. This cushion is built in so that the
// CUJ still has time for teardown steps when the test portion times out.
- mEnforcedTimeoutMs = max(mTotalTimeoutMs - TEARDOWN_LEEWAY_MS, 0);
+ mEnforcedTimeoutMs = max(mTotalTimeoutMs - mTeardownLeewayMs, 0);
mShouldIdle = shouldIdle;
mArguments = arguments;
+ mTeardownLeewayMs =
+ Long.parseLong(
+ arguments.getString(
+ TEARDOWN_LEEWAY_OPTION, String.valueOf(mTeardownLeewayMs)));
}
@Override
@@ -109,9 +116,9 @@ public class ScheduledScenarioRunner extends LongevityClassRunner {
// Run the underlying test and report exceptions.
statement.evaluate();
} finally {
- // If there is time left for idling (i.e. more than TEARDOWN_LEEWAY_MS),
+ // If there is time left for idling (i.e. more than mTeardownLeewayMs),
// and the scenario is set to stay in app, idle for the remainder of
- // its timeout window until TEARDOWN_LEEWAY_MS before the start time of
+ // its timeout window until mTeardownLeewayMs before the start time of
// the next scenario, before executing the scenario's @After methods.
// The above does not apply if current scenario is the last one, in
// which case the idle is never performed regardless of its after_test
@@ -125,7 +132,7 @@ public class ScheduledScenarioRunner extends LongevityClassRunner {
performIdleBeforeTeardown(
max(
getTimeRemainingForTimeoutRule()
- - TEARDOWN_LEEWAY_MS,
+ - mTeardownLeewayMs,
0));
}
}
@@ -254,4 +261,10 @@ public class ScheduledScenarioRunner extends LongevityClassRunner {
context.unregisterReceiver(receiver);
}
}
+
+ /** Expose the teardown leeway since tests rely on it for verifying timing. */
+ @VisibleForTesting
+ long getTeardownLeeway() {
+ return mTeardownLeewayMs;
+ }
}
diff --git a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ProfileSuiteTest.java b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ProfileSuiteTest.java
index 14d66de37..8d7237d3f 100644
--- a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ProfileSuiteTest.java
+++ b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ProfileSuiteTest.java
@@ -291,7 +291,8 @@ public class ProfileSuiteTest {
long expectedTimeout =
suiteTimeoutMsecs
- TimeUnit.SECONDS.toMillis(4)
- - ScheduledScenarioRunner.TEARDOWN_LEEWAY_MS;
+ - ScheduledScenarioRunner
+ .TEARDOWN_LEEWAY_DEFAULT;
return abs(exceptionTimeout - expectedTimeout)
<= SCHEDULE_LEEWAY_MS;
});
diff --git a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ScheduledScenarioRunnerTest.java b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ScheduledScenarioRunnerTest.java
index d36cc36ff..78bc467c1 100644
--- a/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ScheduledScenarioRunnerTest.java
+++ b/libraries/health/runners/longevity/platform/tests/src/android/platform/test/longevity/ScheduledScenarioRunnerTest.java
@@ -141,8 +141,7 @@ public class ScheduledScenarioRunnerTest {
exception
.getTimeUnit()
.toMillis(exception.getTimeout());
- long expectedTimeout =
- timeoutMs - ScheduledScenarioRunner.TEARDOWN_LEEWAY_MS;
+ long expectedTimeout = timeoutMs - runner.getTeardownLeeway();
return abs(exceptionTimeout - expectedTimeout)
<= TIMING_LEEWAY_MS;
});
@@ -196,8 +195,7 @@ public class ScheduledScenarioRunnerTest {
// the leeway set in @{link ScheduledScenarioRunner}.
verify(runner, times(1))
.performIdleBeforeNextScenario(
- getWithinMarginMatcher(
- ScheduledScenarioRunner.TEARDOWN_LEEWAY_MS, TIMING_LEEWAY_MS));
+ getWithinMarginMatcher(runner.getTeardownLeeway(), TIMING_LEEWAY_MS));
}
/** Test that a test set to stay in the app after the test idles after its @Test method. */
@@ -226,8 +224,7 @@ public class ScheduledScenarioRunnerTest {
verify(runner, times(1))
.performIdleBeforeTeardown(
getWithinMarginMatcher(
- timeoutMs - 2 * ScheduledScenarioRunner.TEARDOWN_LEEWAY_MS,
- TIMING_LEEWAY_MS));
+ timeoutMs - 2 * runner.getTeardownLeeway(), TIMING_LEEWAY_MS));
// Test should have passed.
verify(mRunNotifier, never()).fireTestFailure(any(Failure.class));
}
@@ -389,6 +386,23 @@ public class ScheduledScenarioRunnerTest {
Assert.assertTrue(abs(actualSleepDuration - expectedSleepMillis) <= TIMING_LEEWAY_MS);
}
+ /** Test that the teardown leeway override works. */
+ @Test
+ public void testTeardownLeewayOverride() throws Throwable {
+ Bundle args = new Bundle();
+ long leewayOverride = 1000L;
+ args.putString(
+ ScheduledScenarioRunner.TEARDOWN_LEEWAY_OPTION, String.valueOf(leewayOverride));
+ ScheduledScenarioRunner runner =
+ new ScheduledScenarioRunner(
+ ArgumentTest.class,
+ Scenario.newBuilder().build(),
+ TimeUnit.SECONDS.toMillis(6),
+ false,
+ args);
+ Assert.assertEquals(leewayOverride, runner.getTeardownLeeway());
+ }
+
/**
* Helper method to get an argument matcher that checks whether the input value is equal to
* expected value within a margin.
diff --git a/libraries/launcher-helper/src/android/support/test/launcherhelper/AutoLauncherStrategy.java b/libraries/launcher-helper/src/android/support/test/launcherhelper/AutoLauncherStrategy.java
index c547f0d7f..869d87019 100644
--- a/libraries/launcher-helper/src/android/support/test/launcherhelper/AutoLauncherStrategy.java
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/AutoLauncherStrategy.java
@@ -23,14 +23,14 @@ import android.support.test.uiautomator.Direction;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject2;
import android.support.test.uiautomator.Until;
+import android.system.helpers.CommandsHelper;
import java.util.Arrays;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
-import java.util.stream.Stream;
import java.util.stream.Collectors;
-
+import java.util.stream.Stream;
public class AutoLauncherStrategy implements IAutoLauncherStrategy {
private static final String LOG_TAG = AutoLauncherStrategy.class.getSimpleName();
@@ -88,6 +88,7 @@ public class AutoLauncherStrategy implements IAutoLauncherStrategy {
protected UiDevice mDevice;
private Instrumentation mInstrumentation;
+ private CommandsHelper mCommandsHelper;
/**
* {@inheritDoc}
@@ -111,6 +112,7 @@ public class AutoLauncherStrategy implements IAutoLauncherStrategy {
@Override
public void setInstrumentation(Instrumentation instrumentation) {
mInstrumentation = instrumentation;
+ mCommandsHelper = CommandsHelper.getInstance(mInstrumentation);
}
/**
@@ -201,9 +203,21 @@ public class AutoLauncherStrategy implements IAutoLauncherStrategy {
openFacet("Notification");
}
- /**
- * {@inheritDoc}
- */
+ /** {@inheritDoc} */
+ @Override
+ public void openNotifications() {
+ String cmd = "cmd statusbar expand-notifications";
+ mCommandsHelper.executeShellCommand(cmd);
+ }
+
+ /** {@inheritDoc} */
+ @Override
+ public void pressHome() {
+ String cmd = "input keyevent KEYCODE_HOME";
+ mCommandsHelper.executeShellCommand(cmd);
+ }
+
+ /** {@inheritDoc} */
@Override
public void openAssistantFacet() {
openFacet("Google Assistant");
@@ -326,6 +340,18 @@ public class AutoLauncherStrategy implements IAutoLauncherStrategy {
}
}
+ @Override
+ public void openBluetoothAudioApp() {
+ String appName = "Bluetooth Audio";
+ if (checkApplicationExists(appName)) {
+ UiObject2 app = mDevice.findObject(By.clickable(true).hasDescendant(By.text(appName)));
+ app.clickAndWait(Until.newWindow(), APP_LAUNCH_TIMEOUT);
+ mDevice.waitForIdle();
+ } else {
+ throw new RuntimeException(String.format("Application %s not found", appName));
+ }
+ }
+
private UiObject2 findApplication(String appName) {
BySelector appSelector = By.clickable(true).hasDescendant(By.text(appName));
if (mDevice.hasObject(SCROLLABLE_APP_LIST)) {
diff --git a/libraries/launcher-helper/src/android/support/test/launcherhelper/IAutoLauncherStrategy.java b/libraries/launcher-helper/src/android/support/test/launcherhelper/IAutoLauncherStrategy.java
index 8351b82a6..731d39cfa 100644
--- a/libraries/launcher-helper/src/android/support/test/launcherhelper/IAutoLauncherStrategy.java
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/IAutoLauncherStrategy.java
@@ -65,9 +65,10 @@ public interface IAutoLauncherStrategy extends ILauncherStrategy {
*/
void openAssistantFacet();
- /**
- * Open Quick Settings.
- */
+ /** This method is to open Bluetooth Audio application */
+ void openBluetoothAudioApp();
+
+ /** Open Quick Settings. */
void openQuickSettings();
/**
@@ -95,4 +96,10 @@ public interface IAutoLauncherStrategy extends ILauncherStrategy {
* @param appName application to be opened.
*/
void openApp(String appName);
+
+ /** This method is to open notifications */
+ void openNotifications();
+
+ /** This method is to navigate to device home */
+ void pressHome();
}
diff --git a/libraries/launcher-helper/src/android/support/test/launcherhelper/LauncherStrategyFactory.java b/libraries/launcher-helper/src/android/support/test/launcherhelper/LauncherStrategyFactory.java
index f812b991f..8a3db419d 100644
--- a/libraries/launcher-helper/src/android/support/test/launcherhelper/LauncherStrategyFactory.java
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/LauncherStrategyFactory.java
@@ -42,6 +42,7 @@ public class LauncherStrategyFactory {
mKnownLauncherStrategies = new HashSet<>();
registerLauncherStrategy(AospLauncherStrategy.class);
registerLauncherStrategy(AutoLauncherStrategy.class);
+ registerLauncherStrategy(VolvoLauncherStrategy.class);
registerLauncherStrategy(GoogleExperienceLauncherStrategy.class);
registerLauncherStrategy(Launcher3Strategy.class);
registerLauncherStrategy(NexusLauncherStrategy.class);
diff --git a/libraries/launcher-helper/src/android/support/test/launcherhelper/VolvoLauncherStrategy.java b/libraries/launcher-helper/src/android/support/test/launcherhelper/VolvoLauncherStrategy.java
new file mode 100644
index 000000000..695a9b26c
--- /dev/null
+++ b/libraries/launcher-helper/src/android/support/test/launcherhelper/VolvoLauncherStrategy.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2017 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.support.test.launcherhelper;
+
+import android.app.Instrumentation;
+import android.os.SystemClock;
+import android.support.test.uiautomator.By;
+import android.support.test.uiautomator.BySelector;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject2;
+import android.support.test.uiautomator.Until;
+
+import java.util.Map;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+/** Implementation of {@link ILauncherStrategy} to support Volvo launcher */
+public class VolvoLauncherStrategy extends AutoLauncherStrategy {
+ private static final String VOLVO_LAUNCHER_PACKAGE = "com.volvocars.launcher";
+ private static final String SYSTEM_UI_PACKAGE = "com.android.systemui";
+
+ private static final Map<String, BySelector> FACET_MAP =
+ Stream.of(
+ new Object[][] {
+ {
+ "App Grid",
+ By.res(SYSTEM_UI_PACKAGE, "nav_bar_apps").clickable(true)
+ },
+ })
+ .collect(
+ Collectors.toMap(
+ data -> (String) data[0], data -> (BySelector) data[1]));
+
+ private static final Map<String, BySelector> APP_OPEN_VERIFIERS =
+ Stream.of(
+ new Object[][] {
+ {"App Grid", By.res(VOLVO_LAUNCHER_PACKAGE, "apps_pane")},
+ })
+ .collect(
+ Collectors.toMap(
+ data -> (String) data[0], data -> (BySelector) data[1]));
+
+ private static final long APP_LAUNCH_TIMEOUT_MS = 10000;
+ private static final long UI_WAIT_TIMEOUT_MS = 5000;
+ private static final long POLL_INTERVAL = 100;
+
+ protected UiDevice mDevice;
+ private Instrumentation mInstrumentation;
+
+ @Override
+ public String getSupportedLauncherPackage() {
+ return VOLVO_LAUNCHER_PACKAGE;
+ }
+
+ @Override
+ public void setUiDevice(UiDevice uiDevice) {
+ mDevice = uiDevice;
+ }
+
+ @Override
+ public void setInstrumentation(Instrumentation instrumentation) {
+ super.setInstrumentation(instrumentation);
+ mInstrumentation = instrumentation;
+ }
+
+ @Override
+ public void openApp(String appName) {
+ if (checkApplicationExists(appName)) {
+ UiObject2 app = mDevice.findObject(By.clickable(true).hasDescendant(By.text(appName)));
+ app.clickAndWait(Until.newWindow(), APP_LAUNCH_TIMEOUT_MS);
+ mDevice.waitForIdle();
+ } else {
+ throw new RuntimeException(String.format("Application %s not found", appName));
+ }
+ }
+
+ @Override
+ public void openBluetoothAudioApp() {
+ String appName = "Bluetooth Media Player";
+ if (checkApplicationExists(appName)) {
+ UiObject2 app = mDevice.findObject(By.clickable(true).hasDescendant(By.text(appName)));
+ app.clickAndWait(Until.newWindow(), APP_LAUNCH_TIMEOUT_MS);
+ mDevice.waitForIdle();
+ } else {
+ throw new RuntimeException(String.format("Application %s not found", appName));
+ }
+ }
+
+ @Override
+ public boolean checkApplicationExists(String appName) {
+ openAppGridFacet();
+ UiObject2 app = findApplication(appName);
+ return app != null;
+ }
+
+ @Override
+ public void openAppGridFacet() {
+ openFacet("App Grid");
+ }
+
+ @Override
+ public void openMapsFacet() {
+ // Volvo does not have Facet for Maps, so open Maps from App Grid
+ openApp("Maps");
+ }
+
+ private void openFacet(String facetName) {
+ BySelector facetSelector = FACET_MAP.get(facetName);
+ UiObject2 facet = mDevice.findObject(facetSelector);
+ if (facet != null) {
+ facet.click();
+ waitUntilAppOpen(facetName, APP_LAUNCH_TIMEOUT_MS);
+ } else {
+ throw new RuntimeException(String.format("Failed to find %s facet.", facetName));
+ }
+ }
+
+ private void waitUntilAppOpen(String appName, long timeout) {
+ SystemClock.sleep(timeout);
+ long startTime = SystemClock.uptimeMillis();
+ boolean isOpen = false;
+ do {
+ isOpen = mDevice.hasObject(APP_OPEN_VERIFIERS.get(appName));
+ if (isOpen) {
+ break;
+ }
+ SystemClock.sleep(POLL_INTERVAL);
+ } while ((SystemClock.uptimeMillis() - startTime) < timeout);
+ if (!isOpen) {
+ throw new IllegalStateException(
+ String.format(
+ "Did not find any app of %s in foreground after %d ms.",
+ appName, timeout));
+ }
+ }
+
+ private UiObject2 findApplication(String appName) {
+ BySelector appSelector = By.clickable(true).hasDescendant(By.text(appName));
+ return mDevice.findObject(appSelector);
+ }
+}
diff --git a/scripts/perfetto-setup/Android.mk b/scripts/perfetto-setup/Android.mk
new file mode 100644
index 000000000..a742eea8d
--- /dev/null
+++ b/scripts/perfetto-setup/Android.mk
@@ -0,0 +1,48 @@
+#
+# 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.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_MODULE := trace_config_detailed.textproto
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PREBUILT_MODULE_FILE := prebuilts/tools/linux-x86_64/perfetto/configs/trace_config_detailed.textproto
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := long_trace_config.textproto
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PREBUILT_MODULE_FILE := prebuilts/tools/linux-x86_64/perfetto/configs/long_trace_config.textproto
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := trace_config.textproto
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PREBUILT_MODULE_FILE := prebuilts/tools/linux-x86_64/perfetto/configs/trace_config.textproto
+include $(BUILD_PREBUILT)
+
+include $(CLEAR_VARS)
+LOCAL_MODULE := trace_config_experimental.textproto
+LOCAL_MODULE_CLASS := ETC
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA)/local/tmp
+LOCAL_PREBUILT_MODULE_FILE := prebuilts/tools/linux-x86_64/perfetto/configs/trace_config_experimental.textproto
+include $(BUILD_PREBUILT)
+
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/sleep/Idle.java b/tests/health/scenarios/src/android/platform/test/scenario/sleep/Idle.java
index 882523c1c..4efbd071f 100644
--- a/tests/health/scenarios/src/android/platform/test/scenario/sleep/Idle.java
+++ b/tests/health/scenarios/src/android/platform/test/scenario/sleep/Idle.java
@@ -18,8 +18,10 @@ package android.platform.test.scenario.sleep;
import android.os.SystemClock;
import android.platform.test.option.LongOption;
+import android.platform.test.rule.NaturalOrientationRule;
import android.platform.test.scenario.annotation.Scenario;
+import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -31,6 +33,9 @@ import org.junit.runners.JUnit4;
@Scenario
@RunWith(JUnit4.class)
public class Idle {
+ // Class-level rules
+ @ClassRule public static NaturalOrientationRule orientationRule = new NaturalOrientationRule();
+
@Rule public final LongOption mDurationMs = new LongOption("durationMs").setDefault(1000L);
@Test
diff --git a/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java b/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java
index 1f5e23475..a2e1290c4 100644
--- a/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java
+++ b/tests/health/scenarios/src/android/platform/test/scenario/system/ScreenOff.java
@@ -57,8 +57,11 @@ public class ScreenOff {
@After
public void tearDown() throws RemoteException {
if (mTurnScreenBackOn.get()) {
- mDevice.wakeUp();
+ // Wake up the display. wakeUp() is not used here as when the duration is short, the
+ // device might register a double power button press and launch camera.
+ mDevice.pressMenu();
mDevice.waitForIdle();
+ // Unlock the screen.
mDevice.pressMenu();
mDevice.waitForIdle();
}