[Fill Dialog Improvements] Implement Fill Dialog improvements

Implement Fill Dialog improvements.
This change achieves a multitude of changes.
1. Disable pre-trigger flow
2. Show fill dialog if applicable
3. Reset fill dialog state so that fill dialog can be retriggered
   on another fill request in the same session. This part isn't
   tested yet, and would be done later.

Bug: 377868687
Flag: android.service.autofill.improve_fill_dialog_aconfig
Test: atest CtsAutoFillServiceTestCases
      atest android.autofillservice.cts.dialog.LoginActivityTest

Change-Id: I959ecc4e98d26f059a6a354f6c83d653555e89c3
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();
     }
 }