借助 SDK 运行时,SDK 可在与调用方应用不同的专用沙盒中运行。SDK 运行时可为用户数据收集提供增强的保护措施和安全保障。这是通过修改执行环境来实现的,修改后的执行环境可以限制数据访问权限和允许的权限集。如需详细了解 SDK 运行时,请参阅设计提案。
本页上的步骤将指导您完成支持运行时的 SDK 的创建过程,该 SDK 定义了可以远程呈现到调用方应用中的基于网络的视图。
准备工作
开始之前,请先完成以下步骤:
为 Privacy Sandbox on Android 设置开发环境。 支持 SDK 运行时的工具正在积极开发中,因此本指南将要求您使用最新的 Canary 版 Android Studio。您可以并行运行此版本的 Android Studio 和您使用的其他版本,如果您无法满足此要求,请告知我们。
在 Android Studio 中设置项目
如需试用 SDK 运行时,请使用与客户端-服务器模型类似的模型。主要区别在于应用(客户端)和 SDK(“服务器”)在同一设备上运行。
- 在您的项目中添加一个应用模块。此模块将充当驱动 SDK 的客户端。
- 在该应用模块中,启用 SDK 运行时、声明必要的权限并配置 API 专属广告服务。
- 将一个库模块添加到项目中。此模块包含 SDK 代码。
- 在 SDK 模块中,声明必要权限。在此模块中无需配置 API 专属广告服务。
- 在库模块的
build.gradle
文件中,移除 SDK 不使用的dependencies
。在大多数情况下,您可以移除所有依赖项。为此,可以创建一个名称与您的 SDK 相对应的新目录。 使用
com.android.privacy-sandbox-sdk
类型手动创建一个新模块。此模块与 SDK 代码捆绑在一起,用于创建可部署到设备上的 APK。为此,可以创建一个名称与您的 SDK 相对应的新目录。添加一个空的build.gradle
文件。在本指南后面的步骤中将填充此文件的内容。将以下代码段添加到
gradle.properties
文件中:android.experimental.privacysandboxsdk.enable=true
下载 TiramisuPrivacySandbox 模拟器映像,然后使用此映像创建一个包含 Play 商店的模拟器。
根据您是 SDK 开发者还是应用开发者,最终的设置可能与上一段中所述的不同。
使用 Android Studio 或 Android 调试桥 (ADB) 将 SDK 安装到测试设备上,方法与安装应用类似。
为便于您顺利上手,我们使用 Kotlin 和 Java 编程语言开发了一些示例应用,您可以在此 GitHub 代码库中找到这些应用。自述文件及清单文件中的注释说明了必须更改哪些内容,才能在稳定版 Android Studio 中运行相应的示例应用。
准备 SDK
手动创建一个模块级目录。它将作为用于构建 SDK APK 的实现代码的封装容器。在这个新目录中,添加一个
build.gradle
文件并在其中填充以下代码段。为支持运行时的 SDK (RE-SDK) 使用唯一的名称,并提供版本。在dependencies
部分中添加您的库模块。plugins { id 'com.android.privacy-sandbox-sdk' } android { compileSdkPreview 'TiramisuPrivacySandbox' minSdkPreview 'TiramisuPrivacySandbox' namespace = "com.example.example-sdk" bundle { packageName = "com.example.privacysandbox.provider" sdkProviderClassName = "com.example.sdk_implementation.SdkProviderImpl" setVersion(1, 0, 0) } } dependencies { include project(':<your-library-here>') }
在实现库中创建一个类,用作 SDK 的入口点。该类的名称应与
sdkProviderClassName
的值对应,并扩展SandboxedSdkProvider
。
SDK 的入口点扩展了 SandboxedSdkProvider
。SandboxedSdkProvider
包含用于 SDK 的 Context
对象,您可以通过调用 getContext()
来访问此对象。只有在调用 onLoadSdk()
后,才能访问此上下文。
为使 SDK 应用能够编译,您需要重写用于处理 SDK 生命周期的方法:
onLoadSdk()
在沙盒中加载 SDK。当 SDK 准备好处理请求时,将其接口作为
IBinder
对象封装在新的SandboxedSdk
对象内传递,以通知调用方应用。绑定服务指南介绍了提供IBinder
的不同方式。您可以灵活选择所需方式,但 SDK 和调用方应用所用的方式必须保持一致。以 AIDL 为例,您应定义一个 AIDL 文件来提供要共享给应用供其使用的
IBinder
:// ISdkInterface.aidl interface ISdkInterface { // the public functions to share with the App. int doSomthing(); }
getView()
为您的广告创建并设置视图,以初始化任何其他 Android 视图的相同方式初始化该视图,然后返回该视图以在指定宽度和高度(以像素为单位)的窗口中远程呈现。
以下代码段展示了如何重写这些方法:
Kotlin
class SdkProviderImpl : SandboxedSdkProvider() { override fun onLoadSdk(params: Bundle?): SandboxedSdk { // Returns a SandboxedSdk, passed back to the client. The IBinder used // to create the SandboxedSdk object is used by the app to call into the // SDK. return SandboxedSdk(SdkInterfaceProxy()) } override fun getView(windowContext: Context, bundle: Bundle, width: Int, height: Int): View { val webView = WebView(windowContext) val layoutParams = LinearLayout.LayoutParams(width, height) webView.setLayoutParams(layoutParams) webView.loadUrl("https://developer.android.com/privacy-sandbox") return webView } private class SdkInterfaceProxy : ISdkInterface.Stub() { fun doSomething() { // Implementation of the API. } } }
Java
public class SdkProviderImpl extends SandboxedSdkProvider { @Override public SandboxedSdk onLoadSdk(Bundle params) { // Returns a SandboxedSdk, passed back to the client. The IBinder used // to create the SandboxedSdk object is used by the app to call into the // SDK. return new SandboxedSdk(new SdkInterfaceProxy()); } @Override public View getView(Context windowContext, Bundle bundle, int width, int height) { WebView webView = new WebView(windowContext); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(width, height); webView.setLayoutParams(layoutParams); webView.loadUrl("https://developer.android.com/privacy-sandbox"); return webView; } private static class SdkInterfaceProxy extends ISdkInterface.Stub { @Override public void doSomething() { // Implementation of the API. } } }
在 SDK 运行时中测试视频播放器
除了支持横幅广告之外,Privacy Sandbox 还致力于支持在 SDK 运行时中运行的视频播放器。
视频播放器的测试流程与横幅广告的测试流程类似。请更改 SDK 入口点的 getView()
方法,以在返回的 View
对象中添加视频播放器。测试您希望 Privacy Sandbox 支持的所有视频播放器流程。请注意,SDK 与客户端应用之间关于视频生命周期的通信不在当前讨论范围以内,因此尚不需要获取相应的反馈。
通过测试和反馈,您可以确保 SDK 运行时能够支持您的首选视频播放器的所有用例。
以下代码段演示了如何返回一个通过网址加载的简单视频视图。
Kotlin
class SdkProviderImpl : SandboxedSdkProvider() { override fun getView(windowContext: Context, bundle: Bundle, width: Int, height: Int): View { val videoView = VideoView(windowContext) val layoutParams = LinearLayout.LayoutParams(width, height) videoView.setLayoutParams(layoutParams) videoView.setVideoURI(Uri.parse("https://test.website/video.mp4")) videoView.setOnPreparedListener { mp -> mp.start() } return videoView } }
Java
public class SdkProviderImpl extends SandboxedSdkProvider { @Override public View getView(Context windowContext, Bundle bundle, int width, int height) { VideoView videoView = new VideoView(windowContext); LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(width, height); videoView.setLayoutParams(layoutParams); videoView.setVideoURI(Uri.parse("https://test.website/video.mp4")); videoView.setOnPreparedListener(mp -> { mp.start(); }); return videoView; } }
在 SDK 中使用存储 API
SDK 运行时中的 SDK 无法再访问、读取或写入应用的内部存储空间,反之亦然。系统会为 SDK 运行时分配自己的内部存储区,并确保该存储区与应用分隔开来。
SDK 将能够使用 SandboxedSdkProvider#getContext()
返回的 Context
对象上的文件存储 API 访问这一单独的内部存储空间。SDK 只能使用内部存储空间,因此只能使用内部存储空间 API,例如 Context.getFilesDir()
或 Context.getCacheDir()
。如需查看更多示例,请参阅从内部存储空间访问。
不支持从 SDK 运行时访问外部存储空间。调用 API 来访问外部存储空间时会抛出异常或返回 null
。示例如下:
- 使用存储访问框架访问文件会抛出
SecurityException
。 getExternalFilsDir()
将始终返回null
。
在 Android 13 中,SDK 运行时中的所有 SDK 将共用分配给 SDK 运行时的内部存储空间。存储空间将持久保留,直到客户端应用被卸载或客户端应用数据被清理。
您必须使用 SandboxedSdkProvider.getContext()
返回的 Context
进行存储。无法保证在所有情况下或将来对任何其他 Context
对象实例(例如应用上下文)使用文件存储 API 能够有效运行。
以下代码段演示了如何在 SDK 运行时中使用存储空间:
Kotlin
private static class SdkInterfaceStorage extends ISdkInterface.Stub { override fun doSomething() { val filename = "myfile" val fileContents = "content" try { getContext().openFileOutput(filename, Context.MODE_PRIVATE).use { it.write(fileContents.toByteArray()) } catch (e: Exception) { throw RuntimeException(e) } } } }
Java
private static class SdkInterfaceStorage extends ISdkInterface.Stub { @Override public void doSomething() { final filename = "myFile"; final String fileContents = "content"; try (FileOutputStream fos = getContext().openFileOutput(filename, Context.MODE_PRIVATE)) { fos.write(fileContents.toByteArray()); } catch (Exception e) { throw new RuntimeException(e); } } }
SDK 级存储空间
在每个 SDK 运行时的单独内部存储空间中,每个 SDK 都会获得自己的存储目录,称为 SDK 级存储空间。SDK 级存储空间是对 SDK 运行时内部存储空间的逻辑隔离,有助于解释每个 SDK 使用了多少存储空间。
在 Android 13 中,只有一个 API 会返回 SDK 级存储空间的路径:Context#getDataDir()
。我们打算将来让所有 API 都支持 SDK 级存储空间。
读取客户端的 SharedPreferences
客户端应用可以从 SharedPreferences
中选择一组要与 SdkSandbox
共享的密钥。SDK 可以使用 SdkSanboxController#getClientSharedPreferences()
API 读取从客户端应用同步的数据。此 API 返回的 SharedPreferences
仅供读取。您不应向其中写入内容。
访问 Google Play 服务提供的广告 ID
如果您的 SDK 需要访问 Google Play 服务提供的广告 ID,请执行以下操作:
- 在 SDK 的清单中声明
android.permission.ACCESS_ADSERVICES_AD_ID
权限。 - 使用
AdIdManager#getAdId()
异步检索值。
访问 Google Play 服务提供的应用组 ID
如果您的 SDK 需要访问 Google Play 服务提供的应用组 ID,请执行以下操作:
- 使用
AppSetIdManager#getAppSetId()
异步检索值。
更新客户端应用
若要调用在 SDK 运行时中运行的 SDK,请对发出调用的客户端应用进行以下更改:
将
INTERNET
和ACCESS_NETWORK_STATE
权限添加到应用的清单中:<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
在包含广告的应用 activity 中声明以下内容:对
SdkSandboxManager
的引用,一个指示是否已加载 SDK 的布尔值,以及一个SurfaceView
对象(以供远程呈现)。代码如下: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;
检查此设备上 SDK 运行时进程是否可用。
检查
SdkSandboxState
常量 (getSdkSandboxState()
)。SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION
表示 SDK 运行时可用。检查调用
loadSdk()
是否成功。如果未抛出任何异常且接收器是SandboxedSdk
的实例,则调用成功。从前台调用
loadSdk()
,如果从后台调用,则会抛出SecurityException
。检查
OutcomeReceiver
中是否有SandboxedSdk
的实例,以验证是否抛出了LoadSdkException
。如有异常,则表示 SDK 运行时可能不可用。
如果
SdkSandboxState
或loadSdk
调用失败,则 SDK 运行时不可用,并且调用应回退到现有 SDK。通过实现
OutcomeReceiver
定义一个回调类,用于在加载 SDK 后与运行时环境中的 SDK 进行交互。在以下示例中,客户端使用回调以等待该 SDK 成功加载,并在加载成功后尝试从 SDK 呈现网页视图。此步骤的后面部分提供了回调的定义。Kotlin
private inner class LoadSdkOutcomeReceiverImpl private constructor() : OutcomeReceiver
{ override fun onResult(sandboxedSdk: SandboxedSdk) { mSdkLoaded = true val binder: IBinder = sandboxedSdk.getInterface() if (!binderInterface.isPresent()) { // SDK is not loaded anymore. return } val sdkInterface: ISdkInterface = ISdkInterface.Stub.asInterface(binder) sdkInterface.doSomething() Handler(Looper.getMainLooper()).post { val bundle = Bundle() bundle.putInt(SdkSandboxManager.EXTRA_WIDTH_IN_PIXELS, mClientView.getWidth()) bundle.putInt(SdkSandboxManager.EXTRA_HEIGHT_IN_PIXELS, mClientView.getHeight()) bundle.putInt(SdkSandboxManager.EXTRA_DISPLAY_ID, display!!.displayId) bundle.putInt(SdkSandboxManager.EXTRA_HOST_TOKEN, mClientView.getHostToken()) mSdkSandboxManager!!.requestSurfacePackage( SDK_NAME, bundle, { obj: Runnable -> obj.run() }, RequestSurfacePackageOutcomeReceiverImpl()) } } override fun onError(error: LoadSdkException) { // Log or show error. } } Java
import static android.app.sdksandbox.SdkSandboxManager.EXTRA_DISPLAY_ID; import static android.app.sdksandbox.SdkSandboxManager.EXTRA_HEIGHT_IN_PIXELS; import static android.app.sdksandbox.SdkSandboxManager.EXTRA_HOST_TOKEN; import static android.app.sdksandbox.SdkSandboxManager.EXTRA_WIDTH_IN_PIXELS; private class LoadSdkOutcomeReceiverImpl implements OutcomeReceiver
{ private LoadSdkOutcomeReceiverImpl() {} @Override public void onResult(@NonNull SandboxedSdk sandboxedSdk) { mSdkLoaded = true; IBinder binder = sandboxedSdk.getInterface(); if (!binderInterface.isPresent()) { // SDK is not loaded anymore. return; } ISdkInterface sdkInterface = ISdkInterface.Stub.asInterface(binder); sdkInterface.doSomething(); new Handler(Looper.getMainLooper()).post(() -> { Bundle bundle = new Bundle(); bundle.putInt(EXTRA_WIDTH_IN_PIXELS, mClientView.getWidth()); bundle.putInt(EXTRA_HEIGHT_IN_PIXELS, mClientView.getHeight()); bundle.putInt(EXTRA_DISPLAY_ID, getDisplay().getDisplayId()); bundle.putInt(EXTRA_HOST_TOKEN, mClientView.getHostToken()); mSdkSandboxManager.requestSurfacePackage( SDK_NAME, bundle, Runnable::run, new RequestSurfacePackageOutcomeReceiverImpl()); }); } @Override public void onError(@NonNull LoadSdkException error) { // Log or show error. } } 如需在调用
requestSurfacePackage()
时从运行时的 SDK 返回远程视图,请实现OutcomeReceiver<Bundle, RequestSurfacePackageException>
接口:Kotlin
private inner class RequestSurfacePackageOutcomeReceiverImpl : OutcomeReceiver
{ fun onResult(@NonNull result: Bundle) { Handler(Looper.getMainLooper()) .post { val surfacePackage: SurfacePackage = result.getParcelable( EXTRA_SURFACE_PACKAGE, SurfacePackage::class.java) mRenderedView.setChildSurfacePackage(surfacePackage) mRenderedView.setVisibility(View.VISIBLE) } } fun onError(@NonNull error: RequestSurfacePackageException?) { // Error handling } } Java
import static android.app.sdksandbox.SdkSandboxManager.EXTRA_SURFACE_PACKAGE; private class RequestSurfacePackageOutcomeReceiverImpl implements OutcomeReceiver
{ @Override public void onResult(@NonNull Bundle result) { new Handler(Looper.getMainLooper()) .post( () -> { SurfacePackage surfacePackage = result.getParcelable( EXTRA_SURFACE_PACKAGE, SurfacePackage.class); mRenderedView.setChildSurfacePackage(surfacePackage); mRenderedView.setVisibility(View.VISIBLE); }); } @Override public void onError(@NonNull RequestSurfacePackageException error) { // Error handling } } 在
onCreate()
中,初始化SdkSandboxManager
和必要的回调,然后发出加载 SDK 的请求: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 loadSdkCallback = LoadSdkCallbackImpl() mSdkSandboxManager.loadSdk( SDK_NAME, Bundle(), { obj: Runnable -> obj.run() }, loadSdkCallback ) }
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); LoadSdkCallbackImpl loadSdkCallback = new LoadSdkCallbackImpl(); mSdkSandboxManager.loadSdk( SDK_NAME, new Bundle(), Runnable::run, loadSdkCallback); }
应用可以选择与沙盒共享其默认
SharedPreferences
的某些密钥。可通过对SdkSandbox
管理器的任何实例调用SdkSandboxManager#addSyncedSharedPreferencesKeys(Set<String>keys)
方法来实现此目的。应用通知SdkSandboxManager
要同步哪些密钥后,SdkSandboxManager
会将这些密钥的值同步到沙盒和 SDK,然后使用SdkSandboxController#getClientSharedPreferences
读取它们。如需了解详情,请参阅读取客户端的 SharedPreferences。同步后的该组密钥在应用重启后不会保留,同步到沙盒的数据会在沙盒重启时被清除。因此,应用必须在每次启动时调用
addSyncedSharedPreferencesKeys
来开始同步。您可以通过调用
SdkSandboxManager#removeSyncedSharedPreferencesKeys(Set<String>keys)
来移除密钥,以便修改同步后的此组密钥。如需查看正在同步的一组密钥,请使用SdkSandboxManager#getSyncedSharedPreferencesKeys()
。我们建议您尽可能缩减此组密钥的大小,且仅在必要时使用此组密钥。如果您希望将信息传递给 SDK 以用于常规用途,请直接使用 SDK 的
SandboxedSdk
接口与 SDK 通信。使用此类 API 的一种可能情况是,应用使用的是意见征求管理平台 (CMP) SDK,而沙盒内的 SDK 则想要读取 CMP SDK 储存在默认SharedPreferences
中的数据。Kotlin
override fun onCreate(savedInstanceState: Bundle?) { … // At some point, initiate the set of keys for synchronization with sandbox mSdkSandboxManager.addSyncedSharedPreferencesKeys(Set.of("foo", "bar")); }
Java
@Override protected void onCreate(Bundle savedInstanceState) { … // At some point, initiate the set of keys for synchronization with sandbox mSdkSandboxManager.addSyncedSharedPreferencesKeys(Set.of("foo", "bar")); }
为了处理 SDK 沙盒进程意外终止的情况,请为
SdkSandboxProcessDeathCallback
接口定义一个实现:Kotlin
private inner class SdkSandboxLifecycleCallbackImpl() : SdkSandboxProcessDeathCallback { override fun onSdkSandboxDied() { // The SDK runtime process has terminated. To bring back up the // sandbox and continue using SDKs, load the SDKs again. val loadSdkCallback = LoadSdkOutcomeReceiverImpl() mSdkSandboxManager.loadSdk( SDK_NAME, Bundle(), { obj: Runnable -> obj.run() }, loadSdkCallback) } }
Java
private class SdkSandboxLifecycleCallbackImpl implements SdkSandboxProcessDeathCallback { @Override public void onSdkSandboxDied() { // The SDK runtime process has terminated. To bring back up // the sandbox and continue using SDKs, load the SDKs again. LoadSdkOutcomeReceiverImpl loadSdkCallback = new LoadSdkOutcomeReceiverImpl(); mSdkSandboxManager.loadSdk( SDK_NAME, new Bundle(), Runnable::run, loadSdkCallback); } }
如需注册此回调以接收有关 SDK 沙盒何时终止的信息,您可以随时添加以下代码行:
Kotlin
mSdkSandboxManager.addSdkSandboxProcessDeathCallback({ obj: Runnable -> obj.run() }, SdkSandboxLifecycleCallbackImpl())
Java
mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run, new SdkSandboxLifecycleCallbackImpl());
由于沙盒的状态会在其进程终止时丢失,因此已由 SDK 远程呈现的视图可能无法再正常运行。如需继续与 SDK 交互,必须重新加载这些视图,以启动新的沙盒进程。如需监控新创建的沙盒进程的状态,请使用
addSdkSandboxProcessDeathCallback()
重新注册该回调。将 SDK 模块的依赖项添加到客户端应用的
build.gradle
中:dependencies { ... implementation project(':<your-sdk-module>') ... }
测试应用
如需运行客户端应用,请使用 Android Studio 或命令行将 SDK 应用和客户端应用安装到测试设备上。
通过 Android Studio 部署
通过 Android Studio 进行部署时,请完成以下步骤:
- 打开客户端应用的 Android Studio 项目。
- 依次进入 Run > Edit Configurations。此时会显示 Run/Debug Configuration 窗口。
- 在 Launch Options 下,将 Launch 设为 Specspecified Activity。
- 点击“Activity”旁边的三点状菜单,然后为您的客户端选择 Main Activity。
- 点击 Apply,然后点击 OK。
- 点击 Run 图标
,在测试设备上安装客户端应用和 SDK。
通过命令行部署
通过命令行进行部署时,请完成以下列表中的步骤。本部分假定您的 SDK 应用模块的名称为 sdk-app
,您的客户端应用模块的名称为 client-app
。
在命令行终端中,构建 Privacy Sandbox SDK APK:
./gradlew :client-app:buildPrivacySandboxSdkApksForDebug
这会输出生成的 APK 的位置。这些 APK 均使用本地调试密钥进行了签名。在下一个命令中,您需要使用此路径。
在您的设备上安装 APK:
adb install -t /path/to/your/standalone.apk
在 Android Studio 中,依次点击 Run > Edit Configurations。此时会显示 Run/Debug Configuration 窗口。
在 Installation Options 下,将 Deploy 设置为 Default APK。
点击 Apply,然后点击 OK。
点击 Run 以在测试设备上安装 APK 软件包。
调试应用
如需调试客户端应用,请点击 Android Studio 中的 Debug 按钮 。
如需调试 SDK 应用,请依次转到 Run > Attach to Process,此时会弹出一个界面(图 1)。勾选 Show all processes 复选框。在显示的列表中,查找名为 CLIENT_APP_PROCESS_sdk_sandbox
的进程。选择此选项,并在 SDK 应用的代码中添加断点以开始调试您的 SDK。

从命令行启动和停止 SDK 运行时
如需为应用启动 SDK 运行时进程,请使用以下 shell 命令:
adb shell cmd sdk_sandbox start [--user <USER_ID> | current] <CLIENT_APP_PACKAGE>
同样,如需停止 SDK 运行时进程,请运行以下命令:
adb shell cmd sdk_sandbox stop [--user <USER_ID> | current] <CLIENT_APP_PACKAGE>
检查当前加载的 SDK
您可以使用 SdkSandboxManager
中的 getLoadedSdkLibrariesInfo
功能来检查当前已加载的 SDK。
限制
有关 SDK 运行时的正在开发中的功能列表,请查看版本说明。
代码示例
为帮助您开始使用,GitHub 上的 SDK 运行时和隐私保护 API 代码库中提供了一组独立的 Android Studio 项目,其中包括展示如何初始化和调用 SDK 运行时的示例。
报告 bug 和问题
您的反馈对 Privacy Sandbox on Android 至关重要!如果发现任何问题或有任何关于改进 Privacy Sandbox on Android 的想法,欢迎告诉我们。