During early 2022, Google introduced a new model called “Android Privacy Sandbox”.The purpose of this model is to improve user privacy and enable an effective advertising experience for mobile applications.To achieve this goal, the Android Privacy Sandbox introduced new solutions: SDK Runtime and a set of privacy-preserving APIs.In this article I’ll focus on the SDK Runtime solution.
In the current model, SDKs operate in the host application sandbox. This means, SDKs have access to the host application’s storage and memory, and inherits the same permissions and privileges as the host application. This allows a convenient integration between SDKs and apps, but it also raises concerns about potential user data collection and sharing.Additionally, app developers may not be familiar with the SDK’s functionality and the data it can access, making it difficult to control the data collection and sharing practices within their own applications.To address these issues, a new platform capability will be introduced in Android 13. It will enable 3rd party SDKs to operate in a separate and dedicated runtime environment, known as the SDK Runtime.This environment has specific permissions and data access rights designed for SDKs, which provides better protection and assurance regarding user data collection and sharing.
SDKs in the SDK Runtime environment run in a separate process of the app.The app communicate with the SDK using interfaces. The interfaces cross a process boundary into the SDK Runtime process to call the SDK itself.The typical pattern of interaction between an app and an SDK can be summarized as follows:
1. The app initiates a call to the SDK via an interface, and provides it with the necessary callbacks
2. The SDK then processes the requests asynchronously and responds by utilizing the provided callbacks
This general process can be applied to any publisher-subscriber model, which enables an app to register for specific events in the SDK using callbacks. When these events occur, the relevant callbacks are triggered.
To test the SDK Runtime, you’ll need to perform the following steps, as described in the android developers guide:
1. Use the latest Canary version of Android Studio
2. Create an emulator with Play Store included and running API level TiramisuPrivacySandbox / or install the TiramisuPrivacySandbox image on a device
3. Configure Privacy Sandbox on the device/emulator:
End users can turn on/off the privacy sandbox. In the image preview, the privacy sandbox is disabled, by default. To enable it, run the following adb command and turn on the privacy sandbox toggle:
4. Run the following adb command:
Google has provided us with a sample of privacy sandbox, you can find it on GitHub.I’ve used the java sample and modified the project to test the DT Exchange SDK in the Runtime environment.
Before any interaction with the SDK, the client app must load the SDK and register for a callback to be able to track the loading result:
Where LoadSdkCallbackImpl is a class that implements OutcomReceiver<SandboxedSdk, LoadSdkException>.
This class has 2 methods:
Which is called when the SDK is loaded successfully.
In onResult, the sandboxedSdk is received and we need to store it for communicating with the SDK.
And
Which is called when there is an error loading the SDK.
In the current non-SDK Runtime model, SDKs developers expose a public API for their clients and clients can directly reach the SDK using the API.
In the new model, we (SDK developers) must create a cross-process API, such as aidl files.
Please note that in the official sample, there is a single aidl file ‘ISdkApi.aidl’ which is located in ‘example-aidl-library/src/main/java/com.example.exampleaidllibrary’.
To add more aidl files, e.g. for callbacks, rename the ‘java’ folder to ‘aidl’, and remove the following code from build.gradle
Once the SDK is loaded successfully, we can start communicating with it.
The first step is to initialize the SDK. To receive a callback with the initialization status, I created another aidl file to be used as a callback.
ISdkApi.aidl:
OnDTExchangeInitializedListener.aidl:
In the client app, I created a class that extends OnDTExchangeInitializedListener.Stub and implemented the single method void onDTExchangeInitialized (String status);
Here is the code of this class:
To init the SDK, we must call the aidl API using the sandboxsdk binder:
When the SDK initialization process is finished, we receive a callback to the onDTExchangeInitialized method in class InitSDKCallback.
The next challenge was to render a banner from the DT SDK, to the client app, using the SDK Runtime environment.
In the SDK Runtime, an SDK cannot render a view directly in the client app. The SDK must prepare the view, and request to show it using a SurfaceView.
Due to the the cross-process communication, loadBanner method becomes asynchronous. Therefore, I created a new aidl file to serve as a callback when the ad is ready to be shown.
OnAdReady.aidl
I created a new class OnAdReadyCallback that extends OnAdReady.Stub, and pass an instance of it when calling loadBanner:
loadBanner is implemented in the SDK.In our current, non-SDK runtime mode, when the ad is ready, the client app gets the ad’s controller, and the ad view layout, and uses them both to show the ad.
In the SDK Runtime, the SDK must notify the client that the ad is ready, and then the client app requests a surface from the sandbox manager and shows the ad.
Here are some code examples:
In the client app:
OnAdReadyCallback.java
MainActivity.java
In the SDK implementation:
When calling requestSurfacePackage with the SDK_NAME identifier, it arrives to the SDK in a class that extends SandboxedSdkProvider.
In this class, the method getView is invoked:
When the view is ready, a callback in the client app is called (RequestSurfacePackageCallbackImpl) and notifies the client that SurfacePackage is ready to remote render a view from the SDK:
mClientView is a SurfaceView in the client app layout.
Here is the result of a banner ad showing inside a webview: