Merge "Update Framework from Jetpack."
This commit is contained in:
committed by
Android (Google) Code Review
commit
afa2ec89c0
@@ -162,7 +162,7 @@ package android.app.appsearch {
|
||||
public static final class PutDocumentsRequest.Builder {
|
||||
ctor public PutDocumentsRequest.Builder();
|
||||
method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull android.app.appsearch.GenericDocument...);
|
||||
method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull java.util.Collection<android.app.appsearch.GenericDocument>);
|
||||
method @NonNull public android.app.appsearch.PutDocumentsRequest.Builder addGenericDocument(@NonNull java.util.Collection<? extends android.app.appsearch.GenericDocument>);
|
||||
method @NonNull public android.app.appsearch.PutDocumentsRequest build();
|
||||
}
|
||||
|
||||
@@ -182,6 +182,7 @@ package android.app.appsearch {
|
||||
public final class SearchResult {
|
||||
method @NonNull public android.app.appsearch.GenericDocument getDocument();
|
||||
method @NonNull public java.util.List<android.app.appsearch.SearchResult.MatchInfo> getMatches();
|
||||
method @NonNull public String getPackageName();
|
||||
}
|
||||
|
||||
public static final class SearchResult.MatchInfo {
|
||||
@@ -204,9 +205,11 @@ package android.app.appsearch {
|
||||
}
|
||||
|
||||
public final class SearchSpec {
|
||||
method @NonNull public java.util.List<java.lang.String> getFilterPackageNames();
|
||||
method public int getMaxSnippetSize();
|
||||
method @NonNull public java.util.List<java.lang.String> getNamespaces();
|
||||
method public int getOrder();
|
||||
method @NonNull public java.util.Map<java.lang.String,java.util.List<java.lang.String>> getProjections();
|
||||
method public int getRankingStrategy();
|
||||
method public int getResultCountPerPage();
|
||||
method @NonNull public java.util.List<java.lang.String> getSchemaTypes();
|
||||
@@ -215,17 +218,23 @@ package android.app.appsearch {
|
||||
method public int getTermMatch();
|
||||
field public static final int ORDER_ASCENDING = 1; // 0x1
|
||||
field public static final int ORDER_DESCENDING = 0; // 0x0
|
||||
field public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
|
||||
field public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2; // 0x2
|
||||
field public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1; // 0x1
|
||||
field public static final int RANKING_STRATEGY_NONE = 0; // 0x0
|
||||
field public static final int RANKING_STRATEGY_RELEVANCE_SCORE = 3; // 0x3
|
||||
field public static final int TERM_MATCH_EXACT_ONLY = 1; // 0x1
|
||||
field public static final int TERM_MATCH_PREFIX = 2; // 0x2
|
||||
}
|
||||
|
||||
public static final class SearchSpec.Builder {
|
||||
ctor public SearchSpec.Builder();
|
||||
method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.lang.String...);
|
||||
method @NonNull public android.app.appsearch.SearchSpec.Builder addFilterPackageNames(@NonNull java.util.Collection<java.lang.String>);
|
||||
method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.lang.String...);
|
||||
method @NonNull public android.app.appsearch.SearchSpec.Builder addNamespace(@NonNull java.util.Collection<java.lang.String>);
|
||||
method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.lang.String...);
|
||||
method @NonNull public android.app.appsearch.SearchSpec.Builder addProjection(@NonNull String, @NonNull java.util.Collection<java.lang.String>);
|
||||
method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.lang.String...);
|
||||
method @NonNull public android.app.appsearch.SearchSpec.Builder addSchemaType(@NonNull java.util.Collection<java.lang.String>);
|
||||
method @NonNull public android.app.appsearch.SearchSpec build();
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.android.internal.infra.AndroidFuture;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
@@ -234,6 +235,7 @@ public class AppSearchManager {
|
||||
DEFAULT_DATABASE_NAME,
|
||||
schemaBundles,
|
||||
new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
request.isForceOverride(),
|
||||
mContext.getUserId(),
|
||||
new IAppSearchResultCallback.Stub() {
|
||||
|
||||
@@ -22,6 +22,7 @@ import android.annotation.UserIdInt;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelableException;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -161,11 +162,22 @@ public final class AppSearchSession {
|
||||
for (AppSearchSchema schema : request.getSchemas()) {
|
||||
schemaBundles.add(schema.getBundle());
|
||||
}
|
||||
Map<String, List<Bundle>> schemasPackageAccessibleBundles =
|
||||
new ArrayMap<>(request.getSchemasVisibleToPackagesInternal().size());
|
||||
for (Map.Entry<String, Set<PackageIdentifier>> entry :
|
||||
request.getSchemasVisibleToPackagesInternal().entrySet()) {
|
||||
List<Bundle> packageIdentifierBundles = new ArrayList<>(entry.getValue().size());
|
||||
for (PackageIdentifier packageIdentifier : entry.getValue()) {
|
||||
packageIdentifierBundles.add(packageIdentifier.getBundle());
|
||||
}
|
||||
schemasPackageAccessibleBundles.put(entry.getKey(), packageIdentifierBundles);
|
||||
}
|
||||
try {
|
||||
mService.setSchema(
|
||||
mDatabaseName,
|
||||
schemaBundles,
|
||||
new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
|
||||
schemasPackageAccessibleBundles,
|
||||
request.isForceOverride(),
|
||||
mUserId,
|
||||
new IAppSearchResultCallback.Stub() {
|
||||
|
||||
@@ -34,6 +34,8 @@ interface IAppSearchManager {
|
||||
* @param schemaBundles List of {@link AppSearchSchema} bundles.
|
||||
* @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
|
||||
* surfaces.
|
||||
* @param schemasPackageAccessibleBundles Schema types that are visible to the specified
|
||||
* packages. The value List contains PackageIdentifier Bundles.
|
||||
* @param forceOverride Whether to apply the new schema even if it is incompatible. All
|
||||
* incompatible documents will be deleted.
|
||||
* @param userId Id of the calling user
|
||||
@@ -44,11 +46,11 @@ interface IAppSearchManager {
|
||||
in String databaseName,
|
||||
in List<Bundle> schemaBundles,
|
||||
in List<String> schemasNotPlatformSurfaceable,
|
||||
in Map<String, List<Bundle>> schemasPackageAccessibleBundles,
|
||||
boolean forceOverride,
|
||||
in int userId,
|
||||
in IAppSearchResultCallback callback);
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves the AppSearch schema for this database.
|
||||
*
|
||||
|
||||
@@ -17,16 +17,17 @@
|
||||
package android.app.appsearch;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.app.appsearch.util.BundleUtil;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/** This class represents a uniquely identifiable package. */
|
||||
public class PackageIdentifier {
|
||||
private final String mPackageName;
|
||||
private final byte[] mSha256Certificate;
|
||||
private static final String PACKAGE_NAME_FIELD = "packageName";
|
||||
private static final String SHA256_CERTIFICATE_FIELD = "sha256Certificate";
|
||||
|
||||
private final Bundle mBundle;
|
||||
|
||||
/**
|
||||
* Creates a unique identifier for a package.
|
||||
@@ -35,18 +36,30 @@ public class PackageIdentifier {
|
||||
* @param sha256Certificate SHA256 certificate digest of the package.
|
||||
*/
|
||||
public PackageIdentifier(@NonNull String packageName, @NonNull byte[] sha256Certificate) {
|
||||
mPackageName = Preconditions.checkNotNull(packageName);
|
||||
mSha256Certificate = Preconditions.checkNotNull(sha256Certificate);
|
||||
mBundle = new Bundle();
|
||||
mBundle.putString(PACKAGE_NAME_FIELD, packageName);
|
||||
mBundle.putByteArray(SHA256_CERTIFICATE_FIELD, sha256Certificate);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
public PackageIdentifier(@NonNull Bundle bundle) {
|
||||
mBundle = Preconditions.checkNotNull(bundle);
|
||||
}
|
||||
|
||||
/** @hide */
|
||||
@NonNull
|
||||
public Bundle getBundle() {
|
||||
return mBundle;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
return Preconditions.checkNotNull(mBundle.getString(PACKAGE_NAME_FIELD));
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public byte[] getSha256Certificate() {
|
||||
return mSha256Certificate;
|
||||
return Preconditions.checkNotNull(mBundle.getByteArray(SHA256_CERTIFICATE_FIELD));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -58,12 +71,11 @@ public class PackageIdentifier {
|
||||
return false;
|
||||
}
|
||||
final PackageIdentifier other = (PackageIdentifier) obj;
|
||||
return this.mPackageName.equals(other.mPackageName)
|
||||
&& Arrays.equals(this.mSha256Certificate, other.mSha256Certificate);
|
||||
return BundleUtil.deepEquals(mBundle, other.mBundle);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(mPackageName, Arrays.hashCode(mSha256Certificate));
|
||||
return BundleUtil.deepHashCode(mBundle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ import java.util.List;
|
||||
/**
|
||||
* Encapsulates a request to index a document into an {@link AppSearchSession} database.
|
||||
*
|
||||
* @see AppSearchSession#putDocuments
|
||||
* <p>@see AppSearchSession#putDocuments
|
||||
*/
|
||||
public final class PutDocumentsRequest {
|
||||
private final List<GenericDocument> mDocuments;
|
||||
@@ -45,12 +45,16 @@ public final class PutDocumentsRequest {
|
||||
return Collections.unmodifiableList(mDocuments);
|
||||
}
|
||||
|
||||
/** Builder for {@link PutDocumentsRequest} objects. */
|
||||
/**
|
||||
* Builder for {@link PutDocumentsRequest} objects.
|
||||
*
|
||||
* <p>Once {@link #build} is called, the instance can no longer be used.
|
||||
*/
|
||||
public static final class Builder {
|
||||
private final List<GenericDocument> mDocuments = new ArrayList<>();
|
||||
private boolean mBuilt = false;
|
||||
|
||||
/** Adds one or more documents to the request. */
|
||||
/** Adds one or more {@link GenericDocument} objects to the request. */
|
||||
@SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
|
||||
@NonNull
|
||||
public Builder addGenericDocument(@NonNull GenericDocument... documents) {
|
||||
@@ -58,17 +62,18 @@ public final class PutDocumentsRequest {
|
||||
return addGenericDocument(Arrays.asList(documents));
|
||||
}
|
||||
|
||||
/** Adds one or more documents to the request. */
|
||||
/** Adds a collection of {@link GenericDocument} objects to the request. */
|
||||
@SuppressLint("MissingGetterMatchingBuilder") // Merged list available from getDocuments()
|
||||
@NonNull
|
||||
public Builder addGenericDocument(@NonNull Collection<GenericDocument> documents) {
|
||||
public Builder addGenericDocument(
|
||||
@NonNull Collection<? extends GenericDocument> documents) {
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkNotNull(documents);
|
||||
mDocuments.addAll(documents);
|
||||
return this;
|
||||
}
|
||||
|
||||
/** Builds a new {@link PutDocumentsRequest}. */
|
||||
/** Creates a new {@link PutDocumentsRequest} object. */
|
||||
@NonNull
|
||||
public PutDocumentsRequest build() {
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
|
||||
@@ -109,10 +109,9 @@ public final class SearchResult {
|
||||
}
|
||||
|
||||
/**
|
||||
* Contains the package name that stored the {@link GenericDocument}.
|
||||
* Contains the package name of the app that stored the {@link GenericDocument}.
|
||||
*
|
||||
* @return Package name that stored the document
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public String getPackageName() {
|
||||
|
||||
@@ -19,6 +19,7 @@ package android.app.appsearch;
|
||||
import android.annotation.IntDef;
|
||||
import android.annotation.IntRange;
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.appsearch.exceptions.IllegalSearchSpecException;
|
||||
import android.os.Bundle;
|
||||
import android.util.ArrayMap;
|
||||
@@ -42,17 +43,15 @@ import java.util.Set;
|
||||
// TODO(sidchhabra) : AddResultSpec fields for Snippets etc.
|
||||
public final class SearchSpec {
|
||||
/**
|
||||
* Schema type to be used in {@link SearchSpec.Builder#addProjectionTypePropertyPath} to apply
|
||||
* property paths to all results, excepting any types that have had their own, specific property
|
||||
* paths set.
|
||||
*
|
||||
* @hide
|
||||
* Schema type to be used in {@link SearchSpec.Builder#addProjection} to apply property paths to
|
||||
* all results, excepting any types that have had their own, specific property paths set.
|
||||
*/
|
||||
public static final String PROJECTION_SCHEMA_TYPE_WILDCARD = "*";
|
||||
|
||||
static final String TERM_MATCH_TYPE_FIELD = "termMatchType";
|
||||
static final String SCHEMA_TYPE_FIELD = "schemaType";
|
||||
static final String NAMESPACE_FIELD = "namespace";
|
||||
static final String PACKAGE_NAME_FIELD = "packageName";
|
||||
static final String NUM_PER_PAGE_FIELD = "numPerPage";
|
||||
static final String RANKING_STRATEGY_FIELD = "rankingStrategy";
|
||||
static final String ORDER_FIELD = "order";
|
||||
@@ -106,7 +105,8 @@ public final class SearchSpec {
|
||||
value = {
|
||||
RANKING_STRATEGY_NONE,
|
||||
RANKING_STRATEGY_DOCUMENT_SCORE,
|
||||
RANKING_STRATEGY_CREATION_TIMESTAMP
|
||||
RANKING_STRATEGY_CREATION_TIMESTAMP,
|
||||
RANKING_STRATEGY_RELEVANCE_SCORE
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface RankingStrategy {}
|
||||
@@ -117,6 +117,8 @@ public final class SearchSpec {
|
||||
public static final int RANKING_STRATEGY_DOCUMENT_SCORE = 1;
|
||||
/** Ranked by document creation timestamps. */
|
||||
public static final int RANKING_STRATEGY_CREATION_TIMESTAMP = 2;
|
||||
/** Ranked by document relevance score. */
|
||||
public static final int RANKING_STRATEGY_RELEVANCE_SCORE = 3;
|
||||
|
||||
/**
|
||||
* Order for query result.
|
||||
@@ -172,7 +174,7 @@ public final class SearchSpec {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of namespaces to search for.
|
||||
* Returns the list of namespaces to search over.
|
||||
*
|
||||
* <p>If empty, the query will search over all namespaces.
|
||||
*/
|
||||
@@ -185,6 +187,40 @@ public final class SearchSpec {
|
||||
return Collections.unmodifiableList(namespaces);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of package name filters to search over.
|
||||
*
|
||||
* <p>If empty, the query will search over all packages that the caller has access to. If
|
||||
* package names are specified which caller doesn't have access to, then those package names
|
||||
* will be ignored.
|
||||
*/
|
||||
@NonNull
|
||||
public List<String> getFilterPackageNames() {
|
||||
List<String> packageNames = mBundle.getStringArrayList(PACKAGE_NAME_FIELD);
|
||||
if (packageNames == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(packageNames);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of package names to search over.
|
||||
*
|
||||
* <p>If unset, the query will search over all packages that the caller has access to. If
|
||||
* package names are specified which caller doesn't have access to, then those package names
|
||||
* will be ignored.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public List<String> getPackageNames() {
|
||||
List<String> packageNames = mBundle.getStringArrayList(PACKAGE_NAME_FIELD);
|
||||
if (packageNames == null) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return Collections.unmodifiableList(packageNames);
|
||||
}
|
||||
|
||||
/** Returns the number of results per page in the result set. */
|
||||
public int getResultCountPerPage() {
|
||||
return mBundle.getInt(NUM_PER_PAGE_FIELD, DEFAULT_NUM_PER_PAGE);
|
||||
@@ -224,11 +260,9 @@ public final class SearchSpec {
|
||||
*
|
||||
* <p>Calling this function repeatedly is inefficient. Prefer to retain the Map returned by this
|
||||
* function, rather than calling it multiple times.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Map<String, List<String>> getProjectionTypePropertyPaths() {
|
||||
public Map<String, List<String>> getProjections() {
|
||||
Bundle typePropertyPathsBundle = mBundle.getBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD);
|
||||
Set<String> schemaTypes = typePropertyPathsBundle.keySet();
|
||||
Map<String, List<String>> typePropertyPathsMap = new ArrayMap<>(schemaTypes.size());
|
||||
@@ -245,6 +279,7 @@ public final class SearchSpec {
|
||||
private final Bundle mBundle;
|
||||
private final ArrayList<String> mSchemaTypes = new ArrayList<>();
|
||||
private final ArrayList<String> mNamespaces = new ArrayList<>();
|
||||
private final ArrayList<String> mPackageNames = new ArrayList<>();
|
||||
private final Bundle mProjectionTypePropertyMasks = new Bundle();
|
||||
private boolean mBuilt = false;
|
||||
|
||||
@@ -318,6 +353,43 @@ public final class SearchSpec {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a package name filter to {@link SearchSpec} Entry. Only search for documents that
|
||||
* were indexed from the specified packages.
|
||||
*
|
||||
* <p>If unset, the query will search over all packages that the caller has access to. If
|
||||
* package names are specified which caller doesn't have access to, then those package names
|
||||
* will be ignored.
|
||||
*/
|
||||
// Getter is called "getFilterPackageNames" (as opposed to the suggested
|
||||
// "getFilterPackageNameses")
|
||||
@SuppressLint("MissingGetterMatchingBuilder")
|
||||
@NonNull
|
||||
public Builder addFilterPackageNames(@NonNull String... packageNames) {
|
||||
Preconditions.checkNotNull(packageNames);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
return addFilterPackageNames(Arrays.asList(packageNames));
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a package name filter to {@link SearchSpec} Entry. Only search for documents that
|
||||
* were indexed from the specified packages.
|
||||
*
|
||||
* <p>If unset, the query will search over all packages that the caller has access to. If
|
||||
* package names are specified which caller doesn't have access to, then those package names
|
||||
* will be ignored.
|
||||
*/
|
||||
// Getter is called "getFilterPackageNames" (as opposed to the suggested
|
||||
// "getFilterPackageNameses")
|
||||
@SuppressLint("MissingGetterMatchingBuilder")
|
||||
@NonNull
|
||||
public Builder addFilterPackageNames(@NonNull Collection<String> packageNames) {
|
||||
Preconditions.checkNotNull(packageNames);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
mPackageNames.addAll(packageNames);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the number of results per page in the returned object.
|
||||
*
|
||||
@@ -339,7 +411,7 @@ public final class SearchSpec {
|
||||
Preconditions.checkArgumentInRange(
|
||||
rankingStrategy,
|
||||
RANKING_STRATEGY_NONE,
|
||||
RANKING_STRATEGY_CREATION_TIMESTAMP,
|
||||
RANKING_STRATEGY_RELEVANCE_SCORE,
|
||||
"Result ranking strategy");
|
||||
mBundle.putInt(RANKING_STRATEGY_FIELD, rankingStrategy);
|
||||
return this;
|
||||
@@ -480,14 +552,12 @@ public final class SearchSpec {
|
||||
* subject: "IMPORTANT"
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public SearchSpec.Builder addProjectionTypePropertyPaths(
|
||||
public SearchSpec.Builder addProjection(
|
||||
@NonNull String schemaType, @NonNull String... propertyPaths) {
|
||||
Preconditions.checkNotNull(propertyPaths);
|
||||
return addProjectionTypePropertyPaths(schemaType, Arrays.asList(propertyPaths));
|
||||
return addProjection(schemaType, Arrays.asList(propertyPaths));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -503,12 +573,10 @@ public final class SearchSpec {
|
||||
* then those property paths will apply to all results, excepting any types that have their
|
||||
* own, specific property paths set.
|
||||
*
|
||||
* <p>{@see SearchSpec.Builder#addProjectionTypePropertyPath(String, String...)}
|
||||
*
|
||||
* @hide
|
||||
* <p>{@see SearchSpec.Builder#addProjection(String, String...)}
|
||||
*/
|
||||
@NonNull
|
||||
public SearchSpec.Builder addProjectionTypePropertyPaths(
|
||||
public SearchSpec.Builder addProjection(
|
||||
@NonNull String schemaType, @NonNull Collection<String> propertyPaths) {
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
Preconditions.checkNotNull(schemaType);
|
||||
@@ -535,6 +603,7 @@ public final class SearchSpec {
|
||||
}
|
||||
mBundle.putStringArrayList(NAMESPACE_FIELD, mNamespaces);
|
||||
mBundle.putStringArrayList(SCHEMA_TYPE_FIELD, mSchemaTypes);
|
||||
mBundle.putStringArrayList(PACKAGE_NAME_FIELD, mPackageNames);
|
||||
mBundle.putBundle(PROJECTION_TYPE_PROPERTY_PATHS_FIELD, mProjectionTypePropertyMasks);
|
||||
mBuilt = true;
|
||||
return new SearchSpec(mBundle);
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.app.appsearch.GenericDocument;
|
||||
import android.app.appsearch.IAppSearchBatchResultCallback;
|
||||
import android.app.appsearch.IAppSearchManager;
|
||||
import android.app.appsearch.IAppSearchResultCallback;
|
||||
import android.app.appsearch.PackageIdentifier;
|
||||
import android.app.appsearch.SearchResultPage;
|
||||
import android.app.appsearch.SearchSpec;
|
||||
import android.content.Context;
|
||||
@@ -34,6 +35,7 @@ import android.os.Binder;
|
||||
import android.os.Bundle;
|
||||
import android.os.ParcelableException;
|
||||
import android.os.RemoteException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
@@ -42,6 +44,7 @@ import com.android.server.appsearch.external.localstorage.AppSearchImpl;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* TODO(b/142567528): add comments when implement this class
|
||||
@@ -64,6 +67,7 @@ public class AppSearchManagerService extends SystemService {
|
||||
@NonNull String databaseName,
|
||||
@NonNull List<Bundle> schemaBundles,
|
||||
@NonNull List<String> schemasNotPlatformSurfaceable,
|
||||
@NonNull Map<String, List<Bundle>> schemasPackageAccessibleBundles,
|
||||
boolean forceOverride,
|
||||
@UserIdInt int userId,
|
||||
@NonNull IAppSearchResultCallback callback) {
|
||||
@@ -78,9 +82,25 @@ public class AppSearchManagerService extends SystemService {
|
||||
for (int i = 0; i < schemaBundles.size(); i++) {
|
||||
schemas.add(new AppSearchSchema(schemaBundles.get(i)));
|
||||
}
|
||||
Map<String, List<PackageIdentifier>> schemasPackageAccessible =
|
||||
new ArrayMap<>(schemasPackageAccessibleBundles.size());
|
||||
for (Map.Entry<String, List<Bundle>> entry :
|
||||
schemasPackageAccessibleBundles.entrySet()) {
|
||||
List<PackageIdentifier> packageIdentifiers =
|
||||
new ArrayList<>(entry.getValue().size());
|
||||
for (int i = 0; i < packageIdentifiers.size(); i++) {
|
||||
packageIdentifiers.add(new PackageIdentifier(entry.getValue().get(i)));
|
||||
}
|
||||
schemasPackageAccessible.put(entry.getKey(), packageIdentifiers);
|
||||
}
|
||||
AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId);
|
||||
String packageName = convertUidToPackageName(callingUid);
|
||||
impl.setSchema(packageName, databaseName, schemas, schemasNotPlatformSurfaceable,
|
||||
impl.setSchema(
|
||||
packageName,
|
||||
databaseName,
|
||||
schemas,
|
||||
schemasNotPlatformSurfaceable,
|
||||
schemasPackageAccessible,
|
||||
forceOverride);
|
||||
invokeCallbackOnResult(callback,
|
||||
AppSearchResult.newSuccessfulResult(/*result=*/ null));
|
||||
|
||||
@@ -21,10 +21,12 @@ import android.annotation.WorkerThread;
|
||||
import android.app.appsearch.AppSearchResult;
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
import android.app.appsearch.GenericDocument;
|
||||
import android.app.appsearch.PackageIdentifier;
|
||||
import android.app.appsearch.SearchResultPage;
|
||||
import android.app.appsearch.SearchSpec;
|
||||
import android.app.appsearch.exceptions.AppSearchException;
|
||||
import android.os.Bundle;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
import android.util.Log;
|
||||
|
||||
@@ -230,6 +232,7 @@ public final class AppSearchImpl {
|
||||
* @param schemas Schemas to set for this app.
|
||||
* @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform
|
||||
* surfaces.
|
||||
* @param schemasPackageAccessible Schema types that are visible to the specified packages.
|
||||
* @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents
|
||||
* which do not comply with the new schema will be deleted.
|
||||
* @throws AppSearchException on IcingSearchEngine error.
|
||||
@@ -239,6 +242,7 @@ public final class AppSearchImpl {
|
||||
@NonNull String databaseName,
|
||||
@NonNull List<AppSearchSchema> schemas,
|
||||
@NonNull List<String> schemasNotPlatformSurfaceable,
|
||||
@NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible,
|
||||
boolean forceOverride)
|
||||
throws AppSearchException {
|
||||
mReadWriteLock.writeLock().lock();
|
||||
@@ -291,7 +295,18 @@ public final class AppSearchImpl {
|
||||
prefixedSchemasNotPlatformSurfaceable.add(
|
||||
prefix + schemasNotPlatformSurfaceable.get(i));
|
||||
}
|
||||
mVisibilityStoreLocked.setVisibility(prefix, prefixedSchemasNotPlatformSurfaceable);
|
||||
|
||||
Map<String, List<PackageIdentifier>> prefixedSchemasPackageAccessible =
|
||||
new ArrayMap<>(schemasNotPlatformSurfaceable.size());
|
||||
for (Map.Entry<String, List<PackageIdentifier>> entry :
|
||||
schemasPackageAccessible.entrySet()) {
|
||||
prefixedSchemasPackageAccessible.put(prefix + entry.getKey(), entry.getValue());
|
||||
}
|
||||
|
||||
mVisibilityStoreLocked.setVisibility(
|
||||
prefix,
|
||||
prefixedSchemasNotPlatformSurfaceable,
|
||||
prefixedSchemasPackageAccessible);
|
||||
|
||||
// Determine whether to schedule an immediate optimize.
|
||||
if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0
|
||||
@@ -448,6 +463,13 @@ public final class AppSearchImpl {
|
||||
@NonNull String queryExpression,
|
||||
@NonNull SearchSpec searchSpec)
|
||||
throws AppSearchException {
|
||||
if (!searchSpec.getPackageNames().isEmpty()
|
||||
&& !searchSpec.getPackageNames().contains(packageName)) {
|
||||
// Client wanted to query over some packages that weren't its own. This isn't
|
||||
// allowed through local query so we can return early with no results.
|
||||
return new SearchResultPage(Bundle.EMPTY);
|
||||
}
|
||||
|
||||
mReadWriteLock.readLock().lock();
|
||||
try {
|
||||
return doQueryLocked(
|
||||
@@ -480,13 +502,25 @@ public final class AppSearchImpl {
|
||||
// verified.
|
||||
mReadWriteLock.readLock().lock();
|
||||
try {
|
||||
// We use the mNamespaceMap.keySet here because it's the smaller set of valid prefixes
|
||||
// that could exist.
|
||||
Set<String> prefixes = mNamespaceMapLocked.keySet();
|
||||
Set<String> prefixes = new ArraySet<>();
|
||||
Set<String> packageFilters = new ArraySet<>(searchSpec.getPackageNames());
|
||||
|
||||
// Filter out any VisibilityStore documents which are AppSearch-internal only.
|
||||
prefixes.remove(
|
||||
createPrefix(VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME));
|
||||
for (String prefix : mNamespaceMapLocked.keySet()) {
|
||||
if (prefix.equals(VisibilityStore.VISIBILITY_STORE_PREFIX)) {
|
||||
// Filter out any VisibilityStore documents which are AppSearch-internal only.
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!packageFilters.isEmpty() && !packageFilters.contains(getPackageName(prefix))) {
|
||||
// Client wanted to restrict search over specified packages. Since the
|
||||
// specified packages don't include this prefix, don't add it to our search
|
||||
// filters.
|
||||
continue;
|
||||
}
|
||||
|
||||
// Otherwise, include this prefix in our global search.
|
||||
prefixes.add(prefix);
|
||||
}
|
||||
|
||||
return doQueryLocked(prefixes, queryExpression, searchSpec);
|
||||
} finally {
|
||||
@@ -500,22 +534,30 @@ public final class AppSearchImpl {
|
||||
@NonNull String queryExpression,
|
||||
@NonNull SearchSpec searchSpec)
|
||||
throws AppSearchException {
|
||||
SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
|
||||
SearchSpecProto.Builder searchSpecBuilder =
|
||||
searchSpecProto.toBuilder().setQuery(queryExpression);
|
||||
|
||||
ResultSpecProto resultSpec = SearchSpecToProtoConverter.toResultSpecProto(searchSpec);
|
||||
ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
|
||||
SearchResultProto searchResultProto;
|
||||
|
||||
SearchSpecToProtoConverter.toSearchSpecProto(searchSpec).toBuilder()
|
||||
.setQuery(queryExpression);
|
||||
// rewriteSearchSpecForPrefixesLocked will return false if none of the prefixes that the
|
||||
// client is trying to search on exist, so we can return an empty SearchResult and skip
|
||||
// sending request to Icing.
|
||||
if (!rewriteSearchSpecForPrefixesLocked(searchSpecBuilder, prefixes)) {
|
||||
return new SearchResultPage(Bundle.EMPTY);
|
||||
}
|
||||
searchResultProto =
|
||||
mIcingSearchEngineLocked.search(searchSpecBuilder.build(), scoringSpec, resultSpec);
|
||||
|
||||
ResultSpecProto.Builder resultSpecBuilder =
|
||||
SearchSpecToProtoConverter.toResultSpecProto(searchSpec).toBuilder();
|
||||
|
||||
// rewriteResultSpecForPrefixesLocked will return false if none of the prefixes that the
|
||||
// client is trying to search on exist, so we can return an empty SearchResult and skip
|
||||
// sending request to Icing.
|
||||
if (!rewriteResultSpecForPrefixesLocked(resultSpecBuilder, prefixes)) {
|
||||
return new SearchResultPage(Bundle.EMPTY);
|
||||
}
|
||||
|
||||
ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec);
|
||||
SearchResultProto searchResultProto =
|
||||
mIcingSearchEngineLocked.search(
|
||||
searchSpecBuilder.build(), scoringSpec, resultSpecBuilder.build());
|
||||
checkSuccess(searchResultProto.getStatus());
|
||||
|
||||
return rewriteSearchResultProto(searchResultProto);
|
||||
@@ -607,6 +649,14 @@ public final class AppSearchImpl {
|
||||
@NonNull String queryExpression,
|
||||
@NonNull SearchSpec searchSpec)
|
||||
throws AppSearchException {
|
||||
if (!searchSpec.getPackageNames().isEmpty()
|
||||
&& !searchSpec.getPackageNames().contains(packageName)) {
|
||||
// We're only removing documents within the parameter `packageName`. If we're not
|
||||
// restricting our remove-query to this package name, then there's nothing for us to
|
||||
// remove.
|
||||
return;
|
||||
}
|
||||
|
||||
SearchSpecProto searchSpecProto = SearchSpecToProtoConverter.toSearchSpecProto(searchSpec);
|
||||
SearchSpecProto.Builder searchSpecBuilder =
|
||||
searchSpecProto.toBuilder().setQuery(queryExpression);
|
||||
@@ -915,6 +965,47 @@ public final class AppSearchImpl {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rewrites the typePropertyMasks that exist in {@code prefixes}.
|
||||
*
|
||||
* <p>This method should be only called in query methods and get the READ lock to keep thread
|
||||
* safety.
|
||||
*
|
||||
* @return false if none of the requested prefixes exist.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
@GuardedBy("mReadWriteLock")
|
||||
boolean rewriteResultSpecForPrefixesLocked(
|
||||
@NonNull ResultSpecProto.Builder resultSpecBuilder, @NonNull Set<String> prefixes) {
|
||||
// Create a copy since retainAll() modifies the original set.
|
||||
Set<String> existingPrefixes = new ArraySet<>(mNamespaceMapLocked.keySet());
|
||||
existingPrefixes.retainAll(prefixes);
|
||||
|
||||
if (existingPrefixes.isEmpty()) {
|
||||
// None of the prefixes exist, empty query.
|
||||
return false;
|
||||
}
|
||||
|
||||
List<ResultSpecProto.TypePropertyMask> prefixedTypePropertyMasks = new ArrayList<>();
|
||||
// Rewrite filters to include a database prefix.
|
||||
for (String prefix : existingPrefixes) {
|
||||
Set<String> existingSchemaTypes = mSchemaMapLocked.get(prefix);
|
||||
// Qualify the given schema types
|
||||
for (ResultSpecProto.TypePropertyMask typePropertyMask :
|
||||
resultSpecBuilder.getTypePropertyMasksList()) {
|
||||
String qualifiedType = prefix + typePropertyMask.getSchemaType();
|
||||
if (existingSchemaTypes.contains(qualifiedType)) {
|
||||
prefixedTypePropertyMasks.add(
|
||||
typePropertyMask.toBuilder().setSchemaType(qualifiedType).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
resultSpecBuilder
|
||||
.clearTypePropertyMasks()
|
||||
.addAllTypePropertyMasks(prefixedTypePropertyMasks);
|
||||
return true;
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@GuardedBy("mReadWriteLock")
|
||||
SchemaProto getSchemaProtoLocked() throws AppSearchException {
|
||||
|
||||
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
|
||||
import android.app.appsearch.AppSearchResult;
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
import android.app.appsearch.GenericDocument;
|
||||
import android.app.appsearch.PackageIdentifier;
|
||||
import android.app.appsearch.exceptions.AppSearchException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
@@ -27,19 +28,21 @@ import android.util.ArraySet;
|
||||
import com.android.internal.annotations.VisibleForTesting;
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Manages any visibility settings for all the databases that AppSearchImpl knows about. Persists
|
||||
* the visibility settings and reloads them on initialization.
|
||||
* Manages any visibility settings for all the package's databases that AppSearchImpl knows about.
|
||||
* Persists the visibility settings and reloads them on initialization.
|
||||
*
|
||||
* <p>The VisibilityStore creates a document for each database. This document holds the visibility
|
||||
* settings that apply to that database. The VisibilityStore also creates a schema for these
|
||||
* documents and has its own database so that its data doesn't interfere with any clients' data. It
|
||||
* persists the document and schema through AppSearchImpl.
|
||||
* <p>The VisibilityStore creates a document for each package's databases. This document holds the
|
||||
* visibility settings that apply to that package's database. The VisibilityStore also creates a
|
||||
* schema for these documents and has its own package and database so that its data doesn't
|
||||
* interfere with any clients' data. It persists the document and schema through AppSearchImpl.
|
||||
*
|
||||
* <p>These visibility settings are used to ensure AppSearch queries respect the clients' settings
|
||||
* on who their data is visible to.
|
||||
@@ -52,18 +55,31 @@ import java.util.Set;
|
||||
*/
|
||||
class VisibilityStore {
|
||||
/** Schema type for documents that hold AppSearch's metadata, e.g. visibility settings */
|
||||
@VisibleForTesting static final String SCHEMA_TYPE = "Visibility";
|
||||
@VisibleForTesting static final String VISIBILITY_TYPE = "VisibilityType";
|
||||
|
||||
/**
|
||||
* Property that holds the list of platform-hidden schemas, as part of the visibility settings.
|
||||
*/
|
||||
@VisibleForTesting
|
||||
static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable";
|
||||
private static final String NOT_PLATFORM_SURFACEABLE_PROPERTY = "notPlatformSurfaceable";
|
||||
|
||||
/** Schema for the VisibilityStore's docuemnts. */
|
||||
@VisibleForTesting
|
||||
static final AppSearchSchema SCHEMA =
|
||||
new AppSearchSchema.Builder(SCHEMA_TYPE)
|
||||
/** 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.PropertyConfig.Builder(
|
||||
NOT_PLATFORM_SURFACEABLE_PROPERTY)
|
||||
@@ -71,6 +87,39 @@ class VisibilityStore {
|
||||
.setCardinality(
|
||||
AppSearchSchema.PropertyConfig.CARDINALITY_REPEATED)
|
||||
.build())
|
||||
.addProperty(
|
||||
new AppSearchSchema.PropertyConfig.Builder(PACKAGE_ACCESSIBLE_PROPERTY)
|
||||
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_DOCUMENT)
|
||||
.setSchemaType(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.PropertyConfig.Builder(PACKAGE_NAME_PROPERTY)
|
||||
.setCardinality(
|
||||
AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
|
||||
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
|
||||
.build())
|
||||
.addProperty(
|
||||
new AppSearchSchema.PropertyConfig.Builder(SHA_256_CERT_PROPERTY)
|
||||
.setCardinality(
|
||||
AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
|
||||
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_BYTES)
|
||||
.build())
|
||||
.addProperty(
|
||||
new AppSearchSchema.PropertyConfig.Builder(ACCESSIBLE_SCHEMA_PROPERTY)
|
||||
.setCardinality(
|
||||
AppSearchSchema.PropertyConfig.CARDINALITY_OPTIONAL)
|
||||
.setDataType(AppSearchSchema.PropertyConfig.DATA_TYPE_STRING)
|
||||
.build())
|
||||
.build();
|
||||
|
||||
/**
|
||||
@@ -86,7 +135,7 @@ class VisibilityStore {
|
||||
* database name. Tracked here to tell when we're looking at our own prefix when looking through
|
||||
* AppSearchImpl.
|
||||
*/
|
||||
private static final String VISIBILITY_STORE_PREFIX =
|
||||
static final String VISIBILITY_STORE_PREFIX =
|
||||
AppSearchImpl.createPrefix(PACKAGE_NAME, DATABASE_NAME);
|
||||
|
||||
/** Namespace of documents that contain visibility settings */
|
||||
@@ -102,9 +151,22 @@ class VisibilityStore {
|
||||
/**
|
||||
* 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<>();
|
||||
|
||||
/**
|
||||
* 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<>();
|
||||
|
||||
/**
|
||||
* Creates an uninitialized VisibilityStore object. Callers must also call {@link #initialize()}
|
||||
* before using the object.
|
||||
@@ -120,19 +182,22 @@ class VisibilityStore {
|
||||
*
|
||||
* <p>This is kept separate from the constructor because this will call methods on
|
||||
* AppSearchImpl. Some may even then recursively call back into VisibilityStore (for example,
|
||||
* {@link AppSearchImpl#setSchema} will call {@link #setVisibility(String, Set)}. We need to
|
||||
* have both AppSearchImpl and VisibilityStore fully initialized for this call flow to work.
|
||||
* {@link AppSearchImpl#setSchema} will call {@link #setVisibility}. We need to have both
|
||||
* AppSearchImpl and VisibilityStore fully initialized for this call flow to work.
|
||||
*
|
||||
* @throws AppSearchException AppSearchException on AppSearchImpl error.
|
||||
*/
|
||||
public void initialize() throws AppSearchException {
|
||||
if (!mAppSearchImpl.hasSchemaTypeLocked(PACKAGE_NAME, DATABASE_NAME, SCHEMA_TYPE)) {
|
||||
if (!mAppSearchImpl.hasSchemaTypeLocked(PACKAGE_NAME, DATABASE_NAME, VISIBILITY_TYPE)
|
||||
|| !mAppSearchImpl.hasSchemaTypeLocked(
|
||||
PACKAGE_NAME, DATABASE_NAME, PACKAGE_ACCESSIBLE_TYPE)) {
|
||||
// Schema type doesn't exist yet. Add it.
|
||||
mAppSearchImpl.setSchema(
|
||||
PACKAGE_NAME,
|
||||
DATABASE_NAME,
|
||||
Collections.singletonList(SCHEMA),
|
||||
Arrays.asList(VISIBILITY_SCHEMA, PACKAGE_ACCESSIBLE_SCHEMA),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
}
|
||||
|
||||
@@ -153,9 +218,41 @@ class VisibilityStore {
|
||||
NAMESPACE,
|
||||
/*uri=*/ addUriPrefix(prefix));
|
||||
|
||||
// Update platform visibility settings
|
||||
String[] schemas =
|
||||
document.getPropertyStringArray(NOT_PLATFORM_SURFACEABLE_PROPERTY);
|
||||
mNotPlatformSurfaceableMap.put(prefix, new ArraySet<>(Arrays.asList(schemas)));
|
||||
if (schemas != null) {
|
||||
mNotPlatformSurfaceableMap.put(prefix, new ArraySet<>(Arrays.asList(schemas)));
|
||||
}
|
||||
|
||||
// Update 3p package visibility settings
|
||||
Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>();
|
||||
GenericDocument[] packageAccessibleDocuments =
|
||||
document.getPropertyDocumentArray(PACKAGE_ACCESSIBLE_PROPERTY);
|
||||
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);
|
||||
PackageIdentifier packageIdentifier =
|
||||
new PackageIdentifier(packageName, sha256Cert);
|
||||
|
||||
String prefixedSchema =
|
||||
packageAccessibleDocuments[i].getPropertyString(
|
||||
ACCESSIBLE_SCHEMA_PROPERTY);
|
||||
Set<PackageIdentifier> packageIdentifiers =
|
||||
schemaToPackageIdentifierMap.get(prefixedSchema);
|
||||
if (packageIdentifiers == null) {
|
||||
packageIdentifiers = new ArraySet<>();
|
||||
}
|
||||
packageIdentifiers.add(packageIdentifier);
|
||||
schemaToPackageIdentifierMap.put(prefixedSchema, packageIdentifiers);
|
||||
}
|
||||
}
|
||||
mPackageAccessibleMap.put(prefix, schemaToPackageIdentifierMap);
|
||||
} catch (AppSearchException e) {
|
||||
if (e.getResultCode() == AppSearchResult.RESULT_NOT_FOUND) {
|
||||
// TODO(b/172068212): This indicates some desync error. We were expecting a
|
||||
@@ -176,31 +273,67 @@ class VisibilityStore {
|
||||
* @param prefix Prefix that identifies who owns the {@code schemasNotPlatformSurfaceable}.
|
||||
* @param schemasNotPlatformSurfaceable Set of prefixed schemas that should be hidden from the
|
||||
* platform.
|
||||
* @param schemasPackageAccessible Map of prefixed schemas to a list of package identifiers that
|
||||
* have access to the schema.
|
||||
* @throws AppSearchException on AppSearchImpl error.
|
||||
*/
|
||||
public void setVisibility(
|
||||
@NonNull String prefix, @NonNull Set<String> schemasNotPlatformSurfaceable)
|
||||
@NonNull String prefix,
|
||||
@NonNull Set<String> schemasNotPlatformSurfaceable,
|
||||
@NonNull Map<String, List<PackageIdentifier>> schemasPackageAccessible)
|
||||
throws AppSearchException {
|
||||
Preconditions.checkNotNull(prefix);
|
||||
Preconditions.checkNotNull(schemasNotPlatformSurfaceable);
|
||||
Preconditions.checkNotNull(schemasPackageAccessible);
|
||||
|
||||
// Persist the document
|
||||
GenericDocument.Builder visibilityDocument =
|
||||
new GenericDocument.Builder(/*uri=*/ addUriPrefix(prefix), SCHEMA_TYPE)
|
||||
new GenericDocument.Builder(/*uri=*/ addUriPrefix(prefix), VISIBILITY_TYPE)
|
||||
.setNamespace(NAMESPACE);
|
||||
if (!schemasNotPlatformSurfaceable.isEmpty()) {
|
||||
visibilityDocument.setPropertyString(
|
||||
NOT_PLATFORM_SURFACEABLE_PROPERTY,
|
||||
schemasNotPlatformSurfaceable.toArray(new String[0]));
|
||||
}
|
||||
|
||||
Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap = new ArrayMap<>();
|
||||
List<GenericDocument> packageAccessibleDocuments = new ArrayList<>();
|
||||
for (Map.Entry<String, List<PackageIdentifier>> entry :
|
||||
schemasPackageAccessible.entrySet()) {
|
||||
for (int i = 0; i < entry.getValue().size(); i++) {
|
||||
// TODO(b/169883602): remove the "placeholder" uri once upstream changes to relax
|
||||
// nested
|
||||
// document uri rules gets synced down.
|
||||
GenericDocument packageAccessibleDocument =
|
||||
new GenericDocument.Builder(/*uri=*/ "placeholder", PACKAGE_ACCESSIBLE_TYPE)
|
||||
.setNamespace(NAMESPACE)
|
||||
.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();
|
||||
packageAccessibleDocuments.add(packageAccessibleDocument);
|
||||
}
|
||||
schemaToPackageIdentifierMap.put(entry.getKey(), new ArraySet<>(entry.getValue()));
|
||||
}
|
||||
if (!packageAccessibleDocuments.isEmpty()) {
|
||||
visibilityDocument.setPropertyDocument(
|
||||
PACKAGE_ACCESSIBLE_PROPERTY,
|
||||
packageAccessibleDocuments.toArray(new GenericDocument[0]));
|
||||
}
|
||||
|
||||
mAppSearchImpl.putDocument(PACKAGE_NAME, DATABASE_NAME, visibilityDocument.build());
|
||||
|
||||
// Update derived data structures.
|
||||
mNotPlatformSurfaceableMap.put(prefix, schemasNotPlatformSurfaceable);
|
||||
mPackageAccessibleMap.put(prefix, schemaToPackageIdentifierMap);
|
||||
}
|
||||
|
||||
/** Returns if the schema is surfaceable by the platform. */
|
||||
@NonNull
|
||||
// TODO(b/169883602): check permissions against the allowlisted global querier package name.
|
||||
public boolean isSchemaPlatformSurfaceable(
|
||||
@NonNull String prefix, @NonNull String prefixedSchema) {
|
||||
Preconditions.checkNotNull(prefix);
|
||||
@@ -212,6 +345,31 @@ class VisibilityStore {
|
||||
return !notPlatformSurfaceableSchemas.contains(prefixedSchema);
|
||||
}
|
||||
|
||||
/** Returns whether the schema is accessible by {@code accessingPackage}. */
|
||||
// TODO(b/169883602): check certificate and package against the incoming querier's uid/package.
|
||||
public boolean isSchemaPackageAccessible(
|
||||
@NonNull String prefix,
|
||||
@NonNull String prefixedSchema,
|
||||
@NonNull PackageIdentifier accessingPackage) {
|
||||
Preconditions.checkNotNull(prefix);
|
||||
Preconditions.checkNotNull(prefixedSchema);
|
||||
Preconditions.checkNotNull(accessingPackage);
|
||||
|
||||
Map<String, Set<PackageIdentifier>> schemaToPackageIdentifierMap =
|
||||
mPackageAccessibleMap.get(prefix);
|
||||
if (schemaToPackageIdentifierMap == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Set<PackageIdentifier> packageIdentifiers =
|
||||
schemaToPackageIdentifierMap.get(prefixedSchema);
|
||||
if (packageIdentifiers == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return packageIdentifiers.contains(accessingPackage);
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an {@code AppSearchImpl#reset()} by clearing any cached state.
|
||||
*
|
||||
@@ -219,6 +377,7 @@ class VisibilityStore {
|
||||
*/
|
||||
void handleReset() {
|
||||
mNotPlatformSurfaceableMap.clear();
|
||||
mPackageAccessibleMap.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -26,6 +26,9 @@ import com.google.android.icing.proto.ScoringSpecProto;
|
||||
import com.google.android.icing.proto.SearchSpecProto;
|
||||
import com.google.android.icing.proto.TermMatchType;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Translates a {@link SearchSpec} into icing search protos.
|
||||
*
|
||||
@@ -57,14 +60,22 @@ public final class SearchSpecToProtoConverter {
|
||||
@NonNull
|
||||
public static ResultSpecProto toResultSpecProto(@NonNull SearchSpec spec) {
|
||||
Preconditions.checkNotNull(spec);
|
||||
return ResultSpecProto.newBuilder()
|
||||
.setNumPerPage(spec.getResultCountPerPage())
|
||||
.setSnippetSpec(
|
||||
ResultSpecProto.SnippetSpecProto.newBuilder()
|
||||
.setNumToSnippet(spec.getSnippetCount())
|
||||
.setNumMatchesPerProperty(spec.getSnippetCountPerProperty())
|
||||
.setMaxWindowBytes(spec.getMaxSnippetSize()))
|
||||
.build();
|
||||
ResultSpecProto.Builder builder =
|
||||
ResultSpecProto.newBuilder()
|
||||
.setNumPerPage(spec.getResultCountPerPage())
|
||||
.setSnippetSpec(
|
||||
ResultSpecProto.SnippetSpecProto.newBuilder()
|
||||
.setNumToSnippet(spec.getSnippetCount())
|
||||
.setNumMatchesPerProperty(spec.getSnippetCountPerProperty())
|
||||
.setMaxWindowBytes(spec.getMaxSnippetSize()));
|
||||
Map<String, List<String>> projectionTypePropertyPaths = spec.getProjections();
|
||||
for (Map.Entry<String, List<String>> e : projectionTypePropertyPaths.entrySet()) {
|
||||
builder.addTypePropertyMasks(
|
||||
ResultSpecProto.TypePropertyMask.newBuilder()
|
||||
.setSchemaType(e.getKey())
|
||||
.addAllPaths(e.getValue()));
|
||||
}
|
||||
return builder.build();
|
||||
}
|
||||
|
||||
/** Extracts {@link ScoringSpecProto} information from a {@link SearchSpec}. */
|
||||
@@ -79,17 +90,28 @@ public final class SearchSpecToProtoConverter {
|
||||
if (orderCodeProto == null) {
|
||||
throw new IllegalArgumentException("Invalid result ranking order: " + orderCode);
|
||||
}
|
||||
protoBuilder.setOrderBy(orderCodeProto);
|
||||
|
||||
@SearchSpec.RankingStrategy int rankingStrategyCode = spec.getRankingStrategy();
|
||||
ScoringSpecProto.RankingStrategy.Code rankingStrategyCodeProto =
|
||||
ScoringSpecProto.RankingStrategy.Code.forNumber(rankingStrategyCode);
|
||||
if (rankingStrategyCodeProto == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid result ranking strategy: " + rankingStrategyCode);
|
||||
}
|
||||
protoBuilder.setRankBy(rankingStrategyCodeProto);
|
||||
protoBuilder
|
||||
.setOrderBy(orderCodeProto)
|
||||
.setRankBy(toProtoRankingStrategy(spec.getRankingStrategy()));
|
||||
|
||||
return protoBuilder.build();
|
||||
}
|
||||
|
||||
private static ScoringSpecProto.RankingStrategy.Code toProtoRankingStrategy(
|
||||
@SearchSpec.RankingStrategy int rankingStrategyCode) {
|
||||
switch (rankingStrategyCode) {
|
||||
case SearchSpec.RANKING_STRATEGY_NONE:
|
||||
return ScoringSpecProto.RankingStrategy.Code.NONE;
|
||||
case SearchSpec.RANKING_STRATEGY_DOCUMENT_SCORE:
|
||||
return ScoringSpecProto.RankingStrategy.Code.DOCUMENT_SCORE;
|
||||
case SearchSpec.RANKING_STRATEGY_CREATION_TIMESTAMP:
|
||||
return ScoringSpecProto.RankingStrategy.Code.CREATION_TIMESTAMP;
|
||||
case SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE:
|
||||
return ScoringSpecProto.RankingStrategy.Code
|
||||
.RELEVANCE_SCORE_NONFUNCTIONAL_PLACEHOLDER;
|
||||
default:
|
||||
throw new IllegalArgumentException(
|
||||
"Invalid result ranking strategy: " + rankingStrategyCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
I8b7425b3f87153547d1c8f5b560be5a54c9be97e
|
||||
I6745091e5cb97d69ce2e5f85d3d15c073e7e3ef7
|
||||
|
||||
42
core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
vendored
Normal file
42
core/tests/coretests/src/android/app/appsearch/external/app/PutDocumentsRequestTest.java
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/*
|
||||
* Copyright 2020 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 android.app.appsearch;
|
||||
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
public class PutDocumentsRequestTest {
|
||||
|
||||
@Test
|
||||
public void addGenericDocument_byCollection() {
|
||||
Set<AppSearchEmail> emails =
|
||||
ImmutableSet.of(
|
||||
new AppSearchEmail.Builder("test1").build(),
|
||||
new AppSearchEmail.Builder("test2").build());
|
||||
PutDocumentsRequest request =
|
||||
new PutDocumentsRequest.Builder().addGenericDocument(emails).build();
|
||||
|
||||
assertThat(request.getDocuments().get(0).getUri()).isEqualTo("test1");
|
||||
assertThat(request.getDocuments().get(1).getUri()).isEqualTo("test2");
|
||||
}
|
||||
}
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
package android.app.appsearch;
|
||||
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.os.Bundle;
|
||||
@@ -26,6 +27,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class SearchSpecTest {
|
||||
|
||||
@Test
|
||||
public void testGetBundle() {
|
||||
SearchSpec searchSpec =
|
||||
@@ -33,6 +35,7 @@ public class SearchSpecTest {
|
||||
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
|
||||
.addNamespace("namespace1", "namespace2")
|
||||
.addSchemaType("schemaTypes1", "schemaTypes2")
|
||||
.addFilterPackageNames("package1", "package2")
|
||||
.setSnippetCount(5)
|
||||
.setSnippetCountPerProperty(10)
|
||||
.setMaxSnippetSize(15)
|
||||
@@ -48,6 +51,8 @@ public class SearchSpecTest {
|
||||
.containsExactly("namespace1", "namespace2");
|
||||
assertThat(bundle.getStringArrayList(SearchSpec.SCHEMA_TYPE_FIELD))
|
||||
.containsExactly("schemaTypes1", "schemaTypes2");
|
||||
assertThat(bundle.getStringArrayList(SearchSpec.PACKAGE_NAME_FIELD))
|
||||
.containsExactly("package1", "package2");
|
||||
assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_FIELD)).isEqualTo(5);
|
||||
assertThat(bundle.getInt(SearchSpec.SNIPPET_COUNT_PER_PROPERTY_FIELD)).isEqualTo(10);
|
||||
assertThat(bundle.getInt(SearchSpec.MAX_SNIPPET_FIELD)).isEqualTo(15);
|
||||
@@ -62,15 +67,26 @@ public class SearchSpecTest {
|
||||
SearchSpec searchSpec =
|
||||
new SearchSpec.Builder()
|
||||
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
|
||||
.addProjectionTypePropertyPaths("TypeA", "field1", "field2.subfield2")
|
||||
.addProjectionTypePropertyPaths("TypeB", "field7")
|
||||
.addProjectionTypePropertyPaths("TypeC")
|
||||
.addProjection("TypeA", "field1", "field2.subfield2")
|
||||
.addProjection("TypeB", "field7")
|
||||
.addProjection("TypeC")
|
||||
.build();
|
||||
|
||||
Map<String, List<String>> typePropertyPathMap = searchSpec.getProjectionTypePropertyPaths();
|
||||
Map<String, List<String>> typePropertyPathMap = searchSpec.getProjections();
|
||||
assertThat(typePropertyPathMap.keySet()).containsExactly("TypeA", "TypeB", "TypeC");
|
||||
assertThat(typePropertyPathMap.get("TypeA")).containsExactly("field1", "field2.subfield2");
|
||||
assertThat(typePropertyPathMap.get("TypeB")).containsExactly("field7");
|
||||
assertThat(typePropertyPathMap.get("TypeC")).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGetRankingStrategy() {
|
||||
SearchSpec searchSpec =
|
||||
new SearchSpec.Builder()
|
||||
.setTermMatch(SearchSpec.TERM_MATCH_PREFIX)
|
||||
.setRankingStrategy(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE)
|
||||
.build();
|
||||
assertThat(searchSpec.getRankingStrategy())
|
||||
.isEqualTo(SearchSpec.RANKING_STRATEGY_RELEVANCE_SCORE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,13 +22,13 @@ import static org.testng.Assert.expectThrows;
|
||||
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
import android.app.appsearch.GenericDocument;
|
||||
import android.app.appsearch.PackageIdentifier;
|
||||
import android.app.appsearch.SearchResult;
|
||||
import android.app.appsearch.SearchResultPage;
|
||||
import android.app.appsearch.SearchSpec;
|
||||
import android.app.appsearch.exceptions.AppSearchException;
|
||||
|
||||
import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter;
|
||||
import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter;
|
||||
import com.android.server.appsearch.proto.DocumentProto;
|
||||
import com.android.server.appsearch.proto.GetOptimizeInfoResultProto;
|
||||
import com.android.server.appsearch.proto.PropertyConfigProto;
|
||||
@@ -41,6 +41,7 @@ import com.android.server.appsearch.proto.StringIndexingConfig;
|
||||
import com.android.server.appsearch.proto.TermMatchType;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -55,27 +56,10 @@ import java.util.List;
|
||||
public class AppSearchImplTest {
|
||||
@Rule public TemporaryFolder mTemporaryFolder = new TemporaryFolder();
|
||||
private AppSearchImpl mAppSearchImpl;
|
||||
private SchemaTypeConfigProto mVisibilitySchemaProto;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
mAppSearchImpl = AppSearchImpl.create(mTemporaryFolder.newFolder());
|
||||
|
||||
AppSearchSchema visibilitySchema = VisibilityStore.SCHEMA;
|
||||
|
||||
// We need to rewrite the schema type to follow AppSearchImpl's prefixing scheme.
|
||||
AppSearchSchema.Builder rewrittenVisibilitySchema =
|
||||
new AppSearchSchema.Builder(
|
||||
AppSearchImpl.createPrefix(
|
||||
VisibilityStore.PACKAGE_NAME, VisibilityStore.DATABASE_NAME)
|
||||
+ VisibilityStore.SCHEMA_TYPE);
|
||||
List<AppSearchSchema.PropertyConfig> visibilityProperties =
|
||||
visibilitySchema.getProperties();
|
||||
for (AppSearchSchema.PropertyConfig property : visibilityProperties) {
|
||||
rewrittenVisibilitySchema.addProperty(property);
|
||||
}
|
||||
mVisibilitySchemaProto =
|
||||
SchemaToProtoConverter.toSchemaTypeConfigProto(rewrittenVisibilitySchema.build());
|
||||
}
|
||||
|
||||
// TODO(b/175430168) add test to verify reset is working properly.
|
||||
@@ -407,6 +391,7 @@ public class AppSearchImplTest {
|
||||
"database",
|
||||
schemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Insert enough documents.
|
||||
@@ -464,6 +449,7 @@ public class AppSearchImplTest {
|
||||
"database",
|
||||
schemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Insert document
|
||||
@@ -495,12 +481,14 @@ public class AppSearchImplTest {
|
||||
"database1",
|
||||
schemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
mAppSearchImpl.setSchema(
|
||||
"package",
|
||||
"database2",
|
||||
schemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Insert documents
|
||||
@@ -537,6 +525,117 @@ public class AppSearchImplTest {
|
||||
assertThat(searchResultPage.getResults()).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
|
||||
* test until we have official support for multiple-apps indexing at once.
|
||||
*/
|
||||
@Test
|
||||
public void testQueryWithMultiplePackages_noPackageFilters() throws Exception {
|
||||
// Insert package1 schema
|
||||
List<AppSearchSchema> schema1 =
|
||||
ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
|
||||
mAppSearchImpl.setSchema(
|
||||
"package1",
|
||||
"database1",
|
||||
schema1,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Insert package2 schema
|
||||
List<AppSearchSchema> schema2 =
|
||||
ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
|
||||
mAppSearchImpl.setSchema(
|
||||
"package2",
|
||||
"database2",
|
||||
schema2,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Insert package1 document
|
||||
GenericDocument document =
|
||||
new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
|
||||
mAppSearchImpl.putDocument("package1", "database1", document);
|
||||
|
||||
// No query filters specified, package2 shouldn't be able to query for package1's documents.
|
||||
SearchSpec searchSpec =
|
||||
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
|
||||
SearchResultPage searchResultPage =
|
||||
mAppSearchImpl.query("package2", "database2", "", searchSpec);
|
||||
assertThat(searchResultPage.getResults()).isEmpty();
|
||||
|
||||
// Insert package2 document
|
||||
document =
|
||||
new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
|
||||
mAppSearchImpl.putDocument("package2", "database2", document);
|
||||
|
||||
// No query filters specified. package2 should only get its own documents back.
|
||||
searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
|
||||
assertThat(searchResultPage.getResults()).hasSize(1);
|
||||
assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
|
||||
* test until we have official support for multiple-apps indexing at once.
|
||||
*/
|
||||
@Test
|
||||
public void testQueryWithMultiplePackages_withPackageFilters() throws Exception {
|
||||
// Insert package1 schema
|
||||
List<AppSearchSchema> schema1 =
|
||||
ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
|
||||
mAppSearchImpl.setSchema(
|
||||
"package1",
|
||||
"database1",
|
||||
schema1,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Insert package2 schema
|
||||
List<AppSearchSchema> schema2 =
|
||||
ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
|
||||
mAppSearchImpl.setSchema(
|
||||
"package2",
|
||||
"database2",
|
||||
schema2,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Insert package1 document
|
||||
GenericDocument document =
|
||||
new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
|
||||
mAppSearchImpl.putDocument("package1", "database1", document);
|
||||
|
||||
// "package1" filter specified, but package2 shouldn't be able to query for package1's
|
||||
// documents.
|
||||
SearchSpec searchSpec =
|
||||
new SearchSpec.Builder()
|
||||
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
|
||||
.addFilterPackageNames("package1")
|
||||
.build();
|
||||
SearchResultPage searchResultPage =
|
||||
mAppSearchImpl.query("package2", "database2", "", searchSpec);
|
||||
assertThat(searchResultPage.getResults()).isEmpty();
|
||||
|
||||
// Insert package2 document
|
||||
document =
|
||||
new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
|
||||
mAppSearchImpl.putDocument("package2", "database2", document);
|
||||
|
||||
// "package2" filter specified, package2 should only get its own documents back.
|
||||
searchSpec =
|
||||
new SearchSpec.Builder()
|
||||
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
|
||||
.addFilterPackageNames("package2")
|
||||
.build();
|
||||
searchResultPage = mAppSearchImpl.query("package2", "database2", "", searchSpec);
|
||||
assertThat(searchResultPage.getResults()).hasSize(1);
|
||||
assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGlobalQueryEmptyDatabase() throws Exception {
|
||||
SearchSpec searchSpec =
|
||||
@@ -545,6 +644,115 @@ public class AppSearchImplTest {
|
||||
assertThat(searchResultPage.getResults()).isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
|
||||
* test until we have official support for multiple-apps indexing at once.
|
||||
*/
|
||||
@Test
|
||||
public void testGlobalQueryWithMultiplePackages_noPackageFilters() throws Exception {
|
||||
// Insert package1 schema
|
||||
List<AppSearchSchema> schema1 =
|
||||
ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
|
||||
mAppSearchImpl.setSchema(
|
||||
"package1",
|
||||
"database1",
|
||||
schema1,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Insert package2 schema
|
||||
List<AppSearchSchema> schema2 =
|
||||
ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
|
||||
mAppSearchImpl.setSchema(
|
||||
"package2",
|
||||
"database2",
|
||||
schema2,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Insert package1 document
|
||||
GenericDocument document1 =
|
||||
new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
|
||||
mAppSearchImpl.putDocument("package1", "database1", document1);
|
||||
|
||||
// Insert package2 document
|
||||
GenericDocument document2 =
|
||||
new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
|
||||
mAppSearchImpl.putDocument("package2", "database2", document2);
|
||||
|
||||
// No query filters specified, global query can retrieve all documents.
|
||||
SearchSpec searchSpec =
|
||||
new SearchSpec.Builder().setTermMatch(TermMatchType.Code.PREFIX_VALUE).build();
|
||||
SearchResultPage searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
|
||||
assertThat(searchResultPage.getResults()).hasSize(2);
|
||||
|
||||
// Document2 will be first since it got indexed later and has a "better", aka more recent
|
||||
// score.
|
||||
assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
|
||||
assertThat(searchResultPage.getResults().get(1).getDocument()).isEqualTo(document1);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO(b/169883602): This should be an integration test at the cts-level. This is a short-term
|
||||
* test until we have official support for multiple-apps indexing at once.
|
||||
*/
|
||||
@Test
|
||||
public void testGlobalQueryWithMultiplePackages_withPackageFilters() throws Exception {
|
||||
// Insert package1 schema
|
||||
List<AppSearchSchema> schema1 =
|
||||
ImmutableList.of(new AppSearchSchema.Builder("schema1").build());
|
||||
mAppSearchImpl.setSchema(
|
||||
"package1",
|
||||
"database1",
|
||||
schema1,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Insert package2 schema
|
||||
List<AppSearchSchema> schema2 =
|
||||
ImmutableList.of(new AppSearchSchema.Builder("schema2").build());
|
||||
mAppSearchImpl.setSchema(
|
||||
"package2",
|
||||
"database2",
|
||||
schema2,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Insert package1 document
|
||||
GenericDocument document1 =
|
||||
new GenericDocument.Builder<>("uri", "schema1").setNamespace("namespace").build();
|
||||
mAppSearchImpl.putDocument("package1", "database1", document1);
|
||||
|
||||
// Insert package2 document
|
||||
GenericDocument document2 =
|
||||
new GenericDocument.Builder<>("uri", "schema2").setNamespace("namespace").build();
|
||||
mAppSearchImpl.putDocument("package2", "database2", document2);
|
||||
|
||||
// "package1" filter specified
|
||||
SearchSpec searchSpec =
|
||||
new SearchSpec.Builder()
|
||||
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
|
||||
.addFilterPackageNames("package1")
|
||||
.build();
|
||||
SearchResultPage searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
|
||||
assertThat(searchResultPage.getResults()).hasSize(1);
|
||||
assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document1);
|
||||
|
||||
// "package2" filter specified
|
||||
searchSpec =
|
||||
new SearchSpec.Builder()
|
||||
.setTermMatch(TermMatchType.Code.PREFIX_VALUE)
|
||||
.addFilterPackageNames("package2")
|
||||
.build();
|
||||
searchResultPage = mAppSearchImpl.globalQuery("", searchSpec);
|
||||
assertThat(searchResultPage.getResults()).hasSize(1);
|
||||
assertThat(searchResultPage.getResults().get(0).getDocument()).isEqualTo(document2);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveEmptyDatabase_noExceptionThrown() throws Exception {
|
||||
SearchSpec searchSpec =
|
||||
@@ -567,6 +775,9 @@ public class AppSearchImplTest {
|
||||
|
||||
@Test
|
||||
public void testSetSchema() throws Exception {
|
||||
List<SchemaTypeConfigProto> existingSchemas =
|
||||
mAppSearchImpl.getSchemaProtoLocked().getTypesList();
|
||||
|
||||
List<AppSearchSchema> schemas =
|
||||
Collections.singletonList(new AppSearchSchema.Builder("Email").build());
|
||||
// Set schema Email to AppSearch database1
|
||||
@@ -575,6 +786,7 @@ public class AppSearchImplTest {
|
||||
"database1",
|
||||
schemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Create expected schemaType proto.
|
||||
@@ -586,7 +798,7 @@ public class AppSearchImplTest {
|
||||
.build();
|
||||
|
||||
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
|
||||
expectedTypes.add(mVisibilitySchemaProto);
|
||||
expectedTypes.addAll(existingSchemas);
|
||||
expectedTypes.addAll(expectedProto.getTypesList());
|
||||
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
|
||||
.containsExactlyElementsIn(expectedTypes);
|
||||
@@ -594,20 +806,30 @@ public class AppSearchImplTest {
|
||||
|
||||
@Test
|
||||
public void testSetSchema_existingSchemaRetainsVisibilitySetting() throws Exception {
|
||||
PackageIdentifier package1 =
|
||||
new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
|
||||
|
||||
String prefix = AppSearchImpl.createPrefix("package", "database");
|
||||
mAppSearchImpl.setSchema(
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
|
||||
/*schemasPackageAccessible=*/ ImmutableMap.of(
|
||||
"schema1", ImmutableList.of(package1)),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// "schema1" is platform hidden now
|
||||
// "schema1" is platform hidden now and package visible to package1
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
|
||||
.isFalse();
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
|
||||
.isTrue();
|
||||
|
||||
// Add a new schema, and include the already-existing "schema1"
|
||||
mAppSearchImpl.setSchema(
|
||||
@@ -617,24 +839,40 @@ public class AppSearchImplTest {
|
||||
new AppSearchSchema.Builder("schema1").build(),
|
||||
new AppSearchSchema.Builder("schema2").build()),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
|
||||
/*schemasPackageAccessible=*/ ImmutableMap.of(
|
||||
"schema1", ImmutableList.of(package1)),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Check that "schema1" is still platform hidden, but "schema2" is the default platform
|
||||
// visible.
|
||||
// Check that "schema1" still has the same visibility settings
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
|
||||
.isFalse();
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
|
||||
.isTrue();
|
||||
|
||||
// "schema2" has default visibility settings
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPlatformSurfaceable(prefix, prefix + "schema2"))
|
||||
.isTrue();
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPackageAccessible(prefix, prefix + "schema2", package1))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRemoveSchema() throws Exception {
|
||||
List<SchemaTypeConfigProto> existingSchemas =
|
||||
mAppSearchImpl.getSchemaProtoLocked().getTypesList();
|
||||
|
||||
List<AppSearchSchema> schemas =
|
||||
ImmutableList.of(
|
||||
new AppSearchSchema.Builder("Email").build(),
|
||||
@@ -645,6 +883,7 @@ public class AppSearchImplTest {
|
||||
"database1",
|
||||
schemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Create expected schemaType proto.
|
||||
@@ -660,7 +899,7 @@ public class AppSearchImplTest {
|
||||
|
||||
// Check both schema Email and Document saved correctly.
|
||||
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
|
||||
expectedTypes.add(mVisibilitySchemaProto);
|
||||
expectedTypes.addAll(existingSchemas);
|
||||
expectedTypes.addAll(expectedProto.getTypesList());
|
||||
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
|
||||
.containsExactlyElementsIn(expectedTypes);
|
||||
@@ -677,6 +916,7 @@ public class AppSearchImplTest {
|
||||
"database1",
|
||||
finalSchemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false));
|
||||
assertThat(e).hasMessageThat().contains("Schema is incompatible");
|
||||
assertThat(e).hasMessageThat().contains("Deleted types: [package$database1/Document]");
|
||||
@@ -687,6 +927,7 @@ public class AppSearchImplTest {
|
||||
"database1",
|
||||
finalSchemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ true);
|
||||
|
||||
// Check Document schema is removed.
|
||||
@@ -698,7 +939,7 @@ public class AppSearchImplTest {
|
||||
.build();
|
||||
|
||||
expectedTypes = new ArrayList<>();
|
||||
expectedTypes.add(mVisibilitySchemaProto);
|
||||
expectedTypes.addAll(existingSchemas);
|
||||
expectedTypes.addAll(expectedProto.getTypesList());
|
||||
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
|
||||
.containsExactlyElementsIn(expectedTypes);
|
||||
@@ -706,6 +947,9 @@ public class AppSearchImplTest {
|
||||
|
||||
@Test
|
||||
public void testRemoveSchema_differentDataBase() throws Exception {
|
||||
List<SchemaTypeConfigProto> existingSchemas =
|
||||
mAppSearchImpl.getSchemaProtoLocked().getTypesList();
|
||||
|
||||
// Create schemas
|
||||
List<AppSearchSchema> schemas =
|
||||
ImmutableList.of(
|
||||
@@ -718,12 +962,14 @@ public class AppSearchImplTest {
|
||||
"database1",
|
||||
schemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
mAppSearchImpl.setSchema(
|
||||
"package",
|
||||
"database2",
|
||||
schemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// Create expected schemaType proto.
|
||||
@@ -745,7 +991,7 @@ public class AppSearchImplTest {
|
||||
|
||||
// Check Email and Document is saved in database 1 and 2 correctly.
|
||||
List<SchemaTypeConfigProto> expectedTypes = new ArrayList<>();
|
||||
expectedTypes.add(mVisibilitySchemaProto);
|
||||
expectedTypes.addAll(existingSchemas);
|
||||
expectedTypes.addAll(expectedProto.getTypesList());
|
||||
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
|
||||
.containsExactlyElementsIn(expectedTypes);
|
||||
@@ -757,6 +1003,7 @@ public class AppSearchImplTest {
|
||||
"database1",
|
||||
schemas,
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ true);
|
||||
|
||||
// Create expected schemaType list, database 1 should only contain Email but database 2
|
||||
@@ -776,7 +1023,7 @@ public class AppSearchImplTest {
|
||||
|
||||
// Check nothing changed in database2.
|
||||
expectedTypes = new ArrayList<>();
|
||||
expectedTypes.add(mVisibilitySchemaProto);
|
||||
expectedTypes.addAll(existingSchemas);
|
||||
expectedTypes.addAll(expectedProto.getTypesList());
|
||||
assertThat(mAppSearchImpl.getSchemaProtoLocked().getTypesList())
|
||||
.containsExactlyElementsIn(expectedTypes);
|
||||
@@ -784,49 +1031,71 @@ public class AppSearchImplTest {
|
||||
|
||||
@Test
|
||||
public void testRemoveSchema_removedFromVisibilityStore() throws Exception {
|
||||
PackageIdentifier package1 =
|
||||
new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
|
||||
|
||||
String prefix = AppSearchImpl.createPrefix("package", "database");
|
||||
mAppSearchImpl.setSchema(
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("schema1"),
|
||||
/*schemasPackageAccessible=*/ ImmutableMap.of(
|
||||
"schema1", ImmutableList.of(package1)),
|
||||
/*forceOverride=*/ false);
|
||||
|
||||
// "schema1" is platform hidden now
|
||||
// "schema1" is platform hidden now and package accessible
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
|
||||
.isFalse();
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
|
||||
.isTrue();
|
||||
|
||||
// Remove "schema1" by force overriding
|
||||
mAppSearchImpl.setSchema(
|
||||
"package",
|
||||
"database",
|
||||
Collections.emptyList(),
|
||||
/*schemas=*/ Collections.emptyList(),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ true);
|
||||
|
||||
// Check that "schema1" is no longer considered platform hidden
|
||||
// Check that "schema1" is no longer considered platform hidden or package accessible
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
|
||||
.isTrue();
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
|
||||
.isFalse();
|
||||
|
||||
// Add "schema1" back, it gets default visibility settings which means it's not platform
|
||||
// hidden.
|
||||
// hidden and not package accessible
|
||||
mAppSearchImpl.setSchema(
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("schema1").build()),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPlatformSurfaceable(prefix, prefix + "schema1"))
|
||||
.isTrue();
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPackageAccessible(prefix, prefix + "schema1", package1))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -837,6 +1106,7 @@ public class AppSearchImplTest {
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
@@ -853,6 +1123,7 @@ public class AppSearchImplTest {
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.singletonList("Schema"),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
@@ -861,6 +1132,46 @@ public class AppSearchImplTest {
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetSchema_defaultNotPackageAccessible() throws Exception {
|
||||
PackageIdentifier package1 =
|
||||
new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
|
||||
|
||||
String prefix = AppSearchImpl.createPrefix("package", "database");
|
||||
mAppSearchImpl.setSchema(
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPackageAccessible(prefix, prefix + "Schema", package1))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetSchema_packageAccessible() throws Exception {
|
||||
PackageIdentifier package1 =
|
||||
new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
|
||||
|
||||
String prefix = AppSearchImpl.createPrefix("package", "database");
|
||||
mAppSearchImpl.setSchema(
|
||||
"package",
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ ImmutableMap.of("Schema", ImmutableList.of(package1)),
|
||||
/*forceOverride=*/ false);
|
||||
assertThat(
|
||||
mAppSearchImpl
|
||||
.getVisibilityStoreLocked()
|
||||
.isSchemaPackageAccessible(prefix, prefix + "Schema", package1))
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testHasSchemaType() throws Exception {
|
||||
// Nothing exists yet
|
||||
@@ -871,6 +1182,7 @@ public class AppSearchImplTest {
|
||||
"database",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("Schema").build()),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
assertThat(mAppSearchImpl.hasSchemaTypeLocked("package", "database", "Schema")).isTrue();
|
||||
|
||||
@@ -892,6 +1204,7 @@ public class AppSearchImplTest {
|
||||
"database1",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
assertThat(mAppSearchImpl.getPrefixesLocked())
|
||||
.containsExactly(
|
||||
@@ -905,6 +1218,7 @@ public class AppSearchImplTest {
|
||||
"database2",
|
||||
Collections.singletonList(new AppSearchSchema.Builder("schema").build()),
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptyList(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap(),
|
||||
/*forceOverride=*/ false);
|
||||
assertThat(mAppSearchImpl.getPrefixesLocked())
|
||||
.containsExactly(
|
||||
|
||||
@@ -18,6 +18,10 @@ package com.android.server.appsearch.external.localstorage;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.appsearch.PackageIdentifier;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableMap;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -70,11 +74,12 @@ public class VisibilityStoreTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVisibility() throws Exception {
|
||||
public void testSetVisibility_platformSurfaceable() throws Exception {
|
||||
mVisibilityStore.setVisibility(
|
||||
"prefix",
|
||||
/*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
|
||||
"prefix/schema1", "prefix/schema2"));
|
||||
"prefix/schema1", "prefix/schema2"),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap());
|
||||
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1"))
|
||||
.isFalse();
|
||||
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2"))
|
||||
@@ -85,7 +90,8 @@ public class VisibilityStoreTest {
|
||||
mVisibilityStore.setVisibility(
|
||||
"prefix",
|
||||
/*schemasNotPlatformSurfaceable=*/ ImmutableSet.of(
|
||||
"prefix/schema1", "prefix/schema3"));
|
||||
"prefix/schema1", "prefix/schema3"),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap());
|
||||
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1"))
|
||||
.isFalse();
|
||||
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2"))
|
||||
@@ -94,7 +100,9 @@ public class VisibilityStoreTest {
|
||||
.isFalse();
|
||||
|
||||
mVisibilityStore.setVisibility(
|
||||
"prefix", /*schemasNotPlatformSurfaceable=*/ Collections.emptySet());
|
||||
"prefix",
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap());
|
||||
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema1"))
|
||||
.isTrue();
|
||||
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable("prefix", "prefix/schema2"))
|
||||
@@ -103,14 +111,73 @@ public class VisibilityStoreTest {
|
||||
.isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetVisibility_packageAccessible() throws Exception {
|
||||
PackageIdentifier package1 =
|
||||
new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
|
||||
PackageIdentifier package2 =
|
||||
new PackageIdentifier("package2", /*sha256Certificate=*/ new byte[] {100});
|
||||
PackageIdentifier package3 =
|
||||
new PackageIdentifier("package3", /*sha256Certificate=*/ new byte[] {100});
|
||||
|
||||
mVisibilityStore.setVisibility(
|
||||
"prefix",
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
|
||||
/*schemasPackageAccessible=*/ ImmutableMap.of(
|
||||
"prefix/schema1", ImmutableList.of(package1),
|
||||
"prefix/schema2", ImmutableList.of(package2)));
|
||||
assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema1", package1))
|
||||
.isTrue();
|
||||
assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema2", package2))
|
||||
.isTrue();
|
||||
|
||||
// New .setVisibility() call completely overrides previous visibility settings. So
|
||||
// "schema2" isn't preserved.
|
||||
mVisibilityStore.setVisibility(
|
||||
"prefix",
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
|
||||
/*schemasPackageAccessible=*/ ImmutableMap.of(
|
||||
"prefix/schema1", ImmutableList.of(package1),
|
||||
"prefix/schema3", ImmutableList.of(package3)));
|
||||
assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema1", package1))
|
||||
.isTrue();
|
||||
assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema2", package2))
|
||||
.isFalse();
|
||||
assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema3", package3))
|
||||
.isTrue();
|
||||
|
||||
mVisibilityStore.setVisibility(
|
||||
"prefix",
|
||||
/*schemasNotPlatformSurfaceable=*/ Collections.emptySet(),
|
||||
/*schemasPackageAccessible=*/ Collections.emptyMap());
|
||||
assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema1", package1))
|
||||
.isFalse();
|
||||
assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema2", package2))
|
||||
.isFalse();
|
||||
assertThat(mVisibilityStore.isSchemaPackageAccessible("prefix", "prefix/schema3", package3))
|
||||
.isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyPrefix() throws Exception {
|
||||
PackageIdentifier package1 =
|
||||
new PackageIdentifier("package1", /*sha256Certificate=*/ new byte[] {100});
|
||||
PackageIdentifier package2 =
|
||||
new PackageIdentifier("package2", /*sha256Certificate=*/ new byte[] {100});
|
||||
|
||||
mVisibilityStore.setVisibility(
|
||||
/*prefix=*/ "",
|
||||
/*schemasNotPlatformSurfaceable=*/ ImmutableSet.of("schema1", "schema2"));
|
||||
/*schemasNotPlatformSurfaceable=*/ ImmutableSet.of("schema1", "schema2"),
|
||||
/*schemasPackageAccessible=*/ ImmutableMap.of(
|
||||
"schema1", ImmutableList.of(package1),
|
||||
"schema2", ImmutableList.of(package2)));
|
||||
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable(/*prefix=*/ "", "schema1"))
|
||||
.isFalse();
|
||||
assertThat(mVisibilityStore.isSchemaPlatformSurfaceable(/*prefix=*/ "", "schema2"))
|
||||
.isFalse();
|
||||
assertThat(mVisibilityStore.isSchemaPackageAccessible(/*prefix=*/ "", "schema1", package1))
|
||||
.isTrue();
|
||||
assertThat(mVisibilityStore.isSchemaPackageAccessible(/*prefix=*/ "", "schema2", package2))
|
||||
.isTrue();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user