• 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.NonNull;
23 import android.app.appsearch.aidl.AppSearchAttributionSource;
24 import android.app.appsearch.aidl.AppSearchResultParcel;
25 import android.app.appsearch.aidl.GetDocumentsAidlRequest;
26 import android.app.appsearch.aidl.GetSchemaAidlRequest;
27 import android.app.appsearch.aidl.IAppSearchManager;
28 import android.app.appsearch.aidl.IAppSearchResultCallback;
29 import android.app.appsearch.aidl.InitializeAidlRequest;
30 import android.app.appsearch.util.ExceptionUtil;
31 import android.os.RemoteException;
32 import android.os.SystemClock;
33 import android.os.UserHandle;
34 
35 import com.android.internal.annotations.VisibleForTesting;
36 
37 import java.util.Objects;
38 import java.util.concurrent.Executor;
39 import java.util.function.Consumer;
40 
41 /**
42  * Holds the shared implementation for the query methods of {@link GlobalSearchSession} and
43  * EnterpriseGlobalSearchSession. Enterprise calls direct queries to the user's work profile
44  * AppSearch instance.
45  *
46  * @hide
47  */
48 public abstract class ReadOnlyGlobalSearchSession {
49     protected final IAppSearchManager mService;
50     protected final UserHandle mUserHandle;
51     protected final AppSearchAttributionSource mCallerAttributionSource;
52     private final boolean mIsForEnterprise;
53 
54     /**
55      * Creates a read-only search session with the given {@link IAppSearchManager} service, user,
56      * and attribution source.
57      *
58      * @param service The {@link IAppSearchManager} service from which to make api calls
59      * @param userHandle The user for which the session should be created
60      * @param callerAttributionSource The attribution source containing the caller's package name
61      *     and uid
62      * @param isForEnterprise Whether the session should query the user's enterprise profile
63      */
ReadOnlyGlobalSearchSession( @onNull IAppSearchManager service, @NonNull UserHandle userHandle, @NonNull AppSearchAttributionSource callerAttributionSource, boolean isForEnterprise)64     ReadOnlyGlobalSearchSession(
65             @NonNull IAppSearchManager service,
66             @NonNull UserHandle userHandle,
67             @NonNull AppSearchAttributionSource callerAttributionSource,
68             boolean isForEnterprise) {
69         mService = service;
70         mUserHandle = userHandle;
71         mCallerAttributionSource = callerAttributionSource;
72         mIsForEnterprise = isForEnterprise;
73     }
74 
75     // Once the callback.accept has been called here, the class is ready to use.
initialize( @onNull @allbackExecutor Executor executor, @NonNull Consumer<AppSearchResult<Void>> callback)76     protected void initialize(
77             @NonNull @CallbackExecutor Executor executor,
78             @NonNull Consumer<AppSearchResult<Void>> callback) {
79         try {
80             mService.initialize(
81                     new InitializeAidlRequest(
82                             mCallerAttributionSource,
83                             mUserHandle,
84                             /* binderCallStartTimeMillis= */ SystemClock.elapsedRealtime()),
85                     new IAppSearchResultCallback.Stub() {
86                         @Override
87                         @SuppressWarnings({"rawtypes", "unchecked"})
88                         public void onResult(AppSearchResultParcel resultParcel) {
89                             safeExecute(
90                                     executor,
91                                     callback,
92                                     () -> {
93                                         AppSearchResult<Void> result = resultParcel.getResult();
94                                         if (result.isSuccess()) {
95                                             callback.accept(
96                                                     AppSearchResult.newSuccessfulResult(null));
97                                         } else {
98                                             callback.accept(
99                                                     AppSearchResult.newFailedResult(result));
100                                         }
101                                     });
102                         }
103                     });
104         } catch (RemoteException e) {
105             ExceptionUtil.handleRemoteException(e);
106         }
107     }
108 
109     /**
110      * Retrieves {@link GenericDocument} documents, belonging to the specified package name and
111      * database name and identified by the namespace and ids in the request, from the {@link
112      * GlobalSearchSession} database.
113      *
114      * <p>If the package or database doesn't exist or if the calling package doesn't have access,
115      * the gets will be handled as failures in an {@link AppSearchBatchResult} object in the
116      * callback.
117      *
118      * @param packageName the name of the package to get from
119      * @param databaseName the name of the database to get from
120      * @param request a request containing a namespace and IDs to get documents for.
121      * @param executor Executor on which to invoke the callback.
122      * @param callback Callback to receive the pending result of performing this operation. The keys
123      *     of the returned {@link AppSearchBatchResult} are the input IDs. The values are the
124      *     returned {@link GenericDocument}s on success, or a failed {@link AppSearchResult}
125      *     otherwise. IDs that are not found will return a failed {@link AppSearchResult} with a
126      *     result code of {@link AppSearchResult#RESULT_NOT_FOUND}. If an unexpected internal error
127      *     occurs in the AppSearch service, {@link BatchResultCallback#onSystemError} will be
128      *     invoked with a {@link Throwable}.
129      */
getByDocumentId( @onNull String packageName, @NonNull String databaseName, @NonNull GetByDocumentIdRequest request, @NonNull @CallbackExecutor Executor executor, @NonNull BatchResultCallback<String, GenericDocument> callback)130     public void getByDocumentId(
131             @NonNull String packageName,
132             @NonNull String databaseName,
133             @NonNull GetByDocumentIdRequest request,
134             @NonNull @CallbackExecutor Executor executor,
135             @NonNull BatchResultCallback<String, GenericDocument> callback) {
136         Objects.requireNonNull(packageName);
137         Objects.requireNonNull(databaseName);
138         Objects.requireNonNull(request);
139         Objects.requireNonNull(executor);
140         Objects.requireNonNull(callback);
141 
142         try {
143             mService.getDocuments(
144                     new GetDocumentsAidlRequest(
145                             mCallerAttributionSource,
146                             /* targetPackageName= */ packageName,
147                             databaseName,
148                             request,
149                             mUserHandle,
150                             /* binderCallStartTimeMillis= */ SystemClock.elapsedRealtime(),
151                             mIsForEnterprise),
152                     SearchSessionUtil.createGetDocumentCallback(executor, callback));
153         } catch (RemoteException e) {
154             ExceptionUtil.handleRemoteException(e);
155         }
156     }
157 
158     /**
159      * Retrieves documents from all AppSearch databases that the querying application has access to.
160      *
161      * <p>Applications can be granted access to documents by specifying {@link
162      * SetSchemaRequest.Builder#setSchemaTypeVisibilityForPackage} when building a schema.
163      *
164      * <p>Document access can also be granted to system UIs by specifying {@link
165      * SetSchemaRequest.Builder#setSchemaTypeDisplayedBySystem} when building a schema.
166      *
167      * <p>See {@link AppSearchSession#search} for a detailed explanation on forming a query string.
168      *
169      * <p>This method is lightweight. The heavy work will be done in {@link
170      * SearchResults#getNextPage}.
171      *
172      * @param queryExpression query string to search.
173      * @param searchSpec spec for setting document filters, adding projection, setting term match
174      *     type, etc.
175      * @return a {@link SearchResults} object for retrieved matched documents.
176      */
177     @NonNull
search(@onNull String queryExpression, @NonNull SearchSpec searchSpec)178     public SearchResults search(@NonNull String queryExpression, @NonNull SearchSpec searchSpec) {
179         Objects.requireNonNull(queryExpression);
180         Objects.requireNonNull(searchSpec);
181         return new SearchResults(
182                 mService,
183                 mCallerAttributionSource,
184                 /* databaseName= */ null,
185                 queryExpression,
186                 searchSpec,
187                 mUserHandle,
188                 mIsForEnterprise);
189     }
190 
191     /**
192      * Retrieves the collection of schemas most recently successfully provided to {@link
193      * AppSearchSession#setSchema} for any types belonging to the requested package and database
194      * that the caller has been granted access to.
195      *
196      * <p>If the requested package/database combination does not exist or the caller has not been
197      * granted access to it, then an empty GetSchemaResponse will be returned.
198      *
199      * @param packageName the package that owns the requested {@link AppSearchSchema} instances.
200      * @param databaseName the database that owns the requested {@link AppSearchSchema} instances.
201      * @param executor Executor on which to invoke the callback.
202      * @param callback The pending {@link GetSchemaResponse} containing the schemas that the caller
203      *     has access to or an empty GetSchemaResponse if the request package and database does not
204      *     exist, has not set a schema or contains no schemas that are accessible to the caller.
205      */
getSchema( @onNull String packageName, @NonNull String databaseName, @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<AppSearchResult<GetSchemaResponse>> callback)206     public void getSchema(
207             @NonNull String packageName,
208             @NonNull String databaseName,
209             @NonNull @CallbackExecutor Executor executor,
210             @NonNull Consumer<AppSearchResult<GetSchemaResponse>> callback) {
211         Objects.requireNonNull(packageName);
212         Objects.requireNonNull(databaseName);
213         Objects.requireNonNull(executor);
214         Objects.requireNonNull(callback);
215         try {
216             mService.getSchema(
217                     new GetSchemaAidlRequest(
218                             mCallerAttributionSource,
219                             packageName,
220                             databaseName,
221                             mUserHandle,
222                             /* binderCallStartTimeMillis= */ SystemClock.elapsedRealtime(),
223                             mIsForEnterprise),
224                     new IAppSearchResultCallback.Stub() {
225                         @SuppressWarnings({"rawtypes", "unchecked"})
226                         @Override
227                         public void onResult(AppSearchResultParcel resultParcel) {
228                             safeExecute(
229                                     executor,
230                                     callback,
231                                     () -> {
232                                         AppSearchResult<GetSchemaResponse> result =
233                                                 resultParcel.getResult();
234                                         if (result.isSuccess()) {
235                                             GetSchemaResponse response = result.getResultValue();
236                                             callback.accept(
237                                                     AppSearchResult.newSuccessfulResult(response));
238                                         } else {
239                                             callback.accept(
240                                                     AppSearchResult.newFailedResult(result));
241                                         }
242                                     });
243                         }
244                     });
245         } catch (RemoteException e) {
246             ExceptionUtil.handleRemoteException(e);
247         }
248     }
249 
250     /**
251      * Returns the service instance to make IPC calls.
252      *
253      * @hide
254      */
255     @VisibleForTesting
getService()256     public IAppSearchManager getService() {
257         return mService;
258     }
259 
260     /**
261      * Returns if session supports Enterprise flow.
262      *
263      * @hide
264      */
265     @VisibleForTesting
isForEnterprise()266     public boolean isForEnterprise() {
267         return mIsForEnterprise;
268     }
269 }
270