The Android platform uses the concept of app sandboxing to maintain robust execution and security boundaries for app code, along process boundaries. It's a common practice for apps to include third-party code, often in the form of SDKs such as ads SDKs or analytics SDKs. This reuse enables app developers to focus on their app's differentiation while leveraging the work of subject matter experts to scale their execution beyond what they could easily do on their own.
Like most operating systems, in Android SDKs are executed within the host app's sandbox, and inherit the same privileges and permissions of their host app as well as access to the host app's memory and storage. While this architecture enables SDKs and apps to flexibly integrate, it also creates the potential for undisclosed user data collection and sharing. Moreover, app developers may not be fully aware of the extent of a third-party SDK's functionality and the data it accesses, making it challenging to account for the data collection and sharing practices of their app.
In Android 13, we plan to add a new platform capability that allows third-party SDKs to run in a dedicated runtime environment called the SDK Runtime. The SDK Runtime provides the following stronger safeguards and guarantees around user data collection and sharing:
- A modified execution environment
- Well-defined permissions and data access rights for SDKs
This proposal seeks to achieve the following goals:
- Reduce undisclosed access and sharing of a user's app data by third-party SDKs through process isolation and well-defined API and data access control. Learn more about process isolation in a separate section of this document.
- Reduce undisclosed tracking of a user's app usage by third-party SDKs by limiting unique, persistent identifiers from being accessed by SDKs.
- Securely accelerate the distribution of SDK updates to apps by reducing the burden on app developers and end users. Learn more about the proposed trusted SDK distribution model in another section of this document.
- Help app developers better account for the data access and sharing practices of their app.
- Help SDK developers prevent tampering by other SDKs through the limiting of certain unsafe language constructs, such as reflection and JNI code.
- Help ads SDKs detect and prevent invalid traffic and ad fraud through full control over the remote views displaying media.
- Minimize undue impact to app and SDK developers as much as possible.
SDKs execute in an isolated process
The proposed SDK Runtime enables compatible SDKs—referred to throughout the remainder of this document as runtime-enabled (RE) SDKs—to operate in a separate process for the app. The platform facilitates bi-directional communication between the app's process and its SDK Runtime. See the communications section of this document for detail. Non-RE SDKs would remain in the app's process as they do today. Figure 1 illustrates these changes:
New trusted distribution model for SDKs
This proposed separation of SDK from app motivates another separation concept, one for SDK and app distribution. Our proposal requires a trusted distribution and installation mechanism, to ensure the correct SDKs are installed in an app's SDK Runtime. This helps protect users and app developers from invalid SDKs being loaded, while enabling app stores to significantly reduce the burden of SDK distribution from app developers.
SDKs would no longer need to be statically linked and packaged together with their apps before being uploaded to an app store for distribution. The following process would occur instead:
- SDK developers could upload their versioned SDKs to the app stores, separate from the apps themselves.
- App developers could specify their SDK dependencies by version, build, and upload an app release that doesn't include the actual SDK dependencies.
- When a user downloads this app, the installation process could use the app's specified SDK dependencies to then download them from the app store.
This novel distribution mechanism would enable SDK developers to make non-breaking changes (that is, no changes to APIs or their semantics) to their SDKs and distribute to devices without any involvement from app developers. These non-breaking SDK changes could be deployed or rolled back, without necessarily needing to wait for app developers to rebuild their apps with the new SDKs, or waiting for end users to update their apps. Breaking changes would still need to be updated by app developers, but SDK developers could get their latest non-breaking changes and fixes out more quickly and more uniformly to more people, ideally minimizing version support.
Figure 2 illustrates the proposed changes in SDK distribution:
Changes to how SDKs and apps are built, run, and distributed
This is an initial proposal for a flexible SDK Runtime and distribution technology. The following sections propose a series of changes across the following broad categories:
- Access: Permissions, memory, storage
- Execution: Languages, runtime changes, lifecycle, media rendering
- Communications: App-to-SDK and SDK-to-SDK
- Development: How to build, debug, test in this model
- Distribution: How to distribute, update, roll back across versions of Android, apps and SDKs
This document also includes an FAQ to help address common questions.
This is an initial design proposal, and we understand this may be a meaningful change for some members of the ecosystem. This is why we are actively soliciting your feedback and ask that you do so through this issue tracker.
Managing the privacy of a system implies managing how different parties can access different resources. To meet our privacy value proposition we propose updating the model for accessing apps, SDKs, and user data to follow the principle of least privilege to prevent undisclosed access of potentially sensitive data.
As a separate process, the SDK Runtime would have its own well-defined set of permissions, rather than inherit those that the user granted the app. Based on preliminary research on the permissions used by ads-related SDKs, we're proposing that the following permissions would be accessible to SDKs in the SDK Runtime by default:
INTERNET: Access to the internet to be able to communicate with a web service.
ACCESS_NETWORK_STATE: Access information about networks.
- Permissions to access the privacy-preserving APIs, which provide core advertising capabilities without needing access to cross-app identifiers. The permission names haven't been finalized but these APIs would be gated by the app's access to these permissions.
AD_ID: Ability to request the advertising ID. This would also be gated by the app's access to this permission.
BIND_GET_INSTALL_REFERRER_SERVICE: Ability to use the Google Play Install Referrer API to attribute the source of an app's installation.
We are currently investigating whether and how to authorize additional permissions, limiting the impact on end users from both a privacy and a usability perspective. We request feedback on any use cases that may not be met by this set of permissions.
As explained in the SDK permissions section of this document, the SDK Runtime would have its own isolated memory space by virtue of having its own process. This structure by default would deny the SDK access to the app's memory space, and the application would similarly not be able to access the SDK's memory space. We propose keeping this default behavior to keep aligned with the principle of least privilege.
This proposal intends to balance the need for SDKs to access storage for their normal operation and minimize cross-app and across-process tracking using persistent storage. We are proposing the following update to how storage is accessed today:
- An app would not be able to directly access its SDKs storage, and vice versa.
- The device's external storage would not be accessible to SDKs.
- Within each SDK Runtime, there would be both storage accessible to all SDKs, and storage that's private to a given SDK.
Like the current storage model, the storage itself would not have arbitrary limits in size nor duration (in other words, it won't be ephemeral but instead will be removed at app uninstall time).
To ensure a private system between apps, SDKs, and users, the execution context itself (code formats, language constructs, accessible APIs, and system data) needs to reinforce these privacy boundaries, or at the very least not introduce opportunities to circumvent them. At the same time, we want to preserve access to the rich platform and the majority of runtime capabilities that SDKs currently have. Here we propose a set of updates to the runtime environment to strike this balance.
Android code (apps and SDKs) is predominantly interpreted by the Android Runtime (ART), whether the code was written in Kotlin or Java programming language. The richness of the ART and the language constructs it offers, coupled with the verifiability it offers when compared with alternatives—in particular native code—seems to appropriately balance functionality and privacy. We are proposing that SDK code consist exclusively of Dex bytecode, rather than support JNI access.
We are aware that there are use cases, such as the use of custom packaged SQLite, which, given the use of native code, will need to be replaced with an alternative such as the Android SDK's built-in version of SQLite.
On Android, each process (including those running as root) runs with a specific SELinux context, allowing the kernel to manage access control to system services, files, devices, and other processes. In seeking to preserve the majority of SDK use cases while minimizing circumvention of the privacy protections we are trying to move forward, we are proposing the following updates from a non-system app's SELinux context for the SDK Runtime:
- A limited set of system services would be accessible. (under active design)
- SDKs would only be able to load and execute the code in their APK.
- A limited set of system properties would be accessible. (under active design)
Although most Java-based language APIs support the privacy model we are striving to reach in the SDK Runtime, there are a few Java-based APIs that make it challenging to preserve user and SDK privacy, such as reflection and invoke APIs. Consequently, we propose to prevent access to these two sets of APIs from within the SDK Runtime, possibly along with others we are actively investigating, and seek feedback as to impact on various use cases. We will share a full proposal in a future update.
In addition, recent Android platform releases have increasingly restricted access to persistent identifiers in order to improve privacy. For the SDK Runtime we propose further limiting access to identifiers which could be used for cross-app tracking.
Lastly, RE SDKs will not be able to use the notifications APIs to send user notifications at any point in time.
App SDKs currently follow the lifecycle of their host app, meaning when the app enters or leaves the foreground, shuts down, or gets force-stopped by the operating system due to memory pressure, the app's SDKs do so as well. Our proposal to separate an app's SDKs into a different process implies the following lifecycle changes:
- The app can be terminated by the user or the operating system. The SDK Runtime would automatically terminate immediately after.
The SDK Runtime can be terminated by the operating system due to memory pressure, or an unhandled exception in an SDK for example.
The SDK Runtime runs in a slightly lower priority than its associated app; consequently, it's very likely that an app would be terminated shortly after its SDK Runtime due to memory pressure. In the case that it isn't, or in the case that there is a different reason, the proposal offers related lifecycle callback methods to app developers for them to handle this exception and re-initialize the SDK Runtime with an
init()call which would reload it.
In the case of persistent failures, the app developer will need to plan for graceful degradation without the SDK or notify the user if the SDK is crucial to the app's core functionality. For further detail on this app-to-SDK interaction, see the communications section of this document.
Non-RE SDKs can continue to use standard OS primitives available to their embedded app—including services, activities, and broadcasts—whereas RE SDKs cannot.
There are SDKs that render content such as text, images, and video in an
app-specified view. To accomplish this we propose a remote-rendering approach
where the SDK will render the media from within the SDK Runtime, but use the
to allow for the media to render in an app-specified view. This offers the SDK
the capability to render this media in a manner that is private for the user,
while helping prevent and detect invalid or fraudulent user interactions with
the rendered media.
Native ads, those that are not rendered by the SDK but instead by the app, would be supported by SDKs in the SDK Runtime. The signal gathering and creative fetching process would happen consistently with non-native ads; however, the SDK Runtime's rendering protections afforded to typical WebView rendering by SDKs will likely not be possible. This is an active area of investigation.
In-stream video ads are those that run instream with a video, shown in a player within an app. Given that the video plays within a player in the app, rather than a player or view in the SDK, the rendering model differs from other ad formats. We are actively exploring mechanisms to support both server-side ad insertion and SDK-based ad insertion.
We seek to minimize the system health impact the SDK Runtime has on end-user devices, and are designing ways to do so. Most likely however, some entry-level Android 13 devices with very limited system resources, such as Android (Go edition), will not support the SDK Runtime due to the system health impact. We will soon share the minimum requirements necessary to successfully use the SDK Runtime.
Since apps and SDKs currently run in the same process, communication between them is uninhibited and unmediated. In addition, Android allows for inter-app communication even if the communication starts and ends with SDKs. This free-flowing communication model enables various use cases while at the same time introducing the possibility for undisclosed data sharing between apps and between SDKs within and between apps. We are proposing the following updates to this communication model seeking to strike a balance between the value of such communication and the realization of our stated goals.
The interface between the app and the SDK is the most common communication path to an SDK, and an SDK's API is where much of the user-facing differentiation and innovation reside. We seek to preserve SDKs' ability to innovate and differentiate here. Consequently, our proposal empowers SDKs to expose APIs to apps, and ensure that apps can benefit from all of that innovation.
Given the process boundary structure of the SDK Runtime, we are proposing to build a marshaling layer, accessible within the app, to carry the API calls and responses or callbacks across this boundary between the app and the SDK. We are proposing that the interface to this marshaling layer would be defined by SDK developers, and generated by official open-source build tools that we would develop.
With this proposal we seek to remove the boilerplate marshaling work from app and SDK developers, while providing flexibility for SDK developers and ensuring that SDK code runs in the SDK Runtime to realize our privacy goals. Should we take this path, the API definition language and tooling would need to be designed with your input.
The general interaction model would be as follows:
- App calls the SDK through the interface, passing in callbacks.
- SDK asynchronously satisfies the requests and responds using the callbacks.
- This can be generalized to any publisher-subscriber model, meaning an app can subscribe to events in the SDK with callbacks, and when these events happen, the callbacks would be triggered.
A consequence of the new cross-process structure of this proposal is that there are two process lifecycles that would need to be managed: one for the app itself and the other for the SDK Runtime. Our proposal seeks to automate as much of this as possible, minimizing impact to app and SDK developers. Figure 3 shows an approach we're considering:
The platform would expose new APIs for apps to dynamically load SDKs into the SDK Runtime process, get notified about changes to the state of the process, and interact with SDKs loaded into the SDK Runtime.
First, an app would register a callback to get notified on events such as when a SDK Runtime process (re)starts or when an SDK sends data back to the app. An app may, for example, register the callback during app startup.
The following code snippet provides an illustrative API example:
SdkRuntimeManager.registerSdkRuntimeCallback(SdkRuntimeCallback callback, Executor executor)
Before an app could interact with an SDK, the app would first request that the platform load the SDK. To ensure the integrity of the system, apps would specify the SDKs they intend to load in their manifest file, and these SDKs would be the only ones allowed to be loaded. An app would be able to either request to load a specific SDK or all the SDKs defined in its manifest. This would allow the app to load the SDKs during app startup or when the SDK Runtime is restarted.
The following code snippet provides an illustrative API example:
SdkRuntimeManager.loadSdk(String sdkName, LoadSdkCallback callback, Executor executor) SdkRuntimeManager.loadAllSdks(LoadAllSdksCallback callback, Executor executor)
After an SDK is loaded into the SDK Runtime, our proposal allows the app to interact with the SDK through the marshaling layer generated from the SDK's interface. Under the hood, the calls are marshaled through a
sendData()API. Illustrative example:
SdkRuntimeManager.sendData(String sdkName, Bundle data)
As explained in the media rendering section of this document, in order for an app to get an SDK to render media in a view, the app could make a call to
requestSurfacePackage()and fetch the appropriate
The following code snippet provides an illustrative API example:
SdkRuntimeManager.requestSurfacePackage(String sdkName, IBinder appToken, int displayId, Bundle extraParams, RequestSurfacePackageCallback callback, Executor executor)
The app could then embed the returned SurfacePackage into the SurfaceView via the setChildSurfacePackage API in SurfaceView.
The following code snippet provides an illustrative API example:
Our proposal is that the
requestSurfacePackage() APIs be
generic and not intended to be called by the apps directly. Instead, these API
would be called by the generated API reference discussed above to reduce the burden on
This case is where two SDKs in the same app need to communicate. This can happen when a given SDK is architected to be composed of constituent SDKs, and can happen when two SDKs from different parties need to collaborate to satisfy a request from the calling app.
There are two key cases to consider:
- When both SDKs are RE. In this case both SDKs are running in the SDK Runtime with all its protections, including a lack of reflection capabilities for discovery. SDKs would not be able to communicate as they do within an app today. Consequently, we are designing APIs in the SDK Runtime for SDK registration and discovery to facilitate communication.
- When only one is RE.
- If the calling SDK is running in the app, this works no differently from the app itself calling into the second SDK within the SDK Runtime.
- If the calling SDK is running within the SDK Runtime, we propose
exposing a generic
sendData(...)call that code in the app listens for, processes, and responds with the provided callbacks. This is under active investigation.
We're considering the following use cases as we design these primitives:
- Mediation and Bidding. Many advertising SDKs offer a mediation or bidding capability whereby the SDK calls various other SDKs for an ad impression (mediation), or for signal gathering to run an auction (bidding). Typically the coordinating SDK will call other SDKs through an adapter furnished by the coordinating SDK. Given the primitives above, the coordinating SDK, RE or not, should be able to access all RE and non-RE SDKs for normal operation. Rendering in this context is an active area of investigation.
- Feature discovery. Some SDK products consist of smaller SDKs which, through a process of inter-SDK discovery, determine the ultimate feature set that is exposed to the app developer. We expect the registration and discovery primitives would enable this use case.
- Publisher-subscription models. Some SDKs will have a central publisher of events that other SDKs or the app will be able to subscribe to for notifications through callbacks. The primitives above should support this use case.
This is inter-app communication, where at least one of the two processes communicating is an RE SDK. By virtue of this being cross-app communication, this is a natural vector for undisclosed data sharing.
Consequently, the SDK Runtime will not be able to communicate to any other app process through any means, including broadcasts and intents. As a result, no SDK within the SDK Runtime will be able to communicate with any other app or any RE SDK hosted by another app.
A key principle in this proposal is minimizing the impact to the developer ecosystem to the extent possible. Consequently, this proposal will offer developers a comprehensive set of development tools to write, build, debug RE apps and SDKs. However, to ensure the integrity of this proposal, there will be some changes to how RE apps and SDKs are configured, authored, and built.
Android Studio and related tooling will be updated to be SDK Runtime-aware, helping ensure that developers have correctly configured their RE apps and SDKs, and ensuring that legacy or unsupported calls are updated to their newer alternatives where relevant. During the authoring phase, there are some steps our proposal would require developers to take.
Apps would need to specify their RE SDK and SDK certificate dependencies in their app manifest. In our proposal we treat this as the source of truth from the application developer throughout this proposal. For example:
- Name: Package name of the SDK or library.
- Major version: Major version code of the SDK.
- Certificate digest: The certificate digest of the SDK build. For a given build, we propose the SDK developer obtain and register this value through the relevant app store.
This applies to app store-distributed SDKs only, whether RE or not. Apps statically linking SDKs would use current dependency mechanisms.
Given our goal of minimal impact to developers, it is important that once a target API level supporting the SDK Runtime is specified, app developers only ever need to have a single build whether that build runs on devices that do or do not support the SDK Runtime.
In our proposed design, RE SDK developers need to explicitly declare a new element representing the SDK or library entity in the manifest. Additionally, a similar set of values as the dependency would need to be provided plus a minor version:
- Name: Package name of the SDK or library.
- Major version: Major version code of the SDK.
- Minor version: Minor version code of the SDK.
Should RE SDK developers have other RE SDKs as build-time dependencies, they will likely need to declare them in a manner identical to how an app developer would declare the same dependency. RE SDKs depending on non-RE SDKs would statically link them. This may introduce issues that would be detected at build time or during test passes if the non-RE SDKs require functionality the SDK Runtime does not support, or if it must run in the app's process.
RE SDK developers will likely want to continue support for non-RE-enabled devices, such as Android 12 or lower and as mentioned in the System Health section of the document, entry-level Android 13 devices with very limited system resources. We are working through approaches to ensure SDK developers can retain a single code-base to support RE and non-RE environments.
We expect app developers would experience little change in the build step. SDK dependencies, whether locally distributed or app store-distributed (RE or not), would need to exist on the machine for linting, compilation, and builds. We are proposing that Android Studio abstract these details from the app developer with normal usage and make this as transparent as possible.
Although we expect a DEBUG build would need to include all the code and symbols to be present in the DEBUG build for debuggability, RELEASE builds would optionally have all the app store-distributed SDKs (RE or not) removed from the final artifact.
We are earlier in our design phase here and will share more as it materializes.
We are working on a path to ensure that non-RE and RE versions of an SDK can be built into a single artifact for distribution. This would prevent app developers from needing to support separate builds for RE and non-RE versions of an SDK.
Much like for apps, any app store-distributed dependency SDKs would need to exist on the machine for linting, compilation, and builds, and we expect Android Studio should facilitate this seamlessly.
As described in our proposal, app developers would be able to test their apps on devices running Android 13 how they normally would. After they've built their app, the app would be able to be installed on an RE device or emulator. This installation process would ensure the correct SDKs get installed into the SDK Runtime for the device or emulator, whether the SDKs were pulled from a remote SDK repository or cache from the build system.
SDK developers generally use in-house test apps on devices and emulators to test their development. Our proposal doesn't change this, and in-app validation would follow the same steps as outlined for app developers above, with a single build artifact for both RE and non-RE apps. SDK developers will be able to step through their code whether it's in the SDK Runtime or not, though there may be some limitations on advanced debugging and profiling tools. This is an active area of investigation.
Our design proposal for the separation of an app from its SDKs has created the possibility for app store distribution of SDKs. This is a general possibility, not unique to any particular app store. The benefits are clear:
- Ensure the quality and consistency of SDKs.
- Streamline publication for SDK developers.
- Expedite rollout of SDK minor version updates to installed apps.
In order to support SDK distribution, an app store would likely need to provide most of the following capabilities:
- A mechanism for SDK developers to upload their app store-distributable SDKs to the store, update them, roll them back and possibly remove them.
- A mechanism to ensure the integrity of an SDK and its provenance, and an app and its provenance, and resolve their dependencies.
- A mechanism to deploy them onto devices in a consistently reliable and performant manner.
What is an advertising-related SDK?
An SDK that facilitates any part of the targeting of users with messages for commercial ends, on apps that are not owned by the advertiser. This includes, but is not limited to, analytics SDKs where user groups can be created for subsequent targeting, ad serving SDKs, anti-abuse and anti-fraud SDKs for ads, engagement SDKs, and attribution SDKs.
Can any SDK run in the SDK Runtime?
Although the initial focus is for ad-related SDKs, developers of non-ad-related SDKs that seek a pro-privacy posture and believe they can operate under the conditions outlined above can share feedback about their SDKs running in the SDK Runtime. The SDK Runtime isn't designed to be compatible with all SDK designs, however. Beyond the documented limitations, the SDK Runtime is likely unsuitable for SDKs that need real-time or high throughput communications with the hosting app.
Why did we choose process isolation instead of isolation within a process's Java-based runtime?
Currently, the Java-based runtime doesn't readily facilitate the security boundaries necessary for the privacy guarantees we are seeking to offer Android users. Attempting to implement something like this would likely require a multi-year effort, without a guarantee of success. So we chose to use process boundaries, a time-tested and well-understood technology.