From 1d83dda7dc8a4ee0d0871113e199c5c3896b0a0b Mon Sep 17 00:00:00 2001 From: Cassie Wang Date: Fri, 15 Jan 2021 14:39:30 -0800 Subject: [PATCH] Pass package name across binder. Take the client's context package name and pass it across binder so we can forward it to AppSearchImpl. We'll no longer be getting package name from UID, but we'll still verify that the package name belongs to the binder calling UID. Bug: 177752194 Test: presubmit Change-Id: I8aef10594a6130790e28fa34d87ab89717159d79 --- .../app/appsearch/AppSearchManager.java | 226 ++++++++++-------- .../app/appsearch/AppSearchSession.java | 35 ++- .../app/appsearch/GlobalSearchSession.java | 17 +- .../app/appsearch/IAppSearchManager.aidl | 21 +- .../android/app/appsearch/SearchResults.java | 14 +- .../appsearch/AppSearchManagerService.java | 143 ++++++----- 6 files changed, 275 insertions(+), 181 deletions(-) diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java index 6fa8f850100b6..ed4e0347b6a48 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchManager.java @@ -46,6 +46,7 @@ import java.util.function.Consumer; public class AppSearchManager { /** * The default empty database name. + * * @hide */ public static final String DEFAULT_DATABASE_NAME = ""; @@ -70,8 +71,8 @@ public class AppSearchManager { /** * Returns the name of the database to create or open. * - *

Databases with different names are fully separate with distinct types, namespaces, - * and data. + *

Databases with different names are fully separate with distinct types, namespaces, and + * data. */ @NonNull public String getDatabaseName() { @@ -126,11 +127,11 @@ public class AppSearchManager { * initialization process will create one under the user's credential encrypted directory. * * @param searchContext The {@link SearchContext} contains all information to create a new - * {@link AppSearchSession} - * @param executor Executor on which to invoke the callback. - * @param callback The {@link AppSearchResult}<{@link AppSearchSession}> of - * performing this operation. Or a {@link AppSearchResult} with failure - * reason code and error information. + * {@link AppSearchSession} + * @param executor Executor on which to invoke the callback. + * @param callback The {@link AppSearchResult}<{@link AppSearchSession}> of performing + * this operation. Or a {@link AppSearchResult} with failure reason code and error + * information. */ public void createSearchSession( @NonNull SearchContext searchContext, @@ -140,7 +141,12 @@ public class AppSearchManager { Objects.requireNonNull(executor); Objects.requireNonNull(callback); AppSearchSession.createSearchSession( - searchContext, mService, mContext.getUserId(), executor, callback); + searchContext, + mService, + mContext.getUserId(), + getPackageName(), + executor, + callback); } /** @@ -149,10 +155,10 @@ public class AppSearchManager { *

This process requires an AppSearch native indexing file system. If it's not created, the * initialization process will create one under the user's credential encrypted directory. * - * @param executor Executor on which to invoke the callback. - * @param callback The {@link AppSearchResult}<{@link GlobalSearchSession}> of - * performing this operation. Or a {@link AppSearchResult} with failure - * reason code and error information. + * @param executor Executor on which to invoke the callback. + * @param callback The {@link AppSearchResult}<{@link GlobalSearchSession}> of performing + * this operation. Or a {@link AppSearchResult} with failure reason code and error + * information. */ public void createGlobalSearchSession( @NonNull @CallbackExecutor Executor executor, @@ -160,7 +166,7 @@ public class AppSearchManager { Objects.requireNonNull(executor); Objects.requireNonNull(callback); GlobalSearchSession.createGlobalSearchSession( - mService, mContext.getUserId(), executor, callback); + mService, mContext.getUserId(), getPackageName(), executor, callback); } /** @@ -170,40 +176,42 @@ public class AppSearchManager { * to {@link #setSchema}, if any, to determine how to treat existing documents. The following * types of schema modifications are always safe and are made without deleting any existing * documents: + * *

* *

The following types of schema changes are not backwards-compatible: + * *

- *

Supplying a schema with such changes will result in this call returning an - * {@link AppSearchResult} with a code of {@link AppSearchResult#RESULT_INVALID_SCHEMA} and an - * error message describing the incompatibility. In this case the previously set schema will - * remain active. + * + *

Supplying a schema with such changes will result in this call returning an {@link + * AppSearchResult} with a code of {@link AppSearchResult#RESULT_INVALID_SCHEMA} and an error + * message describing the incompatibility. In this case the previously set schema will remain + * active. * *

If you need to make non-backwards-compatible changes as described above, instead use the * {@link #setSchema(List, boolean)} method with the {@code forceOverride} parameter set to @@ -214,8 +222,8 @@ public class AppSearchManager { * * @param request The schema update request. * @return the result of performing this operation. - * @deprecated use {@link AppSearchSession#setSchema} instead. * @hide + * @deprecated use {@link AppSearchSession#setSchema} instead. */ @NonNull public AppSearchResult setSchema(@NonNull SetSchemaRequest request) { @@ -229,6 +237,7 @@ public class AppSearchManager { AndroidFuture future = new AndroidFuture<>(); try { mService.setSchema( + getPackageName(), DEFAULT_DATABASE_NAME, schemaBundles, new ArrayList<>(request.getSchemasNotVisibleToSystemUi()), @@ -249,21 +258,19 @@ public class AppSearchManager { /** * Index {@link GenericDocument}s into AppSearch. * - *

You should not call this method directly; instead, use the - * {@code AppSearch#putDocuments()} API provided by JetPack. + *

You should not call this method directly; instead, use the {@code + * AppSearch#putDocuments()} API provided by JetPack. * *

Each {@link GenericDocument}'s {@code schemaType} field must be set to the name of a * schema type previously registered via the {@link #setSchema} method. * * @param request {@link PutDocumentsRequest} containing documents to be indexed - * @return The pending result of performing this operation. The keys of the returned - * {@link AppSearchBatchResult} are the URIs of the input documents. The values are - * {@code null} if they were successfully indexed, or a failed {@link AppSearchResult} - * otherwise. + * @return The pending result of performing this operation. The keys of the returned {@link + * AppSearchBatchResult} are the URIs of the input documents. The values are {@code null} if + * they were successfully indexed, or a failed {@link AppSearchResult} otherwise. * @throws RuntimeException If an error occurred during the execution. - * - * @deprecated use {@link AppSearchSession#putDocuments} instead. * @hide + * @deprecated use {@link AppSearchSession#putDocuments} instead. */ public AppSearchBatchResult putDocuments(@NonNull PutDocumentsRequest request) { // TODO(b/146386470): Transmit these documents as a RemoteStream instead of sending them in @@ -275,7 +282,11 @@ public class AppSearchManager { } AndroidFuture future = new AndroidFuture<>(); try { - mService.putDocuments(DEFAULT_DATABASE_NAME, documentBundles, mContext.getUserId(), + mService.putDocuments( + getPackageName(), + DEFAULT_DATABASE_NAME, + documentBundles, + mContext.getUserId(), new IAppSearchBatchResultCallback.Stub() { public void onResult(AppSearchBatchResult result) { future.complete(result); @@ -294,19 +305,18 @@ public class AppSearchManager { /** * Retrieves {@link GenericDocument}s by URI. * - *

You should not call this method directly; instead, use the - * {@code AppSearch#getDocuments()} API provided by JetPack. + *

You should not call this method directly; instead, use the {@code + * AppSearch#getDocuments()} API provided by JetPack. * * @param request {@link GetByUriRequest} containing URIs to be retrieved. - * @return The pending result of performing this operation. The keys of the returned - * {@link AppSearchBatchResult} are the input URIs. The values are the returned - * {@link GenericDocument}s on success, or a failed {@link AppSearchResult} otherwise. - * URIs that are not found will return a failed {@link AppSearchResult} with a result code - * of {@link AppSearchResult#RESULT_NOT_FOUND}. + * @return The pending result of performing this operation. The keys of the returned {@link + * AppSearchBatchResult} are the input URIs. The values are the returned {@link + * GenericDocument}s on success, or a failed {@link AppSearchResult} otherwise. URIs that + * are not found will return a failed {@link AppSearchResult} with a result code of {@link + * AppSearchResult#RESULT_NOT_FOUND}. * @throws RuntimeException If an error occurred during the execution. - * - * @deprecated use {@link AppSearchSession#getByUri} instead. * @hide + * @deprecated use {@link AppSearchSession#getByUri} instead. */ public AppSearchBatchResult getByUri( @NonNull GetByUriRequest request) { @@ -315,7 +325,11 @@ public class AppSearchManager { List uris = new ArrayList<>(request.getUris()); AndroidFuture future = new AndroidFuture<>(); try { - mService.getDocuments(DEFAULT_DATABASE_NAME, request.getNamespace(), uris, + mService.getDocuments( + getPackageName(), + DEFAULT_DATABASE_NAME, + request.getNamespace(), + uris, mContext.getUserId(), new IAppSearchBatchResultCallback.Stub() { public void onResult(AppSearchBatchResult result) { @@ -371,43 +385,39 @@ public class AppSearchManager { * provided by JetPack. * *

Currently we support following features in the raw query format: + * *

* * @param queryExpression Query String to search. * @param searchSpec Spec for setting filters, raw query etc. * @throws RuntimeException If an error occurred during the execution. - * - * @deprecated use AppSearchSession#query instead. * @hide + * @deprecated use AppSearchSession#query instead. */ @NonNull public AppSearchResult> query( @@ -416,7 +426,11 @@ public class AppSearchManager { // them in one big list. AndroidFuture future = new AndroidFuture<>(); try { - mService.query(DEFAULT_DATABASE_NAME, queryExpression, searchSpec.getBundle(), + mService.query( + getPackageName(), + DEFAULT_DATABASE_NAME, + queryExpression, + searchSpec.getBundle(), mContext.getUserId(), new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { @@ -425,8 +439,8 @@ public class AppSearchManager { }); AppSearchResult bundleResult = getFutureOrThrow(future); if (!bundleResult.isSuccess()) { - return AppSearchResult.newFailedResult(bundleResult.getResultCode(), - bundleResult.getErrorMessage()); + return AppSearchResult.newFailedResult( + bundleResult.getResultCode(), bundleResult.getErrorMessage()); } SearchResultPage searchResultPage = new SearchResultPage(bundleResult.getResultValue()); return AppSearchResult.newSuccessfulResult(searchResultPage.getResults()); @@ -444,21 +458,23 @@ public class AppSearchManager { * provided by JetPack. * * @param request Request containing URIs to be removed. - * @return The pending result of performing this operation. The keys of the returned - * {@link AppSearchBatchResult} are the input URIs. The values are {@code null} on success, - * or a failed {@link AppSearchResult} otherwise. URIs that are not found will return a - * failed {@link AppSearchResult} with a result code of - * {@link AppSearchResult#RESULT_NOT_FOUND}. + * @return The pending result of performing this operation. The keys of the returned {@link + * AppSearchBatchResult} are the input URIs. The values are {@code null} on success, or a + * failed {@link AppSearchResult} otherwise. URIs that are not found will return a failed + * {@link AppSearchResult} with a result code of {@link AppSearchResult#RESULT_NOT_FOUND}. * @throws RuntimeException If an error occurred during the execution. - * - * @deprecated use {@link AppSearchSession#removeByUri} instead. * @hide + * @deprecated use {@link AppSearchSession#removeByUri} instead. */ public AppSearchBatchResult removeByUri(@NonNull RemoveByUriRequest request) { List uris = new ArrayList<>(request.getUris()); AndroidFuture future = new AndroidFuture<>(); try { - mService.removeByUri(DEFAULT_DATABASE_NAME, request.getNamespace(), uris, + mService.removeByUri( + getPackageName(), + DEFAULT_DATABASE_NAME, + request.getNamespace(), + uris, mContext.getUserId(), new IAppSearchBatchResultCallback.Stub() { public void onResult(AppSearchBatchResult result) { @@ -475,6 +491,12 @@ public class AppSearchManager { return getFutureOrThrow(future); } + /** Returns the package name that should be used for uid verification. */ + @NonNull + private String getPackageName() { + return mContext.getOpPackageName(); + } + private static T getFutureOrThrow(@NonNull AndroidFuture future) { try { return future.get(); diff --git a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java index 042757797dce4..270d601a20d02 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/AppSearchSession.java @@ -45,6 +45,7 @@ import java.util.function.Consumer; */ public final class AppSearchSession implements Closeable { private static final String TAG = "AppSearchSession"; + private final String mPackageName; private final String mDatabaseName; @UserIdInt private final int mUserId; @@ -52,14 +53,20 @@ public final class AppSearchSession implements Closeable { private boolean mIsMutated = false; private boolean mIsClosed = false; + + /** + * Creates a search session for the client, defined by the {@code userId} and + * {@code packageName}. + */ static void createSearchSession( @NonNull AppSearchManager.SearchContext searchContext, @NonNull IAppSearchManager service, @UserIdInt int userId, + @NonNull String packageName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer> callback) { AppSearchSession searchSession = - new AppSearchSession(service, userId, searchContext.mDatabaseName); + new AppSearchSession(service, userId, packageName, searchContext.mDatabaseName); searchSession.initialize(executor, callback); } @@ -87,10 +94,11 @@ public final class AppSearchSession implements Closeable { } private AppSearchSession(@NonNull IAppSearchManager service, @UserIdInt int userId, - @NonNull String databaseName) { - mDatabaseName = databaseName; + @NonNull String packageName, @NonNull String databaseName) { mService = service; mUserId = userId; + mPackageName = packageName; + mDatabaseName = databaseName; } /** @@ -144,7 +152,7 @@ public final class AppSearchSession implements Closeable { * Visibility settings for a schema type do not apply or persist across * {@link SetSchemaRequest}s. * - * @param request The schema update request. + * @param request The schema update request. * @param executor Executor on which to invoke the callback. * @param callback Callback to receive errors resulting from setting the schema. If the * operation succeeds, the callback will be invoked with {@code null}. @@ -175,6 +183,7 @@ public final class AppSearchSession implements Closeable { } try { mService.setSchema( + mPackageName, mDatabaseName, schemaBundles, new ArrayList<>(request.getSchemasNotVisibleToSystemUi()), @@ -206,6 +215,7 @@ public final class AppSearchSession implements Closeable { Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { mService.getSchema( + mPackageName, mDatabaseName, mUserId, new IAppSearchResultCallback.Stub() { @@ -261,7 +271,7 @@ public final class AppSearchSession implements Closeable { documentBundles.add(documents.get(i).getBundle()); } try { - mService.putDocuments(mDatabaseName, documentBundles, mUserId, + mService.putDocuments(mPackageName, mDatabaseName, documentBundles, mUserId, new IAppSearchBatchResultCallback.Stub() { public void onResult(AppSearchBatchResult result) { executor.execute(() -> callback.onResult(result)); @@ -280,7 +290,7 @@ public final class AppSearchSession implements Closeable { /** * Retrieves {@link GenericDocument}s by URI. * - * @param request {@link GetByUriRequest} containing URIs to be retrieved. + * @param request {@link GetByUriRequest} containing URIs to be retrieved. * @param executor Executor on which to invoke the callback. * @param callback Callback to receive the pending result of performing this operation. The keys * of the returned {@link AppSearchBatchResult} are the input URIs. The values @@ -301,7 +311,7 @@ public final class AppSearchSession implements Closeable { Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { - mService.getDocuments(mDatabaseName, request.getNamespace(), + mService.getDocuments(mPackageName, mDatabaseName, request.getNamespace(), new ArrayList<>(request.getUris()), mUserId, new IAppSearchBatchResultCallback.Stub() { public void onResult(AppSearchBatchResult result) { @@ -405,14 +415,14 @@ public final class AppSearchSession implements Closeable { Objects.requireNonNull(searchSpec); Objects.requireNonNull(executor); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); - return new SearchResults(mService, mDatabaseName, queryExpression, searchSpec, mUserId, - executor); + return new SearchResults(mService, mPackageName, mDatabaseName, queryExpression, + searchSpec, mUserId, executor); } /** * Removes {@link GenericDocument}s from the index by URI. * - * @param request Request containing URIs to be removed. + * @param request Request containing URIs to be removed. * @param executor Executor on which to invoke the callback. * @param callback Callback to receive the pending result of performing this operation. The keys * of the returned {@link AppSearchBatchResult} are the input URIs. The values @@ -432,7 +442,7 @@ public final class AppSearchSession implements Closeable { Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { - mService.removeByUri(mDatabaseName, request.getNamespace(), + mService.removeByUri(mPackageName, mDatabaseName, request.getNamespace(), new ArrayList<>(request.getUris()), mUserId, new IAppSearchBatchResultCallback.Stub() { public void onResult(AppSearchBatchResult result) { @@ -478,7 +488,8 @@ public final class AppSearchSession implements Closeable { Objects.requireNonNull(callback); Preconditions.checkState(!mIsClosed, "AppSearchSession has already been closed"); try { - mService.removeByQuery(mDatabaseName, queryExpression, searchSpec.getBundle(), mUserId, + mService.removeByQuery(mPackageName, mDatabaseName, queryExpression, + searchSpec.getBundle(), mUserId, new IAppSearchResultCallback.Stub() { public void onResult(AppSearchResult result) { executor.execute(() -> callback.accept(result)); diff --git a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java index e4e030e0d18ab..6bb855480bc9a 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java +++ b/apex/appsearch/framework/java/android/app/appsearch/GlobalSearchSession.java @@ -37,16 +37,25 @@ import java.util.function.Consumer; public class GlobalSearchSession implements Closeable { private final IAppSearchManager mService; + @UserIdInt private final int mUserId; private boolean mIsClosed = false; + private final String mPackageName; + + /** + * Creates a search session for the client, defined by the {@code userId} and + * {@code packageName}. + */ static void createGlobalSearchSession( @NonNull IAppSearchManager service, @UserIdInt int userId, + @NonNull String packageName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer> callback) { - GlobalSearchSession globalSearchSession = new GlobalSearchSession(service, userId); + GlobalSearchSession globalSearchSession = new GlobalSearchSession(service, userId, + packageName); globalSearchSession.initialize(executor, callback); } @@ -73,9 +82,11 @@ public class GlobalSearchSession implements Closeable { } } - private GlobalSearchSession(@NonNull IAppSearchManager service, @UserIdInt int userId) { + private GlobalSearchSession(@NonNull IAppSearchManager service, @UserIdInt int userId, + @NonNull String packageName) { mService = service; mUserId = userId; + mPackageName = packageName; } /** @@ -131,7 +142,7 @@ public class GlobalSearchSession implements Closeable { Objects.requireNonNull(searchSpec); Objects.requireNonNull(executor); Preconditions.checkState(!mIsClosed, "GlobalSearchSession has already been closed"); - return new SearchResults(mService, /*databaseName=*/null, queryExpression, + return new SearchResults(mService, mPackageName, /*databaseName=*/null, queryExpression, searchSpec, mUserId, executor); } diff --git a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl index 2b437774b4733..e9202cbed6a7c 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl +++ b/apex/appsearch/framework/java/android/app/appsearch/IAppSearchManager.aidl @@ -30,6 +30,7 @@ interface IAppSearchManager { /** * Updates the AppSearch schema for this database. * + * @param packageName The name of the package that owns this schema. * @param databaseName The name of the database where this schema lives. * @param schemaBundles List of {@link AppSearchSchema} bundles. * @param schemasNotPlatformSurfaceable Schema types that should not be surfaced on platform @@ -43,6 +44,7 @@ interface IAppSearchManager { * {@link AppSearchResult}<{@link Void}>. */ void setSchema( + in String packageName, in String databaseName, in List schemaBundles, in List schemasNotPlatformSurfaceable, @@ -54,17 +56,23 @@ interface IAppSearchManager { /** * Retrieves the AppSearch schema for this database. * + * @param packageName The name of the package that owns the schema. * @param databaseName The name of the database to retrieve. * @param userId Id of the calling user * @param callback {@link IAppSearchResultCallback#onResult} will be called with an * {@link AppSearchResult}<{@link List}<{@link Bundle}>>, where the value are * AppSearchSchema bundle. */ - void getSchema(in String databaseName, in int userId, in IAppSearchResultCallback callback); + void getSchema( + in String packageName, + in String databaseName, + in int userId, + in IAppSearchResultCallback callback); /** * Inserts documents into the index. * + * @param packageName The name of the package that owns this document. * @param databaseName The name of the database where this document lives. * @param documentBundes List of GenericDocument bundles. * @param userId Id of the calling user @@ -76,6 +84,7 @@ interface IAppSearchManager { * where the keys are document URIs, and the values are {@code null}. */ void putDocuments( + in String packageName, in String databaseName, in List documentBundles, in int userId, @@ -84,6 +93,7 @@ interface IAppSearchManager { /** * Retrieves documents from the index. * + * @param packageName The name of the package that owns this document. * @param databaseName The databaseName this document resides in. * @param namespace The namespace this document resides in. * @param uris The URIs of the documents to retrieve @@ -96,6 +106,7 @@ interface IAppSearchManager { * where the keys are document URIs, and the values are Document bundles. */ void getDocuments( + in String packageName, in String databaseName, in String namespace, in List uris, @@ -105,6 +116,7 @@ interface IAppSearchManager { /** * Searches a document based on a given specifications. * + * @param packageName The name of the package to query over. * @param databaseName The databaseName this query for. * @param queryExpression String to search for * @param searchSpecBundle SearchSpec bundle @@ -113,6 +125,7 @@ interface IAppSearchManager { * operation. */ void query( + in String packageName, in String databaseName, in String queryExpression, in Bundle searchSpecBundle, @@ -123,6 +136,7 @@ interface IAppSearchManager { * Executes a global query, i.e. over all permitted databases, against the AppSearch index and * returns results. * + * @param packageName The name of the package making the query. * @param queryExpression String to search for * @param searchSpecBundle SearchSpec bundle * @param userId Id of the calling user @@ -130,6 +144,7 @@ interface IAppSearchManager { * operation. */ void globalQuery( + in String packageName, in String queryExpression, in Bundle searchSpecBundle, in int userId, @@ -158,6 +173,7 @@ interface IAppSearchManager { /** * Removes documents by URI. * + * @param packageName The name of the package the document is in. * @param databaseName The databaseName the document is in. * @param namespace Namespace of the document to remove. * @param uris The URIs of the documents to delete @@ -171,6 +187,7 @@ interface IAppSearchManager { * failure where the {@code throwable} is {@code null}. */ void removeByUri( + in String packageName, in String databaseName, in String namespace, in List uris, @@ -180,6 +197,7 @@ interface IAppSearchManager { /** * Removes documents by given query. * + * @param packageName The name of the package to query over. * @param databaseName The databaseName this query for. * @param queryExpression String to search for * @param searchSpecBundle SearchSpec bundle @@ -188,6 +206,7 @@ interface IAppSearchManager { * {@link AppSearchResult}<{@link Void}>. */ void removeByQuery( + in String packageName, in String databaseName, in String queryExpression, in Bundle searchSpecBundle, diff --git a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java index 0cb0ea42483e8..704509bce2a13 100644 --- a/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java +++ b/apex/appsearch/framework/java/android/app/appsearch/SearchResults.java @@ -47,6 +47,9 @@ public class SearchResults implements Closeable { private final IAppSearchManager mService; + // The package name of the caller. + private final String mPackageName; + // The database name to search over. If null, this will search over all database names. @Nullable private final String mDatabaseName; @@ -68,12 +71,14 @@ public class SearchResults implements Closeable { SearchResults( @NonNull IAppSearchManager service, + @NonNull String packageName, @Nullable String databaseName, @NonNull String queryExpression, @NonNull SearchSpec searchSpec, @UserIdInt int userId, @NonNull @CallbackExecutor Executor executor) { mService = Objects.requireNonNull(service); + mPackageName = packageName; mDatabaseName = databaseName; mQueryExpression = Objects.requireNonNull(queryExpression); mSearchSpec = Objects.requireNonNull(searchSpec); @@ -98,13 +103,12 @@ public class SearchResults implements Closeable { mIsFirstLoad = false; if (mDatabaseName == null) { // Global query, there's no one package-database combination to check. - mService.globalQuery(mQueryExpression, mSearchSpec.getBundle(), mUserId, - wrapCallback(callback)); + mService.globalQuery(mPackageName, mQueryExpression, + mSearchSpec.getBundle(), mUserId, wrapCallback(callback)); } else { // Normal local query, pass in specified database. - mService.query( - mDatabaseName, mQueryExpression, mSearchSpec.getBundle(), mUserId, - wrapCallback(callback)); + mService.query(mPackageName, mDatabaseName, mQueryExpression, + mSearchSpec.getBundle(), mUserId, wrapCallback(callback)); } } else { mService.getNextPage(mNextPageToken, mUserId, wrapCallback(callback)); 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 1f1e9a1a28a80..87e0435bb2507 100644 --- a/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java +++ b/apex/appsearch/service/java/com/android/server/appsearch/AppSearchManagerService.java @@ -31,14 +31,17 @@ import android.app.appsearch.PackageIdentifier; import android.app.appsearch.SearchResultPage; import android.app.appsearch.SearchSpec; import android.content.Context; +import android.content.pm.PackageManagerInternal; import android.os.Binder; import android.os.Bundle; import android.os.ParcelableException; import android.os.RemoteException; +import android.os.UserHandle; import android.util.ArrayMap; import android.util.Log; import com.android.internal.util.Preconditions; +import com.android.server.LocalServices; import com.android.server.SystemService; import com.android.server.appsearch.external.localstorage.AppSearchImpl; @@ -46,11 +49,10 @@ import java.util.ArrayList; import java.util.List; import java.util.Map; -/** - * TODO(b/142567528): add comments when implement this class - */ +/** TODO(b/142567528): add comments when implement this class */ public class AppSearchManagerService extends SystemService { private static final String TAG = "AppSearchManagerService"; + private PackageManagerInternal mPackageManagerInternal; public AppSearchManagerService(Context context) { super(context); @@ -59,11 +61,13 @@ public class AppSearchManagerService extends SystemService { @Override public void onStart() { publishBinderService(Context.APP_SEARCH_SERVICE, new Stub()); + mPackageManagerInternal = LocalServices.getService(PackageManagerInternal.class); } private class Stub extends IAppSearchManager.Stub { @Override public void setSchema( + @NonNull String packageName, @NonNull String databaseName, @NonNull List schemaBundles, @NonNull List schemasNotPlatformSurfaceable, @@ -71,6 +75,7 @@ public class AppSearchManagerService extends SystemService { boolean forceOverride, @UserIdInt int userId, @NonNull IAppSearchResultCallback callback) { + Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(schemaBundles); Preconditions.checkNotNull(callback); @@ -78,6 +83,7 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyCallingPackage(callingUid, packageName); List schemas = new ArrayList<>(schemaBundles.size()); for (int i = 0; i < schemaBundles.size(); i++) { schemas.add(new AppSearchSchema(schemaBundles.get(i))); @@ -94,7 +100,6 @@ public class AppSearchManagerService extends SystemService { schemasPackageAccessible.put(entry.getKey(), packageIdentifiers); } AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String packageName = convertUidToPackageName(callingUid); impl.setSchema( packageName, databaseName, @@ -102,8 +107,8 @@ public class AppSearchManagerService extends SystemService { schemasNotPlatformSurfaceable, schemasPackageAccessible, forceOverride); - invokeCallbackOnResult(callback, - AppSearchResult.newSuccessfulResult(/*result=*/ null)); + invokeCallbackOnResult( + callback, AppSearchResult.newSuccessfulResult(/*result=*/ null)); } catch (Throwable t) { invokeCallbackOnError(callback, t); } finally { @@ -113,24 +118,26 @@ public class AppSearchManagerService extends SystemService { @Override public void getSchema( + @NonNull String packageName, @NonNull String databaseName, @UserIdInt int userId, @NonNull IAppSearchResultCallback callback) { + Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUidOrThrow(); int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String packageName = convertUidToPackageName(callingUid); List schemas = impl.getSchema(packageName, databaseName); List schemaBundles = new ArrayList<>(schemas.size()); for (int i = 0; i < schemas.size(); i++) { schemaBundles.add(schemas.get(i).getBundle()); } - invokeCallbackOnResult(callback, - AppSearchResult.newSuccessfulResult(schemaBundles)); + invokeCallbackOnResult( + callback, AppSearchResult.newSuccessfulResult(schemaBundles)); } catch (Throwable t) { invokeCallbackOnError(callback, t); } finally { @@ -140,10 +147,12 @@ public class AppSearchManagerService extends SystemService { @Override public void putDocuments( + @NonNull String packageName, @NonNull String databaseName, @NonNull List documentBundles, @UserIdInt int userId, @NonNull IAppSearchBatchResultCallback callback) { + Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(documentBundles); Preconditions.checkNotNull(callback); @@ -151,10 +160,10 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder resultBuilder = new AppSearchBatchResult.Builder<>(); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String packageName = convertUidToPackageName(callingUid); for (int i = 0; i < documentBundles.size(); i++) { GenericDocument document = new GenericDocument(documentBundles.get(i)); try { @@ -176,11 +185,13 @@ public class AppSearchManagerService extends SystemService { @Override public void getDocuments( + @NonNull String packageName, @NonNull String databaseName, @NonNull String namespace, @NonNull List uris, @UserIdInt int userId, @NonNull IAppSearchBatchResultCallback callback) { + Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(namespace); Preconditions.checkNotNull(uris); @@ -189,15 +200,15 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder resultBuilder = new AppSearchBatchResult.Builder<>(); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String packageName = convertUidToPackageName(callingUid); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { - GenericDocument document = impl.getDocument(packageName, databaseName, - namespace, uri); + GenericDocument document = + impl.getDocument(packageName, databaseName, namespace, uri); resultBuilder.setSuccess(uri, document.getBundle()); } catch (Throwable t) { resultBuilder.setResult(uri, throwableToFailedResult(t)); @@ -214,11 +225,13 @@ public class AppSearchManagerService extends SystemService { // TODO(sidchhabra): Do this in a threadpool. @Override public void query( + @NonNull String packageName, @NonNull String databaseName, @NonNull String queryExpression, @NonNull Bundle searchSpecBundle, @UserIdInt int userId, @NonNull IAppSearchResultCallback callback) { + Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(queryExpression); Preconditions.checkNotNull(searchSpecBundle); @@ -227,14 +240,16 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String packageName = convertUidToPackageName(callingUid); - SearchResultPage searchResultPage = impl.query( - packageName, - databaseName, - queryExpression, - new SearchSpec(searchSpecBundle)); - invokeCallbackOnResult(callback, + SearchResultPage searchResultPage = + impl.query( + packageName, + databaseName, + queryExpression, + new SearchSpec(searchSpecBundle)); + invokeCallbackOnResult( + callback, AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); } catch (Throwable t) { invokeCallbackOnError(callback, t); @@ -245,10 +260,12 @@ public class AppSearchManagerService extends SystemService { @Override public void globalQuery( + @NonNull String packageName, @NonNull String queryExpression, @NonNull Bundle searchSpecBundle, @UserIdInt int userId, @NonNull IAppSearchResultCallback callback) { + Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(queryExpression); Preconditions.checkNotNull(searchSpecBundle); Preconditions.checkNotNull(callback); @@ -256,11 +273,12 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - SearchResultPage searchResultPage = impl.globalQuery( - queryExpression, - new SearchSpec(searchSpecBundle)); - invokeCallbackOnResult(callback, + SearchResultPage searchResultPage = + impl.globalQuery(queryExpression, new SearchSpec(searchSpecBundle)); + invokeCallbackOnResult( + callback, AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); } catch (Throwable t) { invokeCallbackOnError(callback, t); @@ -270,7 +288,9 @@ public class AppSearchManagerService extends SystemService { } @Override - public void getNextPage(long nextPageToken, @UserIdInt int userId, + public void getNextPage( + long nextPageToken, + @UserIdInt int userId, @NonNull IAppSearchResultCallback callback) { Preconditions.checkNotNull(callback); int callingUid = Binder.getCallingUid(); @@ -281,7 +301,8 @@ public class AppSearchManagerService extends SystemService { try { AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); SearchResultPage searchResultPage = impl.getNextPage(nextPageToken); - invokeCallbackOnResult(callback, + invokeCallbackOnResult( + callback, AppSearchResult.newSuccessfulResult(searchResultPage.getBundle())); } catch (Throwable t) { invokeCallbackOnError(callback, t); @@ -307,11 +328,13 @@ public class AppSearchManagerService extends SystemService { @Override public void removeByUri( + @NonNull String packageName, @NonNull String databaseName, @NonNull String namespace, @NonNull List uris, @UserIdInt int userId, @NonNull IAppSearchBatchResultCallback callback) { + Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(uris); Preconditions.checkNotNull(callback); @@ -319,15 +342,15 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyCallingPackage(callingUid, packageName); AppSearchBatchResult.Builder resultBuilder = new AppSearchBatchResult.Builder<>(); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String packageName = convertUidToPackageName(callingUid); for (int i = 0; i < uris.size(); i++) { String uri = uris.get(i); try { impl.remove(packageName, databaseName, namespace, uri); - resultBuilder.setSuccess(uri, /*result= */null); + resultBuilder.setSuccess(uri, /*result= */ null); } catch (Throwable t) { resultBuilder.setResult(uri, throwableToFailedResult(t)); } @@ -342,11 +365,13 @@ public class AppSearchManagerService extends SystemService { @Override public void removeByQuery( + @NonNull String packageName, @NonNull String databaseName, @NonNull String queryExpression, @NonNull Bundle searchSpecBundle, @UserIdInt int userId, @NonNull IAppSearchResultCallback callback) { + Preconditions.checkNotNull(packageName); Preconditions.checkNotNull(databaseName); Preconditions.checkNotNull(queryExpression); Preconditions.checkNotNull(searchSpecBundle); @@ -355,9 +380,12 @@ public class AppSearchManagerService extends SystemService { int callingUserId = handleIncomingUser(userId, callingUid); final long callingIdentity = Binder.clearCallingIdentity(); try { + verifyCallingPackage(callingUid, packageName); AppSearchImpl impl = ImplInstanceManager.getInstance(getContext(), callingUserId); - String packageName = convertUidToPackageName(callingUid); - impl.removeByQuery(packageName, databaseName, queryExpression, + impl.removeByQuery( + packageName, + databaseName, + queryExpression, new SearchSpec(searchSpecBundle)); invokeCallbackOnResult(callback, AppSearchResult.newSuccessfulResult(null)); } catch (Throwable t) { @@ -398,28 +426,22 @@ public class AppSearchManagerService extends SystemService { } } - /** - * Returns a package name for the given uid. - * - *

The current implementation returns the package name of the app with this uid in a - * format like {@code com.example.package} or {@code com.example.sharedname:5678}. - */ - @NonNull - private String convertUidToPackageName(int callingUid) { - // For regular apps, this call will return the package name. If callingUid is an - // android:sharedUserId, this value may be another type of name and have a :uid suffix. - String callingUidName = getContext().getPackageManager().getNameForUid(callingUid); - if (callingUidName == null) { - // Not sure how this is possible --- maybe app was uninstalled? - throw new IllegalStateException( - "Failed to look up package name for uid " + callingUid); + private void verifyCallingPackage(int callingUid, @NonNull String callingPackage) { + Preconditions.checkNotNull(callingPackage); + if (mPackageManagerInternal.getPackageUid( + callingPackage, /*flags=*/ 0, UserHandle.getUserId(callingUid)) + != callingUid) { + throw new SecurityException( + "Specified calling package [" + + callingPackage + + "] does not match the calling uid " + + callingUid); } - return callingUidName; } /** Invokes the {@link IAppSearchResultCallback} with the result. */ - private void invokeCallbackOnResult(IAppSearchResultCallback callback, - AppSearchResult result) { + private void invokeCallbackOnResult( + IAppSearchResultCallback callback, AppSearchResult result) { try { callback.onResult(result); } catch (RemoteException e) { @@ -428,8 +450,8 @@ public class AppSearchManagerService extends SystemService { } /** Invokes the {@link IAppSearchBatchResultCallback} with the result. */ - private void invokeCallbackOnResult(IAppSearchBatchResultCallback callback, - AppSearchBatchResult result) { + private void invokeCallbackOnResult( + IAppSearchBatchResultCallback callback, AppSearchBatchResult result) { try { callback.onResult(result); } catch (RemoteException e) { @@ -455,8 +477,8 @@ public class AppSearchManagerService extends SystemService { * *

The throwable is converted to {@link ParcelableException}. */ - private void invokeCallbackOnError(IAppSearchBatchResultCallback callback, - Throwable throwable) { + private void invokeCallbackOnError( + IAppSearchBatchResultCallback callback, Throwable throwable) { try { callback.onSystemError(new ParcelableException(throwable)); } catch (RemoteException e) { @@ -465,13 +487,18 @@ public class AppSearchManagerService extends SystemService { } } - //TODO(b/173553485) verifying that the caller has permission to access target user's data - //TODO(b/173553485) Handle ACTION_USER_REMOVED broadcast - //TODO(b/173553485) Implement SystemService.onUserStopping() + // TODO(b/173553485) verifying that the caller has permission to access target user's data + // TODO(b/173553485) Handle ACTION_USER_REMOVED broadcast + // TODO(b/173553485) Implement SystemService.onUserStopping() private static int handleIncomingUser(@UserIdInt int userId, int callingUid) { int callingPid = Binder.getCallingPid(); - return ActivityManager.handleIncomingUser(callingPid, callingUid, userId, - /*allowAll=*/ false, /*requireFull=*/ false, - /*name=*/ null, /*callerPackage=*/ null); + return ActivityManager.handleIncomingUser( + callingPid, + callingUid, + userId, + /*allowAll=*/ false, + /*requireFull=*/ false, + /*name=*/ null, + /*callerPackage=*/ null); } }