SDK ランタイム デベロッパー ガイド

Android 版プライバシー サンドボックスのドキュメントをご覧になる際は、[デベロッパー プレビュー] または [ベータ版] ボタンで対象のプログラム バージョンを選択してください(手順が異なる場合があります)。


フィードバックを送信

SDK ランタイムを使用すると、呼び出し元アプリとは別の専用のサンドボックスで SDK を実行でき、ユーザーデータの収集に関する安全性と確実性が向上します。SDK ランタイムは、実行環境を変更してデータアクセス権と許可された権限セットを制限することでこれを実現しています。SDK ランタイムの詳細については設計案をご覧ください。

このページでは、呼び出し元アプリにリモートでレンダリングできるウェブベースのビューを定義するランタイム対応の SDK を作成する手順について説明します。

既知の制限事項

SDK ランタイムに関する開発中の機能の一覧については、リリースノートをご覧ください。

以下の制限事項は、Android プラットフォームの次回のメジャー リリースで修正される予定です。

  • スクロール可能なビュー内での広告レンダリング。たとえば、RecyclerView は正しく機能しません。
    • サイズ変更時にジャンクが発生することがあります。
    • ユーザーのタッチ スクロール イベントがランタイムに正しく渡されません。
  • Storage API

以下の問題は 2023 年に修正される予定です。

  • getAdId API と getAppSetId API は、サポートがまだ有効になっていないため正しく機能しません。

始める前に

まず、次の手順を実施します。

  1. Android 版プライバシー サンドボックス用に開発環境をセットアップします。SDK ランタイムをサポートするツールは現在開発中であるため、このガイドでは Android Studio の最新の Canary バージョンを使用すると想定しています。このバージョンの Android Studio は、使用する他のバージョンと並行して実行できるため、この要件が適していない場合は、Google までお知らせください

  2. サポート対象のデバイスにシステム イメージをインストールするか、Android 版プライバシー サンドボックスのサポートを含むエミュレータをセットアップします。

Android Studio でプロジェクトをセットアップする

SDK ランタイムを試すには、クライアント サーバー モデルに似たモデルを使用します。主な違いは、アプリ(クライアント)と SDK(サーバー)が同じデバイス上で実行されることです。

  1. プロジェクトにアプリ モジュールを追加します。このモジュールは SDK を動作させるクライアントとして機能します。
  2. アプリ モジュールで、SDK ランタイムを有効にし必要な権限を宣言して、API 固有の広告サービスを構成します。
  3. プロジェクトにライブラリ モジュールを 1 つ追加します。このモジュールには SDK コードが含まれています。
  4. SDK モジュールで、必要な権限を宣言します。API 固有の広告サービスをこのモジュールで構成する必要はありません。
  5. SDK が使用していないライブラリ モジュールの build.gradle ファイルから dependencies を削除します。ほとんどの場合、すべての依存関係を削除できます。これを行うには、SDK に対応する名前の新しいディレクトリを作成します。
  6. com.android.privacy-sandbox-sdk タイプを使用して新しいモジュールを手動で作成します。これには、デバイスにデプロイ可能な APK を作成するための SDK コードがバンドルされています。これを行うには、SDK に対応する名前の新しいディレクトリを作成します。空の build.gradle ファイルを追加します。このファイルの内容は、このガイドの後半で追加します。

  7. gradle.properties ファイルに次のスニペットを追加します。

    android.experimental.privacysandboxsdk.enable=true
    

  8. Tiramisu(Extension Level 4) エミュレータ イメージをダウンロードし、このイメージを使用して Play ストアを含むエミュレータを作成します。

SDK デベロッパーなのかアプリ デベロッパーなのかによって、最終的なセットアップが前の段落の内容と異なる場合があります。

アプリをインストールする場合と同様に、Android Studio または Android Debug Bridge(ADB)を使用してテストデバイスに SDK をインストールします。簡単に始められるよう、Kotlin と Java プログラミング言語を使用して作成したサンプルアプリを GitHub リポジトリで公開しています。README ファイルとマニフェスト ファイルには、Android Studio の安定版でサンプルを実行するために変更が必要な点について説明したコメントがあります。

SDK を準備する

  1. モジュール レベルのディレクトリを手動で作成します。これは、SDK APK をビルドする実装コードのラッパーとして機能します。新しいディレクトリに build.gradle ファイルを追加し、このファイルに次のスニペットを入力します。ランタイム対応 SDK(RE-SDK)に一意の名前を使用し、バージョンを指定します。dependencies セクションにライブラリ モジュールを含めます。

    plugins {
        id 'com.android.privacy-sandbox-sdk'
    }
    
    android {
        compileSdk 33
        compileSdkExtension 4
        minSdk 33
        targetSdk 33
        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>')
    }
    
  2. 実装ライブラリ内に、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.
        }
    }
}

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 が返されます。次に例を示します。

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

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 を呼び出すには、呼び出し元のクライアント アプリを次のように変更します。

  1. INTERNET 権限と ACCESS_NETWORK_STATE 権限をアプリ マニフェストに追加します。

    <uses-permission android:name="android.permission.INTERNET"/>
    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
    
  2. 広告を含むアプリのアクティビティで、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;
    
  3. デバイスで SDK ランタイム プロセスが利用可能かどうかを確認します。

    1. SdkSandboxState の定数(getSdkSandboxState())を確認します。SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION は、SDK ランタイムが使用可能であることを示します。

    2. loadSdk() の呼び出しが成功したことを確認します。例外がスローされず、レシーバが SandboxedSdk のインスタンスであれば成功です。

      • フォアグラウンドから loadSdk() を呼び出します。バックグラウンドから呼び出されると、SecurityException がスローされます。

      • OutcomeReceiverSandboxedSdk のインスタンスで LoadSdkException がスローされているかどうかを確認します。例外は、SDK ランタイムが利用できない可能性があることを示します。

    SdkSandboxState または loadSdk の呼び出しが失敗した場合、SDK ランタイムは使用できなくなり、呼び出しは既存の SDK にフォールバックされます。

  4. 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()
    
  5. 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);
    }
    
  6. 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 の操作を継続するには、これらのビューを再度読み込んで、新しいサンドボックス プロセスを開始する必要があります。

  7. SDK モジュールの依存関係をクライアント アプリの build.gradle に追加します。

    dependencies {
        ...
        implementation project(':<your-sdk-module>')
        ...
    }

アプリをテストする

クライアント アプリを実行するには、Android Studio またはコマンドラインを使用して、テストデバイスに SDK アプリとクライアント アプリをインストールします。

Android Studio を使用してデプロイする

Android Studio を使用してデプロイする手順は次のとおりです。

  1. クライアント アプリの Android Studio プロジェクトを開きます。
  2. [Run] > [Edit Configurations] に移動します。[Run/Debug Configuration] ウィンドウが表示されます。
  3. [Launch Options] で、[Launch] を [Specified Activity] に設定します。
  4. [Activity] の横にあるその他メニューをクリックし、クライアントの [Main Activity] を選択します。
  5. [Apply]、[OK] の順にクリックします。
  6. 実行ボタン をクリックして、テストデバイスにクライアント アプリと SDK をインストールします。

コマンドラインでデプロイする

コマンドラインを使用してデプロイする場合の手順は次のとおりです。このセクションでは、SDK アプリ モジュールの名前が sdk-app であり、クライアント アプリ モジュールの名前が client-app であると仮定しています。

  1. コマンドライン ターミナルで、プライバシー サンドボックス SDK APK をビルドします。

    ./gradlew :client-app:buildPrivacySandboxSdkApksForDebug
    

    これにより、生成された APK の場所が出力されます。この APK はローカルのデバッグ鍵で署名されています。このパスは次のコマンドで必要になります。

  2. デバイスに APK をインストールします。

    adb install -t /path/to/your/standalone.apk
    
  3. Android Studio で、[Run] > [Edit Configurations] をクリックします。[Run/Debug Configuration] ウィンドウが表示されます。

  4. [Installation Options] で [Deploy] を [Default APK] に設定します。

  5. [Apply]、[OK] の順にクリックします。

  6. [Run] をクリックして、テストデバイスに APK バンドルをインストールします。

アプリをデバッグする

クライアント アプリをデバッグするには、Android Studio でデバッグボタン をクリックします。

SDK アプリをデバッグするには、[Run] > [Attach to Process] に移動します。ポップアップ画面(下図)が表示されます。[Show all processes] チェックボックスをオンにします。表示されたリストの中から CLIENT_APP_PROCESS_sdk_sandbox というプロセスを探します。このオプションを選択し、SDK アプリのコードにブレークポイントを追加して、SDK のデバッグを開始します。

SDK アプリのプロセスはダイアログ下部のリストビューに表示される
デバッグする SDK アプリを選択できる [Choose process] 画面。

コマンドラインから 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 ランタイムに関する開発中の機能の一覧については、リリースノートをご覧ください。

コードサンプル

GitHub の SDK ランタイムとプライバシー保護 API のリポジトリには、SDK ランタイムの初期化方法と呼び出し方法を示すサンプルなど、使用を開始するために役立つ個別の Android Studio プロジェクトのセットが含まれています。

バグと問題を報告する

皆様からのフィードバックは、Android 版プライバシー サンドボックスに欠かせない要素です。問題が見つかった場合や Android 版プライバシー サンドボックスを改善するためのアイデアがありましたらお知らせください