diff --git a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java index 303c667351d16..2b7af2f0a11e6 100644 --- a/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java +++ b/apct-tests/perftests/inputmethod/src/android/inputmethod/ImePerfTest.java @@ -23,11 +23,14 @@ import static android.view.WindowInsetsAnimation.Callback.DISPATCH_MODE_STOP; import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation; +import static org.junit.Assert.assertTrue; + 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; import android.perftests.utils.ManualBenchmarkState.ManualBenchmarkTest; @@ -88,7 +91,7 @@ public class ImePerfTest extends ImePerfTestBase "ISC.onPostLayout" }; - /** IMF show methods to log in trace. */ + /** IMF show methods to log in trace. */ private String[] mShowMethods = { "IC.showRequestFromIme", "IC.showRequestFromApi", @@ -99,7 +102,23 @@ public class ImePerfTest extends ImePerfTestBase "IMMS.showSoftInput", "IMS.showSoftInput", "IMS.startInput", - "WMS.showImePostLayout" + "WMS.showImePostLayout", + "IMS.updateFullscreenMode", + "IMS.onComputeInsets", + "IMS.showWindow" + }; + + /** IMF show methods to log in trace. */ + private String[] mShowMethodsCold = { + "IMS.bindInput", + "IMS.initializeInternal", + "IMS.restartInput", + "IMS.onCreate", + "IMS.initSoftInputWindow", + "IMS.resetStateForNewConfiguration", + "IMMS.onServiceConnected", + "IMMS.sessionCreated", + "IMMS.startInputOrWindowGainedFocus" }; /** IMF hide lifecycle methods to log in trace. */ @@ -163,6 +182,7 @@ public class ImePerfTest extends ImePerfTestBase public static class BaselineIme extends InputMethodService { public static final int HEIGHT_DP = 100; + private static int sPid; @Override public View onCreateInputView() { @@ -173,9 +193,14 @@ public class ImePerfTest extends ImePerfTestBase view.setPadding(0, 0, 0, 0); view.addView(inner, new FrameLayout.LayoutParams(MATCH_PARENT, height)); inner.setBackgroundColor(0xff01fe10); // green + sPid = Process.myPid(); return view; } + static int getPid() { + return sPid; + } + static ComponentName getName(Context context) { return new ComponentName(context, BaselineIme.class); } @@ -188,8 +213,8 @@ public class ImePerfTest extends ImePerfTestBase flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX | StatsReport.FLAG_COEFFICIENT_VAR)) - public void testShowIme() throws Throwable { - testShowOrHideIme(true /* show */); + public void testShowImeWarm() throws Throwable { + testShowOrHideImeWarm(true /* show */); } @Test @@ -200,10 +225,65 @@ public class ImePerfTest extends ImePerfTestBase | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX | StatsReport.FLAG_COEFFICIENT_VAR)) public void testHideIme() throws Throwable { - testShowOrHideIme(false /* show */); + testShowOrHideImeWarm(false /* show */); } - private void testShowOrHideIme(final boolean show) throws Throwable { + @Test + @ManualBenchmarkTest( + targetTestDurationNs = 10 * TIME_1_S_IN_NS, + statsReport = @StatsReport( + flags = StatsReport.FLAG_ITERATION | StatsReport.FLAG_MEAN + | StatsReport.FLAG_MIN | StatsReport.FLAG_MAX + | StatsReport.FLAG_COEFFICIENT_VAR)) + public void testShowImeCold() throws Throwable { + mTraceMethods = new TraceMarkParser( + buildArray(mCommonMethods, mShowMethods, mShowMethodsCold)); + + final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState(); + state.setCustomizedIterations(getProfilingIterations(), this); + if (state.isWarmingUp()) { + // we don't need to warmup for cold start. + return; + } + + long measuredTimeNs = 0; + while (state.keepRunning(measuredTimeNs)) { + killBaselineIme(); + try (ImeSession imeSession = new ImeSession(BaselineIme.getName( + getInstrumentation().getContext()))) { + final AtomicReference latchStart = new AtomicReference<>(); + final Activity activity = getActivityWithFocus(); + + setImeListener(activity, latchStart, null /* latchEnd */); + latchStart.set(new CountDownLatch(1)); + + if (!mIsTraceStarted) { + startAsyncAtrace(); + } + + final WindowInsetsController controller = + activity.getWindow().getDecorView().getWindowInsetsController(); + AtomicLong startTime = new AtomicLong(); + activity.runOnUiThread(() -> { + startTime.set(SystemClock.elapsedRealtimeNanos()); + controller.show(WindowInsets.Type.ime()); + }); + + measuredTimeNs = waitForAnimationStart(latchStart, startTime); + mActivityRule.finishActivity(); + } + } + stopAsyncAtrace(); + addResultToState(state); + } + + private void killBaselineIme() { + assertTrue("PID of test and IME can't be same", + Process.myPid() != BaselineIme.getPid()); + Process.killProcess(BaselineIme.getPid()); + } + + private void testShowOrHideImeWarm(final boolean show) throws Throwable { mTraceMethods = new TraceMarkParser(buildArray( mCommonMethods, show ? mShowMethods : mHideMethods)); final ManualBenchmarkState state = mPerfStatusReporter.getBenchmarkState(); diff --git a/core/java/android/inputmethodservice/InputMethodService.java b/core/java/android/inputmethodservice/InputMethodService.java index 4a5d831cd7058..67e75d205f977 100644 --- a/core/java/android/inputmethodservice/InputMethodService.java +++ b/core/java/android/inputmethodservice/InputMethodService.java @@ -585,10 +585,12 @@ public class InputMethodService extends AbstractInputMethodService { Log.w(TAG, "The token has already registered, ignore this initialization."); return; } + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initializeInternal"); mPrivOps.set(privilegedOperations); InputMethodPrivilegedOperationsRegistry.put(token, mPrivOps); updateInputMethodDisplay(displayId); attachToken(token); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -642,6 +644,7 @@ public class InputMethodService extends AbstractInputMethodService { @MainThread @Override public void bindInput(InputBinding binding) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.bindInput"); mInputBinding = binding; mInputConnection = binding.getConnection(); if (DEBUG) Log.v(TAG, "bindInput(): binding=" + binding @@ -649,6 +652,7 @@ public class InputMethodService extends AbstractInputMethodService { reportFullscreenMode(); initialize(); onBindInput(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -686,7 +690,9 @@ public class InputMethodService extends AbstractInputMethodService { @Override public void restartInput(InputConnection ic, EditorInfo attribute) { if (DEBUG) Log.v(TAG, "restartInput(): editor=" + attribute); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.restartInput"); doStartInput(ic, attribute, true); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -1253,6 +1259,7 @@ public class InputMethodService extends AbstractInputMethodService { } @Override public void onCreate() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onCreate"); mTheme = Resources.selectSystemTheme(mTheme, getApplicationInfo().targetSdkVersion, android.R.style.Theme_InputMethod, @@ -1273,6 +1280,7 @@ public class InputMethodService extends AbstractInputMethodService { // in non-default display. mInflater = (LayoutInflater)getSystemService( Context.LAYOUT_INFLATER_SERVICE); + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initSoftInputWindow"); mWindow = new SoftInputWindow(this, "InputMethod", mTheme, null, null, mDispatcherState, WindowManager.LayoutParams.TYPE_INPUT_METHOD, Gravity.BOTTOM, false); mWindow.getWindow().getAttributes().setFitInsetsTypes(statusBars() | navigationBars()); @@ -1294,10 +1302,12 @@ public class InputMethodService extends AbstractInputMethodService { initViews(); mWindow.getWindow().setLayout(MATCH_PARENT, WRAP_CONTENT); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); mInlineSuggestionSessionController = new InlineSuggestionSessionController( this::onCreateInlineSuggestionsRequest, this::getHostInputToken, this::onInlineSuggestionsResponse); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -1318,6 +1328,7 @@ public class InputMethodService extends AbstractInputMethodService { } void initViews() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.initViews"); mInitialized = false; mViewsCreated = false; mShowInputRequested = false; @@ -1352,6 +1363,7 @@ public class InputMethodService extends AbstractInputMethodService { mCandidatesVisibility = getCandidatesHiddenVisibility(); mCandidatesFrame.setVisibility(mCandidatesVisibility); mInputFrame.setVisibility(View.GONE); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } @Override public void onDestroy() { @@ -1393,6 +1405,7 @@ public class InputMethodService extends AbstractInputMethodService { } private void resetStateForNewConfiguration() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.resetStateForNewConfiguration"); boolean visible = mDecorViewVisible; int showFlags = mShowInputFlags; boolean showingInput = mShowInputRequested; @@ -1428,6 +1441,7 @@ public class InputMethodService extends AbstractInputMethodService { boolean showing = onEvaluateInputViewShown(); setImeWindowStatus(IME_ACTIVE | (showing ? IME_VISIBLE : 0), mBackDisposition); } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -1589,6 +1603,7 @@ public class InputMethodService extends AbstractInputMethodService { * is currently running in fullscreen mode. */ public void updateFullscreenMode() { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.updateFullscreenMode"); boolean isFullscreen = mShowInputRequested && onEvaluateFullscreenMode(); boolean changed = mLastShowInputRequested != mShowInputRequested; if (mIsFullscreen != isFullscreen || !mFullscreenApplied) { @@ -1627,6 +1642,7 @@ public class InputMethodService extends AbstractInputMethodService { onConfigureWindow(mWindow.getWindow(), isFullscreen, !mShowInputRequested); mLastShowInputRequested = mShowInputRequested; } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -1755,6 +1771,7 @@ public class InputMethodService extends AbstractInputMethodService { * @param outInsets Fill in with the current UI insets. */ public void onComputeInsets(Insets outInsets) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.onComputeInsets"); int[] loc = mTmpLocation; if (mInputFrame.getVisibility() == View.VISIBLE) { mInputFrame.getLocationInWindow(loc); @@ -1775,6 +1792,7 @@ public class InputMethodService extends AbstractInputMethodService { outInsets.visibleTopInsets = loc[1]; outInsets.touchableInsets = Insets.TOUCHABLE_INSETS_VISIBLE; outInsets.touchableRegion.setEmpty(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } /** @@ -2165,7 +2183,7 @@ public class InputMethodService extends AbstractInputMethodService { } ImeTracing.getInstance().triggerServiceDump("InputMethodService#showWindow", this); - + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMS.showWindow"); mDecorViewWasVisible = mDecorViewVisible; mInShowWindow = true; final int previousImeWindowStatus = @@ -2189,6 +2207,7 @@ public class InputMethodService extends AbstractInputMethodService { } mDecorViewWasVisible = true; mInShowWindow = false; + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java index a257cde185f3c..6395094038c76 100644 --- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java +++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java @@ -1537,12 +1537,14 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void sessionCreated(IInputMethodSession session) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.sessionCreated"); final long ident = Binder.clearCallingIdentity(); try { mParentIMMS.onSessionCreated(mMethod, session, mChannel); } finally { Binder.restoreCallingIdentity(ident); } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } } @@ -2615,6 +2617,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub @Override public void onServiceConnected(ComponentName name, IBinder service) { + Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMMS.onServiceConnected"); synchronized (mMethodMap) { if (mCurIntent != null && name.equals(mCurIntent.getComponent())) { mCurMethod = IInputMethod.Stub.asInterface(service); @@ -2630,6 +2633,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub if (mCurToken == null) { Slog.w(TAG, "Service connected without a token!"); unbindCurrentMethodLocked(); + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); return; } if (DEBUG) Slog.v(TAG, "Initiating attach with token: " + mCurToken); @@ -2643,6 +2647,7 @@ public class InputMethodManagerService extends IInputMethodManager.Stub } } } + Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER); } void onSessionCreated(IInputMethod method, IInputMethodSession session,