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