Loading apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +6 −41 Original line number Diff line number Diff line Loading @@ -16,18 +16,15 @@ package com.android.server.appsearch; import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; import android.content.pm.PackageManager; import android.os.Environment; import android.os.UserHandle; import android.util.ArrayMap; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.server.appsearch.external.localstorage.AppSearchImpl; import com.android.server.appsearch.external.localstorage.AppSearchLogger; Loading @@ -49,12 +46,6 @@ public final class ImplInstanceManager { @GuardedBy("mInstancesLocked") private final Map<UserHandle, AppSearchImpl> mInstancesLocked = new ArrayMap<>(); private final String mGlobalQuerierPackage; private ImplInstanceManager(@NonNull String globalQuerierPackage) { mGlobalQuerierPackage = globalQuerierPackage; } /** * Gets an instance of ImplInstanceManager to be used. * Loading @@ -66,9 +57,7 @@ public final class ImplInstanceManager { if (sImplInstanceManager == null) { synchronized (ImplInstanceManager.class) { if (sImplInstanceManager == null) { sImplInstanceManager = new ImplInstanceManager( getGlobalAppSearchDataQuerierPackageName(context)); sImplInstanceManager = new ImplInstanceManager(); } } } Loading @@ -91,7 +80,7 @@ public final class ImplInstanceManager { * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and * one will be created. * * @param context The context * @param context The system context * @param userHandle The multi-user handle of the device user calling AppSearch * @return An initialized {@link AppSearchImpl} for this user */ Loading @@ -106,7 +95,8 @@ public final class ImplInstanceManager { synchronized (mInstancesLocked) { AppSearchImpl instance = mInstancesLocked.get(userHandle); if (instance == null) { instance = createImpl(context, userHandle, logger); Context userContext = context.createContextAsUser(userHandle, /*flags=*/ 0); instance = createImpl(userContext, userHandle, logger); mInstancesLocked.put(userHandle, instance); } return instance; Loading Loading @@ -177,7 +167,7 @@ public final class ImplInstanceManager { } private AppSearchImpl createImpl( @NonNull Context context, @NonNull Context userContext, @NonNull UserHandle userHandle, @Nullable AppSearchLogger logger) throws AppSearchException { Loading @@ -185,33 +175,8 @@ public final class ImplInstanceManager { // TODO(b/181787682): Swap AppSearchImpl and VisibilityStore to accept a UserHandle too return AppSearchImpl.create( appSearchDir, context, userContext, userHandle.getIdentifier(), mGlobalQuerierPackage, /*logger=*/ null); } /** * Returns the global querier package if it's a system package. Otherwise, empty string. * * @param context Context of the system service. */ @NonNull private static String getGlobalAppSearchDataQuerierPackageName(@NonNull Context context) { String globalAppSearchDataQuerierPackage = context.getString(R.string.config_globalAppSearchDataQuerierPackage); try { if (context.getPackageManager() .getPackageInfoAsUser( globalAppSearchDataQuerierPackage, MATCH_FACTORY_ONLY, UserHandle.USER_SYSTEM) == null) { return ""; } } catch (PackageManager.NameNotFoundException e) { return ""; } return globalAppSearchDataQuerierPackage; } } apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +94 −55 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.server.appsearch.external.localstorage; import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.addPrefixToDocument; import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPackagePrefix; import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix; import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getDatabaseName; import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getPackageName; Loading Loading @@ -60,6 +59,7 @@ import com.android.server.appsearch.external.localstorage.stats.InitializeStats; import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; import com.android.server.appsearch.external.localstorage.stats.RemoveStats; import com.android.server.appsearch.external.localstorage.stats.SearchStats; import com.android.server.appsearch.visibilitystore.VisibilityStore; import com.google.android.icing.IcingSearchEngine; import com.google.android.icing.proto.DeleteByQueryResultProto; Loading Loading @@ -98,6 +98,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; Loading Loading @@ -199,14 +200,12 @@ public final class AppSearchImpl implements Closeable { @NonNull public static AppSearchImpl create( @NonNull File icingDir, @NonNull Context context, @NonNull Context userContext, int userId, @NonNull String globalQuerierPackage, @Nullable AppSearchLogger logger) throws AppSearchException { Objects.requireNonNull(icingDir); Objects.requireNonNull(context); Objects.requireNonNull(globalQuerierPackage); Objects.requireNonNull(userContext); long totalLatencyStartMillis = SystemClock.elapsedRealtime(); InitializeStats.Builder initStatsBuilder = null; Loading @@ -215,8 +214,7 @@ public final class AppSearchImpl implements Closeable { } AppSearchImpl appSearchImpl = new AppSearchImpl( icingDir, context, userId, globalQuerierPackage, initStatsBuilder); new AppSearchImpl(icingDir, userContext, userId, initStatsBuilder); long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime(); appSearchImpl.initializeVisibilityStore(); Loading @@ -239,9 +237,8 @@ public final class AppSearchImpl implements Closeable { /** @param initStatsBuilder collects stats for initialization if provided. */ private AppSearchImpl( @NonNull File icingDir, @NonNull Context context, @NonNull Context userContext, int userId, @NonNull String globalQuerierPackage, @Nullable InitializeStats.Builder initStatsBuilder) throws AppSearchException { mReadWriteLock.writeLock().lock(); Loading @@ -259,8 +256,7 @@ public final class AppSearchImpl implements Closeable { "Constructing IcingSearchEngine, response", Objects.hashCode(mIcingSearchEngineLocked)); mVisibilityStoreLocked = new VisibilityStore(this, context, userId, globalQuerierPackage); mVisibilityStoreLocked = new VisibilityStore(this, userContext, userId); // The core initialization procedure. If any part of this fails, we bail into // resetLocked(), deleting all data (but hopefully allowing AppSearchImpl to come up). Loading Loading @@ -495,7 +491,8 @@ public final class AppSearchImpl implements Closeable { } mVisibilityStoreLocked.setVisibility( prefix, packageName, databaseName, prefixedSchemasNotPlatformSurfaceable, prefixedSchemasPackageAccessible); Loading Loading @@ -844,17 +841,17 @@ public final class AppSearchImpl implements Closeable { try { throwIfClosedLocked(); // Convert package filters to prefix filters Set<String> packageFilters = new ArraySet<>(searchSpec.getFilterPackageNames()); Set<String> prefixFilters = new ArraySet<>(); Set<String> allPrefixes = mNamespaceMapLocked.keySet(); if (packageFilters.isEmpty()) { // Client didn't restrict their search over packages. Try to query over all // packages/prefixes prefixFilters = allPrefixes; prefixFilters = mNamespaceMapLocked.keySet(); } else { // Client did restrict their search over packages. Only include the prefixes that // belong to the specified packages. for (String prefix : allPrefixes) { for (String prefix : mNamespaceMapLocked.keySet()) { String packageName = getPackageName(prefix); if (packageFilters.contains(packageName)) { prefixFilters.add(prefix); Loading @@ -862,41 +859,50 @@ public final class AppSearchImpl implements Closeable { } } // Find which schemas the client is allowed to query over. Set<String> allowedPrefixedSchemas = new ArraySet<>(); List<String> schemaFilters = searchSpec.getFilterSchemas(); // Convert schema filters to prefixed schema filters ArraySet<String> prefixedSchemaFilters = new ArraySet<>(); for (String prefix : prefixFilters) { String packageName = getPackageName(prefix); if (!schemaFilters.isEmpty()) { for (String schema : schemaFilters) { List<String> schemaFilters = searchSpec.getFilterSchemas(); if (schemaFilters.isEmpty()) { // Client didn't specify certain schemas to search over, check all schemas prefixedSchemaFilters.addAll(mSchemaMapLocked.get(prefix).keySet()); } else { // Client specified some schemas to search over, check each one String prefixedSchema = prefix + schema; if (packageName.equals(callerPackageName) || mVisibilityStoreLocked.isSchemaSearchableByCaller( prefix, prefixedSchema, callerUid)) { allowedPrefixedSchemas.add(prefixedSchema); } for (int i = 0; i < schemaFilters.size(); i++) { prefixedSchemaFilters.add(prefix + schemaFilters.get(i)); } } else { // Client didn't specify certain schemas to search over, check all schemas Map<String, SchemaTypeConfigProto> prefixedSchemas = mSchemaMapLocked.get(prefix); if (prefixedSchemas != null) { for (String prefixedSchema : prefixedSchemas.keySet()) { if (packageName.equals(callerPackageName) || mVisibilityStoreLocked.isSchemaSearchableByCaller( prefix, prefixedSchema, callerUid)) { allowedPrefixedSchemas.add(prefixedSchema); } } // Remove the schemas the client is not allowed to search over Iterator<String> prefixedSchemaIt = prefixedSchemaFilters.iterator(); while (prefixedSchemaIt.hasNext()) { String prefixedSchema = prefixedSchemaIt.next(); String packageName = getPackageName(prefixedSchema); boolean allow; if (packageName.equals(callerPackageName)) { // Callers can always retrieve their own data allow = true; } else { String databaseName = getDatabaseName(prefixedSchema); allow = mVisibilityStoreLocked.isSchemaSearchableByCaller( packageName, databaseName, prefixedSchema, callerPackageName, callerUid); } if (!allow) { prefixedSchemaIt.remove(); } } return doQueryLocked( prefixFilters, allowedPrefixedSchemas, prefixedSchemaFilters, queryExpression, searchSpec, sStatsBuilder); Loading Loading @@ -1404,20 +1410,47 @@ public final class AppSearchImpl implements Closeable { mReadWriteLock.writeLock().lock(); try { throwIfClosedLocked(); Set<String> existingPackages = getPackageToDatabases().keySet(); if (existingPackages.contains(packageName)) { existingPackages.remove(packageName); prunePackageData(existingPackages); } } finally { mReadWriteLock.writeLock().unlock(); } } /** * Remove all {@link AppSearchSchema}s and {@link GenericDocument}s that doesn't belong to any * of the given installed packages * * @param installedPackages The name of all installed package. * @throws AppSearchException if we cannot remove the data. */ public void prunePackageData(@NonNull Set<String> installedPackages) throws AppSearchException { mReadWriteLock.writeLock().lock(); try { throwIfClosedLocked(); Map<String, Set<String>> packageToDatabases = getPackageToDatabases(); if (installedPackages.containsAll(packageToDatabases.keySet())) { // No package got removed. We are good. return; } // Prune schema proto SchemaProto existingSchema = getSchemaProtoLocked(); SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder(); String prefix = createPackagePrefix(packageName); for (int i = 0; i < existingSchema.getTypesCount(); i++) { if (!existingSchema.getTypes(i).getSchemaType().startsWith(prefix)) { String packageName = getPackageName(existingSchema.getTypes(i).getSchemaType()); if (installedPackages.contains(packageName)) { newSchemaBuilder.addTypes(existingSchema.getTypes(i)); } } SchemaProto finalSchema = newSchemaBuilder.build(); // Apply schema, set force override to true to remove all schemas and documents under // that package. // Apply schema, set force override to true to remove all schemas and documents that // doesn't belong to any of these installed packages. mLogUtil.piiTrace( "clearPackageData.setSchema, request", finalSchema.getTypesCount(), Loading @@ -1432,6 +1465,20 @@ public final class AppSearchImpl implements Closeable { // Determine whether it succeeded. checkSuccess(setSchemaResultProto.getStatus()); // Prune cached maps for (Map.Entry<String, Set<String>> entry : packageToDatabases.entrySet()) { String packageName = entry.getKey(); Set<String> databaseNames = entry.getValue(); if (!installedPackages.contains(packageName) && databaseNames != null) { for (String databaseName : databaseNames) { String removedPrefix = createPrefix(packageName, databaseName); mSchemaMapLocked.remove(removedPrefix); mNamespaceMapLocked.remove(removedPrefix); } } } // TODO(b/145759910) clear visibility setting for package. } finally { mReadWriteLock.writeLock().unlock(); } Loading Loading @@ -1857,14 +1904,6 @@ public final class AppSearchImpl implements Closeable { return schemaProto.getSchema(); } /** Returns a set of all prefixes AppSearchImpl knows about. */ // TODO(b/180058203): Remove this method once platform has switched away from using this method. @GuardedBy("mReadWriteLock") @NonNull Set<String> getPrefixesLocked() { return mSchemaMapLocked.keySet(); } private static void addToMap( Map<String, Set<String>> map, String prefix, String prefixedValue) { Set<String> values = map.get(prefix); Loading apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java +28 −12 Original line number Diff line number Diff line Loading @@ -26,36 +26,52 @@ import java.util.Set; * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} API. * * This object is not thread safe. * @hide */ public class NotPlatformSurfaceableMap { class NotPlatformSurfaceableMap { /** * Maps prefixes to the set of prefixed schemas that are platform-hidden within that prefix. * Maps packages to databases to the set of prefixed schemas that are platform-hidden within * that database. */ private final Map<String, Set<String>> mMap = new ArrayMap<>(); private final Map<String, Map<String, Set<String>>> mMap = new ArrayMap<>(); /** * Sets the prefixed schemas that are opted out of platform surfacing for the prefix. * Sets the prefixed schemas that are opted out of platform surfacing for the database. * * <p>Any existing mappings for this prefix are overwritten. */ public void setNotPlatformSurfaceable(@NonNull String prefix, @NonNull Set<String> schemas) { mMap.put(prefix, schemas); public void setNotPlatformSurfaceable( @NonNull String packageName, @NonNull String databaseName, @NonNull Set<String> prefixedSchemas) { Map<String, Set<String>> databaseToSchemas = mMap.get(packageName); if (databaseToSchemas == null) { databaseToSchemas = new ArrayMap<>(); mMap.put(packageName, databaseToSchemas); } databaseToSchemas.put(databaseName, prefixedSchemas); } /** * Returns whether the given prefixed schema is platform surfaceable (has not opted out) in the * given prefix. * given database. */ public boolean isSchemaPlatformSurfaceable(@NonNull String prefix, @NonNull String schemaType) { Set<String> schemaTypes = mMap.get(prefix); public boolean isSchemaPlatformSurfaceable( @NonNull String packageName, @NonNull String databaseName, @NonNull String prefixedSchema) { Map<String, Set<String>> databaseToSchemaType = mMap.get(packageName); if (databaseToSchemaType == null) { // No opt-outs for this package return true; } Set<String> schemaTypes = databaseToSchemaType.get(databaseName); if (schemaTypes == null) { // No opt-outs for this prefix // No opt-outs for this database 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); return !schemaTypes.contains(prefixedSchema); } /** Discards all data in the map. */ Loading apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java +1 −2 Original line number Diff line number Diff line Loading @@ -26,9 +26,8 @@ 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 { class PackageAccessibleDocument extends GenericDocument { /** Schema type for nested documents that hold package accessible information. */ public static final String SCHEMA_TYPE = "PackageAccessibleType"; Loading apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java +26 −10 Original line number Diff line number Diff line Loading @@ -28,23 +28,31 @@ import java.util.Set; * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} API. * * This object is not thread safe. * @hide */ public class PackageAccessibleMap { class PackageAccessibleMap { /** * Maps prefixes to prefixed schema types to PackageIdentifiers that have access to that schema. * Maps packages to databases to prefixed schemas to PackageIdentifiers that have access to that * schema. */ private final Map<String, Map<String, Set<PackageIdentifier>>> mMap = new ArrayMap<>(); private final Map<String, Map<String, Map<String, Set<PackageIdentifier>>>> mMap = new ArrayMap<>(); /** * Sets the prefixed schemas that have package visibility in the given prefix. * Sets the prefixed schemas that have package visibility in the given database. * * <p>Any existing mappings for this prefix are overwritten. */ public void setPackageAccessible( @NonNull String prefix, @NonNull String packageName, @NonNull String databaseName, @NonNull Map<String, Set<PackageIdentifier>> schemaToPackageIdentifier) { mMap.put(prefix, schemaToPackageIdentifier); Map<String, Map<String, Set<PackageIdentifier>>> databaseToSchemaTypeToVisibility = mMap.get(packageName); if (databaseToSchemaTypeToVisibility == null) { databaseToSchemaTypeToVisibility = new ArrayMap<>(); mMap.put(packageName, databaseToSchemaTypeToVisibility); } databaseToSchemaTypeToVisibility.put(databaseName, schemaToPackageIdentifier); } /** Loading @@ -55,12 +63,20 @@ public class PackageAccessibleMap { */ @NonNull public Set<PackageIdentifier> getAccessiblePackages( @NonNull String prefix, @NonNull String schemaType) { Map<String, Set<PackageIdentifier>> schemaTypeToVisibility = mMap.get(prefix); @NonNull String packageName, @NonNull String databaseName, @NonNull String prefixedSchema) { Map<String, Map<String, Set<PackageIdentifier>>> databaseToSchemaTypeToVisibility = mMap.get(packageName); if (databaseToSchemaTypeToVisibility == null) { return Collections.emptySet(); } Map<String, Set<PackageIdentifier>> schemaTypeToVisibility = databaseToSchemaTypeToVisibility.get(databaseName); if (schemaTypeToVisibility == null) { return Collections.emptySet(); } Set<PackageIdentifier> accessiblePackages = schemaTypeToVisibility.get(schemaType); Set<PackageIdentifier> accessiblePackages = schemaTypeToVisibility.get(prefixedSchema); if (accessiblePackages == null) { return Collections.emptySet(); } Loading Loading
apex/appsearch/service/java/com/android/server/appsearch/ImplInstanceManager.java +6 −41 Original line number Diff line number Diff line Loading @@ -16,18 +16,15 @@ package com.android.server.appsearch; import static android.content.pm.PackageManager.MATCH_FACTORY_ONLY; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; import android.content.pm.PackageManager; import android.os.Environment; import android.os.UserHandle; import android.util.ArrayMap; import com.android.internal.R; import com.android.internal.annotations.GuardedBy; import com.android.server.appsearch.external.localstorage.AppSearchImpl; import com.android.server.appsearch.external.localstorage.AppSearchLogger; Loading @@ -49,12 +46,6 @@ public final class ImplInstanceManager { @GuardedBy("mInstancesLocked") private final Map<UserHandle, AppSearchImpl> mInstancesLocked = new ArrayMap<>(); private final String mGlobalQuerierPackage; private ImplInstanceManager(@NonNull String globalQuerierPackage) { mGlobalQuerierPackage = globalQuerierPackage; } /** * Gets an instance of ImplInstanceManager to be used. * Loading @@ -66,9 +57,7 @@ public final class ImplInstanceManager { if (sImplInstanceManager == null) { synchronized (ImplInstanceManager.class) { if (sImplInstanceManager == null) { sImplInstanceManager = new ImplInstanceManager( getGlobalAppSearchDataQuerierPackageName(context)); sImplInstanceManager = new ImplInstanceManager(); } } } Loading @@ -91,7 +80,7 @@ public final class ImplInstanceManager { * <p>If no AppSearchImpl instance exists for the unlocked user, Icing will be initialized and * one will be created. * * @param context The context * @param context The system context * @param userHandle The multi-user handle of the device user calling AppSearch * @return An initialized {@link AppSearchImpl} for this user */ Loading @@ -106,7 +95,8 @@ public final class ImplInstanceManager { synchronized (mInstancesLocked) { AppSearchImpl instance = mInstancesLocked.get(userHandle); if (instance == null) { instance = createImpl(context, userHandle, logger); Context userContext = context.createContextAsUser(userHandle, /*flags=*/ 0); instance = createImpl(userContext, userHandle, logger); mInstancesLocked.put(userHandle, instance); } return instance; Loading Loading @@ -177,7 +167,7 @@ public final class ImplInstanceManager { } private AppSearchImpl createImpl( @NonNull Context context, @NonNull Context userContext, @NonNull UserHandle userHandle, @Nullable AppSearchLogger logger) throws AppSearchException { Loading @@ -185,33 +175,8 @@ public final class ImplInstanceManager { // TODO(b/181787682): Swap AppSearchImpl and VisibilityStore to accept a UserHandle too return AppSearchImpl.create( appSearchDir, context, userContext, userHandle.getIdentifier(), mGlobalQuerierPackage, /*logger=*/ null); } /** * Returns the global querier package if it's a system package. Otherwise, empty string. * * @param context Context of the system service. */ @NonNull private static String getGlobalAppSearchDataQuerierPackageName(@NonNull Context context) { String globalAppSearchDataQuerierPackage = context.getString(R.string.config_globalAppSearchDataQuerierPackage); try { if (context.getPackageManager() .getPackageInfoAsUser( globalAppSearchDataQuerierPackage, MATCH_FACTORY_ONLY, UserHandle.USER_SYSTEM) == null) { return ""; } } catch (PackageManager.NameNotFoundException e) { return ""; } return globalAppSearchDataQuerierPackage; } }
apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +94 −55 Original line number Diff line number Diff line Loading @@ -17,7 +17,6 @@ package com.android.server.appsearch.external.localstorage; import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.addPrefixToDocument; import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPackagePrefix; import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.createPrefix; import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getDatabaseName; import static com.android.server.appsearch.external.localstorage.util.PrefixUtil.getPackageName; Loading Loading @@ -60,6 +59,7 @@ import com.android.server.appsearch.external.localstorage.stats.InitializeStats; import com.android.server.appsearch.external.localstorage.stats.PutDocumentStats; import com.android.server.appsearch.external.localstorage.stats.RemoveStats; import com.android.server.appsearch.external.localstorage.stats.SearchStats; import com.android.server.appsearch.visibilitystore.VisibilityStore; import com.google.android.icing.IcingSearchEngine; import com.google.android.icing.proto.DeleteByQueryResultProto; Loading Loading @@ -98,6 +98,7 @@ import java.io.File; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Objects; Loading Loading @@ -199,14 +200,12 @@ public final class AppSearchImpl implements Closeable { @NonNull public static AppSearchImpl create( @NonNull File icingDir, @NonNull Context context, @NonNull Context userContext, int userId, @NonNull String globalQuerierPackage, @Nullable AppSearchLogger logger) throws AppSearchException { Objects.requireNonNull(icingDir); Objects.requireNonNull(context); Objects.requireNonNull(globalQuerierPackage); Objects.requireNonNull(userContext); long totalLatencyStartMillis = SystemClock.elapsedRealtime(); InitializeStats.Builder initStatsBuilder = null; Loading @@ -215,8 +214,7 @@ public final class AppSearchImpl implements Closeable { } AppSearchImpl appSearchImpl = new AppSearchImpl( icingDir, context, userId, globalQuerierPackage, initStatsBuilder); new AppSearchImpl(icingDir, userContext, userId, initStatsBuilder); long prepareVisibilityStoreLatencyStartMillis = SystemClock.elapsedRealtime(); appSearchImpl.initializeVisibilityStore(); Loading @@ -239,9 +237,8 @@ public final class AppSearchImpl implements Closeable { /** @param initStatsBuilder collects stats for initialization if provided. */ private AppSearchImpl( @NonNull File icingDir, @NonNull Context context, @NonNull Context userContext, int userId, @NonNull String globalQuerierPackage, @Nullable InitializeStats.Builder initStatsBuilder) throws AppSearchException { mReadWriteLock.writeLock().lock(); Loading @@ -259,8 +256,7 @@ public final class AppSearchImpl implements Closeable { "Constructing IcingSearchEngine, response", Objects.hashCode(mIcingSearchEngineLocked)); mVisibilityStoreLocked = new VisibilityStore(this, context, userId, globalQuerierPackage); mVisibilityStoreLocked = new VisibilityStore(this, userContext, userId); // The core initialization procedure. If any part of this fails, we bail into // resetLocked(), deleting all data (but hopefully allowing AppSearchImpl to come up). Loading Loading @@ -495,7 +491,8 @@ public final class AppSearchImpl implements Closeable { } mVisibilityStoreLocked.setVisibility( prefix, packageName, databaseName, prefixedSchemasNotPlatformSurfaceable, prefixedSchemasPackageAccessible); Loading Loading @@ -844,17 +841,17 @@ public final class AppSearchImpl implements Closeable { try { throwIfClosedLocked(); // Convert package filters to prefix filters Set<String> packageFilters = new ArraySet<>(searchSpec.getFilterPackageNames()); Set<String> prefixFilters = new ArraySet<>(); Set<String> allPrefixes = mNamespaceMapLocked.keySet(); if (packageFilters.isEmpty()) { // Client didn't restrict their search over packages. Try to query over all // packages/prefixes prefixFilters = allPrefixes; prefixFilters = mNamespaceMapLocked.keySet(); } else { // Client did restrict their search over packages. Only include the prefixes that // belong to the specified packages. for (String prefix : allPrefixes) { for (String prefix : mNamespaceMapLocked.keySet()) { String packageName = getPackageName(prefix); if (packageFilters.contains(packageName)) { prefixFilters.add(prefix); Loading @@ -862,41 +859,50 @@ public final class AppSearchImpl implements Closeable { } } // Find which schemas the client is allowed to query over. Set<String> allowedPrefixedSchemas = new ArraySet<>(); List<String> schemaFilters = searchSpec.getFilterSchemas(); // Convert schema filters to prefixed schema filters ArraySet<String> prefixedSchemaFilters = new ArraySet<>(); for (String prefix : prefixFilters) { String packageName = getPackageName(prefix); if (!schemaFilters.isEmpty()) { for (String schema : schemaFilters) { List<String> schemaFilters = searchSpec.getFilterSchemas(); if (schemaFilters.isEmpty()) { // Client didn't specify certain schemas to search over, check all schemas prefixedSchemaFilters.addAll(mSchemaMapLocked.get(prefix).keySet()); } else { // Client specified some schemas to search over, check each one String prefixedSchema = prefix + schema; if (packageName.equals(callerPackageName) || mVisibilityStoreLocked.isSchemaSearchableByCaller( prefix, prefixedSchema, callerUid)) { allowedPrefixedSchemas.add(prefixedSchema); } for (int i = 0; i < schemaFilters.size(); i++) { prefixedSchemaFilters.add(prefix + schemaFilters.get(i)); } } else { // Client didn't specify certain schemas to search over, check all schemas Map<String, SchemaTypeConfigProto> prefixedSchemas = mSchemaMapLocked.get(prefix); if (prefixedSchemas != null) { for (String prefixedSchema : prefixedSchemas.keySet()) { if (packageName.equals(callerPackageName) || mVisibilityStoreLocked.isSchemaSearchableByCaller( prefix, prefixedSchema, callerUid)) { allowedPrefixedSchemas.add(prefixedSchema); } } // Remove the schemas the client is not allowed to search over Iterator<String> prefixedSchemaIt = prefixedSchemaFilters.iterator(); while (prefixedSchemaIt.hasNext()) { String prefixedSchema = prefixedSchemaIt.next(); String packageName = getPackageName(prefixedSchema); boolean allow; if (packageName.equals(callerPackageName)) { // Callers can always retrieve their own data allow = true; } else { String databaseName = getDatabaseName(prefixedSchema); allow = mVisibilityStoreLocked.isSchemaSearchableByCaller( packageName, databaseName, prefixedSchema, callerPackageName, callerUid); } if (!allow) { prefixedSchemaIt.remove(); } } return doQueryLocked( prefixFilters, allowedPrefixedSchemas, prefixedSchemaFilters, queryExpression, searchSpec, sStatsBuilder); Loading Loading @@ -1404,20 +1410,47 @@ public final class AppSearchImpl implements Closeable { mReadWriteLock.writeLock().lock(); try { throwIfClosedLocked(); Set<String> existingPackages = getPackageToDatabases().keySet(); if (existingPackages.contains(packageName)) { existingPackages.remove(packageName); prunePackageData(existingPackages); } } finally { mReadWriteLock.writeLock().unlock(); } } /** * Remove all {@link AppSearchSchema}s and {@link GenericDocument}s that doesn't belong to any * of the given installed packages * * @param installedPackages The name of all installed package. * @throws AppSearchException if we cannot remove the data. */ public void prunePackageData(@NonNull Set<String> installedPackages) throws AppSearchException { mReadWriteLock.writeLock().lock(); try { throwIfClosedLocked(); Map<String, Set<String>> packageToDatabases = getPackageToDatabases(); if (installedPackages.containsAll(packageToDatabases.keySet())) { // No package got removed. We are good. return; } // Prune schema proto SchemaProto existingSchema = getSchemaProtoLocked(); SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder(); String prefix = createPackagePrefix(packageName); for (int i = 0; i < existingSchema.getTypesCount(); i++) { if (!existingSchema.getTypes(i).getSchemaType().startsWith(prefix)) { String packageName = getPackageName(existingSchema.getTypes(i).getSchemaType()); if (installedPackages.contains(packageName)) { newSchemaBuilder.addTypes(existingSchema.getTypes(i)); } } SchemaProto finalSchema = newSchemaBuilder.build(); // Apply schema, set force override to true to remove all schemas and documents under // that package. // Apply schema, set force override to true to remove all schemas and documents that // doesn't belong to any of these installed packages. mLogUtil.piiTrace( "clearPackageData.setSchema, request", finalSchema.getTypesCount(), Loading @@ -1432,6 +1465,20 @@ public final class AppSearchImpl implements Closeable { // Determine whether it succeeded. checkSuccess(setSchemaResultProto.getStatus()); // Prune cached maps for (Map.Entry<String, Set<String>> entry : packageToDatabases.entrySet()) { String packageName = entry.getKey(); Set<String> databaseNames = entry.getValue(); if (!installedPackages.contains(packageName) && databaseNames != null) { for (String databaseName : databaseNames) { String removedPrefix = createPrefix(packageName, databaseName); mSchemaMapLocked.remove(removedPrefix); mNamespaceMapLocked.remove(removedPrefix); } } } // TODO(b/145759910) clear visibility setting for package. } finally { mReadWriteLock.writeLock().unlock(); } Loading Loading @@ -1857,14 +1904,6 @@ public final class AppSearchImpl implements Closeable { return schemaProto.getSchema(); } /** Returns a set of all prefixes AppSearchImpl knows about. */ // TODO(b/180058203): Remove this method once platform has switched away from using this method. @GuardedBy("mReadWriteLock") @NonNull Set<String> getPrefixesLocked() { return mSchemaMapLocked.keySet(); } private static void addToMap( Map<String, Set<String>> map, String prefix, String prefixedValue) { Set<String> values = map.get(prefix); Loading
apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/NotPlatformSurfaceableMap.java +28 −12 Original line number Diff line number Diff line Loading @@ -26,36 +26,52 @@ import java.util.Set; * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} API. * * This object is not thread safe. * @hide */ public class NotPlatformSurfaceableMap { class NotPlatformSurfaceableMap { /** * Maps prefixes to the set of prefixed schemas that are platform-hidden within that prefix. * Maps packages to databases to the set of prefixed schemas that are platform-hidden within * that database. */ private final Map<String, Set<String>> mMap = new ArrayMap<>(); private final Map<String, Map<String, Set<String>>> mMap = new ArrayMap<>(); /** * Sets the prefixed schemas that are opted out of platform surfacing for the prefix. * Sets the prefixed schemas that are opted out of platform surfacing for the database. * * <p>Any existing mappings for this prefix are overwritten. */ public void setNotPlatformSurfaceable(@NonNull String prefix, @NonNull Set<String> schemas) { mMap.put(prefix, schemas); public void setNotPlatformSurfaceable( @NonNull String packageName, @NonNull String databaseName, @NonNull Set<String> prefixedSchemas) { Map<String, Set<String>> databaseToSchemas = mMap.get(packageName); if (databaseToSchemas == null) { databaseToSchemas = new ArrayMap<>(); mMap.put(packageName, databaseToSchemas); } databaseToSchemas.put(databaseName, prefixedSchemas); } /** * Returns whether the given prefixed schema is platform surfaceable (has not opted out) in the * given prefix. * given database. */ public boolean isSchemaPlatformSurfaceable(@NonNull String prefix, @NonNull String schemaType) { Set<String> schemaTypes = mMap.get(prefix); public boolean isSchemaPlatformSurfaceable( @NonNull String packageName, @NonNull String databaseName, @NonNull String prefixedSchema) { Map<String, Set<String>> databaseToSchemaType = mMap.get(packageName); if (databaseToSchemaType == null) { // No opt-outs for this package return true; } Set<String> schemaTypes = databaseToSchemaType.get(databaseName); if (schemaTypes == null) { // No opt-outs for this prefix // No opt-outs for this database 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); return !schemaTypes.contains(prefixedSchema); } /** Discards all data in the map. */ Loading
apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleDocument.java +1 −2 Original line number Diff line number Diff line Loading @@ -26,9 +26,8 @@ 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 { class PackageAccessibleDocument extends GenericDocument { /** Schema type for nested documents that hold package accessible information. */ public static final String SCHEMA_TYPE = "PackageAccessibleType"; Loading
apex/appsearch/service/java/com/android/server/appsearch/visibilitystore/PackageAccessibleMap.java +26 −10 Original line number Diff line number Diff line Loading @@ -28,23 +28,31 @@ import java.util.Set; * {@link android.app.appsearch.SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} API. * * This object is not thread safe. * @hide */ public class PackageAccessibleMap { class PackageAccessibleMap { /** * Maps prefixes to prefixed schema types to PackageIdentifiers that have access to that schema. * Maps packages to databases to prefixed schemas to PackageIdentifiers that have access to that * schema. */ private final Map<String, Map<String, Set<PackageIdentifier>>> mMap = new ArrayMap<>(); private final Map<String, Map<String, Map<String, Set<PackageIdentifier>>>> mMap = new ArrayMap<>(); /** * Sets the prefixed schemas that have package visibility in the given prefix. * Sets the prefixed schemas that have package visibility in the given database. * * <p>Any existing mappings for this prefix are overwritten. */ public void setPackageAccessible( @NonNull String prefix, @NonNull String packageName, @NonNull String databaseName, @NonNull Map<String, Set<PackageIdentifier>> schemaToPackageIdentifier) { mMap.put(prefix, schemaToPackageIdentifier); Map<String, Map<String, Set<PackageIdentifier>>> databaseToSchemaTypeToVisibility = mMap.get(packageName); if (databaseToSchemaTypeToVisibility == null) { databaseToSchemaTypeToVisibility = new ArrayMap<>(); mMap.put(packageName, databaseToSchemaTypeToVisibility); } databaseToSchemaTypeToVisibility.put(databaseName, schemaToPackageIdentifier); } /** Loading @@ -55,12 +63,20 @@ public class PackageAccessibleMap { */ @NonNull public Set<PackageIdentifier> getAccessiblePackages( @NonNull String prefix, @NonNull String schemaType) { Map<String, Set<PackageIdentifier>> schemaTypeToVisibility = mMap.get(prefix); @NonNull String packageName, @NonNull String databaseName, @NonNull String prefixedSchema) { Map<String, Map<String, Set<PackageIdentifier>>> databaseToSchemaTypeToVisibility = mMap.get(packageName); if (databaseToSchemaTypeToVisibility == null) { return Collections.emptySet(); } Map<String, Set<PackageIdentifier>> schemaTypeToVisibility = databaseToSchemaTypeToVisibility.get(databaseName); if (schemaTypeToVisibility == null) { return Collections.emptySet(); } Set<PackageIdentifier> accessiblePackages = schemaTypeToVisibility.get(schemaType); Set<PackageIdentifier> accessiblePackages = schemaTypeToVisibility.get(prefixedSchema); if (accessiblePackages == null) { return Collections.emptySet(); } Loading