diff options
| author | 2021-05-13 05:33:54 +0000 | |
|---|---|---|
| committer | 2021-05-13 05:33:54 +0000 | |
| commit | 332dd22ff97cac8a5cf1942a5f28447a163c6ab4 (patch) | |
| tree | 4918152bbf05e6cfdbd8100bfde98917e45ca199 | |
| parent | 5ac4488fecbccb9779b51798d4f3c9c0c7e06fb3 (diff) | |
| parent | 487406300abb7c8c6c664ea774deb3931744acf1 (diff) | |
Merge "Refactor VisibilityStore's docs and maps to separate objects." into sc-dev
5 files changed, 381 insertions, 161 deletions
diff --git a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java index 0f643c513888..b7e21591a86c 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/VisibilityStore.java @@ -36,6 +36,10 @@ import android.util.ArraySet; import android.util.Log; import com.android.server.appsearch.external.localstorage.util.PrefixUtil; +import com.android.server.appsearch.visibilitystore.NotPlatformSurfaceableMap; +import com.android.server.appsearch.visibilitystore.PackageAccessibleDocument; +import com.android.server.appsearch.visibilitystore.PackageAccessibleMap; +import com.android.server.appsearch.visibilitystore.VisibilityDocument; import com.google.android.icing.proto.PersistType; @@ -74,74 +78,10 @@ public class VisibilityStore { /** No-op user id that won't have any visibility settings. */ public static final int NO_OP_USER_ID = -1; - /** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */ - private static final String VISIBILITY_TYPE = "VisibilityType"; - /** Version for the visibility schema */ private static final int SCHEMA_VERSION = 0; /** - * Property that holds the list of platform-hidden schemas, as part of the visibility settings. - */ - private static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable"; - - /** Property that holds nested documents of package accessible schemas. */ - private static final String PACKAGE_ACCESSIBLE_PROPERTY = "packageAccessible"; - - /** Schema type for nested documents that hold package accessible information. */ - private static final String PACKAGE_ACCESSIBLE_TYPE = "PackageAccessibleType"; - - /** Property that holds the package name that can access a schema. */ - private static final String PACKAGE_NAME_PROPERTY = "packageName"; - - /** Property that holds the SHA 256 certificate of the app that can access a schema. */ - private static final String SHA_256_CERT_PROPERTY = "sha256Cert"; - - /** Property that holds the prefixed schema type that is accessible by some package. */ - private static final String ACCESSIBLE_SCHEMA_PROPERTY = "accessibleSchema"; - - /** Schema for the VisibilityStore's documents. */ - private static final AppSearchSchema VISIBILITY_SCHEMA = - new AppSearchSchema.Builder(VISIBILITY_TYPE) - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder( - NOT_PLATFORM_SURFACEABLE_PROPERTY) - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) - .build()) - .addProperty( - new AppSearchSchema.DocumentPropertyConfig.Builder( - PACKAGE_ACCESSIBLE_PROPERTY, PACKAGE_ACCESSIBLE_TYPE) - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) - .build()) - .build(); - - /** - * Schema for package accessible documents, these will be nested in a top-level visibility - * document. - */ - private static final AppSearchSchema PACKAGE_ACCESSIBLE_SCHEMA = - new AppSearchSchema.Builder(PACKAGE_ACCESSIBLE_TYPE) - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY) - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .build()) - .addProperty( - new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY) - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .build()) - .addProperty( - new AppSearchSchema.StringPropertyConfig.Builder( - ACCESSIBLE_SCHEMA_PROPERTY) - .setCardinality( - AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) - .build()) - .build(); - - /** * These cannot have any of the special characters used by AppSearchImpl (e.g. {@code * AppSearchImpl#PACKAGE_DELIMITER} or {@code AppSearchImpl#DATABASE_DELIMITER}. */ @@ -177,24 +117,12 @@ public class VisibilityStore { // platform-surfaceable content. private int mGlobalQuerierUid; - /** - * Maps prefixes to the set of schemas that are platform-hidden within that prefix. All schemas - * in the map are prefixed. - * - * <p>Although the prefix key isn't used for lookup, it's helpful in ensuring that all previous - * visibility settings for a prefix are completely overridden by new visibility settings. - */ - private final Map<String, Set<String>> mNotPlatformSurfaceableMap = new ArrayMap<>(); + /** Stores the schemas that are platform-hidden. All values are prefixed. */ + private final NotPlatformSurfaceableMap mNotPlatformSurfaceableMap = + new NotPlatformSurfaceableMap(); - /** - * Maps prefixes to a an internal map. The internal map maps prefixed schemas to the set of - * PackageIdentifiers that have access to that schema. - * - * <p>Although the prefix key isn't used for lookup, it's helpful in ensuring that all previous - * visibility settings for a prefix are completely overridden by new visibility settings. - */ - private final Map<String, Map<String, Set<PackageIdentifier>>> mPackageAccessibleMap = - new ArrayMap<>(); + /** Stores the schemas that are package accessible. All values are prefixed. */ + private final PackageAccessibleMap mPackageAccessibleMap = new PackageAccessibleMap(); /** * Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()} @@ -228,9 +156,9 @@ public class VisibilityStore { boolean hasVisibilityType = false; boolean hasPackageAccessibleType = false; for (AppSearchSchema schema : getSchemaResponse.getSchemas()) { - if (schema.getSchemaType().equals(VISIBILITY_TYPE)) { + if (schema.getSchemaType().equals(VisibilityDocument.SCHEMA_TYPE)) { hasVisibilityType = true; - } else if (schema.getSchemaType().equals(PACKAGE_ACCESSIBLE_TYPE)) { + } else if (schema.getSchemaType().equals(PackageAccessibleDocument.SCHEMA_TYPE)) { hasPackageAccessibleType = true; } @@ -244,7 +172,7 @@ public class VisibilityStore { mAppSearchImpl.setSchema( PACKAGE_NAME, DATABASE_NAME, - Arrays.asList(VISIBILITY_SCHEMA, PACKAGE_ACCESSIBLE_SCHEMA), + Arrays.asList(VisibilityDocument.SCHEMA, PackageAccessibleDocument.SCHEMA), /*schemasNotPlatformSurfaceable=*/ Collections.emptyList(), /*schemasPackageAccessible=*/ Collections.emptyMap(), /*forceOverride=*/ false, @@ -261,40 +189,34 @@ public class VisibilityStore { try { // Note: We use the other clients' prefixed names as ids - GenericDocument document = + VisibilityDocument visibilityDocument = new VisibilityDocument( mAppSearchImpl.getDocument( PACKAGE_NAME, DATABASE_NAME, NAMESPACE, /*id=*/ addIdPrefix(prefix), - /*typePropertyPaths=*/ Collections.emptyMap()); + /*typePropertyPaths=*/ Collections.emptyMap())); // Update platform visibility settings String[] notPlatformSurfaceableSchemas = - document.getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY); + visibilityDocument.getNotPlatformSurfaceableSchemas(); if (notPlatformSurfaceableSchemas != null) { - mNotPlatformSurfaceableMap.put( - prefix, new ArraySet<>(Arrays.asList(notPlatformSurfaceableSchemas))); + mNotPlatformSurfaceableMap.setNotPlatformSurfaceable( + prefix, + new ArraySet<>(notPlatformSurfaceableSchemas)); } // Update 3p package visibility settings Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>(); GenericDocument[] packageAccessibleDocuments = - document.getPropertyDocumentArray(PACKAGE_ACCESSIBLE_PROPERTY); + visibilityDocument.getPackageAccessibleSchemas(); if (packageAccessibleDocuments != null) { for (int i = 0; i < packageAccessibleDocuments.length; i++) { - String packageName = - packageAccessibleDocuments[i].getPropertyString( - PACKAGE_NAME_PROPERTY); - byte[] sha256Cert = - packageAccessibleDocuments[i].getPropertyBytes( - SHA_256_CERT_PROPERTY); + PackageAccessibleDocument packageAccessibleDocument = + new PackageAccessibleDocument(packageAccessibleDocuments[i]); PackageIdentifier packageIdentifier = - new PackageIdentifier(packageName, sha256Cert); - - String prefixedSchema = - packageAccessibleDocuments[i].getPropertyString( - ACCESSIBLE_SCHEMA_PROPERTY); + packageAccessibleDocument.getPackageIdentifier(); + String prefixedSchema = packageAccessibleDocument.getAccessibleSchemaType(); Set<PackageIdentifier> packageIdentifiers = schemaToPackageIdentifierMap.get(prefixedSchema); if (packageIdentifiers == null) { @@ -304,7 +226,7 @@ public class VisibilityStore { schemaToPackageIdentifierMap.put(prefixedSchema, packageIdentifiers); } } - mPackageAccessibleMap.put(prefix, schemaToPackageIdentifierMap); + mPackageAccessibleMap.setPackageAccessible(prefix, schemaToPackageIdentifierMap); } catch (AppSearchException e) { if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) { // TODO(b/172068212): This indicates some desync error. We were expecting a @@ -339,38 +261,30 @@ public class VisibilityStore { Objects.requireNonNull(schemasPackageAccessible); // Persist the document - GenericDocument.Builder<?> visibilityDocument = - new GenericDocument.Builder<>( - NAMESPACE, /*id=*/ addIdPrefix(prefix), VISIBILITY_TYPE); + VisibilityDocument.Builder visibilityDocument = + new VisibilityDocument.Builder(NAMESPACE, /*id=*/ addIdPrefix(prefix)); if (!schemasNotPlatformSurfaceable.isEmpty()) { - visibilityDocument.setPropertyString( - NOT_PLATFORM_SURFACEABLE_PROPERTY, + visibilityDocument.setSchemasNotPlatformSurfaceable( schemasNotPlatformSurfaceable.toArray(new String[0])); } Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>(); - List<GenericDocument> packageAccessibleDocuments = new ArrayList<>(); + List<PackageAccessibleDocument> packageAccessibleDocuments = new ArrayList<>(); for (Map.Entry<String, List<PackageIdentifier>> entry : schemasPackageAccessible.entrySet()) { for (int i = 0; i < entry.getValue().size(); i++) { - GenericDocument packageAccessibleDocument = new GenericDocument.Builder<>( - NAMESPACE, /*id=*/ "", PACKAGE_ACCESSIBLE_TYPE) - .setPropertyString( - PACKAGE_NAME_PROPERTY, - entry.getValue().get(i).getPackageName()) - .setPropertyBytes( - SHA_256_CERT_PROPERTY, - entry.getValue().get(i).getSha256Certificate()) - .setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, entry.getKey()) - .build(); + PackageAccessibleDocument packageAccessibleDocument = + new PackageAccessibleDocument.Builder(NAMESPACE, /*id=*/ "") + .setAccessibleSchemaType(entry.getKey()) + .setPackageIdentifier(entry.getValue().get(i)) + .build(); packageAccessibleDocuments.add(packageAccessibleDocument); } schemaToPackageIdentifierMap.put(entry.getKey(), new ArraySet<>(entry.getValue())); } if (!packageAccessibleDocuments.isEmpty()) { - visibilityDocument.setPropertyDocument( - PACKAGE_ACCESSIBLE_PROPERTY, - packageAccessibleDocuments.toArray(new GenericDocument[0])); + visibilityDocument.setPackageAccessibleSchemas( + packageAccessibleDocuments.toArray(new PackageAccessibleDocument[0])); } mAppSearchImpl.putDocument( @@ -379,8 +293,8 @@ public class VisibilityStore { mAppSearchImpl.persistToDisk(PersistType.Code.LITE); // Update derived data structures. - mNotPlatformSurfaceableMap.put(prefix, schemasNotPlatformSurfaceable); - mPackageAccessibleMap.put(prefix, schemaToPackageIdentifierMap); + mNotPlatformSurfaceableMap.setNotPlatformSurfaceable(prefix, schemasNotPlatformSurfaceable); + mPackageAccessibleMap.setPackageAccessible(prefix, schemaToPackageIdentifierMap); } /** Checks whether {@code prefixedSchema} can be searched over by the {@code callerUid}. */ @@ -389,10 +303,14 @@ public class VisibilityStore { Objects.requireNonNull(prefix); Objects.requireNonNull(prefixedSchema); + if (prefix.equals(VISIBILITY_STORE_PREFIX)) { + return false; // VisibilityStore schemas are for internal bookkeeping. + } + // We compare appIds here rather than direct uids because the package's uid may change based // on the user that's running. if (UserHandle.isSameApp(mGlobalQuerierUid, callerUid) - && isSchemaPlatformSurfaceable(prefix, prefixedSchema)) { + && mNotPlatformSurfaceableMap.isSchemaPlatformSurfaceable(prefix, prefixedSchema)) { return true; } @@ -401,29 +319,6 @@ public class VisibilityStore { } /** - * Returns whether the caller has platform query privileges, and if so, that the schema is - * surfaceable on the platform. - */ - private boolean isSchemaPlatformSurfaceable( - @NonNull String prefix, @NonNull String prefixedSchema) { - if (prefix.equals(VISIBILITY_STORE_PREFIX)) { - // VisibilityStore schemas are for internal bookkeeping. - return false; - } - - Set<String> notPlatformSurfaceableSchemas = mNotPlatformSurfaceableMap.get(prefix); - if (notPlatformSurfaceableSchemas == null) { - // No schemas were opted out of being platform-surfaced. So by default, it can be - // surfaced. - return true; - } - - // Some schemas were opted out of being platform-surfaced. As long as this schema - // isn't one of those opt-outs, it's surfaceable. - return !notPlatformSurfaceableSchemas.contains(prefixedSchema); - } - - /** * Returns whether the schema is accessible by the {@code callerUid}. Checks that the callerUid * has one of the allowed PackageIdentifier's package. And if so, that the package also has the * matching certificate. @@ -434,20 +329,8 @@ public class VisibilityStore { */ private boolean isSchemaPackageAccessible( @NonNull String prefix, @NonNull String prefixedSchema, int callerUid) { - Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = - mPackageAccessibleMap.get(prefix); - if (schemaToPackageIdentifierMap == null) { - // No schemas under this prefix have granted package access, return early. - return false; - } - Set<PackageIdentifier> packageIdentifiers = - schemaToPackageIdentifierMap.get(prefixedSchema); - if (packageIdentifiers == null) { - // No package identifiers were granted access for this schema, return early. - return false; - } - + mPackageAccessibleMap.getAccessiblePackages(prefix, prefixedSchema); for (PackageIdentifier packageIdentifier : packageIdentifiers) { // Check that the caller uid matches this allowlisted PackageIdentifier. // TODO(b/169883602): Consider caching the UIDs of packages. Looking this up in the @@ -467,7 +350,6 @@ public class VisibilityStore { return true; } } - // If we can't verify the schema is package accessible, default to no access. return false; } diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java new file mode 100644 index 000000000000..5afdda280c35 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.appsearch.visibilitystore; + +import android.annotation.NonNull; +import android.util.ArrayMap; + +import java.util.Map; +import java.util.Set; + +/** + * Stores information about what types are hidden from platform surfaces through the + * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} API. + * + * This object is not thread safe. + * @hide + */ +public class NotPlatformSurfaceableMap { + /** + * Maps prefixes to the set of prefixed schemas that are platform-hidden within that prefix. + */ + private final Map<String, Set<String>> mMap = new ArrayMap<>(); + + /** + * Sets the prefixed schemas that are opted out of platform surfacing for the prefix. + * + * <p>Any existing mappings for this prefix are overwritten. + */ + public void setNotPlatformSurfaceable(@NonNull String prefix, @NonNull Set<String> schemas) { + mMap.put(prefix, schemas); + } + + /** + * Returns whether the given prefixed schema is platform surfaceable (has not opted out) in the + * given prefix. + */ + public boolean isSchemaPlatformSurfaceable(@NonNull String prefix, @NonNull String schemaType) { + Set<String> schemaTypes = mMap.get(prefix); + if (schemaTypes == null) { + // No opt-outs for this prefix + return true; + } + // Some schemas were opted out of being platform-surfaced. As long as this schema + // isn't one of those opt-outs, it's surfaceable. + return !schemaTypes.contains(schemaType); + } + + /** Discards all data in the map. */ + public void clear() { + mMap.clear(); + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java new file mode 100644 index 000000000000..5601ef93490d --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.appsearch.visibilitystore; + +import android.annotation.NonNull; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; +import android.app.appsearch.PackageIdentifier; + +import androidx.annotation.Nullable; + +/** + * Holds configuration about a package+cert that can access a schema. + * + * @see android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage + * @hide + */ +public class PackageAccessibleDocument extends GenericDocument { + /** Schema type for nested documents that hold package accessible information. */ + public static final String SCHEMA_TYPE = "PackageAccessibleType"; + + /** Property that holds the package name that can access a schema. */ + private static final String PACKAGE_NAME_PROPERTY = "packageName"; + + /** Property that holds the SHA 256 certificate of the app that can access a schema. */ + private static final String SHA_256_CERT_PROPERTY = "sha256Cert"; + + /** Property that holds the prefixed schema type that is accessible by some package. */ + private static final String ACCESSIBLE_SCHEMA_PROPERTY = "accessibleSchema"; + + /** + * Schema for package accessible documents, these will be nested in a top-level + * {@link VisibilityDocument}. + * + * <p>NOTE: If you update this, also update + * {@link com.android.server.appsearch.external.localstorage.VisibilityStore#SCHEMA_VERSION} + */ + public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) + .addProperty(new AppSearchSchema.StringPropertyConfig.Builder(PACKAGE_NAME_PROPERTY) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .build()) + .addProperty(new AppSearchSchema.BytesPropertyConfig.Builder(SHA_256_CERT_PROPERTY) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .build()) + .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( + ACCESSIBLE_SCHEMA_PROPERTY) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL) + .build()) + .build(); + + public PackageAccessibleDocument(@NonNull GenericDocument genericDocument) { + super(genericDocument); + } + + @Nullable + public String getAccessibleSchemaType() { + return getPropertyString(ACCESSIBLE_SCHEMA_PROPERTY); + } + + /** Gets which package is able to access {@link #getAccessibleSchemaType} */ + @NonNull + public PackageIdentifier getPackageIdentifier() { + String packageName = getPropertyString(PACKAGE_NAME_PROPERTY); + byte[] sha256Cert = getPropertyBytes(SHA_256_CERT_PROPERTY); + return new PackageIdentifier(packageName, sha256Cert); + } + + /** Builder for {@link PackageAccessibleDocument} instances. */ + public static class Builder extends GenericDocument.Builder<PackageAccessibleDocument.Builder> { + public Builder(@NonNull String namespace, @NonNull String id) { + super(namespace, id, SCHEMA_TYPE); + } + + /** Sets which prefixed schema type is accessible by the package */ + @NonNull + public Builder setAccessibleSchemaType(@NonNull String schemaType) { + return setPropertyString(ACCESSIBLE_SCHEMA_PROPERTY, schemaType); + } + + /** Sets which package is able to access the {@link #setAccessibleSchemaType}. */ + @NonNull + public Builder setPackageIdentifier(@NonNull PackageIdentifier packageIdentifier) { + return setPropertyString(PACKAGE_NAME_PROPERTY, packageIdentifier.getPackageName()) + .setPropertyBytes(SHA_256_CERT_PROPERTY, + packageIdentifier.getSha256Certificate()); + } + + @Override + @NonNull + public PackageAccessibleDocument build() { + return new PackageAccessibleDocument(super.build()); + } + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java new file mode 100644 index 000000000000..e90e8bf3f118 --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java @@ -0,0 +1,74 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.appsearch.visibilitystore; + +import android.annotation.NonNull; +import android.app.appsearch.PackageIdentifier; +import android.util.ArrayMap; + +import java.util.Collections; +import java.util.Map; +import java.util.Set; + +/** + * Stores information about what types are accessible to which packages through the + * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} API. + * + * This object is not thread safe. + * @hide + */ +public class PackageAccessibleMap { + /** + * Maps prefixes to prefixed schema types to PackageIdentifiers that have access to that schema. + */ + private final Map<String, Map<String, Set<PackageIdentifier>>> mMap = new ArrayMap<>(); + + /** + * Sets the prefixed schemas that have package visibility in the given prefix. + * + * <p>Any existing mappings for this prefix are overwritten. + */ + public void setPackageAccessible( + @NonNull String prefix, + @NonNull Map<String, Set<PackageIdentifier>> schemaToPackageIdentifier) { + mMap.put(prefix, schemaToPackageIdentifier); + } + + /** + * Returns the set of all {@link android.app.appsearch.PackageIdentifier}s which can access the + * given schema type. + * + * <p>If no such settings exist, returns the empty set. + */ + @NonNull + public Set<PackageIdentifier> getAccessiblePackages( + @NonNull String prefix, @NonNull String schemaType) { + Map<String, Set<PackageIdentifier>> schemaTypeToVisibility = mMap.get(prefix); + if (schemaTypeToVisibility == null) { + return Collections.emptySet(); + } + Set<PackageIdentifier> accessiblePackages = schemaTypeToVisibility.get(schemaType); + if (accessiblePackages == null) { + return Collections.emptySet(); + } + return accessiblePackages; + } + + /** Discards all data in the map. */ + public void clear() { + mMap.clear(); + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java new file mode 100644 index 000000000000..327ce854fc5b --- /dev/null +++ b/apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/VisibilityDocument.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.server.appsearch.visibilitystore; + +import android.annotation.NonNull; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; + +import androidx.annotation.Nullable; + +/** + * Holds the visibility settings that apply to a package's databases. + * @hide + */ +public class VisibilityDocument extends GenericDocument { + /** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */ + public static final String SCHEMA_TYPE = "VisibilityType"; + + /** + * Property that holds the list of platform-hidden schemas, as part of the visibility settings. + */ + private static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable"; + + /** Property that holds nested documents of package accessible schemas. */ + private static final String PACKAGE_ACCESSIBLE_PROPERTY = "packageAccessible"; + + /** + * Schema for the VisibilityStore's documents. + * + * <p>NOTE: If you update this, also update + * {@link com.android.server.appsearch.external.localstorage.VisibilityStore#SCHEMA_VERSION} + */ + public static final AppSearchSchema SCHEMA = new AppSearchSchema.Builder(SCHEMA_TYPE) + .addProperty(new AppSearchSchema.StringPropertyConfig.Builder( + NOT_PLATFORM_SURFACEABLE_PROPERTY) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .build()) + .addProperty(new AppSearchSchema.DocumentPropertyConfig.Builder( + PACKAGE_ACCESSIBLE_PROPERTY, PackageAccessibleDocument.SCHEMA_TYPE) + .setCardinality(AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED) + .build()) + .build(); + + public VisibilityDocument(@NonNull GenericDocument genericDocument) { + super(genericDocument); + } + + @Nullable + public String[] getNotPlatformSurfaceableSchemas() { + return getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY); + } + + @Nullable + public GenericDocument[] getPackageAccessibleSchemas() { + return getPropertyDocumentArray(PACKAGE_ACCESSIBLE_PROPERTY); + } + + /** Builder for {@link VisibilityDocument}. */ + public static class Builder extends GenericDocument.Builder<VisibilityDocument.Builder> { + public Builder(@NonNull String namespace, @NonNull String id) { + super(namespace, id, SCHEMA_TYPE); + } + + /** Sets which prefixed schemas have opted out of platform surfacing. */ + @NonNull + public Builder setSchemasNotPlatformSurfaceable( + @NonNull String[] notPlatformSurfaceableSchemas) { + return setPropertyString( + NOT_PLATFORM_SURFACEABLE_PROPERTY, notPlatformSurfaceableSchemas); + } + + /** Sets which prefixed schemas have configured package access. */ + @NonNull + public Builder setPackageAccessibleSchemas( + @NonNull PackageAccessibleDocument[] packageAccessibleSchemas) { + return setPropertyDocument(PACKAGE_ACCESSIBLE_PROPERTY, packageAccessibleSchemas); + } + } +} |