diff options
| -rw-r--r-- | api/current.txt | 8 | ||||
| -rw-r--r-- | core/java/android/service/autofill/FillEventHistory.java | 100 | ||||
| -rw-r--r-- | non-updatable-api/current.txt | 8 | ||||
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java | 23 | ||||
| -rw-r--r-- | services/autofill/java/com/android/server/autofill/Session.java | 194 | 
5 files changed, 295 insertions, 38 deletions
diff --git a/api/current.txt b/api/current.txt index 302c30848fbe..129465ab5731 100644 --- a/api/current.txt +++ b/api/current.txt @@ -43297,8 +43297,16 @@ package android.service.autofill {      method @NonNull public java.util.Map<android.view.autofill.AutofillId,android.service.autofill.FieldClassification> getFieldsClassification();      method @NonNull public java.util.Set<java.lang.String> getIgnoredDatasetIds();      method @NonNull public java.util.Map<android.view.autofill.AutofillId,java.util.Set<java.lang.String>> getManuallyEnteredField(); +    method public int getNoSaveReason();      method @NonNull public java.util.Set<java.lang.String> getSelectedDatasetIds();      method public int getType(); +    field public static final int NO_SAVE_REASON_DATASET_MATCH = 6; // 0x6 +    field public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5; // 0x5 +    field public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3; // 0x3 +    field public static final int NO_SAVE_REASON_NONE = 0; // 0x0 +    field public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1; // 0x1 +    field public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4; // 0x4 +    field public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2; // 0x2      field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2      field public static final int TYPE_CONTEXT_COMMITTED = 4; // 0x4      field public static final int TYPE_DATASETS_SHOWN = 5; // 0x5 diff --git a/core/java/android/service/autofill/FillEventHistory.java b/core/java/android/service/autofill/FillEventHistory.java index 1cd2d62ce55f..f2265281b140 100644 --- a/core/java/android/service/autofill/FillEventHistory.java +++ b/core/java/android/service/autofill/FillEventHistory.java @@ -159,6 +159,7 @@ public final class FillEventHistory implements Parcelable {                      FieldClassification.writeArrayToParcel(parcel,                              event.mDetectedFieldClassifications);                  } +                parcel.writeInt(event.mSaveDialogNotShowReason);              }          }      } @@ -243,6 +244,40 @@ public final class FillEventHistory implements Parcelable {          @Retention(RetentionPolicy.SOURCE)          @interface EventIds{} +        /** No reason for save dialog. */ +        public static final int NO_SAVE_REASON_NONE = 0; + +        /** The SaveInfo associated with the FillResponse is null. */ +        public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1; + +        /** The service asked to delay save. */ +        public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2; + +        /** There was empty value for required ids. */ +        public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3; + +        /** No value has been changed. */ +        public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4; + +        /** Fields failed validation. */ +        public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5; + +        /** All fields matched contents of datasets. */ +        public static final int NO_SAVE_REASON_DATASET_MATCH = 6; + +        /** @hide */ +        @IntDef(prefix = { "NO_SAVE_REASON_" }, value = { +                NO_SAVE_REASON_NONE, +                NO_SAVE_REASON_NO_SAVE_INFO, +                NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG, +                NO_SAVE_REASON_HAS_EMPTY_REQUIRED, +                NO_SAVE_REASON_NO_VALUE_CHANGED, +                NO_SAVE_REASON_FIELD_VALIDATION_FAILED, +                NO_SAVE_REASON_DATASET_MATCH +        }) +        @Retention(RetentionPolicy.SOURCE) +        public @interface NoSaveReason{} +          @EventIds private final int mEventType;          @Nullable private final String mDatasetId;          @Nullable private final Bundle mClientState; @@ -261,6 +296,8 @@ public final class FillEventHistory implements Parcelable {          @Nullable private final AutofillId[] mDetectedFieldIds;          @Nullable private final FieldClassification[] mDetectedFieldClassifications; +        @NoSaveReason private final int mSaveDialogNotShowReason; +          /**           * Returns the type of the event.           * @@ -448,6 +485,18 @@ public final class FillEventHistory implements Parcelable {          }          /** +         * Returns the reason why a save dialog was not shown. +         * +         * <p><b>Note: </b>Only set on events of type {@link #TYPE_CONTEXT_COMMITTED}. For the other +         * event types, the reason is set to NO_SAVE_REASON_NONE. +         * +         * @return The reason why a save dialog was not shown. +         */ +        public int getNoSaveReason() { +            return mSaveDialogNotShowReason; +        } + +        /**           * Creates a new event.           *           * @param eventType The type of the event @@ -481,6 +530,48 @@ public final class FillEventHistory implements Parcelable {                  @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,                  @Nullable AutofillId[] detectedFieldIds,                  @Nullable FieldClassification[] detectedFieldClassifications) { +            this(eventType, datasetId, clientState, selectedDatasetIds, ignoredDatasetIds, +                    changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, +                    manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, +                    NO_SAVE_REASON_NONE); +        } + +        /** +         * Creates a new event. +         * +         * @param eventType The type of the event +         * @param datasetId The dataset the event was on, or {@code null} if the event was on the +         *                  whole response. +         * @param clientState The client state associated with the event. +         * @param selectedDatasetIds The ids of datasets selected by the user. +         * @param ignoredDatasetIds The ids of datasets NOT select by the user. +         * @param changedFieldIds The ids of fields changed by the user. +         * @param changedDatasetIds The ids of the datasets that havd values matching the +         * respective entry on {@code changedFieldIds}. +         * @param manuallyFilledFieldIds The ids of fields that were manually entered by the user +         * and belonged to datasets. +         * @param manuallyFilledDatasetIds The ids of datasets that had values matching the +         * respective entry on {@code manuallyFilledFieldIds}. +         * @param detectedFieldClassifications the field classification matches. +         * @param saveDialogNotShowReason The reason why a save dialog was not shown. +         * +         * @throws IllegalArgumentException If the length of {@code changedFieldIds} and +         * {@code changedDatasetIds} doesn't match. +         * @throws IllegalArgumentException If the length of {@code manuallyFilledFieldIds} and +         * {@code manuallyFilledDatasetIds} doesn't match. +         * +         * @hide +         */ +        public Event(int eventType, @Nullable String datasetId, @Nullable Bundle clientState, +                @Nullable List<String> selectedDatasetIds, +                @Nullable ArraySet<String> ignoredDatasetIds, +                @Nullable ArrayList<AutofillId> changedFieldIds, +                @Nullable ArrayList<String> changedDatasetIds, +                @Nullable ArrayList<AutofillId> manuallyFilledFieldIds, +                @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds, +                @Nullable AutofillId[] detectedFieldIds, +                @Nullable FieldClassification[] detectedFieldClassifications, +                int saveDialogNotShowReason) {              mEventType = Preconditions.checkArgumentInRange(eventType, 0, TYPE_DATASETS_SHOWN,                      "eventType");              mDatasetId = datasetId; @@ -506,6 +597,10 @@ public final class FillEventHistory implements Parcelable {              mDetectedFieldIds = detectedFieldIds;              mDetectedFieldClassifications = detectedFieldClassifications; + +            mSaveDialogNotShowReason = Preconditions.checkArgumentInRange(saveDialogNotShowReason, +                    NO_SAVE_REASON_NONE, NO_SAVE_REASON_DATASET_MATCH, +                    "saveDialogNotShowReason");          }          @Override @@ -521,6 +616,7 @@ public final class FillEventHistory implements Parcelable {                      + ", detectedFieldIds=" + Arrays.toString(mDetectedFieldIds)                      + ", detectedFieldClassifications ="                          + Arrays.toString(mDetectedFieldClassifications) +                    + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason                      + "]";          }      } @@ -562,12 +658,14 @@ public final class FillEventHistory implements Parcelable {                                  (detectedFieldIds != null)                                  ? FieldClassification.readArrayFromParcel(parcel)                                  : null; +                        final int saveDialogNotShowReason = parcel.readInt();                          selection.addEvent(new Event(eventType, datasetId, clientState,                                  selectedDatasetIds, ignoredDatasets,                                  changedFieldIds, changedDatasetIds,                                  manuallyFilledFieldIds, manuallyFilledDatasetIds, -                                detectedFieldIds, detectedFieldClassifications)); +                                detectedFieldIds, detectedFieldClassifications, +                                saveDialogNotShowReason));                      }                      return selection;                  } diff --git a/non-updatable-api/current.txt b/non-updatable-api/current.txt index ab5b8229a5b6..bf2f613d895f 100644 --- a/non-updatable-api/current.txt +++ b/non-updatable-api/current.txt @@ -41431,8 +41431,16 @@ package android.service.autofill {      method @NonNull public java.util.Map<android.view.autofill.AutofillId,android.service.autofill.FieldClassification> getFieldsClassification();      method @NonNull public java.util.Set<java.lang.String> getIgnoredDatasetIds();      method @NonNull public java.util.Map<android.view.autofill.AutofillId,java.util.Set<java.lang.String>> getManuallyEnteredField(); +    method public int getNoSaveReason();      method @NonNull public java.util.Set<java.lang.String> getSelectedDatasetIds();      method public int getType(); +    field public static final int NO_SAVE_REASON_DATASET_MATCH = 6; // 0x6 +    field public static final int NO_SAVE_REASON_FIELD_VALIDATION_FAILED = 5; // 0x5 +    field public static final int NO_SAVE_REASON_HAS_EMPTY_REQUIRED = 3; // 0x3 +    field public static final int NO_SAVE_REASON_NONE = 0; // 0x0 +    field public static final int NO_SAVE_REASON_NO_SAVE_INFO = 1; // 0x1 +    field public static final int NO_SAVE_REASON_NO_VALUE_CHANGED = 4; // 0x4 +    field public static final int NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG = 2; // 0x2      field public static final int TYPE_AUTHENTICATION_SELECTED = 2; // 0x2      field public static final int TYPE_CONTEXT_COMMITTED = 4; // 0x4      field public static final int TYPE_DATASETS_SHOWN = 5; // 0x5 diff --git a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java index d59c955de889..c563b6c11d86 100644 --- a/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java +++ b/services/autofill/java/com/android/server/autofill/AutofillManagerServiceImpl.java @@ -55,6 +55,7 @@ import android.service.autofill.FieldClassification;  import android.service.autofill.FieldClassification.Match;  import android.service.autofill.FillEventHistory;  import android.service.autofill.FillEventHistory.Event; +import android.service.autofill.FillEventHistory.Event.NoSaveReason;  import android.service.autofill.FillResponse;  import android.service.autofill.IAutoFillService;  import android.service.autofill.InlineSuggestionRenderService; @@ -429,9 +430,15 @@ final class AutofillManagerServiceImpl              return;          } -        session.logContextCommitted(); +        final Session.SaveResult saveResult = session.showSaveLocked(); -        final boolean finished = session.showSaveLocked(); +        session.logContextCommitted(saveResult.getNoSaveReason()); + +        if (saveResult.isLogSaveShown()) { +            session.logSaveUiShown(); +        } + +        final boolean finished = saveResult.isRemoveSession();          if (sVerbose) Slog.v(TAG, "finishSessionLocked(): session finished on save? " + finished);          if (finished) { @@ -868,7 +875,9 @@ final class AutofillManagerServiceImpl              @NonNull ComponentName appComponentName, boolean compatMode) {          logContextCommittedLocked(sessionId, clientState, selectedDatasets, ignoredDatasets,                  changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, -                manuallyFilledDatasetIds, null, null, appComponentName, compatMode); +                manuallyFilledDatasetIds, /* detectedFieldIdsList= */ null, +                /* detectedFieldClassificationsList= */ null, appComponentName, compatMode, +                Event.NO_SAVE_REASON_NONE);      }      @GuardedBy("mLock") @@ -881,7 +890,8 @@ final class AutofillManagerServiceImpl              @Nullable ArrayList<ArrayList<String>> manuallyFilledDatasetIds,              @Nullable ArrayList<AutofillId> detectedFieldIdsList,              @Nullable ArrayList<FieldClassification> detectedFieldClassificationsList, -            @NonNull ComponentName appComponentName, boolean compatMode) { +            @NonNull ComponentName appComponentName, boolean compatMode, +            @NoSaveReason int saveDialogNotShowReason) {          if (isValidEventLocked("logDatasetNotSelected()", sessionId)) {              if (sVerbose) {                  Slog.v(TAG, "logContextCommitted() with FieldClassification: id=" + sessionId @@ -893,7 +903,8 @@ final class AutofillManagerServiceImpl                          + ", detectedFieldIds=" + detectedFieldIdsList                          + ", detectedFieldClassifications=" + detectedFieldClassificationsList                          + ", appComponentName=" + appComponentName.toShortString() -                        + ", compatMode=" + compatMode); +                        + ", compatMode=" + compatMode +                        + ", saveDialogNotShowReason=" + saveDialogNotShowReason);              }              AutofillId[] detectedFieldsIds = null;              FieldClassification[] detectedFieldClassifications = null; @@ -929,7 +940,7 @@ final class AutofillManagerServiceImpl                      clientState, selectedDatasets, ignoredDatasets,                      changedFieldIds, changedDatasetIds,                      manuallyFilledFieldIds, manuallyFilledDatasetIds, -                    detectedFieldsIds, detectedFieldClassifications)); +                    detectedFieldsIds, detectedFieldClassifications, saveDialogNotShowReason));          }      } diff --git a/services/autofill/java/com/android/server/autofill/Session.java b/services/autofill/java/com/android/server/autofill/Session.java index 4d2e4f6ee486..6c9d35cb2fef 100644 --- a/services/autofill/java/com/android/server/autofill/Session.java +++ b/services/autofill/java/com/android/server/autofill/Session.java @@ -71,6 +71,8 @@ import android.service.autofill.FieldClassification;  import android.service.autofill.FieldClassification.Match;  import android.service.autofill.FieldClassificationUserData;  import android.service.autofill.FillContext; +import android.service.autofill.FillEventHistory.Event; +import android.service.autofill.FillEventHistory.Event.NoSaveReason;  import android.service.autofill.FillRequest;  import android.service.autofill.FillResponse;  import android.service.autofill.InlinePresentation; @@ -1596,10 +1598,22 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState       * when necessary.       */      public void logContextCommitted() { -        mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this)); +        mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, +                Event.NO_SAVE_REASON_NONE));      } -    private void handleLogContextCommitted() { +    /** +     * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_CONTEXT_COMMITTED} +     * when necessary. +     * +     * @param saveDialogNotShowReason The reason why a save dialog was not shown. +     */ +    public void logContextCommitted(@NoSaveReason int saveDialogNotShowReason) { +        mHandler.sendMessage(obtainMessage(Session::handleLogContextCommitted, this, +                saveDialogNotShowReason)); +    } + +    private void handleLogContextCommitted(@NoSaveReason int saveDialogNotShowReason) {          final FillResponse lastResponse;          synchronized (mLock) {              lastResponse = getLastResponseLocked("logContextCommited(%s)"); @@ -1629,22 +1643,25 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState          // Sets field classification scores          if (userData != null && fcStrategy != null) { -            logFieldClassificationScore(fcStrategy, userData); +            logFieldClassificationScore(fcStrategy, userData, saveDialogNotShowReason);          } else { -            logContextCommitted(null, null); +            logContextCommitted(null, null, saveDialogNotShowReason);          }      }      private void logContextCommitted(@Nullable ArrayList<AutofillId> detectedFieldIds, -            @Nullable ArrayList<FieldClassification> detectedFieldClassifications) { +            @Nullable ArrayList<FieldClassification> detectedFieldClassifications, +            @NoSaveReason int saveDialogNotShowReason) {          synchronized (mLock) { -            logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications); +            logContextCommittedLocked(detectedFieldIds, detectedFieldClassifications, +                    saveDialogNotShowReason);          }      }      @GuardedBy("mLock")      private void logContextCommittedLocked(@Nullable ArrayList<AutofillId> detectedFieldIds, -            @Nullable ArrayList<FieldClassification> detectedFieldClassifications) { +            @Nullable ArrayList<FieldClassification> detectedFieldClassifications, +            @NoSaveReason int saveDialogNotShowReason) {          final FillResponse lastResponse = getLastResponseLocked("logContextCommited(%s)");          if (lastResponse == null) return; @@ -1822,10 +1839,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState              }          } -        mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, -                ignoredDatasets, changedFieldIds, changedDatasetIds, -                manuallyFilledFieldIds, manuallyFilledDatasetIds, detectedFieldIds, -                detectedFieldClassifications, mComponentName, mCompatMode); +        mService.logContextCommittedLocked(id, mClientState, mSelectedDatasetIds, ignoredDatasets, +                changedFieldIds, changedDatasetIds, manuallyFilledFieldIds, +                manuallyFilledDatasetIds, detectedFieldIds, detectedFieldClassifications, +                mComponentName, mCompatMode, saveDialogNotShowReason);      }      /** @@ -1833,7 +1850,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState       * {@code fieldId} based on its {@code currentValue} and {@code userData}.       */      private void logFieldClassificationScore(@NonNull FieldClassificationStrategy fcStrategy, -            @NonNull FieldClassificationUserData userData) { +            @NonNull FieldClassificationUserData userData, +            @NoSaveReason int saveDialogNotShowReason) {          final String[] userValues = userData.getValues();          final String[] categoryIds = userData.getCategoryIds(); @@ -1879,7 +1897,7 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState          final RemoteCallback callback = new RemoteCallback((result) -> {              if (result == null) {                  if (sDebug) Slog.d(TAG, "setFieldClassificationScore(): no results"); -                logContextCommitted(null, null); +                logContextCommitted(null, null, saveDialogNotShowReason);                  return;              }              final Scores scores = result.getParcelable(EXTRA_SCORES); @@ -1940,7 +1958,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                  return;              } -            logContextCommitted(detectedFieldIds, detectedFieldClassifications); +            logContextCommitted(detectedFieldIds, detectedFieldClassifications, +                    saveDialogNotShowReason);          });          fcStrategy.calculateScores(callback, currentValues, userValues, categoryIds, @@ -1948,17 +1967,28 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState      }      /** +     * Generates a {@link android.service.autofill.FillEventHistory.Event#TYPE_SAVE_SHOWN} +     * when necessary. +     * +     * <p>Note: It is necessary to call logContextCommitted() first before calling this method. +     */ +    public void logSaveUiShown() { +        mHandler.sendMessage(obtainMessage(Session::logSaveShown, this)); +    } + +    /**       * Shows the save UI, when session can be saved.       * -     * @return {@code true} if session is done and could be removed, or {@code false} if it's -     * pending user action or the service asked to keep it alive (for multi-screens workflow). +     * @return {@link SaveResult} that contains the save ui display status information.       */      @GuardedBy("mLock") -    public boolean showSaveLocked() { +    @NonNull +    public SaveResult showSaveLocked() {          if (mDestroyed) {              Slog.w(TAG, "Call to Session#showSaveLocked() rejected - session: "                      + id + " destroyed"); -            return false; +            return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false, +                    Event.NO_SAVE_REASON_NONE);          }          final FillResponse response = getLastResponseLocked("showSaveLocked(%s)");          final SaveInfo saveInfo = response == null ? null : response.getSaveInfo(); @@ -1975,13 +2005,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState           */          if (saveInfo == null) {              if (sVerbose) Slog.v(TAG, "showSaveLocked(" + this.id + "): no saveInfo from service"); -            return true; +            return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, +                    Event.NO_SAVE_REASON_NO_SAVE_INFO);          }          if ((saveInfo.getFlags() & SaveInfo.FLAG_DELAY_SAVE) != 0) {              // TODO(b/113281366): log metrics              if (sDebug) Slog.v(TAG, "showSaveLocked(" + this.id + "): service asked to delay save"); -            return false; +            return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ false, +                    Event.NO_SAVE_REASON_WITH_DELAY_SAVE_FLAG);          }          final ArrayMap<AutofillId, InternalSanitizer> sanitizers = createSanitizers(saveInfo); @@ -2073,7 +2105,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState              Slog.v(TAG, "allRequiredAreNotEmpty: " + allRequiredAreNotEmpty + " hasOptional: "                      + (optionalIds != null));          } -        if (allRequiredAreNotEmpty) { +        int saveDialogNotShowReason; +        if (!allRequiredAreNotEmpty) { +            saveDialogNotShowReason = Event.NO_SAVE_REASON_HAS_EMPTY_REQUIRED; +        } else {              // Must look up all optional ids in 2 scenarios:              // - if no required id changed but an optional id did, it should trigger save / update              // - if at least one required id changed but it was not part of a filled dataset, we @@ -2124,7 +2159,9 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                      }                  }              } -            if (atLeastOneChanged) { +            if (!atLeastOneChanged) { +                saveDialogNotShowReason = Event.NO_SAVE_REASON_NO_VALUE_CHANGED; +            } else {                  if (sDebug) {                      Slog.d(TAG, "at least one field changed, validate fields for save UI");                  } @@ -2142,13 +2179,15 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                          Slog.e(TAG, "Not showing save UI because validation failed:", e);                          log.setType(MetricsEvent.TYPE_FAILURE);                          mMetricsLogger.write(log); -                        return true; +                        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, +                                Event.NO_SAVE_REASON_FIELD_VALIDATION_FAILED);                      }                      mMetricsLogger.write(log);                      if (!isValid) {                          Slog.i(TAG, "not showing save UI because fields failed validation"); -                        return true; +                        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, +                                Event.NO_SAVE_REASON_FIELD_VALIDATION_FAILED);                      }                  } @@ -2187,7 +2226,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                              Slog.d(TAG, "ignoring Save UI because all fields match contents of "                                      + "dataset #" + i + ": " + dataset);                          } -                        return true; +                        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, +                                Event.NO_SAVE_REASON_DATASET_MATCH);                      }                  } @@ -2196,9 +2236,6 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                              + id + "!");                  } -                // Use handler so logContextCommitted() is logged first -                mHandler.sendMessage(obtainMessage(Session::logSaveShown, this)); -                  final IAutoFillManagerClient client = getClient();                  mPendingSaveUi = new PendingUi(new Binder(), id, client); @@ -2210,8 +2247,10 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                  }                  if (serviceLabel == null || serviceIcon == null) {                      wtf(null, "showSaveLocked(): no service label or icon"); -                    return true; +                    return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, +                            Event.NO_SAVE_REASON_NONE);                  } +                  getUiForShowing().showSaveUi(serviceLabel, serviceIcon,                          mService.getServicePackageName(), saveInfo, this,                          mComponentName, this, mPendingSaveUi, isUpdate, mCompatMode); @@ -2223,7 +2262,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                      }                  }                  mIsSaving = true; -                return false; +                return new SaveResult(/* logSaveShown= */ true, /* removeSession= */ false, +                        Event.NO_SAVE_REASON_NONE);              }          }          // Nothing changed... @@ -2232,7 +2272,8 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState                      + "allRequiredAreNotNull=" + allRequiredAreNotEmpty                      + ", atLeastOneChanged=" + atLeastOneChanged);          } -        return true; +        return new SaveResult(/* logSaveShown= */ false, /* removeSession= */ true, +                saveDialogNotShowReason);      }      private void logSaveShown() { @@ -3608,6 +3649,97 @@ final class Session implements RemoteFillService.FillServiceCallbacks, ViewState          }      } +    /** +     * The result of checking whether to show the save dialog, when session can be saved. +     * +     * @hide +     */ +    static final class SaveResult { +        /** +         * Whether to record the save dialog has been shown. +         */ +        private boolean mLogSaveShown; + +        /** +         * Whether to remove the session. +         */ +        private boolean mRemoveSession; + +        /** +         * The reason why a save dialog was not shown. +         */ +        @NoSaveReason private int mSaveDialogNotShowReason; + +        SaveResult(boolean logSaveShown, boolean removeSession, +                @NoSaveReason int saveDialogNotShowReason) { +            mLogSaveShown = logSaveShown; +            mRemoveSession = removeSession; +            mSaveDialogNotShowReason = saveDialogNotShowReason; +        } + +        /** +         * Returns whether to record the save dialog has been shown. +         * +         * @return Whether to record the save dialog has been shown. +         */ +        public boolean isLogSaveShown() { +            return mLogSaveShown; +        } + +        /** +         * Sets whether to record the save dialog has been shown. +         * +         * @param logSaveShown Whether to record the save dialog has been shown. +         */ +        public void setLogSaveShown(boolean logSaveShown) { +            mLogSaveShown = logSaveShown; +        } + +        /** +         * Returns whether to remove the session. +         * +         * @return Whether to remove the session. +         */ +        public boolean isRemoveSession() { +            return mRemoveSession; +        } + +        /** +         * Sets whether to remove the session. +         * +         * @param removeSession Whether to remove the session. +         */ +        public void setRemoveSession(boolean removeSession) { +            mRemoveSession = removeSession; +        } + +        /** +         * Returns the reason why a save dialog was not shown. +         * +         * @return The reason why a save dialog was not shown. +         */ +        @NoSaveReason +        public int getNoSaveReason() { +            return mSaveDialogNotShowReason; +        } + +        /** +         * Sets the reason why a save dialog was not shown. +         * +         * @param saveDialogNotShowReason The reason why a save dialog was not shown. +         */ +        public void setSaveDialogNotShowReason(@NoSaveReason int saveDialogNotShowReason) { +            mSaveDialogNotShowReason = saveDialogNotShowReason; +        } + +        @Override +        public String toString() { +            return "SaveResult: [logSaveShown=" + mLogSaveShown +                    + ", removeSession=" + mRemoveSession +                    + ", saveDialogNotShowReason=" + mSaveDialogNotShowReason + "]"; +        } +    } +      @Override      public String toString() {          return "Session: [id=" + id + ", component=" + mComponentName + "]";  |