blob: 6b3009f9f9ef27d419a6618e2cc8ccc13a38d184 [file] [log] [blame]
Felipe Leme6d553872016-12-08 17:13:25 -08001/*
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 */
Felipe Lemeb3c35372017-04-03 10:20:34 -070016
Svet Ganov782043c2017-02-11 00:52:02 +000017package android.service.autofill;
Felipe Leme6d553872016-12-08 17:13:25 -080018
Felipe Leme25960892018-03-21 12:56:05 -070019import static android.service.autofill.AutofillServiceHelper.assertValid;
Philip P. Moltmannc7619632017-04-25 11:57:37 -070020import static android.service.autofill.FillRequest.INVALID_REQUEST_ID;
Felipe Leme9f9ee252017-04-27 13:56:22 -070021import static android.view.autofill.Helper.sDebug;
Felipe Lemed633f072017-02-14 10:17:17 -080022
Felipe Leme59c44642017-10-13 11:05:05 -070023import android.annotation.IntDef;
Svet Ganov0f4928f2017-02-02 20:02:51 -080024import android.annotation.NonNull;
Felipe Leme436ab6a2016-12-15 11:56:15 -080025import android.annotation.Nullable;
Felipe Leme24d71732017-10-20 10:32:57 -070026import android.annotation.TestApi;
Felipe Leme2ef19c12017-06-05 11:32:32 -070027import android.app.Activity;
Svet Ganov0f4928f2017-02-02 20:02:51 -080028import android.content.IntentSender;
Felipe Lemecb96f392017-07-25 15:19:30 -070029import android.content.pm.ParceledListSlice;
Felipe Leme6d553872016-12-08 17:13:25 -080030import android.os.Bundle;
31import android.os.Parcel;
32import android.os.Parcelable;
Felipe Lemeb3c35372017-04-03 10:20:34 -070033import android.view.autofill.AutofillId;
Svet Ganov00c771dc2017-02-19 00:06:22 -080034import android.widget.RemoteViews;
Felipe Leme6d553872016-12-08 17:13:25 -080035
Felipe Leme17292d12017-10-24 14:03:10 -070036import com.android.internal.util.Preconditions;
37
Felipe Leme59c44642017-10-13 11:05:05 -070038import java.lang.annotation.Retention;
39import java.lang.annotation.RetentionPolicy;
Felipe Lemefe35e692017-02-21 13:44:50 -080040import java.util.ArrayList;
Felipe Lemec2430f3c4d62017-05-01 09:35:33 -070041import java.util.Arrays;
Felipe Lemecb96f392017-07-25 15:19:30 -070042import java.util.List;
Felipe Lemefe35e692017-02-21 13:44:50 -080043
Felipe Leme6d553872016-12-08 17:13:25 -080044/**
Felipe Leme78172e72017-12-08 17:01:15 -080045 * Response for an {@link
Felipe Lemee5f9c302017-04-18 17:48:49 -070046 * AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal, FillCallback)}.
Felipe Leme6d553872016-12-08 17:13:25 -080047 *
Felipe Leme2ef19c12017-06-05 11:32:32 -070048 * <p>See the main {@link AutofillService} documentation for more details and examples.
Felipe Leme6d553872016-12-08 17:13:25 -080049 */
50public final class FillResponse implements Parcelable {
Svet Ganov782043c2017-02-11 00:52:02 +000051
Felipe Leme59c44642017-10-13 11:05:05 -070052 /**
Felipe Lemed37f53e2017-12-07 10:41:10 -080053 * Flag used to generate {@link FillEventHistory.Event events} of type
54 * {@link FillEventHistory.Event#TYPE_CONTEXT_COMMITTED}&mdash;if this flag is not passed to
55 * {@link Builder#setFlags(int)}, these events are not generated.
Felipe Leme59c44642017-10-13 11:05:05 -070056 */
57 public static final int FLAG_TRACK_CONTEXT_COMMITED = 0x1;
58
Felipe Leme17292d12017-10-24 14:03:10 -070059 /**
Felipe Lemed37f53e2017-12-07 10:41:10 -080060 * Flag used to change the behavior of {@link FillResponse.Builder#disableAutofill(long)}&mdash;
61 * when this flag is passed to {@link Builder#setFlags(int)}, autofill is disabled only for the
62 * activiy that generated the {@link FillRequest}, not the whole app.
Felipe Leme17292d12017-10-24 14:03:10 -070063 */
64 public static final int FLAG_DISABLE_ACTIVITY_ONLY = 0x2;
65
Felipe Leme59c44642017-10-13 11:05:05 -070066 /** @hide */
Jeff Sharkeyce8db992017-12-13 20:05:05 -070067 @IntDef(flag = true, prefix = { "FLAG_" }, value = {
Felipe Leme17292d12017-10-24 14:03:10 -070068 FLAG_TRACK_CONTEXT_COMMITED,
69 FLAG_DISABLE_ACTIVITY_ONLY
Felipe Leme59c44642017-10-13 11:05:05 -070070 })
71 @Retention(RetentionPolicy.SOURCE)
72 @interface FillResponseFlags {}
73
Felipe Lemecb96f392017-07-25 15:19:30 -070074 private final @Nullable ParceledListSlice<Dataset> mDatasets;
Svet Ganov013efe12017-04-13 21:56:16 -070075 private final @Nullable SaveInfo mSaveInfo;
76 private final @Nullable Bundle mClientState;
77 private final @Nullable RemoteViews mPresentation;
Felipe Lemee9324782017-11-21 18:00:56 -080078 private final @Nullable RemoteViews mHeader;
79 private final @Nullable RemoteViews mFooter;
Svet Ganov013efe12017-04-13 21:56:16 -070080 private final @Nullable IntentSender mAuthentication;
81 private final @Nullable AutofillId[] mAuthenticationIds;
Felipe Leme01ded102017-04-20 15:58:30 -070082 private final @Nullable AutofillId[] mIgnoredIds;
Felipe Leme17292d12017-10-24 14:03:10 -070083 private final long mDisableDuration;
Felipe Leme452886a2017-11-27 13:09:13 -080084 private final @Nullable AutofillId[] mFieldClassificationIds;
Felipe Leme59c44642017-10-13 11:05:05 -070085 private final int mFlags;
Philip P. Moltmannc7619632017-04-25 11:57:37 -070086 private int mRequestId;
Adam He2cc31462018-11-12 16:26:14 -080087 private final @Nullable UserData mUserData;
Felipe Leme6d553872016-12-08 17:13:25 -080088
Svet Ganov0f4928f2017-02-02 20:02:51 -080089 private FillResponse(@NonNull Builder builder) {
Felipe Lemecb96f392017-07-25 15:19:30 -070090 mDatasets = (builder.mDatasets != null) ? new ParceledListSlice<>(builder.mDatasets) : null;
Felipe Leme6ee1ed42017-03-07 19:07:21 -080091 mSaveInfo = builder.mSaveInfo;
Felipe Leme6f12e672017-10-20 13:04:19 -070092 mClientState = builder.mClientState;
Svet Ganov00c771dc2017-02-19 00:06:22 -080093 mPresentation = builder.mPresentation;
Felipe Lemee9324782017-11-21 18:00:56 -080094 mHeader = builder.mHeader;
95 mFooter = builder.mFooter;
Svet Ganov0f4928f2017-02-02 20:02:51 -080096 mAuthentication = builder.mAuthentication;
Felipe Lemeb3c35372017-04-03 10:20:34 -070097 mAuthenticationIds = builder.mAuthenticationIds;
Felipe Leme01ded102017-04-20 15:58:30 -070098 mIgnoredIds = builder.mIgnoredIds;
Felipe Leme17292d12017-10-24 14:03:10 -070099 mDisableDuration = builder.mDisableDuration;
Felipe Leme452886a2017-11-27 13:09:13 -0800100 mFieldClassificationIds = builder.mFieldClassificationIds;
Felipe Leme59c44642017-10-13 11:05:05 -0700101 mFlags = builder.mFlags;
Philip P. Moltmannc7619632017-04-25 11:57:37 -0700102 mRequestId = INVALID_REQUEST_ID;
Adam He2cc31462018-11-12 16:26:14 -0800103 mUserData = builder.mUserData;
Felipe Leme6d553872016-12-08 17:13:25 -0800104 }
105
106 /** @hide */
Svet Ganov013efe12017-04-13 21:56:16 -0700107 public @Nullable Bundle getClientState() {
108 return mClientState;
Felipe Leme6d553872016-12-08 17:13:25 -0800109 }
110
Felipe Leme436ab6a2016-12-15 11:56:15 -0800111 /** @hide */
Felipe Lemecb96f392017-07-25 15:19:30 -0700112 public @Nullable List<Dataset> getDatasets() {
113 return (mDatasets != null) ? mDatasets.getList() : null;
Felipe Leme436ab6a2016-12-15 11:56:15 -0800114 }
115
116 /** @hide */
Felipe Lemef69761f2017-02-23 17:52:01 -0800117 public @Nullable SaveInfo getSaveInfo() {
118 return mSaveInfo;
Felipe Leme436ab6a2016-12-15 11:56:15 -0800119 }
120
121 /** @hide */
Svet Ganov00c771dc2017-02-19 00:06:22 -0800122 public @Nullable RemoteViews getPresentation() {
123 return mPresentation;
124 }
125
126 /** @hide */
Felipe Lemee9324782017-11-21 18:00:56 -0800127 public @Nullable RemoteViews getHeader() {
128 return mHeader;
129 }
130
131 /** @hide */
132 public @Nullable RemoteViews getFooter() {
133 return mFooter;
134 }
135
136 /** @hide */
Svet Ganov0f4928f2017-02-02 20:02:51 -0800137 public @Nullable IntentSender getAuthentication() {
138 return mAuthentication;
Felipe Leme436ab6a2016-12-15 11:56:15 -0800139 }
140
Felipe Lemeb3c35372017-04-03 10:20:34 -0700141 /** @hide */
142 public @Nullable AutofillId[] getAuthenticationIds() {
143 return mAuthenticationIds;
144 }
145
Felipe Leme01ded102017-04-20 15:58:30 -0700146 /** @hide */
147 public @Nullable AutofillId[] getIgnoredIds() {
148 return mIgnoredIds;
149 }
150
Felipe Leme59c44642017-10-13 11:05:05 -0700151 /** @hide */
Felipe Leme17292d12017-10-24 14:03:10 -0700152 public long getDisableDuration() {
153 return mDisableDuration;
154 }
155
156 /** @hide */
Felipe Leme452886a2017-11-27 13:09:13 -0800157 public @Nullable AutofillId[] getFieldClassificationIds() {
158 return mFieldClassificationIds;
Felipe Leme24d71732017-10-20 10:32:57 -0700159 }
160
161 /** @hide */
Adam He2cc31462018-11-12 16:26:14 -0800162 public @Nullable UserData getUserData() {
163 return mUserData;
164 }
165
166 /** @hide */
Felipe Leme78172e72017-12-08 17:01:15 -0800167 @TestApi
Felipe Leme59c44642017-10-13 11:05:05 -0700168 public int getFlags() {
169 return mFlags;
170 }
171
Felipe Leme6d553872016-12-08 17:13:25 -0800172 /**
Philip P. Moltmannc7619632017-04-25 11:57:37 -0700173 * Associates a {@link FillResponse} to a request.
174 *
175 * <p>Set inside of the {@link FillCallback} code, not the {@link AutofillService}.
176 *
177 * @param requestId The id of the request to associate the response to.
178 *
179 * @hide
180 */
181 public void setRequestId(int requestId) {
182 mRequestId = requestId;
183 }
184
185 /** @hide */
186 public int getRequestId() {
187 return mRequestId;
188 }
189
190 /**
Svet Ganov0f4928f2017-02-02 20:02:51 -0800191 * Builder for {@link FillResponse} objects. You must to provide at least
Svet Ganov00c771dc2017-02-19 00:06:22 -0800192 * one dataset or set an authentication intent with a presentation view.
Felipe Leme6d553872016-12-08 17:13:25 -0800193 */
194 public static final class Builder {
Felipe Lemefe35e692017-02-21 13:44:50 -0800195 private ArrayList<Dataset> mDatasets;
Felipe Lemef69761f2017-02-23 17:52:01 -0800196 private SaveInfo mSaveInfo;
Felipe Leme6f12e672017-10-20 13:04:19 -0700197 private Bundle mClientState;
Svet Ganov00c771dc2017-02-19 00:06:22 -0800198 private RemoteViews mPresentation;
Felipe Lemee9324782017-11-21 18:00:56 -0800199 private RemoteViews mHeader;
200 private RemoteViews mFooter;
Svet Ganov0f4928f2017-02-02 20:02:51 -0800201 private IntentSender mAuthentication;
Felipe Lemeb3c35372017-04-03 10:20:34 -0700202 private AutofillId[] mAuthenticationIds;
Felipe Leme01ded102017-04-20 15:58:30 -0700203 private AutofillId[] mIgnoredIds;
Felipe Leme17292d12017-10-24 14:03:10 -0700204 private long mDisableDuration;
Felipe Leme452886a2017-11-27 13:09:13 -0800205 private AutofillId[] mFieldClassificationIds;
Felipe Leme59c44642017-10-13 11:05:05 -0700206 private int mFlags;
Svet Ganov0f4928f2017-02-02 20:02:51 -0800207 private boolean mDestroyed;
Adam He2cc31462018-11-12 16:26:14 -0800208 private UserData mUserData;
Felipe Leme436ab6a2016-12-15 11:56:15 -0800209
Felipe Leme436ab6a2016-12-15 11:56:15 -0800210 /**
Felipe Leme601d2202017-11-17 08:21:23 -0800211 * Triggers a custom UI before before autofilling the screen with any data set in this
212 * response.
213 *
214 * <p><b>Note:</b> Although the name of this method suggests that it should be used just for
215 * authentication flow, it can be used for other advanced flows; see {@link AutofillService}
216 * for examples.
Svet Ganov0f4928f2017-02-02 20:02:51 -0800217 *
Svet Ganov782043c2017-02-11 00:52:02 +0000218 * <p>This is typically useful when a user interaction is required to unlock their
219 * data vault if you encrypt the data set labels and data set data. It is recommended
220 * to encrypt only the sensitive data and not the data set labels which would allow
221 * auth on the data set level leading to a better user experience. Note that if you
222 * use sensitive data as a label, for example an email address, then it should also
223 * be encrypted. The provided {@link android.app.PendingIntent intent} must be an
Felipe Leme2ef19c12017-06-05 11:32:32 -0700224 * {@link Activity} which implements your authentication flow. Also if you provide an auth
Svet Ganov00c771dc2017-02-19 00:06:22 -0800225 * intent you also need to specify the presentation view to be shown in the fill UI
Felipe Lemeb3c35372017-04-03 10:20:34 -0700226 * for the user to trigger your authentication flow.
Svet Ganov0f4928f2017-02-02 20:02:51 -0800227 *
Felipe Leme640f30a2017-03-06 15:44:06 -0800228 * <p>When a user triggers autofill, the system launches the provided intent
Felipe Lemecb96f392017-07-25 15:19:30 -0700229 * whose extras will have the
230 * {@link android.view.autofill.AutofillManager#EXTRA_ASSIST_STRUCTURE screen
Svetoslav Ganova9379d02017-05-09 17:40:24 -0700231 * content} and your {@link android.view.autofill.AutofillManager#EXTRA_CLIENT_STATE
Felipe Leme2ef19c12017-06-05 11:32:32 -0700232 * client state}. Once you complete your authentication flow you should set the
Felipe Lemeb0a40572017-09-05 13:11:37 -0700233 * {@link Activity} result to {@link android.app.Activity#RESULT_OK} and set the
234 * {@link android.view.autofill.AutofillManager#EXTRA_AUTHENTICATION_RESULT} extra
235 * with the fully populated {@link FillResponse response} (or {@code null} if the screen
236 * cannot be autofilled).
237 *
238 * <p>For example, if you provided an empty {@link FillResponse response} because the
Svet Ganov782043c2017-02-11 00:52:02 +0000239 * user's data was locked and marked that the response needs an authentication then
240 * in the response returned if authentication succeeds you need to provide all
241 * available data sets some of which may need to be further authenticated, for
Felipe Lemeb3c35372017-04-03 10:20:34 -0700242 * example a credit card whose CVV needs to be entered.
Svet Ganov0f4928f2017-02-02 20:02:51 -0800243 *
Svet Ganoveb495152017-02-21 17:47:07 -0800244 * <p>If you provide an authentication intent you must also provide a presentation
245 * which is used to visualize visualize the response for triggering the authentication
Felipe Lemeb3c35372017-04-03 10:20:34 -0700246 * flow.
Svet Ganoveb495152017-02-21 17:47:07 -0800247 *
Felipe Leme17292d12017-10-24 14:03:10 -0700248 * <p><b>Note:</b> Do not make the provided pending intent
Svet Ganov0f4928f2017-02-02 20:02:51 -0800249 * immutable by using {@link android.app.PendingIntent#FLAG_IMMUTABLE} as the
Felipe Lemeb3c35372017-04-03 10:20:34 -0700250 * platform needs to fill in the authentication arguments.
Svet Ganov0f4928f2017-02-02 20:02:51 -0800251 *
Dake Gu36b86c22018-04-16 12:49:30 -0700252 * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
253 * or background color: Autofill on different platforms may have different themes.
254 *
Svet Ganov782043c2017-02-11 00:52:02 +0000255 * @param authentication Intent to an activity with your authentication flow.
Svet Ganoveb495152017-02-21 17:47:07 -0800256 * @param presentation The presentation to visualize the response.
Felipe Leme2c888422017-10-26 12:46:35 -0700257 * @param ids id of Views that when focused will display the authentication UI.
Svet Ganov0f4928f2017-02-02 20:02:51 -0800258 *
Felipe Lemeb3c35372017-04-03 10:20:34 -0700259 * @return This builder.
Felipe Leme25960892018-03-21 12:56:05 -0700260 *
261 * @throws IllegalArgumentException if any of the following occurs:
262 * <ul>
263 * <li>{@code ids} is {@code null}</li>
264 * <li>{@code ids} is empty</li>
265 * <li>{@code ids} contains a {@code null} element</li>
266 * <li>both {@code authentication} and {@code presentation} are {@code null}</li>
267 * <li>both {@code authentication} and {@code presentation} are non-{@code null}</li>
268 * </ul>
Felipe Lemec2430f3c4d62017-05-01 09:35:33 -0700269 *
Felipe Lemee9324782017-11-21 18:00:56 -0800270 * @throws IllegalStateException if a {@link #setHeader(RemoteViews) header} or a
271 * {@link #setFooter(RemoteViews) footer} are already set for this builder.
272 *
Svet Ganov0f4928f2017-02-02 20:02:51 -0800273 * @see android.app.PendingIntent#getIntentSender()
274 */
Felipe Leme8e156a62019-03-01 17:16:40 -0800275 @NonNull
276 public Builder setAuthentication(@NonNull AutofillId[] ids,
Felipe Lemeb3c35372017-04-03 10:20:34 -0700277 @Nullable IntentSender authentication, @Nullable RemoteViews presentation) {
Svet Ganov0f4928f2017-02-02 20:02:51 -0800278 throwIfDestroyed();
Felipe Leme17292d12017-10-24 14:03:10 -0700279 throwIfDisableAutofillCalled();
Felipe Lemee9324782017-11-21 18:00:56 -0800280 if (mHeader != null || mFooter != null) {
281 throw new IllegalStateException("Already called #setHeader() or #setFooter()");
282 }
283
Svet Ganoveb495152017-02-21 17:47:07 -0800284 if (authentication == null ^ presentation == null) {
285 throw new IllegalArgumentException("authentication and presentation"
286 + " must be both non-null or null");
287 }
Svet Ganov0f4928f2017-02-02 20:02:51 -0800288 mAuthentication = authentication;
Svet Ganoveb495152017-02-21 17:47:07 -0800289 mPresentation = presentation;
Felipe Leme25960892018-03-21 12:56:05 -0700290 mAuthenticationIds = assertValid(ids);
Felipe Leme436ab6a2016-12-15 11:56:15 -0800291 return this;
292 }
Felipe Leme6d553872016-12-08 17:13:25 -0800293
294 /**
Felipe Lemee5f9c302017-04-18 17:48:49 -0700295 * Specifies views that should not trigger new
296 * {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
297 * FillCallback)} requests.
298 *
Felipe Leme2ef19c12017-06-05 11:32:32 -0700299 * <p>This is typically used when the service cannot autofill the view; for example, a
300 * text field representing the result of a Captcha challenge.
Felipe Lemee5f9c302017-04-18 17:48:49 -0700301 */
Felipe Leme8e156a62019-03-01 17:16:40 -0800302 @NonNull
Felipe Lemee5f9c302017-04-18 17:48:49 -0700303 public Builder setIgnoredIds(AutofillId...ids) {
Felipe Leme17292d12017-10-24 14:03:10 -0700304 throwIfDestroyed();
Felipe Leme01ded102017-04-20 15:58:30 -0700305 mIgnoredIds = ids;
Felipe Lemee5f9c302017-04-18 17:48:49 -0700306 return this;
307 }
308
309 /**
Svet Ganov00c771dc2017-02-19 00:06:22 -0800310 * Adds a new {@link Dataset} to this response.
Felipe Leme6d553872016-12-08 17:13:25 -0800311 *
Felipe Lemecb96f392017-07-25 15:19:30 -0700312 * <p><b>Note: </b> on Android {@link android.os.Build.VERSION_CODES#O}, the total number of
313 * datasets is limited by the Binder transaction size, so it's recommended to keep it
314 * small (in the range of 10-20 at most) and use pagination by adding a fake
Felipe Lemec0c6ab42017-07-28 09:32:42 -0700315 * {@link Dataset.Builder#setAuthentication(IntentSender) authenticated dataset} at the end
316 * with a presentation string like "Next 10" that would return a new {@link FillResponse}
317 * with the next 10 datasets, and so on. This limitation was lifted on
Felipe Lemecb96f392017-07-25 15:19:30 -0700318 * Android {@link android.os.Build.VERSION_CODES#O_MR1}, although the Binder transaction
319 * size can still be reached if each dataset itself is too big.
320 *
Svet Ganov00c771dc2017-02-19 00:06:22 -0800321 * @return This builder.
Felipe Leme6d553872016-12-08 17:13:25 -0800322 */
Felipe Leme8e156a62019-03-01 17:16:40 -0800323 @NonNull
324 public Builder addDataset(@Nullable Dataset dataset) {
Svet Ganov0f4928f2017-02-02 20:02:51 -0800325 throwIfDestroyed();
Felipe Leme17292d12017-10-24 14:03:10 -0700326 throwIfDisableAutofillCalled();
Svet Ganov0f4928f2017-02-02 20:02:51 -0800327 if (dataset == null) {
328 return this;
329 }
330 if (mDatasets == null) {
Felipe Lemefe35e692017-02-21 13:44:50 -0800331 mDatasets = new ArrayList<>();
Svet Ganov0f4928f2017-02-02 20:02:51 -0800332 }
Svet Ganov0f4928f2017-02-02 20:02:51 -0800333 if (!mDatasets.add(dataset)) {
334 return this;
335 }
Felipe Lemef69761f2017-02-23 17:52:01 -0800336 return this;
337 }
338
Felipe Leme6d553872016-12-08 17:13:25 -0800339 /**
Felipe Lemef69761f2017-02-23 17:52:01 -0800340 * Sets the {@link SaveInfo} associated with this response.
Felipe Leme6d553872016-12-08 17:13:25 -0800341 *
Svet Ganov00c771dc2017-02-19 00:06:22 -0800342 * @return This builder.
Felipe Leme6d553872016-12-08 17:13:25 -0800343 */
Felipe Lemef69761f2017-02-23 17:52:01 -0800344 public @NonNull Builder setSaveInfo(@NonNull SaveInfo saveInfo) {
Svet Ganov0f4928f2017-02-02 20:02:51 -0800345 throwIfDestroyed();
Felipe Leme17292d12017-10-24 14:03:10 -0700346 throwIfDisableAutofillCalled();
Felipe Lemef69761f2017-02-23 17:52:01 -0800347 mSaveInfo = saveInfo;
Felipe Leme6d553872016-12-08 17:13:25 -0800348 return this;
349 }
350
Felipe Lemee5f9c302017-04-18 17:48:49 -0700351 /**
Felipe Lemed37f53e2017-12-07 10:41:10 -0800352 * Sets a bundle with state that is passed to subsequent APIs that manipulate this response.
353 *
354 * <p>You can use this bundle to store intermediate state that is passed to subsequent calls
355 * to {@link AutofillService#onFillRequest(FillRequest, android.os.CancellationSignal,
356 * FillCallback)} and {@link AutofillService#onSaveRequest(SaveRequest, SaveCallback)}, and
357 * you can also retrieve it by calling {@link FillEventHistory.Event#getClientState()}.
Svet Ganov00c771dc2017-02-19 00:06:22 -0800358 *
Felipe Leme52d5d3d2017-04-04 13:10:58 -0700359 * <p>If this method is called on multiple {@link FillResponse} objects for the same
Felipe Leme2ef19c12017-06-05 11:32:32 -0700360 * screen, just the latest bundle is passed back to the service.
Felipe Leme52d5d3d2017-04-04 13:10:58 -0700361 *
Svet Ganov013efe12017-04-13 21:56:16 -0700362 * @param clientState The custom client state.
Svet Ganov00c771dc2017-02-19 00:06:22 -0800363 * @return This builder.
Felipe Leme6d553872016-12-08 17:13:25 -0800364 */
Felipe Leme8e156a62019-03-01 17:16:40 -0800365 @NonNull
Svet Ganov013efe12017-04-13 21:56:16 -0700366 public Builder setClientState(@Nullable Bundle clientState) {
Svet Ganov0f4928f2017-02-02 20:02:51 -0800367 throwIfDestroyed();
Felipe Lemeb57080e2017-11-20 17:10:41 -0800368 throwIfDisableAutofillCalled();
Felipe Leme6f12e672017-10-20 13:04:19 -0700369 mClientState = clientState;
Felipe Leme6d553872016-12-08 17:13:25 -0800370 return this;
371 }
372
373 /**
Felipe Leme78172e72017-12-08 17:01:15 -0800374 * Sets which fields are used for
375 * <a href="AutofillService.html#FieldClassification">field classification</a>
Felipe Leme452886a2017-11-27 13:09:13 -0800376 *
Felipe Leme78172e72017-12-08 17:01:15 -0800377 * <p><b>Note:</b> This method automatically adds the
378 * {@link FillResponse#FLAG_TRACK_CONTEXT_COMMITED} to the {@link #setFlags(int) flags}.
379
Felipe Leme452886a2017-11-27 13:09:13 -0800380 * @throws IllegalArgumentException is length of {@code ids} args is more than
381 * {@link UserData#getMaxFieldClassificationIdsSize()}.
382 * @throws IllegalStateException if {@link #build()} or {@link #disableAutofill(long)} was
383 * already called.
384 * @throws NullPointerException if {@code ids} or any element on it is {@code null}.
Felipe Leme24d71732017-10-20 10:32:57 -0700385 */
Felipe Leme8e156a62019-03-01 17:16:40 -0800386 @NonNull
Felipe Leme452886a2017-11-27 13:09:13 -0800387 public Builder setFieldClassificationIds(@NonNull AutofillId... ids) {
Felipe Leme24d71732017-10-20 10:32:57 -0700388 throwIfDestroyed();
389 throwIfDisableAutofillCalled();
Felipe Leme452886a2017-11-27 13:09:13 -0800390 Preconditions.checkArrayElementsNotNull(ids, "ids");
391 Preconditions.checkArgumentInRange(ids.length, 1,
392 UserData.getMaxFieldClassificationIdsSize(), "ids length");
393 mFieldClassificationIds = ids;
Felipe Leme78172e72017-12-08 17:01:15 -0800394 mFlags |= FLAG_TRACK_CONTEXT_COMMITED;
Felipe Leme24d71732017-10-20 10:32:57 -0700395 return this;
396 }
397
398 /**
Felipe Leme59c44642017-10-13 11:05:05 -0700399 * Sets flags changing the response behavior.
400 *
Felipe Leme17292d12017-10-24 14:03:10 -0700401 * @param flags a combination of {@link #FLAG_TRACK_CONTEXT_COMMITED} and
402 * {@link #FLAG_DISABLE_ACTIVITY_ONLY}, or {@code 0}.
Felipe Leme59c44642017-10-13 11:05:05 -0700403 *
404 * @return This builder.
405 */
Felipe Leme8e156a62019-03-01 17:16:40 -0800406 @NonNull
Felipe Leme59c44642017-10-13 11:05:05 -0700407 public Builder setFlags(@FillResponseFlags int flags) {
408 throwIfDestroyed();
Felipe Leme17292d12017-10-24 14:03:10 -0700409 mFlags = Preconditions.checkFlagsArgument(flags,
410 FLAG_TRACK_CONTEXT_COMMITED | FLAG_DISABLE_ACTIVITY_ONLY);
411 return this;
412 }
413
414 /**
415 * Disables autofill for the app or activity.
416 *
417 * <p>This method is useful to optimize performance in cases where the service knows it
418 * can not autofill an app&mdash;for example, when the service has a list of "blacklisted"
419 * apps such as office suites.
420 *
421 * <p>By default, it disables autofill for all activities in the app, unless the response is
422 * {@link #setFlags(int) flagged} with {@link #FLAG_DISABLE_ACTIVITY_ONLY}.
423 *
424 * <p>Autofill for the app or activity is automatically re-enabled after any of the
425 * following conditions:
426 *
427 * <ol>
428 * <li>{@code duration} milliseconds have passed.
429 * <li>The autofill service for the user has changed.
430 * <li>The device has rebooted.
431 * </ol>
432 *
433 * <p><b>Note:</b> Activities that are running when autofill is re-enabled remain
434 * disabled for autofill until they finish and restart.
435 *
436 * @param duration duration to disable autofill, in milliseconds.
437 *
438 * @return this builder
439 *
440 * @throws IllegalArgumentException if {@code duration} is not a positive number.
441 * @throws IllegalStateException if either {@link #addDataset(Dataset)},
Felipe Lemeb57080e2017-11-20 17:10:41 -0800442 * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
Felipe Leme452886a2017-11-27 13:09:13 -0800443 * {@link #setSaveInfo(SaveInfo)}, {@link #setClientState(Bundle)}, or
Felipe Leme78172e72017-12-08 17:01:15 -0800444 * {@link #setFieldClassificationIds(AutofillId...)} was already called.
Felipe Leme17292d12017-10-24 14:03:10 -0700445 */
Felipe Leme8e156a62019-03-01 17:16:40 -0800446 @NonNull
Felipe Leme17292d12017-10-24 14:03:10 -0700447 public Builder disableAutofill(long duration) {
448 throwIfDestroyed();
449 if (duration <= 0) {
450 throw new IllegalArgumentException("duration must be greater than 0");
451 }
Felipe Leme24d71732017-10-20 10:32:57 -0700452 if (mAuthentication != null || mDatasets != null || mSaveInfo != null
Felipe Leme452886a2017-11-27 13:09:13 -0800453 || mFieldClassificationIds != null || mClientState != null) {
Felipe Leme17292d12017-10-24 14:03:10 -0700454 throw new IllegalStateException("disableAutofill() must be the only method called");
455 }
456
457 mDisableDuration = duration;
Felipe Leme59c44642017-10-13 11:05:05 -0700458 return this;
459 }
460
461 /**
Felipe Lemee9324782017-11-21 18:00:56 -0800462 * Sets a header to be shown as the first element in the list of datasets.
463 *
464 * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset},
465 * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this
466 * method should only be used on {@link FillResponse FillResponses} that do not require
467 * authentication (as the header could have been set directly in the main presentation in
468 * these cases).
469 *
Dake Gu36b86c22018-04-16 12:49:30 -0700470 * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
471 * or background color: Autofill on different platforms may have different themes.
472 *
Felipe Lemee9324782017-11-21 18:00:56 -0800473 * @param header a presentation to represent the header. This presentation is not clickable
474 * &mdash;calling
475 * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
476 * have no effect.
477 *
478 * @return this builder
479 *
480 * @throws IllegalStateException if an
481 * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews) authentication} was
482 * already set for this builder.
483 */
484 // TODO(b/69796626): make it sticky / update javadoc
Felipe Leme8e156a62019-03-01 17:16:40 -0800485 @NonNull
Felipe Lemee9324782017-11-21 18:00:56 -0800486 public Builder setHeader(@NonNull RemoteViews header) {
487 throwIfDestroyed();
488 throwIfAuthenticationCalled();
489 mHeader = Preconditions.checkNotNull(header);
490 return this;
491 }
492
493 /**
494 * Sets a footer to be shown as the last element in the list of datasets.
495 *
496 * <p>When this method is called, you must also {@link #addDataset(Dataset) add a dataset},
497 * otherwise {@link #build()} throws an {@link IllegalStateException}. Similarly, this
498 * method should only be used on {@link FillResponse FillResponses} that do not require
499 * authentication (as the footer could have been set directly in the main presentation in
500 * these cases).
501 *
Dake Gu36b86c22018-04-16 12:49:30 -0700502 * <p>Theme does not work with RemoteViews layout. Avoid hardcoded text color
503 * or background color: Autofill on different platforms may have different themes.
504 *
Felipe Lemee9324782017-11-21 18:00:56 -0800505 * @param footer a presentation to represent the footer. This presentation is not clickable
506 * &mdash;calling
507 * {@link RemoteViews#setOnClickPendingIntent(int, android.app.PendingIntent)} on it would
508 * have no effect.
509 *
510 * @return this builder
511 *
512 * @throws IllegalStateException if the FillResponse
513 * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
514 * requires authentication}.
515 */
516 // TODO(b/69796626): make it sticky / update javadoc
Felipe Leme8e156a62019-03-01 17:16:40 -0800517 @NonNull
Felipe Lemee9324782017-11-21 18:00:56 -0800518 public Builder setFooter(@NonNull RemoteViews footer) {
519 throwIfDestroyed();
520 throwIfAuthenticationCalled();
521 mFooter = Preconditions.checkNotNull(footer);
522 return this;
523 }
524
525 /**
Adam He2cc31462018-11-12 16:26:14 -0800526 * Sets a specific {@link UserData} for field classification for this request only.
527 *
Adam He1cb6f802018-12-10 15:15:49 -0800528 * <p>Any fields in this UserData will override corresponding fields in the generic
529 * UserData object
530 *
Adam He2cc31462018-11-12 16:26:14 -0800531 * @return this builder
532 * @throws IllegalStateException if the FillResponse
533 * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)
534 * requires authentication}.
535 */
Felipe Leme8e156a62019-03-01 17:16:40 -0800536 @NonNull
Adam He2cc31462018-11-12 16:26:14 -0800537 public Builder setUserData(@NonNull UserData userData) {
538 throwIfDestroyed();
539 throwIfAuthenticationCalled();
540 mUserData = Preconditions.checkNotNull(userData);
541 return this;
542 }
543
544 /**
Felipe Leme2ef19c12017-06-05 11:32:32 -0700545 * Builds a new {@link FillResponse} instance.
546 *
Felipe Leme17292d12017-10-24 14:03:10 -0700547 * @throws IllegalStateException if any of the following conditions occur:
548 * <ol>
549 * <li>{@link #build()} was already called.
550 * <li>No call was made to {@link #addDataset(Dataset)},
551 * {@link #setAuthentication(AutofillId[], IntentSender, RemoteViews)},
Felipe Lemeb57080e2017-11-20 17:10:41 -0800552 * {@link #setSaveInfo(SaveInfo)}, {@link #disableAutofill(long)},
Felipe Leme452886a2017-11-27 13:09:13 -0800553 * {@link #setClientState(Bundle)},
Felipe Leme78172e72017-12-08 17:01:15 -0800554 * or {@link #setFieldClassificationIds(AutofillId...)}.
Felipe Lemee9324782017-11-21 18:00:56 -0800555 * <li>{@link #setHeader(RemoteViews)} or {@link #setFooter(RemoteViews)} is called
556 * without any previous calls to {@link #addDataset(Dataset)}.
Felipe Leme17292d12017-10-24 14:03:10 -0700557 * </ol>
Svet Ganov00c771dc2017-02-19 00:06:22 -0800558 *
559 * @return A built response.
Felipe Leme6d553872016-12-08 17:13:25 -0800560 */
Felipe Leme8e156a62019-03-01 17:16:40 -0800561 @NonNull
Felipe Leme6d553872016-12-08 17:13:25 -0800562 public FillResponse build() {
Svet Ganov0f4928f2017-02-02 20:02:51 -0800563 throwIfDestroyed();
Felipe Leme17292d12017-10-24 14:03:10 -0700564 if (mAuthentication == null && mDatasets == null && mSaveInfo == null
Felipe Leme452886a2017-11-27 13:09:13 -0800565 && mDisableDuration == 0 && mFieldClassificationIds == null
566 && mClientState == null) {
Felipe Leme24d71732017-10-20 10:32:57 -0700567 throw new IllegalStateException("need to provide: at least one DataSet, or a "
568 + "SaveInfo, or an authentication with a presentation, "
Felipe Lemeb57080e2017-11-20 17:10:41 -0800569 + "or a FieldsDetection, or a client state, or disable autofill");
Svet Ganov00c771dc2017-02-19 00:06:22 -0800570 }
Felipe Lemee9324782017-11-21 18:00:56 -0800571 if (mDatasets == null && (mHeader != null || mFooter != null)) {
572 throw new IllegalStateException(
573 "must add at least 1 dataset when using header or footer");
574 }
Svet Ganov0f4928f2017-02-02 20:02:51 -0800575 mDestroyed = true;
Felipe Leme6d553872016-12-08 17:13:25 -0800576 return new FillResponse(this);
577 }
Svet Ganov0f4928f2017-02-02 20:02:51 -0800578
579 private void throwIfDestroyed() {
580 if (mDestroyed) {
581 throw new IllegalStateException("Already called #build()");
582 }
583 }
Felipe Leme17292d12017-10-24 14:03:10 -0700584
585 private void throwIfDisableAutofillCalled() {
586 if (mDisableDuration > 0) {
587 throw new IllegalStateException("Already called #disableAutofill()");
588 }
589 }
Felipe Lemee9324782017-11-21 18:00:56 -0800590
591 private void throwIfAuthenticationCalled() {
592 if (mAuthentication != null) {
593 throw new IllegalStateException("Already called #setAuthentication()");
594 }
595 }
Felipe Leme6d553872016-12-08 17:13:25 -0800596 }
597
598 /////////////////////////////////////
Felipe Lemeb3c35372017-04-03 10:20:34 -0700599 // Object "contract" methods. //
Felipe Leme6d553872016-12-08 17:13:25 -0800600 /////////////////////////////////////
601 @Override
602 public String toString() {
Felipe Leme9f9ee252017-04-27 13:56:22 -0700603 if (!sDebug) return super.toString();
Felipe Lemef69761f2017-02-23 17:52:01 -0800604
Felipe Lemec2430f3c4d62017-05-01 09:35:33 -0700605 // TODO: create a dump() method instead
Felipe Lemeda9ea342018-03-22 15:19:51 -0700606 final StringBuilder builder = new StringBuilder(
607 "FillResponse : [mRequestId=" + mRequestId);
608 if (mDatasets != null) {
609 builder.append(", datasets=").append(mDatasets.getList());
610 }
611 if (mSaveInfo != null) {
612 builder.append(", saveInfo=").append(mSaveInfo);
613 }
614 if (mClientState != null) {
615 builder.append(", hasClientState");
616 }
617 if (mPresentation != null) {
618 builder.append(", hasPresentation");
619 }
620 if (mHeader != null) {
621 builder.append(", hasHeader");
622 }
623 if (mFooter != null) {
624 builder.append(", hasFooter");
625 }
626 if (mAuthentication != null) {
627 builder.append(", hasAuthentication");
628 }
629 if (mAuthenticationIds != null) {
630 builder.append(", authenticationIds=").append(Arrays.toString(mAuthenticationIds));
631 }
632 builder.append(", disableDuration=").append(mDisableDuration);
633 if (mFlags != 0) {
634 builder.append(", flags=").append(mFlags);
635 }
636 if (mFieldClassificationIds != null) {
637 builder.append(Arrays.toString(mFieldClassificationIds));
638 }
Adam He2cc31462018-11-12 16:26:14 -0800639 if (mUserData != null) {
640 builder.append(", userData=").append(mUserData);
641 }
Felipe Lemeda9ea342018-03-22 15:19:51 -0700642 return builder.append("]").toString();
Felipe Leme6d553872016-12-08 17:13:25 -0800643 }
644
645 /////////////////////////////////////
Felipe Lemeb3c35372017-04-03 10:20:34 -0700646 // Parcelable "contract" methods. //
Felipe Leme6d553872016-12-08 17:13:25 -0800647 /////////////////////////////////////
648
649 @Override
650 public int describeContents() {
651 return 0;
652 }
653
654 @Override
655 public void writeToParcel(Parcel parcel, int flags) {
Felipe Lemecb96f392017-07-25 15:19:30 -0700656 parcel.writeParcelable(mDatasets, flags);
Felipe Lemef69761f2017-02-23 17:52:01 -0800657 parcel.writeParcelable(mSaveInfo, flags);
Svet Ganov013efe12017-04-13 21:56:16 -0700658 parcel.writeParcelable(mClientState, flags);
Felipe Lemeb3c35372017-04-03 10:20:34 -0700659 parcel.writeParcelableArray(mAuthenticationIds, flags);
Svet Ganov00c771dc2017-02-19 00:06:22 -0800660 parcel.writeParcelable(mAuthentication, flags);
Svet Ganoveb495152017-02-21 17:47:07 -0800661 parcel.writeParcelable(mPresentation, flags);
Felipe Lemee9324782017-11-21 18:00:56 -0800662 parcel.writeParcelable(mHeader, flags);
663 parcel.writeParcelable(mFooter, flags);
Adam He2cc31462018-11-12 16:26:14 -0800664 parcel.writeParcelable(mUserData, flags);
Felipe Leme01ded102017-04-20 15:58:30 -0700665 parcel.writeParcelableArray(mIgnoredIds, flags);
Felipe Leme17292d12017-10-24 14:03:10 -0700666 parcel.writeLong(mDisableDuration);
Felipe Leme452886a2017-11-27 13:09:13 -0800667 parcel.writeParcelableArray(mFieldClassificationIds, flags);
Felipe Leme59c44642017-10-13 11:05:05 -0700668 parcel.writeInt(mFlags);
Philip P. Moltmannc7619632017-04-25 11:57:37 -0700669 parcel.writeInt(mRequestId);
Felipe Leme6d553872016-12-08 17:13:25 -0800670 }
671
Jeff Sharkey9e8f83d2019-02-28 12:06:45 -0700672 public static final @android.annotation.NonNull Parcelable.Creator<FillResponse> CREATOR =
Felipe Leme6d553872016-12-08 17:13:25 -0800673 new Parcelable.Creator<FillResponse>() {
674 @Override
Svet Ganov0f4928f2017-02-02 20:02:51 -0800675 public FillResponse createFromParcel(Parcel parcel) {
676 // Always go through the builder to ensure the data ingested by
677 // the system obeys the contract of the builder to avoid attacks
678 // using specially crafted parcels.
Svet Ganov782043c2017-02-11 00:52:02 +0000679 final Builder builder = new Builder();
Felipe Lemecb96f392017-07-25 15:19:30 -0700680 final ParceledListSlice<Dataset> datasetSlice = parcel.readParcelable(null);
681 final List<Dataset> datasets = (datasetSlice != null) ? datasetSlice.getList() : null;
Svet Ganov0f4928f2017-02-02 20:02:51 -0800682 final int datasetCount = (datasets != null) ? datasets.size() : 0;
683 for (int i = 0; i < datasetCount; i++) {
Felipe Lemefe35e692017-02-21 13:44:50 -0800684 builder.addDataset(datasets.get(i));
Svet Ganov0f4928f2017-02-02 20:02:51 -0800685 }
Felipe Lemef69761f2017-02-23 17:52:01 -0800686 builder.setSaveInfo(parcel.readParcelable(null));
Felipe Leme01ded102017-04-20 15:58:30 -0700687 builder.setClientState(parcel.readParcelable(null));
Felipe Lemec2430f3c4d62017-05-01 09:35:33 -0700688
689 // Sets authentication state.
690 final AutofillId[] authenticationIds = parcel.readParcelableArray(null,
691 AutofillId.class);
692 final IntentSender authentication = parcel.readParcelable(null);
693 final RemoteViews presentation = parcel.readParcelable(null);
694 if (authenticationIds != null) {
695 builder.setAuthentication(authenticationIds, authentication, presentation);
696 }
Felipe Lemee9324782017-11-21 18:00:56 -0800697 final RemoteViews header = parcel.readParcelable(null);
698 if (header != null) {
699 builder.setHeader(header);
700 }
701 final RemoteViews footer = parcel.readParcelable(null);
702 if (footer != null) {
703 builder.setFooter(footer);
704 }
Adam He2cc31462018-11-12 16:26:14 -0800705 final UserData userData = parcel.readParcelable(null);
706 if (userData != null) {
707 builder.setUserData(userData);
708 }
Felipe Lemec2430f3c4d62017-05-01 09:35:33 -0700709
Felipe Leme01ded102017-04-20 15:58:30 -0700710 builder.setIgnoredIds(parcel.readParcelableArray(null, AutofillId.class));
Felipe Leme17292d12017-10-24 14:03:10 -0700711 final long disableDuration = parcel.readLong();
712 if (disableDuration > 0) {
713 builder.disableAutofill(disableDuration);
714 }
Felipe Leme452886a2017-11-27 13:09:13 -0800715 final AutofillId[] fieldClassifactionIds =
716 parcel.readParcelableArray(null, AutofillId.class);
717 if (fieldClassifactionIds != null) {
718 builder.setFieldClassificationIds(fieldClassifactionIds);
Felipe Leme24d71732017-10-20 10:32:57 -0700719 }
Felipe Leme59c44642017-10-13 11:05:05 -0700720 builder.setFlags(parcel.readInt());
Philip P. Moltmannc7619632017-04-25 11:57:37 -0700721
Felipe Leme59c44642017-10-13 11:05:05 -0700722 final FillResponse response = builder.build();
Philip P. Moltmannc7619632017-04-25 11:57:37 -0700723 response.setRequestId(parcel.readInt());
724
725 return response;
Felipe Leme6d553872016-12-08 17:13:25 -0800726 }
727
728 @Override
729 public FillResponse[] newArray(int size) {
730 return new FillResponse[size];
731 }
732 };
733}