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