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

Commit d6cd319f authored by Riddle Hsu's avatar Riddle Hsu Committed by Android (Google) Code Review
Browse files

Merge "Extract window related perf test base classes"

parents 0172d2e4 700bdd58
Loading
Loading
Loading
Loading
+2 −128
Original line number Diff line number Diff line
@@ -16,134 +16,8 @@

package android.inputmethod;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.inputmethod.ImePerfTestBase.executeShellCommand;
import static android.inputmethod.ImePerfTestBase.runWithShellPermissionIdentity;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.inputmethod.ImePerfTestBase.SettingsSession;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.SystemClock;
import android.provider.Settings;
import android.util.Log;
import android.view.WindowManagerPolicyConstants;

import androidx.test.platform.app.InstrumentationRegistry;

import com.android.internal.policy.PhoneWindow;

import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.notification.RunListener;

import java.util.List;
import android.perftests.utils.WindowPerfRunPreconditionBase;

/** Prepare the preconditions before running performance test. */
public class ImePerfRunPrecondition extends RunListener {
    private static final String TAG = ImePerfRunPrecondition.class.getSimpleName();

    private static final String ARGUMENT_LOG_ONLY = "log";
    private static final String ARGUMENT_KILL_BACKGROUND = "kill-bg";
    private static final String ARGUMENT_PROFILING_ITERATIONS = "profiling-iterations";
    private static final String ARGUMENT_PROFILING_SAMPLING = "profiling-sampling";
    private static final String DEFAULT_PROFILING_ITERATIONS = "10";
    private static final String DEFAULT_PROFILING_SAMPLING_US = "10";
    private static final long KILL_BACKGROUND_WAIT_MS = 3000;

    /** The requested iterations to run with method profiling. */
    static int sProfilingIterations;

    /** The interval of sample profiling in microseconds. */
    static int sSamplingIntervalUs;

    private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
    private long mWaitPreconditionDoneMs = 500;

    private final SettingsSession<Integer> mStayOnWhilePluggedInSetting = new SettingsSession<>(
            Settings.Global.getInt(mContext.getContentResolver(),
                    Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0),
            value -> executeShellCommand(String.format("settings put global %s %d",
                    Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value)));

    private final SettingsSession<Integer> mNavigationModeSetting = new SettingsSession<>(
            mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_navBarInteractionMode),
            value -> {
                final String navOverlay;
                switch (value) {
                    case WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL:
                    default:
                        navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
                        break;
                }
                executeShellCommand("cmd overlay enable-exclusive " + navOverlay);
            });

    /** It only executes once before all tests. */
    @Override
    public void testRunStarted(Description description) {
        final Bundle arguments = InstrumentationRegistry.getArguments();
        // If true, it only logs the method names without running.
        final boolean skip = Boolean.parseBoolean(arguments.getString(ARGUMENT_LOG_ONLY, "false"));
        Log.i(TAG, "arguments=" + arguments);
        if (skip) {
            return;
        }
        sProfilingIterations = Integer.parseInt(
                arguments.getString(ARGUMENT_PROFILING_ITERATIONS, DEFAULT_PROFILING_ITERATIONS));
        sSamplingIntervalUs = Integer.parseInt(
                arguments.getString(ARGUMENT_PROFILING_SAMPLING, DEFAULT_PROFILING_SAMPLING_US));

        // Use same navigation mode (gesture navigation) across all devices and tests
        // for consistency.
        mNavigationModeSetting.set(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
        // Keep the device awake during testing.
        mStayOnWhilePluggedInSetting.set(BatteryManager.BATTERY_PLUGGED_ANY);

        runWithShellPermissionIdentity(() -> {
            final ActivityTaskManager atm = mContext.getSystemService(ActivityTaskManager.class);
            atm.removeAllVisibleRecentTasks();
            atm.removeRootTasksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD,
                    ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED });
        });
        PhoneWindow.sendCloseSystemWindows(mContext, "ImePerfTests");

        if (Boolean.parseBoolean(arguments.getString(ARGUMENT_KILL_BACKGROUND))) {
            runWithShellPermissionIdentity(this::killBackgroundProcesses);
            mWaitPreconditionDoneMs = KILL_BACKGROUND_WAIT_MS;
        }
        // Wait a while for the precondition setup to complete.
        SystemClock.sleep(mWaitPreconditionDoneMs);
    }

    private void killBackgroundProcesses() {
        Log.i(TAG, "Killing background processes...");
        final ActivityManager am = mContext.getSystemService(ActivityManager.class);
        final List<RunningAppProcessInfo> processes = am.getRunningAppProcesses();
        if (processes == null) {
            return;
        }
        for (RunningAppProcessInfo processInfo : processes) {
            if (processInfo.importanceReasonCode == RunningAppProcessInfo.REASON_UNKNOWN
                    && processInfo.importance > RunningAppProcessInfo.IMPORTANCE_SERVICE) {
                for (String pkg : processInfo.pkgList) {
                    am.forceStopPackage(pkg);
                }
            }
        }
    }

    /** It only executes once after all tests. */
    @Override
    public void testRunFinished(Result result) {
        mNavigationModeSetting.close();
        mStayOnWhilePluggedInSetting.close();
    }
public class ImePerfRunPrecondition extends WindowPerfRunPreconditionBase {
}
+3 −7
Original line number Diff line number Diff line
@@ -29,7 +29,6 @@ import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.inputmethodservice.InputMethodService;
import android.os.ParcelFileDescriptor;
import android.os.Process;
import android.os.SystemClock;
import android.perftests.utils.ManualBenchmarkState;
@@ -420,23 +419,20 @@ public class ImePerfTest extends ImePerfTestBase
                });
    }

    private void startAsyncAtrace() throws IOException {
    private void startAsyncAtrace() {
        mIsTraceStarted = true;
        // IMF uses 'wm' component for trace in InputMethodService, InputMethodManagerService,
        // WindowManagerService and 'view' for client window (InsetsController).
        // TODO(b/167947940): Consider a separate input_method atrace
        UI_AUTOMATION.executeShellCommand("atrace -b 32768 --async_start wm view");
        // Avoid atrace isn't ready immediately.
        SystemClock.sleep(TimeUnit.NANOSECONDS.toMillis(TIME_1_S_IN_NS));
        startAsyncAtrace("wm view");
    }

    private void stopAsyncAtrace() {
        if (!mIsTraceStarted) {
            return;
        }
        final ParcelFileDescriptor pfd = UI_AUTOMATION.executeShellCommand("atrace --async_stop");
        mIsTraceStarted = false;
        final InputStream inputStream = new ParcelFileDescriptor.AutoCloseInputStream(pfd);
        final InputStream inputStream = stopAsyncAtraceWithStream();
        try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) {
            String line;
            while ((line = reader.readLine()) != null) {
+4 −118
Original line number Diff line number Diff line
@@ -18,135 +18,21 @@ package android.inputmethod;

import static android.perftests.utils.PerfTestActivity.INTENT_EXTRA_ADD_EDIT_TEXT;

import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;

import android.app.KeyguardManager;
import android.app.UiAutomation;
import android.content.Context;
import android.content.Intent;
import android.os.ParcelFileDescriptor;
import android.os.PowerManager;
import android.perftests.utils.PerfTestActivity;
import android.perftests.utils.WindowPerfTestBase;


import androidx.test.rule.ActivityTestRule;

import org.junit.BeforeClass;

import java.io.ByteArrayOutputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.Objects;
import java.util.function.Consumer;

public class ImePerfTestBase {
    static final UiAutomation UI_AUTOMATION = getInstrumentation().getUiAutomation();
    static final long NANOS_PER_S = 1000L * 1000 * 1000;
    static final long TIME_1_S_IN_NS = 1 * NANOS_PER_S;
public class ImePerfTestBase extends WindowPerfTestBase {
    static final long TIMEOUT_1_S_IN_MS = 1 * 1000L;

    @BeforeClass
    public static void setUpOnce() {
        final Context context = getInstrumentation().getContext();

        if (!context.getSystemService(PowerManager.class).isInteractive()
                || context.getSystemService(KeyguardManager.class).isKeyguardLocked()) {
            executeShellCommand("input keyevent KEYCODE_WAKEUP");
            executeShellCommand("wm dismiss-keyguard");
        }
        context.startActivity(new Intent(Intent.ACTION_MAIN)
                .addCategory(Intent.CATEGORY_HOME).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
    }

    /**
     * Executes shell command with reading the output. It may also used to block until the current
     * command is completed.
     */
    static ByteArrayOutputStream executeShellCommand(String command) {
        final ParcelFileDescriptor pfd = UI_AUTOMATION.executeShellCommand(command);
        final byte[] buf = new byte[512];
        final ByteArrayOutputStream bytes = new ByteArrayOutputStream();
        int bytesRead;
        try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(pfd)) {
            while ((bytesRead = fis.read(buf)) != -1) {
                bytes.write(buf, 0, bytesRead);
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        return bytes;
    }

    /** Returns how many iterations should run with method tracing. */
    static int getProfilingIterations() {
        return ImePerfRunPrecondition.sProfilingIterations;
    }

    static void runWithShellPermissionIdentity(Runnable runnable) {
        UI_AUTOMATION.adoptShellPermissionIdentity();
        try {
            runnable.run();
        } finally {
            UI_AUTOMATION.dropShellPermissionIdentity();
        }
    }

    static class SettingsSession<T> implements AutoCloseable {
        private final Consumer<T> mSetter;
        private final T mOriginalValue;
        private boolean mChanged;

        SettingsSession(T originalValue, Consumer<T> setter) {
            mOriginalValue = originalValue;
            mSetter = setter;
        }

        void set(T value) {
            if (Objects.equals(value, mOriginalValue)) {
                mChanged = false;
                return;
            }
            mSetter.accept(value);
            mChanged = true;
        }

        @Override
        public void close() {
            if (mChanged) {
                mSetter.accept(mOriginalValue);
            }
        }
    }

    /**
     * Provides an activity that keeps screen on and is able to wait for a stable lifecycle stage.
     */
    static class PerfTestActivityRule extends ActivityTestRule<PerfTestActivity> {
        private final Intent mStartIntent =
                new Intent(getInstrumentation().getTargetContext(), PerfTestActivity.class);

        PerfTestActivityRule() {
            this(false /* launchActivity */);
        }

        PerfTestActivityRule(boolean launchActivity) {
            super(PerfTestActivity.class, false /* initialTouchMode */, launchActivity);
        }

        @Override
        protected Intent getActivityIntent() {
            return mStartIntent;
        }
    /** Provides an activity that contains an edit text view.*/
    static class PerfTestActivityRule extends PerfTestActivityRuleBase {

        @Override
        public PerfTestActivity launchActivity(Intent intent) {
            intent.putExtra(INTENT_EXTRA_ADD_EDIT_TEXT, true);
            return super.launchActivity(intent);
        }

        PerfTestActivity launchActivity() {
            return launchActivity(mStartIntent);
        }
    }

    static String[] buildArray(String[]... arrays) {
+155 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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.perftests.utils;

import static android.app.WindowConfiguration.ACTIVITY_TYPE_ASSISTANT;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_RECENTS;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_STANDARD;
import static android.app.WindowConfiguration.ACTIVITY_TYPE_UNDEFINED;
import static android.perftests.utils.WindowPerfTestBase.executeShellCommand;
import static android.perftests.utils.WindowPerfTestBase.runWithShellPermissionIdentity;

import android.app.ActivityManager;
import android.app.ActivityManager.RunningAppProcessInfo;
import android.app.ActivityTaskManager;
import android.content.Context;
import android.os.BatteryManager;
import android.os.Bundle;
import android.os.SystemClock;
import android.perftests.utils.WindowPerfTestBase.SettingsSession;
import android.provider.Settings;
import android.util.Log;
import android.view.WindowManagerPolicyConstants;

import androidx.test.platform.app.InstrumentationRegistry;

import com.android.internal.policy.PhoneWindow;

import org.junit.runner.Description;
import org.junit.runner.Result;
import org.junit.runner.notification.RunListener;

import java.util.List;

/** Prepare the preconditions before running performance test. */
public class WindowPerfRunPreconditionBase extends RunListener {
    protected final String mTag = getClass().getSimpleName();

    private static final String ARGUMENT_LOG_ONLY = "log";
    private static final String ARGUMENT_KILL_BACKGROUND = "kill-bg";
    private static final String ARGUMENT_PROFILING_ITERATIONS = "profiling-iterations";
    private static final String ARGUMENT_PROFILING_SAMPLING = "profiling-sampling";
    private static final String DEFAULT_PROFILING_ITERATIONS = "0";
    private static final String DEFAULT_PROFILING_SAMPLING_US = "10";
    private static final long KILL_BACKGROUND_WAIT_MS = 3000;

    /** The requested iterations to run with method profiling. */
    static int sProfilingIterations;

    /** The interval of sample profiling in microseconds. */
    static int sSamplingIntervalUs;

    private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
    private long mWaitPreconditionDoneMs = 500;

    private final SettingsSession<Integer> mStayOnWhilePluggedInSetting = new SettingsSession<>(
            Settings.Global.getInt(mContext.getContentResolver(),
                    Settings.Global.STAY_ON_WHILE_PLUGGED_IN, 0),
            value -> executeShellCommand(String.format("settings put global %s %d",
                    Settings.Global.STAY_ON_WHILE_PLUGGED_IN, value)));

    private final SettingsSession<Integer> mNavigationModeSetting = new SettingsSession<>(
            mContext.getResources().getInteger(
                    com.android.internal.R.integer.config_navBarInteractionMode),
            value -> {
                final String navOverlay;
                switch (value) {
                    case WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON:
                        navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_2BUTTON_OVERLAY;
                        break;
                    case WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON:
                        navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_3BUTTON_OVERLAY;
                        break;
                    case WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL:
                    default:
                        navOverlay = WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL_OVERLAY;
                        break;
                }
                executeShellCommand("cmd overlay enable-exclusive " + navOverlay);
            });

    /** It only executes once before all tests. */
    @Override
    public void testRunStarted(Description description) {
        final Bundle arguments = InstrumentationRegistry.getArguments();
        // If true, it only logs the method names without running.
        final boolean skip = Boolean.parseBoolean(arguments.getString(ARGUMENT_LOG_ONLY, "false"));
        Log.i(mTag, "arguments=" + arguments);
        if (skip) {
            return;
        }
        sProfilingIterations = Integer.parseInt(
                arguments.getString(ARGUMENT_PROFILING_ITERATIONS, DEFAULT_PROFILING_ITERATIONS));
        sSamplingIntervalUs = Integer.parseInt(
                arguments.getString(ARGUMENT_PROFILING_SAMPLING, DEFAULT_PROFILING_SAMPLING_US));

        // Use same navigation mode (gesture navigation) across all devices and tests
        // for consistency.
        mNavigationModeSetting.set(WindowManagerPolicyConstants.NAV_BAR_MODE_GESTURAL);
        // Keep the device awake during testing.
        mStayOnWhilePluggedInSetting.set(BatteryManager.BATTERY_PLUGGED_ANY);

        runWithShellPermissionIdentity(() -> {
            final ActivityTaskManager atm = mContext.getSystemService(ActivityTaskManager.class);
            atm.removeAllVisibleRecentTasks();
            atm.removeRootTasksWithActivityTypes(new int[] { ACTIVITY_TYPE_STANDARD,
                    ACTIVITY_TYPE_ASSISTANT, ACTIVITY_TYPE_RECENTS, ACTIVITY_TYPE_UNDEFINED });
        });
        PhoneWindow.sendCloseSystemWindows(mContext, mTag);

        if (Boolean.parseBoolean(arguments.getString(ARGUMENT_KILL_BACKGROUND))) {
            runWithShellPermissionIdentity(this::killBackgroundProcesses);
            mWaitPreconditionDoneMs = KILL_BACKGROUND_WAIT_MS;
        }
        // Wait a while for the precondition setup to complete.
        SystemClock.sleep(mWaitPreconditionDoneMs);
    }

    private void killBackgroundProcesses() {
        Log.i(mTag, "Killing background processes...");
        final ActivityManager am = mContext.getSystemService(ActivityManager.class);
        final List<RunningAppProcessInfo> processes = am.getRunningAppProcesses();
        if (processes == null) {
            return;
        }
        for (RunningAppProcessInfo processInfo : processes) {
            if (processInfo.importanceReasonCode == RunningAppProcessInfo.REASON_UNKNOWN
                    && processInfo.importance > RunningAppProcessInfo.IMPORTANCE_SERVICE) {
                for (String pkg : processInfo.pkgList) {
                    am.forceStopPackage(pkg);
                }
            }
        }
    }

    /** It only executes once after all tests. */
    @Override
    public void testRunFinished(Result result) {
        mNavigationModeSetting.close();
        mStayOnWhilePluggedInSetting.close();
    }
}
+193 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading