diff options
author | Tim Barron <tjbarron@google.com> | 2022-12-13 11:49:15 -0800 |
---|---|---|
committer | Tim Barron <tjbarron@google.com> | 2022-12-13 13:58:03 -0800 |
commit | ffe0a56986f0d400cb63a3d957ac010c3717020f (patch) | |
tree | 3e907e6a371a1abb74eee3c61ff04d5d4dcaa828 /java | |
parent | 4770cb58f874ad72125aa721954a2d6067b7bc9b (diff) | |
parent | 8ddc32ad433ea147de80dcfac2afe58962360f18 (diff) | |
download | icing-ffe0a56986f0d400cb63a3d957ac010c3717020f.tar.gz |
Merge remote-tracking branch 'aosp/upstream-master' into androidx-main
* aosp/upstream-master:
Sync from upstream.
Descriptions:
======================================================================
Add ScoringSpec into JoinSpec. Rename joined_document to child_document.
======================================================================
Create JoinedScoredDocumentHit class and refactor ScoredDocumentHitsRanker.
======================================================================
Implement initial Join workflow
======================================================================
Implement the Lexer for Icing Advanced Query Language
======================================================================
Create struct Options for PersistentHashMap
======================================================================
Premapping FileBackedVector
======================================================================
Create class PersistentHashMapKeyMapper
======================================================================
Add integer sections into TokenizedDocument and rename string sections
======================================================================
Create NumericIndex interface and DocHitInfoIteratorNumeric
======================================================================
Implement DummyNumericIndex and unit test
======================================================================
Change PostingListAccessor::Finalize to rvalue member function
======================================================================
Define the Abstract Syntax Tree for Icing's list_filter parser.
======================================================================
Refactor query processing and score
======================================================================
Refactor IcingSearchEngine for AppSearch Dynamite Module 0p APIs
======================================================================
Implement the Lexer for Icing Advanced Scoring Language
======================================================================
Add a common interface for IcingSearchEngine and dynamite client
======================================================================
Implement a subset of the query grammar.
======================================================================
Refactor index processor
======================================================================
Add integer index into IcingSearchEngine and IndexProcessor
======================================================================
Implement the parser for Icing Advanced Scoring Language
======================================================================
Implement IntegerIndexData and PostingListUsedIntegerIndexDataSerializer
======================================================================
Add PostingListAccessor abstract class for common components and methods
======================================================================
Implement PostingListIntegerIndexDataAccessor
======================================================================
Create PostingListIntegerIndexDataAccessorTest
======================================================================
Fix Icing Segmentation tests for word connectors that changed in ICU 72.
======================================================================
Modify the Advanced Query grammar to allow functions to accept expressions.
======================================================================
Implement QueryVisitor.
======================================================================
Enable the Advanced Query Parser to handle member functions
======================================================================
Refactor the Scorer class to support the Advanced Scoring Language
======================================================================
Integrate advanced query parser with the query processor.
======================================================================
Implement support for JoinSpec in Icing.
======================================================================
Implement the Advanced Scoring Language for basic functions and operators
======================================================================
Bug: 208654892
Bug: 249829533
Bug: 256022027
Bug: 261474063
Bug: 240333360
Bug: 193919210
Change-Id: I07d0878666a9e794ab78f8e468dd48750a9bc12a
Diffstat (limited to 'java')
4 files changed, 1032 insertions, 530 deletions
diff --git a/java/src/com/google/android/icing/IcingSearchEngine.java b/java/src/com/google/android/icing/IcingSearchEngine.java index 81223f2..47b94a5 100644 --- a/java/src/com/google/android/icing/IcingSearchEngine.java +++ b/java/src/com/google/android/icing/IcingSearchEngine.java @@ -14,7 +14,6 @@ package com.google.android.icing; -import android.util.Log; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.google.android.icing.proto.DebugInfoResultProto; @@ -45,17 +44,15 @@ import com.google.android.icing.proto.ScoringSpecProto; import com.google.android.icing.proto.SearchResultProto; import com.google.android.icing.proto.SearchSpecProto; import com.google.android.icing.proto.SetSchemaResultProto; -import com.google.android.icing.proto.StatusProto; import com.google.android.icing.proto.StorageInfoResultProto; import com.google.android.icing.proto.SuggestionResponse; import com.google.android.icing.proto.SuggestionSpecProto; import com.google.android.icing.proto.UsageReport; -import com.google.protobuf.ExtensionRegistryLite; -import com.google.protobuf.InvalidProtocolBufferException; -import java.io.Closeable; /** - * Java wrapper to access native APIs in external/icing/icing/icing-search-engine.h + * Java wrapper to access {@link IcingSearchEngineImpl}. + * + * <p>It converts byte array from {@link IcingSearchEngineImpl} to corresponding protos. * * <p>If this instance has been closed, the instance is no longer usable. * @@ -63,574 +60,197 @@ import java.io.Closeable; * * <p>NOTE: This class is NOT thread-safe. */ -public class IcingSearchEngine implements Closeable { +public class IcingSearchEngine implements IcingSearchEngineInterface { private static final String TAG = "IcingSearchEngine"; - private static final ExtensionRegistryLite EXTENSION_REGISTRY_LITE = - ExtensionRegistryLite.getEmptyRegistry(); - - private long nativePointer; - - private boolean closed = false; - - static { - // NOTE: This can fail with an UnsatisfiedLinkError - System.loadLibrary("icing"); - } + private final IcingSearchEngineImpl icingSearchEngineImpl; /** * @throws IllegalStateException if IcingSearchEngine fails to be created */ public IcingSearchEngine(@NonNull IcingSearchEngineOptions options) { - nativePointer = nativeCreate(options.toByteArray()); - if (nativePointer == 0) { - Log.e(TAG, "Failed to create IcingSearchEngine."); - throw new IllegalStateException("Failed to create IcingSearchEngine."); - } - } - - private void throwIfClosed() { - if (closed) { - throw new IllegalStateException("Trying to use a closed IcingSearchEngine instance."); - } + icingSearchEngineImpl = new IcingSearchEngineImpl(options.toByteArray()); } @Override public void close() { - if (closed) { - return; - } - - if (nativePointer != 0) { - nativeDestroy(this); - } - nativePointer = 0; - closed = true; + icingSearchEngineImpl.close(); } @Override protected void finalize() throws Throwable { - close(); + icingSearchEngineImpl.close(); super.finalize(); } @NonNull + @Override public InitializeResultProto initialize() { - throwIfClosed(); - - byte[] initializeResultBytes = nativeInitialize(this); - if (initializeResultBytes == null) { - Log.e(TAG, "Received null InitializeResult from native."); - return InitializeResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return InitializeResultProto.parseFrom(initializeResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing InitializeResultProto.", e); - return InitializeResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToInitializeResultProto( + icingSearchEngineImpl.initialize()); } @NonNull + @Override public SetSchemaResultProto setSchema(@NonNull SchemaProto schema) { return setSchema(schema, /*ignoreErrorsAndDeleteDocuments=*/ false); } @NonNull + @Override public SetSchemaResultProto setSchema( @NonNull SchemaProto schema, boolean ignoreErrorsAndDeleteDocuments) { - throwIfClosed(); - - byte[] setSchemaResultBytes = - nativeSetSchema(this, schema.toByteArray(), ignoreErrorsAndDeleteDocuments); - if (setSchemaResultBytes == null) { - Log.e(TAG, "Received null SetSchemaResultProto from native."); - return SetSchemaResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return SetSchemaResultProto.parseFrom(setSchemaResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing SetSchemaResultProto.", e); - return SetSchemaResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToSetSchemaResultProto( + icingSearchEngineImpl.setSchema(schema.toByteArray(), ignoreErrorsAndDeleteDocuments)); } @NonNull + @Override public GetSchemaResultProto getSchema() { - throwIfClosed(); - - byte[] getSchemaResultBytes = nativeGetSchema(this); - if (getSchemaResultBytes == null) { - Log.e(TAG, "Received null GetSchemaResultProto from native."); - return GetSchemaResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return GetSchemaResultProto.parseFrom(getSchemaResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing GetSchemaResultProto.", e); - return GetSchemaResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToGetSchemaResultProto( + icingSearchEngineImpl.getSchema()); } @NonNull + @Override public GetSchemaTypeResultProto getSchemaType(@NonNull String schemaType) { - throwIfClosed(); - - byte[] getSchemaTypeResultBytes = nativeGetSchemaType(this, schemaType); - if (getSchemaTypeResultBytes == null) { - Log.e(TAG, "Received null GetSchemaTypeResultProto from native."); - return GetSchemaTypeResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return GetSchemaTypeResultProto.parseFrom(getSchemaTypeResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing GetSchemaTypeResultProto.", e); - return GetSchemaTypeResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToGetSchemaTypeResultProto( + icingSearchEngineImpl.getSchemaType(schemaType)); } @NonNull + @Override public PutResultProto put(@NonNull DocumentProto document) { - throwIfClosed(); - - byte[] putResultBytes = nativePut(this, document.toByteArray()); - if (putResultBytes == null) { - Log.e(TAG, "Received null PutResultProto from native."); - return PutResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return PutResultProto.parseFrom(putResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing PutResultProto.", e); - return PutResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToPutResultProto( + icingSearchEngineImpl.put(document.toByteArray())); } @NonNull + @Override public GetResultProto get( @NonNull String namespace, @NonNull String uri, @NonNull GetResultSpecProto getResultSpec) { - throwIfClosed(); - - byte[] getResultBytes = nativeGet(this, namespace, uri, getResultSpec.toByteArray()); - if (getResultBytes == null) { - Log.e(TAG, "Received null GetResultProto from native."); - return GetResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return GetResultProto.parseFrom(getResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing GetResultProto.", e); - return GetResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToGetResultProto( + icingSearchEngineImpl.get(namespace, uri, getResultSpec.toByteArray())); } @NonNull + @Override public ReportUsageResultProto reportUsage(@NonNull UsageReport usageReport) { - throwIfClosed(); - - byte[] reportUsageResultBytes = nativeReportUsage(this, usageReport.toByteArray()); - if (reportUsageResultBytes == null) { - Log.e(TAG, "Received null ReportUsageResultProto from native."); - return ReportUsageResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return ReportUsageResultProto.parseFrom(reportUsageResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing ReportUsageResultProto.", e); - return ReportUsageResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToReportUsageResultProto( + icingSearchEngineImpl.reportUsage(usageReport.toByteArray())); } @NonNull + @Override public GetAllNamespacesResultProto getAllNamespaces() { - throwIfClosed(); - - byte[] getAllNamespacesResultBytes = nativeGetAllNamespaces(this); - if (getAllNamespacesResultBytes == null) { - Log.e(TAG, "Received null GetAllNamespacesResultProto from native."); - return GetAllNamespacesResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return GetAllNamespacesResultProto.parseFrom( - getAllNamespacesResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing GetAllNamespacesResultProto.", e); - return GetAllNamespacesResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToGetAllNamespacesResultProto( + icingSearchEngineImpl.getAllNamespaces()); } @NonNull + @Override public SearchResultProto search( @NonNull SearchSpecProto searchSpec, @NonNull ScoringSpecProto scoringSpec, @NonNull ResultSpecProto resultSpec) { - throwIfClosed(); - - // Note that on Android System.currentTimeMillis() is the standard "wall" clock and can be set - // by the user or the phone network so the time may jump backwards or forwards unpredictably. - // This could lead to inaccurate final JNI latency calculations or unexpected negative numbers - // in the case where the phone time is changed while sending data across JNI layers. - // However these occurrences should be very rare, so we will keep usage of - // System.currentTimeMillis() due to the lack of better time functions that can provide a - // consistent timestamp across all platforms. - long javaToNativeStartTimestampMs = System.currentTimeMillis(); - byte[] searchResultBytes = - nativeSearch( - this, - searchSpec.toByteArray(), - scoringSpec.toByteArray(), - resultSpec.toByteArray(), - javaToNativeStartTimestampMs); - if (searchResultBytes == null) { - Log.e(TAG, "Received null SearchResultProto from native."); - return SearchResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - SearchResultProto.Builder searchResultProtoBuilder = - SearchResultProto.newBuilder().mergeFrom(searchResultBytes, EXTENSION_REGISTRY_LITE); - setNativeToJavaJniLatency(searchResultProtoBuilder); - return searchResultProtoBuilder.build(); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing SearchResultProto.", e); - return SearchResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToSearchResultProto( + icingSearchEngineImpl.search( + searchSpec.toByteArray(), scoringSpec.toByteArray(), resultSpec.toByteArray())); } @NonNull + @Override public SearchResultProto getNextPage(long nextPageToken) { - throwIfClosed(); - - byte[] searchResultBytes = nativeGetNextPage(this, nextPageToken, System.currentTimeMillis()); - if (searchResultBytes == null) { - Log.e(TAG, "Received null SearchResultProto from native."); - return SearchResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - SearchResultProto.Builder searchResultProtoBuilder = - SearchResultProto.newBuilder().mergeFrom(searchResultBytes, EXTENSION_REGISTRY_LITE); - setNativeToJavaJniLatency(searchResultProtoBuilder); - return searchResultProtoBuilder.build(); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing SearchResultProto.", e); - return SearchResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - } - - private void setNativeToJavaJniLatency(SearchResultProto.Builder searchResultProtoBuilder) { - int nativeToJavaLatencyMs = - (int) - (System.currentTimeMillis() - - searchResultProtoBuilder.getQueryStats().getNativeToJavaStartTimestampMs()); - searchResultProtoBuilder.setQueryStats( - searchResultProtoBuilder.getQueryStats().toBuilder() - .setNativeToJavaJniLatencyMs(nativeToJavaLatencyMs)); + return IcingSearchEngineUtils.byteArrayToSearchResultProto( + icingSearchEngineImpl.getNextPage(nextPageToken)); } @NonNull + @Override public void invalidateNextPageToken(long nextPageToken) { - throwIfClosed(); - - nativeInvalidateNextPageToken(this, nextPageToken); + icingSearchEngineImpl.invalidateNextPageToken(nextPageToken); } @NonNull + @Override public DeleteResultProto delete(@NonNull String namespace, @NonNull String uri) { - throwIfClosed(); - - byte[] deleteResultBytes = nativeDelete(this, namespace, uri); - if (deleteResultBytes == null) { - Log.e(TAG, "Received null DeleteResultProto from native."); - return DeleteResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return DeleteResultProto.parseFrom(deleteResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing DeleteResultProto.", e); - return DeleteResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToDeleteResultProto( + icingSearchEngineImpl.delete(namespace, uri)); } @NonNull + @Override public SuggestionResponse searchSuggestions(@NonNull SuggestionSpecProto suggestionSpec) { - byte[] suggestionResponseBytes = nativeSearchSuggestions(this, suggestionSpec.toByteArray()); - if (suggestionResponseBytes == null) { - Log.e(TAG, "Received null suggestionResponseBytes from native."); - return SuggestionResponse.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return SuggestionResponse.parseFrom(suggestionResponseBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing suggestionResponseBytes.", e); - return SuggestionResponse.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToSuggestionResponse( + icingSearchEngineImpl.searchSuggestions(suggestionSpec.toByteArray())); } @NonNull + @Override public DeleteByNamespaceResultProto deleteByNamespace(@NonNull String namespace) { - throwIfClosed(); - - byte[] deleteByNamespaceResultBytes = nativeDeleteByNamespace(this, namespace); - if (deleteByNamespaceResultBytes == null) { - Log.e(TAG, "Received null DeleteByNamespaceResultProto from native."); - return DeleteByNamespaceResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return DeleteByNamespaceResultProto.parseFrom( - deleteByNamespaceResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing DeleteByNamespaceResultProto.", e); - return DeleteByNamespaceResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToDeleteByNamespaceResultProto( + icingSearchEngineImpl.deleteByNamespace(namespace)); } @NonNull + @Override public DeleteBySchemaTypeResultProto deleteBySchemaType(@NonNull String schemaType) { - throwIfClosed(); - - byte[] deleteBySchemaTypeResultBytes = nativeDeleteBySchemaType(this, schemaType); - if (deleteBySchemaTypeResultBytes == null) { - Log.e(TAG, "Received null DeleteBySchemaTypeResultProto from native."); - return DeleteBySchemaTypeResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return DeleteBySchemaTypeResultProto.parseFrom( - deleteBySchemaTypeResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing DeleteBySchemaTypeResultProto.", e); - return DeleteBySchemaTypeResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToDeleteBySchemaTypeResultProto( + icingSearchEngineImpl.deleteBySchemaType(schemaType)); } @NonNull + @Override public DeleteByQueryResultProto deleteByQuery(@NonNull SearchSpecProto searchSpec) { return deleteByQuery(searchSpec, /*returnDeletedDocumentInfo=*/ false); } @NonNull + @Override public DeleteByQueryResultProto deleteByQuery( @NonNull SearchSpecProto searchSpec, boolean returnDeletedDocumentInfo) { - throwIfClosed(); - - byte[] deleteResultBytes = - nativeDeleteByQuery(this, searchSpec.toByteArray(), returnDeletedDocumentInfo); - if (deleteResultBytes == null) { - Log.e(TAG, "Received null DeleteResultProto from native."); - return DeleteByQueryResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return DeleteByQueryResultProto.parseFrom(deleteResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing DeleteResultProto.", e); - return DeleteByQueryResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToDeleteByQueryResultProto( + icingSearchEngineImpl.deleteByQuery(searchSpec.toByteArray(), returnDeletedDocumentInfo)); } @NonNull + @Override public PersistToDiskResultProto persistToDisk(@NonNull PersistType.Code persistTypeCode) { - throwIfClosed(); - - byte[] persistToDiskResultBytes = nativePersistToDisk(this, persistTypeCode.getNumber()); - if (persistToDiskResultBytes == null) { - Log.e(TAG, "Received null PersistToDiskResultProto from native."); - return PersistToDiskResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return PersistToDiskResultProto.parseFrom(persistToDiskResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing PersistToDiskResultProto.", e); - return PersistToDiskResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToPersistToDiskResultProto( + icingSearchEngineImpl.persistToDisk(persistTypeCode.getNumber())); } @NonNull + @Override public OptimizeResultProto optimize() { - throwIfClosed(); - - byte[] optimizeResultBytes = nativeOptimize(this); - if (optimizeResultBytes == null) { - Log.e(TAG, "Received null OptimizeResultProto from native."); - return OptimizeResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return OptimizeResultProto.parseFrom(optimizeResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing OptimizeResultProto.", e); - return OptimizeResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToOptimizeResultProto(icingSearchEngineImpl.optimize()); } @NonNull + @Override public GetOptimizeInfoResultProto getOptimizeInfo() { - throwIfClosed(); - - byte[] getOptimizeInfoResultBytes = nativeGetOptimizeInfo(this); - if (getOptimizeInfoResultBytes == null) { - Log.e(TAG, "Received null GetOptimizeInfoResultProto from native."); - return GetOptimizeInfoResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return GetOptimizeInfoResultProto.parseFrom( - getOptimizeInfoResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing GetOptimizeInfoResultProto.", e); - return GetOptimizeInfoResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToGetOptimizeInfoResultProto( + icingSearchEngineImpl.getOptimizeInfo()); } @NonNull + @Override public StorageInfoResultProto getStorageInfo() { - throwIfClosed(); - - byte[] storageInfoResultProtoBytes = nativeGetStorageInfo(this); - if (storageInfoResultProtoBytes == null) { - Log.e(TAG, "Received null StorageInfoResultProto from native."); - return StorageInfoResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return StorageInfoResultProto.parseFrom(storageInfoResultProtoBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing GetOptimizeInfoResultProto.", e); - return StorageInfoResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToStorageInfoResultProto( + icingSearchEngineImpl.getStorageInfo()); } @NonNull + @Override public DebugInfoResultProto getDebugInfo(DebugInfoVerbosity.Code verbosity) { - throwIfClosed(); - - byte[] debugInfoResultProtoBytes = nativeGetDebugInfo(this, verbosity.getNumber()); - if (debugInfoResultProtoBytes == null) { - Log.e(TAG, "Received null DebugInfoResultProto from native."); - return DebugInfoResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return DebugInfoResultProto.parseFrom(debugInfoResultProtoBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing DebugInfoResultProto.", e); - return DebugInfoResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToDebugInfoResultProto( + icingSearchEngineImpl.getDebugInfo(verbosity.getNumber())); } @NonNull + @Override public ResetResultProto reset() { - throwIfClosed(); - - byte[] resetResultBytes = nativeReset(this); - if (resetResultBytes == null) { - Log.e(TAG, "Received null ResetResultProto from native."); - return ResetResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } - - try { - return ResetResultProto.parseFrom(resetResultBytes, EXTENSION_REGISTRY_LITE); - } catch (InvalidProtocolBufferException e) { - Log.e(TAG, "Error parsing ResetResultProto.", e); - return ResetResultProto.newBuilder() - .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) - .build(); - } + return IcingSearchEngineUtils.byteArrayToResetResultProto(icingSearchEngineImpl.reset()); } public static boolean shouldLog(LogSeverity.Code severity) { @@ -638,7 +258,7 @@ public class IcingSearchEngine implements Closeable { } public static boolean shouldLog(LogSeverity.Code severity, short verbosity) { - return nativeShouldLog((short) severity.getNumber(), verbosity); + return IcingSearchEngineImpl.shouldLog((short) severity.getNumber(), verbosity); } public static boolean setLoggingLevel(LogSeverity.Code severity) { @@ -646,84 +266,11 @@ public class IcingSearchEngine implements Closeable { } public static boolean setLoggingLevel(LogSeverity.Code severity, short verbosity) { - return nativeSetLoggingLevel((short) severity.getNumber(), verbosity); + return IcingSearchEngineImpl.setLoggingLevel((short) severity.getNumber(), verbosity); } @Nullable public static String getLoggingTag() { - String tag = nativeGetLoggingTag(); - if (tag == null) { - Log.e(TAG, "Received null logging tag from native."); - } - return tag; + return IcingSearchEngineImpl.getLoggingTag(); } - - private static native long nativeCreate(byte[] icingSearchEngineOptionsBytes); - - private static native void nativeDestroy(IcingSearchEngine instance); - - private static native byte[] nativeInitialize(IcingSearchEngine instance); - - private static native byte[] nativeSetSchema( - IcingSearchEngine instance, byte[] schemaBytes, boolean ignoreErrorsAndDeleteDocuments); - - private static native byte[] nativeGetSchema(IcingSearchEngine instance); - - private static native byte[] nativeGetSchemaType(IcingSearchEngine instance, String schemaType); - - private static native byte[] nativePut(IcingSearchEngine instance, byte[] documentBytes); - - private static native byte[] nativeGet( - IcingSearchEngine instance, String namespace, String uri, byte[] getResultSpecBytes); - - private static native byte[] nativeReportUsage( - IcingSearchEngine instance, byte[] usageReportBytes); - - private static native byte[] nativeGetAllNamespaces(IcingSearchEngine instance); - - private static native byte[] nativeSearch( - IcingSearchEngine instance, - byte[] searchSpecBytes, - byte[] scoringSpecBytes, - byte[] resultSpecBytes, - long javaToNativeStartTimestampMs); - - private static native byte[] nativeGetNextPage( - IcingSearchEngine instance, long nextPageToken, long javaToNativeStartTimestampMs); - - private static native void nativeInvalidateNextPageToken( - IcingSearchEngine instance, long nextPageToken); - - private static native byte[] nativeDelete( - IcingSearchEngine instance, String namespace, String uri); - - private static native byte[] nativeDeleteByNamespace( - IcingSearchEngine instance, String namespace); - - private static native byte[] nativeDeleteBySchemaType( - IcingSearchEngine instance, String schemaType); - - private static native byte[] nativeDeleteByQuery( - IcingSearchEngine instance, byte[] searchSpecBytes, boolean returnDeletedDocumentInfo); - - private static native byte[] nativePersistToDisk(IcingSearchEngine instance, int persistType); - - private static native byte[] nativeOptimize(IcingSearchEngine instance); - - private static native byte[] nativeGetOptimizeInfo(IcingSearchEngine instance); - - private static native byte[] nativeGetStorageInfo(IcingSearchEngine instance); - - private static native byte[] nativeReset(IcingSearchEngine instance); - - private static native byte[] nativeSearchSuggestions( - IcingSearchEngine instance, byte[] suggestionSpecBytes); - - private static native byte[] nativeGetDebugInfo(IcingSearchEngine instance, int verbosity); - - private static native boolean nativeShouldLog(short severity, short verbosity); - - private static native boolean nativeSetLoggingLevel(short severity, short verbosity); - - private static native String nativeGetLoggingTag(); } diff --git a/java/src/com/google/android/icing/IcingSearchEngineImpl.java b/java/src/com/google/android/icing/IcingSearchEngineImpl.java new file mode 100644 index 0000000..8e79a88 --- /dev/null +++ b/java/src/com/google/android/icing/IcingSearchEngineImpl.java @@ -0,0 +1,330 @@ +// Copyright (C) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.android.icing; + +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import java.io.Closeable; + +/** + * Java wrapper to access native APIs in external/icing/icing/icing-search-engine.h + * + * <p>If this instance has been closed, the instance is no longer usable. + * + * <p>Keep this class to be non-Final so that it can be mocked in AppSearch. + * + * <p>NOTE: This class is NOT thread-safe. + */ +public class IcingSearchEngineImpl implements Closeable { + + private static final String TAG = "IcingSearchEngineImpl"; + + private long nativePointer; + + private boolean closed = false; + + static { + // NOTE: This can fail with an UnsatisfiedLinkError + System.loadLibrary("icing"); + } + + /** + * @throws IllegalStateException if IcingSearchEngineImpl fails to be created + */ + public IcingSearchEngineImpl(@NonNull byte[] optionsBytes) { + nativePointer = nativeCreate(optionsBytes); + if (nativePointer == 0) { + Log.e(TAG, "Failed to create IcingSearchEngineImpl."); + throw new IllegalStateException("Failed to create IcingSearchEngineImpl."); + } + } + + private void throwIfClosed() { + if (closed) { + throw new IllegalStateException("Trying to use a closed IcingSearchEngineImpl instance."); + } + } + + @Override + public void close() { + if (closed) { + return; + } + + if (nativePointer != 0) { + nativeDestroy(this); + } + nativePointer = 0; + closed = true; + } + + @Override + protected void finalize() throws Throwable { + close(); + super.finalize(); + } + + @Nullable + public byte[] initialize() { + throwIfClosed(); + return nativeInitialize(this); + } + + @Nullable + public byte[] setSchema(@NonNull byte[] schemaBytes) { + return setSchema(schemaBytes, /* ignoreErrorsAndDeleteDocuments= */ false); + } + + @Nullable + public byte[] setSchema(@NonNull byte[] schemaBytes, boolean ignoreErrorsAndDeleteDocuments) { + throwIfClosed(); + return nativeSetSchema(this, schemaBytes, ignoreErrorsAndDeleteDocuments); + } + + @Nullable + public byte[] getSchema() { + throwIfClosed(); + return nativeGetSchema(this); + } + + @Nullable + public byte[] getSchemaType(@NonNull String schemaType) { + throwIfClosed(); + return nativeGetSchemaType(this, schemaType); + } + + @Nullable + public byte[] put(@NonNull byte[] documentBytes) { + throwIfClosed(); + return nativePut(this, documentBytes); + } + + @Nullable + public byte[] get( + @NonNull String namespace, @NonNull String uri, @NonNull byte[] getResultSpecBytes) { + throwIfClosed(); + return nativeGet(this, namespace, uri, getResultSpecBytes); + } + + @Nullable + public byte[] reportUsage(@NonNull byte[] usageReportBytes) { + throwIfClosed(); + return nativeReportUsage(this, usageReportBytes); + } + + @Nullable + public byte[] getAllNamespaces() { + throwIfClosed(); + return nativeGetAllNamespaces(this); + } + + @Nullable + public byte[] search( + @NonNull byte[] searchSpecBytes, + @NonNull byte[] scoringSpecBytes, + @NonNull byte[] resultSpecBytes) { + throwIfClosed(); + + // Note that on Android System.currentTimeMillis() is the standard "wall" clock and can be set + // by the user or the phone network so the time may jump backwards or forwards unpredictably. + // This could lead to inaccurate final JNI latency calculations or unexpected negative numbers + // in the case where the phone time is changed while sending data across JNI layers. + // However these occurrences should be very rare, so we will keep usage of + // System.currentTimeMillis() due to the lack of better time functions that can provide a + // consistent timestamp across all platforms. + long javaToNativeStartTimestampMs = System.currentTimeMillis(); + return nativeSearch( + this, searchSpecBytes, scoringSpecBytes, resultSpecBytes, javaToNativeStartTimestampMs); + } + + @Nullable + public byte[] getNextPage(long nextPageToken) { + throwIfClosed(); + return nativeGetNextPage(this, nextPageToken, System.currentTimeMillis()); + } + + @NonNull + public void invalidateNextPageToken(long nextPageToken) { + throwIfClosed(); + nativeInvalidateNextPageToken(this, nextPageToken); + } + + @Nullable + public byte[] delete(@NonNull String namespace, @NonNull String uri) { + throwIfClosed(); + return nativeDelete(this, namespace, uri); + } + + @Nullable + public byte[] searchSuggestions(@NonNull byte[] suggestionSpecBytes) { + throwIfClosed(); + return nativeSearchSuggestions(this, suggestionSpecBytes); + } + + @Nullable + public byte[] deleteByNamespace(@NonNull String namespace) { + throwIfClosed(); + return nativeDeleteByNamespace(this, namespace); + } + + @Nullable + public byte[] deleteBySchemaType(@NonNull String schemaType) { + throwIfClosed(); + return nativeDeleteBySchemaType(this, schemaType); + } + + @Nullable + public byte[] deleteByQuery(@NonNull byte[] searchSpecBytes) { + return deleteByQuery(searchSpecBytes, /* returnDeletedDocumentInfo= */ false); + } + + @Nullable + public byte[] deleteByQuery(@NonNull byte[] searchSpecBytes, boolean returnDeletedDocumentInfo) { + throwIfClosed(); + return nativeDeleteByQuery(this, searchSpecBytes, returnDeletedDocumentInfo); + } + + @Nullable + public byte[] persistToDisk(int persistTypeCode) { + throwIfClosed(); + return nativePersistToDisk(this, persistTypeCode); + } + + @Nullable + public byte[] optimize() { + throwIfClosed(); + return nativeOptimize(this); + } + + @Nullable + public byte[] getOptimizeInfo() { + throwIfClosed(); + return nativeGetOptimizeInfo(this); + } + + @Nullable + public byte[] getStorageInfo() { + throwIfClosed(); + return nativeGetStorageInfo(this); + } + + @Nullable + public byte[] getDebugInfo(int verbosityCode) { + throwIfClosed(); + return nativeGetDebugInfo(this, verbosityCode); + } + + @Nullable + public byte[] reset() { + throwIfClosed(); + return nativeReset(this); + } + + public static boolean shouldLog(short severity) { + return shouldLog(severity, (short) 0); + } + + public static boolean shouldLog(short severity, short verbosity) { + return nativeShouldLog(severity, verbosity); + } + + public static boolean setLoggingLevel(short severity) { + return setLoggingLevel(severity, (short) 0); + } + + public static boolean setLoggingLevel(short severity, short verbosity) { + return nativeSetLoggingLevel(severity, verbosity); + } + + @Nullable + public static String getLoggingTag() { + String tag = nativeGetLoggingTag(); + if (tag == null) { + Log.e(TAG, "Received null logging tag from native."); + } + return tag; + } + + private static native long nativeCreate(byte[] icingSearchEngineOptionsBytes); + + private static native void nativeDestroy(IcingSearchEngineImpl instance); + + private static native byte[] nativeInitialize(IcingSearchEngineImpl instance); + + private static native byte[] nativeSetSchema( + IcingSearchEngineImpl instance, byte[] schemaBytes, boolean ignoreErrorsAndDeleteDocuments); + + private static native byte[] nativeGetSchema(IcingSearchEngineImpl instance); + + private static native byte[] nativeGetSchemaType( + IcingSearchEngineImpl instance, String schemaType); + + private static native byte[] nativePut(IcingSearchEngineImpl instance, byte[] documentBytes); + + private static native byte[] nativeGet( + IcingSearchEngineImpl instance, String namespace, String uri, byte[] getResultSpecBytes); + + private static native byte[] nativeReportUsage( + IcingSearchEngineImpl instance, byte[] usageReportBytes); + + private static native byte[] nativeGetAllNamespaces(IcingSearchEngineImpl instance); + + private static native byte[] nativeSearch( + IcingSearchEngineImpl instance, + byte[] searchSpecBytes, + byte[] scoringSpecBytes, + byte[] resultSpecBytes, + long javaToNativeStartTimestampMs); + + private static native byte[] nativeGetNextPage( + IcingSearchEngineImpl instance, long nextPageToken, long javaToNativeStartTimestampMs); + + private static native void nativeInvalidateNextPageToken( + IcingSearchEngineImpl instance, long nextPageToken); + + private static native byte[] nativeDelete( + IcingSearchEngineImpl instance, String namespace, String uri); + + private static native byte[] nativeDeleteByNamespace( + IcingSearchEngineImpl instance, String namespace); + + private static native byte[] nativeDeleteBySchemaType( + IcingSearchEngineImpl instance, String schemaType); + + private static native byte[] nativeDeleteByQuery( + IcingSearchEngineImpl instance, byte[] searchSpecBytes, boolean returnDeletedDocumentInfo); + + private static native byte[] nativePersistToDisk(IcingSearchEngineImpl instance, int persistType); + + private static native byte[] nativeOptimize(IcingSearchEngineImpl instance); + + private static native byte[] nativeGetOptimizeInfo(IcingSearchEngineImpl instance); + + private static native byte[] nativeGetStorageInfo(IcingSearchEngineImpl instance); + + private static native byte[] nativeReset(IcingSearchEngineImpl instance); + + private static native byte[] nativeSearchSuggestions( + IcingSearchEngineImpl instance, byte[] suggestionSpecBytes); + + private static native byte[] nativeGetDebugInfo(IcingSearchEngineImpl instance, int verbosity); + + private static native boolean nativeShouldLog(short severity, short verbosity); + + private static native boolean nativeSetLoggingLevel(short severity, short verbosity); + + private static native String nativeGetLoggingTag(); +} diff --git a/java/src/com/google/android/icing/IcingSearchEngineInterface.java b/java/src/com/google/android/icing/IcingSearchEngineInterface.java new file mode 100644 index 0000000..5922716 --- /dev/null +++ b/java/src/com/google/android/icing/IcingSearchEngineInterface.java @@ -0,0 +1,154 @@ +package com.google.android.icing; + +import android.os.RemoteException; +import com.google.android.icing.proto.DebugInfoResultProto; +import com.google.android.icing.proto.DebugInfoVerbosity; +import com.google.android.icing.proto.DeleteByNamespaceResultProto; +import com.google.android.icing.proto.DeleteByQueryResultProto; +import com.google.android.icing.proto.DeleteBySchemaTypeResultProto; +import com.google.android.icing.proto.DeleteResultProto; +import com.google.android.icing.proto.DocumentProto; +import com.google.android.icing.proto.GetAllNamespacesResultProto; +import com.google.android.icing.proto.GetOptimizeInfoResultProto; +import com.google.android.icing.proto.GetResultProto; +import com.google.android.icing.proto.GetResultSpecProto; +import com.google.android.icing.proto.GetSchemaResultProto; +import com.google.android.icing.proto.GetSchemaTypeResultProto; +import com.google.android.icing.proto.InitializeResultProto; +import com.google.android.icing.proto.OptimizeResultProto; +import com.google.android.icing.proto.PersistToDiskResultProto; +import com.google.android.icing.proto.PersistType; +import com.google.android.icing.proto.PutResultProto; +import com.google.android.icing.proto.ReportUsageResultProto; +import com.google.android.icing.proto.ResetResultProto; +import com.google.android.icing.proto.ResultSpecProto; +import com.google.android.icing.proto.SchemaProto; +import com.google.android.icing.proto.ScoringSpecProto; +import com.google.android.icing.proto.SearchResultProto; +import com.google.android.icing.proto.SearchSpecProto; +import com.google.android.icing.proto.SetSchemaResultProto; +import com.google.android.icing.proto.StorageInfoResultProto; +import com.google.android.icing.proto.SuggestionResponse; +import com.google.android.icing.proto.SuggestionSpecProto; +import com.google.android.icing.proto.UsageReport; +import java.io.Closeable; + +/** + * A common user-facing interface to expose the funcationalities provided by Icing Library. + * + * <p>All the methods here throw {@link RemoteException} because the implementation for + * gmscore-appsearch-dynamite will throw it. + */ +public interface IcingSearchEngineInterface extends Closeable { + /** + * Initializes the current IcingSearchEngine implementation. + * + * <p>Internally the icing instance will be initialized. + */ + InitializeResultProto initialize(); + + /** Sets the schema for the icing instance. */ + SetSchemaResultProto setSchema(SchemaProto schema); + + /** + * Sets the schema for the icing instance. + * + * @param ignoreErrorsAndDeleteDocuments force to set the schema and delete documents in case of + * incompatible schema change. + */ + SetSchemaResultProto setSchema(SchemaProto schema, boolean ignoreErrorsAndDeleteDocuments); + + /** Gets the schema for the icing instance. */ + GetSchemaResultProto getSchema(); + + /** + * Gets the schema for the icing instance. + * + * @param schemaType type of the schema. + */ + GetSchemaTypeResultProto getSchemaType(String schemaType); + + /** Puts the document. */ + PutResultProto put(DocumentProto document); + + /** + * Gets the document. + * + * @param namespace namespace of the document. + * @param uri uri of the document. + * @param getResultSpec the spec for getting the document. + */ + GetResultProto get(String namespace, String uri, GetResultSpecProto getResultSpec); + + /** Reports usage. */ + ReportUsageResultProto reportUsage(UsageReport usageReport); + + /** Gets all namespaces. */ + GetAllNamespacesResultProto getAllNamespaces(); + + /** + * Searches over the documents. + * + * <p>Documents need to be retrieved on the following {@link #getNextPage} calls on the returned + * {@link SearchResultProto}. + */ + SearchResultProto search( + SearchSpecProto searchSpec, ScoringSpecProto scoringSpec, ResultSpecProto resultSpec); + + /** Gets the next page. */ + SearchResultProto getNextPage(long nextPageToken); + + /** Invalidates the next page token. */ + void invalidateNextPageToken(long nextPageToken); + + /** + * Deletes the document. + * + * @param namespace the namespace the document to be deleted belong to. + * @param uri the uri for the document to be deleted. + */ + DeleteResultProto delete(String namespace, String uri); + + /** Returns the suggestions for the search query. */ + SuggestionResponse searchSuggestions(SuggestionSpecProto suggestionSpec); + + /** Deletes documents by the namespace. */ + DeleteByNamespaceResultProto deleteByNamespace(String namespace); + + /** Deletes documents by the schema type. */ + DeleteBySchemaTypeResultProto deleteBySchemaType(String schemaType); + + /** Deletes documents by the search query. */ + DeleteByQueryResultProto deleteByQuery(SearchSpecProto searchSpec); + + /** + * Deletes document by the search query + * + * @param returnDeletedDocumentInfo whether additional information about deleted documents will be + * included in {@link DeleteByQueryResultProto}. + */ + DeleteByQueryResultProto deleteByQuery( + SearchSpecProto searchSpec, boolean returnDeletedDocumentInfo); + + /** Makes sure every update/delete received till this point is flushed to disk. */ + PersistToDiskResultProto persistToDisk(PersistType.Code persistTypeCode); + + /** Makes the icing instance run tasks that are too expensive to be run in real-time. */ + OptimizeResultProto optimize(); + + /** Gets information about the optimization. */ + GetOptimizeInfoResultProto getOptimizeInfo(); + + /** Gets information about the storage. */ + StorageInfoResultProto getStorageInfo(); + + /** Gets the debug information for the current icing instance. */ + DebugInfoResultProto getDebugInfo(DebugInfoVerbosity.Code verbosity); + + /** Clears all data from the current icing instance, and reinitializes it. */ + ResetResultProto reset(); + + /** Closes the current icing instance. */ + @Override + void close(); +} diff --git a/java/src/com/google/android/icing/IcingSearchEngineUtils.java b/java/src/com/google/android/icing/IcingSearchEngineUtils.java new file mode 100644 index 0000000..0913216 --- /dev/null +++ b/java/src/com/google/android/icing/IcingSearchEngineUtils.java @@ -0,0 +1,471 @@ +// Copyright (C) 2019 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.android.icing; + +import android.util.Log; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import com.google.android.icing.proto.DebugInfoResultProto; +import com.google.android.icing.proto.DeleteByNamespaceResultProto; +import com.google.android.icing.proto.DeleteByQueryResultProto; +import com.google.android.icing.proto.DeleteBySchemaTypeResultProto; +import com.google.android.icing.proto.DeleteResultProto; +import com.google.android.icing.proto.GetAllNamespacesResultProto; +import com.google.android.icing.proto.GetOptimizeInfoResultProto; +import com.google.android.icing.proto.GetResultProto; +import com.google.android.icing.proto.GetSchemaResultProto; +import com.google.android.icing.proto.GetSchemaTypeResultProto; +import com.google.android.icing.proto.InitializeResultProto; +import com.google.android.icing.proto.OptimizeResultProto; +import com.google.android.icing.proto.PersistToDiskResultProto; +import com.google.android.icing.proto.PutResultProto; +import com.google.android.icing.proto.ReportUsageResultProto; +import com.google.android.icing.proto.ResetResultProto; +import com.google.android.icing.proto.SearchResultProto; +import com.google.android.icing.proto.SetSchemaResultProto; +import com.google.android.icing.proto.StatusProto; +import com.google.android.icing.proto.StorageInfoResultProto; +import com.google.android.icing.proto.SuggestionResponse; +import com.google.protobuf.ExtensionRegistryLite; +import com.google.protobuf.InvalidProtocolBufferException; + +/** + * Contains utility methods for IcingSearchEngine to convert byte arrays to the corresponding + * protos. + * + * <p>It is also being used by AppSearch dynamite 0p client APIs to convert byte arrays to the + * protos. + */ +public final class IcingSearchEngineUtils { + private static final String TAG = "IcingSearchEngineUtils"; + private static final ExtensionRegistryLite EXTENSION_REGISTRY_LITE = + ExtensionRegistryLite.getEmptyRegistry(); + + private IcingSearchEngineUtils() {} + + // TODO(b/240333360) Check to see if we can use one template function to replace those + @NonNull + public static InitializeResultProto byteArrayToInitializeResultProto( + @Nullable byte[] initializeResultBytes) { + if (initializeResultBytes == null) { + Log.e(TAG, "Received null InitializeResult from native."); + return InitializeResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return InitializeResultProto.parseFrom(initializeResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing InitializeResultProto.", e); + return InitializeResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static SetSchemaResultProto byteArrayToSetSchemaResultProto( + @Nullable byte[] setSchemaResultBytes) { + if (setSchemaResultBytes == null) { + Log.e(TAG, "Received null SetSchemaResultProto from native."); + return SetSchemaResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return SetSchemaResultProto.parseFrom(setSchemaResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing SetSchemaResultProto.", e); + return SetSchemaResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static GetSchemaResultProto byteArrayToGetSchemaResultProto( + @Nullable byte[] getSchemaResultBytes) { + if (getSchemaResultBytes == null) { + Log.e(TAG, "Received null GetSchemaResultProto from native."); + return GetSchemaResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return GetSchemaResultProto.parseFrom(getSchemaResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing GetSchemaResultProto.", e); + return GetSchemaResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static GetSchemaTypeResultProto byteArrayToGetSchemaTypeResultProto( + @Nullable byte[] getSchemaTypeResultBytes) { + if (getSchemaTypeResultBytes == null) { + Log.e(TAG, "Received null GetSchemaTypeResultProto from native."); + return GetSchemaTypeResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return GetSchemaTypeResultProto.parseFrom(getSchemaTypeResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing GetSchemaTypeResultProto.", e); + return GetSchemaTypeResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static PutResultProto byteArrayToPutResultProto(@Nullable byte[] putResultBytes) { + if (putResultBytes == null) { + Log.e(TAG, "Received null PutResultProto from native."); + return PutResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return PutResultProto.parseFrom(putResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing PutResultProto.", e); + return PutResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static GetResultProto byteArrayToGetResultProto(@Nullable byte[] getResultBytes) { + if (getResultBytes == null) { + Log.e(TAG, "Received null GetResultProto from native."); + return GetResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return GetResultProto.parseFrom(getResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing GetResultProto.", e); + return GetResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static ReportUsageResultProto byteArrayToReportUsageResultProto( + @Nullable byte[] reportUsageResultBytes) { + if (reportUsageResultBytes == null) { + Log.e(TAG, "Received null ReportUsageResultProto from native."); + return ReportUsageResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return ReportUsageResultProto.parseFrom(reportUsageResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing ReportUsageResultProto.", e); + return ReportUsageResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static GetAllNamespacesResultProto byteArrayToGetAllNamespacesResultProto( + @Nullable byte[] getAllNamespacesResultBytes) { + if (getAllNamespacesResultBytes == null) { + Log.e(TAG, "Received null GetAllNamespacesResultProto from native."); + return GetAllNamespacesResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return GetAllNamespacesResultProto.parseFrom( + getAllNamespacesResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing GetAllNamespacesResultProto.", e); + return GetAllNamespacesResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static SearchResultProto byteArrayToSearchResultProto(@Nullable byte[] searchResultBytes) { + if (searchResultBytes == null) { + Log.e(TAG, "Received null SearchResultProto from native."); + return SearchResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + SearchResultProto.Builder searchResultProtoBuilder = + SearchResultProto.newBuilder().mergeFrom(searchResultBytes, EXTENSION_REGISTRY_LITE); + setNativeToJavaJniLatency(searchResultProtoBuilder); + return searchResultProtoBuilder.build(); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing SearchResultProto.", e); + return SearchResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + private static void setNativeToJavaJniLatency( + SearchResultProto.Builder searchResultProtoBuilder) { + int nativeToJavaLatencyMs = + (int) + (System.currentTimeMillis() + - searchResultProtoBuilder.getQueryStats().getNativeToJavaStartTimestampMs()); + searchResultProtoBuilder.setQueryStats( + searchResultProtoBuilder.getQueryStats().toBuilder() + .setNativeToJavaJniLatencyMs(nativeToJavaLatencyMs)); + } + + @NonNull + public static DeleteResultProto byteArrayToDeleteResultProto(@Nullable byte[] deleteResultBytes) { + if (deleteResultBytes == null) { + Log.e(TAG, "Received null DeleteResultProto from native."); + return DeleteResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return DeleteResultProto.parseFrom(deleteResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing DeleteResultProto.", e); + return DeleteResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static SuggestionResponse byteArrayToSuggestionResponse( + @Nullable byte[] suggestionResponseBytes) { + if (suggestionResponseBytes == null) { + Log.e(TAG, "Received null suggestionResponseBytes from native."); + return SuggestionResponse.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return SuggestionResponse.parseFrom(suggestionResponseBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing suggestionResponseBytes.", e); + return SuggestionResponse.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static DeleteByNamespaceResultProto byteArrayToDeleteByNamespaceResultProto( + @Nullable byte[] deleteByNamespaceResultBytes) { + if (deleteByNamespaceResultBytes == null) { + Log.e(TAG, "Received null DeleteByNamespaceResultProto from native."); + return DeleteByNamespaceResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return DeleteByNamespaceResultProto.parseFrom( + deleteByNamespaceResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing DeleteByNamespaceResultProto.", e); + return DeleteByNamespaceResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static DeleteBySchemaTypeResultProto byteArrayToDeleteBySchemaTypeResultProto( + @Nullable byte[] deleteBySchemaTypeResultBytes) { + if (deleteBySchemaTypeResultBytes == null) { + Log.e(TAG, "Received null DeleteBySchemaTypeResultProto from native."); + return DeleteBySchemaTypeResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return DeleteBySchemaTypeResultProto.parseFrom( + deleteBySchemaTypeResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing DeleteBySchemaTypeResultProto.", e); + return DeleteBySchemaTypeResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static DeleteByQueryResultProto byteArrayToDeleteByQueryResultProto( + @Nullable byte[] deleteResultBytes) { + if (deleteResultBytes == null) { + Log.e(TAG, "Received null DeleteResultProto from native."); + return DeleteByQueryResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return DeleteByQueryResultProto.parseFrom(deleteResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing DeleteResultProto.", e); + return DeleteByQueryResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static PersistToDiskResultProto byteArrayToPersistToDiskResultProto( + @Nullable byte[] persistToDiskResultBytes) { + if (persistToDiskResultBytes == null) { + Log.e(TAG, "Received null PersistToDiskResultProto from native."); + return PersistToDiskResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return PersistToDiskResultProto.parseFrom(persistToDiskResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing PersistToDiskResultProto.", e); + return PersistToDiskResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static OptimizeResultProto byteArrayToOptimizeResultProto( + @Nullable byte[] optimizeResultBytes) { + if (optimizeResultBytes == null) { + Log.e(TAG, "Received null OptimizeResultProto from native."); + return OptimizeResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return OptimizeResultProto.parseFrom(optimizeResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing OptimizeResultProto.", e); + return OptimizeResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static GetOptimizeInfoResultProto byteArrayToGetOptimizeInfoResultProto( + @Nullable byte[] getOptimizeInfoResultBytes) { + if (getOptimizeInfoResultBytes == null) { + Log.e(TAG, "Received null GetOptimizeInfoResultProto from native."); + return GetOptimizeInfoResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return GetOptimizeInfoResultProto.parseFrom( + getOptimizeInfoResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing GetOptimizeInfoResultProto.", e); + return GetOptimizeInfoResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static StorageInfoResultProto byteArrayToStorageInfoResultProto( + @Nullable byte[] storageInfoResultProtoBytes) { + if (storageInfoResultProtoBytes == null) { + Log.e(TAG, "Received null StorageInfoResultProto from native."); + return StorageInfoResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return StorageInfoResultProto.parseFrom(storageInfoResultProtoBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing GetOptimizeInfoResultProto.", e); + return StorageInfoResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static DebugInfoResultProto byteArrayToDebugInfoResultProto( + @Nullable byte[] debugInfoResultProtoBytes) { + if (debugInfoResultProtoBytes == null) { + Log.e(TAG, "Received null DebugInfoResultProto from native."); + return DebugInfoResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return DebugInfoResultProto.parseFrom(debugInfoResultProtoBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing DebugInfoResultProto.", e); + return DebugInfoResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } + + @NonNull + public static ResetResultProto byteArrayToResetResultProto(@Nullable byte[] resetResultBytes) { + if (resetResultBytes == null) { + Log.e(TAG, "Received null ResetResultProto from native."); + return ResetResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + + try { + return ResetResultProto.parseFrom(resetResultBytes, EXTENSION_REGISTRY_LITE); + } catch (InvalidProtocolBufferException e) { + Log.e(TAG, "Error parsing ResetResultProto.", e); + return ResetResultProto.newBuilder() + .setStatus(StatusProto.newBuilder().setCode(StatusProto.Code.INTERNAL)) + .build(); + } + } +} |