Merge "Update framework from Jetpack"
This commit is contained in:
commit
fb182aebae
@ -233,7 +233,7 @@ public class AppSearchManager {
|
||||
mService.setSchema(
|
||||
DEFAULT_DATABASE_NAME,
|
||||
schemaBundles,
|
||||
new ArrayList<>(request.getSchemasNotPlatformSurfaceable()),
|
||||
new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
|
||||
request.isForceOverride(),
|
||||
mContext.getUserId(),
|
||||
new IAppSearchResultCallback.Stub() {
|
||||
|
@ -158,7 +158,7 @@ public final class AppSearchSession {
|
||||
mService.setSchema(
|
||||
mDatabaseName,
|
||||
schemaBundles,
|
||||
new ArrayList<>(request.getSchemasNotPlatformSurfaceable()),
|
||||
new ArrayList<>(request.getSchemasNotVisibleToSystemUi()),
|
||||
request.isForceOverride(),
|
||||
mUserId,
|
||||
new IAppSearchResultCallback.Stub() {
|
||||
|
@ -18,27 +18,38 @@ package android.app.appsearch;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* This class represents a uniquely identifiable package.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
public class PackageIdentifier {
|
||||
public final String packageName;
|
||||
public final byte[] certificate;
|
||||
private final String mPackageName;
|
||||
private final byte[] mSha256Certificate;
|
||||
|
||||
/**
|
||||
* Creates a unique identifier for a package.
|
||||
*
|
||||
* @param packageName Name of the package.
|
||||
* @param certificate SHA256 certificate digest of the package.
|
||||
* @param sha256Certificate SHA256 certificate digest of the package.
|
||||
*/
|
||||
public PackageIdentifier(@NonNull String packageName, @NonNull byte[] certificate) {
|
||||
this.packageName = packageName;
|
||||
this.certificate = certificate;
|
||||
public PackageIdentifier(@NonNull String packageName, @NonNull byte[] sha256Certificate) {
|
||||
mPackageName = Preconditions.checkNotNull(packageName);
|
||||
mSha256Certificate = Preconditions.checkNotNull(sha256Certificate);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public String getPackageName() {
|
||||
return mPackageName;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public byte[] getSha256Certificate() {
|
||||
return mSha256Certificate;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -50,12 +61,12 @@ public class PackageIdentifier {
|
||||
return false;
|
||||
}
|
||||
final PackageIdentifier other = (PackageIdentifier) obj;
|
||||
return this.packageName.equals(other.packageName)
|
||||
&& Arrays.equals(this.certificate, other.certificate);
|
||||
return this.mPackageName.equals(other.mPackageName)
|
||||
&& Arrays.equals(this.mSha256Certificate, other.mSha256Certificate);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return Objects.hash(packageName, Arrays.hashCode(certificate));
|
||||
return Objects.hash(mPackageName, Arrays.hashCode(mSha256Certificate));
|
||||
}
|
||||
}
|
||||
|
@ -18,17 +18,14 @@ package android.app.appsearch;
|
||||
|
||||
import android.annotation.NonNull;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.appsearch.exceptions.AppSearchException;
|
||||
import android.util.ArrayMap;
|
||||
import android.util.ArraySet;
|
||||
|
||||
import com.android.internal.util.Preconditions;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@ -39,18 +36,18 @@ import java.util.Set;
|
||||
*/
|
||||
public final class SetSchemaRequest {
|
||||
private final Set<AppSearchSchema> mSchemas;
|
||||
private final Set<String> mSchemasNotPlatformSurfaceable;
|
||||
private final Map<String, Set<PackageIdentifier>> mSchemasPackageAccessible;
|
||||
private final Set<String> mSchemasNotVisibleToSystemUi;
|
||||
private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages;
|
||||
private final boolean mForceOverride;
|
||||
|
||||
SetSchemaRequest(
|
||||
@NonNull Set<AppSearchSchema> schemas,
|
||||
@NonNull Set<String> schemasNotPlatformSurfaceable,
|
||||
@NonNull Map<String, Set<PackageIdentifier>> schemasPackageAccessible,
|
||||
@NonNull Set<String> schemasNotVisibleToSystemUi,
|
||||
@NonNull Map<String, Set<PackageIdentifier>> schemasVisibleToPackages,
|
||||
boolean forceOverride) {
|
||||
mSchemas = Preconditions.checkNotNull(schemas);
|
||||
mSchemasNotPlatformSurfaceable = Preconditions.checkNotNull(schemasNotPlatformSurfaceable);
|
||||
mSchemasPackageAccessible = Preconditions.checkNotNull(schemasPackageAccessible);
|
||||
mSchemasNotVisibleToSystemUi = Preconditions.checkNotNull(schemasNotVisibleToSystemUi);
|
||||
mSchemasVisibleToPackages = Preconditions.checkNotNull(schemasVisibleToPackages);
|
||||
mForceOverride = forceOverride;
|
||||
}
|
||||
|
||||
@ -62,12 +59,11 @@ public final class SetSchemaRequest {
|
||||
|
||||
/**
|
||||
* Returns the set of schema types that have opted out of being visible on system UI surfaces.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Set<String> getSchemasNotPlatformSurfaceable() {
|
||||
return Collections.unmodifiableSet(mSchemasNotPlatformSurfaceable);
|
||||
public Set<String> getSchemasNotVisibleToSystemUi() {
|
||||
return Collections.unmodifiableSet(mSchemasNotVisibleToSystemUi);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -76,14 +72,13 @@ public final class SetSchemaRequest {
|
||||
* certificate.
|
||||
*
|
||||
* <p>This method is inefficient to call repeatedly.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Map<String, Set<PackageIdentifier>> getSchemasPackageAccessible() {
|
||||
public Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackages() {
|
||||
Map<String, Set<PackageIdentifier>> copy = new ArrayMap<>();
|
||||
for (String key : mSchemasPackageAccessible.keySet()) {
|
||||
copy.put(key, new ArraySet<>(mSchemasPackageAccessible.get(key)));
|
||||
for (String key : mSchemasVisibleToPackages.keySet()) {
|
||||
copy.put(key, new ArraySet<>(mSchemasVisibleToPackages.get(key)));
|
||||
}
|
||||
return copy;
|
||||
}
|
||||
@ -93,14 +88,14 @@ public final class SetSchemaRequest {
|
||||
* type. Each package is represented by a {@link PackageIdentifier}. name and byte[]
|
||||
* certificate.
|
||||
*
|
||||
* <p>A more efficient version of {@code #getSchemasPackageAccessible}, but it returns a
|
||||
* <p>A more efficient version of {@link #getSchemasVisibleToPackages}, but it returns a
|
||||
* modifiable map. This is not meant to be unhidden and should only be used by internal classes.
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@NonNull
|
||||
public Map<String, Set<PackageIdentifier>> getSchemasPackageAccessibleInternal() {
|
||||
return mSchemasPackageAccessible;
|
||||
public Map<String, Set<PackageIdentifier>> getSchemasVisibleToPackagesInternal() {
|
||||
return mSchemasVisibleToPackages;
|
||||
}
|
||||
|
||||
/** Returns whether this request will force the schema to be overridden. */
|
||||
@ -111,8 +106,8 @@ public final class SetSchemaRequest {
|
||||
/** Builder for {@link SetSchemaRequest} objects. */
|
||||
public static final class Builder {
|
||||
private final Set<AppSearchSchema> mSchemas = new ArraySet<>();
|
||||
private final Set<String> mSchemasNotPlatformSurfaceable = new ArraySet<>();
|
||||
private final Map<String, Set<PackageIdentifier>> mSchemasPackageAccessible =
|
||||
private final Set<String> mSchemasNotVisibleToSystemUi = new ArraySet<>();
|
||||
private final Map<String, Set<PackageIdentifier>> mSchemasVisibleToPackages =
|
||||
new ArrayMap<>();
|
||||
private boolean mForceOverride = false;
|
||||
private boolean mBuilt = false;
|
||||
@ -148,6 +143,8 @@ public final class SetSchemaRequest {
|
||||
* @param visible Whether the {@code schemaType} will be visible or not.
|
||||
* @hide
|
||||
*/
|
||||
// Merged list available from getSchemasNotVisibleToSystemUi
|
||||
@SuppressLint("MissingGetterMatchingBuilder")
|
||||
@NonNull
|
||||
public Builder setSchemaTypeVisibilityForSystemUi(
|
||||
@NonNull String schemaType, boolean visible) {
|
||||
@ -155,9 +152,9 @@ public final class SetSchemaRequest {
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
|
||||
if (visible) {
|
||||
mSchemasNotPlatformSurfaceable.remove(schemaType);
|
||||
mSchemasNotVisibleToSystemUi.remove(schemaType);
|
||||
} else {
|
||||
mSchemasNotPlatformSurfaceable.add(schemaType);
|
||||
mSchemasNotVisibleToSystemUi.add(schemaType);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
@ -170,6 +167,8 @@ public final class SetSchemaRequest {
|
||||
* @param packageIdentifier Represents the package that will be granted visibility.
|
||||
* @hide
|
||||
*/
|
||||
// Merged list available from getSchemasVisibleToPackages
|
||||
@SuppressLint("MissingGetterMatchingBuilder")
|
||||
@NonNull
|
||||
public Builder setSchemaTypeVisibilityForPackage(
|
||||
@NonNull String schemaType,
|
||||
@ -179,13 +178,13 @@ public final class SetSchemaRequest {
|
||||
Preconditions.checkNotNull(packageIdentifier);
|
||||
Preconditions.checkState(!mBuilt, "Builder has already been used");
|
||||
|
||||
Set<PackageIdentifier> packageIdentifiers = mSchemasPackageAccessible.get(schemaType);
|
||||
Set<PackageIdentifier> packageIdentifiers = mSchemasVisibleToPackages.get(schemaType);
|
||||
if (visible) {
|
||||
if (packageIdentifiers == null) {
|
||||
packageIdentifiers = new ArraySet<>();
|
||||
}
|
||||
packageIdentifiers.add(packageIdentifier);
|
||||
mSchemasPackageAccessible.put(schemaType, packageIdentifiers);
|
||||
mSchemasVisibleToPackages.put(schemaType, packageIdentifiers);
|
||||
} else {
|
||||
if (packageIdentifiers == null) {
|
||||
// Return early since there was nothing set to begin with.
|
||||
@ -194,7 +193,7 @@ public final class SetSchemaRequest {
|
||||
packageIdentifiers.remove(packageIdentifier);
|
||||
if (packageIdentifiers.isEmpty()) {
|
||||
// Remove the entire key so that we don't have empty sets as values.
|
||||
mSchemasPackageAccessible.remove(schemaType);
|
||||
mSchemasVisibleToPackages.remove(schemaType);
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,8 +228,8 @@ public final class SetSchemaRequest {
|
||||
|
||||
// Verify that any schema types with visibility settings refer to a real schema.
|
||||
// Create a copy because we're going to remove from the set for verification purposes.
|
||||
Set<String> referencedSchemas = new ArraySet<>(mSchemasNotPlatformSurfaceable);
|
||||
referencedSchemas.addAll(mSchemasPackageAccessible.keySet());
|
||||
Set<String> referencedSchemas = new ArraySet<>(mSchemasNotVisibleToSystemUi);
|
||||
referencedSchemas.addAll(mSchemasVisibleToPackages.keySet());
|
||||
|
||||
for (AppSearchSchema schema : mSchemas) {
|
||||
referencedSchemas.remove(schema.getSchemaType());
|
||||
@ -244,8 +243,8 @@ public final class SetSchemaRequest {
|
||||
|
||||
return new SetSchemaRequest(
|
||||
mSchemas,
|
||||
mSchemasNotPlatformSurfaceable,
|
||||
mSchemasPackageAccessible,
|
||||
mSchemasNotVisibleToSystemUi,
|
||||
mSchemasVisibleToPackages,
|
||||
mForceOverride);
|
||||
}
|
||||
}
|
||||
|
@ -156,7 +156,6 @@ public final class AppSearchImpl {
|
||||
}
|
||||
|
||||
private AppSearchImpl(@NonNull File icingDir) throws AppSearchException {
|
||||
boolean isReset = false;
|
||||
mReadWriteLock.writeLock().lock();
|
||||
|
||||
try {
|
||||
@ -168,9 +167,11 @@ public final class AppSearchImpl {
|
||||
.build();
|
||||
mIcingSearchEngineLocked = new IcingSearchEngine(options);
|
||||
|
||||
mVisibilityStoreLocked = new VisibilityStore(this);
|
||||
|
||||
InitializeResultProto initializeResultProto = mIcingSearchEngineLocked.initialize();
|
||||
SchemaProto schemaProto = null;
|
||||
GetAllNamespacesResultProto getAllNamespacesResultProto = null;
|
||||
SchemaProto schemaProto;
|
||||
GetAllNamespacesResultProto getAllNamespacesResultProto;
|
||||
try {
|
||||
checkSuccess(initializeResultProto.getStatus());
|
||||
schemaProto = getSchemaProtoLocked();
|
||||
@ -180,7 +181,7 @@ public final class AppSearchImpl {
|
||||
Log.w(TAG, "Error initializing, resetting IcingSearchEngine.", e);
|
||||
// Some error. Reset and see if it fixes it.
|
||||
reset();
|
||||
isReset = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Populate schema map
|
||||
@ -196,11 +197,8 @@ public final class AppSearchImpl {
|
||||
|
||||
// TODO(b/155939114): It's possible to optimize after init, which would reduce the time
|
||||
// to when we're able to serve queries. Consider moving this optimize call out.
|
||||
if (!isReset) {
|
||||
checkForOptimizeLocked(/* force= */ true);
|
||||
}
|
||||
checkForOptimizeLocked(/* force= */ true);
|
||||
|
||||
mVisibilityStoreLocked = new VisibilityStore(this);
|
||||
} finally {
|
||||
mReadWriteLock.writeLock().unlock();
|
||||
}
|
||||
@ -635,6 +633,9 @@ public final class AppSearchImpl {
|
||||
/**
|
||||
* Clears documents and schema across all packages and databaseNames.
|
||||
*
|
||||
* <p>This method also clear all data in {@link VisibilityStore}, an {@link
|
||||
* #initializeVisibilityStore()} must be called after this.
|
||||
*
|
||||
* <p>This method belongs to mutate group.
|
||||
*
|
||||
* @throws AppSearchException on IcingSearchEngine error.
|
||||
|
@ -213,14 +213,12 @@ class VisibilityStore {
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles an {@link AppSearchImpl#reset()} by clearing any cached state and resetting to a
|
||||
* first-initialized state.
|
||||
* Handles an {@code AppSearchImpl#reset()} by clearing any cached state.
|
||||
*
|
||||
* @throws AppSearchException on AppSearchImpl error.
|
||||
* <p>{@link #initialize()} must be called after this.
|
||||
*/
|
||||
public void handleReset() throws AppSearchException {
|
||||
void handleReset() {
|
||||
mNotPlatformSurfaceableMap.clear();
|
||||
initialize();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1 +1 @@
|
||||
Ia04e81bb574831fa7e8a26c725e53133b39ee3ef
|
||||
I0577839bfddf95a555399df441d317b00c7c7c48
|
||||
|
@ -20,15 +20,19 @@ import android.annotation.NonNull;
|
||||
import android.app.appsearch.AppSearchBatchResult;
|
||||
import android.app.appsearch.AppSearchManager;
|
||||
import android.app.appsearch.AppSearchResult;
|
||||
import android.app.appsearch.AppSearchSchema;
|
||||
import android.app.appsearch.AppSearchSession;
|
||||
import android.app.appsearch.AppSearchSessionShim;
|
||||
import android.app.appsearch.BatchResultCallback;
|
||||
import android.app.appsearch.GenericDocument;
|
||||
import android.app.appsearch.GetByUriRequest;
|
||||
import android.app.appsearch.PutDocumentsRequest;
|
||||
import android.app.appsearch.RemoveByUriRequest;
|
||||
import android.app.appsearch.SearchResults;
|
||||
import android.app.appsearch.SearchResultsShim;
|
||||
import android.app.appsearch.SearchSpec;
|
||||
import android.app.appsearch.SetSchemaRequest;
|
||||
import android.app.appsearch.exceptions.AppSearchException;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.test.core.app.ApplicationProvider;
|
||||
@ -38,6 +42,7 @@ import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@ -46,42 +51,47 @@ import java.util.concurrent.Executors;
|
||||
* a consistent interface.
|
||||
* @hide
|
||||
*/
|
||||
public class AppSearchSessionShim {
|
||||
public class AppSearchSessionShimImpl implements AppSearchSessionShim {
|
||||
private final AppSearchSession mAppSearchSession;
|
||||
private final ExecutorService mExecutor;
|
||||
|
||||
@NonNull
|
||||
public static ListenableFuture<AppSearchResult<AppSearchSessionShim>> createSearchSession(
|
||||
public static ListenableFuture<AppSearchSessionShim> createSearchSession(
|
||||
@NonNull AppSearchManager.SearchContext searchContext) {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
|
||||
SettableFuture<AppSearchResult<AppSearchSession>> future = SettableFuture.create();
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
appSearchManager.createSearchSession(searchContext, executor, future::set);
|
||||
return Futures.transform(future, (instance) -> {
|
||||
if (!instance.isSuccess()) {
|
||||
return AppSearchResult.newFailedResult(
|
||||
instance.getResultCode(), instance.getErrorMessage());
|
||||
}
|
||||
AppSearchSession searchSession = instance.getResultValue();
|
||||
AppSearchSessionShim shim = new AppSearchSessionShim(searchSession, executor);
|
||||
return AppSearchResult.newSuccessfulResult(shim);
|
||||
}, executor);
|
||||
return Futures.transform(
|
||||
future,
|
||||
instance -> new AppSearchSessionShimImpl(instance.getResultValue(), executor),
|
||||
executor);
|
||||
}
|
||||
|
||||
private AppSearchSessionShim(
|
||||
private AppSearchSessionShimImpl(
|
||||
@NonNull AppSearchSession session, @NonNull ExecutorService executor) {
|
||||
mAppSearchSession = Preconditions.checkNotNull(session);
|
||||
mExecutor = Preconditions.checkNotNull(executor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public ListenableFuture<AppSearchResult<Void>> setSchema(@NonNull SetSchemaRequest request) {
|
||||
public ListenableFuture<Void> setSchema(@NonNull SetSchemaRequest request) {
|
||||
SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
|
||||
mAppSearchSession.setSchema(request, mExecutor, future::set);
|
||||
return future;
|
||||
return Futures.transformAsync(future, this::transformResult, mExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public ListenableFuture<Set<AppSearchSchema>> getSchema() {
|
||||
SettableFuture<AppSearchResult<Set<AppSearchSchema>>> future = SettableFuture.create();
|
||||
mAppSearchSession.getSchema(mExecutor, future::set);
|
||||
return Futures.transformAsync(future, this::transformResult, mExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public ListenableFuture<AppSearchBatchResult<String, Void>> putDocuments(
|
||||
@NonNull PutDocumentsRequest request) {
|
||||
@ -91,6 +101,7 @@ public class AppSearchSessionShim {
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByUri(
|
||||
@NonNull GetByUriRequest request) {
|
||||
@ -100,14 +111,16 @@ public class AppSearchSessionShim {
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public SearchResultsShim query(
|
||||
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
|
||||
SearchResults searchResults =
|
||||
mAppSearchSession.query(queryExpression, searchSpec, mExecutor);
|
||||
return new SearchResultsShim(searchResults);
|
||||
return new SearchResultsShimImpl(searchResults, mExecutor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public ListenableFuture<AppSearchBatchResult<String, Void>> removeByUri(
|
||||
@NonNull RemoveByUriRequest request) {
|
||||
@ -116,12 +129,21 @@ public class AppSearchSessionShim {
|
||||
return future;
|
||||
}
|
||||
|
||||
@Override
|
||||
@NonNull
|
||||
public ListenableFuture<AppSearchResult<Void>> removeByQuery(
|
||||
public ListenableFuture<Void> removeByQuery(
|
||||
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
|
||||
SettableFuture<AppSearchResult<Void>> future = SettableFuture.create();
|
||||
mAppSearchSession.removeByQuery(queryExpression, searchSpec, mExecutor, future::set);
|
||||
return future;
|
||||
return Futures.transformAsync(future, this::transformResult, mExecutor);
|
||||
}
|
||||
|
||||
private <T> ListenableFuture<T> transformResult(
|
||||
@NonNull AppSearchResult<T> result) throws AppSearchException {
|
||||
if (!result.isSuccess()) {
|
||||
throw new AppSearchException(result.getResultCode(), result.getErrorMessage());
|
||||
}
|
||||
return Futures.immediateFuture(result.getResultValue());
|
||||
}
|
||||
|
||||
private static final class BatchResultCallbackAdapter<K, V>
|
@ -21,6 +21,7 @@ import android.app.appsearch.AppSearchManager;
|
||||
import android.app.appsearch.AppSearchResult;
|
||||
import android.app.appsearch.GlobalSearchSession;
|
||||
import android.app.appsearch.SearchResults;
|
||||
import android.app.appsearch.SearchResultsShim;
|
||||
import android.app.appsearch.SearchSpec;
|
||||
import android.content.Context;
|
||||
|
||||
@ -39,30 +40,24 @@ import java.util.concurrent.Executors;
|
||||
* a consistent interface.
|
||||
* @hide
|
||||
*/
|
||||
public class GlobalSearchSessionShim {
|
||||
public class GlobalSearchSessionShimImpl {
|
||||
private final GlobalSearchSession mGlobalSearchSession;
|
||||
private final ExecutorService mExecutor;
|
||||
|
||||
@NonNull
|
||||
public static ListenableFuture<AppSearchResult<GlobalSearchSessionShim>>
|
||||
createGlobalSearchSession() {
|
||||
public static ListenableFuture<GlobalSearchSessionShimImpl> createGlobalSearchSession() {
|
||||
Context context = ApplicationProvider.getApplicationContext();
|
||||
AppSearchManager appSearchManager = context.getSystemService(AppSearchManager.class);
|
||||
SettableFuture<AppSearchResult<GlobalSearchSession>> future = SettableFuture.create();
|
||||
ExecutorService executor = Executors.newCachedThreadPool();
|
||||
appSearchManager.createGlobalSearchSession(executor, future::set);
|
||||
return Futures.transform(future, (instance) -> {
|
||||
if (!instance.isSuccess()) {
|
||||
return AppSearchResult.newFailedResult(
|
||||
instance.getResultCode(), instance.getErrorMessage());
|
||||
}
|
||||
GlobalSearchSession searchSession = instance.getResultValue();
|
||||
GlobalSearchSessionShim shim = new GlobalSearchSessionShim(searchSession, executor);
|
||||
return AppSearchResult.newSuccessfulResult(shim);
|
||||
}, executor);
|
||||
return Futures.transform(
|
||||
future,
|
||||
instance -> new GlobalSearchSessionShimImpl(instance.getResultValue(), executor),
|
||||
executor);
|
||||
}
|
||||
|
||||
private GlobalSearchSessionShim(
|
||||
private GlobalSearchSessionShimImpl(
|
||||
@NonNull GlobalSearchSession session, @NonNull ExecutorService executor) {
|
||||
mGlobalSearchSession = Preconditions.checkNotNull(session);
|
||||
mExecutor = Preconditions.checkNotNull(executor);
|
||||
@ -73,6 +68,6 @@ public class GlobalSearchSessionShim {
|
||||
@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
|
||||
SearchResults searchResults =
|
||||
mGlobalSearchSession.query(queryExpression, searchSpec, mExecutor);
|
||||
return new SearchResultsShim(searchResults);
|
||||
return new SearchResultsShimImpl(searchResults, mExecutor);
|
||||
}
|
||||
}
|
@ -20,31 +20,35 @@ import android.annotation.NonNull;
|
||||
import android.app.appsearch.AppSearchResult;
|
||||
import android.app.appsearch.SearchResult;
|
||||
import android.app.appsearch.SearchResults;
|
||||
import android.app.appsearch.SearchResultsShim;
|
||||
|
||||
import com.google.common.base.Preconditions;
|
||||
import com.google.common.util.concurrent.Futures;
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
import com.google.common.util.concurrent.SettableFuture;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
|
||||
/**
|
||||
* This test class adapts the AppSearch Framework API to ListenableFuture, so it can be tested via
|
||||
* a consistent interface.
|
||||
* @hide
|
||||
*/
|
||||
public class SearchResultsShim implements Closeable {
|
||||
public class SearchResultsShimImpl implements SearchResultsShim {
|
||||
private final Executor mExecutor;
|
||||
private final SearchResults mSearchResults;
|
||||
|
||||
SearchResultsShim(@NonNull SearchResults searchResults) {
|
||||
SearchResultsShimImpl(@NonNull SearchResults searchResults, @NonNull Executor executor) {
|
||||
mExecutor = Preconditions.checkNotNull(executor);
|
||||
mSearchResults = Preconditions.checkNotNull(searchResults);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public ListenableFuture<AppSearchResult<List<SearchResult>>> getNextPage() {
|
||||
public ListenableFuture<List<SearchResult>> getNextPage() {
|
||||
SettableFuture<AppSearchResult<List<SearchResult>>> future = SettableFuture.create();
|
||||
mSearchResults.getNextPage(future::set);
|
||||
return future;
|
||||
return Futures.transform(future, AppSearchResult::getResultValue, mExecutor);
|
||||
}
|
||||
|
||||
@Override
|
211
apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
vendored
Normal file
211
apex/appsearch/testing/java/com/android/server/appsearch/testing/external/AppSearchSessionShim.java
vendored
Normal file
@ -0,0 +1,211 @@
|
||||
/*
|
||||
* Copyright (C) 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 android.annotation.NonNull;
|
||||
import android.annotation.SuppressLint;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents a connection to an AppSearch storage system where {@link GenericDocument}s can be
|
||||
* placed and queried.
|
||||
*
|
||||
* All implementations of this interface must be thread safe.
|
||||
*/
|
||||
public interface AppSearchSessionShim {
|
||||
|
||||
/**
|
||||
* Sets the schema that will be used by documents provided to the {@link #putDocuments} method.
|
||||
*
|
||||
* <p>The schema provided here is compared to the stored copy of the schema previously supplied
|
||||
* to {@link #setSchema}, if any, to determine how to treat existing documents. The following
|
||||
* types of schema modifications are always safe and are made without deleting any existing
|
||||
* documents:
|
||||
* <ul>
|
||||
* <li>Addition of new types
|
||||
* <li>Addition of new
|
||||
* {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} or
|
||||
* {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} properties to a
|
||||
* type
|
||||
* <li>Changing the cardinality of a data type to be less restrictive (e.g. changing an
|
||||
* {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a
|
||||
* {@link AppSearchSchema.PropertyConfig#CARDINALITY_REPEATED REPEATED} property.
|
||||
* </ul>
|
||||
*
|
||||
* <p>The following types of schema changes are not backwards-compatible:
|
||||
* <ul>
|
||||
* <li>Removal of an existing type
|
||||
* <li>Removal of a property from a type
|
||||
* <li>Changing the data type ({@code boolean}, {@code long}, etc.) of an existing property
|
||||
* <li>For properties of {@code Document} type, changing the schema type of
|
||||
* {@code Document}s of that property
|
||||
* <li>Changing the cardinality of a data type to be more restrictive (e.g. changing an
|
||||
* {@link AppSearchSchema.PropertyConfig#CARDINALITY_OPTIONAL OPTIONAL} property into a
|
||||
* {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property).
|
||||
* <li>Adding a
|
||||
* {@link AppSearchSchema.PropertyConfig#CARDINALITY_REQUIRED REQUIRED} property.
|
||||
* </ul>
|
||||
* <p>Supplying a schema with such changes will, by default, result in this call completing its
|
||||
* future with an {@link androidx.appsearch.exceptions.AppSearchException} with a code of
|
||||
* {@link AppSearchResult#RESULT_INVALID_SCHEMA} and a message describing the incompatibility.
|
||||
* In this case the previously set schema will remain active.
|
||||
*
|
||||
* <p>If you need to make non-backwards-compatible changes as described above, you can set the
|
||||
* {@link SetSchemaRequest.Builder#setForceOverride} method to {@code true}. In this case,
|
||||
* instead of completing its future with an
|
||||
* {@link androidx.appsearch.exceptions.AppSearchException} with the
|
||||
* {@link AppSearchResult#RESULT_INVALID_SCHEMA} error code, all documents which are not
|
||||
* compatible with the new schema will be deleted and the incompatible schema will be applied.
|
||||
*
|
||||
* <p>It is a no-op to set the same schema as has been previously set; this is handled
|
||||
* efficiently.
|
||||
*
|
||||
* <p>By default, documents are visible on platform surfaces. To opt out, call {@code
|
||||
* SetSchemaRequest.Builder#setPlatformSurfaceable} with {@code surfaceable} as false. Any
|
||||
* visibility settings apply only to the schemas that are included in the {@code request}.
|
||||
* Visibility settings for a schema type do not apply or persist across
|
||||
* {@link SetSchemaRequest}s.
|
||||
*
|
||||
* @param request The schema update request.
|
||||
* @return The pending result of performing this operation.
|
||||
*/
|
||||
// TODO(b/169883602): Change @code references to @link when setPlatformSurfaceable APIs are
|
||||
// exposed.
|
||||
@NonNull
|
||||
ListenableFuture<Void> setSchema(@NonNull SetSchemaRequest request);
|
||||
|
||||
/**
|
||||
* Retrieves the schema most recently successfully provided to {@link #setSchema}.
|
||||
*
|
||||
* @return The pending result of performing this operation.
|
||||
*/
|
||||
// This call hits disk; async API prevents us from treating these calls as properties.
|
||||
@SuppressLint("KotlinPropertyAccess")
|
||||
@NonNull
|
||||
ListenableFuture<Set<AppSearchSchema>> getSchema();
|
||||
|
||||
/**
|
||||
* Indexes documents into AppSearch.
|
||||
*
|
||||
* <p>Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a
|
||||
* schema type previously registered via the {@link #setSchema} method.
|
||||
*
|
||||
* @param request {@link PutDocumentsRequest} containing documents to be indexed
|
||||
* @return The pending result of performing this operation. The keys of the returned
|
||||
* {@link AppSearchBatchResult} are the URIs of the input documents. The values are
|
||||
* {@code null} if they were successfully indexed, or a failed {@link AppSearchResult}
|
||||
* otherwise.
|
||||
*/
|
||||
@NonNull
|
||||
ListenableFuture<AppSearchBatchResult<String, Void>> putDocuments(
|
||||
@NonNull PutDocumentsRequest request);
|
||||
|
||||
/**
|
||||
* Retrieves {@link GenericDocument}s by URI.
|
||||
*
|
||||
* @param request {@link GetByUriRequest} containing URIs to be retrieved.
|
||||
* @return The pending result of performing this operation. The keys of the returned
|
||||
* {@link AppSearchBatchResult} are the input URIs. The values are the returned
|
||||
* {@link GenericDocument}s on success, or a failed {@link AppSearchResult} otherwise.
|
||||
* URIs that are not found will return a failed {@link AppSearchResult} with a result code
|
||||
* of {@link AppSearchResult#RESULT_NOT_FOUND}.
|
||||
*/
|
||||
@NonNull
|
||||
ListenableFuture<AppSearchBatchResult<String, GenericDocument>> getByUri(
|
||||
@NonNull GetByUriRequest request);
|
||||
|
||||
/**
|
||||
* Searches a document based on a given query string.
|
||||
*
|
||||
* <p>Currently we support following features in the raw query format:
|
||||
* <ul>
|
||||
* <li>AND
|
||||
* <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and
|
||||
* ‘cat’”).
|
||||
* Example: hello world matches documents that have both ‘hello’ and ‘world’
|
||||
* <li>OR
|
||||
* <p>OR joins (e.g. “match documents that have either the term ‘dog’ or
|
||||
* ‘cat’”).
|
||||
* Example: dog OR puppy
|
||||
* <li>Exclusion
|
||||
* <p>Exclude a term (e.g. “match documents that do
|
||||
* not have the term ‘dog’”).
|
||||
* Example: -dog excludes the term ‘dog’
|
||||
* <li>Grouping terms
|
||||
* <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g.
|
||||
* “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”).
|
||||
* Example: (dog puppy) (cat kitten) two one group containing two terms.
|
||||
* <li>Property restricts
|
||||
* <p> Specifies which properties of a document to specifically match terms in (e.g.
|
||||
* “match documents where the ‘subject’ property contains ‘important’”).
|
||||
* Example: subject:important matches documents with the term ‘important’ in the
|
||||
* ‘subject’ property
|
||||
* <li>Schema type restricts
|
||||
* <p>This is similar to property restricts, but allows for restricts on top-level document
|
||||
* fields, such as schema_type. Clients should be able to limit their query to documents of
|
||||
* a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”).
|
||||
* Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents
|
||||
* that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the
|
||||
* ‘Video’ schema type.
|
||||
* </ul>
|
||||
*
|
||||
* <p> This method is lightweight. The heavy work will be done in
|
||||
* {@link SearchResults#getNextPage()}.
|
||||
*
|
||||
* @param queryExpression Query String to search.
|
||||
* @param searchSpec Spec for setting filters, raw query etc.
|
||||
* @return The search result of performing this operation.
|
||||
*/
|
||||
@NonNull
|
||||
SearchResultsShim query(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
|
||||
|
||||
/**
|
||||
* Removes {@link GenericDocument}s from the index by URI.
|
||||
*
|
||||
* @param request Request containing URIs to be removed.
|
||||
* @return The pending result of performing this operation. The keys of the returned
|
||||
* {@link AppSearchBatchResult} are the input URIs. The values are {@code null} on success,
|
||||
* or a failed {@link AppSearchResult} otherwise. URIs that are not found will return a
|
||||
* failed {@link AppSearchResult} with a result code of
|
||||
* {@link AppSearchResult#RESULT_NOT_FOUND}.
|
||||
*/
|
||||
@NonNull
|
||||
ListenableFuture<AppSearchBatchResult<String, Void>> removeByUri(
|
||||
@NonNull RemoveByUriRequest request);
|
||||
|
||||
/**
|
||||
* Removes {@link GenericDocument}s from the index by Query. Documents will be removed if they
|
||||
* match the {@code queryExpression} in given namespaces and schemaTypes which is set via
|
||||
* {@link SearchSpec.Builder#addNamespace} and {@link SearchSpec.Builder#addSchemaType}.
|
||||
*
|
||||
* <p> An empty {@code queryExpression} matches all documents.
|
||||
*
|
||||
* <p> An empty set of namespaces or schemaTypes matches all namespaces or schemaTypes in
|
||||
* the current database.
|
||||
*
|
||||
* @param queryExpression Query String to search.
|
||||
* @param searchSpec Spec containing schemaTypes, namespaces and query expression
|
||||
* indicates how document will be removed. All specific about how to
|
||||
* scoring, ordering, snippeting and resulting will be ignored.
|
||||
* @return The pending result of performing this operation.
|
||||
*/
|
||||
@NonNull
|
||||
ListenableFuture<Void> removeByQuery(
|
||||
@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
|
||||
}
|
@ -19,15 +19,11 @@ package com.android.server.appsearch.testing;
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import android.app.appsearch.AppSearchBatchResult;
|
||||
import android.app.appsearch.AppSearchManager;
|
||||
import android.app.appsearch.AppSearchResult;
|
||||
import android.app.appsearch.AppSearchSessionShim;
|
||||
import android.app.appsearch.GenericDocument;
|
||||
import android.app.appsearch.GetByUriRequest;
|
||||
import android.app.appsearch.SearchResult;
|
||||
import android.app.appsearch.SetSchemaRequest;
|
||||
import android.content.Context;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import android.app.appsearch.SearchResultsShim;
|
||||
|
||||
import junit.framework.AssertionFailedError;
|
||||
|
||||
@ -37,32 +33,6 @@ import java.util.concurrent.Future;
|
||||
|
||||
public class AppSearchTestUtils {
|
||||
|
||||
// List of databases that may be used in tests. Keeping them in a centralized location helps
|
||||
// #cleanup know which databases to clear.
|
||||
public static final String DEFAULT_DATABASE = AppSearchManager.DEFAULT_DATABASE_NAME;
|
||||
public static final String DB_1 = "testDb1";
|
||||
public static final String DB_2 = "testDb2";
|
||||
|
||||
public static void cleanup(Context context) throws Exception {
|
||||
List<String> databases = ImmutableList.of(DEFAULT_DATABASE, DB_1, DB_2);
|
||||
for (String database : databases) {
|
||||
AppSearchSessionShim session = checkIsResultSuccess(
|
||||
AppSearchSessionShim.createSearchSession(
|
||||
new AppSearchManager.SearchContext.Builder()
|
||||
.setDatabaseName(database).build()));
|
||||
checkIsResultSuccess(session.setSchema(
|
||||
new SetSchemaRequest.Builder().setForceOverride(true).build()));
|
||||
}
|
||||
}
|
||||
|
||||
public static <V> V checkIsResultSuccess(Future<AppSearchResult<V>> future) throws Exception {
|
||||
AppSearchResult<V> result = future.get();
|
||||
if (!result.isSuccess()) {
|
||||
throw new AssertionFailedError("AppSearchResult not successful: " + result);
|
||||
}
|
||||
return result.getResultValue();
|
||||
}
|
||||
|
||||
public static <K, V> AppSearchBatchResult<K, V> checkIsBatchResultSuccess(
|
||||
Future<AppSearchBatchResult<K, V>> future) throws Exception {
|
||||
AppSearchBatchResult<K, V> result = future.get();
|
||||
@ -74,10 +44,13 @@ public class AppSearchTestUtils {
|
||||
|
||||
public static List<GenericDocument> doGet(
|
||||
AppSearchSessionShim session, String namespace, String... uris) throws Exception {
|
||||
AppSearchBatchResult<String, GenericDocument> result = checkIsBatchResultSuccess(
|
||||
session.getByUri(
|
||||
new GetByUriRequest.Builder()
|
||||
.setNamespace(namespace).addUri(uris).build()));
|
||||
AppSearchBatchResult<String, GenericDocument> result =
|
||||
checkIsBatchResultSuccess(
|
||||
session.getByUri(
|
||||
new GetByUriRequest.Builder()
|
||||
.setNamespace(namespace)
|
||||
.addUri(uris)
|
||||
.build()));
|
||||
assertThat(result.getSuccesses()).hasSize(uris.length);
|
||||
assertThat(result.getFailures()).isEmpty();
|
||||
List<GenericDocument> list = new ArrayList<>(uris.length);
|
||||
@ -89,13 +62,13 @@ public class AppSearchTestUtils {
|
||||
|
||||
public static List<GenericDocument> convertSearchResultsToDocuments(
|
||||
SearchResultsShim searchResults) throws Exception {
|
||||
List<SearchResult> results = checkIsResultSuccess(searchResults.getNextPage());
|
||||
List<SearchResult> results = searchResults.getNextPage().get();
|
||||
List<GenericDocument> documents = new ArrayList<>();
|
||||
while (results.size() > 0) {
|
||||
for (SearchResult result : results) {
|
||||
documents.add(result.getDocument());
|
||||
}
|
||||
results = checkIsResultSuccess(searchResults.getNextPage());
|
||||
results = searchResults.getNextPage().get();
|
||||
}
|
||||
return documents;
|
||||
}
|
||||
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (C) 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 android.annotation.NonNull;
|
||||
|
||||
/**
|
||||
* This class provides global access to the centralized AppSearch index maintained by the system.
|
||||
*
|
||||
* <p>Apps can retrieve indexed documents through the query API.
|
||||
*/
|
||||
public interface GlobalSearchSessionShim {
|
||||
/**
|
||||
* Searches across all documents in the storage based on a given query string.
|
||||
*
|
||||
* <p>Currently we support following features in the raw query format:
|
||||
* <ul>
|
||||
* <li>AND
|
||||
* <p>AND joins (e.g. “match documents that have both the terms ‘dog’ and
|
||||
* ‘cat’”).
|
||||
* Example: hello world matches documents that have both ‘hello’ and ‘world’
|
||||
* <li>OR
|
||||
* <p>OR joins (e.g. “match documents that have either the term ‘dog’ or
|
||||
* ‘cat’”).
|
||||
* Example: dog OR puppy
|
||||
* <li>Exclusion
|
||||
* <p>Exclude a term (e.g. “match documents that do
|
||||
* not have the term ‘dog’”).
|
||||
* Example: -dog excludes the term ‘dog’
|
||||
* <li>Grouping terms
|
||||
* <p>Allow for conceptual grouping of subqueries to enable hierarchical structures (e.g.
|
||||
* “match documents that have either ‘dog’ or ‘puppy’, and either ‘cat’ or ‘kitten’”).
|
||||
* Example: (dog puppy) (cat kitten) two one group containing two terms.
|
||||
* <li>Property restricts
|
||||
* <p> Specifies which properties of a document to specifically match terms in (e.g.
|
||||
* “match documents where the ‘subject’ property contains ‘important’”).
|
||||
* Example: subject:important matches documents with the term ‘important’ in the
|
||||
* ‘subject’ property
|
||||
* <li>Schema type restricts
|
||||
* <p>This is similar to property restricts, but allows for restricts on top-level document
|
||||
* fields, such as schema_type. Clients should be able to limit their query to documents of
|
||||
* a certain schema_type (e.g. “match documents that are of the ‘Email’ schema_type”).
|
||||
* Example: { schema_type_filters: “Email”, “Video”,query: “dog” } will match documents
|
||||
* that contain the query term ‘dog’ and are of either the ‘Email’ schema type or the
|
||||
* ‘Video’ schema type.
|
||||
* </ul>
|
||||
*
|
||||
* <p> This method is lightweight. The heavy work will be done in
|
||||
* {@link SearchResults#getNextPage}.
|
||||
*
|
||||
* @param queryExpression Query String to search.
|
||||
* @param searchSpec Spec for setting filters, raw query etc.
|
||||
* @return The search result of performing this operation.
|
||||
*/
|
||||
@NonNull
|
||||
SearchResultsShim query(@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
|
||||
}
|
52
apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java
vendored
Normal file
52
apex/appsearch/testing/java/com/android/server/appsearch/testing/external/SearchResultsShim.java
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
/*
|
||||
* Copyright (C) 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 android.annotation.NonNull;
|
||||
|
||||
import com.google.common.util.concurrent.ListenableFuture;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* SearchResults are a returned object from a query API.
|
||||
*
|
||||
* <p>Each {@link SearchResult} contains a document and may contain other fields like snippets
|
||||
* based on request.
|
||||
*
|
||||
* <p>Should close this object after finish fetching results.
|
||||
*
|
||||
* <p>This class is not thread safe.
|
||||
*/
|
||||
public interface SearchResultsShim extends Closeable {
|
||||
/**
|
||||
* Gets a whole page of {@link SearchResult}s.
|
||||
*
|
||||
* <p>Re-call this method to get next page of {@link SearchResult}, until it returns an
|
||||
* empty list.
|
||||
*
|
||||
* <p>The page size is set by
|
||||
* {@link android.app.appsearch.SearchSpec.Builder#setResultCountPerPage}.
|
||||
*
|
||||
* @return The pending result of performing this operation.
|
||||
*/
|
||||
@NonNull
|
||||
ListenableFuture<List<SearchResult>> getNextPage();
|
||||
|
||||
@Override
|
||||
void close();
|
||||
}
|
@ -16,9 +16,6 @@
|
||||
|
||||
package android.app.appsearch;
|
||||
|
||||
import static android.app.appsearch.AppSearchSchema.PropertyConfig.INDEXING_TYPE_PREFIXES;
|
||||
import static android.app.appsearch.AppSearchSchema.PropertyConfig.TOKENIZER_TYPE_PLAIN;
|
||||
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.testng.Assert.expectThrows;
|
||||
@ -27,12 +24,22 @@ import android.util.ArrayMap;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class SetSchemaRequestTest {
|
||||
|
||||
private static Collection<String> getSchemaTypesFromSetSchemaRequest(SetSchemaRequest request) {
|
||||
HashSet<String> schemaTypes = new HashSet<>();
|
||||
for (AppSearchSchema schema : request.getSchemas()) {
|
||||
schemaTypes.add(schema.getSchemaType());
|
||||
}
|
||||
return schemaTypes;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInvalidSchemaReferences_fromSystemUiVisibility() {
|
||||
IllegalArgumentException expected =
|
||||
@ -57,7 +64,7 @@ public class SetSchemaRequestTest {
|
||||
/*visible=*/ true,
|
||||
new PackageIdentifier(
|
||||
"com.foo.package",
|
||||
/*certificate=*/ new byte[] {}))
|
||||
/*sha256Certificate=*/ new byte[] {}))
|
||||
.build());
|
||||
assertThat(expected).hasMessageThat().contains("referenced, but were not added");
|
||||
}
|
||||
@ -68,14 +75,14 @@ public class SetSchemaRequestTest {
|
||||
|
||||
// By default, the schema is visible.
|
||||
SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(schema).build();
|
||||
assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty();
|
||||
assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
|
||||
|
||||
request =
|
||||
new SetSchemaRequest.Builder()
|
||||
.addSchema(schema)
|
||||
.setSchemaTypeVisibilityForSystemUi("Schema", true)
|
||||
.build();
|
||||
assertThat(request.getSchemasNotPlatformSurfaceable()).isEmpty();
|
||||
assertThat(request.getSchemasNotVisibleToSystemUi()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -86,7 +93,7 @@ public class SetSchemaRequestTest {
|
||||
.addSchema(schema)
|
||||
.setSchemaTypeVisibilityForSystemUi("Schema", false)
|
||||
.build();
|
||||
assertThat(request.getSchemasNotPlatformSurfaceable()).containsExactly("Schema");
|
||||
assertThat(request.getSchemasNotVisibleToSystemUi()).containsExactly("Schema");
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -95,12 +102,12 @@ public class SetSchemaRequestTest {
|
||||
|
||||
// By default, the schema is not visible.
|
||||
SetSchemaRequest request = new SetSchemaRequest.Builder().addSchema(schema).build();
|
||||
assertThat(request.getSchemasPackageAccessible()).isEmpty();
|
||||
assertThat(request.getSchemasVisibleToPackages()).isEmpty();
|
||||
|
||||
PackageIdentifier packageIdentifier =
|
||||
new PackageIdentifier("com.package.foo", new byte[] {100});
|
||||
Map<String, Set<PackageIdentifier>> expectedPackageVisibleMap = new ArrayMap<>();
|
||||
expectedPackageVisibleMap.put("Schema", Collections.singleton(packageIdentifier));
|
||||
Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>();
|
||||
expectedVisibleToPackagesMap.put("Schema", Collections.singleton(packageIdentifier));
|
||||
|
||||
request =
|
||||
new SetSchemaRequest.Builder()
|
||||
@ -108,8 +115,8 @@ public class SetSchemaRequestTest {
|
||||
.setSchemaTypeVisibilityForPackage(
|
||||
"Schema", /*visible=*/ true, packageIdentifier)
|
||||
.build();
|
||||
assertThat(request.getSchemasPackageAccessible())
|
||||
.containsExactlyEntriesIn(expectedPackageVisibleMap);
|
||||
assertThat(request.getSchemasVisibleToPackages())
|
||||
.containsExactlyEntriesIn(expectedVisibleToPackagesMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -123,9 +130,9 @@ public class SetSchemaRequestTest {
|
||||
"Schema",
|
||||
/*visible=*/ false,
|
||||
new PackageIdentifier(
|
||||
"com.package.foo", /*certificate=*/ new byte[] {}))
|
||||
"com.package.foo", /*sha256Certificate=*/ new byte[] {}))
|
||||
.build();
|
||||
assertThat(request.getSchemasPackageAccessible()).isEmpty();
|
||||
assertThat(request.getSchemasVisibleToPackages()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -134,8 +141,8 @@ public class SetSchemaRequestTest {
|
||||
|
||||
PackageIdentifier packageIdentifier =
|
||||
new PackageIdentifier("com.package.foo", new byte[] {100});
|
||||
Map<String, Set<PackageIdentifier>> expectedPackageVisibleMap = new ArrayMap<>();
|
||||
expectedPackageVisibleMap.put("Schema", Collections.singleton(packageIdentifier));
|
||||
Map<String, Set<PackageIdentifier>> expectedVisibleToPackagesMap = new ArrayMap<>();
|
||||
expectedVisibleToPackagesMap.put("Schema", Collections.singleton(packageIdentifier));
|
||||
|
||||
SetSchemaRequest request =
|
||||
new SetSchemaRequest.Builder()
|
||||
@ -147,8 +154,8 @@ public class SetSchemaRequestTest {
|
||||
.setSchemaTypeVisibilityForPackage(
|
||||
"Schema", /*visible=*/ true, packageIdentifier)
|
||||
.build();
|
||||
assertThat(request.getSchemasPackageAccessible())
|
||||
.containsExactlyEntriesIn(expectedPackageVisibleMap);
|
||||
assertThat(request.getSchemasVisibleToPackages())
|
||||
.containsExactlyEntriesIn(expectedVisibleToPackagesMap);
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -163,16 +170,16 @@ public class SetSchemaRequestTest {
|
||||
"Schema",
|
||||
/*visible=*/ true,
|
||||
new PackageIdentifier(
|
||||
"com.package.foo", /*certificate=*/ new byte[] {100}))
|
||||
"com.package.foo", /*sha256Certificate=*/ new byte[] {100}))
|
||||
// Then make it not visible
|
||||
.setSchemaTypeVisibilityForPackage(
|
||||
"Schema",
|
||||
/*visible=*/ false,
|
||||
new PackageIdentifier(
|
||||
"com.package.foo", /*certificate=*/ new byte[] {100}))
|
||||
"com.package.foo", /*sha256Certificate=*/ new byte[] {100}))
|
||||
.build();
|
||||
|
||||
// Nothing should be visible.
|
||||
assertThat(request.getSchemasPackageAccessible()).isEmpty();
|
||||
assertThat(request.getSchemasVisibleToPackages()).isEmpty();
|
||||
}
|
||||
}
|
||||
|
@ -75,6 +75,8 @@ public class AppSearchImplTest {
|
||||
SchemaToProtoConverter.toSchemaTypeConfigProto(rewrittenVisibilitySchema.build());
|
||||
}
|
||||
|
||||
// TODO(b/175430168) add test to verify reset is working properly.
|
||||
|
||||
/**
|
||||
* Ensure that we can rewrite an incoming schema type by adding the database as a prefix. While
|
||||
* also keeping any other existing schema types that may already be part of Icing's persisted
|
||||
|
Loading…
x
Reference in New Issue
Block a user