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