Handle package changes in autofill manager service

Autofill manager service was not observing package changes thus
we did not properly handle the cases of the service being updated,
added, and removed. Handling, additions is needed to properly
support restore from a backup. Fixed a few missing locks.

Test: all autofill CTS tests pass and manually tested update, add,
      and remove of autofill services.

bug:36638606
bug:36978445

Change-Id: Idd47891774ba2a4e562a1952cbb5a048211fd4e3
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index 34aecdc..695efdd 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -7642,7 +7642,8 @@
         return mContext.getSystemService(AutofillManager.class);
     }
 
-    private boolean isAutofillable() {
+    /** @hide */
+    public boolean isAutofillable() {
         return getAutofillType() != AUTOFILL_TYPE_NONE && !isAutofillBlocked();
     }
 
diff --git a/core/java/android/view/autofill/AutofillManager.java b/core/java/android/view/autofill/AutofillManager.java
index 6e02a84..9ee0cb1 100644
--- a/core/java/android/view/autofill/AutofillManager.java
+++ b/core/java/android/view/autofill/AutofillManager.java
@@ -400,7 +400,6 @@
         }
     }
 
-
     /**
      * Called when a {@link View} that supports autofill is entered.
      *
@@ -780,8 +779,12 @@
             throw e.rethrowFromSystemServer();
         }
 
-        mTrackedViews = null;
+        resetSessionLocked();
+    }
+
+    private void resetSessionLocked() {
         mSessionId = NO_SESSION;
+        mTrackedViews = null;
     }
 
     private void updateSessionLocked(AutofillId id, Rect bounds, AutofillValue value, int flags) {
@@ -903,9 +906,17 @@
         }
     }
 
-    private void setState(boolean enabled) {
+    private void setState(boolean enabled, boolean resetSession, boolean resetClient) {
         synchronized (mLock) {
             mEnabled = enabled;
+            if (!mEnabled || resetSession) {
+                // Reset the session state
+                resetSessionLocked();
+            }
+            if (resetClient) {
+                // Reset connection to system
+                mServiceClient = null;
+            }
         }
     }
 
@@ -1025,13 +1036,14 @@
 
         AutofillCallback callback = null;
         synchronized (mLock) {
-            if (mSessionId == sessionId) {
-                AutofillClient client = getClientLocked();
-
-                if (client != null) {
-                    if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
-                        callback = mCallback;
-                    }
+            // We do not check the session id for two reasons:
+            // 1. If local and remote session id are off sync the UI would be stuck shown
+            // 2. There is a race between the user state being destroyed due the fill
+            //    service being uninstalled and the UI being dismissed.
+            AutofillClient client = getClientLocked();
+            if (client != null) {
+                if (client.autofillCallbackRequestHideFillUi() && mCallback != null) {
+                    callback = mCallback;
                 }
             }
         }
@@ -1343,10 +1355,11 @@
         }
 
         @Override
-        public void setState(boolean enabled) {
+        public void setState(boolean enabled, boolean resetSession, boolean resetClient) {
             final AutofillManager afm = mAfm.get();
             if (afm != null) {
-                afm.mContext.getMainThreadHandler().post(() -> afm.setState(enabled));
+                afm.mContext.getMainThreadHandler().post(
+                        () -> afm.setState(enabled, resetSession, resetClient));
             }
         }
 
diff --git a/core/java/android/view/autofill/IAutoFillManagerClient.aidl b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
index 1a6bad2..aef98b7 100644
--- a/core/java/android/view/autofill/IAutoFillManagerClient.aidl
+++ b/core/java/android/view/autofill/IAutoFillManagerClient.aidl
@@ -35,7 +35,7 @@
     /**
      * Notifies the client when the autofill enabled state changed.
      */
-    void setState(boolean enabled);
+    void setState(boolean enabled, boolean resetSession, boolean resetClient);
 
     /**
       * Autofills the activity with the contents of a dataset.
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 399cfac..406386aa 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -5337,7 +5337,7 @@
             sendAfterTextChanged((Editable) text);
         } else {
             // Always notify AutoFillManager - it will return right away if autofill is disabled.
-            notifyAutoFillManagerAfterTextChanged();
+            notifyAutoFillManagerAfterTextChangedIfNeeded();
         }
 
         // SelectionModifierCursorController depends on textCanBeSelected, which depends on text
@@ -9280,12 +9280,15 @@
         }
 
         // Always notify AutoFillManager - it will return right away if autofill is disabled.
-        notifyAutoFillManagerAfterTextChanged();
+        notifyAutoFillManagerAfterTextChangedIfNeeded();
 
         hideErrorIfUnchanged();
     }
 
-    private void notifyAutoFillManagerAfterTextChanged() {
+    private void notifyAutoFillManagerAfterTextChangedIfNeeded() {
+        if (!isAutofillable()) {
+            return;
+        }
         final AutofillManager afm = mContext.getSystemService(AutofillManager.class);
         if (afm != null) {
             if (DEBUG_AUTOFILL) {
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
index d424d38..f61edc5 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerService.java
@@ -28,6 +28,7 @@
 import android.app.ActivityManager;
 import android.app.ActivityManagerInternal;
 import android.content.BroadcastReceiver;
+import android.content.ComponentName;
 import android.content.ContentResolver;
 import android.content.Context;
 import android.content.Intent;
@@ -49,6 +50,7 @@
 import android.os.UserManagerInternal;
 import android.provider.Settings;
 import android.service.autofill.FillEventHistory;
+import android.text.TextUtils;
 import android.util.LocalLog;
 import android.util.Log;
 import android.util.Slog;
@@ -60,6 +62,7 @@
 import android.view.autofill.IAutoFillManagerClient;
 
 import com.android.internal.annotations.GuardedBy;
+import com.android.internal.content.PackageMonitor;
 import com.android.internal.os.BackgroundThread;
 import com.android.internal.os.IResultReceiver;
 import com.android.internal.util.DumpUtils;
@@ -161,6 +164,84 @@
                 updateCachedServiceLocked(userId, disabledNow);
             }
         });
+        startTrackingPackageChanges();
+    }
+
+
+    private void startTrackingPackageChanges() {
+        PackageMonitor monitor = new PackageMonitor() {
+            @Override
+            public void onSomePackagesChanged() {
+                synchronized (mLock) {
+                    updateCachedServiceLocked(getChangingUserId());
+                }
+            }
+
+            @Override
+            public void onPackageUpdateFinished(String packageName, int uid) {
+                synchronized (mLock) {
+                    final String activePackageName = getActiveAutofillServicePackageName();
+                    if (packageName.equals(activePackageName)) {
+                        removeCachedServiceLocked(getChangingUserId());
+                    }
+                }
+            }
+
+            @Override
+            public void onPackageRemoved(String packageName, int uid) {
+                synchronized (mLock) {
+                    final int userId = getChangingUserId();
+                    final AutofillManagerServiceImpl userState = peekServiceForUserLocked(userId);
+                    if (userState != null) {
+                        final ComponentName componentName = userState.getServiceComponentName();
+                        if (componentName != null) {
+                            if (packageName.equals(componentName.getPackageName())) {
+                                handleActiveAutofillServiceRemoved(userId);
+                            }
+                        }
+                    }
+                }
+            }
+
+            @Override
+            public boolean onHandleForceStop(Intent intent, String[] packages,
+                    int uid, boolean doit) {
+                synchronized (mLock) {
+                    final String activePackageName = getActiveAutofillServicePackageName();
+                    for (String pkg : packages) {
+                        if (pkg.equals(activePackageName)) {
+                            if (!doit) {
+                                return true;
+                            }
+                            handleActiveAutofillServiceRemoved(getChangingUserId());
+                        }
+                    }
+                }
+                return false;
+            }
+
+            private void handleActiveAutofillServiceRemoved(int userId) {
+                removeCachedServiceLocked(userId);
+                Settings.Secure.putStringForUser(mContext.getContentResolver(),
+                        Settings.Secure.AUTOFILL_SERVICE, null, userId);
+            }
+
+            private String getActiveAutofillServicePackageName() {
+                final int userId = getChangingUserId();
+                final AutofillManagerServiceImpl userState = peekServiceForUserLocked(userId);
+                if (userState == null) {
+                    return null;
+                }
+                final ComponentName serviceComponent = userState.getServiceComponentName();
+                if (serviceComponent == null) {
+                    return null;
+                }
+                return serviceComponent.getPackageName();
+            }
+        };
+
+        // package changes
+        monitor.register(mContext, null,  UserHandle.ALL, true);
     }
 
     @Override
@@ -312,6 +393,9 @@
         AutofillManagerServiceImpl service = peekServiceForUserLocked(userId);
         if (service != null) {
             service.updateLocked(disabled);
+            if (!service.isEnabled()) {
+                removeCachedServiceLocked(userId);
+            }
         }
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index 238cdd5..ae782f0 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -53,6 +53,7 @@
 import android.util.Slog;
 import android.util.SparseArray;
 import android.view.autofill.AutofillId;
+import android.view.autofill.AutofillManager;
 import android.view.autofill.AutofillValue;
 import android.view.autofill.IAutoFillManagerClient;
 
@@ -146,11 +147,20 @@
     }
 
     String getPackageName() {
-        if (mInfo == null) {
-            return null;
+        final ComponentName serviceComponent = getServiceComponentName();
+        if (serviceComponent != null) {
+            return serviceComponent.getPackageName();
         }
-        final ComponentName serviceComponent = mInfo.getServiceInfo().getComponentName();
-        return serviceComponent.getPackageName();
+        return null;
+    }
+
+    ComponentName getServiceComponentName() {
+        synchronized (mLock) {
+            if (mInfo == null) {
+                return null;
+            }
+            return mInfo.getServiceInfo().getComponentName();
+        }
     }
 
     private String getComponentNameFromSettings() {
@@ -189,7 +199,7 @@
                         session.removeSelfLocked();
                     }
                 }
-                sendStateToClients();
+                sendStateToClients(false);
             }
         } catch (PackageManager.NameNotFoundException e) {
             Slog.e(TAG, "Bad autofill service name " + componentName + ": " + e);
@@ -240,7 +250,9 @@
         }
         final Session session = mSessions.get(sessionId);
         if (session != null && uid == session.uid) {
-            session.setHasCallback(hasIt);
+            synchronized (mLock) {
+                session.setHasCallbackLocked(hasIt);
+            }
         }
     }
 
@@ -259,14 +271,14 @@
         }
 
         final String historyItem =
-                "id=" + newSession.id + " uid=" + uid + " s=" + mInfo.getServiceInfo().packageName
+                "id=" + newSession.getId() + " uid=" + uid + " s=" + mInfo.getServiceInfo().packageName
                         + " u=" + mUserId + " i=" + autofillId + " b=" + virtualBounds + " hc=" +
                         hasCallback + " f=" + flags;
         mRequestsHistory.log(historyItem);
 
         newSession.updateLocked(autofillId, virtualBounds, value, FLAG_START_SESSION);
 
-        return newSession.id;
+        return newSession.getId();
     }
 
     void finishSessionLocked(int sessionId, int uid) {
@@ -285,7 +297,7 @@
             Log.d(TAG, "finishSessionLocked(): session finished on save? " + finished);
         }
         if (finished) {
-            session.removeSelf();
+            session.removeSelfLocked();
         }
     }
 
@@ -340,7 +352,7 @@
         final Session newSession = new Session(this, mUi, mContext, mHandlerCaller, mUserId, mLock,
                 sessionId, uid, activityToken, windowToken, appCallbackToken, hasCallback, flags,
                 mInfo.getServiceInfo().getComponentName(), packageName);
-        mSessions.put(newSession.id, newSession);
+        mSessions.put(newSession.getId(), newSession);
 
         return newSession;
     }
@@ -423,6 +435,8 @@
             mSessions.valueAt(i).destroyLocked();
         }
         mSessions.clear();
+
+        sendStateToClients(true);
     }
 
     CharSequence getServiceLabel() {
@@ -540,7 +554,7 @@
 
     void destroySessionsLocked() {
         while (mSessions.size() > 0) {
-            mSessions.valueAt(0).removeSelf();
+            mSessions.valueAt(0).removeSelfLocked();
         }
     }
 
@@ -552,7 +566,7 @@
         }
     }
 
-    private void sendStateToClients() {
+    private void sendStateToClients(boolean resetClient) {
         final RemoteCallbackList<IAutoFillManagerClient> clients;
         final int userClientCount;
         synchronized (mLock) {
@@ -566,7 +580,11 @@
             for (int i = 0; i < userClientCount; i++) {
                 final IAutoFillManagerClient client = clients.getBroadcastItem(i);
                 try {
-                    client.setState(isEnabled());
+                    final boolean resetSession;
+                    synchronized (mLock) {
+                        resetSession = resetClient || isClientSessionDestroyedLocked(client);
+                    }
+                    client.setState(isEnabled(), resetSession, resetClient);
                 } catch (RemoteException re) {
                     /* ignore */
                 }
@@ -576,7 +594,18 @@
         }
     }
 
-    private boolean isEnabled() {
+    private boolean isClientSessionDestroyedLocked(IAutoFillManagerClient client) {
+        final int sessionCount = mSessions.size();
+        for (int i = 0; i < sessionCount; i++) {
+            final Session session = mSessions.valueAt(i);
+            if (session.getClient().equals(client)) {
+                return session.isDestroyed();
+            }
+        }
+        return true;
+    }
+
+    boolean isEnabled() {
         return mInfo != null && !mDisabled;
     }
 
diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java
index ac1f34d..0687f6d 100644
--- a/services/autofill/java/com/android/server/autofill/Session.java
+++ b/services/autofill/java/com/android/server/autofill/Session.java
@@ -14,7 +14,6 @@
  * limitations under the License.
  */
 
-
 package com.android.server.autofill;
 
 import static android.service.autofill.FillRequest.FLAG_MANUAL_REQUEST;
@@ -109,7 +108,7 @@
     private static AtomicInteger sIdCounter = new AtomicInteger();
 
     /** Id of the session */
-    public final int id;
+    private final int mId;
 
     /** uid the session is for */
     public final int uid;
@@ -171,6 +170,9 @@
     @GuardedBy("mLock")
     private Bundle mClientState;
 
+    @GuardedBy("mLock")
+    private boolean mDestroyed;
+
     /**
      * Flags used to start the session.
      */
@@ -333,7 +335,7 @@
             @NonNull Object lock, int sessionId, int uid, @NonNull IBinder activityToken,
             @Nullable IBinder windowToken, @NonNull IBinder client, boolean hasCallback,
             int flags, @NonNull ComponentName componentName, @NonNull String packageName) {
-        id = sessionId;
+        mId = sessionId;
         this.uid = uid;
         mService = service;
         mLock = lock;
@@ -355,7 +357,7 @@
      *
      * @return The activity token
      */
-    public IBinder getActivityTokenLocked() {
+    IBinder getActivityTokenLocked() {
         return mActivityToken;
     }
 
@@ -367,6 +369,11 @@
      */
     void switchWindow(@NonNull IBinder newWindow) {
         synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#switchWindow() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
             mWindowToken = newWindow;
         }
     }
@@ -379,6 +386,11 @@
      */
     void switchActivity(@NonNull IBinder newActivity, @NonNull IBinder newClient) {
         synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#switchActivity() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
             mActivityToken = newActivity;
             mClient = IAutoFillManagerClient.Stub.asInterface(newClient);
 
@@ -391,6 +403,13 @@
     @Override
     public void onFillRequestSuccess(@Nullable FillResponse response, int serviceUid,
             @NonNull String servicePackageName) {
+        synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#onFillRequestSuccess() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
+        }
         if (response == null) {
             if ((mFlags & FLAG_MANUAL_REQUEST) != 0) {
                 getUiForShowing().showError(R.string.autofill_error_cannot_autofill);
@@ -430,6 +449,13 @@
     @Override
     public void onFillRequestFailure(@Nullable CharSequence message,
             @NonNull String servicePackageName) {
+        synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#onFillRequestFailure() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
+        }
         LogMaker log = (new LogMaker(MetricsEvent.AUTOFILL_REQUEST))
                 .setType(MetricsEvent.TYPE_FAILURE)
                 .setPackageName(mPackageName)
@@ -443,6 +469,13 @@
     // FillServiceCallbacks
     @Override
     public void onSaveRequestSuccess(@NonNull String servicePackageName) {
+        synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#onSaveRequestSuccess() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
+        }
         LogMaker log = (new LogMaker(
                 MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST))
                 .setType(MetricsEvent.TYPE_SUCCESS)
@@ -458,6 +491,13 @@
     @Override
     public void onSaveRequestFailure(@Nullable CharSequence message,
             @NonNull String servicePackageName) {
+        synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#onSaveRequestFailure() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
+        }
         LogMaker log = (new LogMaker(
                 MetricsEvent.AUTOFILL_DATA_SAVE_REQUEST))
                 .setType(MetricsEvent.TYPE_FAILURE)
@@ -498,6 +538,13 @@
     public void authenticate(int requestId, IntentSender intent, Bundle extras) {
         final Intent fillInIntent;
         synchronized (mLock) {
+            synchronized (mLock) {
+                if (mDestroyed) {
+                    Slog.w(TAG, "Call to Session#authenticate() rejected - session: "
+                            + mId + " destroyed");
+                    return;
+                }
+            }
             fillInIntent = createAuthFillInIntent(
                     getFillContextByRequestIdLocked(requestId).getStructure(), extras);
         }
@@ -517,19 +564,41 @@
     @Override
     public void fill(int requestId, Dataset dataset) {
         mHandlerCaller.getHandler().post(() -> autoFill(requestId, dataset));
+        synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#fill() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
+        }
+        mHandlerCaller.getHandler().post(() -> autoFill(requestId, dataset));
     }
 
     // AutoFillUiCallback
     @Override
     public void save() {
+        synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#save() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
+        }
         mHandlerCaller.getHandler()
-                .obtainMessage(AutofillManagerServiceImpl.MSG_SERVICE_SAVE, id, 0)
+                .obtainMessage(AutofillManagerServiceImpl.MSG_SERVICE_SAVE, mId, 0)
                 .sendToTarget();
     }
 
     // AutoFillUiCallback
     @Override
     public void cancelSave() {
+        synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#cancelSave() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
+        }
         mHandlerCaller.getHandler().post(() -> removeSelf());
     }
 
@@ -538,10 +607,15 @@
     public void requestShowFillUi(AutofillId id, int width, int height,
             IAutofillWindowPresenter presenter) {
         synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#requestShowFillUi() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
             if (id.equals(mCurrentViewId)) {
                 try {
                     final ViewState view = mViewStates.get(id);
-                    mClient.requestShowFillUi(this.id, mWindowToken, id, width, height,
+                    mClient.requestShowFillUi(mId, mWindowToken, id, width, height,
                             view.getVirtualBounds(), presenter);
                 } catch (RemoteException e) {
                     Slog.e(TAG, "Error requesting to show fill UI", e);
@@ -559,8 +633,10 @@
     @Override
     public void requestHideFillUi(AutofillId id) {
         synchronized (mLock) {
+            // NOTE: We allow this call in a destroyed state as the UI is
+            // asked to go away after we get destroyed, so let it do that.
             try {
-                mClient.requestHideFillUi(this.id, mWindowToken, id);
+                mClient.requestHideFillUi(mId, mWindowToken, id);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error requesting to hide fill UI", e);
             }
@@ -571,6 +647,11 @@
     @Override
     public void startIntentSender(IntentSender intentSender) {
         synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#startIntentSender() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
             removeSelfLocked();
         }
         mHandlerCaller.getHandler().post(() -> {
@@ -584,7 +665,12 @@
         });
     }
 
-    public void setAuthenticationResultLocked(Bundle data) {
+    void setAuthenticationResultLocked(Bundle data) {
+        if (mDestroyed) {
+            Slog.w(TAG, "Call to Session#setAuthenticationResultLocked() rejected - session: "
+                    + mId + " destroyed");
+            return;
+        }
         if ((mResponseWaitingAuth == null && mDatasetWaitingAuth == null) || data == null) {
             removeSelf();
         } else {
@@ -619,7 +705,12 @@
         }
     }
 
-    public void setHasCallback(boolean hasIt) {
+    void setHasCallbackLocked(boolean hasIt) {
+        if (mDestroyed) {
+            Slog.w(TAG, "Call to Session#setHasCallbackLocked() rejected - session: "
+                    + mId + " destroyed");
+            return;
+        }
         mHasCallback = hasIt;
     }
 
@@ -629,6 +720,11 @@
      * @return {@code true} if session is done, or {@code false} if it's pending user action.
      */
     public boolean showSaveLocked() {
+        if (mDestroyed) {
+            Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "
+                    + mId + " destroyed");
+            return false;
+        }
         if (mContexts == null) {
             Slog.d(TAG, "showSaveLocked(): no contexts");
             return true;
@@ -748,6 +844,12 @@
      * Calls service when user requested save.
      */
     void callSaveLocked() {
+        if (mDestroyed) {
+            Slog.w(TAG, "Call to Session#callSaveLocked() rejected - session: "
+                    + mId + " destroyed");
+            return;
+        }
+
         if (DEBUG) {
             Slog.d(TAG, "callSaveLocked(): mViewStates=" + mViewStates);
         }
@@ -844,6 +946,11 @@
     }
 
     void updateLocked(AutofillId id, Rect virtualBounds, AutofillValue value, int flags) {
+        if (mDestroyed) {
+            Slog.w(TAG, "Call to Session#updateLocked() rejected - session: "
+                    + mId + " destroyed");
+            return;
+        }
         ViewState viewState = mViewStates.get(id);
 
         if (viewState == null) {
@@ -933,6 +1040,14 @@
     @Override
     public void onFillReady(FillResponse response, AutofillId filledId,
             @Nullable AutofillValue value) {
+        synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#onFillReady() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
+        }
+
         String filterText = null;
         if (value != null && value.isText()) {
             filterText = value.getTextValue().toString();
@@ -941,15 +1056,31 @@
         getUiForShowing().showFillUi(filledId, response, filterText, mPackageName);
     }
 
-    String getFlagAsString(int flag) {
+    static String getFlagAsString(int flag) {
         return DebugUtils.flagsToString(AutofillManager.class, "FLAG_", flag);
     }
 
+    int getId() {
+        return mId;
+    }
+
+    boolean isDestroyed() {
+        synchronized (mLock) {
+            return mDestroyed;
+        }
+    }
+
+    IAutoFillManagerClient getClient() {
+        synchronized (mLock) {
+            return mClient;
+        }
+    }
+
     private void notifyUnavailableToClient() {
         synchronized (mLock) {
             if (!mHasCallback) return;
             try {
-                mClient.notifyNoFillUi(id, mWindowToken, mCurrentViewId);
+                mClient.notifyNoFillUi(mId, mWindowToken, mCurrentViewId);
             } catch (RemoteException e) {
                 Slog.e(TAG, "Error notifying client no fill UI: windowToken=" + mWindowToken
                         + " id=" + mCurrentViewId, e);
@@ -984,7 +1115,7 @@
         }
 
         try {
-            mClient.setTrackedViews(id, trackedViews, saveOnAllViewsInvisible);
+            mClient.setTrackedViews(mId, trackedViews, saveOnAllViewsInvisible);
         } catch (RemoteException e) {
             Slog.w(TAG, "Cannot set tracked ids", e);
         }
@@ -1102,6 +1233,11 @@
 
     void autoFill(int requestId, Dataset dataset) {
         synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#autoFill() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
             // Autofill it directly...
             if (dataset.getAuthentication() == null) {
                 mService.setDatasetSelected(dataset.getId());
@@ -1122,11 +1258,15 @@
     }
 
     CharSequence getServiceName() {
-        return mService.getServiceName();
+        synchronized (mLock) {
+            return mService.getServiceName();
+        }
     }
 
     FillResponse getResponseWaitingAuth() {
-        return mResponseWaitingAuth;
+        synchronized (mLock) {
+            return mResponseWaitingAuth;
+        }
     }
 
     private Intent createAuthFillInIntent(AssistStructure structure, Bundle extras) {
@@ -1141,7 +1281,7 @@
     private void startAuthentication(IntentSender intent, Intent fillInIntent) {
         try {
             synchronized (mLock) {
-                mClient.authenticate(id, intent, fillInIntent);
+                mClient.authenticate(mId, intent, fillInIntent);
             }
         } catch (RemoteException e) {
             Slog.e(TAG, "Error launching auth intent", e);
@@ -1149,7 +1289,7 @@
     }
 
     void dumpLocked(String prefix, PrintWriter pw) {
-        pw.print(prefix); pw.print("id: "); pw.println(id);
+        pw.print(prefix); pw.print("id: "); pw.println(mId);
         pw.print(prefix); pw.print("uid: "); pw.println(uid);
         pw.print(prefix); pw.print("mActivityToken: "); pw.println(mActivityToken);
         pw.print(prefix); pw.print("mFlags: "); pw.println(mFlags);
@@ -1158,6 +1298,7 @@
         pw.print(prefix); pw.print("mDatasetWaitingAuth: "); pw.println(mDatasetWaitingAuth);
         pw.print(prefix); pw.print("mCurrentViewId: "); pw.println(mCurrentViewId);
         pw.print(prefix); pw.print("mViewStates size: "); pw.println(mViewStates.size());
+        pw.print(prefix); pw.print("mDestroyed: "); pw.println(mDestroyed);
         final String prefix2 = prefix + "  ";
         for (Map.Entry<AutofillId, ViewState> entry : mViewStates.entrySet()) {
             pw.print(prefix); pw.print("State for id "); pw.println(entry.getKey());
@@ -1190,11 +1331,17 @@
 
     void autoFillApp(Dataset dataset) {
         synchronized (mLock) {
+            if (mDestroyed) {
+                Slog.w(TAG, "Call to Session#autoFillApp() rejected - session: "
+                        + mId + " destroyed");
+                return;
+            }
             try {
                 if (DEBUG) {
                     Slog.d(TAG, "autoFillApp(): the buck is on the app: " + dataset);
                 }
-                mClient.autofill(id, mWindowToken, dataset.getFieldIds(), dataset.getFieldValues());
+                mClient.autofill(mId, mWindowToken, dataset.getFieldIds(),
+                        dataset.getFieldValues());
                 setViewStatesLocked(null, dataset, ViewState.STATE_AUTOFILLED);
             } catch (RemoteException e) {
                 Slog.w(TAG, "Error autofilling activity: " + e);
@@ -1210,12 +1357,17 @@
     }
 
     void destroyLocked() {
+        if (mDestroyed) {
+            return;
+        }
         mRemoteFillService.destroy();
+        mUi.hideAll();
         mUi.setCallback(null);
+        mDestroyed = true;
         mMetricsLogger.action(MetricsEvent.AUTOFILL_SESSION_FINISHED, mPackageName);
     }
 
-    void removeSelf() {
+    private void removeSelf() {
         synchronized (mLock) {
             removeSelfLocked();
         }
@@ -1225,8 +1377,13 @@
         if (VERBOSE) {
             Slog.v(TAG, "removeSelfLocked()");
         }
+        if (mDestroyed) {
+            Slog.w(TAG, "Call to Session#removeSelfLocked() rejected - session: "
+                    + mId + " destroyed");
+            return;
+        }
         destroyLocked();
-        mService.removeSessionLocked(id);
+        mService.removeSessionLocked(mId);
     }
 
     private int getLastResponseIndex() {