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