Loading apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +34 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ import android.os.Bundle; import android.os.ParcelableException; import android.os.RemoteException; import android.util.ArraySet; import android.util.Log; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.List; Loading @@ -39,10 +42,13 @@ import java.util.function.Consumer; * This class is thread safe. */ public final class AppSearchSession { private static final String TAG = "AppSearchSession"; private final String mDatabaseName; @UserIdInt private final int mUserId; private final IAppSearchManager mService; private boolean mIsMutated = false; private boolean mIsClosed = false; static void createSearchSession( @NonNull AppSearchManager.SearchContext searchContext, Loading Loading @@ -150,6 +156,7 @@ public final class AppSearchSession { Objects.requireNonNull(request); Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); List<Bundle> schemaBundles = new ArrayList<>(request.getSchemas().size()); for (AppSearchSchema schema : request.getSchemas()) { schemaBundles.add(schema.getBundle()); Loading @@ -166,6 +173,7 @@ public final class AppSearchSession { executor.execute(() -> callback.accept(result)); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading @@ -182,6 +190,7 @@ public final class AppSearchSession { @NonNull Consumer<AppSearchResult<Set<AppSearchSchema>>> callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { mService.getSchema( mDatabaseName, Loading Loading @@ -232,6 +241,7 @@ public final class AppSearchSession { Objects.requireNonNull(request); Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); List<GenericDocument> documents = request.getDocuments(); List<Bundle> documentBundles = new ArrayList<>(documents.size()); for (int i = 0; i < documents.size(); i++) { Loading @@ -248,6 +258,7 @@ public final class AppSearchSession { executor.execute(() -> callback.onSystemError(exception.getCause())); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading Loading @@ -275,6 +286,7 @@ public final class AppSearchSession { Objects.requireNonNull(request); Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { mService.getDocuments(mDatabaseName, request.getNamespace(), new ArrayList<>(request.getUris()), mUserId, Loading Loading @@ -379,6 +391,7 @@ public final class AppSearchSession { Objects.requireNonNull(queryExpression); Objects.requireNonNull(searchSpec); Objects.requireNonNull(executor); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); return new SearchResults(mService, mDatabaseName, queryExpression, searchSpec, mUserId, executor); } Loading @@ -404,6 +417,7 @@ public final class AppSearchSession { Objects.requireNonNull(request); Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { mService.removeByUri(mDatabaseName, request.getNamespace(), new ArrayList<>(request.getUris()), mUserId, Loading @@ -416,6 +430,7 @@ public final class AppSearchSession { executor.execute(() -> callback.onSystemError(exception.getCause())); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading Loading @@ -448,6 +463,7 @@ public final class AppSearchSession { Objects.requireNonNull(searchSpec); Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { mService.removeByQuery(mDatabaseName, queryExpression, searchSpec.getBundle(), mUserId, new IAppSearchResultCallback.Stub() { Loading @@ -455,8 +471,26 @@ public final class AppSearchSession { executor.execute(() -> callback.accept(result)); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Closes the SearchSessionImpl to persists all update/delete requests to the disk. * * @hide */ // TODO(b/175637134) when unhide it, implement Closeable and remove this method. public void close() { if (mIsMutated && !mIsClosed) { try { mService.persistToDisk(mUserId); mIsClosed = true; } catch (RemoteException e) { Log.e(TAG, "Unable to close the AppSearchSession", e); } } } } apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +7 −0 Original line number Diff line number Diff line Loading @@ -192,6 +192,13 @@ interface IAppSearchManager { in int userId, in IAppSearchResultCallback callback); /** * Persists all update/delete requests to the disk. * * @param userId Id of the calling user */ void persistToDisk(in int userId); /** * Creates and initializes AppSearchImpl for the calling app. * Loading apex/appsearch/framework/java/android/app/appsearch/SearchResults.java +12 −4 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import android.os.Bundle; import android.os.RemoteException; import android.util.Log; import com.android.internal.util.Preconditions; import java.io.Closeable; import java.util.List; import java.util.Objects; Loading Loading @@ -62,6 +64,8 @@ public class SearchResults implements Closeable { private boolean mIsFirstLoad = true; private boolean mIsClosed = false; SearchResults( @NonNull IAppSearchManager service, @Nullable String databaseName, Loading @@ -88,6 +92,7 @@ public class SearchResults implements Closeable { * @param callback Callback to receive the pending result of performing this operation. */ public void getNextPage(@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) { Preconditions.checkState(!mIsClosed, "SearchResults has already been closed"); try { if (mIsFirstLoad) { mIsFirstLoad = false; Loading @@ -111,10 +116,13 @@ public class SearchResults implements Closeable { @Override public void close() { if (!mIsClosed) { try { mService.invalidateNextPageToken(mNextPageToken, mUserId); mIsClosed = true; } catch (RemoteException e) { Log.d(TAG, "Unable to close the SearchResults", e); Log.e(TAG, "Unable to close the SearchResults", e); } } } Loading apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +20 −5 Original line number Diff line number Diff line Loading @@ -279,7 +279,7 @@ public class AppSearchManagerService extends SystemService { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); impl.invalidateNextPageToken(nextPageToken); } catch (Throwable t) { Log.d(TAG, "Unable to invalidate the query page token", t); Log.e(TAG, "Unable to invalidate the query page token", t); } finally { Binder.restoreCallingIdentity(callingIdentity); } Loading Loading @@ -347,6 +347,21 @@ public class AppSearchManagerService extends SystemService { } } @Override public void persistToDisk(@UserIdInt int userId) { int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); impl.persistToDisk(); } catch (Throwable t) { Log.e(TAG, "Unable to persist the data to disk", t); } finally { Binder.restoreCallingIdentity(callingIdentity); } } @Override public void initialize(@UserIdInt int userId, @NonNull IAppSearchResultCallback callback) { Preconditions.checkNotNull(callback); Loading Loading @@ -388,7 +403,7 @@ public class AppSearchManagerService extends SystemService { try { callback.onResult(result); } catch (RemoteException e) { Log.d(TAG, "Unable to send result to the callback", e); Log.e(TAG, "Unable to send result to the callback", e); } } Loading @@ -398,7 +413,7 @@ public class AppSearchManagerService extends SystemService { try { callback.onResult(result); } catch (RemoteException e) { Log.d(TAG, "Unable to send result to the callback", e); Log.e(TAG, "Unable to send result to the callback", e); } } Loading @@ -411,7 +426,7 @@ public class AppSearchManagerService extends SystemService { try { callback.onResult(throwableToFailedResult(throwable)); } catch (RemoteException e) { Log.d(TAG, "Unable to send result to the callback", e); Log.e(TAG, "Unable to send result to the callback", e); } } Loading @@ -425,7 +440,7 @@ public class AppSearchManagerService extends SystemService { try { callback.onSystemError(new ParcelableException(throwable)); } catch (RemoteException e) { Log.d(TAG, "Unable to send error to the callback", e); Log.e(TAG, "Unable to send error to the callback", e); } } } Loading apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +19 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import com.google.android.icing.proto.GetSchemaResultProto; import com.google.android.icing.proto.IcingSearchEngineOptions; 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.PropertyConfigProto; import com.google.android.icing.proto.PropertyProto; import com.google.android.icing.proto.PutResultProto; Loading Loading @@ -630,6 +631,24 @@ public final class AppSearchImpl { deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND); } /** * Persists all update/delete requests to the disk. * * <p>If the app crashes after a call to PersistToDisk(), Icing would be able to fully recover * all data written up to this point without a costly recovery process. * * <p>If the app crashes before a call to PersistToDisk(), Icing would trigger a costly recovery * process in next initialization. After that, Icing would still be able to recover all written * data. * * @throws AppSearchException */ public void persistToDisk() throws AppSearchException { PersistToDiskResultProto persistToDiskResultProto = mIcingSearchEngineLocked.persistToDisk(); checkSuccess(persistToDiskResultProto.getStatus()); } /** * Clears documents and schema across all packages and databaseNames. * Loading Loading
apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +34 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,9 @@ import android.os.Bundle; import android.os.ParcelableException; import android.os.RemoteException; import android.util.ArraySet; import android.util.Log; import com.android.internal.util.Preconditions; import java.util.ArrayList; import java.util.List; Loading @@ -39,10 +42,13 @@ import java.util.function.Consumer; * This class is thread safe. */ public final class AppSearchSession { private static final String TAG = "AppSearchSession"; private final String mDatabaseName; @UserIdInt private final int mUserId; private final IAppSearchManager mService; private boolean mIsMutated = false; private boolean mIsClosed = false; static void createSearchSession( @NonNull AppSearchManager.SearchContext searchContext, Loading Loading @@ -150,6 +156,7 @@ public final class AppSearchSession { Objects.requireNonNull(request); Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); List<Bundle> schemaBundles = new ArrayList<>(request.getSchemas().size()); for (AppSearchSchema schema : request.getSchemas()) { schemaBundles.add(schema.getBundle()); Loading @@ -166,6 +173,7 @@ public final class AppSearchSession { executor.execute(() -> callback.accept(result)); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading @@ -182,6 +190,7 @@ public final class AppSearchSession { @NonNull Consumer<AppSearchResult<Set<AppSearchSchema>>> callback) { Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { mService.getSchema( mDatabaseName, Loading Loading @@ -232,6 +241,7 @@ public final class AppSearchSession { Objects.requireNonNull(request); Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); List<GenericDocument> documents = request.getDocuments(); List<Bundle> documentBundles = new ArrayList<>(documents.size()); for (int i = 0; i < documents.size(); i++) { Loading @@ -248,6 +258,7 @@ public final class AppSearchSession { executor.execute(() -> callback.onSystemError(exception.getCause())); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading Loading @@ -275,6 +286,7 @@ public final class AppSearchSession { Objects.requireNonNull(request); Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { mService.getDocuments(mDatabaseName, request.getNamespace(), new ArrayList<>(request.getUris()), mUserId, Loading Loading @@ -379,6 +391,7 @@ public final class AppSearchSession { Objects.requireNonNull(queryExpression); Objects.requireNonNull(searchSpec); Objects.requireNonNull(executor); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); return new SearchResults(mService, mDatabaseName, queryExpression, searchSpec, mUserId, executor); } Loading @@ -404,6 +417,7 @@ public final class AppSearchSession { Objects.requireNonNull(request); Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { mService.removeByUri(mDatabaseName, request.getNamespace(), new ArrayList<>(request.getUris()), mUserId, Loading @@ -416,6 +430,7 @@ public final class AppSearchSession { executor.execute(() -> callback.onSystemError(exception.getCause())); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } Loading Loading @@ -448,6 +463,7 @@ public final class AppSearchSession { Objects.requireNonNull(searchSpec); Objects.requireNonNull(executor); Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { mService.removeByQuery(mDatabaseName, queryExpression, searchSpec.getBundle(), mUserId, new IAppSearchResultCallback.Stub() { Loading @@ -455,8 +471,26 @@ public final class AppSearchSession { executor.execute(() -> callback.accept(result)); } }); mIsMutated = true; } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } } /** * Closes the SearchSessionImpl to persists all update/delete requests to the disk. * * @hide */ // TODO(b/175637134) when unhide it, implement Closeable and remove this method. public void close() { if (mIsMutated && !mIsClosed) { try { mService.persistToDisk(mUserId); mIsClosed = true; } catch (RemoteException e) { Log.e(TAG, "Unable to close the AppSearchSession", e); } } } }
apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +7 −0 Original line number Diff line number Diff line Loading @@ -192,6 +192,13 @@ interface IAppSearchManager { in int userId, in IAppSearchResultCallback callback); /** * Persists all update/delete requests to the disk. * * @param userId Id of the calling user */ void persistToDisk(in int userId); /** * Creates and initializes AppSearchImpl for the calling app. * Loading
apex/appsearch/framework/java/android/app/appsearch/SearchResults.java +12 −4 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import android.os.Bundle; import android.os.RemoteException; import android.util.Log; import com.android.internal.util.Preconditions; import java.io.Closeable; import java.util.List; import java.util.Objects; Loading Loading @@ -62,6 +64,8 @@ public class SearchResults implements Closeable { private boolean mIsFirstLoad = true; private boolean mIsClosed = false; SearchResults( @NonNull IAppSearchManager service, @Nullable String databaseName, Loading @@ -88,6 +92,7 @@ public class SearchResults implements Closeable { * @param callback Callback to receive the pending result of performing this operation. */ public void getNextPage(@NonNull Consumer<AppSearchResult<List<SearchResult>>> callback) { Preconditions.checkState(!mIsClosed, "SearchResults has already been closed"); try { if (mIsFirstLoad) { mIsFirstLoad = false; Loading @@ -111,10 +116,13 @@ public class SearchResults implements Closeable { @Override public void close() { if (!mIsClosed) { try { mService.invalidateNextPageToken(mNextPageToken, mUserId); mIsClosed = true; } catch (RemoteException e) { Log.d(TAG, "Unable to close the SearchResults", e); Log.e(TAG, "Unable to close the SearchResults", e); } } } Loading
apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +20 −5 Original line number Diff line number Diff line Loading @@ -279,7 +279,7 @@ public class AppSearchManagerService extends SystemService { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); impl.invalidateNextPageToken(nextPageToken); } catch (Throwable t) { Log.d(TAG, "Unable to invalidate the query page token", t); Log.e(TAG, "Unable to invalidate the query page token", t); } finally { Binder.restoreCallingIdentity(callingIdentity); } Loading Loading @@ -347,6 +347,21 @@ public class AppSearchManagerService extends SystemService { } } @Override public void persistToDisk(@UserIdInt int userId) { int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); impl.persistToDisk(); } catch (Throwable t) { Log.e(TAG, "Unable to persist the data to disk", t); } finally { Binder.restoreCallingIdentity(callingIdentity); } } @Override public void initialize(@UserIdInt int userId, @NonNull IAppSearchResultCallback callback) { Preconditions.checkNotNull(callback); Loading Loading @@ -388,7 +403,7 @@ public class AppSearchManagerService extends SystemService { try { callback.onResult(result); } catch (RemoteException e) { Log.d(TAG, "Unable to send result to the callback", e); Log.e(TAG, "Unable to send result to the callback", e); } } Loading @@ -398,7 +413,7 @@ public class AppSearchManagerService extends SystemService { try { callback.onResult(result); } catch (RemoteException e) { Log.d(TAG, "Unable to send result to the callback", e); Log.e(TAG, "Unable to send result to the callback", e); } } Loading @@ -411,7 +426,7 @@ public class AppSearchManagerService extends SystemService { try { callback.onResult(throwableToFailedResult(throwable)); } catch (RemoteException e) { Log.d(TAG, "Unable to send result to the callback", e); Log.e(TAG, "Unable to send result to the callback", e); } } Loading @@ -425,7 +440,7 @@ public class AppSearchManagerService extends SystemService { try { callback.onSystemError(new ParcelableException(throwable)); } catch (RemoteException e) { Log.d(TAG, "Unable to send error to the callback", e); Log.e(TAG, "Unable to send error to the callback", e); } } } Loading
apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +19 −0 Original line number Diff line number Diff line Loading @@ -46,6 +46,7 @@ import com.google.android.icing.proto.GetSchemaResultProto; import com.google.android.icing.proto.IcingSearchEngineOptions; 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.PropertyConfigProto; import com.google.android.icing.proto.PropertyProto; import com.google.android.icing.proto.PutResultProto; Loading Loading @@ -630,6 +631,24 @@ public final class AppSearchImpl { deleteResultProto.getStatus(), StatusProto.Code.OK, StatusProto.Code.NOT_FOUND); } /** * Persists all update/delete requests to the disk. * * <p>If the app crashes after a call to PersistToDisk(), Icing would be able to fully recover * all data written up to this point without a costly recovery process. * * <p>If the app crashes before a call to PersistToDisk(), Icing would trigger a costly recovery * process in next initialization. After that, Icing would still be able to recover all written * data. * * @throws AppSearchException */ public void persistToDisk() throws AppSearchException { PersistToDiskResultProto persistToDiskResultProto = mIcingSearchEngineLocked.persistToDisk(); checkSuccess(persistToDiskResultProto.getStatus()); } /** * Clears documents and schema across all packages and databaseNames. * Loading