Merge "Enable screen recording in legacy app launch test." into rvc-dev am: 0e82ef5b1d am: 4853f5aee0 am: d6e1c202db am: 3feb950318 am: 6df9508273
Change-Id: I0ffc17cedfc0ef0e13416bf081d8856d136948f0
diff --git a/tests/AppLaunch/Android.bp b/tests/AppLaunch/Android.bp
index f90f26f..75db551 100644
--- a/tests/AppLaunch/Android.bp
+++ b/tests/AppLaunch/Android.bp
@@ -8,6 +8,8 @@
"android.test.base",
"android.test.runner",
],
- static_libs: ["androidx.test.rules"],
+ static_libs: [
+ "androidx.test.rules",
+ "ub-uiautomator"],
test_suites: ["device-tests"],
}
diff --git a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
index 2d2f4db..7d750b7 100644
--- a/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
+++ b/tests/AppLaunch/src/com/android/tests/applaunch/AppLaunch.java
@@ -15,6 +15,8 @@
*/
package com.android.tests.applaunch;
+import static org.junit.Assert.assertNotNull;
+
import android.accounts.Account;
import android.accounts.AccountManager;
import android.app.ActivityManager;
@@ -29,7 +31,9 @@
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
+import android.os.SystemClock;
import android.os.UserHandle;
+import android.support.test.uiautomator.UiDevice;
import android.test.InstrumentationTestCase;
import android.test.InstrumentationTestRunner;
import android.util.Log;
@@ -46,6 +50,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
+import java.nio.file.Paths;
import java.time.format.DateTimeFormatter;
import java.time.ZonedDateTime;
import java.time.ZoneOffset;
@@ -67,6 +72,7 @@
* in the following format:
* -e apps <app name>^<result key>|<app name>^<result key>
*/
+@Deprecated
public class AppLaunch extends InstrumentationTestCase {
private static final int JOIN_TIMEOUT = 10000;
@@ -94,6 +100,9 @@
private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
private static final String KEY_COMPILER_FILTERS = "compiler_filters";
private static final String KEY_FORCE_STOP_APP = "force_stop_app";
+ private static final String ENABLE_SCREEN_RECORDING = "enable_screen_recording";
+ private static final int MAX_RECORDING_PARTS = 5;
+ private static final long VIDEO_TAIL_BUFFER = 500;
private static final String SIMPLEPERF_APP_CMD =
"simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
@@ -144,14 +153,17 @@
private Map<String, Intent> mNameToIntent;
private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
+ private RecordingThread mCurrentThread;
private Map<String, String> mNameToResultKey;
private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
private IActivityManager mAm;
+ private File launchSubDir = null;
private String mSimplePerfCmd = null;
private String mLaunchOrder = null;
private boolean mDropCache = false;
private int mLaunchIterations = 10;
private boolean mForceStopApp = true;
+ private boolean mEnableRecording = false;
private int mTraceLaunchCount = 0;
private String mTraceDirectoryStr = null;
private Bundle mResult = new Bundle();
@@ -166,6 +178,7 @@
private boolean mCycleCleanUp = false;
private boolean mTraceAll = false;
private boolean mIterationCycle = false;
+ private UiDevice mDevice;
enum IorapStatus {
UNDEFINED,
@@ -222,7 +235,7 @@
}
try {
- File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
+ launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
throw new IOException("Unable to create the lauch file sub directory "
@@ -923,9 +936,16 @@
mLaunchIterations = Integer.parseInt(launchIterations);
}
String forceStopApp = args.getString(KEY_FORCE_STOP_APP);
+
if (forceStopApp != null) {
mForceStopApp = Boolean.parseBoolean(forceStopApp);
}
+
+ String enableRecording = args.getString(ENABLE_SCREEN_RECORDING);
+
+ if (enableRecording != null) {
+ mEnableRecording = Boolean.parseBoolean(enableRecording);
+ }
String appList = args.getString(KEY_APPS);
if (appList == null)
return;
@@ -1038,6 +1058,9 @@
private AppLaunchResult startApp(String appName, String launchReason)
throws NameNotFoundException, RemoteException {
Log.i(TAG, "Starting " + appName);
+ if(mEnableRecording) {
+ startRecording(appName, launchReason);
+ }
Intent startIntent = mNameToIntent.get(appName);
if (startIntent == null) {
@@ -1053,6 +1076,10 @@
} catch (InterruptedException e) {
// ignore
}
+
+ if(mEnableRecording) {
+ stopRecording();
+ }
return runnable.getResult();
}
@@ -1360,4 +1387,126 @@
}
}
+
+ /**
+ * Start the screen recording while launching the app.
+ *
+ * @param appName
+ * @param launchReason
+ */
+ private void startRecording(String appName, String launchReason) {
+ Log.v(TAG, "Started Recording");
+ mCurrentThread = new RecordingThread("test-screen-record",
+ String.format("%s_%s", appName, launchReason));
+ mCurrentThread.start();
+ }
+
+ /**
+ * Stop already started screen recording.
+ */
+ private void stopRecording() {
+ // Skip if not directory.
+ if (launchSubDir == null) {
+ return;
+ }
+
+ // Add some extra time to the video end.
+ SystemClock.sleep(VIDEO_TAIL_BUFFER);
+ // Ctrl + C all screen record processes.
+ mCurrentThread.cancel();
+ // Wait for the thread to completely die.
+ try {
+ mCurrentThread.join();
+ } catch (InterruptedException ex) {
+ Log.e(TAG, "Interrupted when joining the recording thread.", ex);
+ }
+ Log.v(TAG, "Stopped Recording");
+ }
+
+ /** Returns the recording's name for part {@code part} of launch description. */
+ private File getOutputFile(String description, int part) {
+ // Omit the iteration number for the first iteration.
+ final String fileName =
+ String.format(
+ "%s-video%s.mp4", description, part == 1 ? "" : part);
+ return Paths.get(launchSubDir.getAbsolutePath(), description).toFile();
+ }
+
+
+ /**
+ * Encapsulates the start and stop screen recording logic.
+ * Copied from ScreenRecordCollector.
+ */
+ private class RecordingThread extends Thread {
+ private final String mDescription;
+ private final List<File> mRecordings;
+
+ private boolean mContinue;
+
+ public RecordingThread(String name, String description) {
+ super(name);
+
+ mContinue = true;
+ mRecordings = new ArrayList<>();
+
+ assertNotNull("No test description provided for recording.", description);
+ mDescription = description;
+ }
+
+ @Override
+ public void run() {
+ try {
+ // Start at i = 1 to encode parts as X.mp4, X2.mp4, X3.mp4, etc.
+ for (int i = 1; i <= MAX_RECORDING_PARTS && mContinue; i++) {
+ File output = getOutputFile(mDescription, i);
+ Log.d(
+ TAG,
+ String.format("Recording screen to %s", output.getAbsolutePath()));
+ mRecordings.add(output);
+ // Make sure not to block on this background command in the main thread so
+ // that the test continues to run, but block in this thread so it does not
+ // trigger a new screen recording session before the prior one completes.
+ getDevice().executeShellCommand(
+ String.format("screenrecord %s", output.getAbsolutePath()));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Caught exception while screen recording.");
+ }
+ }
+
+ public void cancel() {
+ mContinue = false;
+
+ // Identify the screenrecord PIDs and send SIGINT 2 (Ctrl + C) to each.
+ try {
+ String[] pids = getDevice().executeShellCommand(
+ "pidof screenrecord").split(" ");
+ for (String pid : pids) {
+ // Avoid empty process ids, because of weird splitting behavior.
+ if (pid.isEmpty()) {
+ continue;
+ }
+
+ getDevice().executeShellCommand(
+ String.format("kill -2 %s", pid));
+ Log.d(
+ TAG,
+ String.format("Sent SIGINT 2 to screenrecord process (%s)", pid));
+ }
+ } catch (IOException e) {
+ throw new RuntimeException("Failed to kill screen recording process.");
+ }
+ }
+
+ public List<File> getRecordings() {
+ return mRecordings;
+ }
+ }
+
+ public UiDevice getDevice() {
+ if (mDevice == null) {
+ mDevice = UiDevice.getInstance(getInstrumentation());
+ }
+ return mDevice;
+ }
}