1 /*
2  * Copyright (C) 2018 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 package android.service.autofill;
17 
18 import static android.view.autofill.Helper.sDebug;
19 
20 import android.annotation.NonNull;
21 import android.annotation.Nullable;
22 import android.annotation.TestApi;
23 import android.os.Bundle;
24 import android.os.Parcel;
25 import android.os.Parcelable;
26 import android.util.ArrayMap;
27 
28 import com.android.internal.util.Preconditions;
29 
30 import java.util.ArrayList;
31 import java.util.Collections;
32 
33 /**
34  * Holds both a generic and package-specific userData used for
35  * <a href="AutofillService.html#FieldClassification">field classification</a>.
36  *
37  * @hide
38  */
39 @TestApi
40 public final class CompositeUserData implements FieldClassificationUserData, Parcelable {
41 
42     private final UserData mGenericUserData;
43     private final UserData mPackageUserData;
44 
45     private final String[] mCategories;
46     private final String[] mValues;
47 
CompositeUserData(@ullable UserData genericUserData, @NonNull UserData packageUserData)48     public CompositeUserData(@Nullable UserData genericUserData,
49             @NonNull UserData packageUserData) {
50         mGenericUserData = genericUserData;
51         mPackageUserData = packageUserData;
52 
53         final String[] packageCategoryIds = mPackageUserData.getCategoryIds();
54         final String[] packageValues = mPackageUserData.getValues();
55 
56         final ArrayList<String> categoryIds = new ArrayList<>(packageCategoryIds.length);
57         final ArrayList<String> values = new ArrayList<>(packageValues.length);
58 
59         Collections.addAll(categoryIds, packageCategoryIds);
60         Collections.addAll(values, packageValues);
61 
62         if (mGenericUserData != null) {
63             final String[] genericCategoryIds = mGenericUserData.getCategoryIds();
64             final String[] genericValues = mGenericUserData.getValues();
65             final int size = mGenericUserData.getCategoryIds().length;
66             for (int i = 0; i < size; i++) {
67                 if (!categoryIds.contains(genericCategoryIds[i])) {
68                     categoryIds.add(genericCategoryIds[i]);
69                     values.add(genericValues[i]);
70                 }
71             }
72         }
73 
74         mCategories = new String[categoryIds.size()];
75         categoryIds.toArray(mCategories);
76         mValues = new String[values.size()];
77         values.toArray(mValues);
78     }
79 
80     @Nullable
81     @Override
getFieldClassificationAlgorithm()82     public String getFieldClassificationAlgorithm() {
83         final String packageDefaultAlgo = mPackageUserData.getFieldClassificationAlgorithm();
84         if (packageDefaultAlgo != null) {
85             return packageDefaultAlgo;
86         } else {
87             return mGenericUserData == null ? null :
88                     mGenericUserData.getFieldClassificationAlgorithm();
89         }
90     }
91 
92     @Override
getDefaultFieldClassificationArgs()93     public Bundle getDefaultFieldClassificationArgs() {
94         final Bundle packageDefaultArgs = mPackageUserData.getDefaultFieldClassificationArgs();
95         if (packageDefaultArgs != null) {
96             return packageDefaultArgs;
97         } else {
98             return mGenericUserData == null ? null :
99                     mGenericUserData.getDefaultFieldClassificationArgs();
100         }
101     }
102 
103     @Nullable
104     @Override
getFieldClassificationAlgorithmForCategory(@onNull String categoryId)105     public String getFieldClassificationAlgorithmForCategory(@NonNull String categoryId) {
106         Preconditions.checkNotNull(categoryId);
107         final ArrayMap<String, String> categoryAlgorithms = getFieldClassificationAlgorithms();
108         if (categoryAlgorithms == null || !categoryAlgorithms.containsKey(categoryId)) {
109             return null;
110         }
111         return categoryAlgorithms.get(categoryId);
112     }
113 
114     @Override
getFieldClassificationAlgorithms()115     public ArrayMap<String, String> getFieldClassificationAlgorithms() {
116         final ArrayMap<String, String> packageAlgos = mPackageUserData
117                 .getFieldClassificationAlgorithms();
118         final ArrayMap<String, String> genericAlgos = mGenericUserData == null ? null :
119                 mGenericUserData.getFieldClassificationAlgorithms();
120 
121         ArrayMap<String, String> categoryAlgorithms = null;
122         if (packageAlgos != null || genericAlgos != null) {
123             categoryAlgorithms = new ArrayMap<>();
124             if (genericAlgos != null) {
125                 categoryAlgorithms.putAll(genericAlgos);
126             }
127             if (packageAlgos != null) {
128                 categoryAlgorithms.putAll(packageAlgos);
129             }
130         }
131 
132         return categoryAlgorithms;
133     }
134 
135     @Override
getFieldClassificationArgs()136     public ArrayMap<String, Bundle> getFieldClassificationArgs() {
137         final ArrayMap<String, Bundle> packageArgs = mPackageUserData.getFieldClassificationArgs();
138         final ArrayMap<String, Bundle> genericArgs = mGenericUserData == null ? null :
139                 mGenericUserData.getFieldClassificationArgs();
140 
141         ArrayMap<String, Bundle> categoryArgs = null;
142         if (packageArgs != null || genericArgs != null) {
143             categoryArgs = new ArrayMap<>();
144             if (genericArgs != null) {
145                 categoryArgs.putAll(genericArgs);
146             }
147             if (packageArgs != null) {
148                 categoryArgs.putAll(packageArgs);
149             }
150         }
151 
152         return categoryArgs;
153     }
154 
155     @Override
getCategoryIds()156     public String[] getCategoryIds() {
157         return mCategories;
158     }
159 
160     @Override
getValues()161     public String[] getValues() {
162         return mValues;
163     }
164 
165     /////////////////////////////////////
166     // Object "contract" methods. //
167     /////////////////////////////////////
168     @Override
toString()169     public String toString() {
170         if (!sDebug) return super.toString();
171 
172         // OK to print UserData because UserData.toString() is PII-aware
173         final StringBuilder builder = new StringBuilder("genericUserData=")
174                 .append(mGenericUserData)
175                 .append(", packageUserData=").append(mPackageUserData);
176         return builder.toString();
177     }
178 
179     /////////////////////////////////////
180     // Parcelable "contract" methods. //
181     /////////////////////////////////////
182 
183     @Override
describeContents()184     public int describeContents() {
185         return 0;
186     }
187 
188     @Override
writeToParcel(Parcel parcel, int flags)189     public void writeToParcel(Parcel parcel, int flags) {
190         parcel.writeParcelable(mGenericUserData, 0);
191         parcel.writeParcelable(mPackageUserData, 0);
192     }
193 
194     public static final @android.annotation.NonNull Parcelable.Creator<CompositeUserData> CREATOR =
195             new Parcelable.Creator<CompositeUserData>() {
196                 @Override
197                 public CompositeUserData createFromParcel(Parcel parcel) {
198                     // Always go through the builder to ensure the data ingested by
199                     // the system obeys the contract of the builder to avoid attacks
200                     // using specially crafted parcels.
201                     final UserData genericUserData = parcel.readParcelable(null);
202                     final UserData packageUserData = parcel.readParcelable(null);
203                     return new CompositeUserData(genericUserData, packageUserData);
204                 }
205 
206                 @Override
207                 public CompositeUserData[] newArray(int size) {
208                     return new CompositeUserData[size];
209                 }
210             };
211 }
212