diff --git a/apct-tests/perftests/contentcapture/AndroidManifest.xml b/apct-tests/perftests/contentcapture/AndroidManifest.xml index 80957c78abf3f..6b566afe284d2 100644 --- a/apct-tests/perftests/contentcapture/AndroidManifest.xml +++ b/apct-tests/perftests/contentcapture/AndroidManifest.xml @@ -18,6 +18,8 @@ + diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java index 9b853fed81d3a..0ea2dafbb047e 100644 --- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java +++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/AbstractContentCapturePerfTestCase.java @@ -22,18 +22,20 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat import static com.android.compatibility.common.util.ShellUtils.runShellCommand; +import android.app.Activity; import android.app.Application; +import android.app.Instrumentation; import android.content.ContentCaptureOptions; import android.content.Context; import android.content.Intent; import android.os.BatteryManager; import android.os.UserHandle; import android.perftests.utils.PerfStatusReporter; +import android.perftests.utils.PerfTestActivity; import android.provider.Settings; import android.util.Log; import androidx.annotation.NonNull; -import androidx.test.rule.ActivityTestRule; import com.android.compatibility.common.util.ActivitiesWatcher; import com.android.compatibility.common.util.ActivitiesWatcher.ActivityWatcher; @@ -53,18 +55,18 @@ import org.junit.runners.model.Statement; public abstract class AbstractContentCapturePerfTestCase { private static final String TAG = AbstractContentCapturePerfTestCase.class.getSimpleName(); - private static final long GENERIC_TIMEOUT_MS = 10_000; + protected static final long GENERIC_TIMEOUT_MS = 5_000; private static int sOriginalStayOnWhilePluggedIn; - private static Context sContext = getInstrumentation().getTargetContext(); + protected static final Instrumentation sInstrumentation = getInstrumentation(); + protected static final Context sContext = sInstrumentation.getTargetContext(); protected ActivitiesWatcher mActivitiesWatcher; - private MyContentCaptureService.ServiceWatcher mServiceWatcher; + /** A simple activity as the task root to reduce the noise of pause and animation time. */ + protected Activity mEntryActivity; - @Rule - public ActivityTestRule mActivityRule = - new ActivityTestRule<>(CustomTestActivity.class, false, false); + private MyContentCaptureService.ServiceWatcher mServiceWatcher; @Rule public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); @@ -220,6 +222,17 @@ public abstract class AbstractContentCapturePerfTestCase { } } + @Before + public void setUp() { + mEntryActivity = sInstrumentation.startActivitySync( + PerfTestActivity.createLaunchIntent(sInstrumentation.getContext())); + } + + @After + public void tearDown() { + mEntryActivity.finishAndRemoveTask(); + } + /** * Sets {@link MyContentCaptureService} as the service for the current user and waits until * its created, then add the perf test package into allow list. @@ -247,21 +260,25 @@ public abstract class AbstractContentCapturePerfTestCase { return launchActivity(R.layout.test_login_activity, 0); } + /** + * Returns the intent which will launch CustomTestActivity. + */ + protected Intent getLaunchIntent(int layoutId, int numViews) { + final Intent intent = new Intent(sContext, CustomTestActivity.class) + // Use NEW_TASK because the context is not activity. It is still in the same task + // of PerfTestActivity because of the same task affinity. Use NO_ANIMATION because + // this test focuses on launch time instead of animation duration. + .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION); + intent.putExtra(INTENT_EXTRA_LAYOUT_ID, layoutId); + intent.putExtra(INTENT_EXTRA_CUSTOM_VIEWS, numViews); + return intent; + } + /** * Launch test activity with give layout and parameter */ protected CustomTestActivity launchActivity(int layoutId, int numViews) { - final Intent intent = new Intent(sContext, CustomTestActivity.class); - intent.putExtra(INTENT_EXTRA_LAYOUT_ID, layoutId); - intent.putExtra(INTENT_EXTRA_CUSTOM_VIEWS, numViews); - return mActivityRule.launchActivity(intent); - } - - protected void finishActivity() { - try { - mActivityRule.finishActivity(); - } catch (IllegalStateException e) { - // no op - } + final Intent intent = getLaunchIntent(layoutId, numViews); + return (CustomTestActivity) sInstrumentation.startActivitySync(intent); } } diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java index e509837f441a0..c24e79f511bb0 100644 --- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java +++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/CustomTestActivity.java @@ -19,6 +19,10 @@ package android.view.contentcapture; import android.app.Activity; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.os.Looper; +import android.os.RemoteCallback; +import android.view.Choreographer; +import android.view.View; import android.widget.LinearLayout; import android.widget.TextView; @@ -31,6 +35,8 @@ import com.android.perftests.contentcapture.R; public class CustomTestActivity extends Activity { public static final String INTENT_EXTRA_LAYOUT_ID = "layout_id"; public static final String INTENT_EXTRA_CUSTOM_VIEWS = "custom_view_number"; + static final String INTENT_EXTRA_FINISH_ON_IDLE = "finish"; + static final String INTENT_EXTRA_DRAW_CALLBACK = "draw_callback"; public static final int MAX_VIEWS = 500; private static final int CUSTOM_CONTAINER_LAYOUT_ID = R.layout.test_container_activity; @@ -47,6 +53,34 @@ public class CustomTestActivity extends Activity { getIntent().getIntExtra(INTENT_EXTRA_CUSTOM_VIEWS, MAX_VIEWS)); } } + + final RemoteCallback drawCallback = getIntent().getParcelableExtra( + INTENT_EXTRA_DRAW_CALLBACK, RemoteCallback.class); + if (drawCallback != null) { + getWindow().getDecorView().addOnAttachStateChangeListener( + new View.OnAttachStateChangeListener() { + @Override + public void onViewAttachedToWindow(View v) { + Choreographer.getInstance().postCallback( + Choreographer.CALLBACK_COMMIT, + // Report that the first frame is drawn. + () -> drawCallback.sendResult(null), null /* token */); + } + + @Override + public void onViewDetachedFromWindow(View v) { + } + }); + } + + if (getIntent().getBooleanExtra(INTENT_EXTRA_FINISH_ON_IDLE, false)) { + Looper.myQueue().addIdleHandler(() -> { + // Finish without animation. + finish(); + overridePendingTransition(0 /* enterAnim */, 0 /* exitAnim */); + return false; + }); + } } private void createCustomViews(LinearLayout root, int number) { diff --git a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java index 725750976d980..aa95dfdfdf164 100644 --- a/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java +++ b/apct-tests/perftests/contentcapture/src/android/view/contentcapture/LoginTest.java @@ -15,9 +15,10 @@ */ package android.view.contentcapture; -import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.CREATED; import static com.android.compatibility.common.util.ActivitiesWatcher.ActivityLifecycle.DESTROYED; +import android.content.Intent; +import android.os.RemoteCallback; import android.perftests.utils.BenchmarkState; import android.view.View; @@ -80,17 +81,32 @@ public class LoginTest extends AbstractContentCapturePerfTestCase { } private void testActivityLaunchTime(int layoutId, int numViews) throws Throwable { + final Object drawNotifier = new Object(); + final Intent intent = getLaunchIntent(layoutId, numViews); + intent.putExtra(CustomTestActivity.INTENT_EXTRA_FINISH_ON_IDLE, true); + intent.putExtra(CustomTestActivity.INTENT_EXTRA_DRAW_CALLBACK, + new RemoteCallback(result -> { + synchronized (drawNotifier) { + drawNotifier.notifyAll(); + } + })); final ActivityWatcher watcher = startWatcher(); final BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); while (state.keepRunning()) { - launchActivity(layoutId, numViews); + mEntryActivity.startActivity(intent); + synchronized (drawNotifier) { + try { + drawNotifier.wait(GENERIC_TIMEOUT_MS); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } // Ignore the time to finish the activity state.pauseTiming(); - watcher.waitFor(CREATED); - finishActivity(); watcher.waitFor(DESTROYED); + sInstrumentation.waitForIdleSync(); state.resumeTiming(); } } @@ -142,12 +158,12 @@ public class LoginTest extends AbstractContentCapturePerfTestCase { while (state.keepRunning()) { // Only count the time of onVisibilityAggregated() state.pauseTiming(); - mActivityRule.runOnUiThread(() -> { + sInstrumentation.runOnMainSync(() -> { state.resumeTiming(); view.onVisibilityAggregated(false); state.pauseTiming(); }); - mActivityRule.runOnUiThread(() -> { + sInstrumentation.runOnMainSync(() -> { state.resumeTiming(); view.onVisibilityAggregated(true); state.pauseTiming();