Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d434e57 authored by android-build-team Robot's avatar android-build-team Robot
Browse files

Snap for 7428328 from 57b72445 to sc-release

Change-Id: I94440f73b7a53844ee19ca02917b636c2ba5f553
parents 81833820 57b72445
Loading
Loading
Loading
Loading
+6 −41
Original line number Diff line number Diff line
@@ -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;
@@ -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.
     *
@@ -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();
                }
            }
        }
@@ -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
     */
@@ -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;
@@ -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 {
@@ -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;
    }
}
+94 −55
Original line number Diff line number Diff line
@@ -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;
@@ -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;
@@ -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;
@@ -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;
@@ -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();
@@ -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();
@@ -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).
@@ -495,7 +491,8 @@ public final class AppSearchImpl implements Closeable {
            }

            mVisibilityStoreLocked.setVisibility(
                    prefix,
                    packageName,
                    databaseName,
                    prefixedSchemasNotPlatformSurfaceable,
                    prefixedSchemasPackageAccessible);

@@ -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);
@@ -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);
@@ -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(),
@@ -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();
        }
@@ -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);
+28 −12
Original line number Diff line number Diff line
@@ -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. */
+1 −2
Original line number Diff line number Diff line
@@ -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";

+26 −10
Original line number Diff line number Diff line
@@ -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);
    }

    /**
@@ -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