Merge "Address API feedback for ApduServiceInfo" into main
diff --git a/core/java/android/app/ActivityThread.java b/core/java/android/app/ActivityThread.java
index 926e297..e6a2c07 100644
--- a/core/java/android/app/ActivityThread.java
+++ b/core/java/android/app/ActivityThread.java
@@ -237,7 +237,6 @@
 import com.android.internal.util.function.pooled.PooledLambda;
 import com.android.org.conscrypt.TrustedCertificateStore;
 import com.android.server.am.MemInfoDumpProto;
-import com.android.window.flags.Flags;
 
 import dalvik.annotation.optimization.NeverCompile;
 import dalvik.system.AppSpecializationHooks;
@@ -3762,11 +3761,7 @@
         final ClientTransaction clientTransaction = ClientTransaction.obtain(mAppThread);
         final ActivityResultItem activityResultItem = ActivityResultItem.obtain(
                 activityToken, list);
-        if (Flags.bundleClientTransactionFlag()) {
-            clientTransaction.addTransactionItem(activityResultItem);
-        } else {
-            clientTransaction.addCallback(activityResultItem);
-        }
+        clientTransaction.addTransactionItem(activityResultItem);
         try {
             mAppThread.scheduleTransaction(clientTransaction);
         } catch (RemoteException e) {
@@ -4553,11 +4548,7 @@
         final PauseActivityItem pauseActivityItem = PauseActivityItem.obtain(r.token,
                 r.activity.isFinishing(), /* userLeaving */ true, r.activity.mConfigChangeFlags,
                 /* dontReport */ false, /* autoEnteringPip */ false);
-        if (Flags.bundleClientTransactionFlag()) {
-            transaction.addTransactionItem(pauseActivityItem);
-        } else {
-            transaction.setLifecycleStateRequest(pauseActivityItem);
-        }
+        transaction.addTransactionItem(pauseActivityItem);
         executeTransaction(transaction);
     }
 
@@ -4565,11 +4556,7 @@
         final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
         final ResumeActivityItem resumeActivityItem = ResumeActivityItem.obtain(r.token,
                 /* isForward */ false, /* shouldSendCompatFakeFocus */ false);
-        if (Flags.bundleClientTransactionFlag()) {
-            transaction.addTransactionItem(resumeActivityItem);
-        } else {
-            transaction.setLifecycleStateRequest(resumeActivityItem);
-        }
+        transaction.addTransactionItem(resumeActivityItem);
         executeTransaction(transaction);
     }
 
@@ -6189,13 +6176,8 @@
                 TransactionExecutorHelper.getLifecycleRequestForCurrentState(r);
         // Schedule the transaction.
         final ClientTransaction transaction = ClientTransaction.obtain(mAppThread);
-        if (Flags.bundleClientTransactionFlag()) {
-            transaction.addTransactionItem(activityRelaunchItem);
-            transaction.addTransactionItem(lifecycleRequest);
-        } else {
-            transaction.addCallback(activityRelaunchItem);
-            transaction.setLifecycleStateRequest(lifecycleRequest);
-        }
+        transaction.addTransactionItem(activityRelaunchItem);
+        transaction.addTransactionItem(lifecycleRequest);
         executeTransaction(transaction);
     }
 
diff --git a/core/java/android/app/servertransaction/ClientTransaction.java b/core/java/android/app/servertransaction/ClientTransaction.java
index 612d433..79696e0 100644
--- a/core/java/android/app/servertransaction/ClientTransaction.java
+++ b/core/java/android/app/servertransaction/ClientTransaction.java
@@ -29,6 +29,7 @@
 import android.os.RemoteException;
 
 import com.android.internal.annotations.VisibleForTesting;
+import com.android.window.flags.Flags;
 
 import java.io.PrintWriter;
 import java.util.ArrayList;
@@ -85,12 +86,15 @@
      * @param item A single message that can contain a client activity/window request/callback.
      */
     public void addTransactionItem(@NonNull ClientTransactionItem item) {
-        if (mTransactionItems == null) {
-            mTransactionItems = new ArrayList<>();
+        if (Flags.bundleClientTransactionFlag()) {
+            if (mTransactionItems == null) {
+                mTransactionItems = new ArrayList<>();
+            }
+            mTransactionItems.add(item);
         }
-        mTransactionItems.add(item);
 
         // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
+        // Populate even if mTransactionItems is set to support the UnsupportedAppUsage.
         if (item.isActivityLifecycleItem()) {
             setLifecycleStateRequest((ActivityLifecycleItem) item);
         } else {
@@ -114,7 +118,7 @@
      */
     // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
     @Deprecated
-    public void addCallback(@NonNull ClientTransactionItem activityCallback) {
+    private void addCallback(@NonNull ClientTransactionItem activityCallback) {
         if (mActivityCallbacks == null) {
             mActivityCallbacks = new ArrayList<>();
         }
@@ -169,7 +173,7 @@
      */
     // TODO(b/324203798): cleanup after remove UnsupportedAppUsage
     @Deprecated
-    public void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
+    private void setLifecycleStateRequest(@NonNull ActivityLifecycleItem stateRequest) {
         if (mLifecycleStateRequest != null) {
             return;
         }
diff --git a/core/java/android/app/servertransaction/ClientTransactionListenerController.java b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
index 9f97f6f..1a8136e 100644
--- a/core/java/android/app/servertransaction/ClientTransactionListenerController.java
+++ b/core/java/android/app/servertransaction/ClientTransactionListenerController.java
@@ -23,7 +23,6 @@
 import android.annotation.NonNull;
 import android.app.ActivityThread;
 import android.hardware.display.DisplayManagerGlobal;
-import android.os.Process;
 
 import com.android.internal.annotations.VisibleForTesting;
 
@@ -67,7 +66,7 @@
      * window configuration.
      */
     public void onDisplayChanged(int displayId) {
-        if (!isBundleClientTransactionFlagEnabled()) {
+        if (!bundleClientTransactionFlag()) {
             return;
         }
         if (ActivityThread.isSystem()) {
@@ -76,10 +75,4 @@
         }
         mDisplayManager.handleDisplayChangeFromWindowManager(displayId);
     }
-
-    /** Whether {@link #bundleClientTransactionFlag} feature flag is enabled. */
-    public boolean isBundleClientTransactionFlagEnabled() {
-        // Can't read flag from isolated process.
-        return !Process.isIsolated() && bundleClientTransactionFlag();
-    }
 }
diff --git a/core/java/android/app/servertransaction/TransactionExecutor.java b/core/java/android/app/servertransaction/TransactionExecutor.java
index 406e00a..fa73c99 100644
--- a/core/java/android/app/servertransaction/TransactionExecutor.java
+++ b/core/java/android/app/servertransaction/TransactionExecutor.java
@@ -40,7 +40,6 @@
 import android.content.Context;
 import android.content.res.Configuration;
 import android.os.IBinder;
-import android.os.Process;
 import android.os.Trace;
 import android.util.ArrayMap;
 import android.util.ArraySet;
@@ -218,8 +217,6 @@
         final boolean shouldTrackConfigUpdatedContext =
                 // No configuration change for local transaction.
                 !mTransactionHandler.isExecutingLocalTransaction()
-                        // Can't read flag from isolated process.
-                        && !Process.isIsolated()
                         && bundleClientTransactionFlag();
         final Context configUpdatedContext = shouldTrackConfigUpdatedContext
                 ? item.getContextToUpdate(mTransactionHandler)
diff --git a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
index 1cc910c..e47a48d 100644
--- a/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
+++ b/core/java/android/hardware/input/KeyboardLayoutPreviewDrawable.java
@@ -48,14 +48,13 @@
     private static final int GRAVITY_RIGHT = 0x2;
     private static final int GRAVITY_TOP = 0x4;
     private static final int GRAVITY_BOTTOM = 0x8;
-    private static final int GRAVITY_CENTER =
-            GRAVITY_LEFT | GRAVITY_RIGHT | GRAVITY_TOP | GRAVITY_BOTTOM;
-    private static final int GRAVITY_CENTER_HORIZONTAL = GRAVITY_LEFT | GRAVITY_RIGHT;
+    private static final int TEXT_PADDING_IN_DP = 1;
     private static final int KEY_PADDING_IN_DP = 3;
     private static final int KEYBOARD_PADDING_IN_DP = 10;
     private static final int KEY_RADIUS_IN_DP = 5;
     private static final int KEYBOARD_RADIUS_IN_DP = 10;
-    private static final int GLYPH_TEXT_SIZE_IN_SP = 10;
+    private static final int MIN_GLYPH_TEXT_SIZE_IN_SP = 10;
+    private static final int MAX_GLYPH_TEXT_SIZE_IN_SP = 20;
 
     private final List<KeyDrawable> mKeyDrawables = new ArrayList<>();
 
@@ -107,6 +106,8 @@
         }
         int rowCount = keys.length;
         float keyHeight = (float) (height - rowCount * 2 * keyPadding) / rowCount;
+        // Based on key height calculate the max text size that can fit for typing keys
+        mResourceProvider.calculateBestTextSizeForKey(keyHeight);
         float isoEnterKeyLeft = 0;
         float isoEnterKeyTop = 0;
         float isoEnterWidthUnit = 0;
@@ -136,16 +137,19 @@
                 }
                 if (PhysicalKeyLayout.isSpecialKey(row[j])) {
                     mKeyDrawables.add(new TypingKey(null, keyRect, keyRadius,
+                            mResourceProvider.getTextPadding(),
                             mResourceProvider.getSpecialKeyPaint(),
                             mResourceProvider.getSpecialKeyPaint(),
                             mResourceProvider.getSpecialKeyPaint()));
                 } else if (PhysicalKeyLayout.isKeyPositionUnsure(row[j])) {
                     mKeyDrawables.add(new UnsureTypingKey(row[j].glyph(), keyRect,
-                            keyRadius, mResourceProvider.getTypingKeyPaint(),
+                            keyRadius, mResourceProvider.getTextPadding(),
+                            mResourceProvider.getTypingKeyPaint(),
                             mResourceProvider.getPrimaryGlyphPaint(),
                             mResourceProvider.getSecondaryGlyphPaint()));
                 } else {
                     mKeyDrawables.add(new TypingKey(row[j].glyph(), keyRect, keyRadius,
+                            mResourceProvider.getTextPadding(),
                             mResourceProvider.getTypingKeyPaint(),
                             mResourceProvider.getPrimaryGlyphPaint(),
                             mResourceProvider.getSecondaryGlyphPaint()));
@@ -192,15 +196,18 @@
 
         private final RectF mKeyRect;
         private final float mKeyRadius;
+        private final float mTextPadding;
         private final Paint mKeyPaint;
         private final Paint mBaseTextPaint;
         private final Paint mModifierTextPaint;
         private final List<GlyphDrawable> mGlyphDrawables = new ArrayList<>();
 
         private TypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData, RectF keyRect,
-                float keyRadius, Paint keyPaint, Paint baseTextPaint, Paint modifierTextPaint) {
+                float keyRadius, float textPadding, Paint keyPaint, Paint baseTextPaint,
+                Paint modifierTextPaint) {
             mKeyRect = keyRect;
             mKeyRadius = keyRadius;
+            mTextPadding = textPadding;
             mKeyPaint = keyPaint;
             mBaseTextPaint = baseTextPaint;
             mModifierTextPaint = modifierTextPaint;
@@ -219,20 +226,17 @@
             if (!glyphData.hasBaseText()) {
                 return;
             }
-            boolean isCenter = !glyphData.hasValidAltGrText() && !glyphData.hasValidAltShiftText();
             mGlyphDrawables.add(new GlyphDrawable(glyphData.getBaseText(), new RectF(),
-                    GRAVITY_BOTTOM | (isCenter ? GRAVITY_CENTER_HORIZONTAL : GRAVITY_LEFT),
-                    mBaseTextPaint));
+                    GRAVITY_BOTTOM | GRAVITY_LEFT, mBaseTextPaint));
             if (glyphData.hasValidShiftText()) {
                 mGlyphDrawables.add(new GlyphDrawable(glyphData.getShiftText(), new RectF(),
-                        GRAVITY_TOP | (isCenter ? GRAVITY_CENTER_HORIZONTAL : GRAVITY_LEFT),
-                        mModifierTextPaint));
+                        GRAVITY_TOP | GRAVITY_LEFT, mModifierTextPaint));
             }
             if (glyphData.hasValidAltGrText()) {
                 mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrText(), new RectF(),
                         GRAVITY_BOTTOM | GRAVITY_RIGHT, mModifierTextPaint));
             }
-            if (glyphData.hasValidAltShiftText()) {
+            if (glyphData.hasValidAltGrShiftText()) {
                 mGlyphDrawables.add(new GlyphDrawable(glyphData.getAltGrShiftText(), new RectF(),
                         GRAVITY_TOP | GRAVITY_RIGHT, mModifierTextPaint));
             }
@@ -246,15 +250,19 @@
                 float centerY = keyHeight / 2;
                 if ((glyph.gravity & GRAVITY_LEFT) != 0) {
                     centerX -= keyWidth / 4;
+                    centerX += mTextPadding / 2;
                 }
                 if ((glyph.gravity & GRAVITY_RIGHT) != 0) {
                     centerX += keyWidth / 4;
+                    centerX -= mTextPadding / 2;
                 }
                 if ((glyph.gravity & GRAVITY_TOP) != 0) {
                     centerY -= keyHeight / 4;
+                    centerY += mTextPadding / 2;
                 }
                 if ((glyph.gravity & GRAVITY_BOTTOM) != 0) {
                     centerY += keyHeight / 4;
+                    centerY -= mTextPadding / 2;
                 }
                 Rect textBounds = new Rect();
                 glyph.paint.getTextBounds(glyph.text, 0, glyph.text.length(), textBounds);
@@ -285,9 +293,9 @@
     private static class UnsureTypingKey extends TypingKey {
 
         private UnsureTypingKey(@Nullable PhysicalKeyLayout.KeyGlyph glyphData,
-                RectF keyRect, float keyRadius, Paint keyPaint, Paint baseTextPaint,
-                Paint modifierTextPaint) {
-            super(glyphData, keyRect, keyRadius, createGreyedOutPaint(keyPaint),
+                RectF keyRect, float keyRadius, float textPadding, Paint keyPaint,
+                Paint baseTextPaint, Paint modifierTextPaint) {
+            super(glyphData, keyRect, keyRadius, textPadding, createGreyedOutPaint(keyPaint),
                     createGreyedOutPaint(baseTextPaint), createGreyedOutPaint(modifierTextPaint));
         }
     }
@@ -402,8 +410,11 @@
         private final Paint mSecondaryGlyphPaint;
         private final int mKeyPadding;
         private final int mKeyboardPadding;
+        private final float mTextPadding;
         private final float mKeyRadius;
         private final float mBackgroundRadius;
+        private final float mSpToPxMultiplier;
+        private final Paint.FontMetrics mFontMetrics;
 
         private ResourceProvider(Context context) {
             mKeyPadding = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
@@ -414,8 +425,10 @@
                     KEY_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
             mBackgroundRadius = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                     KEYBOARD_RADIUS_IN_DP, context.getResources().getDisplayMetrics());
-            int textSize = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
-                    GLYPH_TEXT_SIZE_IN_SP, context.getResources().getDisplayMetrics());
+            mSpToPxMultiplier = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 1,
+                    context.getResources().getDisplayMetrics());
+            mTextPadding = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+                    TEXT_PADDING_IN_DP, context.getResources().getDisplayMetrics());
             boolean isDark = (context.getResources().getConfiguration().uiMode
                     & Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES;
             int typingKeyColor = context.getColor(
@@ -430,15 +443,37 @@
             int backgroundColor = context.getColor(
                     isDark ? android.R.color.system_surface_container_dark
                             : android.R.color.system_surface_container_light);
-            mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor, textSize,
+            mPrimaryGlyphPaint = createTextPaint(primaryGlyphColor,
+                    MIN_GLYPH_TEXT_SIZE_IN_SP * mSpToPxMultiplier,
                     Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD));
-            mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor, textSize,
+            mSecondaryGlyphPaint = createTextPaint(secondaryGlyphColor,
+                    MIN_GLYPH_TEXT_SIZE_IN_SP * mSpToPxMultiplier,
                     Typeface.create(Typeface.SANS_SERIF, Typeface.NORMAL));
+            mFontMetrics = mPrimaryGlyphPaint.getFontMetrics();
             mTypingKeyPaint = createFillPaint(typingKeyColor);
             mSpecialKeyPaint = createFillPaint(specialKeyColor);
             mBackgroundPaint = createFillPaint(backgroundColor);
         }
 
+        private void calculateBestTextSizeForKey(float keyHeight) {
+            int textSize = (int) (mSpToPxMultiplier * MIN_GLYPH_TEXT_SIZE_IN_SP) + 1;
+            while (textSize < mSpToPxMultiplier * MAX_GLYPH_TEXT_SIZE_IN_SP) {
+                updateTextSize(textSize);
+                if (mFontMetrics.bottom - mFontMetrics.top + 3 * mTextPadding > keyHeight / 2) {
+                    textSize--;
+                    break;
+                }
+                textSize++;
+            }
+            updateTextSize(textSize);
+        }
+
+        private void updateTextSize(float textSize) {
+            mPrimaryGlyphPaint.setTextSize(textSize);
+            mSecondaryGlyphPaint.setTextSize(textSize);
+            mPrimaryGlyphPaint.getFontMetrics(mFontMetrics);
+        }
+
         private Paint getBackgroundPaint() {
             return mBackgroundPaint;
         }
@@ -467,6 +502,10 @@
             return mKeyboardPadding;
         }
 
+        private float getTextPadding() {
+            return mTextPadding;
+        }
+
         private float getKeyRadius() {
             return mKeyRadius;
         }
@@ -476,7 +515,8 @@
         }
     }
 
-    private static Paint createTextPaint(@ColorInt int textColor, int textSize, Typeface typeface) {
+    private static Paint createTextPaint(@ColorInt int textColor, float textSize,
+            Typeface typeface) {
         Paint paint = new Paint();
         paint.setColor(textColor);
         paint.setStyle(Paint.Style.FILL);
diff --git a/core/java/android/hardware/input/PhysicalKeyLayout.java b/core/java/android/hardware/input/PhysicalKeyLayout.java
index 844e02f..cff444f 100644
--- a/core/java/android/hardware/input/PhysicalKeyLayout.java
+++ b/core/java/android/hardware/input/PhysicalKeyLayout.java
@@ -336,11 +336,13 @@
             return "";
         }
         int utf8Char = (kcm.get(keyCode, modifierState) & KeyCharacterMap.COMBINING_ACCENT_MASK);
+        if (utf8Char == 0) {
+            return "";
+        }
         if (Character.isValidCodePoint(utf8Char)) {
             return String.valueOf(Character.toChars(utf8Char));
-        } else {
-            return String.valueOf(kcm.getDisplayLabel(keyCode));
         }
+        return "□";
     }
 
     private static LayoutKey getKey(int keyCode, float keyWeight) {
@@ -434,10 +436,11 @@
         }
 
         public boolean hasValidAltGrText() {
-            return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText);
+            return !TextUtils.isEmpty(mAltGrText) && !TextUtils.equals(mBaseText, mAltGrText)
+                    && !TextUtils.equals(mShiftText, mAltGrText);
         }
 
-        public boolean hasValidAltShiftText() {
+        public boolean hasValidAltGrShiftText() {
             return !TextUtils.isEmpty(mAltGrShiftText)
                     && !TextUtils.equals(mBaseText, mAltGrShiftText)
                     && !TextUtils.equals(mAltGrText, mAltGrShiftText)
diff --git a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
index dc5e0e5..88ca2a4 100644
--- a/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
+++ b/core/java/android/view/inputmethod/IInputMethodManagerGlobalInvoker.java
@@ -66,6 +66,7 @@
 
     @Nullable
     private static volatile IImeTracker sTrackerServiceCache = null;
+    private static int sCurStartInputSeq = 0;
 
     /**
      * @return {@code true} if {@link IInputMethodManager} is available.
@@ -327,6 +328,7 @@
         }
     }
 
+    // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
     @AnyThread
     @NonNull
     @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
@@ -353,6 +355,41 @@
         }
     }
 
+    /**
+     * Returns a sequence number for startInput.
+     */
+    @AnyThread
+    @NonNull
+    @RequiresPermission(value = Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)
+    static int startInputOrWindowGainedFocusAsync(@StartInputReason int startInputReason,
+            @NonNull IInputMethodClient client, @Nullable IBinder windowToken,
+            @StartInputFlags int startInputFlags,
+            @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+            @WindowManager.LayoutParams.Flags int windowFlags, @Nullable EditorInfo editorInfo,
+            @Nullable IRemoteInputConnection remoteInputConnection,
+            @Nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher) {
+        final IInputMethodManager service = getService();
+        if (service == null) {
+            return -1;
+        }
+        try {
+            service.startInputOrWindowGainedFocusAsync(startInputReason, client, windowToken,
+                    startInputFlags, softInputMode, windowFlags, editorInfo, remoteInputConnection,
+                    remoteAccessibilityInputConnection, unverifiedTargetSdkVersion, userId,
+                    imeDispatcher, advanceAngGetStartInputSequenceNumber());
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+        return sCurStartInputSeq;
+    }
+
+    private static int advanceAngGetStartInputSequenceNumber() {
+        return ++sCurStartInputSeq;
+    }
+
+
     @AnyThread
     static void showInputMethodPickerFromClient(@NonNull IInputMethodClient client,
             int auxiliarySubtypeMode) {
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
index f4b09df..72125ba 100644
--- a/core/java/android/view/inputmethod/InputMethodManager.java
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -321,6 +321,22 @@
     };
 
     /**
+     * A runnable that reports {@link InputConnection} opened event for calls to
+     * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync}.
+     */
+    private abstract static class ReportInputConnectionOpenedRunner implements Runnable {
+        /**
+         * Sequence number to track startInput requests to
+         * {@link IInputMethodManagerGlobalInvoker#startInputOrWindowGainedFocusAsync}
+         */
+        int mSequenceNum;
+        ReportInputConnectionOpenedRunner(int sequenceNum) {
+            this.mSequenceNum = sequenceNum;
+        }
+    }
+    private ReportInputConnectionOpenedRunner mReportInputConnectionOpenedRunner;
+
+    /**
      * Ensures that {@link #sInstance} becomes non-{@code null} for application that have directly
      * or indirectly relied on {@link #sInstance} via reflection or something like that.
      *
@@ -691,6 +707,7 @@
     private static final int MSG_UNBIND_ACCESSIBILITY_SERVICE = 12;
     private static final int MSG_SET_INTERACTIVE = 13;
     private static final int MSG_ON_SHOW_REQUESTED = 31;
+    private static final int MSG_START_INPUT_RESULT = 40;
 
     /**
      * Calling this will invalidate Local stylus handwriting availability Cache which
@@ -1045,7 +1062,7 @@
                     return;
                 }
                 case MSG_BIND: {
-                    final InputBindResult res = (InputBindResult)msg.obj;
+                    final InputBindResult res = (InputBindResult) msg.obj;
                     if (DEBUG) {
                         Log.i(TAG, "handleMessage: MSG_BIND " + res.sequence + "," + res.id);
                     }
@@ -1071,6 +1088,60 @@
                     startInputInner(StartInputReason.BOUND_TO_IMMS, null, 0, 0, 0);
                     return;
                 }
+
+                case MSG_START_INPUT_RESULT: {
+                    final InputBindResult res = (InputBindResult) msg.obj;
+                    final int startInputSeq = msg.arg1;
+                    if (res == null) {
+                        // IMMS logs .wtf already.
+                        return;
+                    }
+                    if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
+                    synchronized (mH) {
+                        if (res.id != null) {
+                            updateInputChannelLocked(res.channel);
+                            mCurMethod = res.method; // for @UnsupportedAppUsage
+                            mCurBindState = new BindState(res);
+                            mAccessibilityInputMethodSession.clear();
+                            if (res.accessibilitySessions != null) {
+                                for (int i = 0; i < res.accessibilitySessions.size(); i++) {
+                                    IAccessibilityInputMethodSessionInvoker wrapper =
+                                            IAccessibilityInputMethodSessionInvoker.createOrNull(
+                                                    res.accessibilitySessions.valueAt(i));
+                                    if (wrapper != null) {
+                                        mAccessibilityInputMethodSession.append(
+                                                res.accessibilitySessions.keyAt(i), wrapper);
+                                    }
+                                }
+                            }
+                            mCurId = res.id; // for @UnsupportedAppUsage
+                        } else if (res.channel != null && res.channel != mCurChannel) {
+                            res.channel.dispose();
+                        }
+                        switch (res.result) {
+                            case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
+                                mRestartOnNextWindowFocus = true;
+                                mServedView = null;
+                                break;
+                        }
+                        if (mCompletions != null) {
+                            if (isImeSessionAvailableLocked()) {
+                                mCurBindState.mImeSession.displayCompletions(mCompletions);
+                            }
+                        }
+
+                        if (res != null
+                                && res.method != null
+                                && mServedView != null
+                                && mReportInputConnectionOpenedRunner != null
+                                && mReportInputConnectionOpenedRunner.mSequenceNum
+                                        == startInputSeq) {
+                            mReportInputConnectionOpenedRunner.run();
+                        }
+                        mReportInputConnectionOpenedRunner = null;
+                    }
+                    return;
+                }
                 case MSG_UNBIND: {
                     final int sequence = msg.arg1;
                     @UnbindReason
@@ -1322,6 +1393,12 @@
         }
 
         @Override
+        public void onStartInputResult(InputBindResult res, int startInputSeq) {
+            mH.obtainMessage(MSG_START_INPUT_RESULT, startInputSeq, -1 /* unused */, res)
+                    .sendToTarget();
+        }
+
+        @Override
         public void onBindAccessibilityService(InputBindResult res, int id) {
             mH.obtainMessage(MSG_BIND_ACCESSIBILITY_SERVICE, id, 0, res).sendToTarget();
         }
@@ -2010,6 +2087,7 @@
             mServedConnecting = false;
             clearConnectionLocked();
         }
+        mReportInputConnectionOpenedRunner = null;
         // Clear the back callbacks held by the ime dispatcher to avoid memory leaks.
         mImeDispatcher.clear();
     }
@@ -3080,14 +3158,52 @@
             final int targetUserId = editorInfo.targetInputMethodUser != null
                     ? editorInfo.targetInputMethodUser.getIdentifier() : UserHandle.myUserId();
             Trace.traceBegin(TRACE_TAG_WINDOW_MANAGER, "IMM.startInputOrWindowGainedFocus");
-            res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
-                    startInputReason, mClient, windowGainingFocus, startInputFlags,
-                    softInputMode, windowFlags, editorInfo, servedInputConnection,
-                    servedInputConnection == null ? null
-                            : servedInputConnection.asIRemoteAccessibilityInputConnection(),
-                    view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
-                    mImeDispatcher);
+
+            int startInputSeq = -1;
+            if (Flags.useZeroJankProxy()) {
+                // async result delivered via MSG_START_INPUT_RESULT.
+                startInputSeq = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocusAsync(
+                        startInputReason, mClient, windowGainingFocus, startInputFlags,
+                        softInputMode, windowFlags, editorInfo, servedInputConnection,
+                        servedInputConnection == null ? null
+                                : servedInputConnection.asIRemoteAccessibilityInputConnection(),
+                        view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
+                        mImeDispatcher);
+            } else {
+                res = IInputMethodManagerGlobalInvoker.startInputOrWindowGainedFocus(
+                        startInputReason, mClient, windowGainingFocus, startInputFlags,
+                        softInputMode, windowFlags, editorInfo, servedInputConnection,
+                        servedInputConnection == null ? null
+                                : servedInputConnection.asIRemoteAccessibilityInputConnection(),
+                        view.getContext().getApplicationInfo().targetSdkVersion, targetUserId,
+                        mImeDispatcher);
+            }
             Trace.traceEnd(TRACE_TAG_WINDOW_MANAGER);
+            if (Flags.useZeroJankProxy()) {
+                // Create a runnable for delayed notification to the app that the InputConnection is
+                // initialized and ready for use.
+                if (ic != null) {
+                    final int seqId = startInputSeq;
+                    mReportInputConnectionOpenedRunner =
+                            new ReportInputConnectionOpenedRunner(startInputSeq) {
+                                @Override
+                                public void run() {
+                                    if (DEBUG) {
+                                        Log.v(TAG, "Calling View.onInputConnectionOpened: view= "
+                                                + view
+                                                + ", ic=" + ic + ", editorInfo=" + editorInfo
+                                                + ", handler="
+                                                + icHandler + ", startInputSeq=" + seqId);
+                                    }
+                                    reportInputConnectionOpened(ic, editorInfo, icHandler, view);
+                                }
+                            };
+                } else {
+                    mReportInputConnectionOpenedRunner = null;
+                }
+                return true;
+            }
+
             if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
             if (res == null) {
                 Log.wtf(TAG, "startInputOrWindowGainedFocus must not return"
@@ -3118,6 +3234,7 @@
             } else if (res.channel != null && res.channel != mCurChannel) {
                 res.channel.dispose();
             }
+
             switch (res.result) {
                 case InputBindResult.ResultCode.ERROR_NOT_IME_TARGET_WINDOW:
                     mRestartOnNextWindowFocus = true;
diff --git a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
index 9251d2d..babd9a0 100644
--- a/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
+++ b/core/java/com/android/internal/inputmethod/IInputMethodClient.aidl
@@ -24,6 +24,7 @@
  */
 oneway interface IInputMethodClient {
     void onBindMethod(in InputBindResult res);
+    void onStartInputResult(in InputBindResult res, int startInputSeq);
     void onBindAccessibilityService(in InputBindResult res, int id);
     void onUnbindMethod(int sequence, int unbindReason);
     void onUnbindAccessibilityService(int sequence, int id);
diff --git a/core/java/com/android/internal/inputmethod/InputBindResult.java b/core/java/com/android/internal/inputmethod/InputBindResult.java
index b6eca07..243b103 100644
--- a/core/java/com/android/internal/inputmethod/InputBindResult.java
+++ b/core/java/com/android/internal/inputmethod/InputBindResult.java
@@ -271,6 +271,7 @@
     public String toString() {
         return "InputBindResult{result=" + getResultString() + " method=" + method + " id=" + id
                 + " sequence=" + sequence
+                + " result=" + result
                 + " isInputMethodSuppressingSpellChecker=" + isInputMethodSuppressingSpellChecker
                 + "}";
     }
diff --git a/core/java/com/android/internal/jank/Cuj.java b/core/java/com/android/internal/jank/Cuj.java
index 48c455a..3662d69 100644
--- a/core/java/com/android/internal/jank/Cuj.java
+++ b/core/java/com/android/internal/jank/Cuj.java
@@ -120,7 +120,7 @@
     public static final int CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY = 84;
     public static final int CUJ_PREDICTIVE_BACK_CROSS_TASK = 85;
     public static final int CUJ_PREDICTIVE_BACK_HOME = 86;
-    public static final int CUJ_LAUNCHER_SEARCH_QSB_OPEN = 87;
+    // 87 is reserved - previously assigned to deprecated CUJ_LAUNCHER_SEARCH_QSB_OPEN.
     public static final int CUJ_BACK_PANEL_ARROW = 88;
     public static final int CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK = 89;
     public static final int CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH = 90;
@@ -209,7 +209,6 @@
             CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY,
             CUJ_PREDICTIVE_BACK_CROSS_TASK,
             CUJ_PREDICTIVE_BACK_HOME,
-            CUJ_LAUNCHER_SEARCH_QSB_OPEN,
             CUJ_BACK_PANEL_ARROW,
             CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK,
             CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH,
@@ -304,7 +303,6 @@
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_ACTIVITY] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_ACTIVITY;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_CROSS_TASK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_CROSS_TASK;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_PREDICTIVE_BACK_HOME] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__PREDICTIVE_BACK_HOME;
-        CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_OPEN] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_OPEN;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_BACK_PANEL_ARROW] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__BACK_PANEL_ARROW;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_CLOSE_ALL_APPS_BACK;
         CUJ_TO_STATSD_INTERACTION_TYPE[CUJ_LAUNCHER_SEARCH_QSB_WEB_SEARCH] = FrameworkStatsLog.UIINTERACTION_FRAME_INFO_REPORTED__INTERACTION_TYPE__LAUNCHER_SEARCH_QSB_WEB_SEARCH;
@@ -480,8 +478,6 @@
                 return "PREDICTIVE_BACK_CROSS_TASK";
             case CUJ_PREDICTIVE_BACK_HOME:
                 return "PREDICTIVE_BACK_HOME";
-            case CUJ_LAUNCHER_SEARCH_QSB_OPEN:
-                return "LAUNCHER_SEARCH_QSB_OPEN";
             case CUJ_BACK_PANEL_ARROW:
                 return "BACK_PANEL_ARROW";
             case CUJ_LAUNCHER_CLOSE_ALL_APPS_BACK:
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index e95127b..b90f8bf 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -69,6 +69,8 @@
     boolean hideSoftInput(in IInputMethodClient client, @nullable IBinder windowToken,
             in @nullable ImeTracker.Token statsToken, int flags,
             in @nullable ResultReceiver resultReceiver, int reason);
+
+    // TODO(b/293640003): Remove method once Flags.useZeroJankProxy() is enabled.
     // If windowToken is null, this just does startInput().  Otherwise this reports that a window
     // has gained focus, and if 'editorInfo' is non-null then also does startInput.
     // @NonNull
@@ -85,6 +87,21 @@
             int unverifiedTargetSdkVersion, int userId,
             in ImeOnBackInvokedDispatcher imeDispatcher);
 
+    // If windowToken is null, this just does startInput().  Otherwise this reports that a window
+    // has gained focus, and if 'editorInfo' is non-null then also does startInput.
+    @JavaPassthrough(annotation="@android.annotation.RequiresPermission(value = "
+            + "android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, conditional = true)")
+    void startInputOrWindowGainedFocusAsync(
+            /* @StartInputReason */ int startInputReason,
+            in IInputMethodClient client, in @nullable IBinder windowToken,
+            /* @StartInputFlags */ int startInputFlags,
+            /* @android.view.WindowManager.LayoutParams.SoftInputModeFlags */ int softInputMode,
+            /* @android.view.WindowManager.LayoutParams.Flags */ int windowFlags,
+            in @nullable EditorInfo editorInfo, in @nullable IRemoteInputConnection inputConnection,
+            in @nullable IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+            int unverifiedTargetSdkVersion, int userId,
+            in ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq);
+
     void showInputMethodPickerFromClient(in IInputMethodClient client,
             int auxiliarySubtypeMode);
 
@@ -156,6 +173,7 @@
                 in String delegatePackageName,
                 in String delegatorPackageName);
 
+    // TODO(b/293640003): introduce a new API method to provide async way to return boolean.
     /** Accepts and starts a stylus handwriting session for the delegate view **/
     boolean acceptStylusHandwritingDelegation(in IInputMethodClient client, in int userId,
             in String delegatePackageName, in String delegatorPackageName, int flags);
diff --git a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
index 48ef7e6..ebf4cca 100644
--- a/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
+++ b/core/tests/coretests/src/android/app/activity/ActivityThreadTest.java
@@ -43,7 +43,6 @@
 import android.app.PictureInPictureUiState;
 import android.app.ResourcesManager;
 import android.app.servertransaction.ActivityConfigurationChangeItem;
-import android.app.servertransaction.ActivityLifecycleItem;
 import android.app.servertransaction.ActivityRelaunchItem;
 import android.app.servertransaction.ClientTransaction;
 import android.app.servertransaction.ClientTransactionItem;
@@ -75,7 +74,6 @@
 import androidx.test.runner.AndroidJUnit4;
 
 import com.android.internal.content.ReferrerIntent;
-import com.android.window.flags.Flags;
 
 import org.junit.After;
 import org.junit.Before;
@@ -230,7 +228,7 @@
         try {
             // Send process level config change.
             ClientTransaction transaction = newTransaction(activityThread);
-            addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+            transaction.addTransactionItem(ConfigurationChangeItem.obtain(
                     newConfig, DEVICE_ID_INVALID));
             appThread.scheduleTransaction(transaction);
             InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -247,7 +245,7 @@
             newConfig.seq++;
             newConfig.smallestScreenWidthDp++;
             transaction = newTransaction(activityThread);
-            addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+            transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
                     activity.getActivityToken(), newConfig));
             appThread.scheduleTransaction(transaction);
             InstrumentationRegistry.getInstrumentation().waitForIdleSync();
@@ -448,16 +446,16 @@
         activity.mTestLatch = new CountDownLatch(1);
 
         ClientTransaction transaction = newTransaction(activityThread);
-        addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+        transaction.addTransactionItem(ConfigurationChangeItem.obtain(
                 processConfigLandscape, DEVICE_ID_INVALID));
         appThread.scheduleTransaction(transaction);
 
         transaction = newTransaction(activityThread);
-        addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+        transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
                 activity.getActivityToken(), activityConfigLandscape));
-        addClientTransactionItem(transaction, ConfigurationChangeItem.obtain(
+        transaction.addTransactionItem(ConfigurationChangeItem.obtain(
                 processConfigPortrait, DEVICE_ID_INVALID));
-        addClientTransactionItem(transaction, ActivityConfigurationChangeItem.obtain(
+        transaction.addTransactionItem(ActivityConfigurationChangeItem.obtain(
                 activity.getActivityToken(), activityConfigPortrait));
         appThread.scheduleTransaction(transaction);
 
@@ -847,8 +845,8 @@
                         false /* shouldSendCompatFakeFocus*/);
 
         final ClientTransaction transaction = newTransaction(activity);
-        addClientTransactionItem(transaction, callbackItem);
-        addClientTransactionItem(transaction, resumeStateRequest);
+        transaction.addTransactionItem(callbackItem);
+        transaction.addTransactionItem(resumeStateRequest);
 
         return transaction;
     }
@@ -860,7 +858,7 @@
                         false /* shouldSendCompatFakeFocus */);
 
         final ClientTransaction transaction = newTransaction(activity);
-        addClientTransactionItem(transaction, resumeStateRequest);
+        transaction.addTransactionItem(resumeStateRequest);
 
         return transaction;
     }
@@ -871,7 +869,7 @@
                 activity.getActivityToken(), 0 /* configChanges */);
 
         final ClientTransaction transaction = newTransaction(activity);
-        addClientTransactionItem(transaction, stopStateRequest);
+        transaction.addTransactionItem(stopStateRequest);
 
         return transaction;
     }
@@ -883,7 +881,7 @@
                 activity.getActivityToken(), config);
 
         final ClientTransaction transaction = newTransaction(activity);
-        addClientTransactionItem(transaction, item);
+        transaction.addTransactionItem(item);
 
         return transaction;
     }
@@ -895,7 +893,7 @@
                 resume);
 
         final ClientTransaction transaction = newTransaction(activity);
-        addClientTransactionItem(transaction, item);
+        transaction.addTransactionItem(item);
 
         return transaction;
     }
@@ -910,17 +908,6 @@
         return ClientTransaction.obtain(activityThread.getApplicationThread());
     }
 
-    private static void addClientTransactionItem(@NonNull ClientTransaction transaction,
-            @NonNull ClientTransactionItem item) {
-        if (Flags.bundleClientTransactionFlag()) {
-            transaction.addTransactionItem(item);
-        } else if (item.isActivityLifecycleItem()) {
-            transaction.setLifecycleStateRequest((ActivityLifecycleItem) item);
-        } else {
-            transaction.addCallback(item);
-        }
-    }
-
     // Test activity
     public static class TestActivity extends Activity {
         static final String PIP_REQUESTED_OVERRIDE_ENTER = "pip_requested_override_enter";
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
index 95d5049..213fd7b 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionListenerControllerTest.java
@@ -16,10 +16,13 @@
 
 package android.app.servertransaction;
 
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
 
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
+
 import static org.mockito.Mockito.doReturn;
-import static org.mockito.Mockito.spy;
 import static org.mockito.Mockito.verify;
 
 import android.hardware.display.DisplayManager;
@@ -28,12 +31,14 @@
 import android.os.Handler;
 import android.os.RemoteException;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.view.DisplayInfo;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.Mock;
@@ -49,6 +54,10 @@
 @SmallTest
 @Presubmit
 public class ClientTransactionListenerControllerTest {
+
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
     @Mock
     private IDisplayManager mIDisplayManager;
     @Mock
@@ -60,12 +69,12 @@
 
     @Before
     public void setup() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
         MockitoAnnotations.initMocks(this);
         mDisplayManager = new DisplayManagerGlobal(mIDisplayManager);
         mHandler = getInstrumentation().getContext().getMainThreadHandler();
-        mController = spy(ClientTransactionListenerController.createInstanceForTesting(
-                mDisplayManager));
-        doReturn(true).when(mController).isBundleClientTransactionFlagEnabled();
+        mController = ClientTransactionListenerController.createInstanceForTesting(mDisplayManager);
     }
 
     @Test
diff --git a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
index d10cf16..5272416 100644
--- a/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/ClientTransactionTests.java
@@ -16,16 +16,22 @@
 
 package android.app.servertransaction;
 
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
+
+import static org.mockito.Mockito.doReturn;
 import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.times;
 import static org.mockito.Mockito.verify;
 
 import android.app.ClientTransactionHandler;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -43,31 +49,28 @@
 @Presubmit
 public class ClientTransactionTests {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
     @Test
     public void testPreExecute() {
-        final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
-        final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
-        final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
-        final ClientTransactionHandler clientTransactionHandler =
-                mock(ClientTransactionHandler.class);
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addCallback(callback1);
-        transaction.addCallback(callback2);
-        transaction.setLifecycleStateRequest(stateRequest);
-
-        transaction.preExecute(clientTransactionHandler);
-
-        verify(callback1, times(1)).preExecute(clientTransactionHandler);
-        verify(callback2, times(1)).preExecute(clientTransactionHandler);
-        verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
+        testPreExecuteInner();
     }
 
     @Test
-    public void testPreExecuteTransactionItems() {
+    public void testPreExecute_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testPreExecuteInner();
+    }
+
+    private void testPreExecuteInner() {
         final ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
         final ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
         final ActivityLifecycleItem stateRequest = mock(ActivityLifecycleItem.class);
+        doReturn(true).when(stateRequest).isActivityLifecycleItem();
         final ClientTransactionHandler clientTransactionHandler =
                 mock(ClientTransactionHandler.class);
 
@@ -78,8 +81,8 @@
 
         transaction.preExecute(clientTransactionHandler);
 
-        verify(callback1, times(1)).preExecute(clientTransactionHandler);
-        verify(callback2, times(1)).preExecute(clientTransactionHandler);
-        verify(stateRequest, times(1)).preExecute(clientTransactionHandler);
+        verify(callback1).preExecute(clientTransactionHandler);
+        verify(callback2).preExecute(clientTransactionHandler);
+        verify(stateRequest).preExecute(clientTransactionHandler);
     }
 }
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
index 2315a58..adb6f2a 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionExecutorTests.java
@@ -25,6 +25,9 @@
 import static android.app.servertransaction.ActivityLifecycleItem.ON_STOP;
 import static android.app.servertransaction.ActivityLifecycleItem.PRE_ON_CREATE;
 import static android.app.servertransaction.ActivityLifecycleItem.UNDEFINED;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
 
 import static org.junit.Assert.assertArrayEquals;
 import static org.junit.Assert.assertEquals;
@@ -51,12 +54,14 @@
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 import android.util.ArrayMap;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.mockito.InOrder;
@@ -83,6 +88,9 @@
 @Presubmit
 public class TransactionExecutorTests {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
     @Mock
     private ClientTransactionHandler mTransactionHandler;
     @Mock
@@ -240,29 +248,19 @@
 
     @Test
     public void testTransactionResolution() {
-        ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
-        when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
-        ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
-        when(callback2.getPostExecutionState()).thenReturn(UNDEFINED);
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addCallback(callback1);
-        transaction.addCallback(callback2);
-        transaction.setLifecycleStateRequest(mActivityLifecycleItem);
-
-        transaction.preExecute(mTransactionHandler);
-        mExecutor.execute(transaction);
-
-        InOrder inOrder = inOrder(mTransactionHandler, callback1, callback2,
-                mActivityLifecycleItem);
-        inOrder.verify(callback1).execute(eq(mTransactionHandler), any());
-        inOrder.verify(callback2).execute(eq(mTransactionHandler), any());
-        inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
-                any());
+        testTransactionResolutionInner();
     }
 
     @Test
-    public void testExecuteTransactionItems_transactionResolution() {
+    public void testTransactionResolution_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testTransactionResolutionInner();
+    }
+
+    private void testTransactionResolutionInner() {
         ClientTransactionItem callback1 = mock(ClientTransactionItem.class);
         when(callback1.getPostExecutionState()).thenReturn(UNDEFINED);
         ClientTransactionItem callback2 = mock(ClientTransactionItem.class);
@@ -286,38 +284,19 @@
 
     @Test
     public void testDoNotLaunchDestroyedActivity() {
-        final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
-        when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
-        // Assume launch transaction is still in queue, so there is no client record.
-        when(mTransactionHandler.getActivityClient(any())).thenReturn(null);
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        // An incoming destroy transaction enters binder thread (preExecute).
-        final IBinder token = mock(IBinder.class);
-        final ClientTransaction destroyTransaction = ClientTransaction.obtain(null /* client */);
-        destroyTransaction.setLifecycleStateRequest(
-                DestroyActivityItem.obtain(token, false /* finished */, 0 /* configChanges */));
-        destroyTransaction.preExecute(mTransactionHandler);
-        // The activity should be added to to-be-destroyed container.
-        assertEquals(1, activitiesToBeDestroyed.size());
-
-        // A previous queued launch transaction runs on main thread (execute).
-        final ClientTransaction launchTransaction = ClientTransaction.obtain(null /* client */);
-        final LaunchActivityItem launchItem =
-                spy(new LaunchActivityItemBuilder(token, new Intent(), new ActivityInfo()).build());
-        launchTransaction.addCallback(launchItem);
-        mExecutor.execute(launchTransaction);
-
-        // The launch transaction should not be executed because its token is in the
-        // to-be-destroyed container.
-        verify(launchItem, never()).execute(any(), any());
-
-        // After the destroy transaction has been executed, the token should be removed.
-        mExecutor.execute(destroyTransaction);
-        assertTrue(activitiesToBeDestroyed.isEmpty());
+        testDoNotLaunchDestroyedActivityInner();
     }
 
     @Test
-    public void testExecuteTransactionItems_doNotLaunchDestroyedActivity() {
+    public void testDoNotLaunchDestroyedActivity_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testDoNotLaunchDestroyedActivityInner();
+    }
+
+    private void testDoNotLaunchDestroyedActivityInner() {
         final Map<IBinder, DestroyActivityItem> activitiesToBeDestroyed = new ArrayMap<>();
         when(mTransactionHandler.getActivitiesToBeDestroyed()).thenReturn(activitiesToBeDestroyed);
         // Assume launch transaction is still in queue, so there is no client record.
@@ -350,26 +329,19 @@
 
     @Test
     public void testActivityResultRequiredStateResolution() {
-        when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        PostExecItem postExecItem = new PostExecItem(ON_RESUME);
-
-        ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addCallback(postExecItem);
-
-        // Verify resolution that should get to onPause
-        mClientRecord.setState(ON_RESUME);
-        mExecutor.executeCallbacks(transaction);
-        verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
-
-        // Verify resolution that should get to onStart
-        mClientRecord.setState(ON_STOP);
-        mExecutor.executeCallbacks(transaction);
-        verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
+        testActivityResultRequiredStateResolutionInner();
     }
 
     @Test
-    public void testExecuteTransactionItems_activityResultRequiredStateResolution() {
+    public void testActivityResultRequiredStateResolution_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testActivityResultRequiredStateResolutionInner();
+    }
+
+    private void testActivityResultRequiredStateResolutionInner() {
         when(mTransactionHandler.getActivity(any())).thenReturn(mock(Activity.class));
 
         PostExecItem postExecItem = new PostExecItem(ON_RESUME);
@@ -379,12 +351,12 @@
 
         // Verify resolution that should get to onPause
         mClientRecord.setState(ON_RESUME);
-        mExecutor.executeTransactionItems(transaction);
+        mExecutor.execute(transaction);
         verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_PAUSE), eq(transaction));
 
         // Verify resolution that should get to onStart
         mClientRecord.setState(ON_STOP);
-        mExecutor.executeTransactionItems(transaction);
+        mExecutor.execute(transaction);
         verify(mExecutor).cycleToPath(eq(mClientRecord), eq(ON_START), eq(transaction));
     }
 
@@ -523,18 +495,19 @@
 
     @Test(expected = IllegalArgumentException.class)
     public void testActivityItemNullRecordThrowsException() {
-        final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
-        when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
-        final IBinder token = mock(IBinder.class);
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addCallback(activityItem);
-        when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        mExecutor.executeCallbacks(transaction);
+        testActivityItemNullRecordThrowsExceptionInner();
     }
 
     @Test(expected = IllegalArgumentException.class)
-    public void testExecuteTransactionItems_activityItemNullRecordThrowsException() {
+    public void testActivityItemNullRecordThrowsException_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testActivityItemNullRecordThrowsExceptionInner();
+    }
+
+    private void testActivityItemNullRecordThrowsExceptionInner() {
         final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
         when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
         final IBinder token = mock(IBinder.class);
@@ -542,28 +515,24 @@
         transaction.addTransactionItem(activityItem);
         when(mTransactionHandler.getActivityClient(token)).thenReturn(null);
 
-        mExecutor.executeTransactionItems(transaction);
+        mExecutor.execute(transaction);
     }
 
     @Test
     public void testActivityItemExecute() {
-        final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
-        when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
-        when(activityItem.getActivityToken()).thenReturn(mActivityToken);
-        transaction.addCallback(activityItem);
-        transaction.setLifecycleStateRequest(mActivityLifecycleItem);
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
 
-        mExecutor.execute(transaction);
-
-        final InOrder inOrder = inOrder(activityItem, mActivityLifecycleItem);
-        inOrder.verify(activityItem).execute(eq(mTransactionHandler), eq(mClientRecord), any());
-        inOrder.verify(mActivityLifecycleItem).execute(eq(mTransactionHandler), eq(mClientRecord),
-                any());
+        testActivityItemExecuteInner();
     }
 
     @Test
-    public void testExecuteTransactionItems_activityItemExecute() {
+    public void testActivityItemExecute_bundleClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
+        testActivityItemExecuteInner();
+    }
+
+    private void testActivityItemExecuteInner() {
         final ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
         final ActivityTransactionItem activityItem = mock(ActivityTransactionItem.class);
         when(activityItem.getPostExecutionState()).thenReturn(UNDEFINED);
diff --git a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
index c30d216..aa80013 100644
--- a/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
+++ b/core/tests/coretests/src/android/app/servertransaction/TransactionParcelTests.java
@@ -20,6 +20,9 @@
 import static android.app.servertransaction.TestUtils.mergedConfig;
 import static android.app.servertransaction.TestUtils.referrerIntentList;
 import static android.app.servertransaction.TestUtils.resultInfoList;
+import static android.platform.test.flag.junit.SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT;
+
+import static com.android.window.flags.Flags.FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG;
 
 import static org.junit.Assert.assertEquals;
 
@@ -36,11 +39,13 @@
 import android.os.Parcelable;
 import android.os.PersistableBundle;
 import android.platform.test.annotations.Presubmit;
+import android.platform.test.flag.junit.SetFlagsRule;
 
 import androidx.test.filters.SmallTest;
 import androidx.test.runner.AndroidJUnit4;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 
@@ -60,6 +65,9 @@
 @Presubmit
 public class TransactionParcelTests {
 
+    @Rule
+    public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(DEVICE_DEFAULT);
+
     private Parcel mParcel;
     private IBinder mActivityToken;
 
@@ -275,6 +283,8 @@
 
     @Test
     public void testClientTransaction() {
+        mSetFlagsRule.enableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
         // Write to parcel
         NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
         ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
@@ -300,14 +310,16 @@
 
     @Test
     public void testClientTransactionCallbacksOnly() {
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
         // Write to parcel
         NewIntentItem callback1 = NewIntentItem.obtain(mActivityToken, new ArrayList<>(), true);
         ActivityConfigurationChangeItem callback2 = ActivityConfigurationChangeItem.obtain(
                 mActivityToken, config());
 
         ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.addCallback(callback1);
-        transaction.addCallback(callback2);
+        transaction.addTransactionItem(callback1);
+        transaction.addTransactionItem(callback2);
 
         writeAndPrepareForReading(transaction);
 
@@ -321,12 +333,14 @@
 
     @Test
     public void testClientTransactionLifecycleOnly() {
+        mSetFlagsRule.disableFlags(FLAG_BUNDLE_CLIENT_TRANSACTION_FLAG);
+
         // Write to parcel
         StopActivityItem lifecycleRequest = StopActivityItem.obtain(mActivityToken,
                 78 /* configChanges */);
 
         ClientTransaction transaction = ClientTransaction.obtain(null /* client */);
-        transaction.setLifecycleStateRequest(lifecycleRequest);
+        transaction.addTransactionItem(lifecycleRequest);
 
         writeAndPrepareForReading(transaction);
 
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
index a0149da..1a04bb8 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/ListPreference.kt
@@ -37,11 +37,13 @@
 import androidx.compose.ui.semantics.Role
 import com.android.settingslib.spa.framework.theme.SettingsDimension
 import com.android.settingslib.spa.widget.dialog.SettingsDialog
+import com.android.settingslib.spa.widget.ui.SettingsBody
 import com.android.settingslib.spa.widget.ui.SettingsDialogItem
 
 data class ListPreferenceOption(
     val id: Int,
     val text: String,
+    val summary: String = String()
 )
 
 /**
@@ -129,6 +131,14 @@
     ) {
         RadioButton(selected = selected, onClick = null, enabled = enabled)
         Spacer(modifier = Modifier.width(SettingsDimension.itemPaddingEnd))
-        SettingsDialogItem(text = option.text, enabled = enabled)
+        Column {
+            SettingsDialogItem(text = option.text, enabled = enabled)
+            if (option.summary != String()) {
+                SettingsBody(
+                    body = option.summary,
+                    maxLines = 1
+                )
+            }
+        }
     }
 }
diff --git a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
index e36572f..3216e37 100644
--- a/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
+++ b/packages/SettingsLib/Spa/spa/src/com/android/settingslib/spa/widget/preference/TwoTargetPreference.kt
@@ -24,7 +24,6 @@
 import androidx.compose.foundation.layout.size
 import androidx.compose.material3.MaterialTheme
 import androidx.compose.runtime.Composable
-import androidx.compose.runtime.remember
 import androidx.compose.ui.Alignment
 import androidx.compose.ui.Modifier
 import androidx.compose.ui.unit.dp
@@ -46,14 +45,14 @@
         verticalAlignment = Alignment.CenterVertically,
     ) {
         Box(modifier = Modifier.weight(1f)) {
-            Preference(remember {
+            Preference(
                 object : PreferenceModel {
                     override val title = title
                     override val summary = summary
                     override val icon = icon
                     override val onClick = onClick
                 }
-            })
+            )
         }
         PreferenceDivider()
         widget()
diff --git a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
index 796ac48..417ce6e 100644
--- a/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
+++ b/packages/SettingsLib/Spa/tests/src/com/android/settingslib/spa/widget/preference/ListPreferenceTest.kt
@@ -123,6 +123,26 @@
     }
 
     @Test
+    fun click_optionsNotEmptyAndItemHasSummary_itemShowSummary() {
+        composeTestRule.setContent {
+            ListPreference(remember {
+                object : ListPreferenceModel {
+                    override val title = TITLE
+                    override val options =
+                        listOf(ListPreferenceOption(id = 1, text = "A", summary = "A_Summary"))
+                    override val selectedId = mutableIntStateOf(1)
+                    override val onIdSelected: (Int) -> Unit = {}
+                }
+            })
+        }
+
+        composeTestRule.onNodeWithText(TITLE).performClick()
+
+        composeTestRule.onDialogText(TITLE).assertIsDisplayed()
+        composeTestRule.onNodeWithText("A_Summary").assertIsDisplayed()
+    }
+
+    @Test
     fun select() {
         val selectedId = mutableIntStateOf(1)
         composeTestRule.setContent {
diff --git a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
index 249fa7f..e489bc5 100644
--- a/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
+++ b/packages/SettingsLib/src/com/android/settingslib/applications/ApplicationsState.java
@@ -1702,7 +1702,8 @@
         }
 
         public boolean isPrivateProfile() {
-            return UserManager.USER_TYPE_PROFILE_PRIVATE.equals(mProfileType);
+            return android.os.Flags.allowPrivateProfile()
+                    && UserManager.USER_TYPE_PROFILE_PRIVATE.equals(mProfileType);
         }
 
         /**
diff --git a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
index 213a66e..1ad7d49 100644
--- a/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
+++ b/packages/SettingsLib/tests/integ/src/com/android/settingslib/applications/ApplicationsStateTest.java
@@ -22,21 +22,30 @@
 import static org.mockito.Mockito.when;
 
 import android.content.pm.ApplicationInfo;
+import android.os.Flags;
 import android.os.UserManager;
+import android.platform.test.flag.junit.SetFlagsRule;
+
+import androidx.test.core.app.ApplicationProvider;
 
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
 import org.junit.runner.RunWith;
 import org.junit.runners.JUnit4;
 
 @RunWith(JUnit4.class)
 public class ApplicationsStateTest {
+    private static final int APP_ENTRY_ID = 1;
     private ApplicationsState.AppEntry mEntry;
+    @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
 
     @Before
     public void setUp() {
-        mEntry = mock(ApplicationsState.AppEntry.class);
-        mEntry.info = mock(ApplicationInfo.class);
+        mEntry = new ApplicationsState.AppEntry(
+                ApplicationProvider.getApplicationContext(),
+                mock(ApplicationInfo.class),
+                APP_ENTRY_ID);
     }
 
     @Test
@@ -310,6 +319,8 @@
 
     @Test
     public void testPrivateProfileFilterDisplaysCorrectApps() {
+        mSetFlagsRule.enableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
         mEntry.showInPersonalTab = true;
         mEntry.mProfileType = UserManager.USER_TYPE_FULL_SYSTEM;
         assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isTrue();
@@ -320,4 +331,14 @@
         assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
         assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isTrue();
     }
+
+    @Test
+    public void testPrivateProfileFilterDisplaysCorrectAppsWhenFlagDisabled() {
+        mSetFlagsRule.disableFlags(Flags.FLAG_ALLOW_PRIVATE_PROFILE);
+
+        mEntry.showInPersonalTab = false;
+        mEntry.mProfileType = UserManager.USER_TYPE_PROFILE_PRIVATE;
+        assertThat(ApplicationsState.FILTER_PERSONAL.filterApp(mEntry)).isFalse();
+        assertThat(ApplicationsState.FILTER_PRIVATE_PROFILE.filterApp(mEntry)).isFalse();
+    }
 }
diff --git a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
index 977dbff..84a59b4 100644
--- a/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
+++ b/services/core/java/com/android/server/inputmethod/IInputMethodClientInvoker.java
@@ -117,6 +117,30 @@
     }
 
     @AnyThread
+    void onStartInputResult(@NonNull InputBindResult res, int startInputSeq) {
+        if (mIsProxy) {
+            onStartInputResultInternal(res, startInputSeq);
+        } else {
+            mHandler.post(() -> onStartInputResultInternal(res, startInputSeq));
+        }
+    }
+
+    @AnyThread
+    private void onStartInputResultInternal(@NonNull InputBindResult res, int startInputSeq) {
+        try {
+            mTarget.onStartInputResult(res, startInputSeq);
+        } catch (RemoteException e) {
+            logRemoteException(e);
+        } finally {
+            // Dispose the channel if the input method is not local to this process
+            // because the remote proxy will get its own copy when unparceled.
+            if (res.channel != null && mIsProxy) {
+                res.channel.dispose();
+            }
+        }
+    }
+
+    @AnyThread
     void onBindAccessibilityService(@NonNull InputBindResult res, int id) {
         if (mIsProxy) {
             onBindAccessibilityServiceInternal(res, id);
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
index bc169ca..5574d18 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodManagerService.java
@@ -1539,7 +1539,13 @@
         @Override
         public void onStart() {
             mService.publishLocalService();
-            publishBinderService(Context.INPUT_METHOD_SERVICE, mService, false /*allowIsolated*/,
+            IInputMethodManager.Stub service;
+            if (Flags.useZeroJankProxy()) {
+                service = new ZeroJankProxy(mService.mHandler::post, mService);
+            } else {
+                service = mService;
+            }
+            publishBinderService(Context.INPUT_METHOD_SERVICE, service, false /*allowIsolated*/,
                     DUMP_FLAG_PRIORITY_CRITICAL | DUMP_FLAG_PRIORITY_NORMAL | DUMP_FLAG_PROTO);
         }
 
@@ -2216,6 +2222,14 @@
         }
     }
 
+    @Nullable
+    ClientState getClientState(IInputMethodClient client) {
+        synchronized (ImfLock.class) {
+            return mClientController.getClient(client.asBinder());
+        }
+    }
+
+    // TODO(b/314150112): Move this to ClientController.
     @GuardedBy("ImfLock.class")
     void unbindCurrentClientLocked(@UnbindReason int unbindClientReason) {
         if (mCurClient != null) {
@@ -3741,6 +3755,20 @@
         return imeClientFocus == WindowManagerInternal.ImeClientFocusResult.HAS_IME_FOCUS;
     }
 
+    //TODO(b/293640003): merge with startInputOrWindowGainedFocus once Flags.useZeroJankProxy()
+    // is enabled.
+    @Override
+    public void startInputOrWindowGainedFocusAsync(
+            @StartInputReason int startInputReason, IInputMethodClient client, IBinder windowToken,
+            @StartInputFlags int startInputFlags, @SoftInputModeFlags int softInputMode,
+            int windowFlags, @Nullable EditorInfo editorInfo,
+            IRemoteInputConnection inputConnection,
+            IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq) {
+        // implemented by ZeroJankProxy
+    }
+
     @NonNull
     @Override
     public InputBindResult startInputOrWindowGainedFocus(
diff --git a/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
new file mode 100644
index 0000000..692fd7dc
--- /dev/null
+++ b/services/core/java/com/android/server/inputmethod/ZeroJankProxy.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2024 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.
+ */
+
+/*
+ * Copyright (C) 2024 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 com.android.server.inputmethod;
+
+import static com.android.server.inputmethod.InputMethodManagerService.TAG;
+
+import android.Manifest;
+import android.annotation.BinderThread;
+import android.annotation.EnforcePermission;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.UserIdInt;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ResultReceiver;
+import android.os.ShellCallback;
+import android.util.ExceptionUtils;
+import android.util.Slog;
+import android.view.WindowManager;
+import android.view.inputmethod.CursorAnchorInfo;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.ImeTracker;
+import android.view.inputmethod.InputMethodInfo;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.InputMethodSubtype;
+import android.window.ImeOnBackInvokedDispatcher;
+
+import com.android.internal.inputmethod.DirectBootAwareness;
+import com.android.internal.inputmethod.IConnectionlessHandwritingCallback;
+import com.android.internal.inputmethod.IImeTracker;
+import com.android.internal.inputmethod.IInputMethodClient;
+import com.android.internal.inputmethod.IRemoteAccessibilityInputConnection;
+import com.android.internal.inputmethod.IRemoteInputConnection;
+import com.android.internal.inputmethod.InputBindResult;
+import com.android.internal.inputmethod.SoftInputShowHideReason;
+import com.android.internal.inputmethod.StartInputFlags;
+import com.android.internal.inputmethod.StartInputReason;
+import com.android.internal.util.FunctionalUtils.ThrowingRunnable;
+import com.android.internal.view.IInputMethodManager;
+
+import java.io.FileDescriptor;
+import java.util.List;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+
+/**
+ * A proxy that processes all {@link IInputMethodManager} calls asynchronously.
+ * @hide
+ */
+public class ZeroJankProxy extends IInputMethodManager.Stub {
+
+    private final IInputMethodManager mInner;
+    private final Executor mExecutor;
+
+    ZeroJankProxy(Executor executor, IInputMethodManager inner) {
+        mInner = inner;
+        mExecutor = executor;
+    }
+
+    private void offload(ThrowingRunnable r) {
+        offloadInner(r);
+    }
+
+    private void offload(Runnable r) {
+        offloadInner(r);
+    }
+
+    private void offloadInner(Runnable r) {
+        boolean useThrowingRunnable = r instanceof ThrowingRunnable;
+        final long identity = Binder.clearCallingIdentity();
+        try {
+            mExecutor.execute(() -> {
+                final long inner = Binder.clearCallingIdentity();
+                // Restoring calling identity, so we can still do permission checks on caller.
+                Binder.restoreCallingIdentity(identity);
+                try {
+                    try {
+                        if (useThrowingRunnable) {
+                            ((ThrowingRunnable) r).runOrThrow();
+                        } else {
+                            r.run();
+                        }
+                    } catch (Exception e) {
+                        Slog.e(TAG, "Error in async call", e);
+                        throw ExceptionUtils.propagate(e);
+                    }
+                } finally {
+                    Binder.restoreCallingIdentity(inner);
+                }
+            });
+        } finally {
+            Binder.restoreCallingIdentity(identity);
+        }
+    }
+
+    @Override
+    public void addClient(IInputMethodClient client, IRemoteInputConnection inputConnection,
+            int selfReportedDisplayId) throws RemoteException {
+        offload(() -> mInner.addClient(client, inputConnection, selfReportedDisplayId));
+    }
+
+    @Override
+    public InputMethodInfo getCurrentInputMethodInfoAsUser(int userId) throws RemoteException {
+        return mInner.getCurrentInputMethodInfoAsUser(userId);
+    }
+
+    @Override
+    public List<InputMethodInfo> getInputMethodList(
+            int userId, @DirectBootAwareness int directBootAwareness) throws RemoteException {
+        return mInner.getInputMethodList(userId, directBootAwareness);
+    }
+
+    @Override
+    public List<InputMethodInfo> getEnabledInputMethodList(int userId) throws RemoteException {
+        return mInner.getEnabledInputMethodList(userId);
+    }
+
+    @Override
+    public List<InputMethodSubtype> getEnabledInputMethodSubtypeList(String imiId,
+            boolean allowsImplicitlyEnabledSubtypes, int userId)
+            throws RemoteException {
+        return mInner.getEnabledInputMethodSubtypeList(imiId, allowsImplicitlyEnabledSubtypes,
+                userId);
+    }
+
+    @Override
+    public InputMethodSubtype getLastInputMethodSubtype(int userId) throws RemoteException {
+        return mInner.getLastInputMethodSubtype(userId);
+    }
+
+    @Override
+    public boolean showSoftInput(IInputMethodClient client, IBinder windowToken,
+            @Nullable ImeTracker.Token statsToken, @InputMethodManager.ShowFlags int flags,
+            int lastClickTooType, ResultReceiver resultReceiver,
+            @SoftInputShowHideReason int reason)
+            throws RemoteException {
+        offload(() -> mInner.showSoftInput(client, windowToken, statsToken, flags, lastClickTooType,
+                resultReceiver, reason));
+        return true;
+    }
+
+    @Override
+    public boolean hideSoftInput(IInputMethodClient client, IBinder windowToken,
+            @Nullable ImeTracker.Token statsToken, @InputMethodManager.HideFlags int flags,
+            ResultReceiver resultReceiver, @SoftInputShowHideReason int reason)
+            throws RemoteException {
+        offload(() -> mInner.hideSoftInput(client, windowToken, statsToken, flags, resultReceiver,
+                reason));
+        return true;
+    }
+
+    @Override
+    public void startInputOrWindowGainedFocusAsync(
+            @StartInputReason int startInputReason,
+            IInputMethodClient client, IBinder windowToken,
+            @StartInputFlags int startInputFlags,
+            @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+            int windowFlags, @Nullable EditorInfo editorInfo,
+            IRemoteInputConnection inputConnection,
+            IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher, int startInputSeq)
+            throws RemoteException {
+        offload(() -> {
+            InputBindResult result = mInner.startInputOrWindowGainedFocus(startInputReason, client,
+                    windowToken, startInputFlags, softInputMode, windowFlags,
+                    editorInfo,
+                    inputConnection, remoteAccessibilityInputConnection,
+                    unverifiedTargetSdkVersion,
+                    userId, imeDispatcher);
+            sendOnStartInputResult(client, result, startInputSeq);
+        });
+    }
+
+    @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
+    @Override
+    public InputBindResult startInputOrWindowGainedFocus(
+            @StartInputReason int startInputReason,
+            IInputMethodClient client, IBinder windowToken,
+            @StartInputFlags int startInputFlags,
+            @WindowManager.LayoutParams.SoftInputModeFlags int softInputMode,
+            int windowFlags, @Nullable EditorInfo editorInfo,
+            IRemoteInputConnection inputConnection,
+            IRemoteAccessibilityInputConnection remoteAccessibilityInputConnection,
+            int unverifiedTargetSdkVersion, @UserIdInt int userId,
+            @NonNull ImeOnBackInvokedDispatcher imeDispatcher)
+            throws RemoteException {
+        // Should never be called when flag is enabled i.e. when this proxy is used.
+        return null;
+    }
+
+    @Override
+    public void showInputMethodPickerFromClient(IInputMethodClient client,
+            int auxiliarySubtypeMode)
+            throws RemoteException {
+        offload(() -> mInner.showInputMethodPickerFromClient(client, auxiliarySubtypeMode));
+    }
+
+    @EnforcePermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+    @Override
+    public void showInputMethodPickerFromSystem(int auxiliarySubtypeMode, int displayId)
+            throws RemoteException {
+        mInner.showInputMethodPickerFromSystem(auxiliarySubtypeMode, displayId);
+    }
+
+    @EnforcePermission(Manifest.permission.TEST_INPUT_METHOD)
+    @Override
+    public boolean isInputMethodPickerShownForTest() throws RemoteException {
+        super.isInputMethodPickerShownForTest_enforcePermission();
+        return mInner.isInputMethodPickerShownForTest();
+    }
+
+    @Override
+    public InputMethodSubtype getCurrentInputMethodSubtype(int userId) throws RemoteException {
+        return mInner.getCurrentInputMethodSubtype(userId);
+    }
+
+    @Override
+    public void setAdditionalInputMethodSubtypes(String imiId, InputMethodSubtype[] subtypes,
+            @UserIdInt int userId) throws RemoteException {
+        mInner.setAdditionalInputMethodSubtypes(imiId, subtypes, userId);
+    }
+
+    @Override
+    public void setExplicitlyEnabledInputMethodSubtypes(String imeId,
+            @NonNull int[] subtypeHashCodes, @UserIdInt int userId) throws RemoteException {
+        mInner.setExplicitlyEnabledInputMethodSubtypes(imeId, subtypeHashCodes, userId);
+    }
+
+    @Override
+    public int getInputMethodWindowVisibleHeight(IInputMethodClient client)
+            throws RemoteException {
+        return mInner.getInputMethodWindowVisibleHeight(client);
+    }
+
+    @Override
+    public void reportPerceptibleAsync(IBinder windowToken, boolean perceptible)
+            throws RemoteException {
+        // Already async TODO(b/293640003): ordering issues?
+        mInner.reportPerceptibleAsync(windowToken, perceptible);
+    }
+
+    @EnforcePermission(Manifest.permission.INTERNAL_SYSTEM_WINDOW)
+    @Override
+    public void removeImeSurface() throws RemoteException {
+        mInner.removeImeSurface();
+    }
+
+    @Override
+    public void removeImeSurfaceFromWindowAsync(IBinder windowToken) throws RemoteException {
+        mInner.removeImeSurfaceFromWindowAsync(windowToken);
+    }
+
+    @Override
+    public void startProtoDump(byte[] bytes, int i, String s) throws RemoteException {
+        mInner.startProtoDump(bytes, i, s);
+    }
+
+    @Override
+    public boolean isImeTraceEnabled() throws RemoteException {
+        return mInner.isImeTraceEnabled();
+    }
+
+    @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+    @Override
+    public void startImeTrace() throws RemoteException {
+        mInner.startImeTrace();
+    }
+
+    @EnforcePermission(Manifest.permission.CONTROL_UI_TRACING)
+    @Override
+    public void stopImeTrace() throws RemoteException {
+        mInner.stopImeTrace();
+    }
+
+    @Override
+    public void startStylusHandwriting(IInputMethodClient client)
+            throws RemoteException {
+        offload(() -> mInner.startStylusHandwriting(client));
+    }
+
+    @Override
+    public void startConnectionlessStylusHandwriting(IInputMethodClient client, int userId,
+            @Nullable CursorAnchorInfo cursorAnchorInfo, @Nullable String delegatePackageName,
+            @Nullable String delegatorPackageName,
+            @NonNull IConnectionlessHandwritingCallback callback) throws RemoteException {
+        offload(() -> mInner.startConnectionlessStylusHandwriting(
+                client, userId, cursorAnchorInfo, delegatePackageName, delegatorPackageName,
+                callback));
+    }
+
+    @Override
+    public boolean acceptStylusHandwritingDelegation(
+            @NonNull IInputMethodClient client,
+            @UserIdInt int userId,
+            @NonNull String delegatePackageName,
+            @NonNull String delegatorPackageName,
+            @InputMethodManager.HandwritingDelegateFlags int flags) {
+        try {
+            return CompletableFuture.supplyAsync(() -> {
+                try {
+                    return mInner.acceptStylusHandwritingDelegation(
+                            client, userId, delegatePackageName, delegatorPackageName, flags);
+                } catch (RemoteException e) {
+                    throw new RuntimeException(e);
+                }
+            }, this::offload).get();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        } catch (ExecutionException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    @Override
+    public void prepareStylusHandwritingDelegation(
+            @NonNull IInputMethodClient client,
+            @UserIdInt int userId,
+            @NonNull String delegatePackageName,
+            @NonNull String delegatorPackageName) {
+        offload(() -> mInner.prepareStylusHandwritingDelegation(
+                client, userId, delegatePackageName, delegatorPackageName));
+    }
+
+    @Override
+    public boolean isStylusHandwritingAvailableAsUser(int userId, boolean connectionless)
+            throws RemoteException {
+        return mInner.isStylusHandwritingAvailableAsUser(userId, connectionless);
+    }
+
+    @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+    @Override
+    public void addVirtualStylusIdForTestSession(IInputMethodClient client)
+            throws RemoteException {
+        mInner.addVirtualStylusIdForTestSession(client);
+    }
+
+    @EnforcePermission("android.permission.TEST_INPUT_METHOD")
+    @Override
+    public void setStylusWindowIdleTimeoutForTest(IInputMethodClient client, long timeout)
+            throws RemoteException {
+        mInner.setStylusWindowIdleTimeoutForTest(client, timeout);
+    }
+
+    @Override
+    public IImeTracker getImeTrackerService() throws RemoteException {
+        return mInner.getImeTrackerService();
+    }
+
+    @BinderThread
+    @Override
+    public void onShellCommand(@Nullable FileDescriptor in, @Nullable FileDescriptor out,
+            @Nullable FileDescriptor err,
+            @NonNull String[] args, @Nullable ShellCallback callback,
+            @NonNull ResultReceiver resultReceiver) throws RemoteException {
+        ((InputMethodManagerService) mInner).onShellCommand(
+                in, out, err, args, callback, resultReceiver);
+    }
+
+    private void sendOnStartInputResult(
+            IInputMethodClient client, InputBindResult res, int startInputSeq) {
+        InputMethodManagerService service = (InputMethodManagerService) mInner;
+        final ClientState cs = service.getClientState(client);
+        if (cs != null && cs.mClient != null) {
+            cs.mClient.onStartInputResult(res, startInputSeq);
+        } else {
+            // client is unbound.
+            Slog.i(TAG, "Client that requested startInputOrWindowGainedFocus is no longer"
+                    + " bound. InputBindResult: " + res + " for startInputSeq: " + startInputSeq);
+        }
+    }
+}
+
diff --git a/services/core/java/com/android/server/wm/ClientLifecycleManager.java b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
index 5b4fb3e..e48e4e8 100644
--- a/services/core/java/com/android/server/wm/ClientLifecycleManager.java
+++ b/services/core/java/com/android/server/wm/ClientLifecycleManager.java
@@ -87,11 +87,7 @@
     void scheduleTransactionItemNow(@NonNull IApplicationThread client,
             @NonNull ClientTransactionItem transactionItem) throws RemoteException {
         final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
-        if (transactionItem.isActivityLifecycleItem()) {
-            clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
-        } else {
-            clientTransaction.addCallback(transactionItem);
-        }
+        clientTransaction.addTransactionItem(transactionItem);
         scheduleTransaction(clientTransaction);
     }
 
@@ -115,11 +111,8 @@
         } else {
             // TODO(b/260873529): cleanup after launch.
             final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
-            if (transactionItem.isActivityLifecycleItem()) {
-                clientTransaction.setLifecycleStateRequest((ActivityLifecycleItem) transactionItem);
-            } else {
-                clientTransaction.addCallback(transactionItem);
-            }
+            clientTransaction.addTransactionItem(transactionItem);
+
             scheduleTransaction(clientTransaction);
         }
     }
@@ -160,8 +153,8 @@
         } else {
             // TODO(b/260873529): cleanup after launch.
             final ClientTransaction clientTransaction = ClientTransaction.obtain(client);
-            clientTransaction.addCallback(transactionItem);
-            clientTransaction.setLifecycleStateRequest(lifecycleItem);
+            clientTransaction.addTransactionItem(transactionItem);
+            clientTransaction.addTransactionItem(lifecycleItem);
             scheduleTransaction(clientTransaction);
         }
     }
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index 838ce86..10cbc66 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1590,7 +1590,7 @@
                             mAtmService.getLifecycleManager().scheduleTransactionItem(
                                     appThread, activityResultItem);
                         } else {
-                            transaction.addCallback(activityResultItem);
+                            transaction.addTransactionItem(activityResultItem);
                         }
                     }
                 }
@@ -1602,7 +1602,7 @@
                         mAtmService.getLifecycleManager().scheduleTransactionItem(
                                 appThread, newIntentItem);
                     } else {
-                        transaction.addCallback(newIntentItem);
+                        transaction.addTransactionItem(newIntentItem);
                     }
                 }
 
@@ -1624,7 +1624,7 @@
                     mAtmService.getLifecycleManager().scheduleTransactionItem(
                             appThread, resumeActivityItem);
                 } else {
-                    transaction.setLifecycleStateRequest(resumeActivityItem);
+                    transaction.addTransactionItem(resumeActivityItem);
                     mAtmService.getLifecycleManager().scheduleTransaction(transaction);
                 }
 
diff --git a/tests/BootImageProfileTest/AndroidTest.xml b/tests/BootImageProfileTest/AndroidTest.xml
index 7e97fa3..9b527dc 100644
--- a/tests/BootImageProfileTest/AndroidTest.xml
+++ b/tests/BootImageProfileTest/AndroidTest.xml
@@ -14,6 +14,7 @@
      limitations under the License.
 -->
 <configuration description="Config for BootImageProfileTest">
+    <target_preparer class="com.android.tradefed.targetprep.RootTargetPreparer" />
      <!-- do not use DeviceSetup#set-property because it reboots the device b/136200738.
          furthermore the changes in /data/local.prop don't actually seem to get picked up.
     -->
diff --git a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
index 70e4a71..443de8e 100644
--- a/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/phone/light_landscape_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
index 502c1b4..cb69c0e 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-ansi.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
index 591b2fa..1c6d1b3 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview-jis.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
index 0137a85..c51da05 100644
--- a/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/phone/light_portrait_layout-preview.png
Binary files differ
diff --git a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
index 37a91e1..ab23401 100644
--- a/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
+++ b/tests/InputScreenshotTest/assets/tablet/dark_portrait_layout-preview.png
Binary files differ