Update Framework from Jetpack.

Changes included:
#efb600e:Add Closeable to AppSearchSession, allow it persist data to disk.

Bug: 162450968
Test: presubmit
Change-Id: Ib00cf83e41f0e3e2f79b4c46d9a50407e72bd9bb
This commit is contained in:
Terry Wang
2020-12-16 19:59:08 -08:00
parent 397070ce13
commit 4aba0d8466
8 changed files with 115 additions and 11 deletions

View File

@@ -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;
@@ -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,
@@ -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());
@@ -166,6 +173,7 @@ public final class AppSearchSession {
executor.execute(() -> callback.accept(result));
}
});
mIsMutated = true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -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,
@@ -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++) {
@@ -248,6 +258,7 @@ public final class AppSearchSession {
executor.execute(() -> callback.onSystemError(exception.getCause()));
}
});
mIsMutated = true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -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,
@@ -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);
}
@@ -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,
@@ -416,6 +430,7 @@ public final class AppSearchSession {
executor.execute(() -> callback.onSystemError(exception.getCause()));
}
});
mIsMutated = true;
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
@@ -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() {
@@ -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);
}
}
}
}

View File

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

View File

@@ -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;
@@ -62,6 +64,8 @@ public class SearchResults implements Closeable {
private boolean mIsFirstLoad = true;
private boolean mIsClosed = false;
SearchResults(
@NonNull IAppSearchManager service,
@Nullable String databaseName,
@@ -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;
@@ -111,10 +116,13 @@ public class SearchResults implements Closeable {
@Override
public void close() {
try {
mService.invalidateNextPageToken(mNextPageToken, mUserId);
} catch (RemoteException e) {
Log.d(TAG, "Unable to close the SearchResults", e);
if (!mIsClosed) {
try {
mService.invalidateNextPageToken(mNextPageToken, mUserId);
mIsClosed = true;
} catch (RemoteException e) {
Log.e(TAG, "Unable to close the SearchResults", e);
}
}
}

View File

@@ -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);
}
@@ -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);
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
@@ -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);
}
}
}

View File

@@ -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;
@@ -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.
*

View File

@@ -1 +1 @@
Idd770a064edfeb6dc648571fc6706c087b8e605a
I596ad1269b4d3a4f26db67f5d970aeaa3bf94a9d

View File

@@ -58,10 +58,17 @@ public class AppSearchSessionShimImpl implements AppSearchSessionShim {
@NonNull
public static ListenableFuture<AppSearchSessionShim> createSearchSession(
@NonNull AppSearchManager.SearchContext searchContext) {
return createSearchSession(searchContext, Executors.newCachedThreadPool());
}
/** Creates the SearchSession with given ExecutorService. */
@NonNull
public static ListenableFuture<AppSearchSessionShim> createSearchSession(
@NonNull AppSearchManager.SearchContext searchContext,
@NonNull ExecutorService executor) {
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,
@@ -138,6 +145,11 @@ public class AppSearchSessionShimImpl implements AppSearchSessionShim {
return Futures.transformAsync(future, this::transformResult, mExecutor);
}
@Override
public void close() {
mAppSearchSession.close();
}
private <T> ListenableFuture<T> transformResult(
@NonNull AppSearchResult<T> result) throws AppSearchException {
if (!result.isSuccess()) {

View File

@@ -208,4 +208,13 @@ public interface AppSearchSessionShim {
@NonNull
ListenableFuture<Void> removeByQuery(
@NonNull String queryExpression, @NonNull SearchSpec searchSpec);
/**
* Closes the SearchSessionImpl to persists all update/delete requests to the disk.
*
* @hide
*/
// TODO(b/175637134) when unhide it, extends Closeable and remove this method.
void close();
}