1 /* 2 * Copyright 2022 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 com.android.server.appsearch.external.localstorage.visibilitystore; 17 18 import android.annotation.NonNull; 19 import android.annotation.Nullable; 20 import android.app.appsearch.AppSearchSchema; 21 import android.app.appsearch.GenericDocument; 22 import android.app.appsearch.PackageIdentifier; 23 import android.app.appsearch.annotation.CanIgnoreReturnValue; 24 import android.util.ArraySet; 25 26 import java.util.Objects; 27 import java.util.Set; 28 29 /** 30 * Holds the visibility settings in version 1 that apply to a schema type. 31 * 32 * @hide 33 */ 34 class VisibilityDocumentV1 extends GenericDocument { 35 /** The Schema type for documents that hold AppSearch's metadata, e.g. visibility settings. */ 36 static final String SCHEMA_TYPE = "VisibilityType"; 37 38 /** Namespace of documents that contain visibility settings */ 39 static final String NAMESPACE = ""; 40 41 /** 42 * Property that holds the list of platform-hidden schemas, as part of the visibility settings. 43 */ 44 private static final String NOT_DISPLAYED_BY_SYSTEM_PROPERTY = "notPlatformSurfaceable"; 45 46 /** Property that holds the package name that can access a schema. */ 47 private static final String PACKAGE_NAME_PROPERTY = "packageName"; 48 49 /** Property that holds the SHA 256 certificate of the app that can access a schema. */ 50 private static final String SHA_256_CERT_PROPERTY = "sha256Cert"; 51 52 /** Property that holds the role can access a schema. */ 53 private static final String ROLE_PROPERTY = "role"; 54 55 /** Property that holds the required permissions to access the schema. */ 56 private static final String PERMISSION_PROPERTY = "permission"; 57 58 /** Schema for the VisibilityStore's documents. */ 59 static final AppSearchSchema SCHEMA = 60 new AppSearchSchema.Builder(SCHEMA_TYPE) 61 .addProperty( 62 new AppSearchSchema.BooleanPropertyConfig.Builder( 63 NOT_DISPLAYED_BY_SYSTEM_PROPERTY) 64 .setCardinality( 65 AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) 66 .build()) 67 .addProperty( 68 new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY) 69 .setCardinality( 70 AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 71 .build()) 72 .addProperty( 73 new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY) 74 .setCardinality( 75 AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 76 .build()) 77 .addProperty( 78 new AppSearchSchema.LongPropertyConfig.Builder(ROLE_PROPERTY) 79 .setCardinality( 80 AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 81 .build()) 82 .addProperty( 83 new AppSearchSchema.LongPropertyConfig.Builder(PERMISSION_PROPERTY) 84 .setCardinality( 85 AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) 86 .build()) 87 .build(); 88 VisibilityDocumentV1(@onNull GenericDocument genericDocument)89 VisibilityDocumentV1(@NonNull GenericDocument genericDocument) { 90 super(genericDocument); 91 } 92 93 /** Returns whether this schema is visible to the system. */ isNotDisplayedBySystem()94 boolean isNotDisplayedBySystem() { 95 return getPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY); 96 } 97 98 /** 99 * Returns a package name array which could access this schema. Use {@link #getSha256Certs()} to 100 * get package's sha 256 certs. The same index of package names array and sha256Certs array 101 * represents same package. 102 */ 103 @NonNull getPackageNames()104 String[] getPackageNames() { 105 return Objects.requireNonNull(getPropertyStringArray(PACKAGE_NAME_PROPERTY)); 106 } 107 108 /** 109 * Returns a package sha256Certs array which could access this schema. Use {@link 110 * #getPackageNames()} to get package's name. The same index of package names array and 111 * sha256Certs array represents same package. 112 */ 113 @NonNull getSha256Certs()114 byte[][] getSha256Certs() { 115 return Objects.requireNonNull(getPropertyBytesArray(SHA_256_CERT_PROPERTY)); 116 } 117 118 /** 119 * Returns an array of Android Roles that have access to the schema this {@link 120 * VisibilityDocumentV1} represents. 121 */ 122 @Nullable getVisibleToRoles()123 Set<Integer> getVisibleToRoles() { 124 return toInts(getPropertyLongArray(ROLE_PROPERTY)); 125 } 126 127 /** 128 * Returns an array of Android Permissions that caller mush hold to access the schema this 129 * {@link VisibilityDocumentV1} represents. 130 */ 131 @Nullable getVisibleToPermissions()132 Set<Integer> getVisibleToPermissions() { 133 return toInts(getPropertyLongArray(PERMISSION_PROPERTY)); 134 } 135 136 /** Builder for {@link VisibilityDocumentV1}. */ 137 static class Builder extends GenericDocument.Builder<Builder> { 138 private final Set<PackageIdentifier> mPackageIdentifiers = new ArraySet<>(); 139 140 /** 141 * Creates a {@link Builder} for a {@link VisibilityDocumentV1}. 142 * 143 * @param id The SchemaType of the {@link AppSearchSchema} that this {@link 144 * VisibilityDocumentV1} represents. The package and database prefix will be added in 145 * server side. We are using prefixed schema type to be the final id of this {@link 146 * VisibilityDocumentV1}. 147 */ Builder(@onNull String id)148 Builder(@NonNull String id) { 149 super(NAMESPACE, id, SCHEMA_TYPE); 150 } 151 152 /** Sets whether this schema has opted out of platform surfacing. */ 153 @CanIgnoreReturnValue 154 @NonNull setNotDisplayedBySystem(boolean notDisplayedBySystem)155 Builder setNotDisplayedBySystem(boolean notDisplayedBySystem) { 156 return setPropertyBoolean(NOT_DISPLAYED_BY_SYSTEM_PROPERTY, notDisplayedBySystem); 157 } 158 159 /** Add {@link PackageIdentifier} of packages which has access to this schema. */ 160 @CanIgnoreReturnValue 161 @NonNull addVisibleToPackages(@onNull Set<PackageIdentifier> packageIdentifiers)162 Builder addVisibleToPackages(@NonNull Set<PackageIdentifier> packageIdentifiers) { 163 Objects.requireNonNull(packageIdentifiers); 164 mPackageIdentifiers.addAll(packageIdentifiers); 165 return this; 166 } 167 168 /** Add {@link PackageIdentifier} of packages which has access to this schema. */ 169 @CanIgnoreReturnValue 170 @NonNull addVisibleToPackage(@onNull PackageIdentifier packageIdentifier)171 Builder addVisibleToPackage(@NonNull PackageIdentifier packageIdentifier) { 172 Objects.requireNonNull(packageIdentifier); 173 mPackageIdentifiers.add(packageIdentifier); 174 return this; 175 } 176 177 /** 178 * Add a set of Android role that has access to the schema this {@link VisibilityDocumentV1} 179 * represents. 180 */ 181 @CanIgnoreReturnValue 182 @NonNull setVisibleToRoles(@onNull Set<Integer> visibleToRoles)183 Builder setVisibleToRoles(@NonNull Set<Integer> visibleToRoles) { 184 Objects.requireNonNull(visibleToRoles); 185 setPropertyLong(ROLE_PROPERTY, toLongs(visibleToRoles)); 186 return this; 187 } 188 189 /** 190 * Add a set of Android role that has access to the schema this {@link VisibilityDocumentV1} 191 * represents. 192 */ 193 @CanIgnoreReturnValue 194 @NonNull setVisibleToPermissions(@onNull Set<Integer> visibleToPermissions)195 Builder setVisibleToPermissions(@NonNull Set<Integer> visibleToPermissions) { 196 Objects.requireNonNull(visibleToPermissions); 197 setPropertyLong(PERMISSION_PROPERTY, toLongs(visibleToPermissions)); 198 return this; 199 } 200 201 /** Build a {@link VisibilityDocumentV1} */ 202 @Override 203 @NonNull build()204 public VisibilityDocumentV1 build() { 205 String[] packageNames = new String[mPackageIdentifiers.size()]; 206 byte[][] sha256Certs = new byte[mPackageIdentifiers.size()][32]; 207 int i = 0; 208 for (PackageIdentifier packageIdentifier : mPackageIdentifiers) { 209 packageNames[i] = packageIdentifier.getPackageName(); 210 sha256Certs[i] = packageIdentifier.getSha256Certificate(); 211 ++i; 212 } 213 setPropertyString(PACKAGE_NAME_PROPERTY, packageNames); 214 setPropertyBytes(SHA_256_CERT_PROPERTY, sha256Certs); 215 return new VisibilityDocumentV1(super.build()); 216 } 217 } 218 219 @NonNull toLongs(@onNull Set<Integer> properties)220 static long[] toLongs(@NonNull Set<Integer> properties) { 221 long[] outputs = new long[properties.size()]; 222 int i = 0; 223 for (int property : properties) { 224 outputs[i++] = property; 225 } 226 return outputs; 227 } 228 229 @Nullable toInts(@ullable long[] properties)230 private static Set<Integer> toInts(@Nullable long[] properties) { 231 if (properties == null) { 232 return null; 233 } 234 Set<Integer> outputs = new ArraySet<>(properties.length); 235 for (long property : properties) { 236 outputs.add((int) property); 237 } 238 return outputs; 239 } 240 } 241