1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.server.appfunctions; 18 19 import static android.app.appfunctions.AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_METADATA_DB; 20 import static android.app.appfunctions.AppFunctionRuntimeMetadata.APP_FUNCTION_RUNTIME_NAMESPACE; 21 22 import static com.android.server.appfunctions.AppFunctionExecutors.THREAD_POOL_EXECUTOR; 23 import static com.android.server.appfunctions.CallerValidator.CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION; 24 import static com.android.server.appfunctions.CallerValidator.CAN_EXECUTE_APP_FUNCTIONS_DENIED; 25 26 import android.annotation.NonNull; 27 import android.annotation.Nullable; 28 import android.annotation.WorkerThread; 29 import android.app.appfunctions.AppFunctionException; 30 import android.app.appfunctions.AppFunctionManager; 31 import android.app.appfunctions.AppFunctionManagerHelper; 32 import android.app.appfunctions.AppFunctionManagerHelper.AppFunctionNotFoundException; 33 import android.app.appfunctions.AppFunctionRuntimeMetadata; 34 import android.app.appfunctions.AppFunctionStaticMetadataHelper; 35 import android.app.appfunctions.ExecuteAppFunctionAidlRequest; 36 import android.app.appfunctions.ExecuteAppFunctionResponse; 37 import android.app.appfunctions.IAppFunctionEnabledCallback; 38 import android.app.appfunctions.IAppFunctionManager; 39 import android.app.appfunctions.IAppFunctionService; 40 import android.app.appfunctions.ICancellationCallback; 41 import android.app.appfunctions.IExecuteAppFunctionCallback; 42 import android.app.appfunctions.SafeOneTimeExecuteAppFunctionCallback; 43 import android.app.appsearch.AppSearchBatchResult; 44 import android.app.appsearch.AppSearchManager; 45 import android.app.appsearch.AppSearchManager.SearchContext; 46 import android.app.appsearch.AppSearchResult; 47 import android.app.appsearch.GenericDocument; 48 import android.app.appsearch.GetByDocumentIdRequest; 49 import android.app.appsearch.PutDocumentsRequest; 50 import android.app.appsearch.observer.DocumentChangeInfo; 51 import android.app.appsearch.observer.ObserverCallback; 52 import android.app.appsearch.observer.ObserverSpec; 53 import android.app.appsearch.observer.SchemaChangeInfo; 54 import android.content.Context; 55 import android.content.Intent; 56 import android.content.pm.PackageInfo; 57 import android.content.pm.PackageManager; 58 import android.content.pm.PackageManagerInternal; 59 import android.content.pm.SigningInfo; 60 import android.os.Binder; 61 import android.os.CancellationSignal; 62 import android.os.IBinder; 63 import android.os.ICancellationSignal; 64 import android.os.OutcomeReceiver; 65 import android.os.ParcelableException; 66 import android.os.RemoteException; 67 import android.os.UserHandle; 68 import android.text.TextUtils; 69 import android.util.Slog; 70 71 import com.android.internal.annotations.GuardedBy; 72 import com.android.internal.annotations.VisibleForTesting; 73 import com.android.internal.infra.AndroidFuture; 74 import com.android.internal.util.DumpUtils; 75 import com.android.server.SystemService.TargetUser; 76 77 import java.io.FileDescriptor; 78 import java.io.PrintWriter; 79 import java.util.Collections; 80 import java.util.Map; 81 import java.util.Objects; 82 import java.util.WeakHashMap; 83 import java.util.concurrent.CompletionException; 84 import java.util.concurrent.Executor; 85 86 /** Implementation of the AppFunctionManagerService. */ 87 public class AppFunctionManagerServiceImpl extends IAppFunctionManager.Stub { 88 private static final String TAG = AppFunctionManagerServiceImpl.class.getSimpleName(); 89 90 private final RemoteServiceCaller<IAppFunctionService> mRemoteServiceCaller; 91 private final CallerValidator mCallerValidator; 92 private final ServiceHelper mInternalServiceHelper; 93 private final ServiceConfig mServiceConfig; 94 private final Context mContext; 95 private final Map<String, Object> mLocks = new WeakHashMap<>(); 96 private final AppFunctionsLoggerWrapper mLoggerWrapper; 97 private final PackageManagerInternal mPackageManagerInternal; 98 AppFunctionManagerServiceImpl( @onNull Context context, @NonNull PackageManagerInternal packageManagerInternal)99 public AppFunctionManagerServiceImpl( 100 @NonNull Context context, @NonNull PackageManagerInternal packageManagerInternal) { 101 this( 102 context, 103 new RemoteServiceCallerImpl<>( 104 context, IAppFunctionService.Stub::asInterface, THREAD_POOL_EXECUTOR), 105 new CallerValidatorImpl(context), 106 new ServiceHelperImpl(context), 107 new ServiceConfigImpl(), 108 new AppFunctionsLoggerWrapper(context), 109 packageManagerInternal); 110 } 111 112 @VisibleForTesting AppFunctionManagerServiceImpl( Context context, RemoteServiceCaller<IAppFunctionService> remoteServiceCaller, CallerValidator callerValidator, ServiceHelper appFunctionInternalServiceHelper, ServiceConfig serviceConfig, AppFunctionsLoggerWrapper loggerWrapper, PackageManagerInternal packageManagerInternal)113 AppFunctionManagerServiceImpl( 114 Context context, 115 RemoteServiceCaller<IAppFunctionService> remoteServiceCaller, 116 CallerValidator callerValidator, 117 ServiceHelper appFunctionInternalServiceHelper, 118 ServiceConfig serviceConfig, 119 AppFunctionsLoggerWrapper loggerWrapper, 120 PackageManagerInternal packageManagerInternal) { 121 mContext = Objects.requireNonNull(context); 122 mRemoteServiceCaller = Objects.requireNonNull(remoteServiceCaller); 123 mCallerValidator = Objects.requireNonNull(callerValidator); 124 mInternalServiceHelper = Objects.requireNonNull(appFunctionInternalServiceHelper); 125 mServiceConfig = serviceConfig; 126 mLoggerWrapper = loggerWrapper; 127 mPackageManagerInternal = Objects.requireNonNull(packageManagerInternal); 128 } 129 130 /** Called when the user is unlocked. */ onUserUnlocked(TargetUser user)131 public void onUserUnlocked(TargetUser user) { 132 Objects.requireNonNull(user); 133 134 registerAppSearchObserver(user); 135 trySyncRuntimeMetadata(user); 136 } 137 138 /** Called when the user is stopping. */ onUserStopping(@onNull TargetUser user)139 public void onUserStopping(@NonNull TargetUser user) { 140 Objects.requireNonNull(user); 141 142 MetadataSyncPerUser.removeUserSyncAdapter(user.getUserHandle()); 143 } 144 145 @Override dump(@onNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args)146 public void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter pw, @NonNull String[] args) { 147 if (!DumpUtils.checkDumpPermission(mContext, TAG, pw)) { 148 return; 149 } 150 151 final long token = Binder.clearCallingIdentity(); 152 try { 153 AppFunctionDumpHelper.dumpAppFunctionsState(mContext, pw); 154 } finally { 155 Binder.restoreCallingIdentity(token); 156 } 157 } 158 159 @Override executeAppFunction( @onNull ExecuteAppFunctionAidlRequest requestInternal, @NonNull IExecuteAppFunctionCallback executeAppFunctionCallback)160 public ICancellationSignal executeAppFunction( 161 @NonNull ExecuteAppFunctionAidlRequest requestInternal, 162 @NonNull IExecuteAppFunctionCallback executeAppFunctionCallback) { 163 Objects.requireNonNull(requestInternal); 164 Objects.requireNonNull(executeAppFunctionCallback); 165 166 int callingUid = Binder.getCallingUid(); 167 int callingPid = Binder.getCallingPid(); 168 169 final SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback = 170 initializeSafeExecuteAppFunctionCallback( 171 requestInternal, executeAppFunctionCallback, callingUid); 172 173 String validatedCallingPackage; 174 try { 175 validatedCallingPackage = 176 mCallerValidator.validateCallingPackage(requestInternal.getCallingPackage()); 177 mCallerValidator.verifyTargetUserHandle( 178 requestInternal.getUserHandle(), validatedCallingPackage); 179 } catch (SecurityException exception) { 180 safeExecuteAppFunctionCallback.onError( 181 new AppFunctionException( 182 AppFunctionException.ERROR_DENIED, exception.getMessage())); 183 return null; 184 } 185 186 ICancellationSignal localCancelTransport = CancellationSignal.createTransport(); 187 188 THREAD_POOL_EXECUTOR.execute( 189 () -> { 190 try { 191 executeAppFunctionInternal( 192 requestInternal, 193 callingUid, 194 callingPid, 195 localCancelTransport, 196 safeExecuteAppFunctionCallback, 197 executeAppFunctionCallback.asBinder()); 198 } catch (Exception e) { 199 safeExecuteAppFunctionCallback.onError( 200 mapExceptionToExecuteAppFunctionResponse(e)); 201 } 202 }); 203 return localCancelTransport; 204 } 205 206 @WorkerThread executeAppFunctionInternal( @onNull ExecuteAppFunctionAidlRequest requestInternal, int callingUid, int callingPid, @NonNull ICancellationSignal localCancelTransport, @NonNull SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback, @NonNull IBinder callerBinder)207 private void executeAppFunctionInternal( 208 @NonNull ExecuteAppFunctionAidlRequest requestInternal, 209 int callingUid, 210 int callingPid, 211 @NonNull ICancellationSignal localCancelTransport, 212 @NonNull SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback, 213 @NonNull IBinder callerBinder) { 214 UserHandle targetUser = requestInternal.getUserHandle(); 215 UserHandle callingUser = UserHandle.getUserHandleForUid(callingUid); 216 if (!mCallerValidator.verifyEnterprisePolicyIsAllowed(callingUser, targetUser)) { 217 safeExecuteAppFunctionCallback.onError( 218 new AppFunctionException( 219 AppFunctionException.ERROR_ENTERPRISE_POLICY_DISALLOWED, 220 "Cannot run on a user with a restricted enterprise policy")); 221 return; 222 } 223 224 String targetPackageName = requestInternal.getClientRequest().getTargetPackageName(); 225 if (TextUtils.isEmpty(targetPackageName)) { 226 safeExecuteAppFunctionCallback.onError( 227 new AppFunctionException( 228 AppFunctionException.ERROR_INVALID_ARGUMENT, 229 "Target package name cannot be empty.")); 230 return; 231 } 232 233 mCallerValidator 234 .verifyCallerCanExecuteAppFunction( 235 callingUid, 236 callingPid, 237 targetUser, 238 requestInternal.getCallingPackage(), 239 targetPackageName, 240 requestInternal.getClientRequest().getFunctionIdentifier()) 241 .thenCompose( 242 canExecuteResult -> { 243 if (canExecuteResult == CAN_EXECUTE_APP_FUNCTIONS_DENIED) { 244 return AndroidFuture.failedFuture(new SecurityException( 245 "Caller does not have permission to execute the" 246 + " appfunction")); 247 } 248 return isAppFunctionEnabled( 249 requestInternal 250 .getClientRequest() 251 .getFunctionIdentifier(), 252 requestInternal 253 .getClientRequest() 254 .getTargetPackageName(), 255 getAppSearchManagerAsUser( 256 requestInternal.getUserHandle()), 257 THREAD_POOL_EXECUTOR) 258 .thenApply( 259 isEnabled -> { 260 if (!isEnabled) { 261 throw new DisabledAppFunctionException( 262 "The app function is disabled"); 263 } 264 return canExecuteResult; 265 }); 266 }) 267 .thenAccept( 268 canExecuteResult -> { 269 int bindFlags = Context.BIND_AUTO_CREATE; 270 if (canExecuteResult 271 == CAN_EXECUTE_APP_FUNCTIONS_ALLOWED_HAS_PERMISSION) { 272 // If the caller doesn't have the permission, do not use 273 // BIND_FOREGROUND_SERVICE to avoid it raising its process state by 274 // calling its own AppFunctions. 275 bindFlags |= Context.BIND_FOREGROUND_SERVICE; 276 } 277 Intent serviceIntent = 278 mInternalServiceHelper.resolveAppFunctionService( 279 targetPackageName, targetUser); 280 if (serviceIntent == null) { 281 safeExecuteAppFunctionCallback.onError( 282 new AppFunctionException( 283 AppFunctionException.ERROR_SYSTEM_ERROR, 284 "Cannot find the target service.")); 285 return; 286 } 287 // Grant target app implicit visibility to the caller 288 final int grantRecipientUserId = targetUser.getIdentifier(); 289 final int grantRecipientAppId = 290 UserHandle.getAppId( 291 mPackageManagerInternal.getPackageUid( 292 requestInternal 293 .getClientRequest() 294 .getTargetPackageName(), 295 /* flags= */ 0, 296 /* userId= */ grantRecipientUserId)); 297 if (grantRecipientAppId > 0) { 298 mPackageManagerInternal.grantImplicitAccess( 299 grantRecipientUserId, 300 serviceIntent, 301 grantRecipientAppId, 302 callingUid, 303 /* direct= */ true); 304 } 305 bindAppFunctionServiceUnchecked( 306 requestInternal, 307 serviceIntent, 308 targetUser, 309 localCancelTransport, 310 safeExecuteAppFunctionCallback, 311 bindFlags, 312 callerBinder, 313 callingUid); 314 }) 315 .exceptionally( 316 ex -> { 317 safeExecuteAppFunctionCallback.onError( 318 mapExceptionToExecuteAppFunctionResponse(ex)); 319 return null; 320 }); 321 } 322 isAppFunctionEnabled( @onNull String functionIdentifier, @NonNull String targetPackage, @NonNull AppSearchManager appSearchManager, @NonNull Executor executor)323 private static AndroidFuture<Boolean> isAppFunctionEnabled( 324 @NonNull String functionIdentifier, 325 @NonNull String targetPackage, 326 @NonNull AppSearchManager appSearchManager, 327 @NonNull Executor executor) { 328 AndroidFuture<Boolean> future = new AndroidFuture<>(); 329 AppFunctionManagerHelper.isAppFunctionEnabled( 330 functionIdentifier, 331 targetPackage, 332 appSearchManager, 333 executor, 334 new OutcomeReceiver<>() { 335 @Override 336 public void onResult(@NonNull Boolean result) { 337 future.complete(result); 338 } 339 340 @Override 341 public void onError(@NonNull Exception error) { 342 future.completeExceptionally(error); 343 } 344 }); 345 return future; 346 } 347 348 @Override setAppFunctionEnabled( @onNull String callingPackage, @NonNull String functionIdentifier, @NonNull UserHandle userHandle, @AppFunctionManager.EnabledState int enabledState, @NonNull IAppFunctionEnabledCallback callback)349 public void setAppFunctionEnabled( 350 @NonNull String callingPackage, 351 @NonNull String functionIdentifier, 352 @NonNull UserHandle userHandle, 353 @AppFunctionManager.EnabledState int enabledState, 354 @NonNull IAppFunctionEnabledCallback callback) { 355 try { 356 mCallerValidator.validateCallingPackage(callingPackage); 357 } catch (SecurityException e) { 358 reportException(callback, e); 359 return; 360 } 361 THREAD_POOL_EXECUTOR.execute( 362 () -> { 363 try { 364 synchronized (getLockForPackage(callingPackage)) { 365 setAppFunctionEnabledInternalLocked( 366 callingPackage, functionIdentifier, userHandle, enabledState); 367 } 368 callback.onSuccess(); 369 } catch (Exception e) { 370 Slog.e(TAG, "Error in setAppFunctionEnabled: ", e); 371 reportException(callback, e); 372 } 373 }); 374 } 375 reportException( @onNull IAppFunctionEnabledCallback callback, @NonNull Exception exception)376 private static void reportException( 377 @NonNull IAppFunctionEnabledCallback callback, @NonNull Exception exception) { 378 try { 379 callback.onError(new ParcelableException(exception)); 380 } catch (RemoteException e) { 381 Slog.w(TAG, "Failed to report the exception", e); 382 } 383 } 384 385 /** 386 * Sets the enabled status of a specified app function. 387 * 388 * <p>Required to hold a lock to call this function to avoid document changes during the 389 * process. 390 */ 391 @WorkerThread 392 @GuardedBy("getLockForPackage(callingPackage)") setAppFunctionEnabledInternalLocked( @onNull String callingPackage, @NonNull String functionIdentifier, @NonNull UserHandle userHandle, @AppFunctionManager.EnabledState int enabledState)393 private void setAppFunctionEnabledInternalLocked( 394 @NonNull String callingPackage, 395 @NonNull String functionIdentifier, 396 @NonNull UserHandle userHandle, 397 @AppFunctionManager.EnabledState int enabledState) 398 throws Exception { 399 AppSearchManager perUserAppSearchManager = getAppSearchManagerAsUser(userHandle); 400 401 if (perUserAppSearchManager == null) { 402 throw new IllegalStateException( 403 "AppSearchManager not found for user:" + userHandle.getIdentifier()); 404 } 405 SearchContext runtimeMetadataSearchContext = 406 new SearchContext.Builder(APP_FUNCTION_RUNTIME_METADATA_DB).build(); 407 408 try (FutureAppSearchSession runtimeMetadataSearchSession = 409 new FutureAppSearchSessionImpl( 410 perUserAppSearchManager, 411 THREAD_POOL_EXECUTOR, 412 runtimeMetadataSearchContext)) { 413 AppFunctionRuntimeMetadata existingMetadata = 414 new AppFunctionRuntimeMetadata( 415 getRuntimeMetadataGenericDocument( 416 callingPackage, 417 functionIdentifier, 418 runtimeMetadataSearchSession)); 419 AppFunctionRuntimeMetadata newMetadata = 420 new AppFunctionRuntimeMetadata.Builder(existingMetadata) 421 .setEnabled(enabledState) 422 .build(); 423 AppSearchBatchResult<String, Void> putDocumentBatchResult = 424 runtimeMetadataSearchSession 425 .put( 426 new PutDocumentsRequest.Builder() 427 .addGenericDocuments(newMetadata) 428 .build()) 429 .get(); 430 if (!putDocumentBatchResult.isSuccess()) { 431 throw new IllegalStateException( 432 "Failed writing updated doc to AppSearch due to " + putDocumentBatchResult); 433 } 434 } 435 } 436 437 @WorkerThread 438 @NonNull getRuntimeMetadataGenericDocument( @onNull String packageName, @NonNull String functionId, @NonNull FutureAppSearchSession runtimeMetadataSearchSession)439 private AppFunctionRuntimeMetadata getRuntimeMetadataGenericDocument( 440 @NonNull String packageName, 441 @NonNull String functionId, 442 @NonNull FutureAppSearchSession runtimeMetadataSearchSession) 443 throws Exception { 444 String documentId = 445 AppFunctionRuntimeMetadata.getDocumentIdForAppFunction(packageName, functionId); 446 GetByDocumentIdRequest request = 447 new GetByDocumentIdRequest.Builder(APP_FUNCTION_RUNTIME_NAMESPACE) 448 .addIds(documentId) 449 .build(); 450 AppSearchBatchResult<String, GenericDocument> result = 451 runtimeMetadataSearchSession.getByDocumentId(request).get(); 452 if (result.isSuccess()) { 453 return new AppFunctionRuntimeMetadata((result.getSuccesses().get(documentId))); 454 } 455 throw new IllegalArgumentException("Function " + functionId + " does not exist"); 456 } 457 bindAppFunctionServiceUnchecked( @onNull ExecuteAppFunctionAidlRequest requestInternal, @NonNull Intent serviceIntent, @NonNull UserHandle targetUser, @NonNull ICancellationSignal cancellationSignalTransport, @NonNull SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback, int bindFlags, @NonNull IBinder callerBinder, int callingUid)458 private void bindAppFunctionServiceUnchecked( 459 @NonNull ExecuteAppFunctionAidlRequest requestInternal, 460 @NonNull Intent serviceIntent, 461 @NonNull UserHandle targetUser, 462 @NonNull ICancellationSignal cancellationSignalTransport, 463 @NonNull SafeOneTimeExecuteAppFunctionCallback safeExecuteAppFunctionCallback, 464 int bindFlags, 465 @NonNull IBinder callerBinder, 466 int callingUid) { 467 CancellationSignal cancellationSignal = 468 CancellationSignal.fromTransport(cancellationSignalTransport); 469 ICancellationCallback cancellationCallback = 470 new ICancellationCallback.Stub() { 471 @Override 472 public void sendCancellationTransport( 473 @NonNull ICancellationSignal cancellationTransport) { 474 cancellationSignal.setRemote(cancellationTransport); 475 } 476 }; 477 boolean bindServiceResult = 478 mRemoteServiceCaller.runServiceCall( 479 serviceIntent, 480 bindFlags, 481 targetUser, 482 mServiceConfig.getExecuteAppFunctionCancellationTimeoutMillis(), 483 cancellationSignal, 484 new RunAppFunctionServiceCallback( 485 requestInternal, 486 cancellationCallback, 487 safeExecuteAppFunctionCallback, 488 getPackageSigningInfo( 489 targetUser, 490 requestInternal.getCallingPackage(), 491 callingUid)), 492 callerBinder); 493 494 if (!bindServiceResult) { 495 Slog.e(TAG, "Failed to bind to the AppFunctionService"); 496 safeExecuteAppFunctionCallback.onError( 497 new AppFunctionException( 498 AppFunctionException.ERROR_SYSTEM_ERROR, 499 "Failed to bind the AppFunctionService.")); 500 } 501 } 502 503 @NonNull getPackageSigningInfo( @onNull UserHandle targetUser, @NonNull String packageName, int uid)504 private SigningInfo getPackageSigningInfo( 505 @NonNull UserHandle targetUser, @NonNull String packageName, int uid) { 506 Objects.requireNonNull(packageName); 507 Objects.requireNonNull(targetUser); 508 509 PackageInfo packageInfo; 510 packageInfo = 511 Objects.requireNonNull( 512 mPackageManagerInternal.getPackageInfo( 513 packageName, 514 PackageManager.GET_SIGNING_CERTIFICATES, 515 uid, 516 targetUser.getIdentifier())); 517 return Objects.requireNonNull(packageInfo.signingInfo); 518 } 519 getAppSearchManagerAsUser(@onNull UserHandle userHandle)520 private AppSearchManager getAppSearchManagerAsUser(@NonNull UserHandle userHandle) { 521 return mContext.createContextAsUser(userHandle, /* flags= */ 0) 522 .getSystemService(AppSearchManager.class); 523 } 524 mapExceptionToExecuteAppFunctionResponse(Throwable e)525 private AppFunctionException mapExceptionToExecuteAppFunctionResponse(Throwable e) { 526 if (e instanceof CompletionException) { 527 e = e.getCause(); 528 } 529 int resultCode = AppFunctionException.ERROR_SYSTEM_ERROR; 530 if (e instanceof AppFunctionNotFoundException) { 531 resultCode = AppFunctionException.ERROR_FUNCTION_NOT_FOUND; 532 } else if (e instanceof AppSearchException appSearchException) { 533 resultCode = 534 mapAppSearchResultFailureCodeToExecuteAppFunctionResponse( 535 appSearchException.getResultCode()); 536 } else if (e instanceof SecurityException) { 537 resultCode = AppFunctionException.ERROR_DENIED; 538 } else if (e instanceof DisabledAppFunctionException) { 539 resultCode = AppFunctionException.ERROR_DISABLED; 540 } 541 return new AppFunctionException(resultCode, e.getMessage()); 542 } 543 mapAppSearchResultFailureCodeToExecuteAppFunctionResponse(int resultCode)544 private int mapAppSearchResultFailureCodeToExecuteAppFunctionResponse(int resultCode) { 545 if (resultCode == AppSearchResult.RESULT_OK) { 546 throw new IllegalArgumentException( 547 "This method can only be used to convert failure result codes."); 548 } 549 550 switch (resultCode) { 551 case AppSearchResult.RESULT_NOT_FOUND: 552 return AppFunctionException.ERROR_FUNCTION_NOT_FOUND; 553 case AppSearchResult.RESULT_INVALID_ARGUMENT: 554 case AppSearchResult.RESULT_INTERNAL_ERROR: 555 case AppSearchResult.RESULT_SECURITY_ERROR: 556 // fall-through 557 } 558 return AppFunctionException.ERROR_SYSTEM_ERROR; 559 } 560 registerAppSearchObserver(@onNull TargetUser user)561 private void registerAppSearchObserver(@NonNull TargetUser user) { 562 AppSearchManager perUserAppSearchManager = 563 mContext.createContextAsUser(user.getUserHandle(), /* flags= */ 0) 564 .getSystemService(AppSearchManager.class); 565 if (perUserAppSearchManager == null) { 566 Slog.d(TAG, "AppSearch Manager not found for user: " + user.getUserIdentifier()); 567 return; 568 } 569 FutureGlobalSearchSession futureGlobalSearchSession = 570 new FutureGlobalSearchSession(perUserAppSearchManager, THREAD_POOL_EXECUTOR); 571 AppFunctionMetadataObserver appFunctionMetadataObserver = 572 new AppFunctionMetadataObserver( 573 user.getUserHandle(), 574 mContext.createContextAsUser(user.getUserHandle(), /* flags= */ 0)); 575 var unused = 576 futureGlobalSearchSession 577 .registerObserverCallbackAsync( 578 "android", 579 new ObserverSpec.Builder().build(), 580 THREAD_POOL_EXECUTOR, 581 appFunctionMetadataObserver) 582 .whenComplete( 583 (voidResult, ex) -> { 584 if (ex != null) { 585 Slog.e(TAG, "Failed to register observer: ", ex); 586 } 587 futureGlobalSearchSession.close(); 588 }); 589 } 590 trySyncRuntimeMetadata(@onNull TargetUser user)591 private void trySyncRuntimeMetadata(@NonNull TargetUser user) { 592 MetadataSyncAdapter metadataSyncAdapter = 593 MetadataSyncPerUser.getPerUserMetadataSyncAdapter( 594 user.getUserHandle(), 595 mContext.createContextAsUser(user.getUserHandle(), /* flags= */ 0)); 596 if (metadataSyncAdapter != null) { 597 var unused = 598 metadataSyncAdapter 599 .submitSyncRequest() 600 .whenComplete( 601 (isSuccess, ex) -> { 602 if (ex != null || !isSuccess) { 603 Slog.e(TAG, "Sync was not successful"); 604 } 605 }); 606 } 607 } 608 609 /** 610 * Retrieves the lock object associated with the given package name. 611 * 612 * <p>This method returns the lock object from the {@code mLocks} map if it exists. If no lock 613 * is found for the given package name, a new lock object is created, stored in the map, and 614 * returned. 615 */ 616 @VisibleForTesting 617 @NonNull getLockForPackage(String callingPackage)618 Object getLockForPackage(String callingPackage) { 619 // Synchronized the access to mLocks to prevent race condition. 620 synchronized (mLocks) { 621 // By using a WeakHashMap, we allow the garbage collector to reclaim memory by removing 622 // entries associated with unused callingPackage keys. Therefore, we remove the null 623 // values before getting/computing a new value. The goal is to not let the size of this 624 // map grow without an upper bound. 625 mLocks.values().removeAll(Collections.singleton(null)); // Remove null values 626 return mLocks.computeIfAbsent(callingPackage, k -> new Object()); 627 } 628 } 629 630 /** 631 * Returns a new {@link SafeOneTimeExecuteAppFunctionCallback} initialized with a {@link 632 * SafeOneTimeExecuteAppFunctionCallback.CompletionCallback} that logs the results. 633 */ 634 @VisibleForTesting initializeSafeExecuteAppFunctionCallback( @onNull ExecuteAppFunctionAidlRequest requestInternal, @NonNull IExecuteAppFunctionCallback executeAppFunctionCallback, int callingUid)635 SafeOneTimeExecuteAppFunctionCallback initializeSafeExecuteAppFunctionCallback( 636 @NonNull ExecuteAppFunctionAidlRequest requestInternal, 637 @NonNull IExecuteAppFunctionCallback executeAppFunctionCallback, 638 int callingUid) { 639 return new SafeOneTimeExecuteAppFunctionCallback( 640 executeAppFunctionCallback, 641 new SafeOneTimeExecuteAppFunctionCallback.CompletionCallback() { 642 @Override 643 public void finalizeOnSuccess( 644 @NonNull ExecuteAppFunctionResponse result, 645 long executionStartTimeMillis) { 646 mLoggerWrapper.logAppFunctionSuccess( 647 requestInternal, result, callingUid, executionStartTimeMillis); 648 } 649 650 @Override 651 public void finalizeOnError( 652 @NonNull AppFunctionException error, long executionStartTimeMillis) { 653 mLoggerWrapper.logAppFunctionError( 654 requestInternal, 655 error.getErrorCode(), 656 callingUid, 657 executionStartTimeMillis); 658 } 659 }); 660 } 661 662 private static class AppFunctionMetadataObserver implements ObserverCallback { 663 @Nullable private final MetadataSyncAdapter mPerUserMetadataSyncAdapter; 664 665 AppFunctionMetadataObserver(@NonNull UserHandle userHandle, @NonNull Context userContext) { 666 mPerUserMetadataSyncAdapter = 667 MetadataSyncPerUser.getPerUserMetadataSyncAdapter(userHandle, userContext); 668 } 669 670 @Override 671 public void onDocumentChanged(@NonNull DocumentChangeInfo documentChangeInfo) { 672 if (mPerUserMetadataSyncAdapter == null) { 673 return; 674 } 675 if (documentChangeInfo 676 .getDatabaseName() 677 .equals(AppFunctionStaticMetadataHelper.APP_FUNCTION_STATIC_METADATA_DB) 678 && documentChangeInfo 679 .getNamespace() 680 .equals( 681 AppFunctionStaticMetadataHelper 682 .APP_FUNCTION_STATIC_NAMESPACE)) { 683 var unused = mPerUserMetadataSyncAdapter.submitSyncRequest(); 684 } 685 } 686 687 @Override 688 public void onSchemaChanged(@NonNull SchemaChangeInfo schemaChangeInfo) { 689 if (mPerUserMetadataSyncAdapter == null) { 690 return; 691 } 692 if (schemaChangeInfo 693 .getDatabaseName() 694 .equals(AppFunctionStaticMetadataHelper.APP_FUNCTION_STATIC_METADATA_DB)) { 695 boolean shouldInitiateSync = false; 696 for (String schemaName : schemaChangeInfo.getChangedSchemaNames()) { 697 if (schemaName.startsWith(AppFunctionStaticMetadataHelper.STATIC_SCHEMA_TYPE)) { 698 shouldInitiateSync = true; 699 break; 700 } 701 } 702 if (shouldInitiateSync) { 703 var unused = mPerUserMetadataSyncAdapter.submitSyncRequest(); 704 } 705 } 706 } 707 } 708 709 /** Throws when executing a disabled app function. */ 710 private static class DisabledAppFunctionException extends RuntimeException { 711 private DisabledAppFunctionException(@NonNull String errorMessage) { 712 super(errorMessage); 713 } 714 } 715 } 716