The SDK Runtime allows SDKs to run in a dedicated sandbox that's separate from the calling app. The SDK Runtime provides enhanced safeguards and guarantees around user data collection. This is done through a modified execution environment which limits data access rights and the set of allowed permissions. Learn more about the SDK Runtime in the design proposal.
The steps on this page guide you through the process of creating a runtime-enabled SDK that defines a web-based view that can be remotely rendered into a calling app.
Before you begin
Before getting started, complete the following steps:
- Set up your development environment for the Privacy Sandbox on Android.
- Either install a system image onto a supported device or set up an emulator that includes support for the Privacy Sandbox on Android.
Set up your project in Android Studio
To try out the SDK Runtime, use a model that's similar to the client-server model. The main difference is that apps (the client) and SDKs (the "server") run on the same device.
Add one app module and one SDK module to your project. This makes it easier to run and test your code side by side. You should create two separate apps to ensure that both the app code and SDK code are separated.
Depending on whether you're an SDK developer or an app developer, you may have a different final setup than the one described in the preceding paragraph.
Install the SDK onto a test device, similarly to how you'd install an app, using either Android Studio or the Android Debug Bridge (ADB).
To help you get started, we've created sample apps in the Kotlin and Java programming languages, which can be found in this GitHub repository.
Prepare your SDK
In your SDK app's AndroidManifest.xml
file, include the <sdk-library>
and <property>
elements in the <application>
section, as shown in the
following code snippet. Use a unique name for your runtime-enabled SDK, and
provide a version.
<application ...> <sdk-library android:name="com.example.privacysandbox.provider" android:versionMajor="1" /> <property android:name="android.sdksandbox.PROPERTY_SDK_PROVIDER_CLASS_NAME" android:value="com.example.provider.SdkProviderImpl" /> </application>
The entry point for your SDK extends
SandboxedSdkProvider
.
To get your SDK app to compile, you need to override methods to handle the SDK
lifecycle.
initSdk()
- Initializes the SDK, and notifies the calling app when initialization is complete and ready for use.
getView()
- Creates and sets up the view for your ad, initializes the view the same way as any other Android view, and returns the view for remote rendering.
onExtraDataReceived()
- Placeholder, not available yet. Handles any extra metadata sent in from the host app.
The following code snippet demonstrates how to override these methods:
Kotlin
class SdkProviderImpl : SandboxedSdkProvider() { override fun initSdk( sandboxedSdkContext: SandboxedSdkContext, params: Bundle, executor: Executor, initSdkCallback: InitSdkCallback ) { // Update the callback with optional data to show that the initialization // is complete. executor.execute { initSdkCallback.onInitSdkFinished(Bundle()) } } override fun getView(windowContext: Context, bundle: Bundle): View { val webView = WebView(windowContext) webView.loadUrl("https://developer.android.com/privacy-sandbox") return webView } // Not available yet; this is a placeholder. override fun onExtraDataReceived(bundle: Bundle) {} }
Java
public class SdkProviderImpl extends SandboxedSdkProvider { @Override public void initSdk(SandboxedSdkContext sandboxedSdkContext, Bundle params, Executor executor, InitSdkCallback initSdkCallback) { // Update the callback with optional data to show that the // initialization is complete. executor.execute(() -> initSdkCallback.onInitSdkFinished(new Bundle())); } @Override public View getView(Context windowContext, Bundle bundle) { WebView webView = new WebView(windowContext); webView.loadUrl("https://developer.android.com/privacy-sandbox"); return webView; } // Not available yet; this is a placeholder. @Override public void onExtraDataReceived(@NonNull Bundle bundle) { } }
Update client apps
To call into an SDK that is running in the SDK Runtime, make the following changes to the calling client app:
In your app's activity that includes an ad, declare a reference to the
SdkSandboxManager
, a boolean to know whether the SDK is loaded, and aSurfaceView
object for remote rendering:Kotlin
private lateinit var mSdkSandboxManager: SdkSandboxManager private lateinit var mClientView: SurfaceView private var mSdkLoaded = false companion object { private const val SDK_NAME = "com.example.privacysandbox.provider" }
Java
private static final String SDK_NAME = "com.example.privacysandbox.provider"; private SdkSandboxManager mSdkSandboxManager; private SurfaceView mClientView; private boolean mSdkLoaded = false;
Define a callback class by implementing
RemoteSdkCallback
to interact with the SDK in the runtime:Kotlin
private inner class RemoteSdkCallbackImpl() : RemoteSdkCallback { override fun onLoadSdkSuccess(params: Bundle) { mSdkLoaded = true } override fun onLoadSdkFailure(errorCode: Int, errorMessage: String) { // log/show error } // Loads the remote view specified in the SDK's getView() method. override fun onSurfacePackageReady( surfacePackage: SurfacePackage, surfacePackageId: Int, params: Bundle ) { Handler(Looper.getMainLooper()).post { mClientView.setChildSurfacePackage(surfacePackage) mClientView.visibility = View.VISIBLE } } override fun onSurfacePackageError(errorCode: Int, errorMessage: String) { // log/show error } }
Java
private class RemoteSdkCallbackImpl implements RemoteSdkCallback { private RemoteSdkCallbackImpl() {} @Override public void onLoadSdkSuccess(Bundle params) { mSdkLoaded = true; } @Override public void onLoadSdkFailure(int errorCode, String errorMessage) { // log/show error } // Loads the remote view specified in the SDK's getView() method. @Override public void onSurfacePackageReady(SurfacePackage surfacePackage, int surfacePackageId, Bundle params) { new Handler(Looper.getMainLooper()).post(() -> { mClientView.setChildSurfacePackage(surfacePackage); mClientView.setVisibility(View.VISIBLE); }); } @Override public void onSurfacePackageError(int errorCode, String errorMessage) { // log/show error } }
In
onCreate()
, initialize theSdkSandboxManager
, necessary callbacks, and then make a request to display the remote view:Kotlin
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) mSdkSandboxManager = applicationContext.getSystemService( SdkSandboxManager::class.java ) mClientView = findViewById(R.id.rendered_view) mClientView.setZOrderOnTop(true) val callback = RemoteSdkCallbackImpl() mSdkSandboxManager.loadSdk( SDK_NAME, Bundle(), { obj: Runnable -> obj.run() }, callback ) Handler(Looper.getMainLooper()).post { val bundle = Bundle() mSdkSandboxManager.requestSurfacePackage( SDK_NAME, display!!.displayId, mClientView.width, mClientView.height, bundle ) } }
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mSdkSandboxManager = getApplicationContext().getSystemService( SdkSandboxManager.class); mClientView = findViewById(R.id.rendered_view); mClientView.setZOrderOnTop(true); RemoteSdkCallbackImpl callback = new RemoteSdkCallbackImpl(); mSdkSandboxManager.loadSdk( SDK_NAME, new Bundle(), Runnable::run, callback); new Handler(Looper.getMainLooper()).post(() -> { Bundle bundle = new Bundle(); mSdkSandboxManager.requestSurfacePackage( SDK_NAME, getDisplay().getDisplayId(), mClientView.getWidth(), mClientView.getHeight(), bundle); }); }
Specify the cert digest manually. To find your cert digest, extract it from your debug keystore file using
keytool
. The default password isandroid
.keytool -list -keystore ~/.android/debug.keystore
In the
<application>
element in the app's manifest file, add the<uses-sdk-library>
element. Within this element, set theandroid:certDigest
attribute to the output from the previous step:<application ...> <uses-sdk-library android:name="com.example.basicsandboxservice" android:versionMajor="1" android:certDigest="27:76:B1:2D:...:B1:BE:E0:28:5E" /> </application>
Note: If you enter an incorrect value for
android:certDigest
, the following error occurs:Installation failed due to: 'Failed to commit install session \ SESSION_ID with command cmd package install-commit SESSION_ID. \ Error: INSTALL_FAILED_MISSING_SHARED_LIBRARY: Reconciliation \ failed...: Reconcile failed: Package PACKAGE_NAME \ requires differently signed sdk library; failing!'
Deploy your apps
Before you run the client app, install the SDK app and client app onto your test device using either Android Studio or the command line.
Deploy through Android Studio
When deploying through Android Studio, complete the following steps:
- Open the Android Studio project for your SDK app.
- Go to Run > Edit Configurations. The Run/Debug Configuration window appears.
- Under Launch Options, set Launch to Nothing, since there is no activity to start.
- Click Apply and then OK.
- Click Run
to install the SDK app on your test device.
- In the Project tool window, navigate to your client app module.
- Go to Run > Edit Configurations. The Run/Debug Configuration window appears.
- Set the Launch Options to your client app's main activity.
- Click Apply and then OK.
- Click Run
to install the client app on your test device.
Deploy on the command line
When deploying using the command line, complete the steps in the following list.
This section assumes that the name of your SDK app module is sdk-app
and that
the name of your client app module is client-app
.
Deploy the SDK app:
./gradlew sdk-app:installDebug
Deploy the client app:
./gradlew client-app:installDebug && \ # Start the app's activity. This example uses the sample app. adb shell am start -n \ com.example.privacysandbox.client/com.example.client.MainActivity
Debug your apps
To debug the client app, click the Debug
button in Android Studio.
To debug the SDK app, go to Run > Attach to Process, which
shows you a popup screen (figure 1). Check the Show all processes box. In
the list that appears, look for a process called
sdk_sandbox_CLIENT_APP_UID
. Select this option and
add breakpoints in the SDK app's code to start debugging your SDK.
Limitations
For a list of in-progress capabilities for the SDK Runtime, view the release notes.
Code samples
The SDK Runtime and Privacy Preserving APIs Repository on GitHub contains a set of individual Android Studio projects to help you get started, including samples that demonstrate how to initialize and call the SDK Runtime.
Report bugs and issues
Your feedback is a crucial part of the Privacy Sandbox on Android! Let us know of any issues you find or ideas for improving Privacy Sandbox on Android.