Android 版プライバシー サンドボックスのドキュメントをご覧になる際は、[デベロッパー プレビュー] または [ベータ版] ボタンで対象のプログラム バージョンを選択してください(手順が異なる場合があります)。
SDK ランタイムを使用すると、呼び出し元アプリとは別の専用のサンドボックスで SDK を実行でき、ユーザーデータの収集に関する安全性と確実性が向上します。SDK ランタイムは、実行環境を変更してデータアクセス権と許可された権限セットを制限することでこれを実現しています。SDK ランタイムの詳細については設計案をご覧ください。
このページでは、呼び出し元アプリにリモートでレンダリングできるウェブベースのビューを定義するランタイム対応の SDK を作成する手順について説明します。
既知の制限事項
SDK ランタイムに関する開発中の機能の一覧については、リリースノートをご覧ください。
以下の制限事項は、Android プラットフォームの次回のメジャー リリースで修正される予定です。
- スクロール可能なビュー内での広告レンダリング。たとえば、
RecyclerView
は正しく機能しません。- サイズ変更時にジャンクが発生することがあります。
- ユーザーのタッチ スクロール イベントがランタイムに正しく渡されません。
- Storage API
- Android 13 では、SDK ごとのストレージは使用できません。
以下の問題は 2023 年に修正される予定です。
getAdId
API とgetAppSetId
API は、サポートがまだ有効になっていないため正しく機能しません。
始める前に
まず、次の手順を実施します。
Android 版プライバシー サンドボックス用に開発環境をセットアップします。SDK ランタイムをサポートするツールは現在開発中であるため、このガイドでは Android Studio の最新の Canary バージョンを使用する必要があります。このバージョンの Android Studio は、使用する他のバージョンと並行して実行できるため、この要件が適していない場合は、Google までお知らせください。
サポート対象のデバイスにシステム イメージをインストールするか、Android 版プライバシー サンドボックスのサポートを含むエミュレータをセットアップします。
Android Studio でプロジェクトをセットアップする
SDK ランタイムを試すには、クライアント サーバー モデルに似たモデルを使用します。主な違いは、アプリ(クライアント)と SDK(サーバー)が同じデバイス上で実行されることです。
- プロジェクトにアプリ モジュールを追加します。このモジュールは SDK を動作させるクライアントとして機能します。
- アプリ モジュールで、SDK ランタイムを有効にし、必要な権限を宣言して、API 固有の広告サービスを構成します。
- プロジェクトにライブラリ モジュールを 1 つ追加します。このモジュールには SDK コードが含まれています。
- SDK モジュールで、必要な権限を宣言します。API 固有の広告サービスをこのモジュールで構成する必要はありません。
- SDK が使用していないライブラリ モジュールの
build.gradle
ファイルからdependencies
を削除します。ほとんどの場合、すべての依存関係を削除できます。これを行うには、SDK に対応する名前の新しいディレクトリを作成します。 com.android.privacy-sandbox-sdk
タイプを使用して新しいモジュールを手動で作成します。これには、デバイスにデプロイ可能な APK を作成するための SDK コードがバンドルされています。これを行うには、SDK に対応する名前の新しいディレクトリを作成します。空のbuild.gradle
ファイルを追加します。このファイルの内容は、このガイドの後半で追加します。gradle.properties
ファイルに次のスニペットを追加します。android.experimental.privacysandboxsdk.enable=true
TiramisuPrivacySandboxTiramisuPrivacySandbox エミュレータ イメージをダウンロードし、このイメージを使用して Play ストアを含むエミュレータを作成します。
SDK デベロッパーなのかアプリ デベロッパーなのかによって、最終的なセットアップが前の段落の内容と異なる場合があります。
アプリをインストールする場合と同様に、Android Studio または Android Debug Bridge(ADB)を使用してテストデバイスに SDK をインストールします。簡単に始められるよう、Kotlin と Java プログラミング言語を使用して作成したサンプルアプリを GitHub リポジトリで公開しています。README ファイルとマニフェスト ファイルには、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 がリクエストを処理する準備ができたら、新しい
SandboxedSdk
オブジェクト内にラップされるIBinder
オブジェクトとしてインターフェースを渡して呼び出し元アプリに通知します。バインドされたサービスに関するガイドで、IBinder
を提供するさまざまな方法について説明しています。方法は柔軟に選択できますが、SDK と呼び出し元アプリで一貫している必要があります。たとえば AIDL を使用する場合、アプリと共有されて使用される
IBinder
を提示する AIDL ファイルを定義する必要があります。// ISdkInterface.aidl interface ISdkInterface { // the public functions to share with the App. int doSomething(); }
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. } } }
SdkSandboxController
SdkSandboxController
は、SDK で利用できるコンテキスト ベースのシステム サービス ラッパーです。これは SDK によって、SandboxedSdkProvider#getContext()
から受け取ったコンテキストを使用し、このコンテキストで context.getSystemService(SdkSandboxController.class)
を呼び出すことで取得できます。コントローラには、SDK がプライバシー サンドボックスを操作して情報を取得するための API があります。
SandboxedSdkContext
がアクセスに使用されるコンテキストでない場合、コントローラのほとんどの API は例外をスローします。このサービス ラッパーは、他のコンテキストで利用できないようにする予定です。SdkSandboxController
は SDK プロバイダが使用することを想定しており、アプリ デベロッパーが使用することは推奨されません。
SDK 間の通信
ランタイムの SDK は、メディエーションと関連ユースケースをサポートするために、相互に通信できるようにする必要があります。ここで説明するツールセットには、サンドボックス内の他の SDK で利用できるようにした SDK と連携するためのインターフェースが用意されています。この SDK ランタイム実装は SDK 間の通信を実現するための第一段階であり、プライバシー サンドボックスにおけるメディエーションのユースケースすべてに対応するとは限りません。
SdkSandboxController
の getSandboxedSdks()
API は、プライバシー サンドボックスに読み込まれたすべての SDK について、SandboxedSdk
クラスを提供します。SandboxedSdk
オブジェクトには、SDK に関する詳細と、クライアントが SDK と通信するための sdkInterface
があります。
プライバシー サンドボックス内の SDK は、他の SDK と通信するために次のようなコード スニペットを使用することが想定されています。このコードでは、「SDK1」と「SDK2」はどちらもプライバシー サンドボックス内のアプリによって読み込まれる SDK で、「SDK1」が「SDK2」と通信できるように記述されていると仮定してください。
SdkSandboxController controller = mSdkContext
.getSystemService(SdkSandboxController.class);
List<SandboxedSdk> sandboxedSdks = controller.getSandboxedSdks();
SandboxedSdk sdk2 = sandboxedSdks.stream().filter( // The SDK it wants to
// connect to, based on SDK name or SharedLibraryInfo.
try {
IBinder binder = sdk2.getInterface();
ISdkApi sdkApi = ISdkApi.Stub.asInterface(binder);
// Call API on SDK2
message = sdkApi.getMessage();
} catch (RemoteException e) {
throw new RuntimeException(e);
}
上の例で、SDK1 は SDK2 の AIDL ライブラリを依存関係として追加しています。このクライアント SDK には、AIDL によって生成された Binder コードが含まれています。どちらの SDK も、この AIDL ライブラリをエクスポートする必要があります。これは、アプリがプライバシー サンドボックス内の SDK と通信する際に想定される動作と同じです。
今後のアップデートで、SDK 間でインターフェースを共有するための自動生成方法のサポートが追加される予定です。
ランタイムの SDK は、まだランタイムが有効になっていないアプリの依存関係や広告 SDK と通信する必要がある場合があります。
SdkSandboxManager
の registerAppOwnedSdkSandboxInterface()
API を使用すると、ランタイムが有効になっていない SDK がプラットフォームにインターフェースを登録できるようになります。SdkSandboxController
の getAppOwnedSdkSandboxInterfaces()
API は、登録済みで静的にリンクされたすべての SDK の AppOwnedSdkSandboxInterface
を提供します。
次の例は、インターフェースを登録して、ランタイム対応の SDK 通信で使用できるようにする方法を示しています。
// Register AppOwnedSdkSandboxInterface
mSdkSandboxManager.registerAppOwnedSdkSandboxInterface(
new AppOwnedSdkSandboxInterface(
APP_OWNED_SDK_NAME, (long) APP_OWNED_SDK_VERSION, new AppOwnedSdkApi())
);
次の例は、ランタイム非対応の SDK との通信を計測する方法を示しています。
// Get AppOwnedSdkSandboxInterface
List<AppOwnedSdkSandboxInterface> appOwnedSdks = mSdkContext
.getSystemService(SdkSandboxController.class)
.getAppOwnedSdkSandboxInterfaces();
AppOwnedSdkSandboxInterface appOwnedSdk = appOwnedSdks.stream()
.filter(s -> s.getName().contains(APP_OWNED_SDK_NAME))
.findAny()
.get();
IAppOwnedSdkApi appOwnedSdkApi =
IAppOwnedSdkApi.Stub.asInterface(appOwnedSdk.getInterface());
message = appOwnedSdkApi.getMessage();
ランタイム対応でない広告 SDK は自身で登録できない可能性があるため、登録を処理し、パートナー SDK またはアプリ SDK を直接依存関係として含むメディエータ SDK を作成することをおすすめします。このメディエータ SDK は、ランタイムが有効になっていない SDK と依存関係と、アダプタとして機能するランタイム対応のメディエータ間の通信を確立します。
アクティビティのサポート
ランタイム対応 SDK はマニフェスト ファイルにアクティビティ タグを追加できず、独自のアクティビティを直接開始することもできません。SdkSandboxActivityHandler
を登録してサンドボックス アクティビティを開始することで、Activity
オブジェクトにアクセスできるようになります。
1. SdkSandboxActivityHandler を登録する
SdkSandboxController#registerSdkSandboxActivityHandler(SandboxedActivityHandler)
を使用して SdkSandboxActivityHandler
のインスタンスを登録する
API はそのオブジェクトを登録し、渡された SdkSandboxActivityHandler
を識別する IBinder
オブジェクトを返します。
public interface SdkSandboxActivityHandler {
void onActivityCreated(Activity activity);
}
API はそのオブジェクトを登録し、渡された SdkSandboxActivityHandler
を識別する IBinder
オブジェクトを返します。
2. サンドボックス アクティビティを開始する
SDK は返されたトークンを渡して登録済みの SdkSandboxActivityHandler
を識別し、クライアント アプリに渡します。クライアント アプリは SdkSandboxManager#startSdkSandboxActivity(Activity, Binder)
を呼び出し、サンドボックスを開始するアクティビティと、登録済みの SdkSandboxActivityHandler
を識別するトークンを渡します。
このステップにより、リクエスト元の SDK と同じ SDK ランタイムで実行される新しいプラットフォーム アクティビティが開始されます。
アクティビティが開始すると、Activity#OnCreate(Bundle)
の実行の一環として SdkSandboxActivityHandler#onActivityCreated(Activity)
の呼び出しを介して SDK に通知されます。
たとえば、Activity
オブジェクトにアクセスできる場合、呼び出し元は Activity#setContentView(View)
を呼び出して contentView
にビューを設定できます。
ライフサイクル コールバックを登録するには、Activity#registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks)
を使用します。
渡されたアクティビティに OnBackInvokedCallback
を登録するには、Activity#getOnBackInvokedDispatcher().registerOnBackInvokedCallback(Int,
OnBackInvokedCallback)
を使用します。
SDK ランタイムで動画プレーヤーをテストする
バナー広告のサポートに加え、プライバシー サンドボックスは、SDK ランタイム内で動作する動画プレーヤーのサポートも行っています。
動画プレーヤーをテストするフローは、バナー広告をテストする場合と似ています。返される View
オブジェクトに動画プレーヤーが含まれるように、SDK のエントリ ポイントの getView()
メソッドを変更します。プライバシー サンドボックスでサポートされると思われる動画プレーヤーのフローをすべてテストします。なお、動画のライフサイクルに関する SDK とクライアント アプリ間の通信は対象外であるため、この機能に関してフィードバックはまだ必要ありません。
テストとフィードバックを行うことで、お使いの動画プレーヤーのユースケースがすべて SDK ランタイムでサポートされるようになります。
次のコード スニペットは、URL から読み込むシンプルな動画ビューを返す方法を示しています。
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 は内部ストレージのみを使用できるため、Context.getFilesDir()
や Context.getCacheDir()
などの内部ストレージ API のみが機能します。その他の例については、内部ストレージからアクセスするをご覧ください。
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 が使用するストレージの量を把握するのに役立ちます。
Android 13 では、SDK ごとのストレージへのパスを返す API は 1 つだけです(Context#getDataDir()
)。
Android 14 では、Context
オブジェクトのすべての内部ストレージ API が、各 SDK のストレージパスを返します。場合によっては、次の adb コマンドを実行して、この機能を有効にする必要があります。
adb shell device_config put adservices sdksandbox_customized_sdk_context_enabled true
クライアントの SharedPreferences
の読み取り
クライアント アプリは、SharedPreferences
にあるキーのセットを SdkSandbox
と共有することもできます。SDK は SdkSanboxController#getClientSharedPreferences()
API を使用して、クライアント アプリから同期されたデータを読み取ることができます。この API から返される SharedPreferences
は、読み取り専用です。書き込みはしないでください。
Google Play 開発者サービスから提供された広告 ID にアクセスする
Google Play 開発者サービスから提供された広告 ID に SDK がアクセスする必要がある場合、以下のようにします。
- 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"/>
広告を含むアプリのアクティビティで、
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 } } ビューの表示が完了したら、以下を呼び出して
SurfacePackage
を解放します。surfacePackage.notifyDetachedFromWindow()
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 の操作を継続するには、これらのビューを再度読み込んで、新しいサンドボックス プロセスを開始する必要があります。
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] を [Specified Activity] に設定します。
- [Activity] の横にあるその他メニューをクリックし、クライアントの [Main Activity] を選択します。
- [Apply]、[OK] の順にクリックします。
- 実行ボタン
をクリックして、テストデバイスにクライアント アプリと SDK をインストールします。
コマンドラインでデプロイする
コマンドラインを使用してデプロイする場合の手順は次のとおりです。このセクションでは、SDK アプリ モジュールの名前が sdk-app
であり、クライアント アプリ モジュールの名前が client-app
であると仮定しています。
コマンドライン ターミナルで、プライバシー サンドボックス 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 でデバッグボタン をクリックします。
SDK アプリをデバッグするには、[Run] > [Attach to Process] に移動します。ポップアップ画面(下図)が表示されます。[Show all processes] チェックボックスをオンにします。表示されたリストの中から CLIENT_APP_PROCESS_sdk_sandbox
というプロセスを探します。このオプションを選択し、SDK アプリのコードにブレークポイントを追加して、SDK のデバッグを開始します。
![SDK アプリのプロセスはダイアログ下部のリストビューに表示される](https://developers-dot-devsite-v2-prod.appspot.com/static/privacy-sandbox/assets/images/android/debug-sdk-app.png?authuser=6&hl=ja)
コマンドラインから SDK ランタイムを起動および停止する
アプリの SDK ランタイム プロセスを開始するには、次のシェルコマンドを使用します。
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 を確認する
現在読み込まれている SDK を確認するには、SdkSandboxManager
内の getSandboxedSdks
機能を使用します。
制限事項
SDK ランタイムに関する開発中の機能の一覧については、リリースノートをご覧ください。
コードサンプル
GitHub の SDK ランタイムとプライバシー保護 API のリポジトリには、SDK ランタイムの初期化方法と呼び出し方法を示すサンプルなど、使用を開始するために役立つ個別の Android Studio プロジェクトのセットが含まれています。バグと問題を報告する
皆様からのフィードバックは、Android 版プライバシー サンドボックスに欠かせない要素です。問題が見つかった場合や Android 版プライバシー サンドボックスを改善するためのアイデアがありましたらお知らせください。
あなたへのおすすめ
- 注: JavaScript がオフになっている場合はリンクテキストが表示されます
- SDK ランタイム
- リリースノート
- Android 版 Protected Audience API デベロッパー ガイド