Merge "Fixed cancel() and commit():"
diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
index cde3946..df76009 100644
--- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
+++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java
@@ -235,9 +235,8 @@
                 if (!hasService()) {
                     final int sessionCount = mSessions.size();
                     for (int i = sessionCount - 1; i >= 0; i--) {
-                        Session session = mSessions.valueAt(i);
-                        session.destroyLocked();
-                        mSessions.removeAt(i);
+                        final Session session = mSessions.valueAt(i);
+                        session.removeSelfLocked();
                     }
                 }
                 sendStateToClients();
@@ -328,7 +327,13 @@
             return;
         }
 
-        session.showSaveLocked();
+        final boolean finished = session.showSaveLocked();
+        if (DEBUG) {
+            Log.d(TAG, "finishSessionLocked(): session finished on save? " + finished);
+        }
+        if (finished) {
+            session.removeSelf();
+        }
     }
 
     void cancelSessionLocked(IBinder activityToken) {
@@ -341,8 +346,7 @@
             Slog.w(TAG, "cancelSessionLocked(): no session for " + activityToken);
             return;
         }
-
-        session.destroyLocked();
+        session.removeSelfLocked();
     }
 
     private Session createSessionByTokenLocked(@NonNull IBinder activityToken,
@@ -754,9 +758,7 @@
             synchronized (mLock) {
                 fillInIntent = createAuthFillInIntent(mStructure);
             }
-            mHandlerCaller.getHandler().post(() -> {
-                startAuthentication(intent, fillInIntent);
-            });
+            mHandlerCaller.getHandler().post(() -> startAuthentication(intent, fillInIntent));
         }
 
         // FillServiceCallbacks
@@ -776,8 +778,7 @@
                 Binder.restoreCallingIdentity(identity);
             }
             synchronized (mLock) {
-                destroyLocked();
-                mSessions.remove(this);
+                removeSelfLocked();
             }
         }
 
@@ -790,9 +791,7 @@
         // AutoFillUiCallback
         @Override
         public void fill(Dataset dataset) {
-            mHandlerCaller.getHandler().post(() -> {
-                autoFill(dataset);
-            });
+            mHandlerCaller.getHandler().post(() -> autoFill(dataset));
         }
 
         // AutoFillUiCallback
@@ -805,17 +804,13 @@
         // AutoFillUiCallback
         @Override
         public void cancelSave() {
-            mHandlerCaller.getHandler().post(() -> {
-                removeSelf();
-            });
+            mHandlerCaller.getHandler().post(() -> removeSelf());
         }
 
         // AutoFillUiCallback
         @Override
         public void onEvent(AutofillId id, int event) {
-            mHandlerCaller.getHandler().post(() -> {
-                notifyChangeToClient(id, event);
-            });
+            mHandlerCaller.getHandler().post(() -> notifyChangeToClient(id, event));
         }
 
         public void setAuthenticationResultLocked(Bundle data) {
@@ -845,12 +840,14 @@
         }
 
         /**
-         * Show the save UI, when session can be saved.
+         * Shows the save UI, when session can be saved.
+         *
+         * @return {@code true} if session is done, or {@code false} if it's pending user action.
          */
-        public void showSaveLocked() {
+        public boolean showSaveLocked() {
             if (mStructure == null) {
                 Slog.wtf(TAG, "showSaveLocked(): no mStructure");
-                return;
+                return true;
             }
             if (mCurrentResponse == null) {
                 // Happens when the activity / session was finished before the service replied, or
@@ -858,7 +855,7 @@
                 if (DEBUG) {
                     Slog.d(TAG, "showSaveLocked(): no mCurrentResponse");
                 }
-                return;
+                return true;
             }
             final SaveInfo saveInfo = mCurrentResponse.getSaveInfo();
             if (DEBUG) {
@@ -874,13 +871,13 @@
              */
 
             if (saveInfo == null) {
-                return;
+                return true;
             }
 
             final AutofillId[] requiredIds = saveInfo.getRequiredIds();
             if (requiredIds == null || requiredIds.length == 0) {
                 Slog.w(TAG, "showSaveLocked(): no required ids on saveInfo");
-                return;
+                return true;
             }
 
             boolean allRequiredAreNotEmpty = true;
@@ -950,7 +947,7 @@
                     getUiForShowing().showSaveUi(
                             mInfo.getServiceInfo().loadLabel(mContext.getPackageManager()),
                             saveInfo, mPackageName);
-                    return;
+                    return false;
                 }
             }
             // Nothing changed...
@@ -959,7 +956,7 @@
                         + "allRequiredAreNotNull=" + allRequiredAreNotEmpty
                         + ", atLeastOneChanged=" + atLeastOneChanged);
             }
-            removeSelf();
+            return true;
         }
 
         /**
@@ -1001,7 +998,7 @@
                 mStructure.dump();
             }
 
-            mRemoteFillService.onSaveRequest(mStructure, extras);
+            mRemoteFillService.onSaveRequest(mStructure, extras, () -> removeSelf());
         }
 
         void updateLocked(AutofillId id, Rect bounds, AutofillValue value, int flags) {
@@ -1255,15 +1252,18 @@
                     mPackageName);
         }
 
-        private void removeSelf() {
-            if (VERBOSE) {
-                Slog.v(TAG, "removeSelf()");
-            }
-
+        void removeSelf() {
             synchronized (mLock) {
-                destroyLocked();
-                mSessions.remove(mActivityToken);
+                removeSelfLocked();
             }
         }
+
+        private void removeSelfLocked() {
+            if (VERBOSE) {
+                Slog.v(TAG, "removeSelfLocked()");
+            }
+            destroyLocked();
+            mSessions.remove(mActivityToken);
+        }
     }
 }
diff --git a/services/autofill/java/com/android/server/autofill/RemoteFillService.java b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
index c41ac05..d1c8b4f8 100644
--- a/services/autofill/java/com/android/server/autofill/RemoteFillService.java
+++ b/services/autofill/java/com/android/server/autofill/RemoteFillService.java
@@ -16,6 +16,8 @@
 
 package com.android.server.autofill;
 
+import static com.android.server.autofill.Helper.DEBUG;
+
 import android.annotation.NonNull;
 import android.annotation.Nullable;
 import android.app.assist.AssistStructure;
@@ -38,8 +40,10 @@
 import android.service.autofill.ISaveCallback;
 import android.text.format.DateUtils;
 import android.util.Slog;
+
 import com.android.internal.os.HandlerCaller;
 import com.android.server.FgThread;
+import com.android.server.autofill.AutofillManagerServiceImpl.Session;
 
 import java.io.PrintWriter;
 import java.lang.ref.WeakReference;
@@ -55,8 +59,6 @@
 final class RemoteFillService implements DeathRecipient {
     private static final String LOG_TAG = "RemoteFillService";
 
-    private static final boolean DEBUG = Helper.DEBUG;
-
     // How long after the last interaction with the service we would unbind
     private static final long TIMEOUT_IDLE_BIND_MILLIS = 5 * DateUtils.SECOND_IN_MILLIS;
 
@@ -139,9 +141,10 @@
         mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
     }
 
-    public void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras) {
+    public void onSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras,
+            @Nullable Runnable finalizer) {
         cancelScheduledUnbind();
-        final PendingSaveRequest request = new PendingSaveRequest(structure, extras, this);
+        final PendingSaveRequest request = new PendingSaveRequest(structure, extras, this, finalizer);
         mHandler.obtainMessageO(MyHandler.MSG_ON_PENDING_REQUEST, request).sendToTarget();
     }
 
@@ -414,8 +417,8 @@
     private static final class PendingFillRequest extends PendingRequest {
         private final Object mLock = new Object();
         private final WeakReference<RemoteFillService> mWeakService;
-        private AssistStructure mStructure;
-        private Bundle mExtras;
+        private final AssistStructure mStructure;
+        private final Bundle mExtras;
         private final IFillCallback mCallback;
         private ICancellationSignal mCancellation;
         private boolean mCancelled;
@@ -473,10 +476,6 @@
                 try {
                     remoteService.mAutoFillService.onFillRequest(mStructure,
                             mExtras, mCallback, mFlags);
-                    synchronized (mLock) {
-                        mStructure = null;
-                        mExtras = null;
-                    }
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Error calling on fill request", e);
                     cancel();
@@ -506,17 +505,18 @@
     }
 
     private static final class PendingSaveRequest extends PendingRequest {
-        private final Object mLock = new Object();
         private final WeakReference<RemoteFillService> mWeakService;
-        private AssistStructure mStructure;
-        private Bundle mExtras;
+        private final AssistStructure mStructure;
+        private final Bundle mExtras;
         private final ISaveCallback mCallback;
+        private final Runnable mFinalizer;
 
-        public PendingSaveRequest(@NonNull AssistStructure structure,
-                @Nullable Bundle extras, @NonNull RemoteFillService service) {
+        public PendingSaveRequest(@NonNull AssistStructure structure, @Nullable Bundle extras,
+                @NonNull RemoteFillService service, @Nullable Runnable finalizer) {
             mStructure = structure;
             mExtras = extras;
             mWeakService = new WeakReference<>(service);
+            mFinalizer = finalizer;
             mCallback = new ISaveCallback.Stub() {
                 @Override
                 public void onSuccess() {
@@ -540,19 +540,17 @@
 
         @Override
         public void run() {
-            RemoteFillService service = mWeakService.get();
+            final RemoteFillService service = mWeakService.get();
             if (service != null) {
                 try {
-                    service.mAutoFillService.onSaveRequest(mStructure,
-                            mExtras, mCallback);
-                    synchronized (mLock) {
-                        mStructure = null;
-                        mExtras = null;
-                    }
+                    service.mAutoFillService.onSaveRequest(mStructure, mExtras, mCallback);
                 } catch (RemoteException e) {
                     Slog.e(LOG_TAG, "Error calling on save request", e);
                 }
             }
+            if (mFinalizer != null) {
+              mFinalizer.run();
+            }
         }
 
         @Override
diff --git a/services/autofill/java/com/android/server/autofill/ui/Helper.java b/services/autofill/java/com/android/server/autofill/ui/Helper.java
new file mode 100644
index 0000000..996e421
--- /dev/null
+++ b/services/autofill/java/com/android/server/autofill/ui/Helper.java
@@ -0,0 +1,26 @@
+/*
+ * Copyright (C) 2017 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.autofill.ui;
+
+final class Helper {
+
+    static final boolean DEBUG = true; // TODO(b/33197203): set to false when stable
+    static final boolean VERBOSE = false;
+    private Helper() {
+        throw new UnsupportedOperationException("contains static members only");
+    }
+}
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index 644abe6..509351b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -16,6 +16,8 @@
 
 package com.android.server.autofill.ui;
 
+import static com.android.server.autofill.ui.Helper.DEBUG;
+
 import android.annotation.NonNull;
 import android.app.Dialog;
 import android.content.Context;
@@ -23,6 +25,7 @@
 import android.os.Handler;
 import android.service.autofill.SaveInfo;
 import android.text.format.DateUtils;
+import android.util.Slog;
 import android.view.Gravity;
 import android.view.Window;
 import android.view.WindowManager;
@@ -37,23 +40,66 @@
  * Autofill Save Prompt
  */
 final class SaveUi {
+
+    private static final String TAG = "SaveUi";
+
     public interface OnSaveListener {
         void onSave();
         void onCancel(IntentSender listener);
         void onDestroy();
     }
 
+    private class OneTimeListener implements OnSaveListener {
+
+        private final OnSaveListener mRealListener;
+        private boolean mDone;
+
+        OneTimeListener(OnSaveListener realListener) {
+            mRealListener = realListener;
+        }
+
+        @Override
+        public void onSave() {
+            if (DEBUG) Slog.d(TAG, "onSave(): " + mDone);
+            if (mDone) {
+                return;
+            }
+            mDone = true;
+            mRealListener.onSave();
+        }
+
+        @Override
+        public void onCancel(IntentSender listener) {
+            if (DEBUG) Slog.d(TAG, "onCancel(): " + mDone);
+            if (mDone) {
+                return;
+            }
+            mDone = true;
+            mRealListener.onCancel(listener);
+        }
+
+        @Override
+        public void onDestroy() {
+            if (DEBUG) Slog.d(TAG, "onDestroy(): " + mDone);
+            if (mDone) {
+                return;
+            }
+            mDone = true;
+            mRealListener.onDestroy();
+        }
+    }
+
     private final Handler mHandler = UiThread.getHandler();
 
     private final @NonNull Dialog mDialog;
 
-    private final @NonNull OnSaveListener mListener;
+    private final @NonNull OneTimeListener mListener;
 
     private boolean mDestroyed;
 
     SaveUi(@NonNull Context context, @NonNull CharSequence providerLabel, @NonNull SaveInfo info,
             @NonNull OnSaveListener listener, int lifeTimeMs) {
-        mListener = listener;
+        mListener = new OneTimeListener(listener);
 
         final LayoutInflater inflater = LayoutInflater.from(context);
         final View view = inflater.inflate(R.layout.autofill_save, null);
@@ -118,7 +164,12 @@
 
         mDialog.show();
 
-        mHandler.postDelayed(() -> mListener.onCancel(null), lifeTimeMs);
+        mHandler.postDelayed(() -> {
+            if (!mListener.mDone) {
+                mListener.onCancel(null);
+                Slog.d(TAG, "Save snackbar timed out after " + lifeTimeMs + "ms");
+            }
+        }, lifeTimeMs);
     }
 
     void destroy() {