diff options
Diffstat (limited to 'apex')
13 files changed, 385 insertions, 352 deletions
diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index 18bc59b91aa0..f2c9942edbb3 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -267,14 +267,14 @@ public class AppSearchManager { searchResultsFuture.completeExceptionally(e); } - // Translate the list of Bundle into a list of SearchResult - AppSearchResult<SearchResults> searchResultsResult = getFutureOrThrow(searchResultsFuture); - if (!searchResultsResult.isSuccess()) { - return AppSearchResult.newFailedResult( - searchResultsResult.getResultCode(), searchResultsResult.getErrorMessage()); + // Translate the Bundle into a searchResultPage. + AppSearchResult<Bundle> bundleResult = getFutureOrThrow(searchResultsFuture); + if (!bundleResult.isSuccess()) { + return AppSearchResult.newFailedResult(bundleResult.getResultCode(), + bundleResult.getErrorMessage()); } - SearchResults searchResults = searchResultsResult.getResultValue(); - return AppSearchResult.newSuccessfulResult(searchResults.mResults); + SearchResultPage searchResultPage = new SearchResultPage(bundleResult.getResultValue()); + return AppSearchResult.newSuccessfulResult(searchResultPage.getResults()); } /** @@ -294,39 +294,7 @@ public class AppSearchManager { List<String> uris = new ArrayList<>(request.getUris()); AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>(); try { - mService.delete(DEFAULT_DATABASE, request.getNamespace(), uris, future); - } catch (RemoteException e) { - future.completeExceptionally(e); - } - return getFutureOrThrow(future); - } - - /** - * Deletes {@link android.app.appsearch.GenericDocument}s by schema type. - * - * <p>You should not call this method directly; instead, use the - * {@code AppSearch#deleteByType()} API provided by JetPack. - * - * @param schemaTypes Schema types whose documents to delete. - * @return An {@link AppSearchBatchResult} mapping each schema type to a {@code null} success if - * deletion was successful, to a {@code null} failure if the type did not exist, or to a - * {@code throwable} failure if deletion failed for another reason. - */ - public AppSearchBatchResult<String, Void> deleteByTypes(@NonNull List<String> schemaTypes) { - AndroidFuture<AppSearchBatchResult> future = new AndroidFuture<>(); - try { - mService.deleteByTypes(DEFAULT_DATABASE, schemaTypes, future); - } catch (RemoteException e) { - future.completeExceptionally(e); - } - return getFutureOrThrow(future); - } - - /** Deletes all documents owned by the calling app. */ - public AppSearchResult<Void> deleteAll() { - AndroidFuture<AppSearchResult> future = new AndroidFuture<>(); - try { - mService.deleteAll(DEFAULT_DATABASE, future); + mService.removeByUri(DEFAULT_DATABASE, request.getNamespace(), uris, future); } catch (RemoteException e) { future.completeExceptionally(e); } diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl index 352a980eef27..01260ea193f6 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl @@ -92,7 +92,7 @@ interface IAppSearchManager { in AndroidFuture<AppSearchResult> callback); /** - * Deletes documents by URI. + * Removes documents by URI. * * @param databaseName The databaseName the document is in. * @param namespace Namespace of the document to remove. @@ -105,36 +105,23 @@ interface IAppSearchManager { * where the keys are document URIs. If a document doesn't exist, it will be reported as a * failure where the {@code throwable} is {@code null}. */ - void delete( + void removeByUri( in String databaseName, in String namespace, in List<String> uris, in AndroidFuture<AppSearchBatchResult> callback); /** - * Deletes documents by schema type. + * Removes documents by given query. * - * @param databaseName The databaseName the document is in. - * @param schemaTypes The schema types of the documents to delete - * @param callback - * {@link AndroidFuture}<{@link AppSearchBatchResult}<{@link String}, {@link Void}>>. - * If the call fails to start, {@code callback} will be completed exceptionally. Otherwise, - * {@code callback} will be completed with an - * {@link AppSearchBatchResult}<{@link String}, {@link Void}> - * where the keys are schema types. If a schema type doesn't exist, it will be reported as a - * failure where the {@code throwable} is {@code null}. + * @param databaseName The databaseName this query for. + * @param queryExpression String to search for + * @param searchSpecBundle SearchSpec bundle + * @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link SearchResults}>> */ - void deleteByTypes( + void removeByQuery( in String databaseName, - in List<String> schemaTypes, - in AndroidFuture<AppSearchBatchResult> callback); - - /** - * Deletes all documents belonging to the calling app. - * - * @param databaseName The databaseName to remove all documents from. - * @param callback {@link AndroidFuture}<{@link AppSearchResult}<{@link Void}>>. - * Will be completed with the result of the call. - */ - void deleteAll(in String databaseName, in AndroidFuture<AppSearchResult> callback); + in String queryExpression, + in Bundle searchSpecBundle, + in AndroidFuture<AppSearchResult> callback); } diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java new file mode 100644 index 000000000000..756d1b5d673f --- /dev/null +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResultPage.java @@ -0,0 +1,78 @@ +/* + * Copyright 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.app.appsearch; + +import android.os.Bundle; + +import android.annotation.NonNull; +import android.annotation.Nullable; + +import com.android.internal.util.Preconditions; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * This class represents a page of {@link SearchResult}s + * @hide + */ + +public class SearchResultPage { + public static final String RESULTS_FIELD = "results"; + public static final String NEXT_PAGE_TOKEN_FIELD = "nextPageToken"; + private final long mNextPageToken; + + @Nullable + private List<SearchResult> mResults; + + @NonNull + private final Bundle mBundle; + + public SearchResultPage(@NonNull Bundle bundle) { + mBundle = Preconditions.checkNotNull(bundle); + mNextPageToken = mBundle.getLong(NEXT_PAGE_TOKEN_FIELD); + } + + /** Returns the {@link Bundle} of this class. */ + @NonNull + public Bundle getBundle() { + return mBundle; + } + + /** Returns the Token to get next {@link SearchResultPage}. */ + public long getNextPageToken() { + return mNextPageToken; + } + + /** Returns all {@link android.app.appsearch.SearchResult}s of this page */ + @NonNull + public List<SearchResult> getResults() { + if (mResults == null) { + ArrayList<Bundle> resultBundles = mBundle.getParcelableArrayList(RESULTS_FIELD); + if (resultBundles == null) { + mResults = Collections.emptyList(); + } else { + mResults = new ArrayList<>(resultBundles.size()); + for (int i = 0; i < resultBundles.size(); i++) { + mResults.add(new SearchResult(resultBundles.get(i))); + } + } + } + return mResults; + } +} diff --git a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java index f2830e5b8e6d..1dfde528e69b 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -21,33 +21,23 @@ import android.app.appsearch.AppSearchResult; import android.app.appsearch.AppSearchSchema; import android.app.appsearch.GenericDocument; import android.app.appsearch.IAppSearchManager; -import android.app.appsearch.SearchResult; -import android.app.appsearch.SearchResults; +import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; import android.app.appsearch.exceptions.AppSearchException; import android.content.Context; import android.os.Binder; import android.os.Bundle; import android.os.UserHandle; +import android.util.ArraySet; import com.android.internal.infra.AndroidFuture; import com.android.internal.util.Preconditions; import com.android.server.SystemService; import com.android.server.appsearch.external.localstorage.AppSearchImpl; -import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; -import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter; -import com.android.server.appsearch.external.localstorage.converter.SearchResultToProtoConverter; -import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter; - -import com.google.android.icing.proto.DocumentProto; -import com.google.android.icing.proto.SchemaProto; -import com.google.android.icing.proto.SchemaTypeConfigProto; -import com.google.android.icing.proto.SearchResultProto; -import com.google.android.icing.proto.SearchSpecProto; import java.io.IOException; -import java.util.ArrayList; import java.util.List; +import java.util.Set; /** * TODO(b/142567528): add comments when implement this class @@ -72,21 +62,20 @@ public class AppSearchManagerService extends SystemService { @NonNull List<Bundle> schemaBundles, boolean forceOverride, @NonNull AndroidFuture<AppSearchResult> callback) { + Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(schemaBundles); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - SchemaProto.Builder schemaProtoBuilder = SchemaProto.newBuilder(); + Set<AppSearchSchema> schemas = new ArraySet<>(schemaBundles.size()); for (int i = 0; i < schemaBundles.size(); i++) { - AppSearchSchema schema = new AppSearchSchema(schemaBundles.get(i)); - SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema); - schemaProtoBuilder.addTypes(schemaTypeProto); + schemas.add(new AppSearchSchema(schemaBundles.get(i))); } AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); - impl.setSchema(databaseName, schemaProtoBuilder.build(), forceOverride); + impl.setSchema(databaseName, schemas, forceOverride); callback.complete(AppSearchResult.newSuccessfulResult(/*result=*/ null)); } catch (Throwable t) { callback.complete(throwableToFailedResult(t)); @@ -100,6 +89,7 @@ public class AppSearchManagerService extends SystemService { @NonNull String databaseName, @NonNull List<Bundle> documentBundles, @NonNull AndroidFuture<AppSearchBatchResult> callback) { + Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(documentBundles); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); @@ -112,9 +102,8 @@ public class AppSearchManagerService extends SystemService { new AppSearchBatchResult.Builder<>(); for (int i = 0; i < documentBundles.size(); i++) { GenericDocument document = new GenericDocument(documentBundles.get(i)); - DocumentProto documentProto = GenericDocumentToProtoConverter.convert(document); try { - impl.putDocument(databaseName, documentProto); + impl.putDocument(databaseName, document); resultBuilder.setSuccess(document.getUri(), /*result=*/ null); } catch (Throwable t) { resultBuilder.setResult(document.getUri(), throwableToFailedResult(t)); @@ -131,6 +120,8 @@ public class AppSearchManagerService extends SystemService { @Override public void getDocuments(@NonNull String databaseName, @NonNull String namespace, @NonNull List<String> uris, @NonNull AndroidFuture<AppSearchBatchResult> callback) { + Preconditions.checkNotNull(databaseName); + Preconditions.checkNotNull(namespace); Preconditions.checkNotNull(uris); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); @@ -144,16 +135,8 @@ public class AppSearchManagerService extends SystemService { for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { - DocumentProto documentProto = impl.getDocument( - databaseName, namespace, uri); - if (documentProto == null) { - resultBuilder.setFailure( - uri, AppSearchResult.RESULT_NOT_FOUND, /*errorMessage=*/ null); - } else { - GenericDocument genericDocument = - GenericDocumentToProtoConverter.convert(documentProto); - resultBuilder.setSuccess(uri, genericDocument.getBundle()); - } + GenericDocument document = impl.getDocument(databaseName, namespace, uri); + resultBuilder.setSuccess(uri, document.getBundle()); } catch (Throwable t) { resultBuilder.setResult(uri, throwableToFailedResult(t)); } @@ -167,12 +150,14 @@ public class AppSearchManagerService extends SystemService { } // TODO(sidchhabra): Do this in a threadpool. + // TODO(b/162450968) handle pagination after getNextPage and SearchResults is ready. @Override public void query( @NonNull String databaseName, @NonNull String queryExpression, @NonNull Bundle searchSpecBundle, @NonNull AndroidFuture<AppSearchResult> callback) { + Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(queryExpression); Preconditions.checkNotNull(searchSpecBundle); Preconditions.checkNotNull(callback); @@ -180,29 +165,14 @@ public class AppSearchManagerService extends SystemService { int callingUserId = UserHandle.getUserId(callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { - SearchSpec searchSpec = new SearchSpec(searchSpecBundle); - SearchSpecProto searchSpecProto = - SearchSpecToProtoConverter.toSearchSpecProto(searchSpec); - searchSpecProto = searchSpecProto.toBuilder() - .setQuery(queryExpression).build(); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); - // TODO(adorokhine): handle pagination - SearchResultProto searchResultProto = impl.query( + SearchResultPage searchResultPage = impl.query( databaseName, - searchSpecProto, - SearchSpecToProtoConverter.toResultSpecProto(searchSpec), - SearchSpecToProtoConverter.toScoringSpecProto(searchSpec)); - List<SearchResult> searchResultList = - new ArrayList<>(searchResultProto.getResultsCount()); - for (int i = 0; i < searchResultProto.getResultsCount(); i++) { - SearchResult result = SearchResultToProtoConverter.convertSearchResult( - searchResultProto.getResults(i)); - searchResultList.add(result); - } - SearchResults searchResults = - new SearchResults(searchResultList, searchResultProto.getNextPageToken()); - callback.complete(AppSearchResult.newSuccessfulResult(searchResults)); + queryExpression, + new SearchSpec(searchSpecBundle)); + callback.complete( + AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); } catch (Throwable t) { callback.complete(throwableToFailedResult(t)); } finally { @@ -211,8 +181,10 @@ public class AppSearchManagerService extends SystemService { } @Override - public void delete(@NonNull String databaseName, @NonNull String namespace, + public void removeByUri(@NonNull String databaseName, @NonNull String namespace, List<String> uris, AndroidFuture<AppSearchBatchResult> callback) { + Preconditions.checkNotNull(databaseName); + Preconditions.checkNotNull(namespace); Preconditions.checkNotNull(uris); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); @@ -241,38 +213,14 @@ public class AppSearchManagerService extends SystemService { } @Override - public void deleteByTypes(@NonNull String databaseName, - List<String> schemaTypes, AndroidFuture<AppSearchBatchResult> callback) { - Preconditions.checkNotNull(schemaTypes); - Preconditions.checkNotNull(callback); - int callingUid = Binder.getCallingUidOrThrow(); - int callingUserId = UserHandle.getUserId(callingUid); - final long callingIdentity = Binder.clearCallingIdentity(); - try { - AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); - AppSearchBatchResult.Builder<String, Void> resultBuilder = - new AppSearchBatchResult.Builder<>(); - for (int i = 0; i < schemaTypes.size(); i++) { - String schemaType = schemaTypes.get(i); - try { - impl.removeByType(databaseName, schemaType); - resultBuilder.setSuccess(schemaType, /*result=*/ null); - } catch (Throwable t) { - resultBuilder.setResult(schemaType, throwableToFailedResult(t)); - } - } - callback.complete(resultBuilder.build()); - } catch (Throwable t) { - callback.completeExceptionally(t); - } finally { - Binder.restoreCallingIdentity(callingIdentity); - } - } - - @Override - public void deleteAll(@NonNull String databaseName, + public void removeByQuery( + @NonNull String databaseName, + @NonNull String queryExpression, + @NonNull Bundle searchSpecBundle, @NonNull AndroidFuture<AppSearchResult> callback) { + Preconditions.checkNotNull(databaseName); + Preconditions.checkNotNull(queryExpression); + Preconditions.checkNotNull(searchSpecBundle); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = UserHandle.getUserId(callingUid); @@ -280,8 +228,8 @@ public class AppSearchManagerService extends SystemService { try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); databaseName = rewriteDatabaseNameWithUid(databaseName, callingUid); - impl.removeAll(databaseName); - callback.complete(AppSearchResult.newSuccessfulResult(null)); + impl.removeByQuery(databaseName, queryExpression, new SearchSpec(searchSpecBundle)); + callback.complete(AppSearchResult.newSuccessfulResult(/*result= */null)); } catch (Throwable t) { callback.complete(throwableToFailedResult(t)); } finally { diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java index b1a79f84714d..e021544976b5 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/AppSearchImpl.java @@ -16,21 +16,27 @@ package com.android.server.appsearch.external.localstorage; +import android.os.Bundle; import android.util.Log; import com.android.internal.annotations.GuardedBy; import android.annotation.NonNull; -import android.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import android.annotation.WorkerThread; import android.app.appsearch.AppSearchResult; +import android.app.appsearch.AppSearchSchema; +import android.app.appsearch.GenericDocument; +import android.app.appsearch.SearchResultPage; +import android.app.appsearch.SearchSpec; import android.app.appsearch.exceptions.AppSearchException; import com.android.internal.util.Preconditions; +import com.android.server.appsearch.external.localstorage.converter.GenericDocumentToProtoConverter; +import com.android.server.appsearch.external.localstorage.converter.SchemaToProtoConverter; +import com.android.server.appsearch.external.localstorage.converter.SearchResultToProtoConverter; +import com.android.server.appsearch.external.localstorage.converter.SearchSpecToProtoConverter; import com.google.android.icing.IcingSearchEngine; -import com.google.android.icing.proto.DeleteByNamespaceResultProto; -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; @@ -54,8 +60,10 @@ import com.google.android.icing.proto.SetSchemaResultProto; import com.google.android.icing.proto.StatusProto; import java.io.File; +import java.util.Collections; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.locks.ReadWriteLock; @@ -89,12 +97,14 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; * </ul> * * <p>This class is thread safe. + * * @hide */ @WorkerThread public final class AppSearchImpl { private static final String TAG = "AppSearchImpl"; + private static final char DATABASE_DELIMITER = '/'; @VisibleForTesting static final int OPTIMIZE_THRESHOLD_DOC_COUNT = 1000; @@ -173,20 +183,27 @@ public final class AppSearchImpl { * <p>This method belongs to mutate group. * * @param databaseName The name of the database where this schema lives. - * @param origSchema The schema to set for this app. + * @param schemas Schemas to set for this app. * @param forceOverride Whether to force-apply the schema even if it is incompatible. Documents * which do not comply with the new schema will be deleted. * @throws AppSearchException on IcingSearchEngine error. */ - public void setSchema(@NonNull String databaseName, @NonNull SchemaProto origSchema, + public void setSchema(@NonNull String databaseName, @NonNull Set<AppSearchSchema> schemas, boolean forceOverride) throws AppSearchException { SchemaProto schemaProto = getSchemaProto(); SchemaProto.Builder existingSchemaBuilder = schemaProto.toBuilder(); + SchemaProto.Builder newSchemaBuilder = SchemaProto.newBuilder(); + for (AppSearchSchema schema : schemas) { + SchemaTypeConfigProto schemaTypeProto = SchemaToProtoConverter.convert(schema); + newSchemaBuilder.addTypes(schemaTypeProto); + } + // Combine the existing schema (which may have types from other databases) with this // database's new schema. Modifies the existingSchemaBuilder. - Set<String> newTypeNames = rewriteSchema(databaseName, existingSchemaBuilder, origSchema); + Set<String> newTypeNames = rewriteSchema(databaseName, existingSchemaBuilder, + newSchemaBuilder.build()); SetSchemaResultProto setSchemaResultProto; mReadWriteLock.writeLock().lock(); @@ -220,7 +237,7 @@ public final class AppSearchImpl { if (setSchemaResultProto.getDeletedSchemaTypesCount() > 0 || (setSchemaResultProto.getIncompatibleSchemaTypesCount() > 0 && forceOverride)) { - // Any existing schemas which is not in origSchema will be deleted, and all + // Any existing schemas which is not in 'schemas' will be deleted, and all // documents of these types were also deleted. And so well if we force override // incompatible schemas. checkForOptimize(/* force= */true); @@ -239,10 +256,11 @@ public final class AppSearchImpl { * @param document The document to index. * @throws AppSearchException on IcingSearchEngine error. */ - public void putDocument(@NonNull String databaseName, @NonNull DocumentProto document) + public void putDocument(@NonNull String databaseName, @NonNull GenericDocument document) throws AppSearchException { - DocumentProto.Builder documentBuilder = document.toBuilder(); - rewriteDocumentTypes(getDatabasePrefix(databaseName), documentBuilder, /*add=*/ true); + DocumentProto.Builder documentBuilder = GenericDocumentToProtoConverter.convert( + document).toBuilder(); + addPrefixToDocument(documentBuilder, getDatabasePrefix(databaseName)); PutResultProto putResultProto; mReadWriteLock.writeLock().lock(); @@ -266,11 +284,11 @@ public final class AppSearchImpl { * @param databaseName The databaseName this document resides in. * @param namespace The namespace this document resides in. * @param uri The URI of the document to get. - * @return The Document contents, or {@code null} if no such URI exists in the system. + * @return The Document contents * @throws AppSearchException on IcingSearchEngine error. */ - @Nullable - public DocumentProto getDocument(@NonNull String databaseName, @NonNull String namespace, + @NonNull + public GenericDocument getDocument(@NonNull String databaseName, @NonNull String namespace, @NonNull String uri) throws AppSearchException { GetResultProto getResultProto; mReadWriteLock.readLock().lock(); @@ -283,8 +301,8 @@ public final class AppSearchImpl { checkSuccess(getResultProto.getStatus()); DocumentProto.Builder documentBuilder = getResultProto.getDocument().toBuilder(); - rewriteDocumentTypes(getDatabasePrefix(databaseName), documentBuilder, /*add=*/ false); - return documentBuilder.build(); + removeDatabasesFromDocument(documentBuilder); + return GenericDocumentToProtoConverter.convert(documentBuilder.build()); } /** @@ -292,33 +310,60 @@ public final class AppSearchImpl { * * <p>This method belongs to query group. * - * @param databaseName The databaseName this query for. - * @param searchSpec Defines what and how to search - * @param resultSpec Defines what results to show - * @param scoringSpec Defines how to order results - * @return The results of performing this search The proto might have no {@code results} if no - * documents matched the query. + * @param databaseName The databaseName this query for. + * @param queryExpression Query String to search. + * @param searchSpec Spec for setting filters, raw query etc. + * @return The results of performing this search. It may contain an empty list of results if + * no documents matched the query. * @throws AppSearchException on IcingSearchEngine error. */ @NonNull - public SearchResultProto query( + public SearchResultPage query( @NonNull String databaseName, - @NonNull SearchSpecProto searchSpec, - @NonNull ResultSpecProto resultSpec, - @NonNull ScoringSpecProto scoringSpec) throws AppSearchException { - SearchSpecProto.Builder searchSpecBuilder = searchSpec.toBuilder(); + @NonNull String queryExpression, + @NonNull SearchSpec searchSpec) throws AppSearchException { + return doQuery(Collections.singleton(databaseName), queryExpression, searchSpec); + } + + /** + * Executes a global query, i.e. over all permitted databases, against the AppSearch index and + * returns results. + * + * <p>This method belongs to query group. + * + * @param queryExpression Query String to search. + * @param searchSpec Spec for setting filters, raw query etc. + * @return The results of performing this search. It may contain an empty list of results if + * no documents matched the query. + * @throws AppSearchException on IcingSearchEngine error. + */ + @NonNull + public SearchResultPage globalQuery( + @NonNull String queryExpression, + @NonNull SearchSpec searchSpec) throws AppSearchException { + return doQuery(mNamespaceMap.keySet(), queryExpression, searchSpec); + } + + private SearchResultPage doQuery( + @NonNull Set<String> databases, @NonNull String queryExpression, + @NonNull SearchSpec searchSpec) + throws AppSearchException { + SearchSpecProto searchSpecProto = + SearchSpecToProtoConverter.toSearchSpecProto(searchSpec); + SearchSpecProto.Builder searchSpecBuilder = searchSpecProto.toBuilder() + .setQuery(queryExpression); + + ResultSpecProto resultSpec = SearchSpecToProtoConverter.toResultSpecProto(searchSpec); + ScoringSpecProto scoringSpec = SearchSpecToProtoConverter.toScoringSpecProto(searchSpec); SearchResultProto searchResultProto; mReadWriteLock.readLock().lock(); try { - // Only rewrite SearchSpec for non empty database. - // rewriteSearchSpecForNonEmptyDatabase will return false for empty database, we - // should just return an empty SearchResult and skip sending request to Icing. - if (!rewriteSearchSpecForNonEmptyDatabase(databaseName, searchSpecBuilder)) { - return SearchResultProto.newBuilder() - .setStatus(StatusProto.newBuilder() - .setCode(StatusProto.Code.OK) - .build()) - .build(); + // rewriteSearchSpecForDatabases will return false if none of the databases have + // documents, so we can return an empty SearchResult and skip sending request to Icing. + // We use the mNamespaceMap.keySet here because it's the smaller set of valid databases + // that could exist. + if (!rewriteSearchSpecForDatabases(searchSpecBuilder, databases)) { + return new SearchResultPage(Bundle.EMPTY); } searchResultProto = mIcingSearchEngine.search( searchSpecBuilder.build(), scoringSpec, resultSpec); @@ -326,34 +371,32 @@ public final class AppSearchImpl { mReadWriteLock.readLock().unlock(); } checkSuccess(searchResultProto.getStatus()); - if (searchResultProto.getResultsCount() == 0) { - return searchResultProto; - } - return rewriteSearchResultProto(databaseName, searchResultProto); + return rewriteSearchResultProto(searchResultProto); } /** * Fetches the next page of results of a previously executed query. Results can be empty if * next-page token is invalid or all pages have been returned. * - * @param databaseName The databaseName of the previously executed query. + * <p>This method belongs to query group. + * * @param nextPageToken The token of pre-loaded results of previously executed query. * @return The next page of results of previously executed query. * @throws AppSearchException on IcingSearchEngine error. */ @NonNull - public SearchResultProto getNextPage(@NonNull String databaseName, long nextPageToken) + public SearchResultPage getNextPage(long nextPageToken) throws AppSearchException { SearchResultProto searchResultProto = mIcingSearchEngine.getNextPage(nextPageToken); checkSuccess(searchResultProto.getStatus()); - if (searchResultProto.getResultsCount() == 0) { - return searchResultProto; - } - return rewriteSearchResultProto(databaseName, searchResultProto); + return rewriteSearchResultProto(searchResultProto); } /** * Invalidates the next-page token so that no more results of the related query can be returned. + * + * <p>This method belongs to query group. + * * @param nextPageToken The token of pre-loaded results of previously executed query to be * Invalidated. */ @@ -386,93 +429,43 @@ public final class AppSearchImpl { } /** - * Removes all documents having the given {@code schemaType} in given database. + * Removes documents by given query. * * <p>This method belongs to mutate group. * - * @param databaseName The databaseName that contains documents of schemaType. - * @param schemaType The schemaType of documents to remove. + * @param databaseName The databaseName the document is in. + * @param queryExpression Query String to search. + * @param searchSpec Defines what and how to remove * @throws AppSearchException on IcingSearchEngine error. */ - public void removeByType(@NonNull String databaseName, @NonNull String schemaType) + public void removeByQuery(@NonNull String databaseName, @NonNull String queryExpression, + @NonNull SearchSpec searchSpec) throws AppSearchException { - String qualifiedType = getDatabasePrefix(databaseName) + schemaType; - DeleteBySchemaTypeResultProto deleteBySchemaTypeResultProto; - mReadWriteLock.writeLock().lock(); - try { - Set<String> existingSchemaTypes = mSchemaMap.get(databaseName); - if (existingSchemaTypes == null || !existingSchemaTypes.contains(qualifiedType)) { - return; - } - deleteBySchemaTypeResultProto = mIcingSearchEngine.deleteBySchemaType(qualifiedType); - checkForOptimize(/* force= */true); - } finally { - mReadWriteLock.writeLock().unlock(); - } - checkSuccess(deleteBySchemaTypeResultProto.getStatus()); - } - - /** - * Removes all documents having the given {@code namespace} in given database. - * - * <p>This method belongs to mutate group. - * - * @param databaseName The databaseName that contains documents of namespace. - * @param namespace The namespace of documents to remove. - * @throws AppSearchException on IcingSearchEngine error. - */ - public void removeByNamespace(@NonNull String databaseName, @NonNull String namespace) - throws AppSearchException { - String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace; - DeleteByNamespaceResultProto deleteByNamespaceResultProto; - mReadWriteLock.writeLock().lock(); - try { - Set<String> existingNamespaces = mNamespaceMap.get(databaseName); - if (existingNamespaces == null || !existingNamespaces.contains(qualifiedNamespace)) { - return; - } - deleteByNamespaceResultProto = mIcingSearchEngine.deleteByNamespace(qualifiedNamespace); - checkForOptimize(/* force= */true); - } finally { - mReadWriteLock.writeLock().unlock(); - } - checkSuccess(deleteByNamespaceResultProto.getStatus()); - } - /** - * Clears the given database by removing all documents and types. - * - * <p>The schemas will remain. To clear everything including schemas, please call - * {@link #setSchema} with an empty schema and {@code forceOverride} set to true. - * - * <p>This method belongs to mutate group. - * - * @param databaseName The databaseName to remove all documents from. - * @throws AppSearchException on IcingSearchEngine error. - */ - public void removeAll(@NonNull String databaseName) - throws AppSearchException { + SearchSpecProto searchSpecProto = + SearchSpecToProtoConverter.toSearchSpecProto(searchSpec); + SearchSpecProto.Builder searchSpecBuilder = searchSpecProto.toBuilder() + .setQuery(queryExpression); + DeleteResultProto deleteResultProto; mReadWriteLock.writeLock().lock(); try { - Set<String> existingNamespaces = mNamespaceMap.get(databaseName); - if (existingNamespaces == null) { + // Only rewrite SearchSpec for non empty database. + // rewriteSearchSpecForNonEmptyDatabase will return false for empty database, we + // should skip sending request to Icing and return in here. + if (!rewriteSearchSpecForDatabases(searchSpecBuilder, + Collections.singleton(databaseName))) { return; } - for (String namespace : existingNamespaces) { - DeleteByNamespaceResultProto deleteByNamespaceResultProto = - mIcingSearchEngine.deleteByNamespace(namespace); - // There's no way for AppSearch to know that all documents in a particular - // namespace have been deleted, but if you try to delete an empty namespace, Icing - // returns NOT_FOUND. Just ignore that code. - checkCodeOneOf( - deleteByNamespaceResultProto.getStatus(), - StatusProto.Code.OK, StatusProto.Code.NOT_FOUND); - } - mNamespaceMap.remove(databaseName); + deleteResultProto = mIcingSearchEngine.deleteByQuery( + searchSpecBuilder.build()); checkForOptimize(/* force= */true); } finally { mReadWriteLock.writeLock().unlock(); } + // It seems that the caller wants to get success if the data matching the query is not in + // the DB because it was not there or was successfully deleted. + checkCodeOneOf(deleteResultProto.getStatus(), + StatusProto.Code.OK, StatusProto.Code.NOT_FOUND); } /** @@ -565,36 +558,63 @@ public final class AppSearchImpl { } /** - * Rewrites all types and namespaces mentioned anywhere in {@code documentBuilder} to prepend - * or remove {@code prefix}. + * Prepends {@code prefix} to all types and namespaces mentioned anywhere in + * {@code documentBuilder}. * - * @param prefix The prefix to add or remove * @param documentBuilder The document to mutate - * @param add Whether to add prefix to the types and namespaces. If {@code false}, - * prefix will be removed. - * @throws IllegalStateException If {@code add=false} and the document has a type or namespace - * that doesn't start with {@code prefix}. + * @param prefix The prefix to add */ @VisibleForTesting - void rewriteDocumentTypes( - @NonNull String prefix, + void addPrefixToDocument( @NonNull DocumentProto.Builder documentBuilder, - boolean add) { + @NonNull String prefix) { // Rewrite the type name to include/remove the prefix. - String newSchema; - if (add) { - newSchema = prefix + documentBuilder.getSchema(); - } else { - newSchema = removePrefix(prefix, "schemaType", documentBuilder.getSchema()); - } + String newSchema = prefix + documentBuilder.getSchema(); documentBuilder.setSchema(newSchema); // Rewrite the namespace to include/remove the prefix. - if (add) { - documentBuilder.setNamespace(prefix + documentBuilder.getNamespace()); - } else { - documentBuilder.setNamespace( - removePrefix(prefix, "namespace", documentBuilder.getNamespace())); + documentBuilder.setNamespace(prefix + documentBuilder.getNamespace()); + + // Recurse into derived documents + for (int propertyIdx = 0; + propertyIdx < documentBuilder.getPropertiesCount(); + propertyIdx++) { + int documentCount = documentBuilder.getProperties(propertyIdx).getDocumentValuesCount(); + if (documentCount > 0) { + PropertyProto.Builder propertyBuilder = + documentBuilder.getProperties(propertyIdx).toBuilder(); + for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) { + DocumentProto.Builder derivedDocumentBuilder = + propertyBuilder.getDocumentValues(documentIdx).toBuilder(); + addPrefixToDocument(derivedDocumentBuilder, prefix); + propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder); + } + documentBuilder.setProperties(propertyIdx, propertyBuilder); + } + } + } + + /** + * Removes any database names from types and namespaces mentioned anywhere in + * {@code documentBuilder}. + * + * @param documentBuilder The document to mutate + */ + @VisibleForTesting + void removeDatabasesFromDocument(@NonNull DocumentProto.Builder documentBuilder) { + int delimiterIndex; + if ((delimiterIndex = documentBuilder.getSchema().indexOf(DATABASE_DELIMITER)) != -1) { + // Rewrite the type name to remove the prefix. + // Add 1 to include the char size of the DATABASE_DELIMITER + String newSchema = documentBuilder.getSchema().substring(delimiterIndex + 1); + documentBuilder.setSchema(newSchema); + } + + if ((delimiterIndex = documentBuilder.getNamespace().indexOf(DATABASE_DELIMITER)) != -1) { + // Rewrite the namespace to remove the prefix. + // Add 1 to include the char size of the DATABASE_DELIMITER + String newNamespace = documentBuilder.getNamespace().substring(delimiterIndex + 1); + documentBuilder.setNamespace(newNamespace); } // Recurse into derived documents @@ -608,7 +628,7 @@ public final class AppSearchImpl { for (int documentIdx = 0; documentIdx < documentCount; documentIdx++) { DocumentProto.Builder derivedDocumentBuilder = propertyBuilder.getDocumentValues(documentIdx).toBuilder(); - rewriteDocumentTypes(prefix, derivedDocumentBuilder, add); + removeDatabasesFromDocument(derivedDocumentBuilder); propertyBuilder.setDocumentValues(documentIdx, derivedDocumentBuilder); } documentBuilder.setProperties(propertyIdx, propertyBuilder); @@ -617,51 +637,68 @@ public final class AppSearchImpl { } /** - * Rewrites searchSpec by adding schemaTypeFilter and namespacesFilter + * Rewrites the schemaTypeFilters and namespacesFilters that exist in {@code databaseNames}. * - * <p>If user input empty filter lists, will look up {@link #mSchemaMap} and - * {@link #mNamespaceMap} and put all values belong to current database to narrow down Icing - * search area. + * <p>If the searchSpec has empty filter lists, all existing databases from + * {@code databaseNames} will be added. * <p>This method should be only called in query methods and get the READ lock to keep thread * safety. - * @return false if the current database is brand new and contains nothing. We should just - * return an empty query result to user. + * + * @return false if none of the requested databases exist. */ @VisibleForTesting @GuardedBy("mReadWriteLock") - boolean rewriteSearchSpecForNonEmptyDatabase(@NonNull String databaseName, - @NonNull SearchSpecProto.Builder searchSpecBuilder) { - Set<String> existingSchemaTypes = mSchemaMap.get(databaseName); - Set<String> existingNamespaces = mNamespaceMap.get(databaseName); - if (existingSchemaTypes == null || existingSchemaTypes.isEmpty() - || existingNamespaces == null || existingNamespaces.isEmpty()) { + boolean rewriteSearchSpecForDatabases( + @NonNull SearchSpecProto.Builder searchSpecBuilder, + @NonNull Set<String> databaseNames) { + // Create a copy since retainAll() modifies the original set. + Set<String> existingDatabases = new HashSet<>(mNamespaceMap.keySet()); + existingDatabases.retainAll(databaseNames); + + if (existingDatabases.isEmpty()) { + // None of the databases exist, empty query. return false; } - // Rewrite any existing schema types specified in the searchSpec, or add schema types to - // limit the search to this database instance. - if (searchSpecBuilder.getSchemaTypeFiltersCount() > 0) { - for (int i = 0; i < searchSpecBuilder.getSchemaTypeFiltersCount(); i++) { - String qualifiedType = getDatabasePrefix(databaseName) - + searchSpecBuilder.getSchemaTypeFilters(i); - if (existingSchemaTypes.contains(qualifiedType)) { - searchSpecBuilder.setSchemaTypeFilters(i, qualifiedType); + + // Cache the schema type filters and namespaces before clearing everything. + List<String> schemaTypeFilters = searchSpecBuilder.getSchemaTypeFiltersList(); + searchSpecBuilder.clearSchemaTypeFilters(); + + List<String> namespaceFilters = searchSpecBuilder.getNamespaceFiltersList(); + searchSpecBuilder.clearNamespaceFilters(); + + // Rewrite filters to include a database prefix. + for (String databaseName : existingDatabases) { + Set<String> existingSchemaTypes = mSchemaMap.get(databaseName); + if (schemaTypeFilters.isEmpty()) { + // Include all schema types + searchSpecBuilder.addAllSchemaTypeFilters(existingSchemaTypes); + } else { + // Qualify the given schema types + for (String schemaType : schemaTypeFilters) { + String qualifiedType = getDatabasePrefix(databaseName) + schemaType; + if (existingSchemaTypes.contains(qualifiedType)) { + searchSpecBuilder.addSchemaTypeFilters(qualifiedType); + } + } } - } else { - searchSpecBuilder.addAllSchemaTypeFilters(existingSchemaTypes); - } - // Rewrite any existing namespaces specified in the searchSpec, or add namespaces to - // limit the search to this database instance. - if (searchSpecBuilder.getNamespaceFiltersCount() > 0) { - for (int i = 0; i < searchSpecBuilder.getNamespaceFiltersCount(); i++) { - String qualifiedNamespace = getDatabasePrefix(databaseName) - + searchSpecBuilder.getNamespaceFilters(i); - searchSpecBuilder.setNamespaceFilters(i, qualifiedNamespace); + Set<String> existingNamespaces = mNamespaceMap.get(databaseName); + if (namespaceFilters.isEmpty()) { + // Include all namespaces + searchSpecBuilder.addAllNamespaceFilters(existingNamespaces); + } else { + // Qualify the given namespaces. + for (String namespace : namespaceFilters) { + String qualifiedNamespace = getDatabasePrefix(databaseName) + namespace; + if (existingNamespaces.contains(qualifiedNamespace)) { + searchSpecBuilder.addNamespaceFilters(qualifiedNamespace); + } + } } - } else { - searchSpecBuilder.addAllNamespaceFilters(existingNamespaces); } + return true; } @@ -676,12 +713,13 @@ public final class AppSearchImpl { @NonNull private String getDatabasePrefix(@NonNull String databaseName) { - return databaseName + "/"; + // TODO(b/170370381): Reconsider the way we separate database names for security reasons. + return databaseName + DATABASE_DELIMITER; } @NonNull private String getDatabaseName(@NonNull String prefixedValue) throws AppSearchException { - int delimiterIndex = prefixedValue.indexOf('/'); + int delimiterIndex = prefixedValue.indexOf(DATABASE_DELIMITER); if (delimiterIndex == -1) { throw new AppSearchException(AppSearchResult.RESULT_UNKNOWN_ERROR, "The databaseName prefixed value doesn't contains a valid database name."); @@ -689,17 +727,6 @@ public final class AppSearchImpl { return prefixedValue.substring(0, delimiterIndex); } - @NonNull - private static String removePrefix(@NonNull String prefix, @NonNull String inputType, - @NonNull String input) { - if (!input.startsWith(prefix)) { - throw new IllegalStateException( - "Unexpected " + inputType + " \"" + input - + "\" does not start with \"" + prefix + "\""); - } - return input.substring(prefix.length()); - } - @GuardedBy("mReadWriteLock") private void addToMap(Map<String, Set<String>> map, String databaseName, String prefixedValue) { Set<String> values = map.get(databaseName); @@ -733,7 +760,7 @@ public final class AppSearchImpl { } if (statusProto.getCode() == StatusProto.Code.WARNING_DATA_LOSS) { - // TODO: May want to propagate WARNING_DATA_LOSS up to AppSearchManager so they can + // TODO: May want to propagate WARNING_DATA_LOSS up to AppSearchSession so they can // choose to log the error or potentially pass it on to clients. Log.w(TAG, "Encountered WARNING_DATA_LOSS: " + statusProto.getMessage()); return; @@ -776,22 +803,21 @@ public final class AppSearchImpl { } } - /** Remove the rewritten schema types from any result documents.*/ - private SearchResultProto rewriteSearchResultProto(@NonNull String databaseName, + /** Remove the rewritten schema types from any result documents. */ + private SearchResultPage rewriteSearchResultProto( @NonNull SearchResultProto searchResultProto) { - SearchResultProto.Builder searchResultsBuilder = searchResultProto.toBuilder(); - for (int i = 0; i < searchResultsBuilder.getResultsCount(); i++) { + SearchResultProto.Builder resultsBuilder = searchResultProto.toBuilder(); + for (int i = 0; i < searchResultProto.getResultsCount(); i++) { if (searchResultProto.getResults(i).hasDocument()) { SearchResultProto.ResultProto.Builder resultBuilder = - searchResultsBuilder.getResults(i).toBuilder(); + searchResultProto.getResults(i).toBuilder(); DocumentProto.Builder documentBuilder = resultBuilder.getDocument().toBuilder(); - rewriteDocumentTypes( - getDatabasePrefix(databaseName), documentBuilder, /*add=*/false); + removeDatabasesFromDocument(documentBuilder); resultBuilder.setDocument(documentBuilder); - searchResultsBuilder.setResults(i, resultBuilder); + resultsBuilder.setResults(i, resultBuilder); } } - return searchResultsBuilder.build(); + return SearchResultToProtoConverter.convertToSearchResultPage(resultsBuilder); } @VisibleForTesting diff --git a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java index 9f7c6968e993..4310b4216266 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/external/localstorage/converter/SearchResultToProtoConverter.java @@ -22,8 +22,10 @@ import android.annotation.NonNull; import android.app.appsearch.GenericDocument; import android.app.appsearch.SearchResult; +import android.app.appsearch.SearchResultPage; import com.google.android.icing.proto.SearchResultProto; +import com.google.android.icing.proto.SearchResultProtoOrBuilder; import com.google.android.icing.proto.SnippetMatchProto; import com.google.android.icing.proto.SnippetProto; @@ -38,9 +40,24 @@ import java.util.ArrayList; public class SearchResultToProtoConverter { private SearchResultToProtoConverter() {} + + /** Translate a {@link SearchResultProto} into {@link SearchResultPage}. */ + @NonNull + public static SearchResultPage convertToSearchResultPage( + @NonNull SearchResultProtoOrBuilder proto) { + Bundle bundle = new Bundle(); + bundle.putLong(SearchResultPage.NEXT_PAGE_TOKEN_FIELD, proto.getNextPageToken()); + ArrayList<Bundle> resultBundles = new ArrayList<>(proto.getResultsCount()); + for (int i = 0; i < proto.getResultsCount(); i++) { + resultBundles.add(convertToSearchResultBundle(proto.getResults(i))); + } + bundle.putParcelableArrayList(SearchResultPage.RESULTS_FIELD, resultBundles); + return new SearchResultPage(bundle); + } + /** Translate a {@link SearchResultProto.ResultProto} into {@link SearchResult}. */ @NonNull - public static SearchResult convertSearchResult( + private static Bundle convertToSearchResultBundle( @NonNull SearchResultProto.ResultProtoOrBuilder proto) { Bundle bundle = new Bundle(); GenericDocument document = GenericDocumentToProtoConverter.convert(proto.getDocument()); @@ -59,7 +76,7 @@ public class SearchResultToProtoConverter { } bundle.putParcelableArrayList(SearchResult.MATCHES_FIELD, matchList); - return new SearchResult(bundle); + return bundle; } private static Bundle convertToMatchInfoBundle( diff --git a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java index 62c90dfa8a86..e251ff00a47d 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobParameters.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobParameters.java @@ -21,6 +21,7 @@ import android.annotation.Nullable; import android.compat.annotation.UnsupportedAppUsage; import android.content.ClipData; import android.net.Network; +import android.net.NetworkRequest; import android.net.Uri; import android.os.Bundle; import android.os.IBinder; @@ -242,8 +243,9 @@ public class JobParameters implements Parcelable { * * @return the network that should be used to perform any network requests * for this job, or {@code null} if this job didn't set any required - * network type. + * network type or if the job executed when there was no available network to use. * @see JobInfo.Builder#setRequiredNetworkType(int) + * @see JobInfo.Builder#setRequiredNetwork(NetworkRequest) */ public @Nullable Network getNetwork() { return network; diff --git a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java index bf5781beb52b..361325dae7fd 100644 --- a/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java +++ b/apex/jobscheduler/framework/java/android/app/job/JobScheduler.java @@ -169,6 +169,7 @@ public abstract class JobScheduler { * @param tag Debugging tag for dumps associated with this job (instead of the service class) * @hide */ + @SuppressWarnings("HiddenAbstractMethod") @SystemApi @RequiresPermission(android.Manifest.permission.UPDATE_DEVICE_STATS) public abstract @Result int scheduleAsPackage(@NonNull JobInfo job, @NonNull String packageName, @@ -211,6 +212,7 @@ public abstract class JobScheduler { * Returns a list of all currently-executing jobs. * @hide */ + @SuppressWarnings("HiddenAbstractMethod") public abstract List<JobInfo> getStartedJobs(); /** @@ -220,5 +222,6 @@ public abstract class JobScheduler { * <p class="note">This is a slow operation, so it should be called sparingly. * @hide */ + @SuppressWarnings("HiddenAbstractMethod") public abstract List<JobSnapshot> getAllJobSnapshots(); }
\ No newline at end of file diff --git a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java index ba2a8a34a92b..d4ea7af06ed1 100644 --- a/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java +++ b/apex/jobscheduler/service/java/com/android/server/alarm/AlarmManagerService.java @@ -439,7 +439,7 @@ public class AlarmManagerService extends SystemService { private static final int DEFAULT_APP_STANDBY_RESTRICTED_QUOTA = 1; private static final long DEFAULT_APP_STANDBY_RESTRICTED_WINDOW = MILLIS_IN_DAY; - private static final boolean DEFAULT_LAZY_BATCHING = false; + private static final boolean DEFAULT_LAZY_BATCHING = true; // Minimum futurity of a new alarm public long MIN_FUTURITY = DEFAULT_MIN_FUTURITY; diff --git a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java index cc202130ab07..1e7206287566 100644 --- a/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java +++ b/apex/jobscheduler/service/java/com/android/server/job/JobSchedulerShellCommand.java @@ -20,11 +20,10 @@ import android.app.ActivityManager; import android.app.AppGlobals; import android.content.pm.IPackageManager; import android.content.pm.PackageManager; +import android.os.BasicShellCommandHandler; import android.os.Binder; import android.os.UserHandle; -import com.android.modules.utils.BasicShellCommandHandler; - import java.io.PrintWriter; public final class JobSchedulerShellCommand extends BasicShellCommandHandler { diff --git a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java index 6f7dde292c56..1157ee905b86 100644 --- a/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java +++ b/apex/jobscheduler/service/java/com/android/server/usage/AppStandbyController.java @@ -2390,7 +2390,8 @@ public class AppStandbyController implements AppStandbyInternal { false, this); mInjector.registerDeviceConfigPropertiesChangedListener(this); // Load all the constants. - onPropertiesChanged(mInjector.getDeviceConfigProperties()); + // postOneTimeCheckIdleStates() doesn't need to be called on boot. + processProperties(mInjector.getDeviceConfigProperties()); updateSettings(); } @@ -2402,6 +2403,11 @@ public class AppStandbyController implements AppStandbyInternal { @Override public void onPropertiesChanged(DeviceConfig.Properties properties) { + processProperties(properties); + postOneTimeCheckIdleStates(); + } + + private void processProperties(DeviceConfig.Properties properties) { boolean timeThresholdsUpdated = false; synchronized (mAppIdleLock) { for (String name : properties.getKeyset()) { @@ -2482,7 +2488,6 @@ public class AppStandbyController implements AppStandbyInternal { } } } - postOneTimeCheckIdleStates(); } private void updateTimeThresholds() { diff --git a/apex/permission/Android.bp b/apex/permission/Android.bp index 71a52bb216ea..e30df05b2340 100644 --- a/apex/permission/Android.bp +++ b/apex/permission/Android.bp @@ -21,7 +21,7 @@ apex { apex_defaults { name: "com.android.permission-defaults", updatable: true, - min_sdk_version: "R", + min_sdk_version: "30", key: "com.android.permission.key", certificate: ":com.android.permission.certificate", java_libs: [ diff --git a/apex/statsd/Android.bp b/apex/statsd/Android.bp index ede8852c5905..f13861e7ee85 100644 --- a/apex/statsd/Android.bp +++ b/apex/statsd/Android.bp @@ -35,7 +35,7 @@ apex_defaults { prebuilts: ["com.android.os.statsd.init.rc"], name: "com.android.os.statsd-defaults", updatable: true, - min_sdk_version: "R", + min_sdk_version: "30", key: "com.android.os.statsd.key", certificate: ":com.android.os.statsd.certificate", } |