SaveInfo.java revision b72f012cb49a5930010fb0766776b40c2955ee3e
1/*
2 * Copyright (C) 2017 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
17package android.service.autofill;
18
19import static android.view.autofill.Helper.DEBUG;
20
21import android.annotation.IntDef;
22import android.annotation.NonNull;
23import android.annotation.Nullable;
24import android.os.Bundle;
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.util.ArraySet;
28import android.view.autofill.AutoFillId;
29
30import java.lang.annotation.Retention;
31import java.lang.annotation.RetentionPolicy;
32import java.util.ArrayList;
33
34/**
35 * Information used to indicate that a service is interested on saving the user-inputed data for
36 * future use.
37 *
38 * <p>A {@link SaveInfo} is always associated with a {@link FillResponse}.
39 *
40 * <p>A {@link SaveInfo} must define the type it represents, and contain at least one
41 * {@code savableId}. A {@code savableId} is the {@link AutoFillId} of a view the service is
42 * interested to save in a {@code onSaveRequest()}; the ids of all {@link Dataset} present in the
43 * {@link FillResponse} associated with this {@link SaveInfo} are already marked as savable,
44 * but additional ids can be added through {@link Builder#addSavableIds(AutoFillId...)}.
45 *
46 * <p>See {@link AutoFillService#onSaveRequest(android.app.assist.AssistStructure, Bundle,
47 * SaveCallback)} and {@link FillResponse} for more info.
48 */
49public final class SaveInfo implements Parcelable {
50
51    /**
52     * Type used on when the service can save the contents of an activity, but cannot describe what
53     * the content is for.
54     */
55    public static final int SAVE_DATA_TYPE_GENERIC = 0;
56
57    /**
58     * Type used when the {@link FillResponse} represents user credentials that have a password.
59     */
60    public static final int SAVE_DATA_TYPE_PASSWORD = 1;
61
62
63    /**
64     * Type used on when the {@link FillResponse} represents a physical address (such as street,
65     * city, state, etc).
66     */
67    public static final int SAVE_DATA_TYPE_ADDRESS = 2;
68
69    /**
70     * Type used when the {@link FillResponse} represents a credit card.
71     */
72    public static final int SAVE_DATA_TYPE_CREDIT_CARD = 3;
73
74    private final @SaveDataType int mType;
75    private ArraySet<AutoFillId> mSavableIds;
76    private final CharSequence mDescription;
77
78    /** @hide */
79    @IntDef({
80        SAVE_DATA_TYPE_GENERIC,
81        SAVE_DATA_TYPE_PASSWORD,
82        SAVE_DATA_TYPE_ADDRESS,
83        SAVE_DATA_TYPE_CREDIT_CARD
84    })
85    @Retention(RetentionPolicy.SOURCE)
86    public @interface SaveDataType {
87    }
88
89    private SaveInfo(Builder builder) {
90        mType = builder.mType;
91        mSavableIds = builder.mSavableIds;
92        mDescription = builder.mDescription;
93    }
94
95    /** @hide */
96    public @Nullable ArraySet<AutoFillId> getSavableIds() {
97        return mSavableIds;
98    }
99
100    /** @hide */
101    public int getType() {
102        return mType;
103    }
104
105    /** @hide */
106    public CharSequence getDescription() {
107        return mDescription;
108    }
109
110    /** @hide */
111    public void addSavableIds(@Nullable ArrayList<Dataset> datasets) {
112        if (datasets != null) {
113            for (Dataset dataset : datasets) {
114                final ArrayList<AutoFillId> ids = dataset.getFieldIds();
115                if (ids != null) {
116                    final int fieldCount = ids.size();
117                    for (int i = 0; i < fieldCount; i++) {
118                        final AutoFillId id = ids.get(i);
119                        if (mSavableIds == null) {
120                            mSavableIds = new ArraySet<>();
121                        }
122                        mSavableIds.add(id);
123                    }
124                }
125            }
126        }
127    }
128
129    /**
130     * A builder for {@link SaveInfo} objects.
131     */
132    public static final class Builder {
133
134        private final @SaveDataType int mType;
135        private ArraySet<AutoFillId> mSavableIds;
136        private CharSequence mDescription;
137        private boolean mDestroyed;
138
139        /**
140         * Creates a new builder.
141         *
142         * @param type the type of information the associated {@link FillResponse} represents. Must
143         * be {@link SaveInfo#SAVE_DATA_TYPE_GENERIC}, {@link SaveInfo#SAVE_DATA_TYPE_PASSWORD},
144         * {@link SaveInfo#SAVE_DATA_TYPE_ADDRESS}, or {@link SaveInfo#SAVE_DATA_TYPE_CREDIT_CARD};
145         * otherwise it will assume {@link SaveInfo#SAVE_DATA_TYPE_GENERIC}.
146         */
147        public Builder(@SaveDataType int type) {
148            switch (type) {
149                case SAVE_DATA_TYPE_PASSWORD:
150                case SAVE_DATA_TYPE_ADDRESS:
151                case SAVE_DATA_TYPE_CREDIT_CARD:
152                    mType = type;
153                    break;
154                default:
155                    mType = SAVE_DATA_TYPE_GENERIC;
156            }
157        }
158
159        /**
160         * Adds ids of additional views the service would be interested to save, but were not
161         * indirectly set through {@link FillResponse.Builder#addDataset(Dataset)}.
162         *
163         * @param ids The savable ids.
164         * @return This builder.
165         *
166         * @see FillResponse
167         */
168        public @NonNull Builder addSavableIds(@Nullable AutoFillId... ids) {
169            throwIfDestroyed();
170
171            if (ids == null) {
172                return this;
173            }
174            for (AutoFillId id : ids) {
175                if (mSavableIds == null) {
176                    mSavableIds = new ArraySet<>();
177                }
178                mSavableIds.add(id);
179            }
180            return this;
181        }
182
183        /**
184         * Sets an optional description to be shown in the UI when the user is asked to save.
185         *
186         * <p>Typically, it describes how the data will be stored by the service, so it can help
187         * users to decide whether they can trust the service to save their data.
188         *
189         * @param description a succint description.
190         * @return This Builder.
191         */
192        public @NonNull Builder setDescription(@Nullable CharSequence description) {
193            mDescription = description;
194            return this;
195        }
196
197        /**
198         * Builds a new {@link SaveInfo} instance.
199         */
200        public SaveInfo build() {
201            throwIfDestroyed();
202            mDestroyed = true;
203            return new SaveInfo(this);
204        }
205
206        private void throwIfDestroyed() {
207            if (mDestroyed) {
208                throw new IllegalStateException("Already called #build()");
209            }
210        }
211
212    }
213
214    /////////////////////////////////////
215    // Object "contract" methods. //
216    /////////////////////////////////////
217    @Override
218    public String toString() {
219        if (!DEBUG) return super.toString();
220
221        return new StringBuilder("SaveInfo: [type=").append(mType)
222                .append(", savableIds=").append(mSavableIds)
223                .append("]").toString();
224    }
225
226    /////////////////////////////////////
227    // Parcelable "contract" methods. //
228    /////////////////////////////////////
229
230    @Override
231    public int describeContents() {
232        return 0;
233    }
234
235    @Override
236    public void writeToParcel(Parcel parcel, int flags) {
237        parcel.writeInt(mType);
238        parcel.writeTypedArraySet(mSavableIds, flags);
239        parcel.writeCharSequence(mDescription);
240    }
241
242    public static final Parcelable.Creator<SaveInfo> CREATOR = new Parcelable.Creator<SaveInfo>() {
243        @Override
244        public SaveInfo createFromParcel(Parcel parcel) {
245            // Always go through the builder to ensure the data ingested by
246            // the system obeys the contract of the builder to avoid attacks
247            // using specially crafted parcels.
248            final Builder builder = new Builder(parcel.readInt());
249            final ArraySet<AutoFillId> savableIds = parcel.readTypedArraySet(null);
250            final int savableIdsCount = (savableIds != null) ? savableIds.size() : 0;
251            for (int i = 0; i < savableIdsCount; i++) {
252                builder.addSavableIds(savableIds.valueAt(i));
253            }
254            builder.setDescription(parcel.readCharSequence());
255            return builder.build();
256        }
257
258        @Override
259        public SaveInfo[] newArray(int size) {
260            return new SaveInfo[size];
261        }
262    };
263}
264