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;
+    }
 }