• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.app.appsearch;
18 
19 import static android.app.appsearch.SearchSessionUtil.safeExecute;
20 
21 import android.annotation.CallbackExecutor;
22 import android.annotation.FlaggedApi;
23 import android.annotation.NonNull;
24 import android.app.appsearch.aidl.AppSearchAttributionSource;
25 import android.app.appsearch.aidl.AppSearchResultCallback;
26 import android.app.appsearch.aidl.GetDocumentsAidlRequest;
27 import android.app.appsearch.aidl.GetSchemaAidlRequest;
28 import android.app.appsearch.aidl.IAppSearchManager;
29 import android.app.appsearch.aidl.InitializeAidlRequest;
30 import android.app.appsearch.aidl.OpenBlobForReadAidlRequest;
31 import android.app.appsearch.util.ExceptionUtil;
32 import android.os.RemoteException;
33 import android.os.SystemClock;
34 import android.os.UserHandle;
35 
36 import com.android.appsearch.flags.Flags;
37 import com.android.internal.annotations.VisibleForTesting;
38 
39 import java.util.ArrayList;
40 import java.util.Objects;
41 import java.util.Set;
42 import java.util.concurrent.Executor;
43 import java.util.function.Consumer;
44 
45 /**
46  * Holds the shared implementation for the query methods of {@link GlobalSearchSession} and
47  * EnterpriseGlobalSearchSession. Enterprise calls direct queries to the user's work profile
48  * AppSearch instance.
49  *
50  * @hide
51  */
52 public abstract class ReadOnlyGlobalSearchSession {
53     protected final IAppSearchManager mService;
54     protected final UserHandle mUserHandle;
55     protected final AppSearchAttributionSource mCallerAttributionSource;
56     private final boolean mIsForEnterprise;
57 
58     /**
59      * Creates a read-only search session with the given {@link IAppSearchManager} service, user,
60      * and attribution source.
61      *
62      * @param service The {@link IAppSearchManager} service from which to make api calls
63      * @param userHandle The user for which the session should be created
64      * @param callerAttributionSource The attribution source containing the caller's package name
65      *     and uid
66      * @param isForEnterprise Whether the session should query the user's enterprise profile
67      */
ReadOnlyGlobalSearchSession( @onNull IAppSearchManager service, @NonNull UserHandle userHandle, @NonNull AppSearchAttributionSource callerAttributionSource, boolean isForEnterprise)68     ReadOnlyGlobalSearchSession(
69             @NonNull IAppSearchManager service,
70             @NonNull UserHandle userHandle,
71             @NonNull AppSearchAttributionSource callerAttributionSource,
72             boolean isForEnterprise) {
73         mService = service;
74         mUserHandle = userHandle;
75         mCallerAttributionSource = callerAttributionSource;
76         mIsForEnterprise = isForEnterprise;
77     }
78 
79     // Once the callback.accept has been called here, the class is ready to use.
initialize( @onNull @allbackExecutor Executor executor, @NonNull Consumer<AppSearchResult<Void>> callback)80     protected void initialize(
81             @NonNull @CallbackExecutor Executor executor,
82             @NonNull Consumer<AppSearchResult<Void>> callback) {
83         try {
84             mService.initialize(
85                     new InitializeAidlRequest(
86                             mCallerAttributionSource,
87                             mUserHandle,
88                             /* binderCallStartTimeMillis= */ SystemClock.elapsedRealtime()),
89                     new AppSearchResultCallback<Void>() {
90                         @Override
91                         public void onResult(@NonNull AppSearchResult<Void> result) {
92                             safeExecute(
93                                     executor,
94                                     callback,
95                                     () -> {
96                                         if (result.isSuccess()) {
97                                             callback.accept(
98                                                     AppSearchResult.newSuccessfulResult(null));
99                                         } else {
100                                             callback.accept(
101                                                     AppSearchResult.newFailedResult(result));
102                                         }
103                                     });
104                         }
105                     });
106         } catch (RemoteException e) {
107             ExceptionUtil.handleRemoteException(e);
108         }
109     }
110 
111     /**
112      * Retrieves {@link GenericDocument} documents, belonging to the specified package name and
113      * database name and identified by the namespace and ids in the request, from the {@link
114      * GlobalSearchSession} database.
115      *
116      * <p>If the package or database doesn't exist or if the calling package doesn't have access,
117      * the gets will be handled as failures in an {@link AppSearchBatchResult} object in the
118      * callback.
119      *
120      * @param packageName the name of the package to get from
121      * @param databaseName the name of the database to get from
122      * @param request a request containing a namespace and IDs to get documents for.
123      * @param executor Executor on which to invoke the callback.
124      * @param callback Callback to receive the pending result of performing this operation. The keys
125      *     of the returned {@link AppSearchBatchResult} are the input IDs. The values are the
126      *     returned {@link GenericDocument}s on success, or a failed {@link AppSearchResult}
127      *     otherwise. IDs that are not found will return a failed {@link AppSearchResult} with a
128      *     result code of {@link AppSearchResult#RESULT_NOT_FOUND}. If an unexpected internal error
129      *     occurs in the AppSearch service, {@link BatchResultCallback#onSystemError} will be
130      *     invoked with a {@link Throwable}.
131      */
getByDocumentId( @onNull String packageName, @NonNull String databaseName, @NonNull GetByDocumentIdRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull BatchResultCallback<String, GenericDocument> callback)132     public void getByDocumentId(
133             @NonNull String packageName,
134             @NonNull String databaseName,
135             @NonNull GetByDocumentIdRequest request,
136             @NonNull @CallbackExecutor Executor executor,
137             @NonNull BatchResultCallback<String, GenericDocument> callback) {
138         Objects.requireNonNull(packageName);
139         Objects.requireNonNull(databaseName);
140         Objects.requireNonNull(request);
141         Objects.requireNonNull(executor);
142         Objects.requireNonNull(callback);
143 
144         try {
145             mService.getDocuments(
146                     new GetDocumentsAidlRequest(
147                             mCallerAttributionSource,
148                             /* targetPackageName= */ packageName,
149                             databaseName,
150                             request,
151                             mUserHandle,
152                             /* binderCallStartTimeMillis= */ SystemClock.elapsedRealtime(),
153                             mIsForEnterprise),
154                     SearchSessionUtil.createGetDocumentCallback(executor, callback));
155         } catch (RemoteException e) {
156             ExceptionUtil.handleRemoteException(e);
157         }
158     }
159 
160     /**
161      * Opens a batch of AppSearch Blobs for reading.
162      *
163      * <p>See {@link AppSearchSession#openBlobForRead} for a general description when a blob is for
164      * read.
165      *
166      * <p class="caution">The returned {@link OpenBlobForReadResponse} must be closed after use to
167      * avoid resource leaks. Failing to close it will result in system file descriptor exhaustion.
168      *
169      * @param handles The {@link AppSearchBlobHandle}s that identifies the blobs.
170      * @param executor Executor on which to invoke the callback.
171      * @param callback Callback to receive the {@link OpenBlobForReadResponse}.
172      * @see GenericDocument.Builder#setPropertyBlobHandle
173      */
174     @FlaggedApi(Flags.FLAG_ENABLE_BLOB_STORE)
openBlobForRead( @onNull Set<AppSearchBlobHandle> handles, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<AppSearchResult<OpenBlobForReadResponse>> callback)175     public void openBlobForRead(
176             @NonNull Set<AppSearchBlobHandle> handles,
177             @NonNull @CallbackExecutor Executor executor,
178             @NonNull Consumer<AppSearchResult<OpenBlobForReadResponse>> callback) {
179         try {
180             mService.openBlobForRead(
181                     new OpenBlobForReadAidlRequest(
182                             mCallerAttributionSource,
183                             /* callingDatabaseName= */ null,
184                             new ArrayList<>(handles),
185                             mUserHandle,
186                             /* binderCallStartTimeMillis= */ SystemClock.elapsedRealtime()),
187                     new AppSearchResultCallback<OpenBlobForReadResponse>() {
188                         @Override
189                         public void onResult(
190                                 @NonNull AppSearchResult<OpenBlobForReadResponse> result) {
191                             safeExecute(executor, callback, () -> callback.accept(result));
192                         }
193                     });
194         } catch (RemoteException e) {
195             ExceptionUtil.handleRemoteException(e);
196         }
197     }
198 
199     /**
200      * Retrieves documents from all AppSearch databases that the querying application has access to.
201      *
202      * <p>Applications can be granted access to documents by specifying {@link
203      * SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} when building a schema.
204      *
205      * <p>Document access can also be granted to system UIs by specifying {@link
206      * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} when building a schema.
207      *
208      * <p>See {@link AppSearchSession#search} for a detailed explanation on forming a query string.
209      *
210      * <p>This method is lightweight. The heavy work will be done in {@link
211      * SearchResults#getNextPage}.
212      *
213      * @param queryExpression query string to search.
214      * @param searchSpec spec for setting document filters, adding projection, setting term match
215      *     type, etc.
216      * @return a {@link SearchResults} object for retrieved matched documents.
217      */
218     @NonNull
search(@onNull String queryExpression, @NonNull SearchSpec searchSpec)219     public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
220         Objects.requireNonNull(queryExpression);
221         Objects.requireNonNull(searchSpec);
222         return new SearchResults(
223                 mService,
224                 mCallerAttributionSource,
225                 /* databaseName= */ null,
226                 queryExpression,
227                 searchSpec,
228                 mUserHandle,
229                 mIsForEnterprise);
230     }
231 
232     /**
233      * Retrieves the collection of schemas most recently successfully provided to {@link
234      * AppSearchSession#setSchema} for any types belonging to the requested package and database
235      * that the caller has been granted access to.
236      *
237      * <p>If the requested package/database combination does not exist or the caller has not been
238      * granted access to it, then an empty GetSchemaResponse will be returned.
239      *
240      * @param packageName the package that owns the requested {@link AppSearchSchema} instances.
241      * @param databaseName the database that owns the requested {@link AppSearchSchema} instances.
242      * @param executor Executor on which to invoke the callback.
243      * @param callback The pending {@link GetSchemaResponse} containing the schemas that the caller
244      *     has access to or an empty GetSchemaResponse if the request package and database does not
245      *     exist, has not set a schema or contains no schemas that are accessible to the caller.
246      */
getSchema( @onNull String packageName, @NonNull String databaseName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<AppSearchResult<GetSchemaResponse>> callback)247     public void getSchema(
248             @NonNull String packageName,
249             @NonNull String databaseName,
250             @NonNull @CallbackExecutor Executor executor,
251             @NonNull Consumer<AppSearchResult<GetSchemaResponse>> callback) {
252         Objects.requireNonNull(packageName);
253         Objects.requireNonNull(databaseName);
254         Objects.requireNonNull(executor);
255         Objects.requireNonNull(callback);
256         try {
257             mService.getSchema(
258                     new GetSchemaAidlRequest(
259                             mCallerAttributionSource,
260                             packageName,
261                             databaseName,
262                             mUserHandle,
263                             /* binderCallStartTimeMillis= */ SystemClock.elapsedRealtime(),
264                             mIsForEnterprise),
265                     new AppSearchResultCallback<GetSchemaResponse>() {
266                         @Override
267                         public void onResult(@NonNull AppSearchResult<GetSchemaResponse> result) {
268                             safeExecute(executor, callback, () -> callback.accept(result));
269                         }
270                     });
271         } catch (RemoteException e) {
272             ExceptionUtil.handleRemoteException(e);
273         }
274     }
275 
276     /**
277      * Returns the service instance to make IPC calls.
278      *
279      * @hide
280      */
281     @VisibleForTesting
getService()282     public IAppSearchManager getService() {
283         return mService;
284     }
285 
286     /**
287      * Returns if session supports Enterprise flow.
288      *
289      * @hide
290      */
291     @VisibleForTesting
isForEnterprise()292     public boolean isForEnterprise() {
293         return mIsForEnterprise;
294     }
295 }
296