Руководство разработчика среды выполнения 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. Загрузите образ эмулятора TiramisuPrivacySandbox и создайте эмулятор с этим образом, включающий 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 {
        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>')
    }
    
  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.
        }
    }
}

SdkSandboxController

SdkSandboxController — это оболочка системной службы на основе контекста, доступная для SDK. Его можно получить с помощью SDK, используя контекст, полученный от SandboxedSdkProvider#getContext() , и вызывая context.getSystemService(SdkSandboxController.class) в этом контексте. Контроллер имеет API, которые помогают SDK взаимодействовать и получать информацию из Privacy Sandbox.

Большинство API в контроллере выдают исключения, если SandboxedSdkContext не является контекстом, используемым для доступа. Мы планируем сделать оболочку сервиса недоступной для других контекстов. SdkSandboxController предназначен для использования поставщиками SDK и не рекомендуется для использования разработчиками приложений.

Связь между SDK и SDK

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

API getSandboxedSdks() в SdkSandboxController предоставляет класс SandboxedSdk для всех загруженных SDK в Privacy Sandbox. Объект SandboxedSdk содержит сведения о SDK и sdkInterface , позволяющем клиентам взаимодействовать с ним.

Ожидается, что SDK в Privacy Sandbox будут использовать фрагменты кода, подобные приведенным ниже, для взаимодействия с другими 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 добавляет библиотеку AIDL из SDK2 в качестве зависимости. Этот клиентский SDK содержит код Binder, созданный AIDL. Оба SDK должны экспортировать эту библиотеку AIDL. Это идентично тому, что должны делать приложения при взаимодействии с SDK в Privacy Sandbox.

Поддержка автоматически создаваемого способа совместного использования интерфейсов между SDK будет добавлена ​​в будущем обновлении.

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

API registerAppOwnedSdkSandboxInterface() в SdkSandboxManager предоставляет SDK, которые не поддерживают среду выполнения, регистрировать свои интерфейсы на платформе. API getAppOwnedSdkSandboxInterfaces() в SdkSandboxController предоставляет AppOwnedSdkSandboxInterface для всех зарегистрированных статически связанных SDK.

В следующем примере показано, как зарегистрировать интерфейсы, чтобы сделать их доступными для связи 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 с поддержкой среды выполнения не могут добавлять тег активности в свой файл манифеста и не могут напрямую запускать свои собственные действия. Доступ к объекту Activity предоставляется путем регистрации SdkSandboxActivityHandler и запуска активности песочницы:

1. Зарегистрируйте SdkSandboxActivityHandler.

Зарегистрируйте экземпляр SdkSandboxActivityHandler с помощью SdkSandboxController#registerSdkSandboxActivityHandler(SandboxedActivityHandler)

API регистрирует этот объект и возвращает объект IBinder , который идентифицирует переданный SdkSandboxActivityHandler .

public interface SdkSandboxActivityHandler {
    void onActivityCreated(Activity activity);
}

API регистрирует этот объект и возвращает объект IBinder , который идентифицирует переданный SdkSandboxActivityHandler .

2. Запустите действие в песочнице.

SDK передает возвращенный токен для идентификации зарегистрированного SdkSandboxActivityHandler клиентскому приложению. Затем клиентское приложение вызывает SdkSandboxManager#startSdkSandboxActivity(Activity, Binder) , передавая действие для запуска песочницы и токен, идентифицирующий зарегистрированный SdkSandboxActivityHandler .

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

Когда действие запускается, SDK уведомляется посредством вызова SdkSandboxActivityHandler#onActivityCreated(Activity) как часть выполнения Activity#OnCreate(Bundle) .

Например, имея доступ к объекту Activity , вызывающая сторона может установить представление contentView , вызвав Activity#setContentView(View) .

Чтобы зарегистрировать обратные вызовы жизненного цикла, используйте Activity#registerActivityLifecycleCallbacks(Application.ActivityLifecycleCallbacks) .

Чтобы зарегистрировать OnBackInvokedCallback для переданного действия, используйте Activity#getOnBackInvokedDispatcher().registerOnBackInvokedCallback(Int, OnBackInvokedCallback) .

Тестирование видеоплееров в среде выполнения 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

Чтение SharedPreferences клиента

Клиентские приложения могут поделиться набором ключей из своих SharedPreferences с SdkSandbox . SDK могут считывать данные, синхронизированные из клиентского приложения, с помощью API SdkSanboxController#getClientSharedPreferences() . SharedPreferences , возвращаемые этим API, предназначены только для чтения. Не стоит ему писать.

Получите доступ к рекламному идентификатору, предоставленному сервисами 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<SandboxedSdk?, LoadSdkException?> {
    
          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<LoadSdkResponse, LoadSdkException> {
            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<Bundle, RequestSurfacePackageException> {
            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<Bundle, RequestSurfacePackageException> {
            @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. Приложение может поделиться определенными ключами своих общих SharedPreferences по умолчанию с песочницей. Они могут сделать это, вызвав метод SdkSandboxManager#addSyncedSharedPreferencesKeys(Set<String>keys) в любом экземпляре менеджера SdkSandbox . Как только приложение уведомит SdkSandboxManager какие ключи нужно синхронизировать, SdkSandboxManager синхронизирует значения этих ключей в песочнице и SDK, а затем сможет прочитать их с помощью SdkSandboxController#getClientSharedPreferences . Дополнительную информацию см. в разделе «Чтение общих настроек клиента» .

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

    Вы можете изменить набор синхронизируемых ключей, вызвав SdkSandboxManager#removeSyncedSharedPreferencesKeys(Set<String>keys) для удаления ключей. Чтобы просмотреть текущий набор синхронизируемых ключей, используйте SdkSandboxManager#getSyncedSharedPreferencesKeys() .

    Мы предлагаем сохранять набор ключей как можно меньшим и использовать его только в случае необходимости. Если вы хотите передать информацию в SDK общего назначения, свяжитесь с SDK напрямую, используя их интерфейс SandboxedSdk . Возможный сценарий использования этих API: если приложение использует SDK платформы управления согласием (CMP), тогда SDK внутри песочницы будут заинтересованы в чтении данных, которые CMP SDK хранит в SharedPreferences по умолчанию.

    Котлин

    override fun onCreate(savedInstanceState: Bundle?) {
        
        // At some point, initiate the set of keys for synchronization with sandbox
        mSdkSandboxManager.addSyncedSharedPreferencesKeys(Set.of("foo", "bar"));
    }
    

    Ява

    @Override
        protected void onCreate(Bundle savedInstanceState) {
        
        // At some point, initiate the set of keys for synchronization with sandbox
        mSdkSandboxManager.addSyncedSharedPreferencesKeys(Set.of("foo", "bar"));
    }
    
  7. Чтобы обработать случай, когда процесс песочницы 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, эти представления необходимо загрузить еще раз, чтобы запустить новый процесс песочницы.

  8. Добавьте зависимость от вашего модуля 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 загружены в данный момент

Вы можете проверить, какие SDK загружены в данный момент, используя функцию getSandboxedSdks внутри SdkSandboxManager .

Ограничения

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

Примеры кода

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

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

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

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