1 /* 2 * Copyright (C) 2021 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.app.appsearch; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.app.appsearch.annotation.CanIgnoreReturnValue; 21 import android.os.Bundle; 22 import android.util.ArraySet; 23 24 import java.util.ArrayList; 25 import java.util.Collections; 26 import java.util.List; 27 import java.util.Map; 28 import java.util.Objects; 29 import java.util.Set; 30 31 /** 32 * Holds the visibility settings that apply to a schema type. 33 * 34 * @hide 35 */ 36 public class VisibilityDocument extends GenericDocument { 37 /** 38 * The Schema type for documents that hold AppSearch's metadata, such as visibility settings. 39 */ 40 public static final String SCHEMA_TYPE = "VisibilityType"; 41 /** Namespace of documents that contain visibility settings */ 42 public static final String NAMESPACE = ""; 43 44 /** 45 * Property that holds the list of platform-hidden schemas, as part of the visibility settings. 46 */ 47 private static final String NOT_DISPLAYED_BY_SYSTEM_PROPERTY = "notPlatformSurfaceable"; 48 49 /** Property that holds the package name that can access a schema. */ 50 private static final String PACKAGE_NAME_PROPERTY = "packageName"; 51 52 /** Property that holds the SHA 256 certificate of the app that can access a schema. */ 53 private static final String SHA_256_CERT_PROPERTY = "sha256Cert"; 54 55 /** Property that holds the required permissions to access the schema. */ 56 private static final String PERMISSION_PROPERTY = "permission"; 57 58 // The initial schema version, one VisibilityDocument contains all visibility information for 59 // whole package. 60 public static final int SCHEMA_VERSION_DOC_PER_PACKAGE = 0; 61 62 // One VisibilityDocument contains visibility information for a single schema. 63 public static final int SCHEMA_VERSION_DOC_PER_SCHEMA = 1; 64 65 // One VisibilityDocument contains visibility information for a single schema. 66 public static final int SCHEMA_VERSION_NESTED_PERMISSION_SCHEMA = 2; 67 68 public static final int SCHEMA_VERSION_LATEST = SCHEMA_VERSION_NESTED_PERMISSION_SCHEMA; 69 70 /** 71 * Schema for the VisibilityStore's documents. 72 * 73 * <p>NOTE: If you update this, also update {@link #SCHEMA_VERSION_LATEST}. 74 */ 75 public static final AppSearchSchema SCHEMA = 76 new AppSearchSchema.Builder(SCHEMA_TYPE) 77 .addProperty( 78 new AppSearchSchema.BooleanPropertyConfig.Builder( 79 NOT_DISPLAYED_BY_SYSTEM_PROPERTY) 80 .setCardinality( 81 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) 82 .build()) 83 .addProperty( 84 new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY) 85 .setCardinality( 86 AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 87 .build()) 88 .addProperty( 89 new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY) 90 .setCardinality( 91 AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 92 .build()) 93 .addProperty( 94 new AppSearchSchema.DocumentPropertyConfig.Builder( 95 PERMISSION_PROPERTY, 96 VisibilityPermissionDocument.SCHEMA_TYPE) 97 .setCardinality( 98 AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 99 .build()) 100 .build(); 101 VisibilityDocument(@onNull GenericDocument genericDocument)102 public VisibilityDocument(@NonNull GenericDocument genericDocument) { 103 super(genericDocument); 104 } 105 VisibilityDocument(@onNull Bundle bundle)106 public VisibilityDocument(@NonNull Bundle bundle) { 107 super(bundle); 108 } 109 110 /** Returns whether this schema is visible to the system. */ isNotDisplayedBySystem()111 public boolean isNotDisplayedBySystem() { 112 return getPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY); 113 } 114 115 /** 116 * Returns a package name array which could access this schema. Use {@link #getSha256Certs()} to 117 * get package's sha 256 certs. The same index of package names array and sha256Certs array 118 * represents same package. 119 */ 120 @NonNull getPackageNames()121 public String[] getPackageNames() { 122 return Objects.requireNonNull(getPropertyStringArray(PACKAGE_NAME_PROPERTY)); 123 } 124 125 /** 126 * Returns a package sha256Certs array which could access this schema. Use {@link 127 * #getPackageNames()} to get package's name. The same index of package names array and 128 * sha256Certs array represents same package. 129 */ 130 @NonNull getSha256Certs()131 public byte[][] getSha256Certs() { 132 return Objects.requireNonNull(getPropertyBytesArray(SHA_256_CERT_PROPERTY)); 133 } 134 135 /** 136 * Returns an array of Android Permissions that caller mush hold to access the schema this 137 * {@link VisibilityDocument} represents. 138 */ 139 @Nullable getVisibleToPermissions()140 public Set<Set<Integer>> getVisibleToPermissions() { 141 GenericDocument[] permissionDocuments = getPropertyDocumentArray(PERMISSION_PROPERTY); 142 if (permissionDocuments == null) { 143 return Collections.emptySet(); 144 } 145 Set<Set<Integer>> visibleToPermissions = new ArraySet<>(permissionDocuments.length); 146 for (GenericDocument permissionDocument : permissionDocuments) { 147 Set<Integer> requiredPermissions = 148 new VisibilityPermissionDocument(permissionDocument) 149 .getAllRequiredPermissions(); 150 if (requiredPermissions != null) { 151 visibleToPermissions.add(requiredPermissions); 152 } 153 } 154 return visibleToPermissions; 155 } 156 157 /** Builder for {@link VisibilityDocument}. */ 158 public static class Builder extends GenericDocument.Builder<Builder> { 159 private final Set<PackageIdentifier> mPackageIdentifiers = new ArraySet<>(); 160 161 /** 162 * Creates a {@link Builder} for a {@link VisibilityDocument}. 163 * 164 * @param id The SchemaType of the {@link AppSearchSchema} that this {@link 165 * VisibilityDocument} represents. The package and database prefix will be added in 166 * server side. We are using prefixed schema type to be the final id of this {@link 167 * VisibilityDocument}. 168 */ Builder(@onNull String id)169 public Builder(@NonNull String id) { 170 super(NAMESPACE, id, SCHEMA_TYPE); 171 } 172 173 /** Sets whether this schema has opted out of platform surfacing. */ 174 @CanIgnoreReturnValue 175 @NonNull setNotDisplayedBySystem(boolean notDisplayedBySystem)176 public Builder setNotDisplayedBySystem(boolean notDisplayedBySystem) { 177 return setPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY, notDisplayedBySystem); 178 } 179 180 /** Add {@link PackageIdentifier} of packages which has access to this schema. */ 181 @CanIgnoreReturnValue 182 @NonNull addVisibleToPackages(@onNull Set<PackageIdentifier> packageIdentifiers)183 public Builder addVisibleToPackages(@NonNull Set<PackageIdentifier> packageIdentifiers) { 184 Objects.requireNonNull(packageIdentifiers); 185 mPackageIdentifiers.addAll(packageIdentifiers); 186 return this; 187 } 188 189 /** Add {@link PackageIdentifier} of packages which has access to this schema. */ 190 @CanIgnoreReturnValue 191 @NonNull addVisibleToPackage(@onNull PackageIdentifier packageIdentifier)192 public Builder addVisibleToPackage(@NonNull PackageIdentifier packageIdentifier) { 193 Objects.requireNonNull(packageIdentifier); 194 mPackageIdentifiers.add(packageIdentifier); 195 return this; 196 } 197 198 /** 199 * Sets required permission sets for a package needs to hold to the schema this {@link 200 * VisibilityDocument} represents. 201 * 202 * <p>The querier could have access if they holds ALL required permissions of ANY of the 203 * individual value sets. 204 */ 205 @CanIgnoreReturnValue 206 @NonNull setVisibleToPermissions(@onNull Set<Set<Integer>> visibleToPermissions)207 public Builder setVisibleToPermissions(@NonNull Set<Set<Integer>> visibleToPermissions) { 208 Objects.requireNonNull(visibleToPermissions); 209 VisibilityPermissionDocument[] permissionDocuments = 210 new VisibilityPermissionDocument[visibleToPermissions.size()]; 211 int i = 0; 212 for (Set<Integer> allRequiredPermissions : visibleToPermissions) { 213 permissionDocuments[i++] = 214 new VisibilityPermissionDocument.Builder( 215 NAMESPACE, /*id=*/ String.valueOf(i)) 216 .setVisibleToAllRequiredPermissions(allRequiredPermissions) 217 .build(); 218 } 219 setPropertyDocument(PERMISSION_PROPERTY, permissionDocuments); 220 return this; 221 } 222 223 /** Build a {@link VisibilityDocument} */ 224 @Override 225 @NonNull build()226 public VisibilityDocument build() { 227 String[] packageNames = new String[mPackageIdentifiers.size()]; 228 byte[][] sha256Certs = new byte[mPackageIdentifiers.size()][32]; 229 int i = 0; 230 for (PackageIdentifier packageIdentifier : mPackageIdentifiers) { 231 packageNames[i] = packageIdentifier.getPackageName(); 232 sha256Certs[i] = packageIdentifier.getSha256Certificate(); 233 ++i; 234 } 235 setPropertyString(PACKAGE_NAME_PROPERTY, packageNames); 236 setPropertyBytes(SHA_256_CERT_PROPERTY, sha256Certs); 237 return new VisibilityDocument(super.build()); 238 } 239 } 240 241 /** Build the List of {@link VisibilityDocument} from visibility settings. */ 242 @NonNull toVisibilityDocuments( @onNull SetSchemaRequest setSchemaRequest)243 public static List<VisibilityDocument> toVisibilityDocuments( 244 @NonNull SetSchemaRequest setSchemaRequest) { 245 Set<AppSearchSchema> searchSchemas = setSchemaRequest.getSchemas(); 246 Set<String> schemasNotDisplayedBySystem = setSchemaRequest.getSchemasNotDisplayedBySystem(); 247 Map<String, Set<PackageIdentifier>> schemasVisibleToPackages = 248 setSchemaRequest.getSchemasVisibleToPackages(); 249 Map<String, Set<Set<Integer>>> schemasVisibleToPermissions = 250 setSchemaRequest.getRequiredPermissionsForSchemaTypeVisibility(); 251 252 List<VisibilityDocument> visibilityDocuments = new ArrayList<>(searchSchemas.size()); 253 254 for (AppSearchSchema searchSchema : searchSchemas) { 255 String schemaType = searchSchema.getSchemaType(); 256 VisibilityDocument.Builder documentBuilder = 257 new VisibilityDocument.Builder(/*id=*/ searchSchema.getSchemaType()); 258 documentBuilder.setNotDisplayedBySystem( 259 schemasNotDisplayedBySystem.contains(schemaType)); 260 261 if (schemasVisibleToPackages.containsKey(schemaType)) { 262 documentBuilder.addVisibleToPackages(schemasVisibleToPackages.get(schemaType)); 263 } 264 265 if (schemasVisibleToPermissions.containsKey(schemaType)) { 266 documentBuilder.setVisibleToPermissions( 267 schemasVisibleToPermissions.get(schemaType)); 268 } 269 visibilityDocuments.add(documentBuilder.build()); 270 } 271 return visibilityDocuments; 272 } 273 } 274