• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.service.autofill;
18 
19 import static android.service.autofill.AutofillServiceHelper.assertValid;
20 import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
21 import static android.view.autofill.Helper.sDebug;
22 
23 import android.annotation.IntDef;
24 import android.annotation.NonNull;
25 import android.annotation.Nullable;
26 import android.annotation.SuppressLint;
27 import android.annotation.TestApi;
28 import android.app.Activity;
29 import android.content.Intent;
30 import android.content.IntentSender;
31 import android.content.pm.ParceledListSlice;
32 import android.os.Bundle;
33 import android.os.Parcel;
34 import android.os.Parcelable;
35 import android.view.autofill.AutofillId;
36 import android.widget.RemoteViews;
37 
38 import com.android.internal.util.Preconditions;
39 
40 import java.lang.annotation.Retention;
41 import java.lang.annotation.RetentionPolicy;
42 import java.util.ArrayList;
43 import java.util.Arrays;
44 import java.util.List;
45 import java.util.Objects;
46 
47 /**
48  * Response for an {@link
49  * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
50  *
51  * <p>See the main {@link AutofillService} documentation for more details and examples.
52  */
53 public final class FillResponse implements Parcelable {
54 
55     /**
56      * Flag used to generate {@link FillEventHistory.Event events} of type
57      * {@link FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}&mdash;if this flag is not passed to
58      * {@link Builder#setFlags(int)}, these events are not generated.
59      */
60     public static final int FLAG_TRACK_CONTEXT_COMMITED = 0x1;
61 
62     /**
63      * Flag used to change the behavior of {@link FillResponse.Builder#disableAutofill(long)}&mdash;
64      * when this flag is passed to {@link Builder#setFlags(int)}, autofill is disabled only for the
65      * activiy that generated the {@link FillRequest}, not the whole app.
66      */
67     public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2;
68 
69     /**
70      * Flag used to request to wait for a delayed fill from the remote Autofill service if it's
71      * passed to {@link Builder#setFlags(int)}.
72      *
73      * <p>Some datasets (i.e. OTP) take time to produce. This flags allows remote service to send
74      * a {@link FillResponse} to the latest {@link FillRequest} via
75      * {@link FillRequest#getDelayedFillIntentSender()} even if the original {@link FillCallback}
76      * has timed out.
77      */
78     public static final int FLAG_DELAY_FILL = 0x4;
79 
80     /** @hide */
81     @IntDef(flag = true, prefix = { "FLAG_" }, value = {
82             FLAG_TRACK_CONTEXT_COMMITED,
83             FLAG_DISABLE_ACTIVITY_ONLY,
84             FLAG_DELAY_FILL
85     })
86     @Retention(RetentionPolicy.SOURCE)
87     @interface FillResponseFlags {}
88 
89     private final @Nullable ParceledListSlice<Dataset> mDatasets;
90     private final @Nullable SaveInfo mSaveInfo;
91     private final @Nullable Bundle mClientState;
92     private final @Nullable RemoteViews mPresentation;
93     private final @Nullable InlinePresentation mInlinePresentation;
94     private final @Nullable InlinePresentation mInlineTooltipPresentation;
95     private final @Nullable RemoteViews mDialogPresentation;
96     private final @Nullable RemoteViews mDialogHeader;
97     private final @Nullable RemoteViews mHeader;
98     private final @Nullable RemoteViews mFooter;
99     private final @Nullable IntentSender mAuthentication;
100     private final @Nullable AutofillId[] mAuthenticationIds;
101     private final @Nullable AutofillId[] mIgnoredIds;
102     private final @Nullable AutofillId[] mFillDialogTriggerIds;
103     private final long mDisableDuration;
104     private final @Nullable AutofillId[] mFieldClassificationIds;
105     private final int mFlags;
106     private int mRequestId;
107     private final @Nullable UserData mUserData;
108     private final @Nullable int[] mCancelIds;
109     private final boolean mSupportsInlineSuggestions;
110 
FillResponse(@onNull Builder builder)111     private FillResponse(@NonNull Builder builder) {
112         mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
113         mSaveInfo = builder.mSaveInfo;
114         mClientState = builder.mClientState;
115         mPresentation = builder.mPresentation;
116         mInlinePresentation = builder.mInlinePresentation;
117         mInlineTooltipPresentation = builder.mInlineTooltipPresentation;
118         mDialogPresentation = builder.mDialogPresentation;
119         mDialogHeader = builder.mDialogHeader;
120         mHeader = builder.mHeader;
121         mFooter = builder.mFooter;
122         mAuthentication = builder.mAuthentication;
123         mAuthenticationIds = builder.mAuthenticationIds;
124         mFillDialogTriggerIds = builder.mFillDialogTriggerIds;
125         mIgnoredIds = builder.mIgnoredIds;
126         mDisableDuration = builder.mDisableDuration;
127         mFieldClassificationIds = builder.mFieldClassificationIds;
128         mFlags = builder.mFlags;
129         mRequestId = INVALID_REQUEST_ID;
130         mUserData = builder.mUserData;
131         mCancelIds = builder.mCancelIds;
132         mSupportsInlineSuggestions = builder.mSupportsInlineSuggestions;
133     }
134 
135     /** @hide */
getClientState()136     public @Nullable Bundle getClientState() {
137         return mClientState;
138     }
139 
140     /** @hide */
getDatasets()141     public @Nullable List<Dataset> getDatasets() {
142         return (mDatasets != null) ? mDatasets.getList() : null;
143     }
144 
145     /** @hide */
getSaveInfo()146     public @Nullable SaveInfo getSaveInfo() {
147         return mSaveInfo;
148     }
149 
150     /** @hide */
getPresentation()151     public @Nullable RemoteViews getPresentation() {
152         return mPresentation;
153     }
154 
155     /** @hide */
getInlinePresentation()156     public @Nullable InlinePresentation getInlinePresentation() {
157         return mInlinePresentation;
158     }
159 
160     /** @hide */
getInlineTooltipPresentation()161     public @Nullable InlinePresentation getInlineTooltipPresentation() {
162         return mInlineTooltipPresentation;
163     }
164 
165     /** @hide */
getDialogPresentation()166     public @Nullable RemoteViews getDialogPresentation() {
167         return mDialogPresentation;
168     }
169 
170     /** @hide */
getDialogHeader()171     public @Nullable RemoteViews getDialogHeader() {
172         return mDialogHeader;
173     }
174 
175     /** @hide */
getHeader()176     public @Nullable RemoteViews getHeader() {
177         return mHeader;
178     }
179 
180     /** @hide */
getFooter()181     public @Nullable RemoteViews getFooter() {
182         return mFooter;
183     }
184 
185     /** @hide */
getAuthentication()186     public @Nullable IntentSender getAuthentication() {
187         return mAuthentication;
188     }
189 
190     /** @hide */
getAuthenticationIds()191     public @Nullable AutofillId[] getAuthenticationIds() {
192         return mAuthenticationIds;
193     }
194 
195     /** @hide */
getFillDialogTriggerIds()196     public @Nullable AutofillId[] getFillDialogTriggerIds() {
197         return mFillDialogTriggerIds;
198     }
199 
200     /** @hide */
getIgnoredIds()201     public @Nullable AutofillId[] getIgnoredIds() {
202         return mIgnoredIds;
203     }
204 
205     /** @hide */
getDisableDuration()206     public long getDisableDuration() {
207         return mDisableDuration;
208     }
209 
210     /** @hide */
getFieldClassificationIds()211     public @Nullable AutofillId[] getFieldClassificationIds() {
212         return mFieldClassificationIds;
213     }
214 
215     /** @hide */
getUserData()216     public @Nullable UserData getUserData() {
217         return mUserData;
218     }
219 
220     /** @hide */
221     @TestApi
getFlags()222     public int getFlags() {
223         return mFlags;
224     }
225 
226     /**
227      * Associates a {@link FillResponse} to a request.
228      *
229      * <p>Set inside of the {@link FillCallback} code, not the {@link AutofillService}.
230      *
231      * @param requestId The id of the request to associate the response to.
232      *
233      * @hide
234      */
setRequestId(int requestId)235     public void setRequestId(int requestId) {
236         mRequestId = requestId;
237     }
238 
239     /** @hide */
getRequestId()240     public int getRequestId() {
241         return mRequestId;
242     }
243 
244     /** @hide */
245     @Nullable
getCancelIds()246     public int[] getCancelIds() {
247         return mCancelIds;
248     }
249 
250     /** @hide */
supportsInlineSuggestions()251     public boolean supportsInlineSuggestions() {
252         return mSupportsInlineSuggestions;
253     }
254 
255     /**
256      * Builder for {@link FillResponse} objects. You must to provide at least
257      * one dataset or set an authentication intent with a presentation view.
258      */
259     public static final class Builder {
260         private ArrayList<Dataset> mDatasets;
261         private SaveInfo mSaveInfo;
262         private Bundle mClientState;
263         private RemoteViews mPresentation;
264         private InlinePresentation mInlinePresentation;
265         private InlinePresentation mInlineTooltipPresentation;
266         private RemoteViews mDialogPresentation;
267         private RemoteViews mDialogHeader;
268         private RemoteViews mHeader;
269         private RemoteViews mFooter;
270         private IntentSender mAuthentication;
271         private AutofillId[] mAuthenticationIds;
272         private AutofillId[] mIgnoredIds;
273         private long mDisableDuration;
274         private AutofillId[] mFieldClassificationIds;
275         private AutofillId[] mFillDialogTriggerIds;
276         private int mFlags;
277         private boolean mDestroyed;
278         private UserData mUserData;
279         private int[] mCancelIds;
280         private boolean mSupportsInlineSuggestions;
281 
282         /**
283          * Triggers a custom UI before autofilling the screen with any data set in this
284          * response.
285          *
286          * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
287          * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
288          * for examples.
289          *
290          * <p>This is typically useful when a user interaction is required to unlock their
291          * data vault if you encrypt the data set labels and data set data. It is recommended
292          * to encrypt only the sensitive data and not the data set labels which would allow
293          * auth on the data set level leading to a better user experience. Note that if you
294          * use sensitive data as a label, for example an email address, then it should also
295          * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
296          * {@link Activity} which implements your authentication flow. Also if you provide an auth
297          * intent you also need to specify the presentation view to be shown in the fill UI
298          * for the user to trigger your authentication flow.
299          *
300          * <p>When a user triggers autofill, the system launches the provided intent
301          * whose extras will have the
302          * {@link android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen
303          * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE
304          * client state}. Once you complete your authentication flow you should set the
305          * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and set the
306          * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra
307          * with the fully populated {@link FillResponse response} (or {@code null} if the screen
308          * cannot be autofilled).
309          *
310          * <p> <b>IMPORTANT</b>: Extras must be non-null on the intent being set for Android 12
311          * otherwise it will cause a crash. Do not use {@link Activity#setResult(int)}, instead use
312          * {@link Activity#setResult(int, Intent) with non-null extras. Consider setting {
313          * @link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} to null or use
314          * {@link Bundle#EMPTY} with {@link Intent#putExtras(Bundle)} on the intent when
315          * finishing activity to avoid crash). </p>
316          *
317          * <p>For example, if you provided an empty {@link FillResponse response} because the
318          * user's data was locked and marked that the response needs an authentication then
319          * in the response returned if authentication succeeds you need to provide all
320          * available data sets some of which may need to be further authenticated, for
321          * example a credit card whose CVV needs to be entered.
322          *
323          * <p>If you provide an authentication intent you must also provide a presentation
324          * which is used to visualize the response for triggering the authentication
325          * flow.
326          *
327          * <p><b>Note:</b> Do not make the provided pending intent
328          * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
329          * platform needs to fill in the authentication arguments.
330          *
331          * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
332          * or background color: Autofill on different platforms may have different themes.
333          *
334          * @param authentication Intent to an activity with your authentication flow.
335          * @param presentation The presentation to visualize the response.
336          * @param ids id of Views that when focused will display the authentication UI.
337          *
338          * @return This builder.
339          *
340          * @throws IllegalArgumentException if any of the following occurs:
341          * <ul>
342          *   <li>{@code ids} is {@code null}</li>
343          *   <li>{@code ids} is empty</li>
344          *   <li>{@code ids} contains a {@code null} element</li>
345          *   <li>both {@code authentication} and {@code presentation} are {@code null}</li>
346          *   <li>both {@code authentication} and {@code presentation} are non-{@code null}</li>
347          * </ul>
348          *
349          * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
350          * {@link #setFooter(RemoteViews) footer} are already set for this builder.
351          *
352          * @see android.app.PendingIntent#getIntentSender()
353          * @deprecated Use
354          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
355          * instead.
356          */
357         @Deprecated
358         @NonNull
setAuthentication(@onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation)359         public Builder setAuthentication(@NonNull AutofillId[] ids,
360                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation) {
361             throwIfDestroyed();
362             throwIfDisableAutofillCalled();
363             if (mHeader != null || mFooter != null) {
364                 throw new IllegalStateException("Already called #setHeader() or #setFooter()");
365             }
366 
367             if (authentication == null ^ presentation == null) {
368                 throw new IllegalArgumentException("authentication and presentation"
369                         + " must be both non-null or null");
370             }
371             mAuthentication = authentication;
372             mPresentation = presentation;
373             mAuthenticationIds = assertValid(ids);
374             return this;
375         }
376 
377         /**
378          * Triggers a custom UI before autofilling the screen with any data set in this
379          * response.
380          *
381          * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
382          * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
383          * for examples.
384          *
385          * <p>This method is similar to
386          * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)}, but also accepts
387          * an {@link InlinePresentation} presentation which is required for authenticating through
388          * the inline autofill flow.
389          *
390          * <p><b>Note:</b> {@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} does
391          * not work with {@link InlinePresentation}.</p>
392          *
393          * @param authentication Intent to an activity with your authentication flow.
394          * @param presentation The presentation to visualize the response.
395          * @param inlinePresentation The inlinePresentation to visualize the response inline.
396          * @param ids id of Views that when focused will display the authentication UI.
397          *
398          * @return This builder.
399          *
400          * @throws IllegalArgumentException if any of the following occurs:
401          * <ul>
402          *   <li>{@code ids} is {@code null}</li>
403          *   <li>{@code ids} is empty</li>
404          *   <li>{@code ids} contains a {@code null} element</li>
405          *   <li>both {@code authentication} and {@code presentation} are {@code null}</li>
406          *   <li>both {@code authentication} and {@code presentation} are non-{@code null}</li>
407          *   <li>both {@code authentication} and {@code inlinePresentation} are {@code null}</li>
408          *   <li>both {@code authentication} and {@code inlinePresentation} are
409          *   non-{@code null}</li>
410          * </ul>
411          *
412          * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
413          * {@link #setFooter(RemoteViews) footer} are already set for this builder.
414          *
415          * @see android.app.PendingIntent#getIntentSender()
416          * @deprecated Use
417          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
418          * instead.
419          */
420         @Deprecated
421         @NonNull
setAuthentication(@onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation)422         public Builder setAuthentication(@NonNull AutofillId[] ids,
423                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
424                 @Nullable InlinePresentation inlinePresentation) {
425             return setAuthentication(ids, authentication, presentation, inlinePresentation, null);
426         }
427 
428         /**
429          * Triggers a custom UI before autofilling the screen with any data set in this
430          * response.
431          *
432          * <p>This method like
433          * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews, InlinePresentation)}
434          * but allows setting an {@link InlinePresentation} for the inline suggestion tooltip.
435          *
436          * @deprecated Use
437          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)}
438          * instead.
439          */
440         @Deprecated
441         @NonNull
setAuthentication(@uppressLint"ArrayReturn") @onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation inlineTooltipPresentation)442         public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
443                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
444                 @Nullable InlinePresentation inlinePresentation,
445                 @Nullable InlinePresentation inlineTooltipPresentation) {
446             throwIfDestroyed();
447             throwIfDisableAutofillCalled();
448             return setAuthentication(ids, authentication, presentation,
449                     inlinePresentation, inlineTooltipPresentation, null);
450         }
451 
452         /**
453          * Triggers a custom UI before autofilling the screen with any data set in this
454          * response.
455          *
456          * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
457          * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
458          * for examples.
459          *
460          * <p>This is typically useful when a user interaction is required to unlock their
461          * data vault if you encrypt the data set labels and data set data. It is recommended
462          * to encrypt only the sensitive data and not the data set labels which would allow
463          * auth on the data set level leading to a better user experience. Note that if you
464          * use sensitive data as a label, for example an email address, then it should also
465          * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
466          * {@link Activity} which implements your authentication flow. Also if you provide an auth
467          * intent you also need to specify the presentation view to be shown in the fill UI
468          * for the user to trigger your authentication flow.
469          *
470          * <p>When a user triggers autofill, the system launches the provided intent
471          * whose extras will have the
472          * {@link android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen
473          * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE
474          * client state}. Once you complete your authentication flow you should set the
475          * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and set the
476          * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra
477          * with the fully populated {@link FillResponse response} (or {@code null} if the screen
478          * cannot be autofilled).
479          *
480          * <p>For example, if you provided an empty {@link FillResponse response} because the
481          * user's data was locked and marked that the response needs an authentication then
482          * in the response returned if authentication succeeds you need to provide all
483          * available data sets some of which may need to be further authenticated, for
484          * example a credit card whose CVV needs to be entered.
485          *
486          * <p>If you provide an authentication intent you must also provide a presentation
487          * which is used to visualize the response for triggering the authentication
488          * flow.
489          *
490          * <p><b>Note:</b> Do not make the provided pending intent
491          * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
492          * platform needs to fill in the authentication arguments.
493          *
494          * <p><b>Note:</b> {@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} does
495          * not work with {@link InlinePresentation}.</p>
496          *
497          * @param ids id of Views that when focused will display the authentication UI.
498          * @param authentication Intent to an activity with your authentication flow.
499          * @param presentations The presentations to visualize the response.
500          *
501          * @throws IllegalArgumentException if any of the following occurs:
502          * <ul>
503          *   <li>{@code ids} is {@code null}</li>
504          *   <li>{@code ids} is empty</li>
505          *   <li>{@code ids} contains a {@code null} element</li>
506          *   <li>{@code authentication} is {@code null}, but either or both of
507          *   {@code presentations.getPresentation()} and
508          *   {@code presentations.getInlinePresentation()} is non-{@code null}</li>
509          *   <li>{@code authentication} is non-{{@code null}, but both
510          *   {@code presentations.getPresentation()} and
511          *   {@code presentations.getInlinePresentation()} are {@code null}</li>
512          * </ul>
513          *
514          * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
515          * {@link #setFooter(RemoteViews) footer} are already set for this builder.
516          *
517          * @return This builder.
518          */
519         @NonNull
setAuthentication(@uppressLint"ArrayReturn") @onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable Presentations presentations)520         public Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
521                 @Nullable IntentSender authentication,
522                 @Nullable Presentations presentations) {
523             throwIfDestroyed();
524             throwIfDisableAutofillCalled();
525             if (presentations == null) {
526                 return setAuthentication(ids, authentication, null, null, null, null);
527             }
528             return setAuthentication(ids, authentication,
529                     presentations.getMenuPresentation(),
530                     presentations.getInlinePresentation(),
531                     presentations.getInlineTooltipPresentation(),
532                     presentations.getDialogPresentation());
533         }
534 
535         /**
536          * Triggers a custom UI before autofilling the screen with any data set in this
537          * response.
538          */
539         @NonNull
setAuthentication(@uppressLint"ArrayReturn") @onNull AutofillId[] ids, @Nullable IntentSender authentication, @Nullable RemoteViews presentation, @Nullable InlinePresentation inlinePresentation, @Nullable InlinePresentation inlineTooltipPresentation, @Nullable RemoteViews dialogPresentation)540         private Builder setAuthentication(@SuppressLint("ArrayReturn") @NonNull AutofillId[] ids,
541                 @Nullable IntentSender authentication, @Nullable RemoteViews presentation,
542                 @Nullable InlinePresentation inlinePresentation,
543                 @Nullable InlinePresentation inlineTooltipPresentation,
544                 @Nullable RemoteViews dialogPresentation) {
545             throwIfDestroyed();
546             throwIfDisableAutofillCalled();
547             if (mHeader != null || mFooter != null) {
548                 throw new IllegalStateException("Already called #setHeader() or #setFooter()");
549             }
550 
551             if (authentication == null ^ (presentation == null && inlinePresentation == null)) {
552                 throw new IllegalArgumentException("authentication and presentation "
553                         + "(dropdown or inline), must be both non-null or null");
554             }
555             mAuthentication = authentication;
556             mPresentation = presentation;
557             mInlinePresentation = inlinePresentation;
558             mInlineTooltipPresentation = inlineTooltipPresentation;
559             mDialogPresentation = dialogPresentation;
560             mAuthenticationIds = assertValid(ids);
561             return this;
562         }
563 
564         /**
565          * Specifies views that should not trigger new
566          * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
567          * FillCallback)} requests.
568          *
569          * <p>This is typically used when the service cannot autofill the view; for example, a
570          * text field representing the result of a Captcha challenge.
571          */
572         @NonNull
setIgnoredIds(AutofillId...ids)573         public Builder setIgnoredIds(AutofillId...ids) {
574             throwIfDestroyed();
575             mIgnoredIds = ids;
576             return this;
577         }
578 
579         /**
580          * Adds a new {@link Dataset} to this response.
581          *
582          * <p><b>Note: </b> on Android {@link android.os.Build.VERSION_CODES#O}, the total number of
583          * datasets is limited by the Binder transaction size, so it's recommended to keep it
584          * small (in the range of 10-20 at most) and use pagination by adding a fake
585          * {@link Dataset.Builder#setAuthentication(IntentSender) authenticated dataset} at the end
586          * with a presentation string like "Next 10" that would return a new {@link FillResponse}
587          * with the next 10 datasets, and so on. This limitation was lifted on
588          * Android {@link android.os.Build.VERSION_CODES#O_MR1}, although the Binder transaction
589          * size can still be reached if each dataset itself is too big.
590          *
591          * @return This builder.
592          */
593         @NonNull
addDataset(@ullable Dataset dataset)594         public Builder addDataset(@Nullable Dataset dataset) {
595             throwIfDestroyed();
596             throwIfDisableAutofillCalled();
597             if (dataset == null) {
598                 return this;
599             }
600             if (mDatasets == null) {
601                 mDatasets = new ArrayList<>();
602             }
603             if (!mDatasets.add(dataset)) {
604                 return this;
605             }
606             return this;
607         }
608 
609         /**
610          * Sets the {@link SaveInfo} associated with this response.
611          *
612          * @return This builder.
613          */
setSaveInfo(@onNull SaveInfo saveInfo)614         public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) {
615             throwIfDestroyed();
616             throwIfDisableAutofillCalled();
617             mSaveInfo = saveInfo;
618             return this;
619         }
620 
621         /**
622          * Sets a bundle with state that is passed to subsequent APIs that manipulate this response.
623          *
624          * <p>You can use this bundle to store intermediate state that is passed to subsequent calls
625          * to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
626          * FillCallback)} and {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}, and
627          * you can also retrieve it by calling {@link FillEventHistory.Event#getClientState()}.
628          *
629          * <p>If this method is called on multiple {@link FillResponse} objects for the same
630          * screen, just the latest bundle is passed back to the service.
631          *
632          * @param clientState The custom client state.
633          * @return This builder.
634          */
635         @NonNull
setClientState(@ullable Bundle clientState)636         public Builder setClientState(@Nullable Bundle clientState) {
637             throwIfDestroyed();
638             throwIfDisableAutofillCalled();
639             mClientState = clientState;
640             return this;
641         }
642 
643         /**
644          * Sets which fields are used for
645          * <a href="AutofillService.html#FieldClassification">field classification</a>
646          *
647          * <p><b>Note:</b> This method automatically adds the
648          * {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} to the {@link #setFlags(int) flags}.
649 
650          * @throws IllegalArgumentException is length of {@code ids} args is more than
651          * {@link UserData#getMaxFieldClassificationIdsSize()}.
652          * @throws IllegalStateException if {@link #build()} or {@link #disableAutofill(long)} was
653          * already called.
654          * @throws NullPointerException if {@code ids} or any element on it is {@code null}.
655          */
656         @NonNull
setFieldClassificationIds(@onNull AutofillId... ids)657         public Builder setFieldClassificationIds(@NonNull AutofillId... ids) {
658             throwIfDestroyed();
659             throwIfDisableAutofillCalled();
660             Preconditions.checkArrayElementsNotNull(ids, "ids");
661             Preconditions.checkArgumentInRange(ids.length, 1,
662                     UserData.getMaxFieldClassificationIdsSize(), "ids length");
663             mFieldClassificationIds = ids;
664             mFlags |= FLAG_TRACK_CONTEXT_COMMITED;
665             return this;
666         }
667 
668         /**
669          * Sets flags changing the response behavior.
670          *
671          * @param flags a combination of {@link #FLAG_TRACK_CONTEXT_COMMITED} and
672          * {@link #FLAG_DISABLE_ACTIVITY_ONLY}, or {@code 0}.
673          *
674          * @return This builder.
675          */
676         @NonNull
setFlags(@illResponseFlags int flags)677         public Builder setFlags(@FillResponseFlags int flags) {
678             throwIfDestroyed();
679             mFlags = Preconditions.checkFlagsArgument(flags,
680                     FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY | FLAG_DELAY_FILL);
681             return this;
682         }
683 
684         /**
685          * Disables autofill for the app or activity.
686          *
687          * <p>This method is useful to optimize performance in cases where the service knows it
688          * can not autofill an app&mdash;for example, when the service has a list of "denylisted"
689          * apps such as office suites.
690          *
691          * <p>By default, it disables autofill for all activities in the app, unless the response is
692          * {@link #setFlags(int) flagged} with {@link #FLAG_DISABLE_ACTIVITY_ONLY}.
693          *
694          * <p>Autofill for the app or activity is automatically re-enabled after any of the
695          * following conditions:
696          *
697          * <ol>
698          *   <li>{@code duration} milliseconds have passed.
699          *   <li>The autofill service for the user has changed.
700          *   <li>The device has rebooted.
701          * </ol>
702          *
703          * <p><b>Note:</b> Activities that are running when autofill is re-enabled remain
704          * disabled for autofill until they finish and restart.
705          *
706          * @param duration duration to disable autofill, in milliseconds.
707          *
708          * @return this builder
709          *
710          * @throws IllegalArgumentException if {@code duration} is not a positive number.
711          * @throws IllegalStateException if either {@link #addDataset(Dataset)},
712          *       {@link #setAuthentication(AutofillId[], IntentSender, Presentations)},
713          *       {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
714          *       {@link #setFieldClassificationIds(AutofillId...)} was already called.
715          */
716         @NonNull
disableAutofill(long duration)717         public Builder disableAutofill(long duration) {
718             throwIfDestroyed();
719             if (duration <= 0) {
720                 throw new IllegalArgumentException("duration must be greater than 0");
721             }
722             if (mAuthentication != null || mDatasets != null || mSaveInfo != null
723                     || mFieldClassificationIds != null || mClientState != null) {
724                 throw new IllegalStateException("disableAutofill() must be the only method called");
725             }
726 
727             mDisableDuration = duration;
728             return this;
729         }
730 
731         /**
732          * Sets a header to be shown as the first element in the list of datasets.
733          *
734          * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset},
735          * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this
736          * method should only be used on {@link FillResponse FillResponses} that do not require
737          * authentication (as the header could have been set directly in the main presentation in
738          * these cases).
739          *
740          * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
741          * or background color: Autofill on different platforms may have different themes.
742          *
743          * @param header a presentation to represent the header. This presentation is not clickable
744          * &mdash;calling
745          * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
746          * have no effect.
747          *
748          * @return this builder
749          *
750          * @throws IllegalStateException if an
751          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
752          * authentication} was already set for this builder.
753          */
754         // TODO(b/69796626): make it sticky / update javadoc
755         @NonNull
setHeader(@onNull RemoteViews header)756         public Builder setHeader(@NonNull RemoteViews header) {
757             throwIfDestroyed();
758             throwIfAuthenticationCalled();
759             mHeader = Objects.requireNonNull(header);
760             return this;
761         }
762 
763         /**
764          * Sets a footer to be shown as the last element in the list of datasets.
765          *
766          * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset},
767          * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this
768          * method should only be used on {@link FillResponse FillResponses} that do not require
769          * authentication (as the footer could have been set directly in the main presentation in
770          * these cases).
771          *
772          * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
773          * or background color: Autofill on different platforms may have different themes.
774          *
775          * @param footer a presentation to represent the footer. This presentation is not clickable
776          * &mdash;calling
777          * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
778          * have no effect.
779          *
780          * @return this builder
781          *
782          * @throws IllegalStateException if the FillResponse
783          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
784          * requires authentication}.
785          */
786         // TODO(b/69796626): make it sticky / update javadoc
787         @NonNull
setFooter(@onNull RemoteViews footer)788         public Builder setFooter(@NonNull RemoteViews footer) {
789             throwIfDestroyed();
790             throwIfAuthenticationCalled();
791             mFooter = Objects.requireNonNull(footer);
792             return this;
793         }
794 
795         /**
796          * Sets a specific {@link UserData} for field classification for this request only.
797          *
798          * <p>Any fields in this UserData will override corresponding fields in the generic
799          * UserData object
800          *
801          * @return this builder
802          * @throws IllegalStateException if the FillResponse
803          * {@link #setAuthentication(AutofillId[], IntentSender, Presentations)
804          * requires authentication}.
805          */
806         @NonNull
setUserData(@onNull UserData userData)807         public Builder setUserData(@NonNull UserData userData) {
808             throwIfDestroyed();
809             throwIfAuthenticationCalled();
810             mUserData = Objects.requireNonNull(userData);
811             return this;
812         }
813 
814         /**
815          * Sets target resource IDs of the child view in {@link RemoteViews Presentation Template}
816          * which will cancel the session when clicked.
817          * Those targets will be respectively applied to a child of the header, footer and
818          * each {@link Dataset}.
819          *
820          * @param ids array of the resource id. Empty list or non-existing id has no effect.
821          *
822          * @return this builder
823          *
824          * @throws IllegalStateException if {@link #build()} was already called.
825          */
826         @NonNull
setPresentationCancelIds(@ullable int[] ids)827         public Builder setPresentationCancelIds(@Nullable int[] ids) {
828             throwIfDestroyed();
829             mCancelIds = ids;
830             return this;
831         }
832 
833         /**
834          * Sets the presentation of header in fill dialog UI. The header should have
835          * a prompt for what datasets are shown in the dialog. If this is not set,
836          * the dialog only shows your application icon.
837          *
838          * More details about the fill dialog, see
839          * <a href="Dataset.html#FillDialogUI">fill dialog UI</a>
840          */
841         @NonNull
setDialogHeader(@onNull RemoteViews header)842         public Builder setDialogHeader(@NonNull RemoteViews header) {
843             throwIfDestroyed();
844             Objects.requireNonNull(header);
845             mDialogHeader = header;
846             return this;
847         }
848 
849         /**
850          * Sets which fields are used for the fill dialog UI.
851          *
852          * More details about the fill dialog, see
853          * <a href="Dataset.html#FillDialogUI">fill dialog UI</a>
854          *
855          * @throws IllegalStateException if {@link #build()} was already called.
856          * @throws NullPointerException if {@code ids} or any element on it is {@code null}.
857          */
858         @NonNull
setFillDialogTriggerIds(@onNull AutofillId... ids)859         public Builder setFillDialogTriggerIds(@NonNull AutofillId... ids) {
860             throwIfDestroyed();
861             Preconditions.checkArrayElementsNotNull(ids, "ids");
862             mFillDialogTriggerIds = ids;
863             return this;
864         }
865 
866         /**
867          * Builds a new {@link FillResponse} instance.
868          *
869          * @throws IllegalStateException if any of the following conditions occur:
870          * <ol>
871          *   <li>{@link #build()} was already called.
872          *   <li>No call was made to {@link #addDataset(Dataset)},
873          *       {@link #setAuthentication(AutofillId[], IntentSender, Presentations)},
874          *       {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
875          *       {@link #setClientState(Bundle)},
876          *       or {@link #setFieldClassificationIds(AutofillId...)}.
877          *   <li>{@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} is called
878          *       without any previous calls to {@link #addDataset(Dataset)}.
879          * </ol>
880          *
881          * @return A built response.
882          */
883         @NonNull
build()884         public FillResponse build() {
885             throwIfDestroyed();
886             if (mAuthentication == null && mDatasets == null && mSaveInfo == null
887                     && mDisableDuration == 0 && mFieldClassificationIds == null
888                     && mClientState == null) {
889                 throw new IllegalStateException("need to provide: at least one DataSet, or a "
890                         + "SaveInfo, or an authentication with a presentation, "
891                         + "or a FieldsDetection, or a client state, or disable autofill");
892             }
893             if (mDatasets == null && (mHeader != null || mFooter != null)) {
894                 throw new IllegalStateException(
895                         "must add at least 1 dataset when using header or footer");
896             }
897 
898             if (mDatasets != null) {
899                 for (final Dataset dataset : mDatasets) {
900                     if (dataset.getFieldInlinePresentation(0) != null) {
901                         mSupportsInlineSuggestions = true;
902                         break;
903                     }
904                 }
905             } else if (mInlinePresentation != null) {
906                 mSupportsInlineSuggestions = true;
907             }
908 
909             mDestroyed = true;
910             return new FillResponse(this);
911         }
912 
throwIfDestroyed()913         private void throwIfDestroyed() {
914             if (mDestroyed) {
915                 throw new IllegalStateException("Already called #build()");
916             }
917         }
918 
throwIfDisableAutofillCalled()919         private void throwIfDisableAutofillCalled() {
920             if (mDisableDuration > 0) {
921                 throw new IllegalStateException("Already called #disableAutofill()");
922             }
923         }
924 
throwIfAuthenticationCalled()925         private void throwIfAuthenticationCalled() {
926             if (mAuthentication != null) {
927                 throw new IllegalStateException("Already called #setAuthentication()");
928             }
929         }
930     }
931 
932     /////////////////////////////////////
933     // Object "contract" methods. //
934     /////////////////////////////////////
935     @Override
toString()936     public String toString() {
937         if (!sDebug) return super.toString();
938 
939         // TODO: create a dump() method instead
940         final StringBuilder builder = new StringBuilder(
941                 "FillResponse : [mRequestId=" + mRequestId);
942         if (mDatasets != null) {
943             builder.append(", datasets=").append(mDatasets.getList());
944         }
945         if (mSaveInfo != null) {
946             builder.append(", saveInfo=").append(mSaveInfo);
947         }
948         if (mClientState != null) {
949             builder.append(", hasClientState");
950         }
951         if (mPresentation != null) {
952             builder.append(", hasPresentation");
953         }
954         if (mInlinePresentation != null) {
955             builder.append(", hasInlinePresentation");
956         }
957         if (mInlineTooltipPresentation != null) {
958             builder.append(", hasInlineTooltipPresentation");
959         }
960         if (mDialogPresentation != null) {
961             builder.append(", hasDialogPresentation");
962         }
963         if (mDialogHeader != null) {
964             builder.append(", hasDialogHeader");
965         }
966         if (mHeader != null) {
967             builder.append(", hasHeader");
968         }
969         if (mFooter != null) {
970             builder.append(", hasFooter");
971         }
972         if (mAuthentication != null) {
973             builder.append(", hasAuthentication");
974         }
975         if (mAuthenticationIds != null) {
976             builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds));
977         }
978         if (mFillDialogTriggerIds != null) {
979             builder.append(", fillDialogTriggerIds=")
980                     .append(Arrays.toString(mFillDialogTriggerIds));
981         }
982         builder.append(", disableDuration=").append(mDisableDuration);
983         if (mFlags != 0) {
984             builder.append(", flags=").append(mFlags);
985         }
986         if (mFieldClassificationIds != null) {
987             builder.append(Arrays.toString(mFieldClassificationIds));
988         }
989         if (mUserData != null) {
990             builder.append(", userData=").append(mUserData);
991         }
992         if (mCancelIds != null) {
993             builder.append(", mCancelIds=").append(mCancelIds.length);
994         }
995         builder.append(", mSupportInlinePresentations=").append(mSupportsInlineSuggestions);
996         return builder.append("]").toString();
997     }
998 
999     /////////////////////////////////////
1000     // Parcelable "contract" methods. //
1001     /////////////////////////////////////
1002 
1003     @Override
describeContents()1004     public int describeContents() {
1005         return 0;
1006     }
1007 
1008     @Override
writeToParcel(Parcel parcel, int flags)1009     public void writeToParcel(Parcel parcel, int flags) {
1010         parcel.writeParcelable(mDatasets, flags);
1011         parcel.writeParcelable(mSaveInfo, flags);
1012         parcel.writeParcelable(mClientState, flags);
1013         parcel.writeParcelableArray(mAuthenticationIds, flags);
1014         parcel.writeParcelable(mAuthentication, flags);
1015         parcel.writeParcelable(mPresentation, flags);
1016         parcel.writeParcelable(mInlinePresentation, flags);
1017         parcel.writeParcelable(mInlineTooltipPresentation, flags);
1018         parcel.writeParcelable(mDialogPresentation, flags);
1019         parcel.writeParcelable(mDialogHeader, flags);
1020         parcel.writeParcelableArray(mFillDialogTriggerIds, flags);
1021         parcel.writeParcelable(mHeader, flags);
1022         parcel.writeParcelable(mFooter, flags);
1023         parcel.writeParcelable(mUserData, flags);
1024         parcel.writeParcelableArray(mIgnoredIds, flags);
1025         parcel.writeLong(mDisableDuration);
1026         parcel.writeParcelableArray(mFieldClassificationIds, flags);
1027         parcel.writeInt(mFlags);
1028         parcel.writeIntArray(mCancelIds);
1029         parcel.writeInt(mRequestId);
1030     }
1031 
1032     public static final @android.annotation.NonNull Parcelable.Creator<FillResponse> CREATOR =
1033             new Parcelable.Creator<FillResponse>() {
1034         @Override
1035         public FillResponse createFromParcel(Parcel parcel) {
1036             // Always go through the builder to ensure the data ingested by
1037             // the system obeys the contract of the builder to avoid attacks
1038             // using specially crafted parcels.
1039             final Builder builder = new Builder();
1040             final ParceledListSlice<Dataset> datasetSlice = parcel.readParcelable(null, android.content.pm.ParceledListSlice.class);
1041             final List<Dataset> datasets = (datasetSlice != null) ? datasetSlice.getList() : null;
1042             final int datasetCount = (datasets != null) ? datasets.size() : 0;
1043             for (int i = 0; i < datasetCount; i++) {
1044                 builder.addDataset(datasets.get(i));
1045             }
1046             builder.setSaveInfo(parcel.readParcelable(null, android.service.autofill.SaveInfo.class));
1047             builder.setClientState(parcel.readParcelable(null, android.os.Bundle.class));
1048 
1049             // Sets authentication state.
1050             final AutofillId[] authenticationIds = parcel.readParcelableArray(null,
1051                     AutofillId.class);
1052             final IntentSender authentication = parcel.readParcelable(null, android.content.IntentSender.class);
1053             final RemoteViews presentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
1054             final InlinePresentation inlinePresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
1055             final InlinePresentation inlineTooltipPresentation = parcel.readParcelable(null, android.service.autofill.InlinePresentation.class);
1056             final RemoteViews dialogPresentation = parcel.readParcelable(null, android.widget.RemoteViews.class);
1057             if (authenticationIds != null) {
1058                 builder.setAuthentication(authenticationIds, authentication, presentation,
1059                         inlinePresentation, inlineTooltipPresentation, dialogPresentation);
1060             }
1061             final RemoteViews dialogHeader = parcel.readParcelable(null, android.widget.RemoteViews.class);
1062             if (dialogHeader != null) {
1063                 builder.setDialogHeader(dialogHeader);
1064             }
1065             final AutofillId[] triggerIds = parcel.readParcelableArray(null, AutofillId.class);
1066             if (triggerIds != null) {
1067                 builder.setFillDialogTriggerIds(triggerIds);
1068             }
1069             final RemoteViews header = parcel.readParcelable(null, android.widget.RemoteViews.class);
1070             if (header != null) {
1071                 builder.setHeader(header);
1072             }
1073             final RemoteViews footer = parcel.readParcelable(null, android.widget.RemoteViews.class);
1074             if (footer != null) {
1075                 builder.setFooter(footer);
1076             }
1077             final UserData userData = parcel.readParcelable(null, android.service.autofill.UserData.class);
1078             if (userData != null) {
1079                 builder.setUserData(userData);
1080             }
1081 
1082             builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class));
1083             final long disableDuration = parcel.readLong();
1084             if (disableDuration > 0) {
1085                 builder.disableAutofill(disableDuration);
1086             }
1087             final AutofillId[] fieldClassifactionIds =
1088                     parcel.readParcelableArray(null, AutofillId.class);
1089             if (fieldClassifactionIds != null) {
1090                 builder.setFieldClassificationIds(fieldClassifactionIds);
1091             }
1092             builder.setFlags(parcel.readInt());
1093             final int[] cancelIds = parcel.createIntArray();
1094             builder.setPresentationCancelIds(cancelIds);
1095 
1096             final FillResponse response = builder.build();
1097             response.setRequestId(parcel.readInt());
1098 
1099             return response;
1100         }
1101 
1102         @Override
1103         public FillResponse[] newArray(int size) {
1104             return new FillResponse[size];
1105         }
1106     };
1107 }
1108