1 /* 2 * Copyright 2023 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 17 package android.app.appsearch; 18 19 import android.annotation.FlaggedApi; 20 import android.app.appsearch.annotation.CanIgnoreReturnValue; 21 import android.app.appsearch.safeparcel.AbstractSafeParcelable; 22 import android.app.appsearch.safeparcel.SafeParcelable; 23 import android.os.Parcel; 24 import android.os.Parcelable; 25 import android.util.ArraySet; 26 27 import com.android.appsearch.flags.Flags; 28 29 import org.jspecify.annotations.NonNull; 30 import org.jspecify.annotations.Nullable; 31 32 import java.util.ArrayList; 33 import java.util.List; 34 import java.util.Map; 35 import java.util.Objects; 36 import java.util.Set; 37 38 /** 39 * An expanded version of {@link SchemaVisibilityConfig} which includes fields for internal use by 40 * AppSearch. 41 * 42 * @hide 43 */ 44 @SafeParcelable.Class(creator = "InternalVisibilityConfigCreator") 45 public final class InternalVisibilityConfig extends AbstractSafeParcelable { 46 47 public static final Parcelable.@NonNull Creator<InternalVisibilityConfig> CREATOR = 48 new InternalVisibilityConfigCreator(); 49 50 /** Build the List of {@link InternalVisibilityConfig}s from given {@link SetSchemaRequest}. */ toInternalVisibilityConfigs( @onNull SetSchemaRequest setSchemaRequest)51 public static @NonNull List<InternalVisibilityConfig> toInternalVisibilityConfigs( 52 @NonNull SetSchemaRequest setSchemaRequest) { 53 Set<AppSearchSchema> searchSchemas = setSchemaRequest.getSchemas(); 54 Set<String> schemasNotDisplayedBySystem = setSchemaRequest.getSchemasNotDisplayedBySystem(); 55 Map<String, Set<PackageIdentifier>> schemasVisibleToPackages = 56 setSchemaRequest.getSchemasVisibleToPackages(); 57 Map<String, Set<Set<Integer>>> schemasVisibleToPermissions = 58 setSchemaRequest.getRequiredPermissionsForSchemaTypeVisibility(); 59 Map<String, PackageIdentifier> publiclyVisibleSchemas = 60 setSchemaRequest.getPubliclyVisibleSchemas(); 61 Map<String, Set<SchemaVisibilityConfig>> schemasVisibleToConfigs = 62 setSchemaRequest.getSchemasVisibleToConfigs(); 63 64 List<InternalVisibilityConfig> result = new ArrayList<>(searchSchemas.size()); 65 for (AppSearchSchema searchSchema : searchSchemas) { 66 String schemaType = searchSchema.getSchemaType(); 67 InternalVisibilityConfig.Builder builder = 68 new InternalVisibilityConfig.Builder(schemaType) 69 .setNotDisplayedBySystem( 70 schemasNotDisplayedBySystem.contains(schemaType)); 71 72 Set<PackageIdentifier> visibleToPackages = schemasVisibleToPackages.get(schemaType); 73 if (visibleToPackages != null) { 74 for (PackageIdentifier packageIdentifier : visibleToPackages) { 75 builder.addVisibleToPackage(packageIdentifier); 76 } 77 } 78 79 Set<Set<Integer>> visibleToPermissionSets = schemasVisibleToPermissions.get(schemaType); 80 if (visibleToPermissionSets != null) { 81 for (Set<Integer> visibleToPermissions : visibleToPermissionSets) { 82 builder.addVisibleToPermissions(visibleToPermissions); 83 } 84 } 85 86 PackageIdentifier publiclyVisibleTargetPackage = publiclyVisibleSchemas.get(schemaType); 87 if (publiclyVisibleTargetPackage != null) { 88 builder.setPubliclyVisibleTargetPackage(publiclyVisibleTargetPackage); 89 } 90 91 Set<SchemaVisibilityConfig> visibleToConfigs = schemasVisibleToConfigs.get(schemaType); 92 if (visibleToConfigs != null) { 93 for (SchemaVisibilityConfig schemaVisibilityConfig : visibleToConfigs) { 94 builder.addVisibleToConfig(schemaVisibilityConfig); 95 } 96 } 97 98 result.add(builder.build()); 99 } 100 return result; 101 } 102 103 /** 104 * Build the List of {@link InternalVisibilityConfig}s from given {@link 105 * SetBlobVisibilityRequest}. 106 */ toInternalVisibilityConfigs( @onNull SetBlobVisibilityRequest setBlobVisibilityRequest)107 public static @NonNull List<InternalVisibilityConfig> toInternalVisibilityConfigs( 108 @NonNull SetBlobVisibilityRequest setBlobVisibilityRequest) { 109 110 Set<String> blobNamespacesNotDisplayedBySystem = 111 setBlobVisibilityRequest.getNamespacesNotDisplayedBySystem(); 112 Map<String, Set<SchemaVisibilityConfig>> blobNamespacesVisibleToConfigs = 113 setBlobVisibilityRequest.getNamespacesVisibleToConfigs(); 114 115 Set<String> allBlobNamespaces = new ArraySet<>(blobNamespacesNotDisplayedBySystem); 116 allBlobNamespaces.addAll(blobNamespacesVisibleToConfigs.keySet()); 117 118 List<InternalVisibilityConfig> result = new ArrayList<>(); 119 for (String namespace : allBlobNamespaces) { 120 InternalVisibilityConfig.Builder builder = 121 new InternalVisibilityConfig.Builder(namespace) 122 .setNotDisplayedBySystem( 123 blobNamespacesNotDisplayedBySystem.contains(namespace)); 124 125 Set<SchemaVisibilityConfig> visibleToConfigs = 126 blobNamespacesVisibleToConfigs.get(namespace); 127 if (visibleToConfigs != null) { 128 for (SchemaVisibilityConfig schemaVisibilityConfig : visibleToConfigs) { 129 builder.addVisibleToConfig(schemaVisibilityConfig); 130 } 131 } 132 133 result.add(builder.build()); 134 } 135 return result; 136 } 137 138 @Field(id = 1, getter = "getSchemaType") 139 private final @NonNull String mSchemaType; 140 141 @Field(id = 2, getter = "isNotDisplayedBySystem") 142 private final boolean mIsNotDisplayedBySystem; 143 144 /** The public visibility settings available in VisibilityConfig. */ 145 @Field(id = 3, getter = "getVisibilityConfig") 146 private final @NonNull SchemaVisibilityConfig mVisibilityConfig; 147 148 /** Extended visibility settings from {@link SetSchemaRequest#getSchemasVisibleToConfigs()} */ 149 @Field(id = 4) 150 final @NonNull List<SchemaVisibilityConfig> mVisibleToConfigs; 151 152 @Constructor InternalVisibilityConfig( @aramid = 1) @onNull String schemaType, @Param(id = 2) boolean isNotDisplayedBySystem, @Param(id = 3) @NonNull SchemaVisibilityConfig schemaVisibilityConfig, @Param(id = 4) @NonNull List<SchemaVisibilityConfig> visibleToConfigs)153 InternalVisibilityConfig( 154 @Param(id = 1) @NonNull String schemaType, 155 @Param(id = 2) boolean isNotDisplayedBySystem, 156 @Param(id = 3) @NonNull SchemaVisibilityConfig schemaVisibilityConfig, 157 @Param(id = 4) @NonNull List<SchemaVisibilityConfig> visibleToConfigs) { 158 mIsNotDisplayedBySystem = isNotDisplayedBySystem; 159 mSchemaType = Objects.requireNonNull(schemaType); 160 mVisibilityConfig = Objects.requireNonNull(schemaVisibilityConfig); 161 mVisibleToConfigs = Objects.requireNonNull(visibleToConfigs); 162 } 163 164 /** 165 * Gets the schemaType for this VisibilityConfig. 166 * 167 * <p>This is being used as the document id when we convert a {@link InternalVisibilityConfig} 168 * to a {@link GenericDocument}. 169 */ getSchemaType()170 public @NonNull String getSchemaType() { 171 return mSchemaType; 172 } 173 174 /** Returns whether this schema is visible to the system. */ isNotDisplayedBySystem()175 public boolean isNotDisplayedBySystem() { 176 return mIsNotDisplayedBySystem; 177 } 178 179 /** 180 * Returns the visibility settings stored in the public {@link SchemaVisibilityConfig} object. 181 */ getVisibilityConfig()182 public @NonNull SchemaVisibilityConfig getVisibilityConfig() { 183 return mVisibilityConfig; 184 } 185 186 /** 187 * Returns required {@link SchemaVisibilityConfig} sets for a caller need to match to access the 188 * schema this {@link InternalVisibilityConfig} represents. 189 */ getVisibleToConfigs()190 public @NonNull Set<SchemaVisibilityConfig> getVisibleToConfigs() { 191 return new ArraySet<>(mVisibleToConfigs); 192 } 193 194 @Override writeToParcel(@onNull Parcel dest, int flags)195 public void writeToParcel(@NonNull Parcel dest, int flags) { 196 InternalVisibilityConfigCreator.writeToParcel(this, dest, flags); 197 } 198 199 @Override equals(@ullable Object o)200 public boolean equals(@Nullable Object o) { 201 if (this == o) { 202 return true; 203 } 204 if (o == null) { 205 return false; 206 } 207 if (!(o instanceof InternalVisibilityConfig)) { 208 return false; 209 } 210 InternalVisibilityConfig that = (InternalVisibilityConfig) o; 211 return mIsNotDisplayedBySystem == that.mIsNotDisplayedBySystem 212 && Objects.equals(mSchemaType, that.mSchemaType) 213 && Objects.equals(mVisibilityConfig, that.mVisibilityConfig) 214 && Objects.equals(mVisibleToConfigs, that.mVisibleToConfigs); 215 } 216 217 @Override hashCode()218 public int hashCode() { 219 return Objects.hash( 220 mIsNotDisplayedBySystem, mSchemaType, mVisibilityConfig, mVisibleToConfigs); 221 } 222 223 /** The builder class of {@link InternalVisibilityConfig}. */ 224 @FlaggedApi(Flags.FLAG_ENABLE_SET_SCHEMA_VISIBLE_TO_CONFIGS) 225 public static final class Builder { 226 private String mSchemaType; 227 private boolean mIsNotDisplayedBySystem; 228 private SchemaVisibilityConfig.Builder mVisibilityConfigBuilder; 229 private List<SchemaVisibilityConfig> mVisibleToConfigs = new ArrayList<>(); 230 private boolean mBuilt; 231 232 /** 233 * Creates a {@link Builder} for a {@link InternalVisibilityConfig}. 234 * 235 * @param schemaType The SchemaType of the {@link AppSearchSchema} that this {@link 236 * InternalVisibilityConfig} represents. The package and database prefix will be added 237 * in server side. We are using prefixed schema type to be the final id of this {@link 238 * InternalVisibilityConfig}. This will be used as as an AppSearch id. 239 * @see GenericDocument#getId 240 */ Builder(@onNull String schemaType)241 public Builder(@NonNull String schemaType) { 242 mSchemaType = Objects.requireNonNull(schemaType); 243 mVisibilityConfigBuilder = new SchemaVisibilityConfig.Builder(); 244 } 245 246 /** Creates a {@link Builder} from an existing {@link InternalVisibilityConfig} */ Builder(@onNull InternalVisibilityConfig internalVisibilityConfig)247 public Builder(@NonNull InternalVisibilityConfig internalVisibilityConfig) { 248 Objects.requireNonNull(internalVisibilityConfig); 249 mSchemaType = internalVisibilityConfig.mSchemaType; 250 mIsNotDisplayedBySystem = internalVisibilityConfig.mIsNotDisplayedBySystem; 251 mVisibilityConfigBuilder = 252 new SchemaVisibilityConfig.Builder( 253 internalVisibilityConfig.getVisibilityConfig()); 254 mVisibleToConfigs = internalVisibilityConfig.mVisibleToConfigs; 255 } 256 257 /** Sets schemaType, which will be as the id when converting to {@link GenericDocument}. */ 258 @CanIgnoreReturnValue setSchemaType(@onNull String schemaType)259 public @NonNull Builder setSchemaType(@NonNull String schemaType) { 260 resetIfBuilt(); 261 mSchemaType = Objects.requireNonNull(schemaType); 262 return this; 263 } 264 265 /** 266 * Resets all values contained in the VisibilityConfig with the values from the given 267 * VisibiltiyConfig. 268 */ 269 @CanIgnoreReturnValue setVisibilityConfig( @onNull SchemaVisibilityConfig schemaVisibilityConfig)270 public @NonNull Builder setVisibilityConfig( 271 @NonNull SchemaVisibilityConfig schemaVisibilityConfig) { 272 resetIfBuilt(); 273 mVisibilityConfigBuilder = new SchemaVisibilityConfig.Builder(schemaVisibilityConfig); 274 return this; 275 } 276 277 /** Sets whether this schema has opted out of platform surfacing. */ 278 @CanIgnoreReturnValue setNotDisplayedBySystem(boolean notDisplayedBySystem)279 public @NonNull Builder setNotDisplayedBySystem(boolean notDisplayedBySystem) { 280 resetIfBuilt(); 281 mIsNotDisplayedBySystem = notDisplayedBySystem; 282 return this; 283 } 284 285 /** 286 * Add {@link PackageIdentifier} of packages which has access to this schema. 287 * 288 * @see SchemaVisibilityConfig.Builder#addAllowedPackage 289 */ 290 @CanIgnoreReturnValue addVisibleToPackage(@onNull PackageIdentifier packageIdentifier)291 public @NonNull Builder addVisibleToPackage(@NonNull PackageIdentifier packageIdentifier) { 292 resetIfBuilt(); 293 mVisibilityConfigBuilder.addAllowedPackage(packageIdentifier); 294 return this; 295 } 296 297 /** 298 * Clears the list of packages which have access to this schema. 299 * 300 * @see SchemaVisibilityConfig.Builder#clearAllowedPackages 301 */ 302 @CanIgnoreReturnValue clearVisibleToPackages()303 public @NonNull Builder clearVisibleToPackages() { 304 resetIfBuilt(); 305 mVisibilityConfigBuilder.clearAllowedPackages(); 306 return this; 307 } 308 309 /** 310 * Adds a set of required Android {@link android.Manifest.permission} combination a package 311 * needs to hold to access the schema. 312 * 313 * @see SchemaVisibilityConfig.Builder#addRequiredPermissions 314 */ 315 @CanIgnoreReturnValue addVisibleToPermissions( @onNull Set<Integer> visibleToPermissions)316 public @NonNull Builder addVisibleToPermissions( 317 @NonNull Set<Integer> visibleToPermissions) { 318 resetIfBuilt(); 319 mVisibilityConfigBuilder.addRequiredPermissions(visibleToPermissions); 320 return this; 321 } 322 323 /** 324 * Clears all required permissions combinations set to this {@link SchemaVisibilityConfig}. 325 * 326 * @see SchemaVisibilityConfig.Builder#clearRequiredPermissions 327 */ 328 @CanIgnoreReturnValue clearVisibleToPermissions()329 public @NonNull Builder clearVisibleToPermissions() { 330 resetIfBuilt(); 331 mVisibilityConfigBuilder.clearRequiredPermissions(); 332 return this; 333 } 334 335 /** 336 * Specify that this schema should be publicly available, to the same packages that have 337 * visibility to the package passed as a parameter. This visibility is determined by the 338 * result of {@link android.content.pm.PackageManager#canPackageQuery}. 339 * 340 * @see SchemaVisibilityConfig.Builder#setPubliclyVisibleTargetPackage 341 */ 342 @CanIgnoreReturnValue setPubliclyVisibleTargetPackage( @ullable PackageIdentifier packageIdentifier)343 public @NonNull Builder setPubliclyVisibleTargetPackage( 344 @Nullable PackageIdentifier packageIdentifier) { 345 resetIfBuilt(); 346 mVisibilityConfigBuilder.setPubliclyVisibleTargetPackage(packageIdentifier); 347 return this; 348 } 349 350 /** 351 * Add the {@link SchemaVisibilityConfig} for a caller need to match to access the schema 352 * this {@link InternalVisibilityConfig} represents. 353 * 354 * <p>You can call this method repeatedly to add multiple {@link SchemaVisibilityConfig}, 355 * and the querier will have access if they match ANY of the {@link SchemaVisibilityConfig}. 356 * 357 * @param schemaVisibilityConfig The {@link SchemaVisibilityConfig} hold all requirements 358 * that a call must match to access the schema. 359 */ 360 @CanIgnoreReturnValue addVisibleToConfig( @onNull SchemaVisibilityConfig schemaVisibilityConfig)361 public @NonNull Builder addVisibleToConfig( 362 @NonNull SchemaVisibilityConfig schemaVisibilityConfig) { 363 Objects.requireNonNull(schemaVisibilityConfig); 364 resetIfBuilt(); 365 mVisibleToConfigs.add(schemaVisibilityConfig); 366 return this; 367 } 368 369 /** Clears the set of {@link SchemaVisibilityConfig} which have access to this schema. */ 370 @CanIgnoreReturnValue clearVisibleToConfig()371 public @NonNull Builder clearVisibleToConfig() { 372 resetIfBuilt(); 373 mVisibleToConfigs.clear(); 374 return this; 375 } 376 resetIfBuilt()377 private void resetIfBuilt() { 378 if (mBuilt) { 379 mVisibleToConfigs = new ArrayList<>(mVisibleToConfigs); 380 mBuilt = false; 381 } 382 } 383 384 /** Build a {@link InternalVisibilityConfig} */ build()385 public @NonNull InternalVisibilityConfig build() { 386 mBuilt = true; 387 return new InternalVisibilityConfig( 388 mSchemaType, 389 mIsNotDisplayedBySystem, 390 mVisibilityConfigBuilder.build(), 391 mVisibleToConfigs); 392 } 393 } 394 } 395