1 /* 2 * Copyright (C) 2020 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 package com.android.server.appsearch; 17 18 import static android.app.appsearch.AppSearchResult.RESULT_DENIED; 19 import static android.app.appsearch.AppSearchResult.RESULT_INTERNAL_ERROR; 20 import static android.app.appsearch.AppSearchResult.RESULT_INVALID_ARGUMENT; 21 import static android.app.appsearch.AppSearchResult.RESULT_NOT_FOUND; 22 import static android.app.appsearch.AppSearchResult.RESULT_OK; 23 import static android.app.appsearch.AppSearchResult.RESULT_RATE_LIMITED; 24 import static android.app.appsearch.AppSearchResult.RESULT_SECURITY_ERROR; 25 import static android.app.appsearch.AppSearchResult.RESULT_TIMED_OUT; 26 import static android.app.appsearch.AppSearchResult.throwableToFailedResult; 27 import static android.app.appsearch.functions.AppFunctionManager.PERMISSION_BIND_APP_FUNCTION_SERVICE; 28 import static android.os.Process.INVALID_UID; 29 30 import static com.android.server.appsearch.external.localstorage.stats.SearchStats.VISIBILITY_SCOPE_GLOBAL; 31 import static com.android.server.appsearch.external.localstorage.stats.SearchStats.VISIBILITY_SCOPE_LOCAL; 32 import static com.android.server.appsearch.util.ServiceImplHelper.invokeCallbackOnError; 33 import static com.android.server.appsearch.util.ServiceImplHelper.invokeCallbackOnResult; 34 35 import android.annotation.BinderThread; 36 import android.annotation.NonNull; 37 import android.annotation.Nullable; 38 import android.annotation.UserIdInt; 39 import android.annotation.WorkerThread; 40 import android.app.appsearch.AppSearchBatchResult; 41 import android.app.appsearch.AppSearchEnvironment; 42 import android.app.appsearch.AppSearchEnvironmentFactory; 43 import android.app.appsearch.AppSearchMigrationHelper; 44 import android.app.appsearch.AppSearchResult; 45 import android.app.appsearch.GenericDocument; 46 import android.app.appsearch.GetSchemaResponse; 47 import android.app.appsearch.InternalSetSchemaResponse; 48 import android.app.appsearch.SearchResultPage; 49 import android.app.appsearch.SearchSpec; 50 import android.app.appsearch.SearchSuggestionResult; 51 import android.app.appsearch.SetSchemaResponse; 52 import android.app.appsearch.SetSchemaResponse.MigrationFailure; 53 import android.app.appsearch.StorageInfo; 54 import android.app.appsearch.aidl.AppSearchBatchResultParcel; 55 import android.app.appsearch.aidl.AppSearchResultParcel; 56 import android.app.appsearch.aidl.ExecuteAppFunctionAidlRequest; 57 import android.app.appsearch.aidl.GetDocumentsAidlRequest; 58 import android.app.appsearch.aidl.GetNamespacesAidlRequest; 59 import android.app.appsearch.aidl.GetNextPageAidlRequest; 60 import android.app.appsearch.aidl.GetSchemaAidlRequest; 61 import android.app.appsearch.aidl.GetStorageInfoAidlRequest; 62 import android.app.appsearch.aidl.GlobalSearchAidlRequest; 63 import android.app.appsearch.aidl.IAppFunctionService; 64 import android.app.appsearch.aidl.IAppSearchBatchResultCallback; 65 import android.app.appsearch.aidl.IAppSearchManager; 66 import android.app.appsearch.aidl.IAppSearchObserverProxy; 67 import android.app.appsearch.aidl.IAppSearchResultCallback; 68 import android.app.appsearch.aidl.InitializeAidlRequest; 69 import android.app.appsearch.aidl.InvalidateNextPageTokenAidlRequest; 70 import android.app.appsearch.aidl.PersistToDiskAidlRequest; 71 import android.app.appsearch.aidl.PutDocumentsAidlRequest; 72 import android.app.appsearch.aidl.PutDocumentsFromFileAidlRequest; 73 import android.app.appsearch.aidl.RegisterObserverCallbackAidlRequest; 74 import android.app.appsearch.aidl.RemoveByDocumentIdAidlRequest; 75 import android.app.appsearch.aidl.RemoveByQueryAidlRequest; 76 import android.app.appsearch.aidl.ReportUsageAidlRequest; 77 import android.app.appsearch.aidl.SearchAidlRequest; 78 import android.app.appsearch.aidl.SearchSuggestionAidlRequest; 79 import android.app.appsearch.aidl.SetSchemaAidlRequest; 80 import android.app.appsearch.aidl.UnregisterObserverCallbackAidlRequest; 81 import android.app.appsearch.aidl.WriteSearchResultsToFileAidlRequest; 82 import android.app.appsearch.exceptions.AppSearchException; 83 import android.app.appsearch.functions.AppFunctionService; 84 import android.app.appsearch.functions.ExecuteAppFunctionRequest; 85 import android.app.appsearch.functions.SafeOneTimeAppSearchResultCallback; 86 import android.app.appsearch.functions.ServiceCallHelper; 87 import android.app.appsearch.functions.ServiceCallHelper.ServiceUsageCompleteListener; 88 import android.app.appsearch.functions.ServiceCallHelperImpl; 89 import android.app.appsearch.safeparcel.GenericDocumentParcel; 90 import android.app.appsearch.stats.SchemaMigrationStats; 91 import android.app.appsearch.util.ExceptionUtil; 92 import android.app.appsearch.util.LogUtil; 93 import android.app.role.RoleManager; 94 import android.content.BroadcastReceiver; 95 import android.content.ComponentName; 96 import android.content.Context; 97 import android.content.Intent; 98 import android.content.IntentFilter; 99 import android.content.pm.PackageInfo; 100 import android.content.pm.PackageManager; 101 import android.content.pm.PackageStats; 102 import android.content.pm.ResolveInfo; 103 import android.content.pm.ServiceInfo; 104 import android.net.Uri; 105 import android.os.Binder; 106 import android.os.RemoteException; 107 import android.os.SystemClock; 108 import android.os.UserHandle; 109 import android.text.TextUtils; 110 import android.util.ArraySet; 111 import android.util.Log; 112 113 import com.android.internal.annotations.VisibleForTesting; 114 import com.android.server.LocalManagerRegistry; 115 import com.android.server.SystemService; 116 import com.android.server.appsearch.external.localstorage.stats.CallStats; 117 import com.android.server.appsearch.external.localstorage.stats.OptimizeStats; 118 import com.android.server.appsearch.external.localstorage.stats.SearchStats; 119 import com.android.server.appsearch.external.localstorage.stats.SetSchemaStats; 120 import com.android.server.appsearch.external.localstorage.usagereporting.SearchSessionStatsExtractor; 121 import com.android.server.appsearch.external.localstorage.visibilitystore.VisibilityStore; 122 import com.android.server.appsearch.observer.AppSearchObserverProxy; 123 import com.android.server.appsearch.stats.StatsCollector; 124 import com.android.server.appsearch.transformer.EnterpriseSearchResultPageTransformer; 125 import com.android.server.appsearch.transformer.EnterpriseSearchSpecTransformer; 126 import com.android.server.appsearch.util.AdbDumpUtil; 127 import com.android.server.appsearch.util.ApiCallRecord; 128 import com.android.server.appsearch.util.ExecutorManager; 129 import com.android.server.appsearch.util.PackageManagerUtil; 130 import com.android.server.appsearch.util.ServiceImplHelper; 131 import com.android.server.appsearch.visibilitystore.FrameworkCallerAccess; 132 import com.android.server.usage.StorageStatsManagerLocal; 133 import com.android.server.usage.StorageStatsManagerLocal.StorageStatsAugmenter; 134 135 import com.google.android.icing.proto.DebugInfoProto; 136 import com.google.android.icing.proto.DebugInfoVerbosity; 137 import com.google.android.icing.proto.PersistType; 138 139 import java.io.DataInputStream; 140 import java.io.DataOutputStream; 141 import java.io.EOFException; 142 import java.io.FileDescriptor; 143 import java.io.FileInputStream; 144 import java.io.FileOutputStream; 145 import java.io.IOException; 146 import java.io.PrintWriter; 147 import java.util.ArrayList; 148 import java.util.List; 149 import java.util.Map; 150 import java.util.Objects; 151 import java.util.Set; 152 import java.util.concurrent.Executor; 153 154 /** 155 * The main service implementation which contains AppSearch's platform functionality. 156 * 157 * @hide 158 */ 159 public class AppSearchManagerService extends SystemService { 160 private static final String TAG = "AppSearchManagerService"; 161 @VisibleForTesting 162 static final String SYSTEM_UI_INTELLIGENCE = "android.app.role.SYSTEM_UI_INTELLIGENCE"; 163 164 /** 165 * An executor for system activity not tied to any particular user. 166 * 167 * <p>NOTE: Never call shutdownNow(). AppSearchManagerService persists forever even as 168 * individual users are added and removed -- without this pool the service will be broken. And, 169 * clients waiting for callbacks will never receive anything and will hang. 170 */ 171 private static final Executor SHARED_EXECUTOR = ExecutorManager.createDefaultExecutorService(); 172 173 private final Context mContext; 174 private final ExecutorManager mExecutorManager; 175 private final AppSearchEnvironment mAppSearchEnvironment; 176 private final ServiceAppSearchConfig mAppSearchConfig; 177 178 private PackageManager mPackageManager; 179 private RoleManager mRoleManager; 180 private ServiceImplHelper mServiceImplHelper; 181 private AppSearchUserInstanceManager mAppSearchUserInstanceManager; 182 183 // Keep a reference for the lifecycle instance, so we can access other services like 184 // ContactsIndexer for dumpsys purpose. 185 private final AppSearchModule.Lifecycle mLifecycle; 186 private final ServiceCallHelper<IAppFunctionService> mAppFunctionServiceCallHelper; 187 private final SearchSessionStatsExtractor mSearchSessionStatsExtractor; 188 AppSearchManagerService(Context context, AppSearchModule.Lifecycle lifecycle)189 public AppSearchManagerService(Context context, AppSearchModule.Lifecycle lifecycle) { 190 this(context, lifecycle, new ServiceCallHelperImpl<>( 191 context, IAppFunctionService.Stub::asInterface, SHARED_EXECUTOR)); 192 } 193 194 @VisibleForTesting AppSearchManagerService( Context context, AppSearchModule.Lifecycle lifecycle, ServiceCallHelper<IAppFunctionService> appFunctionServiceCallHelper)195 public AppSearchManagerService( 196 Context context, 197 AppSearchModule.Lifecycle lifecycle, 198 ServiceCallHelper<IAppFunctionService> appFunctionServiceCallHelper) { 199 super(context); 200 mContext = Objects.requireNonNull(context); 201 mLifecycle = Objects.requireNonNull(lifecycle); 202 mAppSearchEnvironment = AppSearchEnvironmentFactory.getEnvironmentInstance(); 203 mAppSearchConfig = AppSearchComponentFactory.getConfigInstance(SHARED_EXECUTOR); 204 mExecutorManager = new ExecutorManager(mAppSearchConfig); 205 mAppFunctionServiceCallHelper = Objects.requireNonNull(appFunctionServiceCallHelper); 206 mSearchSessionStatsExtractor = new SearchSessionStatsExtractor(); 207 } 208 209 @Override onStart()210 public void onStart() { 211 publishBinderService(Context.APP_SEARCH_SERVICE, new Stub()); 212 mPackageManager = getContext().getPackageManager(); 213 mRoleManager = getContext().getSystemService(RoleManager.class); 214 mServiceImplHelper = new ServiceImplHelper(mContext, mExecutorManager); 215 mAppSearchUserInstanceManager = AppSearchUserInstanceManager.getInstance(); 216 registerReceivers(); 217 LocalManagerRegistry.getManager(StorageStatsManagerLocal.class) 218 .registerStorageStatsAugmenter(new AppSearchStorageStatsAugmenter(), TAG); 219 LocalManagerRegistry.addManager(LocalService.class, new LocalService()); 220 } 221 222 @Override onBootPhase( int phase)223 public void onBootPhase(/* @BootPhase */ int phase) { 224 if (phase == PHASE_BOOT_COMPLETED) { 225 StatsCollector.getInstance(mContext, SHARED_EXECUTOR); 226 } 227 } 228 registerReceivers()229 private void registerReceivers() { 230 mContext.registerReceiverForAllUsers( 231 new UserActionReceiver(), 232 new IntentFilter(Intent.ACTION_USER_REMOVED), 233 /* broadcastPermission= */ null, 234 /* scheduler= */ null); 235 236 //TODO(b/145759910) Add a direct callback when user clears the data instead of relying on 237 // broadcasts 238 IntentFilter packageChangedFilter = new IntentFilter(); 239 packageChangedFilter.addAction(Intent.ACTION_PACKAGE_FULLY_REMOVED); 240 packageChangedFilter.addAction(Intent.ACTION_PACKAGE_DATA_CLEARED); 241 packageChangedFilter.addDataScheme("package"); 242 packageChangedFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 243 mContext.registerReceiverForAllUsers( 244 new PackageChangedReceiver(), 245 packageChangedFilter, 246 /* broadcastPermission= */ null, 247 /* scheduler= */ null); 248 } 249 250 private class UserActionReceiver extends BroadcastReceiver { 251 @Override onReceive(@onNull Context context, @NonNull Intent intent)252 public void onReceive(@NonNull Context context, @NonNull Intent intent) { 253 Objects.requireNonNull(context); 254 Objects.requireNonNull(intent); 255 if (Intent.ACTION_USER_REMOVED.equals(intent.getAction())) { 256 UserHandle userHandle = intent.getParcelableExtra(Intent.EXTRA_USER); 257 if (userHandle == null) { 258 Log.e(TAG, 259 "Extra " + Intent.EXTRA_USER + " is missing in the intent: " + intent); 260 return; 261 } 262 // We can handle user removal the same way as user stopping: shut down the executor 263 // and close icing. The data of AppSearch is saved in the "credential encrypted" 264 // system directory of each user. That directory will be auto-deleted when a user is 265 // removed, so we don't need it handle it specially. 266 onUserStopping(userHandle); 267 } else { 268 Log.e(TAG, "Received unknown intent: " + intent); 269 } 270 } 271 } 272 273 private class PackageChangedReceiver extends BroadcastReceiver { 274 @Override onReceive(@onNull Context context, @NonNull Intent intent)275 public void onReceive(@NonNull Context context, @NonNull Intent intent) { 276 Objects.requireNonNull(context); 277 Objects.requireNonNull(intent); 278 279 String action = intent.getAction(); 280 if (action == null) { 281 return; 282 } 283 284 switch (action) { 285 case Intent.ACTION_PACKAGE_FULLY_REMOVED: 286 case Intent.ACTION_PACKAGE_DATA_CLEARED: 287 Uri data = intent.getData(); 288 if (data == null) { 289 Log.e(TAG, "Data is missing in the intent: " + intent); 290 return; 291 } 292 293 String packageName = data.getSchemeSpecificPart(); 294 if (packageName == null) { 295 Log.e(TAG, "Package name is missing in the intent: " + intent); 296 return; 297 } 298 299 if (LogUtil.DEBUG) { 300 Log.d(TAG, "Received " + action + " broadcast on package: " + packageName); 301 } 302 303 int uid = intent.getIntExtra(Intent.EXTRA_UID, INVALID_UID); 304 if (uid == INVALID_UID) { 305 Log.e(TAG, "uid is missing in the intent: " + intent); 306 return; 307 } 308 309 handlePackageRemoved(packageName, uid); 310 break; 311 default: 312 Log.e(TAG, "Received unknown intent: " + intent); 313 } 314 } 315 } 316 handlePackageRemoved(@onNull String packageName, int uid)317 private void handlePackageRemoved(@NonNull String packageName, int uid) { 318 UserHandle userHandle = UserHandle.getUserHandleForUid(uid); 319 if (mServiceImplHelper.isUserLocked(userHandle)) { 320 // We cannot access a locked user's directory and remove package data from it. 321 // We should remove those uninstalled package data when the user is unlocking. 322 return; 323 } 324 // Only clear the package's data if AppSearch exists for this user. 325 if (mAppSearchEnvironment.getAppSearchDir(mContext, userHandle).exists()) { 326 mExecutorManager.getOrCreateUserExecutor(userHandle).execute(() -> { 327 try { 328 Context userContext = mAppSearchEnvironment 329 .createContextAsUser(mContext, userHandle); 330 AppSearchUserInstance instance = 331 mAppSearchUserInstanceManager.getOrCreateUserInstance( 332 userContext, 333 userHandle, 334 mAppSearchConfig); 335 instance.getAppSearchImpl().clearPackageData(packageName); 336 dispatchChangeNotifications(instance); 337 instance.getLogger().removeCacheForPackage(packageName); 338 } catch (AppSearchException | RuntimeException e) { 339 Log.e(TAG, "Unable to remove data for package: " + packageName, e); 340 ExceptionUtil.handleException(e); 341 } 342 }); 343 } 344 } 345 346 @Override onUserUnlocking(@onNull TargetUser user)347 public void onUserUnlocking(@NonNull TargetUser user) { 348 Objects.requireNonNull(user); 349 UserHandle userHandle = user.getUserHandle(); 350 mServiceImplHelper.setUserIsLocked(userHandle, false); 351 352 // Only schedule task if AppSearch exists for this user. 353 if (mAppSearchEnvironment.getAppSearchDir(mContext, userHandle).exists()) { 354 mExecutorManager.getOrCreateUserExecutor(userHandle).execute(() -> { 355 // Try to prune garbage package data, this is to recover if user remove a package 356 // and reboot the device before we prune the package data. 357 try { 358 Context userContext = mAppSearchEnvironment 359 .createContextAsUser(mContext, userHandle); 360 AppSearchUserInstance instance = 361 mAppSearchUserInstanceManager.getOrCreateUserInstance( 362 userContext, 363 userHandle, 364 mAppSearchConfig); 365 List<PackageInfo> installedPackageInfos = userContext 366 .getPackageManager() 367 .getInstalledPackages(/* flags= */ 0); 368 Set<String> packagesToKeep = new ArraySet<>(installedPackageInfos.size()); 369 for (int i = 0; i < installedPackageInfos.size(); i++) { 370 packagesToKeep.add(installedPackageInfos.get(i).packageName); 371 } 372 packagesToKeep.add(VisibilityStore.VISIBILITY_PACKAGE_NAME); 373 instance.getAppSearchImpl().prunePackageData(packagesToKeep); 374 } catch (AppSearchException | RuntimeException e) { 375 Log.e(TAG, "Unable to prune packages for " + user, e); 376 ExceptionUtil.handleException(e); 377 } 378 379 // Try to schedule fully persist job. 380 try { 381 AppSearchMaintenanceService.scheduleFullyPersistJob(mContext, 382 userHandle.getIdentifier(), 383 mAppSearchConfig.getCachedFullyPersistJobIntervalMillis()); 384 } catch (RuntimeException e) { 385 Log.e(TAG, "Unable to schedule fully persist job for " + user, e); 386 ExceptionUtil.handleException(e); 387 } 388 }); 389 } 390 } 391 392 @Override onUserStopping(@onNull TargetUser user)393 public void onUserStopping(@NonNull TargetUser user) { 394 Objects.requireNonNull(user); 395 onUserStopping(user.getUserHandle()); 396 } 397 onUserStopping(@onNull UserHandle userHandle)398 private void onUserStopping(@NonNull UserHandle userHandle) { 399 Objects.requireNonNull(userHandle); 400 if (LogUtil.INFO) { 401 Log.i(TAG, "Shutting down AppSearch for user " + userHandle); 402 } 403 try { 404 mServiceImplHelper.setUserIsLocked(userHandle, true); 405 mExecutorManager.shutDownAndRemoveUserExecutor(userHandle); 406 mAppSearchUserInstanceManager.closeAndRemoveUserInstance(userHandle); 407 AppSearchMaintenanceService.cancelFullyPersistJobIfScheduled( 408 mContext, userHandle.getIdentifier()); 409 if (LogUtil.INFO) { 410 Log.i(TAG, "Removed AppSearchImpl instance for: " + userHandle); 411 } 412 } catch (InterruptedException | RuntimeException e) { 413 Log.e(TAG, "Unable to remove data for: " + userHandle, e); 414 ExceptionUtil.handleException(e); 415 } 416 } 417 418 class LocalService { 419 /** Persist all pending mutation operation to disk for the given user. */ doFullyPersistForUser(@serIdInt int userId)420 public void doFullyPersistForUser(@UserIdInt int userId) throws AppSearchException { 421 UserHandle targetUser = UserHandle.getUserHandleForUid(userId); 422 AppSearchUserInstance instance = 423 mAppSearchUserInstanceManager.getUserInstance(targetUser); 424 instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL); 425 } 426 } 427 428 private class Stub extends IAppSearchManager.Stub { 429 @Override setSchema( @onNull SetSchemaAidlRequest request, @NonNull IAppSearchResultCallback callback)430 public void setSchema( 431 @NonNull SetSchemaAidlRequest request, 432 @NonNull IAppSearchResultCallback callback) { 433 Objects.requireNonNull(request); 434 Objects.requireNonNull(callback); 435 436 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 437 long verifyIncomingCallLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 438 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 439 request.getCallerAttributionSource(), request.getUserHandle(), callback); 440 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 441 if (targetUser == null) { 442 return; // Verification failed; verifyIncomingCall triggered callback. 443 } 444 if (checkCallDenied(callingPackageName, request.getDatabaseName(), 445 CallStats.CALL_TYPE_SET_SCHEMA, callback, targetUser, 446 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 447 /* numOperations= */ 1)) { 448 return; 449 } 450 long verifyIncomingCallLatencyEndTimeMillis = SystemClock.elapsedRealtime(); 451 452 long waitExecutorStartTimeMillis = SystemClock.elapsedRealtime(); 453 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync( 454 targetUser, callback, callingPackageName, CallStats.CALL_TYPE_SET_SCHEMA, 455 () -> { 456 long waitExecutorEndTimeMillis = SystemClock.elapsedRealtime(); 457 458 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 459 AppSearchUserInstance instance = null; 460 SetSchemaStats.Builder setSchemaStatsBuilder = new SetSchemaStats.Builder( 461 callingPackageName, request.getDatabaseName()); 462 int operationSuccessCount = 0; 463 int operationFailureCount = 0; 464 try { 465 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 466 InternalSetSchemaResponse internalSetSchemaResponse = 467 instance.getAppSearchImpl().setSchema( 468 callingPackageName, 469 request.getDatabaseName(), 470 request.getSchemas(), 471 request.getVisibilityConfigs(), 472 request.isForceOverride(), 473 request.getSchemaVersion(), 474 setSchemaStatsBuilder); 475 ++operationSuccessCount; 476 invokeCallbackOnResult(callback, AppSearchResultParcel 477 .fromInternalSetSchemaResponse(internalSetSchemaResponse)); 478 479 // Schedule a task to dispatch change notifications. See requirements for where 480 // the method is called documented in the method description. 481 long dispatchNotificationLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 482 dispatchChangeNotifications(instance); 483 long dispatchNotificationLatencyEndTimeMillis = SystemClock.elapsedRealtime(); 484 485 // setSchema will sync the schemas in the request to AppSearch, any existing 486 // schemas which are not included in the request will be deleted if we force 487 // override incompatible schemas. And all documents of these types will be 488 // deleted as well. We should checkForOptimize for these deletion. 489 long checkForOptimizeLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 490 checkForOptimize(targetUser, instance); 491 long checkForOptimizeLatencyEndTimeMillis = SystemClock.elapsedRealtime(); 492 493 setSchemaStatsBuilder 494 .setVerifyIncomingCallLatencyMillis( 495 (int) (verifyIncomingCallLatencyEndTimeMillis 496 - verifyIncomingCallLatencyStartTimeMillis)) 497 .setExecutorAcquisitionLatencyMillis( 498 (int) (waitExecutorEndTimeMillis 499 - waitExecutorStartTimeMillis)) 500 // This operation no longer exists, so this latency is always 0 501 .setRebuildFromBundleLatencyMillis(0) 502 .setDispatchChangeNotificationsLatencyMillis( 503 (int) (dispatchNotificationLatencyEndTimeMillis 504 - dispatchNotificationLatencyStartTimeMillis)) 505 .setOptimizeLatencyMillis( 506 (int) (checkForOptimizeLatencyEndTimeMillis 507 - checkForOptimizeLatencyStartTimeMillis)); 508 } catch (AppSearchException | RuntimeException e) { 509 ++operationFailureCount; 510 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 511 statusCode = failedResult.getResultCode(); 512 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 513 failedResult)); 514 } finally { 515 if (instance != null) { 516 int estimatedBinderLatencyMillis = 517 2 * (int) (totalLatencyStartTimeMillis - 518 request.getBinderCallStartTimeMillis()); 519 int totalLatencyMillis = 520 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 521 instance.getLogger().logStats(new CallStats.Builder() 522 .setPackageName(callingPackageName) 523 .setDatabase(request.getDatabaseName()) 524 .setStatusCode(statusCode) 525 .setTotalLatencyMillis(totalLatencyMillis) 526 .setCallType(CallStats.CALL_TYPE_SET_SCHEMA) 527 // TODO(b/173532925) check the existing binder call latency chart 528 // is good enough for us: 529 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 530 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 531 .setNumOperationsSucceeded(operationSuccessCount) 532 .setNumOperationsFailed(operationFailureCount) 533 .build()); 534 instance.getLogger().logStats(setSchemaStatsBuilder 535 .setStatusCode(statusCode) 536 .setSchemaMigrationCallType(request.getSchemaMigrationCallType()) 537 .setTotalLatencyMillis(totalLatencyMillis) 538 .build()); 539 } 540 } 541 }); 542 if (!callAccepted) { 543 logRateLimitedOrCallDeniedCallStats(callingPackageName, request.getDatabaseName(), 544 CallStats.CALL_TYPE_SET_SCHEMA, targetUser, 545 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 546 /*numOperations=*/ 1, RESULT_RATE_LIMITED); 547 } 548 } 549 550 @Override getSchema( @onNull GetSchemaAidlRequest request, @NonNull IAppSearchResultCallback callback)551 public void getSchema( 552 @NonNull GetSchemaAidlRequest request, 553 @NonNull IAppSearchResultCallback callback) { 554 Objects.requireNonNull(request); 555 Objects.requireNonNull(callback); 556 557 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 558 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 559 request.getCallerAttributionSource(), request.getUserHandle(), callback); 560 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 561 if (targetUser == null) { 562 return; // Verification failed; verifyIncomingCall triggered callback. 563 } 564 // Get the enterprise user for enterprise calls 565 UserHandle userToQuery = mServiceImplHelper.getUserToQuery(request.isForEnterprise(), 566 targetUser); 567 if (userToQuery == null) { 568 // Return an empty response if we tried to and couldn't get the enterprise user 569 invokeCallbackOnResult(callback, AppSearchResultParcel.fromGetSchemaResponse( 570 new GetSchemaResponse.Builder().build())); 571 return; 572 } 573 boolean global = isGlobalCall(callingPackageName, request.getTargetPackageName(), 574 request.isForEnterprise()); 575 // We deny based on the calling package and calling database names. If the calling 576 // package does not match the target package, then the call is global and the target 577 // database is not a calling database. 578 String callingDatabaseName = global ? null : request.getDatabaseName(); 579 int callType = global ? CallStats.CALL_TYPE_GLOBAL_GET_SCHEMA 580 : CallStats.CALL_TYPE_GET_SCHEMA; 581 if (checkCallDenied(callingPackageName, callingDatabaseName, callType, callback, 582 targetUser, request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 583 /* numOperations= */ 1)) { 584 return; 585 } 586 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 587 callback, callingPackageName, callType, () -> { 588 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 589 AppSearchUserInstance instance = null; 590 int operationSuccessCount = 0; 591 int operationFailureCount = 0; 592 try { 593 instance = mAppSearchUserInstanceManager.getUserInstance(userToQuery); 594 595 boolean callerHasSystemAccess = instance.getVisibilityChecker() 596 .doesCallerHaveSystemAccess(callingPackageName); 597 GetSchemaResponse response = 598 instance.getAppSearchImpl().getSchema( 599 request.getTargetPackageName(), 600 request.getDatabaseName(), 601 new FrameworkCallerAccess(request.getCallerAttributionSource(), 602 callerHasSystemAccess, request.isForEnterprise())); 603 ++operationSuccessCount; 604 invokeCallbackOnResult(callback, AppSearchResultParcel 605 .fromGetSchemaResponse(response) 606 ); 607 } catch (AppSearchException | RuntimeException e) { 608 ++operationFailureCount; 609 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 610 statusCode = failedResult.getResultCode(); 611 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 612 failedResult)); 613 } finally { 614 if (instance != null) { 615 int estimatedBinderLatencyMillis = 616 2 * (int) (totalLatencyStartTimeMillis 617 - request.getBinderCallStartTimeMillis()); 618 int totalLatencyMillis = 619 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 620 instance.getLogger().logStats(new CallStats.Builder() 621 .setPackageName(callingPackageName) 622 .setDatabase(request.getDatabaseName()) 623 .setStatusCode(statusCode) 624 .setTotalLatencyMillis(totalLatencyMillis) 625 .setCallType(callType) 626 // TODO(b/173532925) check the existing binder call latency chart 627 // is good enough for us: 628 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 629 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 630 .setNumOperationsSucceeded(operationSuccessCount) 631 .setNumOperationsFailed(operationFailureCount) 632 .build()); 633 } 634 } 635 }); 636 if (!callAccepted) { 637 logRateLimitedOrCallDeniedCallStats(callingPackageName, callingDatabaseName, 638 callType, targetUser, request.getBinderCallStartTimeMillis(), 639 totalLatencyStartTimeMillis, /*numOperations=*/ 1, RESULT_RATE_LIMITED); 640 } 641 } 642 643 @Override getNamespaces( @onNull GetNamespacesAidlRequest request, @NonNull IAppSearchResultCallback callback)644 public void getNamespaces( 645 @NonNull GetNamespacesAidlRequest request, 646 @NonNull IAppSearchResultCallback callback) { 647 Objects.requireNonNull(request); 648 Objects.requireNonNull(callback); 649 650 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 651 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 652 request.getCallerAttributionSource(), request.getUserHandle(), callback); 653 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 654 if (targetUser == null) { 655 return; // Verification failed; verifyIncomingCall triggered callback. 656 } 657 if (checkCallDenied(callingPackageName, request.getDatabaseName(), 658 CallStats.CALL_TYPE_GET_NAMESPACES, callback, targetUser, 659 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 660 /* numOperations= */ 1)) { 661 return; 662 } 663 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 664 callback, callingPackageName, CallStats.CALL_TYPE_GET_NAMESPACES, () -> { 665 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 666 AppSearchUserInstance instance = null; 667 int operationSuccessCount = 0; 668 int operationFailureCount = 0; 669 try { 670 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 671 List<String> namespaces = 672 instance.getAppSearchImpl().getNamespaces( 673 callingPackageName, request.getDatabaseName()); 674 ++operationSuccessCount; 675 invokeCallbackOnResult(callback, AppSearchResultParcel 676 .fromStringList(namespaces) 677 ); 678 } catch (AppSearchException | RuntimeException e) { 679 ++operationFailureCount; 680 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 681 statusCode = failedResult.getResultCode(); 682 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 683 failedResult)); 684 } finally { 685 if (instance != null) { 686 int estimatedBinderLatencyMillis = 687 2 * (int) (totalLatencyStartTimeMillis 688 - request.getBinderCallStartTimeMillis()); 689 int totalLatencyMillis = 690 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 691 instance.getLogger().logStats(new CallStats.Builder() 692 .setPackageName(callingPackageName) 693 .setDatabase(request.getDatabaseName()) 694 .setStatusCode(statusCode) 695 .setTotalLatencyMillis(totalLatencyMillis) 696 .setCallType(CallStats.CALL_TYPE_GET_NAMESPACES) 697 // TODO(b/173532925) check the existing binder call latency chart 698 // is good enough for us: 699 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 700 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 701 .setNumOperationsSucceeded(operationSuccessCount) 702 .setNumOperationsFailed(operationFailureCount) 703 .build()); 704 } 705 } 706 }); 707 if (!callAccepted) { 708 logRateLimitedOrCallDeniedCallStats(callingPackageName, request.getDatabaseName(), 709 CallStats.CALL_TYPE_GET_NAMESPACES, targetUser, 710 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 711 /*numOperations=*/ 1, RESULT_RATE_LIMITED); 712 } 713 } 714 715 @Override putDocuments( @onNull PutDocumentsAidlRequest request, @NonNull IAppSearchBatchResultCallback callback)716 public void putDocuments( 717 @NonNull PutDocumentsAidlRequest request, 718 @NonNull IAppSearchBatchResultCallback callback) { 719 Objects.requireNonNull(request); 720 Objects.requireNonNull(callback); 721 722 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 723 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 724 request.getCallerAttributionSource(), request.getUserHandle(), callback); 725 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 726 if (targetUser == null) { 727 return; // Verification failed; verifyIncomingCall triggered callback. 728 } 729 if (checkCallDenied(callingPackageName, request.getDatabaseName(), 730 CallStats.CALL_TYPE_PUT_DOCUMENTS, callback, targetUser, 731 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 732 /* numOperations= */ request.getDocumentsParcel().getTotalDocumentCount())) { 733 return; 734 } 735 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 736 callback, callingPackageName, CallStats.CALL_TYPE_PUT_DOCUMENTS, () -> { 737 @AppSearchResult.ResultCode int statusCode = RESULT_OK; 738 AppSearchUserInstance instance = null; 739 int operationSuccessCount = 0; 740 int operationFailureCount = 0; 741 List<GenericDocument> takenActionGenericDocuments = null; // initialize later 742 743 try { 744 AppSearchBatchResult.Builder<String, Void> resultBuilder = 745 new AppSearchBatchResult.Builder<>(); 746 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 747 List<GenericDocumentParcel> documentParcels = 748 request.getDocumentsParcel().getDocumentParcels(); 749 List<GenericDocumentParcel> takenActionDocumentParcels = 750 request.getDocumentsParcel().getTakenActionGenericDocumentParcels(); 751 752 // Write GenericDocument of general documents 753 for (int i = 0; i < documentParcels.size(); i++) { 754 GenericDocument document = new GenericDocument(documentParcels.get(i)); 755 try { 756 instance.getAppSearchImpl().putDocument( 757 callingPackageName, 758 request.getDatabaseName(), 759 document, 760 /* sendChangeNotifications= */ true, 761 instance.getLogger()); 762 resultBuilder.setSuccess(document.getId(), /* value= */ null); 763 ++operationSuccessCount; 764 } catch (AppSearchException | RuntimeException e) { 765 // We don't rethrow here, so we can keep trying with the 766 // following documents. 767 AppSearchResult<Void> result = throwableToFailedResult(e); 768 resultBuilder.setResult(document.getId(), result); 769 // Since we can only include one status code in the atom, 770 // for failures, we would just save the one for the last failure 771 statusCode = result.getResultCode(); 772 ++operationFailureCount; 773 } 774 } 775 776 // Write GenericDocument of taken actions 777 if (!takenActionDocumentParcels.isEmpty()) { 778 takenActionGenericDocuments = 779 new ArrayList<>(takenActionDocumentParcels.size()); 780 } 781 for (int i = 0; i < takenActionDocumentParcels.size(); i++) { 782 GenericDocument document = 783 new GenericDocument(takenActionDocumentParcels.get(i)); 784 takenActionGenericDocuments.add(document); 785 try { 786 instance.getAppSearchImpl().putDocument( 787 callingPackageName, 788 request.getDatabaseName(), 789 document, 790 /* sendChangeNotifications= */ true, 791 instance.getLogger()); 792 resultBuilder.setSuccess(document.getId(), /* value= */ null); 793 ++operationSuccessCount; 794 } catch (AppSearchException | RuntimeException e) { 795 // We don't rethrow here, so we can keep trying with the 796 // following documents. 797 AppSearchResult<Void> result = throwableToFailedResult(e); 798 resultBuilder.setResult(document.getId(), result); 799 // Since we can only include one status code in the atom, 800 // for failures, we would just save the one for the last failure 801 statusCode = result.getResultCode(); 802 ++operationFailureCount; 803 } 804 } 805 806 // Now that the batch has been written. Persist the newly written data. 807 instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); 808 invokeCallbackOnResult(callback, AppSearchBatchResultParcel 809 .fromStringToVoid(resultBuilder.build())); 810 811 // Schedule a task to dispatch change notifications. See requirements for where 812 // the method is called documented in the method description. 813 dispatchChangeNotifications(instance); 814 815 // The existing documents with same ID will be deleted, so there may be some 816 // resources that could be released after optimize(). 817 checkForOptimize( 818 targetUser, 819 instance, 820 /* mutateBatchSize= */ 821 request.getDocumentsParcel().getTotalDocumentCount()); 822 } catch (AppSearchException | RuntimeException e) { 823 ++operationFailureCount; 824 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 825 statusCode = failedResult.getResultCode(); 826 invokeCallbackOnError(callback, failedResult); 827 } finally { 828 // TODO(b/261959320) add outstanding latency fields in AppSearch stats 829 if (instance != null) { 830 int estimatedBinderLatencyMillis = 831 2 * (int) (totalLatencyStartTimeMillis 832 - request.getBinderCallStartTimeMillis()); 833 int totalLatencyMillis = 834 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 835 instance.getLogger().logStats(new CallStats.Builder() 836 .setPackageName(callingPackageName) 837 .setDatabase(request.getDatabaseName()) 838 .setStatusCode(statusCode) 839 .setTotalLatencyMillis(totalLatencyMillis) 840 .setCallType(CallStats.CALL_TYPE_PUT_DOCUMENTS) 841 // TODO(b/173532925) check the existing binder call latency chart 842 // is good enough for us: 843 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 844 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 845 .setNumOperationsSucceeded(operationSuccessCount) 846 .setNumOperationsFailed(operationFailureCount) 847 .build()); 848 849 // Extract metrics from taken action generic documents and add log. 850 if (takenActionGenericDocuments != null 851 && !takenActionGenericDocuments.isEmpty()) { 852 instance.getLogger() 853 .logStats(mSearchSessionStatsExtractor.extract( 854 callingPackageName, 855 request.getDatabaseName(), 856 takenActionGenericDocuments)); 857 } 858 } 859 } 860 }); 861 if (!callAccepted) { 862 logRateLimitedOrCallDeniedCallStats(callingPackageName, request.getDatabaseName(), 863 CallStats.CALL_TYPE_PUT_DOCUMENTS, targetUser, 864 request.getBinderCallStartTimeMillis(), 865 totalLatencyStartTimeMillis, 866 /* numOperations= */ 867 request.getDocumentsParcel().getTotalDocumentCount(), RESULT_RATE_LIMITED); 868 } 869 } 870 871 @Override getDocuments( @onNull GetDocumentsAidlRequest request, @NonNull IAppSearchBatchResultCallback callback)872 public void getDocuments( 873 @NonNull GetDocumentsAidlRequest request, 874 @NonNull IAppSearchBatchResultCallback callback) { 875 Objects.requireNonNull(request); 876 Objects.requireNonNull(callback); 877 878 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 879 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 880 request.getCallerAttributionSource(), request.getUserHandle(), callback); 881 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 882 if (targetUser == null) { 883 return; // Verification failed; verifyIncomingCall triggered callback. 884 } 885 // Get the enterprise user for enterprise calls 886 UserHandle userToQuery = mServiceImplHelper.getUserToQuery( 887 request.isForEnterprise(), targetUser); 888 if (userToQuery == null) { 889 // Return an empty batch result if we tried to and couldn't get the enterprise user 890 invokeCallbackOnResult(callback, AppSearchBatchResultParcel 891 .fromStringToGenericDocumentParcel(new AppSearchBatchResult 892 .Builder<String, GenericDocumentParcel>().build())); 893 return; 894 } 895 // TODO(b/319315074): consider removing local getDocument and just use globalGetDocument 896 // instead; this would simplify the code and assure us that enterprise calls definitely 897 // go through visibility checks 898 boolean global = isGlobalCall(callingPackageName, request.getTargetPackageName(), 899 request.isForEnterprise()); 900 // We deny based on the calling package and calling database names. If the calling 901 // package does not match the target package, then the call is global and the target 902 // database is not a calling database. 903 String callingDatabaseName = global ? null : request.getDatabaseName(); 904 int callType = global ? CallStats.CALL_TYPE_GLOBAL_GET_DOCUMENT_BY_ID 905 : CallStats.CALL_TYPE_GET_DOCUMENTS; 906 if (checkCallDenied(callingPackageName, callingDatabaseName, callType, callback, 907 targetUser, request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 908 /* numOperations= */ request.getGetByDocumentIdRequest().getIds().size())) { 909 return; 910 } 911 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 912 callback, callingPackageName, callType, () -> { 913 @AppSearchResult.ResultCode int statusCode = RESULT_OK; 914 AppSearchUserInstance instance = null; 915 int operationSuccessCount = 0; 916 int operationFailureCount = 0; 917 try { 918 AppSearchBatchResult.Builder<String, GenericDocumentParcel> resultBuilder = 919 new AppSearchBatchResult.Builder<>(); 920 instance = mAppSearchUserInstanceManager.getUserInstance(userToQuery); 921 for (String id : request.getGetByDocumentIdRequest().getIds()) { 922 try { 923 GenericDocument document; 924 if (global) { 925 boolean callerHasSystemAccess = instance.getVisibilityChecker() 926 .doesCallerHaveSystemAccess( 927 request.getCallerAttributionSource() 928 .getPackageName()); 929 Map<String, List<String>> typePropertyPaths = 930 request.getGetByDocumentIdRequest().getProjections(); 931 if (request.isForEnterprise()) { 932 EnterpriseSearchSpecTransformer.transformPropertiesMap( 933 typePropertyPaths); 934 } 935 document = instance.getAppSearchImpl().globalGetDocument( 936 request.getTargetPackageName(), 937 request.getDatabaseName(), 938 request.getGetByDocumentIdRequest().getNamespace(), 939 id, 940 typePropertyPaths, 941 new FrameworkCallerAccess( 942 request.getCallerAttributionSource(), 943 callerHasSystemAccess, 944 request.isForEnterprise())); 945 if (request.isForEnterprise()) { 946 document = 947 EnterpriseSearchResultPageTransformer.transformDocument( 948 request.getTargetPackageName(), 949 request.getDatabaseName(), 950 document); 951 } 952 } else { 953 document = instance.getAppSearchImpl().getDocument( 954 request.getTargetPackageName(), 955 request.getDatabaseName(), 956 request.getGetByDocumentIdRequest().getNamespace(), 957 id, 958 request.getGetByDocumentIdRequest().getProjections()); 959 } 960 ++operationSuccessCount; 961 resultBuilder.setSuccess(id, document.getDocumentParcel()); 962 } catch (AppSearchException | RuntimeException e) { 963 // Since we can only include one status code in the atom, 964 // for failures, we would just save the one for the last failure 965 // Also, we don't rethrow here, so we can keep trying for 966 // the following ones. 967 AppSearchResult<GenericDocumentParcel> result = 968 throwableToFailedResult(e); 969 resultBuilder.setResult(id, result); 970 statusCode = result.getResultCode(); 971 ++operationFailureCount; 972 } 973 } 974 invokeCallbackOnResult(callback, AppSearchBatchResultParcel 975 .fromStringToGenericDocumentParcel(resultBuilder.build())); 976 } catch (RuntimeException e) { 977 ++operationFailureCount; 978 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 979 statusCode = failedResult.getResultCode(); 980 invokeCallbackOnError(callback, failedResult); 981 } finally { 982 // TODO(b/261959320) add outstanding latency fields in AppSearch stats 983 if (instance != null) { 984 int estimatedBinderLatencyMillis = 985 2 * (int) (totalLatencyStartTimeMillis - 986 request.getBinderCallStartTimeMillis()); 987 int totalLatencyMillis = 988 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 989 instance.getLogger().logStats(new CallStats.Builder() 990 .setPackageName(callingPackageName) 991 .setDatabase(request.getDatabaseName()) 992 .setStatusCode(statusCode) 993 .setTotalLatencyMillis(totalLatencyMillis) 994 .setCallType(callType) 995 // TODO(b/173532925) check the existing binder call latency chart 996 // is good enough for us: 997 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 998 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 999 .setNumOperationsSucceeded(operationSuccessCount) 1000 .setNumOperationsFailed(operationFailureCount) 1001 .build()); 1002 } 1003 } 1004 }); 1005 if (!callAccepted) { 1006 logRateLimitedOrCallDeniedCallStats(callingPackageName, callingDatabaseName, 1007 callType, targetUser, request.getBinderCallStartTimeMillis(), 1008 totalLatencyStartTimeMillis, 1009 /* numOperations= */ request.getGetByDocumentIdRequest().getIds().size(), 1010 RESULT_RATE_LIMITED); 1011 1012 } 1013 } 1014 1015 @Override search( @onNull SearchAidlRequest request, @NonNull IAppSearchResultCallback callback)1016 public void search( 1017 @NonNull SearchAidlRequest request, 1018 @NonNull IAppSearchResultCallback callback) { 1019 Objects.requireNonNull(request); 1020 Objects.requireNonNull(callback); 1021 1022 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 1023 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 1024 request.getCallerAttributionSource(), request.getUserHandle(), callback); 1025 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 1026 if (targetUser == null) { 1027 return; // Verification failed; verifyIncomingCall triggered callback. 1028 } 1029 if (checkCallDenied(callingPackageName, request.getDatabaseName(), 1030 CallStats.CALL_TYPE_SEARCH, callback, targetUser, 1031 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1032 /* numOperations= */ 1)) { 1033 return; 1034 } 1035 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 1036 callback, callingPackageName, CallStats.CALL_TYPE_SEARCH, () -> { 1037 @AppSearchResult.ResultCode int statusCode = RESULT_OK; 1038 AppSearchUserInstance instance = null; 1039 int operationSuccessCount = 0; 1040 int operationFailureCount = 0; 1041 try { 1042 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 1043 SearchResultPage searchResultPage = instance.getAppSearchImpl().query( 1044 callingPackageName, 1045 request.getDatabaseName(), 1046 request.getSearchExpression(), 1047 request.getSearchSpec(), 1048 instance.getLogger()); 1049 ++operationSuccessCount; 1050 invokeCallbackOnResult( 1051 callback, 1052 AppSearchResultParcel.fromSearchResultPage(searchResultPage)); 1053 } catch (AppSearchException | RuntimeException e) { 1054 ++operationFailureCount; 1055 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 1056 statusCode = failedResult.getResultCode(); 1057 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 1058 failedResult)); 1059 } finally { 1060 if (instance != null) { 1061 int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis 1062 - request.getBinderCallStartTimeMillis()); 1063 int totalLatencyMillis = 1064 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 1065 instance.getLogger().logStats(new CallStats.Builder() 1066 .setPackageName(callingPackageName) 1067 .setDatabase(request.getDatabaseName()) 1068 .setStatusCode(statusCode) 1069 .setTotalLatencyMillis(totalLatencyMillis) 1070 .setCallType(CallStats.CALL_TYPE_SEARCH) 1071 // TODO(b/173532925) check the existing binder call latency chart 1072 // is good enough for us: 1073 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 1074 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 1075 .setNumOperationsSucceeded(operationSuccessCount) 1076 .setNumOperationsFailed(operationFailureCount) 1077 .build()); 1078 } 1079 } 1080 }); 1081 if (!callAccepted) { 1082 logRateLimitedOrCallDeniedCallStats(callingPackageName, request.getDatabaseName(), 1083 CallStats.CALL_TYPE_SEARCH, targetUser, 1084 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1085 /* numOperations= */ 1, RESULT_RATE_LIMITED); 1086 } 1087 } 1088 1089 @Override globalSearch( @onNull GlobalSearchAidlRequest request, @NonNull IAppSearchResultCallback callback)1090 public void globalSearch( 1091 @NonNull GlobalSearchAidlRequest request, 1092 @NonNull IAppSearchResultCallback callback) { 1093 Objects.requireNonNull(request); 1094 Objects.requireNonNull(callback); 1095 1096 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 1097 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 1098 request.getCallerAttributionSource(), request.getUserHandle(), callback); 1099 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 1100 if (targetUser == null) { 1101 return; // Verification failed; verifyIncomingCall triggered callback. 1102 } 1103 // Get the enterprise user for enterprise calls 1104 UserHandle userToQuery = mServiceImplHelper.getUserToQuery(request.isForEnterprise(), 1105 targetUser); 1106 if (userToQuery == null) { 1107 // Return an empty result if we tried to and couldn't get the enterprise user 1108 invokeCallbackOnResult(callback, 1109 AppSearchResultParcel.fromSearchResultPage(new SearchResultPage())); 1110 return; 1111 } 1112 if (checkCallDenied(callingPackageName, /* callingDatabaseName= */ null, 1113 CallStats.CALL_TYPE_GLOBAL_SEARCH, callback, targetUser, 1114 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1115 /* numOperations= */ 1)) { 1116 return; 1117 } 1118 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 1119 callback, callingPackageName, CallStats.CALL_TYPE_GLOBAL_SEARCH, () -> { 1120 @AppSearchResult.ResultCode int statusCode = RESULT_OK; 1121 AppSearchUserInstance instance = null; 1122 int operationSuccessCount = 0; 1123 int operationFailureCount = 0; 1124 try { 1125 instance = mAppSearchUserInstanceManager.getUserInstance(userToQuery); 1126 boolean callerHasSystemAccess = instance.getVisibilityChecker() 1127 .doesCallerHaveSystemAccess(callingPackageName); 1128 SearchSpec querySearchSpec = request.isForEnterprise() 1129 ? EnterpriseSearchSpecTransformer.transformSearchSpec( 1130 request.getSearchSpec()) : request.getSearchSpec(); 1131 SearchResultPage searchResultPage = instance.getAppSearchImpl().globalQuery( 1132 request.getSearchExpression(), 1133 querySearchSpec, 1134 new FrameworkCallerAccess(request.getCallerAttributionSource(), 1135 callerHasSystemAccess, request.isForEnterprise()), 1136 instance.getLogger()); 1137 if (request.isForEnterprise()) { 1138 searchResultPage = 1139 EnterpriseSearchResultPageTransformer.transformSearchResultPage( 1140 searchResultPage); 1141 } 1142 ++operationSuccessCount; 1143 invokeCallbackOnResult( 1144 callback, 1145 AppSearchResultParcel.fromSearchResultPage(searchResultPage)); 1146 } catch (AppSearchException | RuntimeException e) { 1147 ++operationFailureCount; 1148 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 1149 statusCode = failedResult.getResultCode(); 1150 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 1151 failedResult)); 1152 } finally { 1153 if (instance != null) { 1154 int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis 1155 - request.getBinderCallStartTimeMillis()); 1156 int totalLatencyMillis = 1157 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 1158 instance.getLogger().logStats(new CallStats.Builder() 1159 .setPackageName(callingPackageName) 1160 .setStatusCode(statusCode) 1161 .setTotalLatencyMillis(totalLatencyMillis) 1162 .setCallType(CallStats.CALL_TYPE_GLOBAL_SEARCH) 1163 // TODO(b/173532925) check the existing binder call latency chart 1164 // is good enough for us: 1165 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 1166 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 1167 .setNumOperationsSucceeded(operationSuccessCount) 1168 .setNumOperationsFailed(operationFailureCount) 1169 .build()); 1170 } 1171 } 1172 }); 1173 if (!callAccepted) { 1174 logRateLimitedOrCallDeniedCallStats(callingPackageName, 1175 /* callingDatabaseName= */ null, CallStats.CALL_TYPE_GLOBAL_SEARCH, 1176 targetUser, request.getBinderCallStartTimeMillis(), 1177 totalLatencyStartTimeMillis, /* numOperations= */ 1, RESULT_RATE_LIMITED); 1178 } 1179 } 1180 1181 @Override getNextPage( @onNull GetNextPageAidlRequest request, @NonNull IAppSearchResultCallback callback)1182 public void getNextPage( 1183 @NonNull GetNextPageAidlRequest request, 1184 @NonNull IAppSearchResultCallback callback) { 1185 Objects.requireNonNull(request); 1186 Objects.requireNonNull(callback); 1187 1188 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 1189 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 1190 request.getCallerAttributionSource(), request.getUserHandle(), callback); 1191 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 1192 if (targetUser == null) { 1193 return; // Verification failed; verifyIncomingCall triggered callback. 1194 } 1195 // Get the enterprise user for enterprise calls 1196 UserHandle userToQuery = mServiceImplHelper.getUserToQuery(request.isForEnterprise(), 1197 targetUser); 1198 if (userToQuery == null) { 1199 // Return an empty result if we tried to and couldn't get the enterprise user 1200 invokeCallbackOnResult(callback, 1201 AppSearchResultParcel.fromSearchResultPage(new SearchResultPage())); 1202 return; 1203 } 1204 // Enterprise session calls are considered global for CallStats logging 1205 boolean global = request.getDatabaseName() == null || request.isForEnterprise(); 1206 int callType = global ? CallStats.CALL_TYPE_GLOBAL_GET_NEXT_PAGE 1207 : CallStats.CALL_TYPE_GET_NEXT_PAGE; 1208 if (checkCallDenied(callingPackageName, request.getDatabaseName(), callType, callback, 1209 targetUser, request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1210 /* numOperations= */ 1)) { 1211 return; 1212 } 1213 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 1214 callback, callingPackageName, callType, () -> { 1215 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 1216 AppSearchUserInstance instance = null; 1217 int operationSuccessCount = 0; 1218 int operationFailureCount = 0; 1219 SearchStats.Builder statsBuilder; 1220 if (global) { 1221 statsBuilder = new SearchStats.Builder(VISIBILITY_SCOPE_GLOBAL, 1222 callingPackageName) 1223 .setJoinType(request.getJoinType()); 1224 } else { 1225 statsBuilder = new SearchStats.Builder(VISIBILITY_SCOPE_LOCAL, 1226 callingPackageName) 1227 .setDatabase(request.getDatabaseName()) 1228 .setJoinType(request.getJoinType()); 1229 } 1230 try { 1231 instance = mAppSearchUserInstanceManager.getUserInstance(userToQuery); 1232 SearchResultPage searchResultPage = 1233 instance.getAppSearchImpl().getNextPage(callingPackageName, 1234 request.getNextPageToken(), statsBuilder); 1235 if (request.isForEnterprise()) { 1236 searchResultPage = 1237 EnterpriseSearchResultPageTransformer.transformSearchResultPage( 1238 searchResultPage); 1239 } 1240 ++operationSuccessCount; 1241 invokeCallbackOnResult( 1242 callback, 1243 AppSearchResultParcel.fromSearchResultPage(searchResultPage)); 1244 } catch (AppSearchException | RuntimeException e) { 1245 ++operationFailureCount; 1246 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 1247 statusCode = failedResult.getResultCode(); 1248 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 1249 failedResult)); 1250 } finally { 1251 if (instance != null) { 1252 int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis 1253 - request.getBinderCallStartTimeMillis()); 1254 int totalLatencyMillis = 1255 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 1256 CallStats.Builder builder = new CallStats.Builder() 1257 .setPackageName(callingPackageName) 1258 .setDatabase(request.getDatabaseName()) 1259 .setStatusCode(statusCode) 1260 .setTotalLatencyMillis(totalLatencyMillis) 1261 .setCallType(callType) 1262 // TODO(b/173532925) check the existing binder call latency chart 1263 // is good enough for us: 1264 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 1265 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 1266 .setNumOperationsSucceeded(operationSuccessCount) 1267 .setNumOperationsFailed(operationFailureCount); 1268 instance.getLogger().logStats(builder.build()); 1269 instance.getLogger().logStats(statsBuilder.build()); 1270 } 1271 } 1272 }); 1273 if (!callAccepted) { 1274 logRateLimitedOrCallDeniedCallStats(callingPackageName, request.getDatabaseName(), 1275 callType, targetUser, request.getBinderCallStartTimeMillis(), 1276 totalLatencyStartTimeMillis, /* numOperations= */ 1, RESULT_RATE_LIMITED); 1277 } 1278 } 1279 1280 @Override invalidateNextPageToken(@onNull InvalidateNextPageTokenAidlRequest request)1281 public void invalidateNextPageToken(@NonNull InvalidateNextPageTokenAidlRequest request) { 1282 Objects.requireNonNull(request); 1283 1284 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 1285 try { 1286 UserHandle targetUser = mServiceImplHelper.verifyIncomingCall( 1287 request.getCallerAttributionSource(), request.getUserHandle()); 1288 // Get the enterprise user for enterprise calls 1289 UserHandle userToQuery = mServiceImplHelper.getUserToQuery( 1290 request.isForEnterprise(), targetUser); 1291 if (userToQuery == null) { 1292 // Return if we tried to and couldn't get the enterprise user 1293 return; 1294 } 1295 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 1296 if (checkCallDenied(callingPackageName, /* callingDatabaseName= */ null, 1297 CallStats.CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN, targetUser, 1298 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1299 /* numOperations= */ 1)) { 1300 return; 1301 } 1302 boolean callAccepted = mServiceImplHelper.executeLambdaForUserNoCallbackAsync( 1303 targetUser, callingPackageName, 1304 CallStats.CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN, () -> { 1305 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 1306 AppSearchUserInstance instance = null; 1307 int operationSuccessCount = 0; 1308 int operationFailureCount = 0; 1309 try { 1310 instance = mAppSearchUserInstanceManager.getUserInstance(userToQuery); 1311 instance.getAppSearchImpl().invalidateNextPageToken( 1312 callingPackageName, request.getNextPageToken()); 1313 operationSuccessCount++; 1314 } catch (AppSearchException | RuntimeException e) { 1315 ++operationFailureCount; 1316 statusCode = throwableToFailedResult(e).getResultCode(); 1317 Log.e(TAG, "Unable to invalidate the query page token", e); 1318 ExceptionUtil.handleException(e); 1319 } finally { 1320 if (instance != null) { 1321 int estimatedBinderLatencyMillis = 1322 2 * (int) (totalLatencyStartTimeMillis 1323 - request.getBinderCallStartTimeMillis()); 1324 int totalLatencyMillis = 1325 (int) (SystemClock.elapsedRealtime() 1326 - totalLatencyStartTimeMillis); 1327 instance.getLogger().logStats(new CallStats.Builder() 1328 .setPackageName(callingPackageName) 1329 .setStatusCode(statusCode) 1330 .setTotalLatencyMillis(totalLatencyMillis) 1331 .setCallType(CallStats.CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN) 1332 // TODO(b/173532925) check the existing binder call latency 1333 // chart 1334 // is good enough for us: 1335 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 1336 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 1337 .setNumOperationsSucceeded(operationSuccessCount) 1338 .setNumOperationsFailed(operationFailureCount) 1339 .build()); 1340 } 1341 } 1342 }); 1343 if (!callAccepted) { 1344 logRateLimitedOrCallDeniedCallStats( 1345 callingPackageName, /* callingDatabaseName= */ null, 1346 CallStats.CALL_TYPE_INVALIDATE_NEXT_PAGE_TOKEN, targetUser, 1347 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1348 /* numOperations= */ 1, RESULT_RATE_LIMITED); 1349 } 1350 } catch (RuntimeException e) { 1351 Log.e(TAG, "Unable to invalidate the query page token", e); 1352 ExceptionUtil.handleException(e); 1353 } 1354 } 1355 1356 @Override writeSearchResultsToFile( @onNull WriteSearchResultsToFileAidlRequest request, @NonNull IAppSearchResultCallback callback)1357 public void writeSearchResultsToFile( 1358 @NonNull WriteSearchResultsToFileAidlRequest request, 1359 @NonNull IAppSearchResultCallback callback) { 1360 Objects.requireNonNull(request); 1361 Objects.requireNonNull(callback); 1362 1363 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 1364 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 1365 request.getCallerAttributionSource(), request.getUserHandle(), callback); 1366 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 1367 if (targetUser == null) { 1368 return; // Verification failed; verifyIncomingCall triggered callback. 1369 } 1370 if (checkCallDenied(callingPackageName, request.getDatabaseName(), 1371 CallStats.CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE, callback, targetUser, 1372 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1373 /* numOperations= */ 1)) { 1374 return; 1375 } 1376 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 1377 callback, callingPackageName, CallStats.CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE, 1378 () -> { 1379 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 1380 AppSearchUserInstance instance = null; 1381 int operationSuccessCount = 0; 1382 int operationFailureCount = 0; 1383 try { 1384 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 1385 // we don't need to append the file. The file is always brand new. 1386 try (DataOutputStream outputStream = new DataOutputStream(new FileOutputStream( 1387 request.getParcelFileDescriptor().getFileDescriptor()))) { 1388 SearchResultPage searchResultPage = instance.getAppSearchImpl().query( 1389 callingPackageName, 1390 request.getDatabaseName(), 1391 request.getSearchExpression(), 1392 request.getSearchSpec(), 1393 /* logger= */ null); 1394 while (!searchResultPage.getResults().isEmpty()) { 1395 for (int i = 0; i < searchResultPage.getResults().size(); i++) { 1396 AppSearchMigrationHelper.writeDocumentToOutputStream( 1397 outputStream, 1398 searchResultPage.getResults().get(i).getGenericDocument()); 1399 } 1400 operationSuccessCount += searchResultPage.getResults().size(); 1401 // TODO(b/173532925): Implement logging for statsBuilder 1402 searchResultPage = instance.getAppSearchImpl().getNextPage( 1403 callingPackageName, 1404 searchResultPage.getNextPageToken(), 1405 /* sStatsBuilder= */ null); 1406 } 1407 } 1408 invokeCallbackOnResult(callback, AppSearchResultParcel.fromVoid()); 1409 } catch (AppSearchException | IOException | RuntimeException e) { 1410 ++operationFailureCount; 1411 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 1412 statusCode = failedResult.getResultCode(); 1413 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 1414 failedResult)); 1415 } finally { 1416 if (instance != null) { 1417 int estimatedBinderLatencyMillis = 2 * (int) (totalLatencyStartTimeMillis 1418 - request.getBinderCallStartTimeMillis()); 1419 int totalLatencyMillis = 1420 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 1421 instance.getLogger().logStats(new CallStats.Builder() 1422 .setPackageName(callingPackageName) 1423 .setDatabase(request.getDatabaseName()) 1424 .setStatusCode(statusCode) 1425 .setTotalLatencyMillis(totalLatencyMillis) 1426 .setCallType(CallStats.CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE) 1427 // TODO(b/173532925) check the existing binder call latency chart 1428 // is good enough for us: 1429 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 1430 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 1431 .setNumOperationsSucceeded(operationSuccessCount) 1432 .setNumOperationsFailed(operationFailureCount) 1433 .build()); 1434 } 1435 } 1436 }); 1437 if (!callAccepted) { 1438 logRateLimitedOrCallDeniedCallStats(callingPackageName, request.getDatabaseName(), 1439 CallStats.CALL_TYPE_WRITE_SEARCH_RESULTS_TO_FILE, targetUser, 1440 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1441 /* numOperations= */ 1, RESULT_RATE_LIMITED); 1442 } 1443 } 1444 1445 @Override putDocumentsFromFile( @onNull PutDocumentsFromFileAidlRequest request, @NonNull IAppSearchResultCallback callback)1446 public void putDocumentsFromFile( 1447 @NonNull PutDocumentsFromFileAidlRequest request, 1448 @NonNull IAppSearchResultCallback callback) { 1449 Objects.requireNonNull(request); 1450 Objects.requireNonNull(callback); 1451 1452 long callStatsTotalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 1453 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 1454 request.getCallerAttributionSource(), request.getUserHandle(), callback); 1455 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 1456 if (targetUser == null) { 1457 return; // Verification failed; verifyIncomingCall triggered callback. 1458 } 1459 // Since we don't read from the given file, we don't know the number of documents so we 1460 // just set numOperations to 1 instead 1461 if (checkCallDenied(callingPackageName, request.getDatabaseName(), 1462 CallStats.CALL_TYPE_PUT_DOCUMENTS_FROM_FILE, callback, targetUser, 1463 request.getBinderCallStartTimeMillis(), callStatsTotalLatencyStartTimeMillis, 1464 /* numOperations= */ 1)) { 1465 return; 1466 } 1467 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 1468 callback, callingPackageName, CallStats.CALL_TYPE_PUT_DOCUMENTS_FROM_FILE, 1469 () -> { 1470 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 1471 AppSearchUserInstance instance = null; 1472 int operationSuccessCount = 0; 1473 int operationFailureCount = 0; 1474 SchemaMigrationStats.Builder schemaMigrationStatsBuilder = new SchemaMigrationStats 1475 .Builder(request.getSchemaMigrationStats()); 1476 try { 1477 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 1478 1479 GenericDocument document; 1480 ArrayList<MigrationFailure> migrationFailures = new ArrayList<>(); 1481 try (DataInputStream inputStream = new DataInputStream(new FileInputStream( 1482 request.getParcelFileDescriptor().getFileDescriptor()))) { 1483 while (true) { 1484 try { 1485 document = AppSearchMigrationHelper 1486 .readDocumentFromInputStream(inputStream); 1487 } catch (EOFException e) { 1488 // nothing wrong, we just finish the reading. 1489 break; 1490 } 1491 try { 1492 // Per this method's documentation, individual document change 1493 // notifications are not dispatched. 1494 instance.getAppSearchImpl().putDocument( 1495 callingPackageName, 1496 request.getDatabaseName(), 1497 document, 1498 /* sendChangeNotifications= */ false, 1499 /* logger= */ null); 1500 ++operationSuccessCount; 1501 } catch (AppSearchException | RuntimeException e) { 1502 // We don't rethrow here, so we can still keep going with the 1503 // following documents. 1504 ++operationFailureCount; 1505 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 1506 statusCode = failedResult.getResultCode(); 1507 migrationFailures.add(new SetSchemaResponse.MigrationFailure( 1508 document.getNamespace(), 1509 document.getId(), 1510 document.getSchemaType(), 1511 failedResult)); 1512 } 1513 } 1514 } 1515 instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL); 1516 1517 schemaMigrationStatsBuilder 1518 .setTotalSuccessMigratedDocumentCount(operationSuccessCount) 1519 .setMigrationFailureCount(migrationFailures.size()); 1520 invokeCallbackOnResult(callback, AppSearchResultParcel 1521 .fromMigrationFailuresList(migrationFailures)); 1522 } catch (AppSearchException | IOException | RuntimeException e) { 1523 ++operationFailureCount; 1524 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 1525 statusCode = failedResult.getResultCode(); 1526 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 1527 failedResult)); 1528 } finally { 1529 if (instance != null) { 1530 long latencyEndTimeMillis = 1531 SystemClock.elapsedRealtime(); 1532 int estimatedBinderLatencyMillis = 1533 2 * (int) (callStatsTotalLatencyStartTimeMillis 1534 - request.getBinderCallStartTimeMillis()); 1535 int callStatsTotalLatencyMillis = 1536 (int) (latencyEndTimeMillis - callStatsTotalLatencyStartTimeMillis); 1537 // totalLatencyStartTimeMillis is captured in the SDK side, and 1538 // put migrate documents is the last step of migration process. 1539 // This should includes whole schema migration process. 1540 // Like get old schema, first and second set schema, query old 1541 // documents, transform documents and save migrated documents. 1542 int totalLatencyMillis = (int) (latencyEndTimeMillis 1543 - request.getTotalLatencyStartTimeMillis()); 1544 int saveDocumentLatencyMillis = (int) (latencyEndTimeMillis 1545 - request.getBinderCallStartTimeMillis()); 1546 instance.getLogger().logStats(new CallStats.Builder() 1547 .setPackageName(callingPackageName) 1548 .setDatabase(request.getDatabaseName()) 1549 .setStatusCode(statusCode) 1550 .setTotalLatencyMillis(callStatsTotalLatencyMillis) 1551 .setCallType(CallStats.CALL_TYPE_PUT_DOCUMENTS_FROM_FILE) 1552 // TODO(b/173532925) check the existing binder call latency chart 1553 // is good enough for us: 1554 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 1555 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 1556 .setNumOperationsSucceeded(operationSuccessCount) 1557 .setNumOperationsFailed(operationFailureCount) 1558 .build()); 1559 instance.getLogger().logStats(schemaMigrationStatsBuilder 1560 .setStatusCode(statusCode) 1561 .setTotalLatencyMillis(totalLatencyMillis) 1562 .setSaveDocumentLatencyMillis(saveDocumentLatencyMillis) 1563 .build()); 1564 } 1565 } 1566 }); 1567 if (!callAccepted) { 1568 logRateLimitedOrCallDeniedCallStats(callingPackageName, request.getDatabaseName(), 1569 CallStats.CALL_TYPE_PUT_DOCUMENTS_FROM_FILE, targetUser, 1570 request.getBinderCallStartTimeMillis(), 1571 callStatsTotalLatencyStartTimeMillis, /* numOperations= */ 1, 1572 RESULT_RATE_LIMITED); 1573 } 1574 } 1575 1576 @Override searchSuggestion( @onNull SearchSuggestionAidlRequest request, @NonNull IAppSearchResultCallback callback)1577 public void searchSuggestion( 1578 @NonNull SearchSuggestionAidlRequest request, 1579 @NonNull IAppSearchResultCallback callback) { 1580 Objects.requireNonNull(request); 1581 Objects.requireNonNull(callback); 1582 1583 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 1584 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 1585 request.getCallerAttributionSource(), request.getUserHandle(), callback); 1586 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 1587 if (targetUser == null) { 1588 return; // Verification failed; verifyIncomingCall triggered callback. 1589 } 1590 if (checkCallDenied(callingPackageName, request.getDatabaseName(), 1591 CallStats.CALL_TYPE_SEARCH_SUGGESTION, callback, targetUser, 1592 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1593 /* numOperations= */ 1)) { 1594 return; 1595 } 1596 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 1597 callback, callingPackageName, CallStats.CALL_TYPE_SEARCH_SUGGESTION, 1598 () -> { 1599 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 1600 AppSearchUserInstance instance = null; 1601 int operationSuccessCount = 0; 1602 int operationFailureCount = 0; 1603 try { 1604 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 1605 // TODO(b/173532925): Implement logging for statsBuilder 1606 List<SearchSuggestionResult> searchSuggestionResults = 1607 instance.getAppSearchImpl().searchSuggestion( 1608 callingPackageName, 1609 request.getDatabaseName(), 1610 request.getSuggestionQueryExpression(), 1611 request.getSearchSuggestionSpec()); 1612 ++operationSuccessCount; 1613 invokeCallbackOnResult( 1614 callback, AppSearchResultParcel 1615 .fromSearchSuggestionResultList(searchSuggestionResults)); 1616 } catch (AppSearchException | RuntimeException e) { 1617 ++operationFailureCount; 1618 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 1619 statusCode = failedResult.getResultCode(); 1620 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 1621 failedResult)); 1622 } finally { 1623 if (instance != null) { 1624 int estimatedBinderLatencyMillis = 1625 2 * (int) (totalLatencyStartTimeMillis 1626 - request.getBinderCallStartTimeMillis()); 1627 int totalLatencyMillis = 1628 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 1629 instance.getLogger().logStats(new CallStats.Builder() 1630 .setPackageName(callingPackageName) 1631 .setDatabase(request.getDatabaseName()) 1632 .setStatusCode(statusCode) 1633 .setTotalLatencyMillis(totalLatencyMillis) 1634 .setCallType(CallStats.CALL_TYPE_SEARCH_SUGGESTION) 1635 // TODO(b/173532925) check the existing binder call latency chart 1636 // is good enough for us: 1637 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 1638 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 1639 .setNumOperationsSucceeded(operationSuccessCount) 1640 .setNumOperationsFailed(operationFailureCount) 1641 .build()); 1642 } 1643 } 1644 }); 1645 if (!callAccepted) { 1646 logRateLimitedOrCallDeniedCallStats(callingPackageName, request.getDatabaseName(), 1647 CallStats.CALL_TYPE_SEARCH_SUGGESTION, targetUser, 1648 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1649 /* numOperations= */ 1, RESULT_RATE_LIMITED); 1650 } 1651 } 1652 1653 @Override reportUsage( @onNull ReportUsageAidlRequest request, @NonNull IAppSearchResultCallback callback)1654 public void reportUsage( 1655 @NonNull ReportUsageAidlRequest request, 1656 @NonNull IAppSearchResultCallback callback) { 1657 Objects.requireNonNull(request); 1658 Objects.requireNonNull(callback); 1659 1660 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 1661 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 1662 request.getCallerAttributionSource(), request.getUserHandle(), callback); 1663 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 1664 if (targetUser == null) { 1665 return; // Verification failed; verifyIncomingCall triggered callback. 1666 } 1667 // We deny based on the calling package and calling database names. If the API call is 1668 // intended for system usage, then the call is global, and the target database is not a 1669 // calling database. 1670 String callingDatabaseName = request.isSystemUsage() 1671 ? null : request.getDatabaseName(); 1672 int callType = request.isSystemUsage() ? CallStats.CALL_TYPE_REPORT_SYSTEM_USAGE 1673 : CallStats.CALL_TYPE_REPORT_USAGE; 1674 if (checkCallDenied(callingPackageName, callingDatabaseName, callType, callback, 1675 targetUser, request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1676 /* numOperations= */ 1)) { 1677 return; 1678 } 1679 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 1680 callback, callingPackageName, CallStats.CALL_TYPE_REPORT_USAGE, 1681 () -> { 1682 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 1683 AppSearchUserInstance instance = null; 1684 int operationSuccessCount = 0; 1685 int operationFailureCount = 0; 1686 try { 1687 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 1688 if (request.isSystemUsage()) { 1689 if (!instance.getVisibilityChecker().doesCallerHaveSystemAccess( 1690 callingPackageName)) { 1691 throw new AppSearchException(RESULT_SECURITY_ERROR, 1692 callingPackageName 1693 + " does not have access to report system usage"); 1694 } 1695 } else { 1696 if (!callingPackageName.equals(request.getTargetPackageName())) { 1697 throw new AppSearchException(RESULT_SECURITY_ERROR, 1698 "Cannot report usage to different package: " 1699 + request.getTargetPackageName() + " from package: " 1700 + callingPackageName); 1701 } 1702 } 1703 1704 instance.getAppSearchImpl().reportUsage(request.getTargetPackageName(), 1705 request.getDatabaseName(), 1706 request.getReportUsageRequest().getNamespace(), 1707 request.getReportUsageRequest().getDocumentId(), 1708 request.getReportUsageRequest().getUsageTimestampMillis(), 1709 request.isSystemUsage()); 1710 ++operationSuccessCount; 1711 invokeCallbackOnResult(callback, AppSearchResultParcel.fromVoid()); 1712 } catch (AppSearchException | RuntimeException e) { 1713 ++operationFailureCount; 1714 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 1715 statusCode = failedResult.getResultCode(); 1716 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 1717 failedResult)); 1718 } finally { 1719 if (instance != null) { 1720 int estimatedBinderLatencyMillis = 1721 2 * (int) (totalLatencyStartTimeMillis - 1722 request.getBinderCallStartTimeMillis()); 1723 int totalLatencyMillis = 1724 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 1725 instance.getLogger().logStats(new CallStats.Builder() 1726 .setPackageName(callingPackageName) 1727 .setDatabase(request.getDatabaseName()) 1728 .setStatusCode(statusCode) 1729 .setTotalLatencyMillis(totalLatencyMillis) 1730 .setCallType(callType) 1731 // TODO(b/173532925) check the existing binder call latency chart 1732 // is good enough for us: 1733 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 1734 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 1735 .setNumOperationsSucceeded(operationSuccessCount) 1736 .setNumOperationsFailed(operationFailureCount) 1737 .build()); 1738 } 1739 } 1740 }); 1741 if (!callAccepted) { 1742 logRateLimitedOrCallDeniedCallStats(callingPackageName, callingDatabaseName, 1743 callType, targetUser, request.getBinderCallStartTimeMillis(), 1744 totalLatencyStartTimeMillis, 1745 /* numOperations= */ 1, RESULT_RATE_LIMITED); 1746 } 1747 } 1748 1749 @Override removeByDocumentId( @onNull RemoveByDocumentIdAidlRequest request, @NonNull IAppSearchBatchResultCallback callback)1750 public void removeByDocumentId( 1751 @NonNull RemoveByDocumentIdAidlRequest request, 1752 @NonNull IAppSearchBatchResultCallback callback) { 1753 Objects.requireNonNull(request); 1754 Objects.requireNonNull(callback); 1755 1756 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 1757 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 1758 request.getCallerAttributionSource(), request.getUserHandle(), callback); 1759 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 1760 if (targetUser == null) { 1761 return; // Verification failed; verifyIncomingCall triggered callback. 1762 } 1763 if (checkCallDenied(callingPackageName, request.getDatabaseName(), 1764 CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_ID, callback, targetUser, 1765 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1766 /* numOperations= */ request.getRemoveByDocumentIdRequest().getIds().size())) { 1767 return; 1768 } 1769 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 1770 callback, callingPackageName, CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_ID, 1771 () -> { 1772 @AppSearchResult.ResultCode int statusCode = RESULT_OK; 1773 AppSearchUserInstance instance = null; 1774 int operationSuccessCount = 0; 1775 int operationFailureCount = 0; 1776 try { 1777 AppSearchBatchResult.Builder<String, Void> resultBuilder = 1778 new AppSearchBatchResult.Builder<>(); 1779 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 1780 for (String id : request.getRemoveByDocumentIdRequest().getIds()) { 1781 try { 1782 instance.getAppSearchImpl().remove( 1783 callingPackageName, 1784 request.getDatabaseName(), 1785 request.getRemoveByDocumentIdRequest().getNamespace(), 1786 id, 1787 /* removeStatsBuilder= */ null); 1788 ++operationSuccessCount; 1789 resultBuilder.setSuccess(id, /*result= */ null); 1790 } catch (AppSearchException | RuntimeException e) { 1791 // We don't rethrow here, so we can still keep trying for the following 1792 // ones. 1793 AppSearchResult<Void> result = throwableToFailedResult(e); 1794 resultBuilder.setResult(id, result); 1795 // Since we can only include one status code in the atom, 1796 // for failures, we would just save the one for the last failure 1797 statusCode = result.getResultCode(); 1798 ++operationFailureCount; 1799 } 1800 } 1801 // Now that the batch has been written. Persist the newly written data. 1802 instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); 1803 invokeCallbackOnResult(callback, AppSearchBatchResultParcel.fromStringToVoid( 1804 resultBuilder.build())); 1805 1806 // Schedule a task to dispatch change notifications. See requirements for where 1807 // the method is called documented in the method description. 1808 dispatchChangeNotifications(instance); 1809 1810 checkForOptimize(targetUser, instance, 1811 request.getRemoveByDocumentIdRequest().getIds().size()); 1812 } catch (AppSearchException | RuntimeException e) { 1813 ++operationFailureCount; 1814 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 1815 statusCode = failedResult.getResultCode(); 1816 invokeCallbackOnError(callback, failedResult); 1817 } finally { 1818 // TODO(b/261959320) add outstanding latency fields in AppSearch stats 1819 if (instance != null) { 1820 int estimatedBinderLatencyMillis = 1821 2 * (int) (totalLatencyStartTimeMillis 1822 - request.getBinderCallStartTimeMillis()); 1823 int totalLatencyMillis = 1824 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 1825 instance.getLogger().logStats(new CallStats.Builder() 1826 .setPackageName(callingPackageName) 1827 .setDatabase(request.getDatabaseName()) 1828 .setStatusCode(statusCode) 1829 .setTotalLatencyMillis(totalLatencyMillis) 1830 .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_ID) 1831 // TODO(b/173532925) check the existing binder call latency chart 1832 // is good enough for us: 1833 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 1834 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 1835 .setNumOperationsSucceeded(operationSuccessCount) 1836 .setNumOperationsFailed(operationFailureCount) 1837 .build()); 1838 } 1839 } 1840 }); 1841 if (!callAccepted) { 1842 logRateLimitedOrCallDeniedCallStats(callingPackageName, request.getDatabaseName(), 1843 CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_ID, targetUser, 1844 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1845 /* numOperations= */ request.getRemoveByDocumentIdRequest().getIds().size(), 1846 RESULT_RATE_LIMITED); 1847 } 1848 } 1849 1850 @Override removeByQuery( @onNull RemoveByQueryAidlRequest request, @NonNull IAppSearchResultCallback callback)1851 public void removeByQuery( 1852 @NonNull RemoveByQueryAidlRequest request, 1853 @NonNull IAppSearchResultCallback callback) { 1854 Objects.requireNonNull(request); 1855 Objects.requireNonNull(callback); 1856 1857 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 1858 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 1859 request.getCallerAttributionSource(), request.getUserHandle(), callback); 1860 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 1861 if (targetUser == null) { 1862 return; // Verification failed; verifyIncomingCall triggered callback. 1863 } 1864 if (checkCallDenied(callingPackageName, request.getDatabaseName(), 1865 CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH, callback, targetUser, 1866 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1867 /* numOperations= */ 1)) { 1868 return; 1869 } 1870 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 1871 callback, callingPackageName, CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH, 1872 () -> { 1873 @AppSearchResult.ResultCode int statusCode = RESULT_OK; 1874 AppSearchUserInstance instance = null; 1875 int operationSuccessCount = 0; 1876 int operationFailureCount = 0; 1877 try { 1878 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 1879 instance.getAppSearchImpl().removeByQuery( 1880 callingPackageName, 1881 request.getDatabaseName(), 1882 request.getQueryExpression(), 1883 request.getSearchSpec(), 1884 /* removeStatsBuilder= */ null); 1885 // Now that the batch has been written. Persist the newly written data. 1886 instance.getAppSearchImpl().persistToDisk(PersistType.Code.LITE); 1887 ++operationSuccessCount; 1888 invokeCallbackOnResult(callback, AppSearchResultParcel.fromVoid()); 1889 1890 // Schedule a task to dispatch change notifications. See requirements for where 1891 // the method is called documented in the method description. 1892 dispatchChangeNotifications(instance); 1893 1894 checkForOptimize(targetUser, instance); 1895 } catch (AppSearchException | RuntimeException e) { 1896 ++operationFailureCount; 1897 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 1898 statusCode = failedResult.getResultCode(); 1899 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 1900 failedResult)); 1901 } finally { 1902 // TODO(b/261959320) add outstanding latency fields in AppSearch stats 1903 if (instance != null) { 1904 int estimatedBinderLatencyMillis = 1905 2 * (int) (totalLatencyStartTimeMillis 1906 - request.getBinderCallStartTimeMillis()); 1907 int totalLatencyMillis = 1908 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 1909 instance.getLogger().logStats(new CallStats.Builder() 1910 .setPackageName(callingPackageName) 1911 .setDatabase(request.getDatabaseName()) 1912 .setStatusCode(statusCode) 1913 .setTotalLatencyMillis(totalLatencyMillis) 1914 .setCallType(CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH) 1915 // TODO(b/173532925) check the existing binder call latency chart 1916 // is good enough for us: 1917 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 1918 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 1919 .setNumOperationsSucceeded(operationSuccessCount) 1920 .setNumOperationsFailed(operationFailureCount) 1921 .build()); 1922 } 1923 } 1924 }); 1925 if (!callAccepted) { 1926 logRateLimitedOrCallDeniedCallStats(callingPackageName, request.getDatabaseName(), 1927 CallStats.CALL_TYPE_REMOVE_DOCUMENTS_BY_SEARCH, targetUser, 1928 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1929 /* numOperations= */ 1, RESULT_RATE_LIMITED); 1930 } 1931 } 1932 1933 @Override getStorageInfo( @onNull GetStorageInfoAidlRequest request, @NonNull IAppSearchResultCallback callback)1934 public void getStorageInfo( 1935 @NonNull GetStorageInfoAidlRequest request, 1936 @NonNull IAppSearchResultCallback callback) { 1937 Objects.requireNonNull(request); 1938 Objects.requireNonNull(callback); 1939 1940 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 1941 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 1942 request.getCallerAttributionSource(), request.getUserHandle(), callback); 1943 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 1944 if (targetUser == null) { 1945 return; // Verification failed; verifyIncomingCall triggered callback. 1946 } 1947 if (checkCallDenied(callingPackageName, request.getDatabaseName(), 1948 CallStats.CALL_TYPE_GET_STORAGE_INFO, callback, targetUser, 1949 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1950 /* numOperations= */ 1)) { 1951 return; 1952 } 1953 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync(targetUser, 1954 callback, callingPackageName, CallStats.CALL_TYPE_GET_STORAGE_INFO, () -> { 1955 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 1956 AppSearchUserInstance instance = null; 1957 int operationSuccessCount = 0; 1958 int operationFailureCount = 0; 1959 try { 1960 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 1961 StorageInfo storageInfo = instance.getAppSearchImpl().getStorageInfoForDatabase( 1962 callingPackageName, request.getDatabaseName()); 1963 ++operationSuccessCount; 1964 invokeCallbackOnResult( 1965 callback, AppSearchResultParcel.fromStorageInfo(storageInfo)); 1966 } catch (AppSearchException | RuntimeException e) { 1967 ++operationFailureCount; 1968 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 1969 statusCode = failedResult.getResultCode(); 1970 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 1971 failedResult)); 1972 } finally { 1973 if (instance != null) { 1974 int estimatedBinderLatencyMillis = 1975 2 * (int) (totalLatencyStartTimeMillis 1976 - request.getBinderCallStartTimeMillis()); 1977 int totalLatencyMillis = 1978 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 1979 instance.getLogger().logStats(new CallStats.Builder() 1980 .setPackageName(callingPackageName) 1981 .setDatabase(request.getDatabaseName()) 1982 .setStatusCode(statusCode) 1983 .setTotalLatencyMillis(totalLatencyMillis) 1984 .setCallType(CallStats.CALL_TYPE_GET_STORAGE_INFO) 1985 // TODO(b/173532925) check the existing binder call latency chart 1986 // is good enough for us: 1987 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 1988 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 1989 .setNumOperationsSucceeded(operationSuccessCount) 1990 .setNumOperationsFailed(operationFailureCount) 1991 .build()); 1992 } 1993 } 1994 }); 1995 if (!callAccepted) { 1996 logRateLimitedOrCallDeniedCallStats(callingPackageName, request.getDatabaseName(), 1997 CallStats.CALL_TYPE_GET_STORAGE_INFO, targetUser, 1998 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 1999 /* numOperations= */ 1, RESULT_RATE_LIMITED); 2000 } 2001 } 2002 2003 @Override persistToDisk(@onNull PersistToDiskAidlRequest request)2004 public void persistToDisk(@NonNull PersistToDiskAidlRequest request) { 2005 Objects.requireNonNull(request); 2006 2007 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 2008 try { 2009 UserHandle targetUser = mServiceImplHelper.verifyIncomingCall( 2010 request.getCallerAttributionSource(), request.getUserHandle()); 2011 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 2012 if (checkCallDenied(callingPackageName, /* callingDatabaseName= */ null, 2013 CallStats.CALL_TYPE_FLUSH, targetUser, 2014 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 2015 /* numOperations= */ 1)) { 2016 return; 2017 } 2018 boolean callAccepted = mServiceImplHelper.executeLambdaForUserNoCallbackAsync( 2019 targetUser, callingPackageName, CallStats.CALL_TYPE_FLUSH, () -> { 2020 @AppSearchResult.ResultCode int statusCode = RESULT_OK; 2021 AppSearchUserInstance instance = null; 2022 int operationSuccessCount = 0; 2023 int operationFailureCount = 0; 2024 try { 2025 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 2026 instance.getAppSearchImpl().persistToDisk(PersistType.Code.FULL); 2027 ++operationSuccessCount; 2028 } catch (AppSearchException | RuntimeException e) { 2029 ++operationFailureCount; 2030 statusCode = throwableToFailedResult(e).getResultCode(); 2031 // We will print two error messages if we rethrow, but I would rather keep 2032 // this print statement here, so we know where the actual exception 2033 // comes from. 2034 Log.e(TAG, "Unable to persist the data to disk", e); 2035 ExceptionUtil.handleException(e); 2036 } finally { 2037 if (instance != null) { 2038 int estimatedBinderLatencyMillis = 2039 2 * (int) (totalLatencyStartTimeMillis 2040 - request.getBinderCallStartTimeMillis()); 2041 int totalLatencyMillis = 2042 (int) (SystemClock.elapsedRealtime() 2043 - totalLatencyStartTimeMillis); 2044 instance.getLogger().logStats(new CallStats.Builder() 2045 .setPackageName(callingPackageName) 2046 .setStatusCode(statusCode) 2047 .setTotalLatencyMillis(totalLatencyMillis) 2048 .setCallType(CallStats.CALL_TYPE_FLUSH) 2049 // TODO(b/173532925) check the existing binder call latency 2050 // chart is good enough for us: 2051 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 2052 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 2053 .setNumOperationsSucceeded(operationSuccessCount) 2054 .setNumOperationsFailed(operationFailureCount) 2055 .build()); 2056 } 2057 } 2058 }); 2059 if (!callAccepted) { 2060 logRateLimitedOrCallDeniedCallStats( 2061 callingPackageName, /* callingDatabaseName= */ null, 2062 CallStats.CALL_TYPE_FLUSH, targetUser, 2063 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 2064 /* numOperations= */ 1, RESULT_RATE_LIMITED); 2065 } 2066 } catch (RuntimeException e) { 2067 Log.e(TAG, "Unable to persist the data to disk", e); 2068 ExceptionUtil.handleException(e); 2069 } 2070 } 2071 2072 @Override registerObserverCallback( @onNull RegisterObserverCallbackAidlRequest request, @NonNull IAppSearchObserverProxy observerProxyStub)2073 public AppSearchResultParcel<Void> registerObserverCallback( 2074 @NonNull RegisterObserverCallbackAidlRequest request, 2075 @NonNull IAppSearchObserverProxy observerProxyStub) { 2076 Objects.requireNonNull(request); 2077 Objects.requireNonNull(observerProxyStub); 2078 2079 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 2080 AppSearchUserInstance instance = null; 2081 String callingPackageName = null; 2082 int operationSuccessCount = 0; 2083 int operationFailureCount = 0; 2084 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 2085 // Note: registerObserverCallback is performed on the binder thread, unlike most 2086 // AppSearch APIs 2087 try { 2088 UserHandle targetUser = mServiceImplHelper.verifyIncomingCall( 2089 request.getCallerAttributionSource(), request.getUserHandle()); 2090 callingPackageName = request.getCallerAttributionSource().getPackageName(); 2091 if (checkCallDenied(callingPackageName, /* callingDatabaseName= */ null, 2092 CallStats.CALL_TYPE_REGISTER_OBSERVER_CALLBACK, targetUser, 2093 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 2094 /* numOperations= */ 1)) { 2095 return AppSearchResultParcel.fromFailedResult(AppSearchResult.newFailedResult( 2096 RESULT_DENIED, null)); 2097 } 2098 long callingIdentity = Binder.clearCallingIdentity(); 2099 try { 2100 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 2101 2102 // Prepare a new ObserverProxy linked to this binder. 2103 AppSearchObserverProxy observerProxy = 2104 new AppSearchObserverProxy(observerProxyStub); 2105 2106 // Watch for client disconnection, unregistering the observer if it happens. 2107 final AppSearchUserInstance finalInstance = instance; 2108 observerProxyStub.asBinder().linkToDeath( 2109 () -> finalInstance.getAppSearchImpl() 2110 .unregisterObserverCallback( 2111 request.getTargetPackageName(), observerProxy), 2112 /* flags= */ 0); 2113 2114 // Register the observer. 2115 boolean callerHasSystemAccess = instance.getVisibilityChecker() 2116 .doesCallerHaveSystemAccess(callingPackageName); 2117 instance.getAppSearchImpl().registerObserverCallback( 2118 new FrameworkCallerAccess(request.getCallerAttributionSource(), 2119 callerHasSystemAccess, /*isForEnterprise=*/ false), 2120 request.getTargetPackageName(), 2121 request.getObserverSpec(), 2122 mExecutorManager.getOrCreateUserExecutor(targetUser), 2123 new AppSearchObserverProxy(observerProxyStub)); 2124 ++operationSuccessCount; 2125 return AppSearchResultParcel.fromVoid(); 2126 } finally { 2127 Binder.restoreCallingIdentity(callingIdentity); 2128 } 2129 } catch (RemoteException | RuntimeException e) { 2130 ++operationFailureCount; 2131 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 2132 statusCode = failedResult.getResultCode(); 2133 return AppSearchResultParcel.fromFailedResult(failedResult); 2134 } finally { 2135 if (instance != null) { 2136 int estimatedBinderLatencyMillis = 2137 2 * (int) (totalLatencyStartTimeMillis 2138 - request.getBinderCallStartTimeMillis()); 2139 int totalLatencyMillis = 2140 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 2141 instance.getLogger().logStats(new CallStats.Builder() 2142 .setPackageName(callingPackageName) 2143 .setStatusCode(statusCode) 2144 .setTotalLatencyMillis(totalLatencyMillis) 2145 .setCallType(CallStats.CALL_TYPE_REGISTER_OBSERVER_CALLBACK) 2146 // TODO(b/173532925) check the existing binder call latency chart 2147 // is good enough for us: 2148 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 2149 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 2150 .setNumOperationsSucceeded(operationSuccessCount) 2151 .setNumOperationsFailed(operationFailureCount) 2152 .build()); 2153 } 2154 } 2155 } 2156 2157 @Override unregisterObserverCallback( @onNull UnregisterObserverCallbackAidlRequest request, @NonNull IAppSearchObserverProxy observerProxyStub)2158 public AppSearchResultParcel<Void> unregisterObserverCallback( 2159 @NonNull UnregisterObserverCallbackAidlRequest request, 2160 @NonNull IAppSearchObserverProxy observerProxyStub) { 2161 Objects.requireNonNull(request); 2162 Objects.requireNonNull(observerProxyStub); 2163 2164 @AppSearchResult.ResultCode int statusCode = AppSearchResult.RESULT_OK; 2165 AppSearchUserInstance instance = null; 2166 int operationSuccessCount = 0; 2167 int operationFailureCount = 0; 2168 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 2169 // Note: unregisterObserverCallback is performed on the binder thread, unlike most 2170 // AppSearch APIs 2171 try { 2172 UserHandle targetUser = mServiceImplHelper.verifyIncomingCall( 2173 request.getCallerAttributionSource(), request.getUserHandle()); 2174 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 2175 if (checkCallDenied(callingPackageName, /* callingDatabaseName= */ null, 2176 CallStats.CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK, targetUser, 2177 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 2178 /* numOperations= */ 1)) { 2179 return AppSearchResultParcel.fromFailedResult(AppSearchResult.newFailedResult( 2180 RESULT_DENIED, null)); 2181 } 2182 long callingIdentity = Binder.clearCallingIdentity(); 2183 try { 2184 instance = mAppSearchUserInstanceManager.getUserInstance(targetUser); 2185 instance.getAppSearchImpl().unregisterObserverCallback( 2186 request.getObservedPackage(), 2187 new AppSearchObserverProxy(observerProxyStub)); 2188 ++operationSuccessCount; 2189 return AppSearchResultParcel.fromVoid(); 2190 } finally { 2191 Binder.restoreCallingIdentity(callingIdentity); 2192 } 2193 } catch (RuntimeException e) { 2194 ++operationFailureCount; 2195 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 2196 statusCode = failedResult.getResultCode(); 2197 return AppSearchResultParcel.fromFailedResult(failedResult); 2198 } finally { 2199 if (instance != null) { 2200 int estimatedBinderLatencyMillis = 2201 2 * (int) (totalLatencyStartTimeMillis 2202 - request.getBinderCallStartTimeMillis()); 2203 int totalLatencyMillis = 2204 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 2205 String callingPackageName = request.getCallerAttributionSource() 2206 .getPackageName(); 2207 instance.getLogger().logStats(new CallStats.Builder() 2208 .setPackageName(callingPackageName) 2209 .setStatusCode(statusCode) 2210 .setTotalLatencyMillis(totalLatencyMillis) 2211 .setCallType(CallStats.CALL_TYPE_UNREGISTER_OBSERVER_CALLBACK) 2212 // TODO(b/173532925) check the existing binder call latency chart 2213 // is good enough for us: 2214 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 2215 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 2216 .setNumOperationsSucceeded(operationSuccessCount) 2217 .setNumOperationsFailed(operationFailureCount) 2218 .build()); 2219 } 2220 } 2221 } 2222 2223 @Override initialize( @onNull InitializeAidlRequest request, @NonNull IAppSearchResultCallback callback)2224 public void initialize( 2225 @NonNull InitializeAidlRequest request, 2226 @NonNull IAppSearchResultCallback callback) { 2227 Objects.requireNonNull(request); 2228 Objects.requireNonNull(callback); 2229 2230 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 2231 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 2232 request.getCallerAttributionSource(), request.getUserHandle(), callback); 2233 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 2234 if (targetUser == null) { 2235 return; // Verification failed; verifyIncomingCall triggered callback. 2236 } 2237 if (mAppSearchConfig.getCachedDenylist().checkDeniedPackage(callingPackageName, 2238 CallStats.CALL_TYPE_INITIALIZE)) { 2239 // Note: can't log CallStats here since UserInstance isn't guaranteed to (and most 2240 // likely does not) exist 2241 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 2242 AppSearchResult.newFailedResult(RESULT_DENIED, null))); 2243 return; 2244 } 2245 mServiceImplHelper.executeLambdaForUserAsync(targetUser, callback, callingPackageName, 2246 CallStats.CALL_TYPE_INITIALIZE, () -> { 2247 @AppSearchResult.ResultCode int statusCode = RESULT_OK; 2248 AppSearchUserInstance instance = null; 2249 int operationSuccessCount = 0; 2250 int operationFailureCount = 0; 2251 try { 2252 Context targetUserContext = mAppSearchEnvironment 2253 .createContextAsUser(mContext, request.getUserHandle()); 2254 instance = mAppSearchUserInstanceManager.getOrCreateUserInstance( 2255 targetUserContext, 2256 targetUser, 2257 mAppSearchConfig); 2258 ++operationSuccessCount; 2259 invokeCallbackOnResult(callback, AppSearchResultParcel.fromVoid()); 2260 } catch (AppSearchException | RuntimeException e) { 2261 ++operationFailureCount; 2262 AppSearchResult<Void> failedResult = throwableToFailedResult(e); 2263 statusCode = failedResult.getResultCode(); 2264 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 2265 failedResult)); 2266 } finally { 2267 if (instance != null) { 2268 int estimatedBinderLatencyMillis = 2269 2 * (int) (totalLatencyStartTimeMillis 2270 - request.getBinderCallStartTimeMillis()); 2271 int totalLatencyMillis = 2272 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 2273 instance.getLogger().logStats(new CallStats.Builder() 2274 .setPackageName(callingPackageName) 2275 .setStatusCode(statusCode) 2276 .setTotalLatencyMillis(totalLatencyMillis) 2277 .setCallType(CallStats.CALL_TYPE_INITIALIZE) 2278 // TODO(b/173532925) check the existing binder call latency chart 2279 // is good enough for us: 2280 // http://dashboards/view/_72c98f9a_91d9_41d4_ab9a_bc14f79742b4 2281 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 2282 .setNumOperationsSucceeded(operationSuccessCount) 2283 .setNumOperationsFailed(operationFailureCount) 2284 .build()); 2285 } 2286 } 2287 }); 2288 } 2289 2290 @Override executeAppFunction( @onNull ExecuteAppFunctionAidlRequest request, @NonNull IAppSearchResultCallback callback)2291 public void executeAppFunction( 2292 @NonNull ExecuteAppFunctionAidlRequest request, 2293 @NonNull IAppSearchResultCallback callback) { 2294 Objects.requireNonNull(request); 2295 Objects.requireNonNull(callback); 2296 2297 long totalLatencyStartTimeMillis = SystemClock.elapsedRealtime(); 2298 2299 String callingPackageName = request.getCallerAttributionSource().getPackageName(); 2300 UserHandle targetUser = mServiceImplHelper.verifyIncomingCallWithCallback( 2301 request.getCallerAttributionSource(), request.getUserHandle(), callback); 2302 if (targetUser == null) { 2303 return; // Verification failed; verifyIncomingCall triggered callback. 2304 } 2305 if (checkCallDenied( 2306 callingPackageName, /* databaseName= */ null, 2307 CallStats.CALL_TYPE_EXECUTE_APP_FUNCTION, callback, targetUser, 2308 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 2309 /* numOperations= */ 1)) { 2310 return; 2311 } 2312 2313 // Log the stats as well whenever we invoke the AppSearchResultCallback. 2314 final SafeOneTimeAppSearchResultCallback safeCallback = 2315 new SafeOneTimeAppSearchResultCallback(callback, result -> { 2316 AppSearchUserInstance instance = 2317 mAppSearchUserInstanceManager.getUserInstance(targetUser); 2318 int totalLatencyMillis = 2319 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 2320 int estimatedBinderLatencyMillis = 2321 2 * (int) (totalLatencyStartTimeMillis 2322 - request.getBinderCallStartTimeMillis()); 2323 instance.getLogger().logStats(new CallStats.Builder() 2324 .setPackageName(callingPackageName) 2325 .setStatusCode(result.getResultCode()) 2326 .setTotalLatencyMillis(totalLatencyMillis) 2327 .setCallType(CallStats.CALL_TYPE_EXECUTE_APP_FUNCTION) 2328 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 2329 .build()); 2330 }); 2331 2332 // TODO(b/327134039): Add a new policy for this in W timeframe. 2333 if (mServiceImplHelper.isUserOrganizationManaged(targetUser)) { 2334 safeCallback.onFailedResult(AppSearchResult.newFailedResult( 2335 RESULT_SECURITY_ERROR, 2336 "Cannot run on a device with a device owner or from the managed profile.")); 2337 return; 2338 } 2339 2340 String targetPackageName = request.getClientRequest().getTargetPackageName(); 2341 if (TextUtils.isEmpty(targetPackageName)) { 2342 safeCallback.onFailedResult(AppSearchResult.newFailedResult( 2343 RESULT_INVALID_ARGUMENT, 2344 "targetPackageName cannot be empty.")); 2345 return; 2346 } 2347 if (!verifyExecuteAppFunctionCaller( 2348 callingPackageName, 2349 targetPackageName, 2350 targetUser)) { 2351 safeCallback.onFailedResult(AppSearchResult.newFailedResult( 2352 RESULT_SECURITY_ERROR, 2353 callingPackageName + " is not allowed to call executeAppFunction")); 2354 return; 2355 } 2356 2357 boolean callAccepted = mServiceImplHelper.executeLambdaForUserAsync( 2358 targetUser, callback, callingPackageName, 2359 CallStats.CALL_TYPE_EXECUTE_APP_FUNCTION, 2360 () -> executeAppFunctionUnchecked( 2361 request.getClientRequest(), 2362 targetUser, 2363 safeCallback)); 2364 if (!callAccepted) { 2365 logRateLimitedOrCallDeniedCallStats(callingPackageName, /* databaseName= */ null, 2366 CallStats.CALL_TYPE_EXECUTE_APP_FUNCTION, targetUser, 2367 request.getBinderCallStartTimeMillis(), totalLatencyStartTimeMillis, 2368 /*numOperations=*/ 1, RESULT_RATE_LIMITED); 2369 } 2370 } 2371 2372 /** 2373 * The same as {@link #executeAppFunction}, except this is without the caller check. 2374 * This method runs on the user-local thread pool. 2375 */ 2376 @WorkerThread executeAppFunctionUnchecked( @onNull ExecuteAppFunctionRequest request, @NonNull UserHandle userHandle, @NonNull SafeOneTimeAppSearchResultCallback safeCallback)2377 private void executeAppFunctionUnchecked( 2378 @NonNull ExecuteAppFunctionRequest request, 2379 @NonNull UserHandle userHandle, 2380 @NonNull SafeOneTimeAppSearchResultCallback safeCallback) { 2381 Intent serviceIntent = new Intent(AppFunctionService.SERVICE_INTERFACE); 2382 serviceIntent.setPackage(request.getTargetPackageName()); 2383 2384 Context userContext = mAppSearchEnvironment.createContextAsUser(mContext, userHandle); 2385 ResolveInfo resolveInfo = userContext.getPackageManager() 2386 .resolveService(serviceIntent, 0); 2387 if (resolveInfo == null || resolveInfo.serviceInfo == null) { 2388 safeCallback.onFailedResult(AppSearchResult.newFailedResult( 2389 RESULT_NOT_FOUND, "Cannot find the target service.")); 2390 return; 2391 } 2392 ServiceInfo serviceInfo = resolveInfo.serviceInfo; 2393 if (!PERMISSION_BIND_APP_FUNCTION_SERVICE.equals(serviceInfo.permission)) { 2394 safeCallback.onFailedResult(AppSearchResult.newFailedResult( 2395 RESULT_NOT_FOUND, 2396 "Failed to find a valid target service. The resolved service is missing " 2397 + "the BIND_APP_FUNCTION_SERVICE permission.")); 2398 return; 2399 } 2400 serviceIntent.setComponent( 2401 new ComponentName(serviceInfo.packageName, serviceInfo.name)); 2402 2403 if (request.getSha256Certificate() != null) { 2404 if (!PackageManagerUtil.hasSigningCertificate( 2405 mContext, request.getTargetPackageName(), request.getSha256Certificate())) { 2406 safeCallback.onFailedResult( 2407 AppSearchResult.newFailedResult( 2408 RESULT_NOT_FOUND, "Cannot find the target service")); 2409 return; 2410 } 2411 } 2412 2413 boolean bindServiceResult = mAppFunctionServiceCallHelper.runServiceCall( 2414 serviceIntent, 2415 Context.BIND_ALLOW_BACKGROUND_ACTIVITY_STARTS | Context.BIND_AUTO_CREATE, 2416 mAppSearchConfig.getAppFunctionCallTimeoutMillis(), 2417 userHandle, 2418 new ServiceCallHelper.RunServiceCallCallback<>() { 2419 @Override 2420 public void onServiceConnected( 2421 @NonNull IAppFunctionService service, 2422 @NonNull ServiceUsageCompleteListener completeListener) { 2423 try { 2424 service.executeAppFunction( 2425 request, 2426 new IAppSearchResultCallback.Stub() { 2427 @Override 2428 public void onResult( 2429 AppSearchResultParcel resultParcel) { 2430 safeCallback.onResult(resultParcel); 2431 completeListener.onCompleted(); 2432 } 2433 }); 2434 } catch (Exception e) { 2435 safeCallback.onFailedResult(AppSearchResult 2436 .throwableToFailedResult(e)); 2437 completeListener.onCompleted(); 2438 } 2439 } 2440 2441 @Override 2442 public void onFailedToConnect() { 2443 safeCallback.onFailedResult( 2444 AppSearchResult.newFailedResult(RESULT_INTERNAL_ERROR, null)); 2445 } 2446 2447 @Override 2448 public void onTimedOut() { 2449 safeCallback.onFailedResult( 2450 AppSearchResult.newFailedResult(RESULT_TIMED_OUT, null)); 2451 } 2452 }); 2453 if (!bindServiceResult) { 2454 safeCallback.onFailedResult(AppSearchResult.newFailedResult( 2455 RESULT_INTERNAL_ERROR, "Failed to bind the target service.")); 2456 } 2457 } 2458 2459 /** 2460 * Determines whether the caller is authorized to execute an app function via 2461 * {@link #executeAppFunction}. 2462 * <p> 2463 * Authorization is granted under the following conditions: 2464 * <ul> 2465 * <li>The caller is the same app that owns the target function.</li> 2466 * <li>The caller possesses the SYSTEM_UI_INTELLIGENCE role for the target user. </li> 2467 * </ul> 2468 * 2469 * @param callingPackage The validated package name of the calling app. 2470 * @param targetPackage The package name of the target app. 2471 * @param targetUser The target user. 2472 * @return {@code true} if the caller is authorized, {@code false} otherwise. 2473 */ verifyExecuteAppFunctionCaller( @onNull String callingPackage, @NonNull String targetPackage, @NonNull UserHandle targetUser)2474 private boolean verifyExecuteAppFunctionCaller( 2475 @NonNull String callingPackage, 2476 @NonNull String targetPackage, 2477 @NonNull UserHandle targetUser) { 2478 // While adding new system role-based permissions through mainline updates is possible, 2479 // granting them to system apps in previous android versions is not. System apps must 2480 // request permissions in their prebuilt APKs included in the system image. We cannot 2481 // modify prebuilts in older images anymore. 2482 // TODO(b/327134039): Enforce permission checking for Android V+ or W+, depending on 2483 // whether the new prebuilt can be included in the system image on time. 2484 if (callingPackage.equals(targetPackage)) { 2485 return true; 2486 } 2487 long originalToken = Binder.clearCallingIdentity(); 2488 try { 2489 List<String> systemUiIntelligencePackages = 2490 mRoleManager.getRoleHoldersAsUser(SYSTEM_UI_INTELLIGENCE, targetUser); 2491 return systemUiIntelligencePackages.contains(callingPackage); 2492 } finally { 2493 Binder.restoreCallingIdentity(originalToken); 2494 } 2495 } 2496 2497 @BinderThread dumpContactsIndexer(@onNull PrintWriter pw, boolean verbose)2498 private void dumpContactsIndexer(@NonNull PrintWriter pw, boolean verbose) { 2499 Objects.requireNonNull(pw); 2500 UserHandle currentUser = UserHandle.getUserHandleForUid(Binder.getCallingUid()); 2501 try { 2502 pw.println("ContactsIndexer stats for " + currentUser); 2503 mLifecycle.dumpContactsIndexerForUser(currentUser, pw, verbose); 2504 } catch (Exception e) { 2505 String errorMessage = 2506 "Unable to dump the internal contacts indexer state for the user: " 2507 + currentUser; 2508 Log.e(TAG, errorMessage, e); 2509 pw.println(errorMessage); 2510 } 2511 } 2512 2513 @BinderThread dumpAppSearch(@onNull PrintWriter pw, boolean verbose)2514 private void dumpAppSearch(@NonNull PrintWriter pw, boolean verbose) { 2515 Objects.requireNonNull(pw); 2516 2517 UserHandle currentUser = UserHandle.getUserHandleForUid(Binder.getCallingUid()); 2518 try { 2519 AppSearchUserInstance instance = mAppSearchUserInstanceManager.getUserInstance( 2520 currentUser); 2521 2522 // Print out the recorded last called APIs. 2523 List<ApiCallRecord> lastCalledApis = instance.getLogger().getLastCalledApis(); 2524 if (!lastCalledApis.isEmpty()) { 2525 pw.println("Last Called APIs:"); 2526 for (int i = 0; i < lastCalledApis.size(); i++) { 2527 pw.println(lastCalledApis.get(i)); 2528 } 2529 pw.println(); 2530 } 2531 2532 DebugInfoProto debugInfo = instance.getAppSearchImpl().getRawDebugInfoProto( 2533 verbose ? DebugInfoVerbosity.Code.DETAILED 2534 : DebugInfoVerbosity.Code.BASIC); 2535 // TODO(b/229778472) Consider showing the original names of namespaces and types 2536 // for a specific package if the package name is passed as a parameter from users. 2537 debugInfo = AdbDumpUtil.desensitizeDebugInfo(debugInfo); 2538 pw.println(debugInfo.getIndexInfo().getIndexStorageInfo()); 2539 pw.println(); 2540 pw.println("lite_index_info:"); 2541 pw.println(debugInfo.getIndexInfo().getLiteIndexInfo()); 2542 pw.println(); 2543 pw.println("main_index_info:"); 2544 pw.println(debugInfo.getIndexInfo().getMainIndexInfo()); 2545 pw.println(); 2546 pw.println(debugInfo.getDocumentInfo()); 2547 pw.println(); 2548 pw.println(debugInfo.getSchemaInfo()); 2549 } catch (Exception e) { 2550 String errorMessage = 2551 "Unable to dump the internal state for the user: " + currentUser; 2552 Log.e(TAG, errorMessage, e); 2553 pw.println(errorMessage); 2554 } 2555 } 2556 2557 @Override dump(FileDescriptor fd, PrintWriter pw, String[] args)2558 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2559 if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP) 2560 != PackageManager.PERMISSION_GRANTED) { 2561 pw.println("Permission Denial: can't dump AppSearchManagerService from pid=" 2562 + Binder.getCallingPid() + ", uid=" + Binder.getCallingUid() 2563 + " due to missing android.permission.DUMP permission"); 2564 return; 2565 } 2566 boolean verbose = false; 2567 if (args != null) { 2568 for (int i = 0; i < args.length; i++) { 2569 String arg = args[i]; 2570 if (Objects.equals(arg, "-h")) { 2571 pw.println( 2572 "Dumps the internal state of AppSearch platform storage and " 2573 + "AppSearch Contacts Indexer for the current user."); 2574 pw.println("-v, verbose mode"); 2575 return; 2576 } else if (Objects.equals(arg, "-v") || Objects.equals(arg, "-a")) { 2577 // "-a" is included when adb dumps all services e.g. in adb bugreport so we 2578 // want to run in verbose mode when this happens 2579 verbose = true; 2580 } 2581 } 2582 } 2583 dumpAppSearch(pw, verbose); 2584 dumpContactsIndexer(pw, verbose); 2585 } 2586 } 2587 2588 private class AppSearchStorageStatsAugmenter implements StorageStatsAugmenter { 2589 @Override augmentStatsForPackageForUser( @onNull PackageStats stats, @NonNull String packageName, @NonNull UserHandle userHandle, boolean canCallerAccessAllStats)2590 public void augmentStatsForPackageForUser( 2591 @NonNull PackageStats stats, 2592 @NonNull String packageName, 2593 @NonNull UserHandle userHandle, 2594 boolean canCallerAccessAllStats) { 2595 Objects.requireNonNull(stats); 2596 Objects.requireNonNull(packageName); 2597 Objects.requireNonNull(userHandle); 2598 2599 try { 2600 mServiceImplHelper.verifyUserUnlocked(userHandle); 2601 AppSearchUserInstance instance = 2602 mAppSearchUserInstanceManager.getUserInstanceOrNull(userHandle); 2603 if (instance == null) { 2604 // augment storage info from file 2605 Context userContext = mAppSearchEnvironment 2606 .createContextAsUser(mContext, userHandle); 2607 UserStorageInfo userStorageInfo = 2608 mAppSearchUserInstanceManager.getOrCreateUserStorageInfoInstance( 2609 userContext, userHandle); 2610 stats.dataSize += 2611 userStorageInfo.getSizeBytesForPackage(packageName); 2612 } else { 2613 stats.dataSize += instance.getAppSearchImpl() 2614 .getStorageInfoForPackage(packageName).getSizeBytes(); 2615 } 2616 } catch (AppSearchException | RuntimeException e) { 2617 Log.e( 2618 TAG, 2619 "Unable to augment storage stats for " 2620 + userHandle 2621 + " packageName " 2622 + packageName, 2623 e); 2624 ExceptionUtil.handleException(e); 2625 } 2626 } 2627 2628 @Override augmentStatsForUid( @onNull PackageStats stats, int uid, boolean canCallerAccessAllStats)2629 public void augmentStatsForUid( 2630 @NonNull PackageStats stats, int uid, boolean canCallerAccessAllStats) { 2631 Objects.requireNonNull(stats); 2632 2633 UserHandle userHandle = UserHandle.getUserHandleForUid(uid); 2634 try { 2635 mServiceImplHelper.verifyUserUnlocked(userHandle); 2636 String[] packagesForUid = mPackageManager.getPackagesForUid(uid); 2637 if (packagesForUid == null) { 2638 return; 2639 } 2640 AppSearchUserInstance instance = 2641 mAppSearchUserInstanceManager.getUserInstanceOrNull(userHandle); 2642 if (instance == null) { 2643 // augment storage info from file 2644 Context userContext = mAppSearchEnvironment 2645 .createContextAsUser(mContext, userHandle); 2646 UserStorageInfo userStorageInfo = 2647 mAppSearchUserInstanceManager.getOrCreateUserStorageInfoInstance( 2648 userContext, userHandle); 2649 for (int i = 0; i < packagesForUid.length; i++) { 2650 stats.dataSize += userStorageInfo.getSizeBytesForPackage( 2651 packagesForUid[i]); 2652 } 2653 } else { 2654 for (int i = 0; i < packagesForUid.length; i++) { 2655 stats.dataSize += instance.getAppSearchImpl() 2656 .getStorageInfoForPackage(packagesForUid[i]).getSizeBytes(); 2657 } 2658 } 2659 } catch (AppSearchException | RuntimeException e) { 2660 Log.e(TAG, "Unable to augment storage stats for uid " + uid, e); 2661 ExceptionUtil.handleException(e); 2662 } 2663 } 2664 2665 @Override augmentStatsForUser( @onNull PackageStats stats, @NonNull UserHandle userHandle)2666 public void augmentStatsForUser( 2667 @NonNull PackageStats stats, @NonNull UserHandle userHandle) { 2668 // TODO(b/179160886): this implementation could incur many jni calls and a lot of 2669 // in-memory processing from getStorageInfoForPackage. Instead, we can just compute the 2670 // size of the icing dir (or use the overall StorageInfo without interpolating it). 2671 Objects.requireNonNull(stats); 2672 Objects.requireNonNull(userHandle); 2673 2674 try { 2675 mServiceImplHelper.verifyUserUnlocked(userHandle); 2676 AppSearchUserInstance instance = 2677 mAppSearchUserInstanceManager.getUserInstanceOrNull(userHandle); 2678 if (instance == null) { 2679 // augment storage info from file 2680 Context userContext = mAppSearchEnvironment 2681 .createContextAsUser(mContext, userHandle); 2682 UserStorageInfo userStorageInfo = 2683 mAppSearchUserInstanceManager.getOrCreateUserStorageInfoInstance( 2684 userContext, userHandle); 2685 stats.dataSize += userStorageInfo.getTotalSizeBytes(); 2686 } else { 2687 List<PackageInfo> packagesForUser = mPackageManager.getInstalledPackagesAsUser( 2688 /* flags= */ 0, userHandle.getIdentifier()); 2689 if (packagesForUser != null) { 2690 for (int i = 0; i < packagesForUser.size(); i++) { 2691 String packageName = packagesForUser.get(i).packageName; 2692 stats.dataSize += instance.getAppSearchImpl() 2693 .getStorageInfoForPackage(packageName).getSizeBytes(); 2694 } 2695 } 2696 } 2697 } catch (AppSearchException | RuntimeException e) { 2698 Log.e(TAG, "Unable to augment storage stats for " + userHandle, e); 2699 ExceptionUtil.handleException(e); 2700 } 2701 } 2702 } 2703 2704 /** 2705 * Dispatches change notifications if there are any to dispatch. 2706 * 2707 * <p>This method is async; notifications are dispatched onto their own registered executors. 2708 * 2709 * <p>IMPORTANT: You must always call this within the background task that contains the 2710 * operation that mutated the index. If you called it outside of that task, it could start 2711 * before the task completes, causing notifications to be missed. 2712 */ 2713 @WorkerThread dispatchChangeNotifications(@onNull AppSearchUserInstance instance)2714 private void dispatchChangeNotifications(@NonNull AppSearchUserInstance instance) { 2715 instance.getAppSearchImpl().dispatchAndClearChangeNotifications(); 2716 } 2717 2718 @WorkerThread checkForOptimize( @onNull UserHandle targetUser, @NonNull AppSearchUserInstance instance, int mutateBatchSize)2719 private void checkForOptimize( 2720 @NonNull UserHandle targetUser, 2721 @NonNull AppSearchUserInstance instance, 2722 int mutateBatchSize) { 2723 if (mServiceImplHelper.isUserLocked(targetUser)) { 2724 // We shouldn't schedule any task to locked user. 2725 return; 2726 } 2727 mExecutorManager.getOrCreateUserExecutor(targetUser).execute(() -> { 2728 long totalLatencyStartMillis = SystemClock.elapsedRealtime(); 2729 OptimizeStats.Builder builder = new OptimizeStats.Builder(); 2730 try { 2731 instance.getAppSearchImpl().checkForOptimize(mutateBatchSize, builder); 2732 } catch (Exception e) { 2733 Log.w(TAG, "Error occurred when check for optimize", e); 2734 } finally { 2735 OptimizeStats oStats = builder 2736 .setTotalLatencyMillis( 2737 (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)) 2738 .build(); 2739 if (oStats.getOriginalDocumentCount() > 0) { 2740 // see if optimize has been run by checking originalDocumentCount 2741 instance.getLogger().logStats(oStats); 2742 } 2743 } 2744 }); 2745 } 2746 2747 @WorkerThread checkForOptimize( @onNull UserHandle targetUser, @NonNull AppSearchUserInstance instance)2748 private void checkForOptimize( 2749 @NonNull UserHandle targetUser, 2750 @NonNull AppSearchUserInstance instance) { 2751 if (mServiceImplHelper.isUserLocked(targetUser)) { 2752 // We shouldn't schedule any task to locked user. 2753 return; 2754 } 2755 mExecutorManager.getOrCreateUserExecutor(targetUser).execute(() -> { 2756 long totalLatencyStartMillis = SystemClock.elapsedRealtime(); 2757 OptimizeStats.Builder builder = new OptimizeStats.Builder(); 2758 try { 2759 instance.getAppSearchImpl().checkForOptimize(builder); 2760 } catch (Exception e) { 2761 Log.w(TAG, "Error occurred when check for optimize", e); 2762 } finally { 2763 OptimizeStats oStats = builder 2764 .setTotalLatencyMillis( 2765 (int) (SystemClock.elapsedRealtime() - totalLatencyStartMillis)) 2766 .build(); 2767 if (oStats.getOriginalDocumentCount() > 0) { 2768 // see if optimize has been run by checking originalDocumentCount 2769 instance.getLogger().logStats(oStats); 2770 } 2771 } 2772 }); 2773 } 2774 2775 /** 2776 * An API call is considered global if the calling package and target package names do not 2777 * match. 2778 * <p> 2779 * Enterprise session calls do not necessarily have access to same-package data; therefore, even 2780 * if the calling and target packages are the same, enterprise session calls must always be 2781 * global to go through the proper visibility checks. (Enterprise session calls are also always 2782 * considered global for CallStats logging.) 2783 */ isGlobalCall(@onNull String callingPackageName, @NonNull String targetPackageName, boolean isForEnterprise)2784 private boolean isGlobalCall(@NonNull String callingPackageName, 2785 @NonNull String targetPackageName, boolean isForEnterprise) { 2786 return !callingPackageName.equals(targetPackageName) || isForEnterprise; 2787 } 2788 2789 /** 2790 * Logs rate-limited or denied calls to CallStats. 2791 */ logRateLimitedOrCallDeniedCallStats(@onNull String callingPackageName, @Nullable String callingDatabaseName, @CallStats.CallType int apiType, @NonNull UserHandle targetUser, long binderCallStartTimeMillis, long totalLatencyStartTimeMillis, int numOperations, @AppSearchResult.ResultCode int statusCode)2792 private void logRateLimitedOrCallDeniedCallStats(@NonNull String callingPackageName, 2793 @Nullable String callingDatabaseName, @CallStats.CallType int apiType, 2794 @NonNull UserHandle targetUser, long binderCallStartTimeMillis, 2795 long totalLatencyStartTimeMillis, int numOperations, 2796 @AppSearchResult.ResultCode int statusCode) { 2797 Objects.requireNonNull(callingPackageName); 2798 Objects.requireNonNull(targetUser); 2799 int estimatedBinderLatencyMillis = 2800 2 * (int) (totalLatencyStartTimeMillis - binderCallStartTimeMillis); 2801 int totalLatencyMillis = 2802 (int) (SystemClock.elapsedRealtime() - totalLatencyStartTimeMillis); 2803 mAppSearchUserInstanceManager.getUserInstance(targetUser).getLogger().logStats( 2804 new CallStats.Builder() 2805 .setPackageName(callingPackageName) 2806 .setDatabase(callingDatabaseName) 2807 .setStatusCode(statusCode) 2808 .setTotalLatencyMillis(totalLatencyMillis) 2809 .setCallType(apiType) 2810 .setEstimatedBinderLatencyMillis(estimatedBinderLatencyMillis) 2811 .setNumOperationsFailed(numOperations) 2812 .build()); 2813 } 2814 2815 /** 2816 * Checks if an API call for a given calling package and calling database should be denied 2817 * according to the denylist. If the call is denied, also logs the denial through CallStats. 2818 * 2819 * @return true if the given api call should be denied for the given calling package and calling 2820 * database; otherwise false 2821 */ checkCallDenied(@onNull String callingPackageName, @Nullable String callingDatabaseName, @CallStats.CallType int apiType, @NonNull UserHandle targetUser, long binderCallStartTimeMillis, long totalLatencyStartTimeMillis, int numOperations)2822 private boolean checkCallDenied(@NonNull String callingPackageName, 2823 @Nullable String callingDatabaseName, @CallStats.CallType int apiType, 2824 @NonNull UserHandle targetUser, long binderCallStartTimeMillis, 2825 long totalLatencyStartTimeMillis, int numOperations) { 2826 Denylist denylist = mAppSearchConfig.getCachedDenylist(); 2827 boolean denied = callingDatabaseName == null ? denylist.checkDeniedPackage( 2828 callingPackageName, apiType) : denylist.checkDeniedPackageDatabase( 2829 callingPackageName, callingDatabaseName, apiType); 2830 if (denied) { 2831 logRateLimitedOrCallDeniedCallStats(callingPackageName, callingDatabaseName, apiType, 2832 targetUser, binderCallStartTimeMillis, totalLatencyStartTimeMillis, 2833 numOperations, RESULT_DENIED); 2834 } 2835 return denied; 2836 } 2837 2838 /** 2839 * Checks if an API call for a given calling package and calling database should be denied 2840 * according to the denylist. If the call is denied, also logs the denial through CallStats and 2841 * invokes the given {@link IAppSearchResultCallback} with a failed result. 2842 * 2843 * @return true if the given api call should be denied for the given calling package and calling 2844 * database; otherwise false 2845 */ checkCallDenied(@onNull String callingPackageName, @Nullable String callingDatabaseName, @CallStats.CallType int apiType, @NonNull IAppSearchResultCallback callback, @NonNull UserHandle targetUser, long binderCallStartTimeMillis, long totalLatencyStartTimeMillis, int numOperations)2846 private boolean checkCallDenied(@NonNull String callingPackageName, 2847 @Nullable String callingDatabaseName, @CallStats.CallType int apiType, 2848 @NonNull IAppSearchResultCallback callback, @NonNull UserHandle targetUser, 2849 long binderCallStartTimeMillis, long totalLatencyStartTimeMillis, int numOperations) { 2850 if (checkCallDenied(callingPackageName, callingDatabaseName, apiType, targetUser, 2851 binderCallStartTimeMillis, totalLatencyStartTimeMillis, numOperations)) { 2852 invokeCallbackOnResult(callback, AppSearchResultParcel.fromFailedResult( 2853 AppSearchResult.newFailedResult(RESULT_DENIED, null))); 2854 return true; 2855 } 2856 return false; 2857 } 2858 2859 /** 2860 * Checks if an API call for a given calling package and calling database should be denied 2861 * according to the denylist. If the call is denied, also logs the denial through CallStats and 2862 * invokes the given {@link IAppSearchBatchResultCallback} with a failed result. 2863 * 2864 * @return true if the given api call should be denied for the given calling package and calling 2865 * database; otherwise false 2866 */ checkCallDenied(@onNull String callingPackageName, @Nullable String callingDatabaseName, @CallStats.CallType int apiType, @NonNull IAppSearchBatchResultCallback callback, @NonNull UserHandle targetUser, long binderCallStartTimeMillis, long totalLatencyStartTimeMillis, int numOperations)2867 private boolean checkCallDenied(@NonNull String callingPackageName, 2868 @Nullable String callingDatabaseName, @CallStats.CallType int apiType, 2869 @NonNull IAppSearchBatchResultCallback callback, @NonNull UserHandle targetUser, 2870 long binderCallStartTimeMillis, long totalLatencyStartTimeMillis, int numOperations) { 2871 if (checkCallDenied(callingPackageName, callingDatabaseName, apiType, targetUser, 2872 binderCallStartTimeMillis, totalLatencyStartTimeMillis, numOperations)) { 2873 invokeCallbackOnError(callback, AppSearchResult.newFailedResult(RESULT_DENIED, null)); 2874 return true; 2875 } 2876 return false; 2877 } 2878 } 2879