Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 583c704e authored by Gopinath Elanchezhian's avatar Gopinath Elanchezhian
Browse files

Enable screen recording in legacy app launch test.

Bug: b/151967185

Test: AppLaunchTest
Change-Id: I3e14b3119b08432ad8b81e57685354867407e7e3
parent 08447f26
Loading
Loading
Loading
Loading
+3 −1
Original line number Original line Diff line number Diff line
@@ -8,6 +8,8 @@ android_test {
        "android.test.base",
        "android.test.base",
        "android.test.runner",
        "android.test.runner",
    ],
    ],
    static_libs: ["androidx.test.rules"],
    static_libs: [
        "androidx.test.rules",
        "ub-uiautomator"],
    test_suites: ["device-tests"],
    test_suites: ["device-tests"],
}
}
+150 −1
Original line number Original line Diff line number Diff line
@@ -15,6 +15,8 @@
 */
 */
package com.android.tests.applaunch;
package com.android.tests.applaunch;


import static org.junit.Assert.assertNotNull;

import android.accounts.Account;
import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManager;
import android.app.ActivityManager;
import android.app.ActivityManager;
@@ -29,7 +31,9 @@ import android.content.pm.ResolveInfo;
import android.os.Bundle;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor;
import android.os.RemoteException;
import android.os.RemoteException;
import android.os.SystemClock;
import android.os.UserHandle;
import android.os.UserHandle;
import android.support.test.uiautomator.UiDevice;
import android.test.InstrumentationTestCase;
import android.test.InstrumentationTestCase;
import android.test.InstrumentationTestRunner;
import android.test.InstrumentationTestRunner;
import android.util.Log;
import android.util.Log;
@@ -46,6 +50,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.OutputStreamWriter;
import java.nio.file.Paths;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeFormatter;
import java.time.ZonedDateTime;
import java.time.ZonedDateTime;
import java.time.ZoneOffset;
import java.time.ZoneOffset;
@@ -67,6 +72,7 @@ import java.util.Set;
 * in the following format:
 * in the following format:
 * -e apps <app name>^<result key>|<app name>^<result key>
 * -e apps <app name>^<result key>|<app name>^<result key>
 */
 */
@Deprecated
public class AppLaunch extends InstrumentationTestCase {
public class AppLaunch extends InstrumentationTestCase {


    private static final int JOIN_TIMEOUT = 10000;
    private static final int JOIN_TIMEOUT = 10000;
@@ -94,6 +100,9 @@ public class AppLaunch extends InstrumentationTestCase {
    private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
    private static final String KEY_TRACE_DUMPINTERVAL = "tracedump_interval";
    private static final String KEY_COMPILER_FILTERS = "compiler_filters";
    private static final String KEY_COMPILER_FILTERS = "compiler_filters";
    private static final String KEY_FORCE_STOP_APP = "force_stop_app";
    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 =
    private static final String SIMPLEPERF_APP_CMD =
            "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
            "simpleperf --log fatal stat --csv -e cpu-cycles,major-faults --app %s & %s";
@@ -144,14 +153,17 @@ public class AppLaunch extends InstrumentationTestCase {


    private Map<String, Intent> mNameToIntent;
    private Map<String, Intent> mNameToIntent;
    private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
    private List<LaunchOrder> mLaunchOrderList = new ArrayList<LaunchOrder>();
    private RecordingThread mCurrentThread;
    private Map<String, String> mNameToResultKey;
    private Map<String, String> mNameToResultKey;
    private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
    private Map<String, Map<String, List<AppLaunchResult>>> mNameToLaunchTime;
    private IActivityManager mAm;
    private IActivityManager mAm;
    private File launchSubDir = null;
    private String mSimplePerfCmd = null;
    private String mSimplePerfCmd = null;
    private String mLaunchOrder = null;
    private String mLaunchOrder = null;
    private boolean mDropCache = false;
    private boolean mDropCache = false;
    private int mLaunchIterations = 10;
    private int mLaunchIterations = 10;
    private boolean mForceStopApp = true;
    private boolean mForceStopApp = true;
    private boolean mEnableRecording = false;
    private int mTraceLaunchCount = 0;
    private int mTraceLaunchCount = 0;
    private String mTraceDirectoryStr = null;
    private String mTraceDirectoryStr = null;
    private Bundle mResult = new Bundle();
    private Bundle mResult = new Bundle();
@@ -166,6 +178,7 @@ public class AppLaunch extends InstrumentationTestCase {
    private boolean mCycleCleanUp = false;
    private boolean mCycleCleanUp = false;
    private boolean mTraceAll = false;
    private boolean mTraceAll = false;
    private boolean mIterationCycle = false;
    private boolean mIterationCycle = false;
    private UiDevice mDevice;


    enum IorapStatus {
    enum IorapStatus {
        UNDEFINED,
        UNDEFINED,
@@ -222,7 +235,7 @@ public class AppLaunch extends InstrumentationTestCase {
        }
        }


        try {
        try {
            File launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);
            launchSubDir = new File(launchRootDir, LAUNCH_SUB_DIRECTORY);


            if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
            if (!launchSubDir.exists() && !launchSubDir.mkdirs()) {
                throw new IOException("Unable to create the lauch file sub directory "
                throw new IOException("Unable to create the lauch file sub directory "
@@ -921,9 +934,16 @@ public class AppLaunch extends InstrumentationTestCase {
            mLaunchIterations = Integer.parseInt(launchIterations);
            mLaunchIterations = Integer.parseInt(launchIterations);
        }
        }
        String forceStopApp = args.getString(KEY_FORCE_STOP_APP);
        String forceStopApp = args.getString(KEY_FORCE_STOP_APP);

        if (forceStopApp != null) {
        if (forceStopApp != null) {
            mForceStopApp = Boolean.parseBoolean(forceStopApp);
            mForceStopApp = Boolean.parseBoolean(forceStopApp);
        }
        }

        String enableRecording = args.getString(ENABLE_SCREEN_RECORDING);

        if (enableRecording != null) {
            mEnableRecording = Boolean.parseBoolean(enableRecording);
        }
        String appList = args.getString(KEY_APPS);
        String appList = args.getString(KEY_APPS);
        if (appList == null)
        if (appList == null)
            return;
            return;
@@ -1036,6 +1056,9 @@ public class AppLaunch extends InstrumentationTestCase {
    private AppLaunchResult startApp(String appName, String launchReason)
    private AppLaunchResult startApp(String appName, String launchReason)
            throws NameNotFoundException, RemoteException {
            throws NameNotFoundException, RemoteException {
        Log.i(TAG, "Starting " + appName);
        Log.i(TAG, "Starting " + appName);
        if(mEnableRecording) {
            startRecording(appName, launchReason);
        }


        Intent startIntent = mNameToIntent.get(appName);
        Intent startIntent = mNameToIntent.get(appName);
        if (startIntent == null) {
        if (startIntent == null) {
@@ -1051,6 +1074,10 @@ public class AppLaunch extends InstrumentationTestCase {
        } catch (InterruptedException e) {
        } catch (InterruptedException e) {
            // ignore
            // ignore
        }
        }

        if(mEnableRecording) {
            stopRecording();
        }
        return runnable.getResult();
        return runnable.getResult();
    }
    }


@@ -1358,4 +1385,126 @@ public class AppLaunch extends InstrumentationTestCase {
        }
        }


    }
    }

    /**
     * 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;
    }
}
}