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