Руководство разработчика среды выполнения SDK,Руководство разработчика среды выполнения SDK

для разработчиков

Читая документацию Privacy Sandbox для Android, используйте кнопку Developer Preview или Beta , чтобы выбрать версию программы, с которой вы работаете, поскольку инструкции могут отличаться.


Обеспечить обратную связь

Среда выполнения SDK позволяет запускать SDK в выделенной изолированной программной среде, отдельной от вызывающего приложения. SDK Runtime обеспечивает улучшенную защиту и гарантии сбора пользовательских данных. Это делается с помощью модифицированной среды выполнения, которая ограничивает права доступа к данным и набор разрешенных разрешений. Узнайте больше о среде выполнения SDK в проектном предложении .

Шаги на этой странице проведут вас через процесс создания SDK с поддержкой среды выполнения, который определяет веб-представление, которое можно удаленно отображать в вызывающем приложении.

Известные ограничения

Список текущих возможностей для среды выполнения SDK см. в примечаниях к выпуску.

Ожидается, что следующие ограничения будут исправлены в следующем основном выпуске платформы Android.

  • Рендеринг рекламы в режиме прокрутки. Например, RecyclerView работает неправильно.
    • Вы можете столкнуться с зависанием при изменении размера.
    • События сенсорной прокрутки пользователя не передаются в среду выполнения должным образом.
  • API хранилища

В 2023 году будет исправлена ​​следующая проблема:

  • API getAdId и getAppSetId пока не работают должным образом, поскольку их поддержка еще не активирована.

Прежде чем вы начнете

Прежде чем приступить к работе, выполните следующие шаги:

  1. Настройте среду разработки для Privacy Sandbox на Android. Инструменты для поддержки среды выполнения SDK находятся в активной разработке, поэтому для этого руководства вам потребуется использовать последнюю версию Canary Android Studio . Вы можете запускать эту версию Android Studio параллельно с другими используемыми вами версиями, поэтому сообщите нам, если это требование вас не устраивает.

  2. Либо установите образ системы на поддерживаемое устройство , либо настройте эмулятор , включающий поддержку Privacy Sandbox на Android.

Настройте свой проект в Android Studio

Чтобы опробовать SDK Runtime, используйте модель, аналогичную модели клиент-сервер . Основное отличие состоит в том, что приложения (клиент) и SDK («сервер») запускаются на одном устройстве.

  1. Добавьте модуль приложения в свой проект. Этот модуль служит клиентом, управляющим SDK.
  2. В модуле приложения включите SDK Runtime , объявите необходимые разрешения и настройте рекламные службы для конкретного API .
  3. Добавьте в свой проект один библиотечный модуль. Этот модуль содержит ваш код SDK.
  4. В вашем модуле SDK объявите необходимые разрешения. Вам не нужно настраивать рекламные службы для конкретного API в этом модуле.
  5. Удалите dependencies в файле build.gradle вашего библиотечного модуля, которые не использует ваш SDK. В большинстве случаев вы можете удалить все зависимости. Вы можете сделать это, создав новый каталог, имя которого соответствует вашему SDK.
  6. Вручную создайте новый модуль, используя тип com.android.privacy-sandbox-sdk . Он входит в состав кода SDK для создания APK, который можно развернуть на вашем устройстве. Вы можете сделать это, создав новый каталог, имя которого соответствует вашему SDK. Добавьте пустой файл build.gradle . Содержимое этого файла будет заполнено позже в этом руководстве.

  7. Добавьте следующий фрагмент в файл gradle.properties :

    android.experimental.privacysandboxsdk.enable=true
    
  8. Загрузите образ эмулятора Тирамису (уровень расширения 4) и создайте эмулятор с этим образом, включающий Play Store.

В зависимости от того, являетесь ли вы разработчиком SDK или разработчиком приложения, окончательная настройка может отличаться от той, которая описана в предыдущем абзаце.

Установите SDK на тестовое устройство аналогично тому, как вы устанавливаете приложение, используя Android Studio или Android Debug Bridge (ADB) .Чтобы помочь вам начать работу, мы создали примеры приложений на языках программирования Kotlin и Java, которые можно найти в этом репозитории GitHub . Файлы README и манифеста содержат комментарии, описывающие, что необходимо изменить для запуска примера в стабильных версиях Android Studio.

Подготовьте свой SDK

  1. Вручную создайте каталог уровня модуля. Это служит оболочкой вашего кода реализации для сборки APK SDK. В новом каталоге добавьте файл 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 содержит объект Context для вашего SDK, к которому вы можете получить доступ, вызвав 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 doSomething();
}
getView()

Создает и настраивает представление для вашего объявления, инициализирует его так же, как и любое другое представление Android, и возвращает представление для удаленного отображения в окне заданной ширины и высоты в пикселях.

Следующий фрагмент кода демонстрирует, как переопределить эти методы:

Котлин

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.
        }
    }
}

Джава

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.

Процесс тестирования видеоплееров аналогичен тестированию баннерной рекламы. Измените метод getView() точки входа вашего SDK, чтобы включить видеоплеер в возвращаемый объект View . Проверьте все потоки видеопроигрывателя, которые, как вы ожидаете, будут поддерживаться Privacy Sandbox. Обратите внимание, что обмен данными между SDK и клиентским приложением о жизненном цикле видео выходит за рамки, поэтому для этой функции обратная связь пока не требуется.

Ваше тестирование и отзывы гарантируют, что среда выполнения SDK поддерживает все варианты использования предпочитаемого вами видеоплеера.

В следующем фрагменте кода показано, как вернуть простой просмотр видео, загружаемый по URL-адресу.

Котлин

    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
        }
    }

Джава

    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;
        }
    }

Использование API хранилища в вашем SDK

SDK в среде выполнения SDK больше не могут получать доступ, читать или записывать во внутреннюю память приложения, и наоборот. Среде выполнения SDK будет выделена собственная внутренняя область хранения, которая гарантированно будет отделена от приложения.

SDK смогут получить доступ к этому отдельному внутреннему хранилищу с помощью API-интерфейсов хранилища файлов для объекта Context , возвращаемого SandboxedSdkProvider#getContext() . SDK могут использовать только внутреннее хранилище, поэтому будут работать только API внутреннего хранилища, такие как Context.getFilesDir() или Context.getCacheDir() . Дополнительные примеры см. в разделе Доступ из внутренней памяти .

Доступ к внешнему хранилищу из SDK Runtime не поддерживается. Вызов API для доступа к внешнему хранилищу либо выдаст исключение, либо вернет значение null . Несколько примеров:

  • Доступ к файлам с использованием Storage Access Framework приведет к возникновению SecurityException .
  • getExternalFilsDir() всегда будет возвращать null .

В Android 13 все SDK в среде выполнения SDK будут совместно использовать внутреннюю память, выделенную для среды выполнения SDK. Хранилище будет сохраняться до тех пор, пока клиентское приложение не будет удалено или пока не будут очищены данные клиентского приложения.

Вы должны использовать для хранения Context , возвращаемый SandboxedSdkProvider.getContext() . Использование API хранилища файлов в любом другом экземпляре объекта Context , например в контексте приложения, не гарантируется, что будет работать должным образом во всех ситуациях или в будущем.

Следующий фрагмент кода демонстрирует, как использовать хранилище в среде выполнения SDK:

Котлин

    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)
            }
        }
    }
}

    

Джава

    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 только один API возвращает путь к хранилищу каждого SDK: Context#getDataDir() .

В Android 14 все API внутреннего хранилища объекта Context возвращают путь к хранилищу для каждого SDK. Возможно, вам придется включить эту функцию, выполнив следующую команду adb:

adb shell device_config put adservices sdksandbox_customized_sdk_context_enabled true

Получите доступ к рекламному идентификатору, предоставленному сервисами Google Play.

Если вашему SDK требуется доступ к рекламному идентификатору, предоставленному сервисами Google Play:

  • Объявите разрешение android.permission.ACCESS_ADSERVICES_AD_ID в манифесте SDK.
  • Используйте AdIdManager#getAdId() для асинхронного получения значения.

Получите доступ к идентификатору набора приложений, предоставленному сервисами Google Play.

Если вашему SDK требуется доступ к идентификатору набора приложений, предоставленному сервисами Google Play:

  • Используйте 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 для удаленного рендеринга:

    Котлин

        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"
        }
    

    Джава

        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 .

      • Проверьте OutcomeReceiver на наличие экземпляра SandboxedSdk , чтобы убедиться, что было создано исключение LoadSdkException . Исключение указывает на то, что среда выполнения SDK может быть недоступна.

    Если вызов SdkSandboxState или loadSdk завершается неудачей, среда выполнения SDK недоступна, и вызов должен вернуться к существующему SDK.

  4. Определите класс обратного вызова, реализовав OutcomeReceiver для взаимодействия с SDK во время выполнения после его загрузки. В следующем примере клиент использует обратный вызов для ожидания успешной загрузки SDK, а затем пытается визуализировать веб-представление из SDK. Обратные вызовы определяются позже на этом этапе.

    Котлин

        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.
          }
        }
    

    Джава

        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.
            }
        }
    

    Чтобы получить удаленное представление из SDK во время выполнения при вызове requestSurfacePackage() , реализуйте интерфейс OutcomeReceiver<Bundle, RequestSurfacePackageException> :

    Котлин

        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
            }
        }
    

    Джава

        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:

    Котлин

    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
        )
    }
    

    Джава

    @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 :

    Котлин

        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)
            }
        }
    

    Джава

          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, добавьте в любой момент следующую строку:

    Котлин

        mSdkSandboxManager.addSdkSandboxProcessDeathCallback({ obj: Runnable -> obj.run() },
                SdkSandboxLifecycleCallbackImpl())
    

    Джава

        mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run,
                new SdkSandboxLifecycleCallbackImpl());
    

    Поскольку состояние песочницы теряется при завершении ее процесса, представления, удаленно обработанные SDK, могут перестать работать корректно. Чтобы продолжить взаимодействие с SDK, эти представления необходимо загрузить еще раз, чтобы запустить новый процесс песочницы.

  7. Добавьте зависимость от вашего модуля SDK в build.gradle вашего клиентского приложения:

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

Тестируйте свои приложения

Чтобы запустить клиентское приложение, установите приложение SDK и клиентское приложение на тестовое устройство с помощью Android Studio или командной строки.

Развертывание через Android Studio

При развертывании через Android Studio выполните следующие шаги:

  1. Откройте проект Android Studio для своего клиентского приложения.
  2. Перейдите в «Выполнить» > «Изменить конфигурации» . Появится окно конфигурации запуска/отладки .
  3. В разделе «Параметры запуска» установите для параметра «Запуск» значение «Указанное действие» .
  4. Нажмите трехточечное меню рядом с пунктом «Действие» и выберите « Основное действие» для вашего клиента.
  5. Нажмите «Применить» , а затем «ОК» .
  6. Нажмите «Выполнить». чтобы установить клиентское приложение и SDK на тестовое устройство.

Развертывание в командной строке

При развертывании с помощью командной строки выполните действия, указанные в следующем списке. В этом разделе предполагается, что имя вашего модуля приложения SDK — sdk-app , а имя вашего модуля клиентского приложения — client-app .

  1. В терминале командной строки создайте APK-файлы Privacy Sandbox SDK:

    ./gradlew :client-app:buildPrivacySandboxSdkApksForDebug
    

    Это выводит местоположение сгенерированных APK. Эти APK-файлы подписаны вашим локальным ключом отладки. Этот путь понадобится вам в следующей команде.

  2. Установите APK на свое устройство:

    adb install -t /path/to/your/standalone.apk
    
  3. В Android Studio нажмите «Выполнить» > «Изменить конфигурации» . Появится окно конфигурации запуска/отладки .

  4. В разделе «Параметры установки» установите «Развернуть в APK по умолчанию» .

  5. Нажмите «Применить» , а затем «ОК» .

  6. Нажмите «Выполнить» , чтобы установить пакет APK на тестовое устройство.

Отладка ваших приложений

Чтобы отладить клиентское приложение, нажмите кнопку «Отладка». кнопка в Android Studio.

Чтобы отладить приложение SDK, выберите «Выполнить» > «Присоединиться к процессу» , после чего появится всплывающий экран (показанный ниже). Установите флажок «Показать все процессы» . В появившемся списке найдите процесс с именем CLIENT_APP_PROCESS _sdk_sandbox . Выберите этот параметр и добавьте точки останова в код приложения SDK, чтобы начать отладку вашего SDK.

Процесс приложения SDK отображается в виде списка в нижней части диалогового окна.
Экран «Выбор процесса» , на котором можно выбрать приложение SDK для отладки.

Запускайте и останавливайте среду выполнения 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 см. в примечаниях к выпуску .

Примеры кода

Репозиторий API-интерфейсов среды выполнения SDK и обеспечения конфиденциальности на GitHub содержит набор отдельных проектов Android Studio, которые помогут вам начать работу, включая примеры, демонстрирующие, как инициализировать и вызывать среду выполнения SDK.

Сообщайте об ошибках и проблемах

Ваш отзыв — важная часть Privacy Sandbox на Android! Сообщайте нам о любых обнаруженных вами проблемах или идеях по улучшению Privacy Sandbox на Android.

{% дословно %} {% дословно %} {% дословно %} {% endverbatim %} ,
для разработчиков

Читая документацию Privacy Sandbox для Android, используйте кнопку Developer Preview или Beta , чтобы выбрать версию программы, с которой вы работаете, поскольку инструкции могут отличаться.


Обеспечить обратную связь

Среда выполнения SDK позволяет запускать SDK в выделенной изолированной программной среде, отдельной от вызывающего приложения. SDK Runtime обеспечивает улучшенную защиту и гарантии сбора пользовательских данных. Это делается с помощью модифицированной среды выполнения, которая ограничивает права доступа к данным и набор разрешенных разрешений. Узнайте больше о среде выполнения SDK в проектном предложении .

Шаги на этой странице проведут вас через процесс создания SDK с поддержкой среды выполнения, который определяет веб-представление, которое можно удаленно отображать в вызывающем приложении.

Известные ограничения

Список текущих возможностей для среды выполнения SDK см. в примечаниях к выпуску.

Ожидается, что следующие ограничения будут исправлены в следующем основном выпуске платформы Android.

  • Рендеринг рекламы в режиме прокрутки. Например, RecyclerView работает неправильно.
    • Вы можете столкнуться с зависанием при изменении размера.
    • События сенсорной прокрутки пользователя не передаются в среду выполнения должным образом.
  • API хранилища

В 2023 году будет исправлена ​​следующая проблема:

  • API getAdId и getAppSetId пока не работают должным образом, поскольку их поддержка еще не активирована.

Прежде чем вы начнете

Прежде чем приступить к работе, выполните следующие шаги:

  1. Настройте среду разработки для Privacy Sandbox на Android. Инструменты для поддержки среды выполнения SDK находятся в активной разработке, поэтому для этого руководства вам потребуется использовать последнюю версию Canary Android Studio . Вы можете запускать эту версию Android Studio параллельно с другими используемыми вами версиями, поэтому сообщите нам, если это требование вас не устраивает.

  2. Либо установите образ системы на поддерживаемое устройство , либо настройте эмулятор , включающий поддержку Privacy Sandbox на Android.

Настройте свой проект в Android Studio

Чтобы опробовать SDK Runtime, используйте модель, аналогичную модели клиент-сервер . Основное отличие состоит в том, что приложения (клиент) и SDK («сервер») запускаются на одном устройстве.

  1. Добавьте модуль приложения в свой проект. Этот модуль служит клиентом, управляющим SDK.
  2. В модуле приложения включите SDK Runtime , объявите необходимые разрешения и настройте рекламные службы для конкретного API .
  3. Добавьте в свой проект один библиотечный модуль. Этот модуль содержит ваш код SDK.
  4. В вашем модуле SDK объявите необходимые разрешения. Вам не нужно настраивать рекламные службы для конкретного API в этом модуле.
  5. Удалите dependencies в файле build.gradle вашего библиотечного модуля, которые не использует ваш SDK. В большинстве случаев вы можете удалить все зависимости. Вы можете сделать это, создав новый каталог, имя которого соответствует вашему SDK.
  6. Вручную создайте новый модуль, используя тип com.android.privacy-sandbox-sdk . Он входит в состав кода SDK для создания APK, который можно развернуть на вашем устройстве. Вы можете сделать это, создав новый каталог, имя которого соответствует вашему SDK. Добавьте пустой файл build.gradle . Содержимое этого файла будет заполнено позже в этом руководстве.

  7. Добавьте следующий фрагмент в файл gradle.properties :

    android.experimental.privacysandboxsdk.enable=true
    
  8. Загрузите образ эмулятора Тирамису (уровень расширения 4) и создайте эмулятор с этим образом, включающий Play Store.

В зависимости от того, являетесь ли вы разработчиком SDK или разработчиком приложения, окончательная настройка может отличаться от той, которая описана в предыдущем абзаце.

Установите SDK на тестовое устройство аналогично тому, как вы устанавливаете приложение, используя Android Studio или Android Debug Bridge (ADB) .Чтобы помочь вам начать работу, мы создали примеры приложений на языках программирования Kotlin и Java, которые можно найти в этом репозитории GitHub . Файлы README и манифеста содержат комментарии, описывающие, что необходимо изменить для запуска примера в стабильных версиях Android Studio.

Подготовьте свой SDK

  1. Вручную создайте каталог уровня модуля. Это служит оболочкой вашего кода реализации для сборки APK SDK. В новом каталоге добавьте файл 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 содержит объект Context для вашего SDK, к которому вы можете получить доступ, вызвав 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 doSomething();
}
getView()

Создает и настраивает представление для вашего объявления, инициализирует его так же, как и любое другое представление Android, и возвращает представление для удаленного отображения в окне заданной ширины и высоты в пикселях.

Следующий фрагмент кода демонстрирует, как переопределить эти методы:

Котлин

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.
        }
    }
}

Джава

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.

Поток для тестирования видеопрогнеристов похож на рекламу баннеров тестирования. Измените метод getView() точки входа вашей SDK, чтобы включить видеоплеер в возвращенный объект View . Проверьте все потоки видеоплееры, которые, как вы ожидаете, будут поддерживать песочницу конфиденциальности. Обратите внимание, что связь между SDK и клиентским приложением о жизненном цикле видео не имеет возможности, поэтому обратная связь еще не требуется для этой функциональности.

Ваше тестирование и обратная связь гарантируют, что среда выполнения SDK поддерживает все варианты использования вашего предпочтительного видеоплеера.

Следующий фрагмент кода демонстрирует, как вернуть простой просмотр видео, который загружается из URL.

Котлин

    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
        }
    }

Джава

    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;
        }
    }

Использование API -интерфейсов хранилища в вашем SDK

SDK в среде выполнения SDK больше не могут доступа, читать или записывать во внутреннем хранилище приложения и наоборот. Среда выполнения SDK будет выделена собственная внутренняя область хранения, которая гарантированно будет отделена от приложения.

SDK смогут получить доступ к этому отдельному внутреннему хранилищу, используя API хранилища файлов в Context объекте, возвращаемом SandboxedSdkProvider#getContext() . SDK могут использовать только внутреннее хранилище, поэтому будут работать только внутренние API -интерфейсы, такие как Context.getFilesDir() или Context.getCacheDir() . Смотрите больше примеров в доступе из внутренней хранилища .

Доступ к внешнему хранилищу из среды выполнения SDK не поддерживается. Вызов API для доступа к внешнему хранилищу либо бросит исключение, либо вернет null . Несколько примеров:

В Android 13 все SDK в среде выполнения SDK поделится внутренним хранилищем, выделенным для времени выполнения SDK. Хранение будет сохраняться до тех пор, пока клиентское приложение не удастся, или после очистки данных о клиентских приложениях.

Вы должны использовать Context , возвращаемый SandboxedSdkProvider.getContext() для хранения. Использование API хранилища файлов в любом другом экземпляре объекта Context , таком как контекст приложения, не гарантированно будет работать, как и ожидалось во всех ситуациях или в будущем.

Следующий фрагмент кода демонстрирует, как использовать хранилище во время выполнения SDK:

Котлин

    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)
            }
        }
    }
}

    

Джава

    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 Runtime, которая помогает учитывать, сколько хранилища использует каждый SDK.

В Android 13 только один API возвращает путь к хранилищу для каждого SDK: Context#getDataDir() .

На Android 14 все внутренние API -интерфейсы на объекте Context возвращают путь хранения для каждого SDK. Вам может потребоваться включить эту функцию, выполнив следующую команду ADB:

adb shell device_config put adservices sdksandbox_customized_sdk_context_enabled true

Доступ к рекламному идентификатору, предоставленному Google Play Services

Если ваш SDK нуждается в доступе к рекламному идентификатору, предоставленному Google Play Services:

  • Объявите разрешение android.permission.ACCESS_ADSERVICES_AD_ID в манифесте SDK.
  • Используйте AdIdManager#getAdId() , чтобы получить значение асинхронно.

Доступ к идентификатору набора приложений, предоставленным Google Play Services

Если ваш SDK нуждается в доступе к идентификатору набора приложений, предоставленным Google Play Services:

  • Используйте AppSetIdManager#getAppSetId() , чтобы получить значение асинхронно.

Обновить клиентские приложения

Чтобы вызвать SDK, который работает во время выполнения SDK, внесите следующие изменения в приложение Calling Client:

  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 для удаленного рендеринга:

    Котлин

        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"
        }
    

    Джава

        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 .

      • Проверьте OutcomeReceiver для экземпляра SandboxedSdk , чтобы проверить, была ли брошена LoadSdkException . Исключение указывает на то, что среда выполнения SDK может быть недоступна.

    Если SdkSandboxState или вызов loadSdk не выполняется, время выполнения SDK недоступно, а вызов должен отступить на существующий SDK.

  4. Определите класс обратного вызовов, реализуя OutcomeReceiver взаимодействовать с SDK во время выполнения после его загрузки. В следующем примере клиент использует обратный вызов, чтобы подождать, пока SDK не будет успешно загружен, а затем пытается отобразить веб -представление из SDK. Обратные вызовы определяются позже на этом шаге.

    Котлин

        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.
          }
        }
    

    Джава

        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.
            }
        }
    

    Чтобы вернуть удаленный представление от SDK во время выполнения во время вызова requestSurfacePackage() , реализуйте OutcomeReceiver<Bundle, RequestSurfacePackageException> Интерфейс:

    Котлин

        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
            }
        }
    

    Джава

        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. In onCreate() , инициализируйте SdkSandboxManager , необходимые обратные вызовы, а затем сделайте запрос на загрузку SDK:

    Котлин

    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
        )
    }
    

    Джава

    @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 :

    Котлин

        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)
            }
        }
    

    Джава

          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 прекратилась, добавьте следующую строку в любое время:

    Котлин

        mSdkSandboxManager.addSdkSandboxProcessDeathCallback({ obj: Runnable -> obj.run() },
                SdkSandboxLifecycleCallbackImpl())
    

    Джава

        mSdkSandboxManager.addSdkSandboxProcessDeathCallback(Runnable::run,
                new SdkSandboxLifecycleCallbackImpl());
    

    Поскольку состояние песочницы теряется при завершении процесса, взгляды, которые были отдаленно отображаются SDK, больше не работают правильно. Чтобы продолжить взаимодействие с SDK, эти представления должны быть загружены снова, чтобы начать новый процесс песочницы.

  7. Добавьте зависимость от вашего SDK -модуля в build.gradle вашего клиента.

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

Тестируйте свои приложения

Чтобы запустить ваше клиентское приложение, установите приложение SDK и клиентское приложение на ваше тестовое устройство с помощью Android Studio или командной строки.

Развернуть через Android Studio

При развертывании через Android Studio выполните следующие шаги:

  1. Откройте проект Android Studio для вашего клиентского приложения.
  2. Перейдите, чтобы запустить> Редактировать конфигурации . Появится окно конфигурации запуска/отладки .
  3. Под параметрами запуска установите запуск на указанную деятельность .
  4. Нажмите на три тота -меню рядом с деятельностью и выберите основное действие для вашего клиента.
  5. Нажмите «Применить» , а затем «ОК» .
  6. Нажмите «Выполнить». Чтобы установить клиентское приложение и SDK на ваше тестовое устройство.

Развернуть в командной строке

При развертывании с помощью командной строки выполните шаги в следующем списке. В этом разделе предполагается, что имя вашего модуля приложения SDK является sdk-app и что имя вашего клиентского приложения модуля является client-app .

  1. Из терминала командной строки постройте Sandk Sandbox Sdk Apks: конфиденциальность:

    ./gradlew :client-app:buildPrivacySandboxSdkApksForDebug
    

    Это выводит местоположение для сгенерированных APK. Эти APK подписаны с вашим локальным ключом отладки. Вам нужен этот путь в следующей команде.

  2. Установите APK на вашем устройстве:

    adb install -t /path/to/your/standalone.apk
    
  3. В Android Studio нажмите «Запуск»> «Редактировать конфигурации» . Появится окно конфигурации запуска/отладки .

  4. Под параметрами установки установите развертывание в APK по умолчанию .

  5. Нажмите «Применить» , а затем «ОК» .

  6. Нажмите «Запустить» , чтобы установить пакет APK на вашем тестовом устройстве.

Отлаживать свои приложения

Чтобы отлаживать приложение клиента, нажмите отладку Кнопка в Android Studio.

Чтобы отлаживать приложение SDK, перейдите , чтобы запустить> Прикрепить к процессу , что показывает вам всплывающий экран (показан ниже). Проверьте поле «Показать все процессы» . В появившемся списке ищите процесс с именем CLIENT_APP_PROCESS _sdk_sandbox . Выберите эту опцию и добавьте точки останова в коде приложения SDK, чтобы начать отладку вашего SDK.

Процесс приложения 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, просмотреть заметки о выпуске .

Примеры кода

SDK-времени выполнения и конференции по сохранению конфиденциальности на GitHub содержит набор отдельных проектов Android Studio, которые помогут вам начать работу, включая образцы, которые демонстрируют, как инициализировать и вызвать время выполнения SDK.

Сообщайте об ошибках и проблемах

Ваша отзыв является важной частью песочницей конфиденциальности на Android! Сообщите нам о любых проблемах, которые вы найдете, или идеи для улучшения конфиденциальности песочницы на Android.

{% дословно %} { % endverbatim %} {% дословно %} { % endverbatim %}