1 /* 2 * Copyright (C) 2022 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.app.sdksandbox; 18 19 import static android.app.sdksandbox.SandboxLatencyInfo.RESULT_CODE_LOAD_SDK_ALREADY_LOADED; 20 import static android.app.sdksandbox.SandboxLatencyInfo.RESULT_CODE_LOAD_SDK_INTERNAL_ERROR; 21 import static android.app.sdksandbox.SandboxLatencyInfo.RESULT_CODE_LOAD_SDK_NOT_FOUND; 22 import static android.app.sdksandbox.SandboxLatencyInfo.RESULT_CODE_LOAD_SDK_SDK_DEFINED_ERROR; 23 import static android.app.sdksandbox.SandboxLatencyInfo.RESULT_CODE_LOAD_SDK_SDK_SANDBOX_DISABLED; 24 import static android.app.sdksandbox.SandboxLatencyInfo.RESULT_CODE_SDK_SANDBOX_PROCESS_NOT_AVAILABLE; 25 import static android.app.sdksandbox.SandboxLatencyInfo.RESULT_CODE_UNSPECIFIED; 26 import static android.app.sdksandbox.SdkSandboxManager.SDK_SANDBOX_SERVICE; 27 28 import android.annotation.CallbackExecutor; 29 import android.annotation.IntDef; 30 import android.annotation.NonNull; 31 import android.annotation.RequiresPermission; 32 import android.annotation.SdkConstant; 33 import android.annotation.SystemApi; 34 import android.annotation.SystemService; 35 import android.annotation.TestApi; 36 import android.app.Activity; 37 import android.app.sdksandbox.sdkprovider.SdkSandboxActivityHandler; 38 import android.app.sdksandbox.sdkprovider.SdkSandboxController; 39 import android.content.Context; 40 import android.content.Intent; 41 import android.content.SharedPreferences; 42 import android.os.Build; 43 import android.os.Bundle; 44 import android.os.IBinder; 45 import android.os.OutcomeReceiver; 46 import android.os.RemoteException; 47 import android.os.SystemClock; 48 import android.util.Log; 49 import android.view.SurfaceControlViewHost.SurfacePackage; 50 51 import androidx.annotation.RequiresApi; 52 53 import com.android.internal.annotations.GuardedBy; 54 import com.android.modules.utils.build.SdkLevel; 55 56 import java.lang.annotation.Retention; 57 import java.lang.annotation.RetentionPolicy; 58 import java.util.ArrayList; 59 import java.util.List; 60 import java.util.Objects; 61 import java.util.Set; 62 import java.util.concurrent.Executor; 63 64 /** 65 * Provides APIs to load {@link android.content.pm.SharedLibraryInfo#TYPE_SDK_PACKAGE SDKs} into the 66 * SDK sandbox process, and then interact with them. 67 * 68 * <p>SDK sandbox is a java process running in a separate uid range. Each app may have its own SDK 69 * sandbox process. 70 * 71 * <p>The app first needs to declare SDKs it depends on in its manifest using the {@code 72 * <uses-sdk-library>} tag. Apps may only load SDKs they depend on into the SDK sandbox. 73 * 74 * @see android.content.pm.SharedLibraryInfo#TYPE_SDK_PACKAGE 75 * @see <a href="https://developer.android.com/design-for-safety/ads/sdk-runtime">SDK Runtime design 76 * proposal</a> 77 */ 78 @SystemService(SDK_SANDBOX_SERVICE) 79 public final class SdkSandboxManager { 80 81 /** 82 * Use with {@link Context#getSystemService(String)} to retrieve an {@link SdkSandboxManager} 83 * for interacting with the SDKs belonging to this client application. 84 */ 85 public static final String SDK_SANDBOX_SERVICE = "sdk_sandbox"; 86 87 /** 88 * SDK sandbox process is not available. 89 * 90 * <p>This indicates that the SDK sandbox process is not available, either because it has died, 91 * disconnected or was not created in the first place. 92 */ 93 public static final int SDK_SANDBOX_PROCESS_NOT_AVAILABLE = 503; 94 95 /** 96 * SDK not found. 97 * 98 * <p>This indicates that client application tried to load a non-existing SDK by calling {@link 99 * SdkSandboxManager#loadSdk(String, Bundle, Executor, OutcomeReceiver)}. 100 */ 101 public static final int LOAD_SDK_NOT_FOUND = 100; 102 103 /** 104 * SDK is already loaded. 105 * 106 * <p>This indicates that client application tried to reload the same SDK by calling {@link 107 * SdkSandboxManager#loadSdk(String, Bundle, Executor, OutcomeReceiver)} after being 108 * successfully loaded. 109 */ 110 public static final int LOAD_SDK_ALREADY_LOADED = 101; 111 112 /** 113 * SDK error after being loaded. 114 * 115 * <p>This indicates that the SDK encountered an error during post-load initialization. The 116 * details of this can be obtained from the Bundle returned in {@link LoadSdkException} through 117 * the {@link OutcomeReceiver} passed in to {@link SdkSandboxManager#loadSdk}. 118 */ 119 public static final int LOAD_SDK_SDK_DEFINED_ERROR = 102; 120 121 /** 122 * SDK sandbox is disabled. 123 * 124 * <p>This indicates that the SDK sandbox is disabled. Any subsequent attempts to load SDKs in 125 * this boot will also fail. 126 */ 127 public static final int LOAD_SDK_SDK_SANDBOX_DISABLED = 103; 128 129 /** 130 * Internal error while loading SDK. 131 * 132 * <p>This indicates a generic internal error happened while applying the call from client 133 * application. 134 */ 135 public static final int LOAD_SDK_INTERNAL_ERROR = 500; 136 137 /** 138 * Action name for the intent which starts {@link Activity} in SDK sandbox. 139 * 140 * <p>System services would know if the intent is created to start {@link Activity} in sandbox 141 * by comparing the action of the intent to the value of this field. 142 * 143 * <p>This intent should contain an extra param with key equals to {@link 144 * #EXTRA_SANDBOXED_ACTIVITY_HANDLER} and value equals to the {@link IBinder} that identifies 145 * the {@link SdkSandboxActivityHandler} that registered before by an SDK. If the extra param is 146 * missing, the {@link Activity} will fail to start. 147 * 148 * @hide 149 */ 150 @SdkConstant(SdkConstant.SdkConstantType.ACTIVITY_INTENT_ACTION) 151 @SystemApi(client = SystemApi.Client.MODULE_LIBRARIES) 152 public static final String ACTION_START_SANDBOXED_ACTIVITY = 153 "android.app.sdksandbox.action.START_SANDBOXED_ACTIVITY"; 154 155 /** 156 * The key for an element in {@link Activity} intent extra params, the value is an {@link 157 * SdkSandboxActivityHandler} registered by an SDK. 158 * 159 * @hide 160 */ 161 public static final String EXTRA_SANDBOXED_ACTIVITY_HANDLER = 162 "android.app.sdksandbox.extra.SANDBOXED_ACTIVITY_HANDLER"; 163 164 /** 165 * The key for an element in {@link Activity} intent extra params, the value is set while 166 * calling {@link #startSdkSandboxActivity(Activity, IBinder)}. 167 * 168 * @hide 169 */ 170 public static final String EXTRA_SANDBOXED_ACTIVITY_INITIATION_TIME = 171 "android.app.sdksandbox.extra.EXTRA_SANDBOXED_ACTIVITY_INITIATION_TIME"; 172 173 private static final String TAG = "SdkSandboxManager"; 174 private TimeProvider mTimeProvider; 175 176 static class TimeProvider { elapsedRealtime()177 long elapsedRealtime() { 178 return SystemClock.elapsedRealtime(); 179 } 180 } 181 182 /** @hide */ 183 @IntDef( 184 value = { 185 LOAD_SDK_NOT_FOUND, 186 LOAD_SDK_ALREADY_LOADED, 187 LOAD_SDK_SDK_DEFINED_ERROR, 188 LOAD_SDK_SDK_SANDBOX_DISABLED, 189 LOAD_SDK_INTERNAL_ERROR, 190 SDK_SANDBOX_PROCESS_NOT_AVAILABLE 191 }) 192 @Retention(RetentionPolicy.SOURCE) 193 public @interface LoadSdkErrorCode {} 194 195 /** Internal error while requesting a {@link SurfacePackage}. 196 * 197 * <p>This indicates a generic internal error happened while requesting a 198 * {@link SurfacePackage}. 199 */ 200 public static final int REQUEST_SURFACE_PACKAGE_INTERNAL_ERROR = 700; 201 202 /** 203 * SDK is not loaded while requesting a {@link SurfacePackage}. 204 * 205 * <p>This indicates that the SDK for which the {@link SurfacePackage} is being requested is not 206 * loaded, either because the sandbox died or because it was not loaded in the first place. 207 */ 208 public static final int REQUEST_SURFACE_PACKAGE_SDK_NOT_LOADED = 701; 209 210 /** @hide */ 211 @IntDef( 212 prefix = "REQUEST_SURFACE_PACKAGE_", 213 value = { 214 REQUEST_SURFACE_PACKAGE_INTERNAL_ERROR, 215 REQUEST_SURFACE_PACKAGE_SDK_NOT_LOADED 216 }) 217 @Retention(RetentionPolicy.SOURCE) 218 public @interface RequestSurfacePackageErrorCode {} 219 220 /** 221 * SDK sandbox is disabled. 222 * 223 * <p>{@link SdkSandboxManager} APIs are hidden. Attempts at calling them will result in {@link 224 * UnsupportedOperationException}. 225 */ 226 public static final int SDK_SANDBOX_STATE_DISABLED = 0; 227 228 /** 229 * SDK sandbox is enabled. 230 * 231 * <p>App can use {@link SdkSandboxManager} APIs to load {@code SDKs} it depends on into the 232 * corresponding SDK sandbox process. 233 */ 234 public static final int SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION = 2; 235 236 /** @hide */ 237 @Retention(RetentionPolicy.SOURCE) 238 @IntDef(prefix = "SDK_SANDBOX_STATUS_", value = { 239 SDK_SANDBOX_STATE_DISABLED, 240 SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION, 241 }) 242 public @interface SdkSandboxState {} 243 244 /** 245 * The name of key to be used in the Bundle fields of {@link #requestSurfacePackage(String, 246 * Bundle, Executor, OutcomeReceiver)}, its value should define the integer width of the {@link 247 * SurfacePackage} in pixels. 248 * 249 * @deprecated Parameter for {@link #requestSurfacePackage(String, Bundle, Executor, 250 * OutcomeReceiver)} which is getting deprecated. 251 */ 252 @Deprecated 253 public static final String EXTRA_WIDTH_IN_PIXELS = 254 "android.app.sdksandbox.extra.WIDTH_IN_PIXELS"; 255 /** 256 * The name of key to be used in the Bundle fields of {@link #requestSurfacePackage(String, 257 * Bundle, Executor, OutcomeReceiver)}, its value should define the integer height of the {@link 258 * SurfacePackage} in pixels. 259 * 260 * @deprecated Parameter for {@link #requestSurfacePackage(String, Bundle, Executor, 261 * OutcomeReceiver)} which is getting deprecated. 262 */ 263 @Deprecated 264 public static final String EXTRA_HEIGHT_IN_PIXELS = 265 "android.app.sdksandbox.extra.HEIGHT_IN_PIXELS"; 266 /** 267 * The name of key to be used in the Bundle fields of {@link #requestSurfacePackage(String, 268 * Bundle, Executor, OutcomeReceiver)}, its value should define the integer ID of the logical 269 * display to display the {@link SurfacePackage}. 270 * 271 * @deprecated Parameter for {@link #requestSurfacePackage(String, Bundle, Executor, 272 * OutcomeReceiver)} which is getting deprecated. 273 */ 274 @Deprecated 275 public static final String EXTRA_DISPLAY_ID = "android.app.sdksandbox.extra.DISPLAY_ID"; 276 277 /** 278 * The name of key to be used in the Bundle fields of {@link #requestSurfacePackage(String, 279 * Bundle, Executor, OutcomeReceiver)}, its value should present the token returned by {@link 280 * android.view.SurfaceView#getHostToken()} once the {@link android.view.SurfaceView} has been 281 * added to the view hierarchy. Only a non-null value is accepted to enable ANR reporting. 282 * 283 * @deprecated Parameter for {@link #requestSurfacePackage(String, Bundle, Executor, 284 * OutcomeReceiver)} which is getting deprecated. 285 */ 286 @Deprecated 287 public static final String EXTRA_HOST_TOKEN = "android.app.sdksandbox.extra.HOST_TOKEN"; 288 289 /** 290 * The name of key in the Bundle which is passed to the {@code onResult} function of the {@link 291 * OutcomeReceiver} which is field of {@link #requestSurfacePackage(String, Bundle, Executor, 292 * OutcomeReceiver)}, its value presents the requested {@link SurfacePackage}. 293 * 294 * @deprecated Parameter for {@link #requestSurfacePackage(String, Bundle, Executor, 295 * OutcomeReceiver)} which is getting deprecated. 296 */ 297 @Deprecated 298 public static final String EXTRA_SURFACE_PACKAGE = 299 "android.app.sdksandbox.extra.SURFACE_PACKAGE"; 300 301 private final ISdkSandboxManager mService; 302 private final Context mContext; 303 304 @GuardedBy("mLifecycleCallbacks") 305 private final ArrayList<SdkSandboxProcessDeathCallbackProxy> mLifecycleCallbacks = 306 new ArrayList<>(); 307 308 private final SharedPreferencesSyncManager mSyncManager; 309 310 /** @hide */ SdkSandboxManager(@onNull Context context, @NonNull ISdkSandboxManager binder)311 public SdkSandboxManager(@NonNull Context context, @NonNull ISdkSandboxManager binder) { 312 mContext = Objects.requireNonNull(context, "context should not be null"); 313 mService = Objects.requireNonNull(binder, "binder should not be null"); 314 // TODO(b/239403323): There can be multiple package in the same app process 315 mSyncManager = SharedPreferencesSyncManager.getInstance(context, binder); 316 mTimeProvider = new TimeProvider(); 317 } 318 319 /** Returns the current state of the availability of the SDK sandbox feature. */ 320 @SdkSandboxState getSdkSandboxState()321 public static int getSdkSandboxState() { 322 return SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION; 323 } 324 325 /** 326 * Returns if SDK sandbox process corresponding to the app currently running. 327 * 328 * @hide 329 */ 330 @TestApi isSdkSandboxServiceRunning()331 public boolean isSdkSandboxServiceRunning() { 332 try { 333 return mService.isSdkSandboxServiceRunning(mContext.getPackageName()); 334 } catch (RemoteException e) { 335 throw e.rethrowFromSystemServer(); 336 } 337 } 338 339 /** 340 * Stops the SDK sandbox process corresponding to the app. 341 * 342 * @hide 343 */ 344 @TestApi 345 @RequiresPermission("com.android.app.sdksandbox.permission.STOP_SDK_SANDBOX") stopSdkSandbox()346 public void stopSdkSandbox() { 347 try { 348 mService.stopSdkSandbox(mContext.getPackageName()); 349 } catch (RemoteException e) { 350 throw e.rethrowFromSystemServer(); 351 } 352 } 353 354 /** 355 * Adds a callback which gets registered for SDK sandbox lifecycle events, such as SDK sandbox 356 * death. If the sandbox has not yet been created when this is called, the request will be 357 * stored until a sandbox is created, at which point it is activated for that sandbox. Multiple 358 * callbacks can be added to detect death and will not be removed when the sandbox dies. 359 * 360 * @param callbackExecutor the {@link Executor} on which to invoke the callback 361 * @param callback the {@link SdkSandboxProcessDeathCallback} which will receive SDK sandbox 362 * lifecycle events. 363 */ addSdkSandboxProcessDeathCallback( @onNull @allbackExecutor Executor callbackExecutor, @NonNull SdkSandboxProcessDeathCallback callback)364 public void addSdkSandboxProcessDeathCallback( 365 @NonNull @CallbackExecutor Executor callbackExecutor, 366 @NonNull SdkSandboxProcessDeathCallback callback) { 367 Objects.requireNonNull(callbackExecutor, "callbackExecutor should not be null"); 368 Objects.requireNonNull(callback, "callback should not be null"); 369 370 synchronized (mLifecycleCallbacks) { 371 final SdkSandboxProcessDeathCallbackProxy callbackProxy = 372 new SdkSandboxProcessDeathCallbackProxy(callbackExecutor, callback); 373 SandboxLatencyInfo sandboxLatencyInfo = 374 new SandboxLatencyInfo( 375 SandboxLatencyInfo.METHOD_ADD_SDK_SANDBOX_LIFECYCLE_CALLBACK); 376 sandboxLatencyInfo.setTimeAppCalledSystemServer(mTimeProvider.elapsedRealtime()); 377 try { 378 mService.addSdkSandboxProcessDeathCallback( 379 mContext.getPackageName(), sandboxLatencyInfo, callbackProxy); 380 } catch (RemoteException e) { 381 throw e.rethrowFromSystemServer(); 382 } 383 mLifecycleCallbacks.add(callbackProxy); 384 } 385 } 386 387 /** 388 * Removes an {@link SdkSandboxProcessDeathCallback} that was previously added using {@link 389 * SdkSandboxManager#addSdkSandboxProcessDeathCallback(Executor, 390 * SdkSandboxProcessDeathCallback)} 391 * 392 * @param callback the {@link SdkSandboxProcessDeathCallback} which was previously added using 393 * {@link SdkSandboxManager#addSdkSandboxProcessDeathCallback(Executor, 394 * SdkSandboxProcessDeathCallback)} 395 */ removeSdkSandboxProcessDeathCallback( @onNull SdkSandboxProcessDeathCallback callback)396 public void removeSdkSandboxProcessDeathCallback( 397 @NonNull SdkSandboxProcessDeathCallback callback) { 398 Objects.requireNonNull(callback, "callback should not be null"); 399 synchronized (mLifecycleCallbacks) { 400 for (int i = mLifecycleCallbacks.size() - 1; i >= 0; i--) { 401 final SdkSandboxProcessDeathCallbackProxy callbackProxy = 402 mLifecycleCallbacks.get(i); 403 if (callbackProxy.callback == callback) { 404 SandboxLatencyInfo sandboxLatencyInfo = 405 new SandboxLatencyInfo( 406 SandboxLatencyInfo 407 .METHOD_REMOVE_SDK_SANDBOX_LIFECYCLE_CALLBACK); 408 sandboxLatencyInfo.setTimeAppCalledSystemServer( 409 mTimeProvider.elapsedRealtime()); 410 try { 411 mService.removeSdkSandboxProcessDeathCallback( 412 mContext.getPackageName(), sandboxLatencyInfo, callbackProxy); 413 } catch (RemoteException e) { 414 throw e.rethrowFromSystemServer(); 415 } 416 mLifecycleCallbacks.remove(i); 417 } 418 } 419 } 420 } 421 422 /** 423 * Registers {@link AppOwnedSdkSandboxInterface} for an app process. 424 * 425 * <p>Registering an {@link AppOwnedSdkSandboxInterface} that has same name as a previously 426 * registered interface will result in {@link IllegalStateException}. 427 * 428 * <p>{@link AppOwnedSdkSandboxInterface#getName()} refers to the name of the interface. 429 * 430 * @param appOwnedSdkSandboxInterface the AppOwnedSdkSandboxInterface to be registered 431 */ registerAppOwnedSdkSandboxInterface( @onNull AppOwnedSdkSandboxInterface appOwnedSdkSandboxInterface)432 public void registerAppOwnedSdkSandboxInterface( 433 @NonNull AppOwnedSdkSandboxInterface appOwnedSdkSandboxInterface) { 434 SandboxLatencyInfo sandboxLatencyInfo = 435 new SandboxLatencyInfo( 436 SandboxLatencyInfo.METHOD_REGISTER_APP_OWNED_SDK_SANDBOX_INTERFACE); 437 sandboxLatencyInfo.setTimeAppCalledSystemServer(mTimeProvider.elapsedRealtime()); 438 try { 439 mService.registerAppOwnedSdkSandboxInterface( 440 mContext.getPackageName(), appOwnedSdkSandboxInterface, sandboxLatencyInfo); 441 } catch (RemoteException e) { 442 throw e.rethrowFromSystemServer(); 443 } 444 } 445 446 /** 447 * Unregisters {@link AppOwnedSdkSandboxInterface}s for an app process. 448 * 449 * @param name the name under which AppOwnedSdkSandboxInterface was registered. 450 */ unregisterAppOwnedSdkSandboxInterface(@onNull String name)451 public void unregisterAppOwnedSdkSandboxInterface(@NonNull String name) { 452 SandboxLatencyInfo sandboxLatencyInfo = 453 new SandboxLatencyInfo( 454 SandboxLatencyInfo.METHOD_UNREGISTER_APP_OWNED_SDK_SANDBOX_INTERFACE); 455 sandboxLatencyInfo.setTimeAppCalledSystemServer(mTimeProvider.elapsedRealtime()); 456 try { 457 mService.unregisterAppOwnedSdkSandboxInterface( 458 mContext.getPackageName(), name, sandboxLatencyInfo); 459 } catch (RemoteException e) { 460 throw e.rethrowFromSystemServer(); 461 } 462 } 463 464 /** 465 * Fetches a list of {@link AppOwnedSdkSandboxInterface} registered for an app 466 * 467 * @return empty list if callingInfo not found in map otherwise a list of {@link 468 * AppOwnedSdkSandboxInterface} 469 */ getAppOwnedSdkSandboxInterfaces()470 public @NonNull List<AppOwnedSdkSandboxInterface> getAppOwnedSdkSandboxInterfaces() { 471 SandboxLatencyInfo sandboxLatencyInfo = 472 new SandboxLatencyInfo( 473 SandboxLatencyInfo.METHOD_GET_APP_OWNED_SDK_SANDBOX_INTERFACES); 474 sandboxLatencyInfo.setTimeAppCalledSystemServer(mTimeProvider.elapsedRealtime()); 475 try { 476 return mService.getAppOwnedSdkSandboxInterfaces( 477 mContext.getPackageName(), sandboxLatencyInfo); 478 } catch (RemoteException e) { 479 throw e.rethrowFromSystemServer(); 480 } 481 } 482 483 /** 484 * Loads SDK in an SDK sandbox java process. 485 * 486 * <p>Loads SDK library with {@code sdkName} to an SDK sandbox process asynchronously. The 487 * caller will be notified through the {@code receiver}. 488 * 489 * <p>The caller should already declare {@code SDKs} it depends on in its manifest using {@code 490 * <uses-sdk-library>} tag. The caller may only load {@code SDKs} it depends on into the SDK 491 * sandbox. 492 * 493 * <p>When the client application loads the first SDK, a new SDK sandbox process will be 494 * created. If a sandbox has already been created for the client application, additional SDKs 495 * will be loaded into the same sandbox. 496 * 497 * <p>This API may only be called while the caller is running in the foreground. Calls from the 498 * background will result in returning {@link LoadSdkException} in the {@code receiver}. 499 * 500 * @param sdkName name of the SDK to be loaded. 501 * @param params additional parameters to be passed to the SDK in the form of a {@link Bundle} 502 * as agreed between the client and the SDK. 503 * @param executor the {@link Executor} on which to invoke the receiver. 504 * @param receiver This either receives a {@link SandboxedSdk} on a successful run, or {@link 505 * LoadSdkException}. 506 */ loadSdk( @onNull String sdkName, @NonNull Bundle params, @NonNull @CallbackExecutor Executor executor, @NonNull OutcomeReceiver<SandboxedSdk, LoadSdkException> receiver)507 public void loadSdk( 508 @NonNull String sdkName, 509 @NonNull Bundle params, 510 @NonNull @CallbackExecutor Executor executor, 511 @NonNull OutcomeReceiver<SandboxedSdk, LoadSdkException> receiver) { 512 Objects.requireNonNull(sdkName, "sdkName should not be null"); 513 Objects.requireNonNull(params, "params should not be null"); 514 Objects.requireNonNull(executor, "executor should not be null"); 515 Objects.requireNonNull(receiver, "receiver should not be null"); 516 final LoadSdkReceiverProxy callbackProxy = 517 new LoadSdkReceiverProxy(executor, receiver, mService); 518 519 IBinder appProcessToken; 520 // Context.getProcessToken() only exists on U+. 521 if (SdkLevel.isAtLeastU()) { 522 appProcessToken = mContext.getProcessToken(); 523 } else { 524 appProcessToken = null; 525 } 526 // TODO(b/297352617): add timeAppCalledSystemServer to the constructor. 527 SandboxLatencyInfo sandboxLatencyInfo = 528 new SandboxLatencyInfo(SandboxLatencyInfo.METHOD_LOAD_SDK); 529 sandboxLatencyInfo.setTimeAppCalledSystemServer(mTimeProvider.elapsedRealtime()); 530 try { 531 mService.loadSdk( 532 mContext.getPackageName(), 533 appProcessToken, 534 sdkName, 535 sandboxLatencyInfo, 536 params, 537 callbackProxy); 538 } catch (RemoteException e) { 539 throw e.rethrowFromSystemServer(); 540 } 541 } 542 543 /** 544 * Fetches information about SDKs that are loaded in the sandbox. 545 * 546 * @return List of {@link SandboxedSdk} containing all currently loaded SDKs. 547 */ getSandboxedSdks()548 public @NonNull List<SandboxedSdk> getSandboxedSdks() { 549 SandboxLatencyInfo sandboxLatencyInfo = 550 new SandboxLatencyInfo(SandboxLatencyInfo.METHOD_GET_SANDBOXED_SDKS); 551 sandboxLatencyInfo.setTimeAppCalledSystemServer(mTimeProvider.elapsedRealtime()); 552 try { 553 return mService.getSandboxedSdks(mContext.getPackageName(), sandboxLatencyInfo); 554 } catch (RemoteException e) { 555 throw e.rethrowFromSystemServer(); 556 } 557 } 558 559 /** 560 * Unloads an SDK that has been previously loaded by the caller. 561 * 562 * <p>It is not guaranteed that the memory allocated for this SDK will be freed immediately. All 563 * subsequent calls to {@link #requestSurfacePackage(String, Bundle, Executor, OutcomeReceiver)} 564 * for the given {@code sdkName} will fail. 565 * 566 * <p>This API may only be called while the caller is running in the foreground. Calls from the 567 * background will result in a {@link SecurityException} being thrown. 568 * 569 * @param sdkName name of the SDK to be unloaded. 570 */ unloadSdk(@onNull String sdkName)571 public void unloadSdk(@NonNull String sdkName) { 572 Objects.requireNonNull(sdkName, "sdkName should not be null"); 573 try { 574 SandboxLatencyInfo sandboxLatencyInfo = 575 new SandboxLatencyInfo(SandboxLatencyInfo.METHOD_UNLOAD_SDK); 576 sandboxLatencyInfo.setTimeAppCalledSystemServer(mTimeProvider.elapsedRealtime()); 577 mService.unloadSdk(mContext.getPackageName(), sdkName, sandboxLatencyInfo); 578 } catch (RemoteException e) { 579 throw e.rethrowFromSystemServer(); 580 } 581 } 582 583 /** 584 * Sends a request for a surface package to the SDK. 585 * 586 * <p>After the client application receives a signal about a successful SDK loading, and has 587 * added a {@link android.view.SurfaceView} to the view hierarchy, it may asynchronously request 588 * a {@link SurfacePackage} to render a view from the SDK. 589 * 590 * <p>When the {@link SurfacePackage} is ready, the {@link OutcomeReceiver#onResult} callback of 591 * the passed {@code receiver} will be invoked. This callback will contain a {@link Bundle} 592 * object, which will contain the key {@link SdkSandboxManager#EXTRA_SURFACE_PACKAGE} whose 593 * associated value is the requested {@link SurfacePackage}. 594 * 595 * <p>The passed {@code params} must contain the following keys: {@link 596 * SdkSandboxManager#EXTRA_WIDTH_IN_PIXELS}, {@link SdkSandboxManager#EXTRA_HEIGHT_IN_PIXELS}, 597 * {@link SdkSandboxManager#EXTRA_DISPLAY_ID} and {@link SdkSandboxManager#EXTRA_HOST_TOKEN}. If 598 * any of these keys are missing or invalid, an {@link IllegalArgumentException} will be thrown. 599 * 600 * <p>This API may only be called while the caller is running in the foreground. Calls from the 601 * background will result in returning RequestSurfacePackageException in the {@code receiver}. 602 * 603 * @param sdkName name of the SDK loaded into the SDK sandbox. 604 * @param params the parameters which the client application passes to the SDK. 605 * @param callbackExecutor the {@link Executor} on which to invoke the callback 606 * @param receiver This either returns a {@link Bundle} on success which will contain the key 607 * {@link SdkSandboxManager#EXTRA_SURFACE_PACKAGE} with a {@link SurfacePackage} value, or 608 * {@link RequestSurfacePackageException} on failure. 609 * @throws IllegalArgumentException if {@code params} does not contain all required keys. 610 * @see android.app.sdksandbox.SdkSandboxManager#EXTRA_WIDTH_IN_PIXELS 611 * @see android.app.sdksandbox.SdkSandboxManager#EXTRA_HEIGHT_IN_PIXELS 612 * @see android.app.sdksandbox.SdkSandboxManager#EXTRA_DISPLAY_ID 613 * @see android.app.sdksandbox.SdkSandboxManager#EXTRA_HOST_TOKEN 614 * @deprecated This method will no longer be supported through {@link SdkSandboxManager}. Please 615 * consider using androidx.privacysandbox library as an alternative 616 */ 617 @Deprecated requestSurfacePackage( @onNull String sdkName, @NonNull Bundle params, @NonNull @CallbackExecutor Executor callbackExecutor, @NonNull OutcomeReceiver<Bundle, RequestSurfacePackageException> receiver)618 public void requestSurfacePackage( 619 @NonNull String sdkName, 620 @NonNull Bundle params, 621 @NonNull @CallbackExecutor Executor callbackExecutor, 622 @NonNull OutcomeReceiver<Bundle, RequestSurfacePackageException> receiver) { 623 Objects.requireNonNull(sdkName, "sdkName should not be null"); 624 Objects.requireNonNull(params, "params should not be null"); 625 Objects.requireNonNull(callbackExecutor, "callbackExecutor should not be null"); 626 Objects.requireNonNull(receiver, "receiver should not be null"); 627 try { 628 int width = params.getInt(EXTRA_WIDTH_IN_PIXELS, -1); // -1 means invalid width 629 if (width <= 0) { 630 throw new IllegalArgumentException( 631 "Field params should have the entry for the key (" 632 + EXTRA_WIDTH_IN_PIXELS 633 + ") with positive integer value"); 634 } 635 636 int height = params.getInt(EXTRA_HEIGHT_IN_PIXELS, -1); // -1 means invalid height 637 if (height <= 0) { 638 throw new IllegalArgumentException( 639 "Field params should have the entry for the key (" 640 + EXTRA_HEIGHT_IN_PIXELS 641 + ") with positive integer value"); 642 } 643 644 int displayId = params.getInt(EXTRA_DISPLAY_ID, -1); // -1 means invalid displayId 645 if (displayId < 0) { 646 throw new IllegalArgumentException( 647 "Field params should have the entry for the key (" 648 + EXTRA_DISPLAY_ID 649 + ") with integer >= 0"); 650 } 651 652 IBinder hostToken = params.getBinder(EXTRA_HOST_TOKEN); 653 if (hostToken == null) { 654 throw new IllegalArgumentException( 655 "Field params should have the entry for the key (" 656 + EXTRA_HOST_TOKEN 657 + ") with not null IBinder value"); 658 } 659 660 SandboxLatencyInfo sandboxLatencyInfo = 661 new SandboxLatencyInfo(SandboxLatencyInfo.METHOD_REQUEST_SURFACE_PACKAGE); 662 sandboxLatencyInfo.setTimeAppCalledSystemServer(mTimeProvider.elapsedRealtime()); 663 664 final RequestSurfacePackageReceiverProxy callbackProxy = 665 new RequestSurfacePackageReceiverProxy(callbackExecutor, receiver, mService); 666 667 mService.requestSurfacePackage( 668 mContext.getPackageName(), 669 sdkName, 670 hostToken, 671 displayId, 672 width, 673 height, 674 sandboxLatencyInfo, 675 params, 676 callbackProxy); 677 } catch (RemoteException e) { 678 throw e.rethrowFromSystemServer(); 679 } 680 } 681 682 /** 683 * Starts an {@link Activity} in the SDK sandbox. 684 * 685 * <p>This function will start a new {@link Activity} in the same task of the passed {@code 686 * fromActivity} and pass it to the SDK that shared the passed {@code sdkActivityToken} that 687 * identifies a request from that SDK to stat this {@link Activity}. 688 * 689 * <p>The {@link Activity} will not start in the following cases: 690 * 691 * <ul> 692 * <li>The App calling this API is in the background. 693 * <li>The passed {@code sdkActivityToken} does not map to a request for an {@link Activity} 694 * form the SDK that shared it with the caller app. 695 * <li>The SDK that shared the passed {@code sdkActivityToken} removed its request for this 696 * {@link Activity}. 697 * <li>The sandbox {@link Activity} is already created. 698 * </ul> 699 * 700 * @param fromActivity the {@link Activity} will be used to start the new sandbox {@link 701 * Activity} by calling {@link Activity#startActivity(Intent)} against it. 702 * @param sdkActivityToken the identifier that is shared by the SDK which requests the {@link 703 * Activity}. 704 */ 705 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) startSdkSandboxActivity( @onNull Activity fromActivity, @NonNull IBinder sdkActivityToken)706 public void startSdkSandboxActivity( 707 @NonNull Activity fromActivity, @NonNull IBinder sdkActivityToken) { 708 if (!SdkLevel.isAtLeastU()) { 709 throw new UnsupportedOperationException(); 710 } 711 712 long timeEventStarted = mTimeProvider.elapsedRealtime(); 713 714 Intent intent = new Intent(); 715 intent.setAction(ACTION_START_SANDBOXED_ACTIVITY); 716 intent.setPackage(mContext.getPackageManager().getSdkSandboxPackageName()); 717 718 Bundle params = new Bundle(); 719 params.putBinder(EXTRA_SANDBOXED_ACTIVITY_HANDLER, sdkActivityToken); 720 params.putLong(EXTRA_SANDBOXED_ACTIVITY_INITIATION_TIME, timeEventStarted); 721 intent.putExtras(params); 722 723 fromActivity.startActivity(intent); 724 725 logStartSdkSandboxActivityLatency(timeEventStarted); 726 } 727 728 // TODO(b/304459399): move Sandbox Activity latency logging to its own class 729 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE) logStartSdkSandboxActivityLatency(long timeEventStarted)730 private void logStartSdkSandboxActivityLatency(long timeEventStarted) { 731 try { 732 // TODO(b/305240130): retrieve SDK info from sandbox process 733 mService.logSandboxActivityApiLatency( 734 StatsdUtil.SANDBOX_ACTIVITY_EVENT_OCCURRED__METHOD__START_SDK_SANDBOX_ACTIVITY, 735 StatsdUtil.SANDBOX_ACTIVITY_EVENT_OCCURRED__CALL_RESULT__SUCCESS, 736 (int) (mTimeProvider.elapsedRealtime() - timeEventStarted)); 737 } catch (RemoteException e) { 738 throw e.rethrowFromSystemServer(); 739 } 740 } 741 742 /** 743 * A callback for tracking events SDK sandbox death. 744 * 745 * <p>The callback can be added using {@link 746 * SdkSandboxManager#addSdkSandboxProcessDeathCallback(Executor, 747 * SdkSandboxProcessDeathCallback)} and removed using {@link 748 * SdkSandboxManager#removeSdkSandboxProcessDeathCallback(SdkSandboxProcessDeathCallback)} 749 */ 750 public interface SdkSandboxProcessDeathCallback { 751 /** 752 * Notifies the client application that the SDK sandbox has died. The sandbox could die for 753 * various reasons, for example, due to memory pressure on the system, or a crash in the 754 * sandbox. 755 * 756 * The system will automatically restart the sandbox process if it died due to a crash. 757 * However, the state of the sandbox will be lost - so any SDKs that were loaded previously 758 * would have to be loaded again, using {@link SdkSandboxManager#loadSdk(String, Bundle, 759 * Executor, OutcomeReceiver)} to continue using them. 760 */ onSdkSandboxDied()761 void onSdkSandboxDied(); 762 } 763 764 /** @hide */ getResultCodeForLoadSdkException( LoadSdkException exception)765 public static @SandboxLatencyInfo.ResultCode int getResultCodeForLoadSdkException( 766 LoadSdkException exception) { 767 return switch (exception.getLoadSdkErrorCode()) { 768 case LOAD_SDK_NOT_FOUND -> RESULT_CODE_LOAD_SDK_NOT_FOUND; 769 case LOAD_SDK_ALREADY_LOADED -> RESULT_CODE_LOAD_SDK_ALREADY_LOADED; 770 case LOAD_SDK_SDK_DEFINED_ERROR -> RESULT_CODE_LOAD_SDK_SDK_DEFINED_ERROR; 771 case LOAD_SDK_SDK_SANDBOX_DISABLED -> RESULT_CODE_LOAD_SDK_SDK_SANDBOX_DISABLED; 772 case LOAD_SDK_INTERNAL_ERROR -> RESULT_CODE_LOAD_SDK_INTERNAL_ERROR; 773 case SDK_SANDBOX_PROCESS_NOT_AVAILABLE -> RESULT_CODE_SDK_SANDBOX_PROCESS_NOT_AVAILABLE; 774 default -> { 775 Log.e( 776 TAG, 777 "Unexpected load SDK exception code: " + exception.getLoadSdkErrorCode()); 778 yield RESULT_CODE_UNSPECIFIED; 779 } 780 }; 781 } 782 783 /** @hide */ 784 private static class SdkSandboxProcessDeathCallbackProxy 785 extends ISdkSandboxProcessDeathCallback.Stub { 786 private final Executor mExecutor; 787 public final SdkSandboxProcessDeathCallback callback; 788 SdkSandboxProcessDeathCallbackProxy( Executor executor, SdkSandboxProcessDeathCallback lifecycleCallback)789 SdkSandboxProcessDeathCallbackProxy( 790 Executor executor, SdkSandboxProcessDeathCallback lifecycleCallback) { 791 mExecutor = executor; 792 callback = lifecycleCallback; 793 } 794 795 @Override onSdkSandboxDied()796 public void onSdkSandboxDied() { 797 mExecutor.execute(() -> callback.onSdkSandboxDied()); 798 } 799 } 800 801 /** 802 * Adds keys to set of keys being synced from app's default {@link SharedPreferences} to the SDK 803 * sandbox. 804 * 805 * <p>Synced data will be available for SDKs to read using the {@link 806 * SdkSandboxController#getClientSharedPreferences()} API. 807 * 808 * <p>To stop syncing any key that has been added using this API, use {@link 809 * #removeSyncedSharedPreferencesKeys(Set)}. 810 * 811 * <p>The sync breaks if the app restarts and user must call this API again to rebuild the pool 812 * of keys for syncing. 813 * 814 * <p>Note: This class does not support use across multiple processes. 815 * 816 * @param keys set of keys that will be synced to Sandbox. 817 */ addSyncedSharedPreferencesKeys(@onNull Set<String> keys)818 public void addSyncedSharedPreferencesKeys(@NonNull Set<String> keys) { 819 Objects.requireNonNull(keys, "keys cannot be null"); 820 for (String key : keys) { 821 if (key == null) { 822 throw new IllegalArgumentException("keys cannot contain null"); 823 } 824 } 825 mSyncManager.addSharedPreferencesSyncKeys(keys); 826 } 827 828 /** 829 * Removes keys from set of keys that have been added using {@link 830 * #addSyncedSharedPreferencesKeys(Set)} 831 * 832 * <p>Removed keys will be erased from the SDK sandbox if they have been synced already. 833 * 834 * @param keys set of key names that should no longer be synced to Sandbox. 835 */ removeSyncedSharedPreferencesKeys(@onNull Set<String> keys)836 public void removeSyncedSharedPreferencesKeys(@NonNull Set<String> keys) { 837 for (String key : keys) { 838 if (key == null) { 839 throw new IllegalArgumentException("keys cannot contain null"); 840 } 841 } 842 mSyncManager.removeSharedPreferencesSyncKeys(keys); 843 } 844 845 /** 846 * Returns the set keys that are being synced from app's default {@link SharedPreferences} to 847 * the SDK sandbox. 848 */ 849 @NonNull getSyncedSharedPreferencesKeys()850 public Set<String> getSyncedSharedPreferencesKeys() { 851 return mSyncManager.getSharedPreferencesSyncKeys(); 852 } 853 854 /** @hide */ 855 private static class LoadSdkReceiverProxy extends ILoadSdkCallback.Stub { 856 private final Executor mExecutor; 857 private final OutcomeReceiver<SandboxedSdk, LoadSdkException> mCallback; 858 private final ISdkSandboxManager mService; 859 LoadSdkReceiverProxy( Executor executor, OutcomeReceiver<SandboxedSdk, LoadSdkException> callback, ISdkSandboxManager service)860 LoadSdkReceiverProxy( 861 Executor executor, 862 OutcomeReceiver<SandboxedSdk, LoadSdkException> callback, 863 ISdkSandboxManager service) { 864 mExecutor = executor; 865 mCallback = callback; 866 mService = service; 867 } 868 869 @Override onLoadSdkSuccess( SandboxedSdk sandboxedSdk, SandboxLatencyInfo sandboxLatencyInfo)870 public void onLoadSdkSuccess( 871 SandboxedSdk sandboxedSdk, SandboxLatencyInfo sandboxLatencyInfo) { 872 logSandboxApiLatency(sandboxLatencyInfo); 873 mExecutor.execute(() -> mCallback.onResult(sandboxedSdk)); 874 } 875 876 @Override onLoadSdkFailure( LoadSdkException exception, SandboxLatencyInfo sandboxLatencyInfo)877 public void onLoadSdkFailure( 878 LoadSdkException exception, SandboxLatencyInfo sandboxLatencyInfo) { 879 sandboxLatencyInfo.setResultCode(getResultCodeForLoadSdkException(exception)); 880 logSandboxApiLatency(sandboxLatencyInfo); 881 mExecutor.execute(() -> mCallback.onError(exception)); 882 } 883 logSandboxApiLatency(SandboxLatencyInfo sandboxLatencyInfo)884 private void logSandboxApiLatency(SandboxLatencyInfo sandboxLatencyInfo) { 885 sandboxLatencyInfo.setTimeAppReceivedCallFromSystemServer( 886 SystemClock.elapsedRealtime()); 887 try { 888 mService.logSandboxApiLatency(sandboxLatencyInfo); 889 } catch (RemoteException e) { 890 Log.w( 891 TAG, 892 "Remote exception while calling logSandboxApiLatency." 893 + "Error: " 894 + e.getMessage()); 895 } 896 } 897 } 898 899 /** @hide */ 900 private static class RequestSurfacePackageReceiverProxy 901 extends IRequestSurfacePackageCallback.Stub { 902 private final Executor mExecutor; 903 private final OutcomeReceiver<Bundle, RequestSurfacePackageException> mReceiver; 904 private final ISdkSandboxManager mService; 905 RequestSurfacePackageReceiverProxy( Executor executor, OutcomeReceiver<Bundle, RequestSurfacePackageException> receiver, ISdkSandboxManager service)906 RequestSurfacePackageReceiverProxy( 907 Executor executor, 908 OutcomeReceiver<Bundle, RequestSurfacePackageException> receiver, 909 ISdkSandboxManager service) { 910 mExecutor = executor; 911 mReceiver = receiver; 912 mService = service; 913 } 914 915 @Override onSurfacePackageReady( SurfacePackage surfacePackage, int surfacePackageId, Bundle params, SandboxLatencyInfo sandboxLatencyInfo)916 public void onSurfacePackageReady( 917 SurfacePackage surfacePackage, 918 int surfacePackageId, 919 Bundle params, 920 SandboxLatencyInfo sandboxLatencyInfo) { 921 logSandboxApiLatency(sandboxLatencyInfo); 922 mExecutor.execute( 923 () -> { 924 params.putParcelable(EXTRA_SURFACE_PACKAGE, surfacePackage); 925 mReceiver.onResult(params); 926 }); 927 } 928 929 @Override onSurfacePackageError( int errorCode, String errorMsg, SandboxLatencyInfo sandboxLatencyInfo)930 public void onSurfacePackageError( 931 int errorCode, String errorMsg, SandboxLatencyInfo sandboxLatencyInfo) { 932 logSandboxApiLatency(sandboxLatencyInfo); 933 mExecutor.execute( 934 () -> 935 mReceiver.onError( 936 new RequestSurfacePackageException(errorCode, errorMsg))); 937 } 938 logSandboxApiLatency(SandboxLatencyInfo sandboxLatencyInfo)939 private void logSandboxApiLatency(SandboxLatencyInfo sandboxLatencyInfo) { 940 sandboxLatencyInfo.setTimeAppReceivedCallFromSystemServer( 941 SystemClock.elapsedRealtime()); 942 try { 943 mService.logSandboxApiLatency(sandboxLatencyInfo); 944 } catch (RemoteException e) { 945 Log.w( 946 TAG, 947 "Remote exception while calling logSandboxApiLatency." 948 + "Error: " 949 + e.getMessage()); 950 } 951 } 952 } 953 954 /** 955 * Return the AdServicesManager 956 * 957 * @hide 958 */ getAdServicesManager()959 public IBinder getAdServicesManager() { 960 try { 961 return mService.getAdServicesManager(); 962 } catch (RemoteException e) { 963 throw e.rethrowFromSystemServer(); 964 } 965 } 966 } 967