Merge "[Fill Dialog Improvements] Implement Fill Dialog improvements" into main
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 1de0474..60e528c 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -26,6 +26,7 @@
 import static android.service.autofill.FillRequest.FLAG_VIEW_NOT_FOCUSED;
 import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
 import static android.service.autofill.Flags.FLAG_FILL_DIALOG_IMPROVEMENTS;
+import static android.service.autofill.Flags.improveFillDialogAconfig;
 import static android.service.autofill.Flags.relayoutFix;
 import static android.view.ContentInfo.SOURCE_AUTOFILL;
 import static android.view.autofill.Helper.sDebug;
@@ -787,6 +788,11 @@
 
     private AutofillStateFingerprint mAutofillStateFingerprint;
 
+    /**
+     * Whether improveFillDialog feature is enabled or not.
+     */
+    private boolean mImproveFillDialogEnabled;
+
     /** @hide */
     public interface AutofillClient {
         /**
@@ -1017,6 +1023,17 @@
         mRelayoutFix = relayoutFix() && AutofillFeatureFlags.enableRelayoutFixes();
         mRelativePositionForRelayout = AutofillFeatureFlags.enableRelativeLocationForRelayout();
         mIsCredmanIntegrationEnabled = Flags.autofillCredmanIntegration();
+        mImproveFillDialogEnabled =
+                improveFillDialogAconfig() && AutofillFeatureFlags.isImproveFillDialogEnabled();
+    }
+
+    /**
+     * Whether improvement to fill dialog is enabled.
+     *
+     * @hide
+     */
+    public boolean isImproveFillDialogEnabled() {
+        return mImproveFillDialogEnabled;
     }
 
     /**
@@ -1679,6 +1696,11 @@
 
     private void notifyViewReadyInner(AutofillId id, @Nullable String[] autofillHints,
             boolean isCredmanRequested) {
+        if (isImproveFillDialogEnabled() && !isCredmanRequested) {
+            // We do not want to send pre-trigger request.
+            // TODO(b/377868687): verify if we can remove the flow for isCredmanRequested too.
+            return;
+        }
         if (sDebug) {
             Log.d(TAG, "notifyViewReadyInner:" + id);
         }
@@ -2046,6 +2068,34 @@
     }
 
     /**
+     * Notify autofill system that IME animation has started
+     * @param startTimeMs start time as measured by SystemClock.elapsedRealtime()
+     */
+    void notifyImeAnimationStart(long startTimeMs) {
+        try {
+            mService.notifyImeAnimationStart(mSessionId, startTimeMs, mContext.getUserId());
+        } catch (RemoteException e) {
+            // The failure could be a consequence of something going wrong on the
+            // server side. Just log the exception and move-on.
+            Log.w(TAG, "notifyImeAnimationStart(): RemoteException caught but ignored " + e);
+        }
+    }
+
+    /**
+     * Notify autofill system that IME animation has ended
+     * @param endTimeMs end time as measured by SystemClock.elapsedRealtime()
+     */
+    void notifyImeAnimationEnd(long endTimeMs) {
+        try {
+            mService.notifyImeAnimationEnd(mSessionId, endTimeMs, mContext.getUserId());
+        } catch (RemoteException e) {
+            // The failure could be a consequence of something going wrong on the
+            // server side. Just log the exception and move-on.
+            Log.w(TAG, "notifyImeAnimationStart(): RemoteException caught but ignored " + e);
+        }
+    }
+
+    /**
      * Called when a virtual view that supports autofill is exited.
      *
      * @param view the virtual view parent.
@@ -4050,6 +4100,10 @@
     @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
     @Deprecated
     public boolean showAutofillDialog(@NonNull View view) {
+        if (isImproveFillDialogEnabled()) {
+            Log.i(TAG, "showAutofillDialog() return false due to improve fill dialog");
+            return false;
+        }
         Objects.requireNonNull(view);
         if (shouldShowAutofillDialog(view, view.getAutofillId())) {
             mShowAutofillDialogCalled = true;
@@ -4093,6 +4147,10 @@
     @FlaggedApi(FLAG_FILL_DIALOG_IMPROVEMENTS)
     @Deprecated
     public boolean showAutofillDialog(@NonNull View view, int virtualId) {
+        if (isImproveFillDialogEnabled()) {
+            Log.i(TAG, "showAutofillDialog() return false due to improve fill dialog");
+            return false;
+        }
         Objects.requireNonNull(view);
         if (shouldShowAutofillDialog(view, getAutofillId(view, virtualId))) {
             mShowAutofillDialogCalled = true;
@@ -4117,7 +4175,7 @@
             return false;
         }
 
-        if (getImeStateFlag(view) == FLAG_IME_SHOWING) {
+        if (getImeStateFlag(view) == FLAG_IME_SHOWING && !isImproveFillDialogEnabled()) {
             // IME is showing
             return false;
         }
diff --git a/core/java/android/view/autofill/IAutoFillManager.aidl b/core/java/android/view/autofill/IAutoFillManager.aidl
index f67405f..28f8577 100644
--- a/core/java/android/view/autofill/IAutoFillManager.aidl
+++ b/core/java/android/view/autofill/IAutoFillManager.aidl
@@ -70,4 +70,6 @@
     void notifyNotExpiringResponseDuringAuth(int sessionId, int userId);
     void notifyViewEnteredIgnoredDuringAuthCount(int sessionId, int userId);
     void setAutofillIdsAttemptedForRefill(int sessionId, in List<AutofillId> ids, int userId);
+    void notifyImeAnimationStart(int sessionId, long startTimeMs, int userId);
+    void notifyImeAnimationEnd(int sessionId, long endTimeMs, int userId);
 }
diff --git a/services/autofill/bugfixes.aconfig b/services/autofill/bugfixes.aconfig
index 5e1b147..9c83757 100644
--- a/services/autofill/bugfixes.aconfig
+++ b/services/autofill/bugfixes.aconfig
@@ -9,6 +9,16 @@
 }
 
 flag {
+  name: "improve_fill_dialog_aconfig"
+  namespace: "autofill"
+  description: "Improvements for Fill Dialog. Guard DeviceConfig rollout "
+  bug: "382493181"
+  metadata {
+    purpose: PURPOSE_BUGFIX
+  }
+}
+
+flag {
   name: "fill_fields_from_current_session_only"
   namespace: "autofill"
   description: "Only fill autofill fields that are part of the current session."
diff --git a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
index 219b788..5e7e557 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillInlineSuggestionsRequestSession.java
@@ -354,6 +354,13 @@
         }
     }
 
+    private void handleOnInputMethodStartInputView() {
+        synchronized (mLock) {
+            mUiCallback.onInputMethodStartInputView();
+            handleOnReceiveImeStatusUpdated(true, true);
+        }
+    }
+
     /**
      * Handles the IME session status received from the IME.
      *
@@ -437,8 +444,8 @@
             final AutofillInlineSuggestionsRequestSession session = mSession.get();
             if (session != null) {
                 session.mHandler.sendMessage(obtainMessage(
-                        AutofillInlineSuggestionsRequestSession::handleOnReceiveImeStatusUpdated,
-                        session, true, true));
+                        AutofillInlineSuggestionsRequestSession::handleOnInputMethodStartInputView,
+                        session));
             }
         }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index 259ea14..cba8c66 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -2139,6 +2139,32 @@
         }
 
         @Override
+        public void notifyImeAnimationStart(int sessionId, long startTimeMs, int userId) {
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service =
+                        peekServiceForUserWithLocalBinderIdentityLocked(userId);
+                if (service != null) {
+                    service.notifyImeAnimationStart(sessionId, startTimeMs, getCallingUid());
+                } else if (sVerbose) {
+                    Slog.v(TAG, "notifyImeAnimationStart(): no service for " + userId);
+                }
+            }
+        }
+
+        @Override
+        public void notifyImeAnimationEnd(int sessionId, long endTimeMs, int userId) {
+            synchronized (mLock) {
+                final AutofillManagerServiceImpl service =
+                        peekServiceForUserWithLocalBinderIdentityLocked(userId);
+                if (service != null) {
+                    service.notifyImeAnimationEnd(sessionId, endTimeMs, getCallingUid());
+                } else if (sVerbose) {
+                    Slog.v(TAG, "notifyImeAnimationEnd(): no service for " + userId);
+                }
+            }
+        }
+
+        @Override
         public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
             if (!DumpUtils.checkDumpPermission(getContext(), TAG, pw)) return;
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 5cf96bf..0fa43ac 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -823,6 +823,36 @@
     }
 
     @GuardedBy("mLock")
+    public void notifyImeAnimationStart(int sessionId, long startTimeMs, int uid) {
+        if (!isEnabledLocked()) {
+            Slog.wtf(TAG, "Service not enabled");
+            return;
+        }
+        final Session session = mSessions.get(sessionId);
+        if (session == null || uid != session.uid) {
+            Slog.v(TAG, "notifyImeAnimationStart(): no session for "
+                    + sessionId + "(" + uid + ")");
+            return;
+        }
+        session.notifyImeAnimationStart(startTimeMs);
+    }
+
+    @GuardedBy("mLock")
+    public void notifyImeAnimationEnd(int sessionId, long endTimeMs, int uid) {
+        if (!isEnabledLocked()) {
+            Slog.wtf(TAG, "Service not enabled");
+            return;
+        }
+        final Session session = mSessions.get(sessionId);
+        if (session == null || uid != session.uid) {
+            Slog.v(TAG, "notifyImeAnimationEnd(): no session for "
+                    + sessionId + "(" + uid + ")");
+            return;
+        }
+        session.notifyImeAnimationEnd(endTimeMs);
+    }
+
+    @GuardedBy("mLock")
     @Override // from PerUserSystemService
     protected void handlePackageUpdateLocked(@NonNull String packageName) {
         final ServiceInfo serviceInfo = mFieldClassificationStrategy.getServiceInfo();
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index 6b227d7..9c6e4741 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -40,6 +40,7 @@
 import static android.service.autofill.FillRequest.FLAG_VIEW_REQUESTS_CREDMAN_SERVICE;
 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
 import static android.service.autofill.Flags.highlightAutofillSingleField;
+import static android.service.autofill.Flags.improveFillDialogAconfig;
 import static android.view.autofill.AutofillManager.ACTION_RESPONSE_EXPIRED;
 import static android.view.autofill.AutofillManager.ACTION_START_SESSION;
 import static android.view.autofill.AutofillManager.ACTION_VALUE_CHANGED;
@@ -50,6 +51,7 @@
 import static android.view.autofill.AutofillManager.EXTRA_AUTOFILL_REQUEST_ID;
 import static android.view.autofill.AutofillManager.FLAG_SMART_SUGGESTION_SYSTEM;
 import static android.view.autofill.AutofillManager.getSmartSuggestionModeToString;
+
 import static com.android.internal.util.function.pooled.PooledLambda.obtainMessage;
 import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_EXPLICITLY_REQUESTED;
 import static com.android.server.autofill.FillRequestEventLogger.TRIGGER_REASON_NORMAL_TRIGGER;
@@ -184,6 +186,7 @@
 import android.view.autofill.IAutofillWindowPresenter;
 import android.view.inputmethod.InlineSuggestionsRequest;
 import android.widget.RemoteViews;
+
 import com.android.internal.R;
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.logging.MetricsLogger;
@@ -195,6 +198,7 @@
 import com.android.server.autofill.ui.PendingUi;
 import com.android.server.inputmethod.InputMethodManagerInternal;
 import com.android.server.wm.ActivityTaskManagerInternal;
+
 import java.io.PrintWriter;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -248,6 +252,8 @@
     private static final int DEFAULT__FILL_REQUEST_ID_SNAPSHOT = -2;
     private static final int DEFAULT__FIELD_CLASSIFICATION_REQUEST_ID_SNAPSHOT = -2;
 
+    private static final long DEFAULT_UNASSIGNED_TIME = -1;
+
     static final String SESSION_ID_KEY = "autofill_session_id";
     static final String REQUEST_ID_KEY = "autofill_request_id";
 
@@ -292,6 +298,31 @@
     @Retention(RetentionPolicy.SOURCE)
     @interface SessionState {}
 
+    /**
+     * Indicates fill dialog will not be shown.
+     */
+    private static final int SHOW_FILL_DIALOG_NO = 0;
+
+    /**
+     * Indicates fill dialog is shown.
+     */
+    private static final int SHOW_FILL_DIALOG_YES = 1;
+
+    /**
+     * Indicates fill dialog can be shown, but we need to wait.
+     * For eg, if the IME animation is happening, we need for it to complete before fill dialog can
+     * be shown.
+     */
+    private static final int SHOW_FILL_DIALOG_WAIT = 2;
+
+    @IntDef(prefix = { "SHOW_FILL_DIALOG_" }, value = {
+            SHOW_FILL_DIALOG_NO,
+            SHOW_FILL_DIALOG_YES,
+            SHOW_FILL_DIALOG_WAIT
+    })
+    @Retention(RetentionPolicy.SOURCE)
+    private @interface ShowFillDialogState{}
+
     @GuardedBy("mLock")
     private final SessionFlags mSessionFlags;
 
@@ -576,6 +607,47 @@
 
     private boolean mIgnoreViewStateResetToEmpty;
 
+    /**
+     * Whether improveFillDialog feature is enabled or not.
+     * Configured via device config flag and aconfig flag.
+     */
+    private final boolean mImproveFillDialogEnabled;
+
+    /**
+     * Timeout, after which, fill dialog won't be shown.
+     * Configured via device config flag.
+     */
+    private final long mFillDialogTimeoutMs;
+
+    /**
+     * Time to wait after ime Animation ends before showing up fill dialog.
+     * Configured via device config flag.
+     */
+    private final long mFillDialogMinWaitAfterImeAnimationMs;
+
+    /**
+     * Indicates the need to wait for ime animation to end before showing up fill dialog.
+     * This is set when we receive notification of ime animation start.
+     * Focussing on one input field from another wouldn't cause ime to re-animate. So this will let
+     * us know whether we need to wait for ime animation finish notification.
+     */
+    private boolean mWaitForImeAnimation;
+
+    /**
+     * A runnable set to run when there is a need to wait for ime animation to end before showing
+     * up fill dialog. This is set only if the fill response has been received, and the response
+     * is eligible for showing up fill dialog, but the ime animation hasn't completed. This allows
+     * for this runnable to be scheduled/run when the ime animation ends, in order to show fill
+     * dialog.
+     */
+    private Runnable mFillDialogRunnable;
+
+    private long mImeAnimationFinishTimeMs = DEFAULT_UNASSIGNED_TIME;
+
+    private long mImeAnimationStartTimeMs = DEFAULT_UNASSIGNED_TIME;
+
+    private long mLastInputStartTime = DEFAULT_UNASSIGNED_TIME;
+
     /*
      * Id of the previous view that was entered. Once set, it would only be replaced by non-null
      * view ids.
@@ -1493,6 +1565,12 @@
         // Now request the assist structure data.
         requestAssistStructureLocked(requestId, flags);
 
+        if (mImproveFillDialogEnabled) {
+            // New request has been sent, so re-enable fill dialog.
+            // Fill dialog is eligible to be shown after each Fill request.
+            enableFillDialog();
+        }
+
         return Optional.of(requestId);
     }
 
@@ -1657,6 +1735,11 @@
         mSaveEventLogger = SaveEventLogger.forSessionId(sessionId, mLatencyBaseTime);
         mIsPrimaryCredential = isPrimaryCredential;
         mIgnoreViewStateResetToEmpty = AutofillFeatureFlags.shouldIgnoreViewStateResetToEmpty();
+        mImproveFillDialogEnabled =
+                improveFillDialogAconfig() && AutofillFeatureFlags.isImproveFillDialogEnabled();
+        mFillDialogTimeoutMs = AutofillFeatureFlags.getFillDialogTimeoutMs();
+        mFillDialogMinWaitAfterImeAnimationMs =
+                AutofillFeatureFlags.getFillDialogMinWaitAfterImeAnimationtEndMs();
 
         synchronized (mLock) {
             mSessionFlags = new SessionFlags();
@@ -1682,6 +1765,13 @@
                             public void notifyInlineUiHidden(AutofillId autofillId) {
                                 notifyFillUiHidden(autofillId);
                             }
+
+                            @Override
+                            public void onInputMethodStartInputView() {
+                                // TODO(b/377868687): This method isn't called when IME doesn't
+                                //  support inline suggestion. Handle those cases as well.
+                                onInputMethodStart();
+                            }
                         });
 
         mMetricsLogger.write(
@@ -3044,6 +3134,12 @@
         }
     }
 
+    private void onInputMethodStart() {
+        synchronized (mLock) {
+            mLastInputStartTime = SystemClock.elapsedRealtime();
+        }
+    }
+
     private void doStartIntentSender(IntentSender intentSender, Intent intent) {
         try {
             synchronized (mLock) {
@@ -5407,6 +5503,15 @@
         }
     }
 
+    private void resetImeAnimationState() {
+        synchronized (mLock) {
+            mWaitForImeAnimation = false;
+            mImeAnimationStartTimeMs = DEFAULT_UNASSIGNED_TIME;
+            mImeAnimationFinishTimeMs = DEFAULT_UNASSIGNED_TIME;
+            mLastInputStartTime = DEFAULT_UNASSIGNED_TIME;
+        }
+    }
+
     @Override
     public void onFillReady(
             @NonNull FillResponse response,
@@ -5452,18 +5557,24 @@
 
         final AutofillId[] ids = response.getFillDialogTriggerIds();
         if (ids != null && ArrayUtils.contains(ids, filledId)) {
-            if (requestShowFillDialog(response, filledId, filterText, flags)) {
+            @ShowFillDialogState int fillDialogState =
+                    requestShowFillDialog(response, filledId, filterText, flags);
+            if (fillDialogState == SHOW_FILL_DIALOG_YES) {
                 synchronized (mLock) {
                     final ViewState currentView = mViewStates.get(mCurrentViewId);
                     currentView.setState(ViewState.STATE_FILL_DIALOG_SHOWN);
                 }
-                // Just show fill dialog once, so disabled after shown.
+                // Just show fill dialog once per fill request, so disabled after shown.
                 // Note: Cannot disable before requestShowFillDialog() because the method
-                //       need to check whether fill dialog enabled.
+                //       need to check whether fill dialog is enabled.
                 setFillDialogDisabled();
+                resetImeAnimationState();
                 return;
-            } else {
+            }  else if (fillDialogState == SHOW_FILL_DIALOG_NO) {
+                resetImeAnimationState();
                 setFillDialogDisabled();
+            } else { // SHOW_FILL_DIALOG_WAIT
+                return;
             }
         }
 
@@ -5559,7 +5670,20 @@
         }
     }
 
+    private void enableFillDialog() {
+        if (sVerbose) {
+            Slog.v(TAG, "Enabling Fill Dialog....");
+        }
+        synchronized (mLock) {
+            mSessionFlags.mFillDialogDisabled = false;
+        }
+        notifyClientFillDialogTriggerIds(null);
+    }
+
     private void setFillDialogDisabled() {
+        if (sVerbose) {
+            Slog.v(TAG, "Disabling Fill Dialog.");
+        }
         synchronized (mLock) {
             mSessionFlags.mFillDialogDisabled = true;
         }
@@ -5577,24 +5701,28 @@
         }
     }
 
-    private boolean requestShowFillDialog(
+    private @ShowFillDialogState int requestShowFillDialog(
             FillResponse response, AutofillId filledId, String filterText, int flags) {
         if (!isFillDialogUiEnabled()) {
+            // TODO(b/377868687): The above check includes credman fields. We may want to show
+            //  credman fields again.
             // Unsupported fill dialog UI
-            if (sDebug) Log.w(TAG, "requestShowFillDialog: fill dialog is disabled");
-            return false;
+            if (sDebug) Log.w(TAG, "requestShowFillDialog(): fill dialog is disabled");
+            return SHOW_FILL_DIALOG_NO;
         }
 
-        if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) {
-            // IME is showing, fallback to normal suggestions UI
-            if (sDebug) Log.w(TAG, "requestShowFillDialog: IME is showing");
-            return false;
-        }
+        if (!mImproveFillDialogEnabled) {
+            if ((flags & FillRequest.FLAG_IME_SHOWING) != 0) {
+                // IME is showing, fallback to normal suggestions UI
+                if (sDebug) Log.w(TAG, "requestShowFillDialog(): IME is showing");
+                return SHOW_FILL_DIALOG_NO;
+            }
 
-        if (mInlineSessionController.isImeShowing()) {
-            // IME is showing, fallback to normal suggestions UI
-            // Note: only work when inline suggestions supported
-            return false;
+            if (mInlineSessionController.isImeShowing()) {
+                // IME is showing, fallback to normal suggestions UI
+                // Note: only work when inline suggestions supported
+                return SHOW_FILL_DIALOG_NO;
+            }
         }
 
         synchronized (mLock) {
@@ -5602,29 +5730,84 @@
                     || !ArrayUtils.contains(mLastFillDialogTriggerIds, filledId)) {
                 // Last fill dialog triggered ids are changed.
                 if (sDebug) Log.w(TAG, "Last fill dialog triggered ids are changed.");
-                return false;
+                return SHOW_FILL_DIALOG_NO;
+            }
+
+            if (mImproveFillDialogEnabled && mInlineSessionController.isImeShowing()) {
+                long currentTimestampMs = SystemClock.elapsedRealtime();
+                long durationMs = currentTimestampMs - mLastInputStartTime;
+                if (sVerbose) {
+                    Log.d(TAG, "IME is showing. Checking for elapsed time ");
+                    Log.d(TAG, "IME is showing. Timestamps start: " + mLastInputStartTime
+                            + " current: " +  currentTimestampMs + " duration: " + durationMs
+                            + " mFillDialogTimeoutMs: " + mFillDialogTimeoutMs);
+                }
+
+                // Following situations can arise wrt IME animation.
+                // 1. No animation happening (eg IME already animated). In that case,
+                // mWaitForImeAnimation should be false. This is possible if the IME is already up
+                // on a field, but the user focusses on another field. Under such condition,
+                // since IME has already animated, there won't be another animation. However,
+                // onInputStartInputView is still called.
+                // 2. Animation is still proceeding. We should wait for animation to finish,
+                // and then proceed.
+                // 3. Animation is complete.
+                if (mWaitForImeAnimation) {
+                    // we need to wait for animation to happen. We can't return from here yet.
+                    // This is the situation #2 described above.
+                    Log.d(TAG, "Waiting for ime animation to complete before showing fill dialog");
+                    mFillDialogRunnable = createFillDialogEvalRunnable(
+                            response, filledId, filterText, flags);
+                    return SHOW_FILL_DIALOG_WAIT;
+                }
+
+                // Incorporate situations 1 & 3 discussed above. We calculate the duration from the
+                // max of start input time or the ime finish time
+                long effectiveDuration = currentTimestampMs
+                        - Math.max(mLastInputStartTime, mImeAnimationFinishTimeMs);
+                if (effectiveDuration >= mFillDialogTimeoutMs) {
+                    Log.d(TAG, "Fill dialog not shown since IME has been up for more time than "
+                            + mFillDialogTimeoutMs + "ms");
+                    return SHOW_FILL_DIALOG_NO;
+                } else if (effectiveDuration < mFillDialogMinWaitAfterImeAnimationMs) {
+                    // we need to wait for some time after animation ends
+                    Runnable runnable = createFillDialogEvalRunnable(
+                            response, filledId, filterText, flags);
+                    mHandler.postDelayed(runnable,
+                            mFillDialogMinWaitAfterImeAnimationMs - effectiveDuration);
+                    return SHOW_FILL_DIALOG_WAIT;
+                }
             }
         }
 
+        showFillDialog(response, filledId, filterText);
+        return SHOW_FILL_DIALOG_YES;
+    }
+
+    private Runnable createFillDialogEvalRunnable(
+            @NonNull FillResponse response,
+            @NonNull AutofillId filledId,
+            String filterText,
+            int flags) {
+        return () -> {
+            synchronized (mLock) {
+                AutofillValue value = AutofillValue.forText(filterText);
+                onFillReady(response, filledId, value, flags);
+            }
+        };
+    }
+
+    private void showFillDialog(FillResponse response, AutofillId filledId, String filterText) {
         Drawable serviceIcon = null;
+        PresentationStatsEventLogger logger = null;
         synchronized (mLock) {
             serviceIcon = getServiceIcon(response);
+            logger = mPresentationStatsEventLogger;
         }
 
-        getUiForShowing()
-                .showFillDialog(
-                        filledId,
-                        response,
-                        filterText,
-                        mService.getServicePackageName(),
-                        mComponentName,
-                        serviceIcon,
-                        this,
-                        id,
-                        mCompatMode,
-                        mPresentationStatsEventLogger,
-                        mLock);
-        return true;
+        getUiForShowing().showFillDialog(filledId, response, filterText,
+                mService.getServicePackageName(), mComponentName, serviceIcon, this,
+                id, mCompatMode, logger, mLock);
     }
 
     /**
@@ -7689,6 +7872,30 @@
         mSessionState = STATE_REMOVED;
     }
 
+    @GuardedBy("mLock")
+    public void notifyImeAnimationStart(long startTimeMs) {
+        mImeAnimationStartTimeMs = startTimeMs;
+        mWaitForImeAnimation = true;
+    }
+
+    @GuardedBy("mLock")
+    public void notifyImeAnimationEnd(long endTimeMs) {
+        mImeAnimationFinishTimeMs = endTimeMs;
+        // Make sure to use mRunnable with synchronized
+        if (mFillDialogRunnable != null) {
+            if (sVerbose) {
+                Log.v(TAG, "Ime animation ended, starting fill dialog.");
+            }
+            mHandler.postDelayed(mFillDialogRunnable, mFillDialogMinWaitAfterImeAnimationMs);
+            mFillDialogRunnable = null;
+        } else {
+            if (sVerbose) {
+                Log.v(TAG, "Ime animation ended, no runnable present.");
+            }
+        }
+        mWaitForImeAnimation = false;
+    }
+
     void onPendingSaveUi(int operation, @NonNull IBinder token) {
         getUiForShowing().onPendingSaveUi(operation, token);
     }
diff --git a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
index ffc80ee..7287bdd 100644
--- a/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/InlineFillUi.java
@@ -409,5 +409,10 @@
          * Callback to notify inline ui is hidden.
          */
         void notifyInlineUiHidden(@NonNull AutofillId autofillId);
+
+        /**
+         * Callback to notify input method started.
+         */
+        void onInputMethodStartInputView();
     }
 }