Читая документацию Privacy Sandbox для Android, используйте кнопку Developer Preview или Beta , чтобы выбрать версию программы, с которой вы работаете, поскольку инструкции могут отличаться.
Среда выполнения SDK позволяет запускать SDK в выделенной изолированной программной среде, отдельной от вызывающего приложения. SDK Runtime обеспечивает улучшенную защиту и гарантии сбора пользовательских данных. Это делается с помощью модифицированной среды выполнения, которая ограничивает права доступа к данным и набор разрешенных разрешений. Узнайте больше о среде выполнения SDK в проектном предложении .
Шаги на этой странице проведут вас через процесс создания SDK с поддержкой среды выполнения, который определяет веб-представление, которое можно удаленно отображать в вызывающем приложении.
Известные ограничения
Список текущих возможностей для среды выполнения SDK см. в примечаниях к выпуску.
Ожидается, что следующие ограничения будут исправлены в следующем основном выпуске платформы Android.
- Рендеринг рекламы в режиме прокрутки. Например,
RecyclerView
работает неправильно.- Вы можете столкнуться с зависанием при изменении размера.
- События сенсорной прокрутки пользователя не передаются в среду выполнения должным образом.
- API хранилища
- Хранилище для каждого SDK недоступно в Android 13.
В 2023 году будет исправлена следующая проблема:
- API
getAdId
иgetAppSetId
пока не работают должным образом, поскольку их поддержка еще не активирована.
Прежде чем вы начнете
Прежде чем приступить к работе, выполните следующие шаги:
Настройте среду разработки для Privacy Sandbox на Android. Инструменты для поддержки среды выполнения SDK находятся в активной разработке, поэтому для этого руководства вам потребуется использовать последнюю версию Canary Android Studio . Вы можете запускать эту версию Android Studio параллельно с другими используемыми вами версиями, поэтому сообщите нам, если это требование вас не устраивает.
Либо установите образ системы на поддерживаемое устройство , либо настройте эмулятор , включающий поддержку Privacy Sandbox на Android.
Настройте свой проект в Android Studio
Чтобы опробовать SDK Runtime, используйте модель, аналогичную модели клиент-сервер . Основное отличие состоит в том, что приложения (клиент) и SDK («сервер») запускаются на одном устройстве.
- Добавьте модуль приложения в свой проект. Этот модуль служит клиентом, управляющим SDK.
- В модуле приложения включите SDK Runtime , объявите необходимые разрешения и настройте рекламные службы для конкретного API .
- Добавьте в свой проект один библиотечный модуль. Этот модуль содержит ваш код SDK.
- В вашем модуле SDK объявите необходимые разрешения. Вам не нужно настраивать рекламные службы для конкретного API в этом модуле.
- Удалите
dependencies
в файлеbuild.gradle
вашего библиотечного модуля, которые не использует ваш SDK. В большинстве случаев вы можете удалить все зависимости. Вы можете сделать это, создав новый каталог, имя которого соответствует вашему SDK. Вручную создайте новый модуль, используя тип
com.android.privacy-sandbox-sdk
. Он входит в состав кода SDK для создания APK, который можно развернуть на вашем устройстве. Вы можете сделать это, создав новый каталог, имя которого соответствует вашему SDK. Добавьте пустой файлbuild.gradle
. Содержимое этого файла будет заполнено позже в этом руководстве.Добавьте следующий фрагмент в файл
gradle.properties
:android.experimental.privacysandboxsdk.enable=true
Загрузите образ эмулятора TiramisuPrivacySandbox и создайте эмулятор с этим образом, включающий Play Store.
В зависимости от того, являетесь ли вы разработчиком SDK или разработчиком приложения, окончательная настройка может отличаться от той, которая описана в предыдущем абзаце.
Установите SDK на тестовое устройство аналогично тому, как вы устанавливаете приложение, используя Android Studio или Android Debug Bridge (ADB) .Чтобы помочь вам начать работу, мы создали примеры приложений на языках программирования Kotlin и Java, которые можно найти в этом репозитории GitHub . Файлы README и манифеста содержат комментарии, описывающие, что необходимо изменить для запуска примера в стабильных версиях Android Studio.
Подготовьте свой SDK
Вручную создайте каталог уровня модуля. Это служит оболочкой вашего кода реализации для сборки 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>') }
Создайте класс в своей библиотеке реализации, который будет служить точкой входа для вашего 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», оба из которых загружаются приложением в Privacy Sandbox.
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, внесите следующие изменения в вызывающее клиентское приложение:
Добавьте разрешения
INTERNET
иACCESS_NETWORK_STATE
в манифест вашего приложения:<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
В активности вашего приложения, включающей рекламу, объявите ссылку на
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;
Проверьте, доступен ли процесс выполнения SDK на устройстве.
Проверьте константу
SdkSandboxState
(getSdkSandboxState()
).SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION
означает, что среда выполнения SDK доступна.Убедитесь, что вызов
loadSdk()
прошел успешно. Это успешно, если исключений не возникает, а получателем является экземплярSandboxedSdk
.Вызовите
loadSdk()
с переднего плана. Если он вызывается в фоновом режиме, будет выброшеноSecurityException
.Проверьте
OutcomeReceiver
на наличие экземпляраSandboxedSdk
, чтобы убедиться, что было создано исключениеLoadSdkException
. Исключение указывает на то, что среда выполнения SDK может быть недоступна.
Если вызов
SdkSandboxState
илиloadSdk
завершается неудачей, среда выполнения SDK недоступна, и вызов должен вернуться к существующему SDK.Определите класс обратного вызова, реализовав
OutcomeReceiver
для взаимодействия с SDK во время выполнения после его загрузки. В следующем примере клиент использует обратный вызов для ожидания успешной загрузки SDK, а затем пытается визуализировать веб-представление из SDK. Обратные вызовы определяются позже на этом этапе.Котлин
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()
В
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); }
Приложение может поделиться определенными ключами своих общих настроек
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")); }
Чтобы обработать случай, когда процесс песочницы 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, эти представления необходимо загрузить еще раз, чтобы запустить новый процесс песочницы.
Добавьте зависимость от вашего модуля SDK в
build.gradle
вашего клиентского приложения:dependencies { ... implementation project(':<your-sdk-module>') ... }
Тестируйте свои приложения
Чтобы запустить клиентское приложение, установите приложение SDK и клиентское приложение на тестовое устройство с помощью Android Studio или командной строки.
Развертывание через Android Studio
При развертывании через Android Studio выполните следующие шаги:
- Откройте проект Android Studio для своего клиентского приложения.
- Перейдите в «Выполнить» > «Изменить конфигурации» . Появится окно конфигурации запуска/отладки .
- В разделе «Параметры запуска» установите для параметра «Запуск» значение «Указанное действие» .
- Нажмите трехточечное меню рядом с пунктом «Действие» и выберите « Основное действие» для вашего клиента.
- Нажмите «Применить» , а затем «ОК» .
- Нажмите «Выполнить».
чтобы установить клиентское приложение и SDK на тестовое устройство.
Развертывание в командной строке
При развертывании с помощью командной строки выполните действия, указанные в следующем списке. В этом разделе предполагается, что имя вашего модуля приложения SDK — sdk-app
, а имя вашего модуля клиентского приложения — client-app
.
В терминале командной строки создайте APK-файлы Privacy Sandbox SDK:
./gradlew :client-app:buildPrivacySandboxSdkApksForDebug
Это выводит местоположение сгенерированных APK. Эти APK-файлы подписаны вашим локальным ключом отладки. Этот путь понадобится вам в следующей команде.
Установите APK на свое устройство:
adb install -t /path/to/your/standalone.apk
В Android Studio нажмите «Выполнить» > «Изменить конфигурации» . Появится окно конфигурации запуска/отладки .
В разделе «Параметры установки» установите «Развернуть в APK по умолчанию» .
Нажмите «Применить» , а затем «ОК» .
Нажмите «Выполнить» , чтобы установить пакет APK на тестовое устройство.
Отладка ваших приложений
Чтобы отладить клиентское приложение, нажмите кнопку «Отладка». кнопка в Android Studio.
Чтобы отладить приложение SDK, выберите «Выполнить» > «Присоединиться к процессу» , после чего появится всплывающий экран (показанный ниже). Установите флажок «Показать все процессы» . В появившемся списке найдите процесс с именем CLIENT_APP_PROCESS _sdk_sandbox
. Выберите этот параметр и добавьте точки останова в код приложения SDK, чтобы начать отладку вашего SDK.
![Процесс приложения SDK отображается в виде списка в нижней части диалогового окна.](https://developers-dot-devsite-v2-prod.appspot.com/static/privacy-sandbox/assets/images/android/debug-sdk-app.png?authuser=8&hl=ru)
Запускайте и останавливайте среду выполнения 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.
{% дословно %}Рекомендуется для вас
- Примечание. Текст ссылки отображается, когда JavaScript отключен.
- Среда выполнения SDK
- Примечания к выпуску
- API защищенной аудитории в руководстве для разработчиков Android
Читая документацию Privacy Sandbox для Android, используйте кнопку Developer Preview или Beta , чтобы выбрать версию программы, с которой вы работаете, поскольку инструкции могут отличаться.
Среда выполнения SDK позволяет запускать SDK в выделенной изолированной программной среде, отдельной от вызывающего приложения. SDK Runtime обеспечивает улучшенную защиту и гарантии сбора пользовательских данных. Это делается с помощью модифицированной среды выполнения, которая ограничивает права доступа к данным и набор разрешенных разрешений. Узнайте больше о среде выполнения SDK в проектном предложении .
Шаги на этой странице проведут вас через процесс создания SDK с поддержкой среды выполнения, который определяет веб-представление, которое можно удаленно отображать в вызывающем приложении.
Известные ограничения
Список текущих возможностей для среды выполнения SDK см. в примечаниях к выпуску.
Ожидается, что следующие ограничения будут исправлены в следующем основном выпуске платформы Android.
- Рендеринг рекламы в режиме прокрутки. Например,
RecyclerView
работает неправильно.- Вы можете столкнуться с зависанием при изменении размера.
- События сенсорной прокрутки пользователя не передаются в среду выполнения должным образом.
- API хранилища
- Хранилище для каждого SDK недоступно в Android 13.
В 2023 году будет исправлена следующая проблема:
- API
getAdId
иgetAppSetId
пока не работают должным образом, поскольку их поддержка еще не активирована.
Прежде чем вы начнете
Прежде чем приступить к работе, выполните следующие шаги:
Настройте среду разработки для Privacy Sandbox на Android. Инструменты для поддержки среды выполнения SDK находятся в активной разработке, поэтому для этого руководства вам потребуется использовать последнюю версию Canary Android Studio . Вы можете запускать эту версию Android Studio параллельно с другими используемыми вами версиями, поэтому сообщите нам, если это требование вас не устраивает.
Либо установите образ системы на поддерживаемое устройство , либо настройте эмулятор , включающий поддержку Privacy Sandbox на Android.
Настройте свой проект в Android Studio
Чтобы опробовать SDK Runtime, используйте модель, аналогичную модели клиент-сервер . Основное отличие состоит в том, что приложения (клиент) и SDK («сервер») запускаются на одном устройстве.
- Добавьте модуль приложения в свой проект. Этот модуль служит клиентом, управляющим SDK.
- В модуле приложения включите SDK Runtime , объявите необходимые разрешения и настройте рекламные службы для конкретного API .
- Добавьте в свой проект один библиотечный модуль. Этот модуль содержит ваш код SDK.
- В вашем модуле SDK объявите необходимые разрешения. Вам не нужно настраивать рекламные службы для конкретного API в этом модуле.
- Удалите
dependencies
в файлеbuild.gradle
вашего библиотечного модуля, которые не использует ваш SDK. В большинстве случаев вы можете удалить все зависимости. Вы можете сделать это, создав новый каталог, имя которого соответствует вашему SDK. Вручную создайте новый модуль, используя тип
com.android.privacy-sandbox-sdk
. Он входит в состав кода SDK для создания APK, который можно развернуть на вашем устройстве. Вы можете сделать это, создав новый каталог, имя которого соответствует вашему SDK. Добавьте пустой файлbuild.gradle
. Содержимое этого файла будет заполнено позже в этом руководстве.Добавьте следующий фрагмент в файл
gradle.properties
:android.experimental.privacysandboxsdk.enable=true
Загрузите образ эмулятора TiramisuPrivacySandbox и создайте эмулятор с этим образом, включающий Play Store.
В зависимости от того, являетесь ли вы разработчиком SDK или разработчиком приложения, окончательная настройка может отличаться от той, которая описана в предыдущем абзаце.
Установите SDK на тестовое устройство аналогично тому, как вы устанавливаете приложение, используя Android Studio или Android Debug Bridge (ADB) .Чтобы помочь вам начать работу, мы создали примеры приложений на языках программирования Kotlin и Java, которые можно найти в этом репозитории GitHub . Файлы README и манифеста содержат комментарии, описывающие, что необходимо изменить для запуска примера в стабильных версиях Android Studio.
Подготовьте свой SDK
Вручную создайте каталог уровня модуля. Это служит оболочкой вашего кода реализации для сборки 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>') }
Создайте класс в своей библиотеке реализации, который будет служить точкой входа для вашего 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 взаимодействовать и получать информацию из песочницей конфиденциальности.
Большинство API в исключениях контроллера бросают, если SandboxedSdkContext
не является контекстом, используемым для доступа. Мы планируем сделать обертку услуги недоступной для других контекстов. SdkSandboxController
предназначен для использования поставщиками SDK, и не рекомендуется для использования разработчиками приложений.
SDK к SDK Communication
SDK во время выполнения должны иметь возможность общаться друг с другом для поддержки посредничества и связанных с ними вариантов использования. Набор инструментов, описанный здесь, предоставляет интерфейс для работы с SDK, которые предоставляются другим SDK в песочнице. Эта реализация времени выполнения SDK является первым шагом к тому, чтобы обеспечить связь SDK в SDK и может еще не охватить все варианты использования для посредничества в песочнице конфиденциальности.
API getSandboxedSdks()
в SdkSandboxController
предоставляет класс SandboxedSdk
для всех загруженных SDK в песочнице конфиденциальности. Объект SandboxedSdk
имеет подробную информацию о SDK и sdkInterface
для клиентов, чтобы общаться с ним.
Ожидается, что SDK в песочнице конфиденциальности будут использовать фрагменты кода, аналогичные следующим, чтобы поговорить с другими 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 содержит код связующего, сгенерированный AIDL. Оба SDK должны экспортировать эту библиотеку AIDL. Это идентично тому, что должны делать приложения, когда они общаются с SDK в песочнице конфиденциальности.
Поддержка автоматического сгенерированного способа обмена интерфейсами между SDK будет добавлена в будущем обновлении.
SDK во время выполнения могут потребоваться общаться с зависимостями приложений и AD SDK, которые еще не с поддержкой времени выполнения.
API 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())
);
Этот пример демонстрирует, как общаться с SDKS без поддержки времени выполнения:
// 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();
Поскольку AD 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
В дополнение к поддержке рекламных баннеров, песочница конфиденциальности привержена поддержке видеопрогнеристов, работающих во время выполнения 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
. Несколько примеров:
- Доступ к файлам с использованием структуры доступа к хранилищам вызовет
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 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
Чтение SharedPreferences
клиента
Клиентские приложения могут выбрать поделиться набором ключей из своих SharedPreferences
с SdkSandbox
. SDK могут читать синхронизацию данных из клиентского приложения с помощью SdkSanboxController#getClientSharedPreferences()
API. SharedPreferences
, возвращенные этим API, предназначены только для чтения. Вы не должны писать на это.
Доступ к рекламному идентификатору, предоставленному 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:
Добавьте разрешение
INTERNET
иACCESS_NETWORK_STATE
в манифест вашего приложения:<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
В действии вашего приложения, которое включает в себя объявление, объявить ссылку на
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;
Проверьте, доступен ли процесс выполнения SDK на устройстве.
Проверьте постоянную
SdkSandboxState
(getSdkSandboxState()
).SDK_SANDBOX_STATE_ENABLED_PROCESS_ISOLATION
означает, что время выполнения SDK доступно.Проверьте, что вызов
loadSdk()
успешно. Это успешно, если нет исключений, а приемник является экземпляромSandboxedSdk
.Вызовите
loadSdk()
с переднего плана. Если это будет вызвано с фона, будет выброшеноSecurityException
.Проверьте
OutcomeReceiver
для экземпляраSandboxedSdk
, чтобы проверить, была ли брошенаLoadSdkException
. Исключение указывает на то, что среда выполнения SDK может быть недоступна.
Если
SdkSandboxState
или вызовloadSdk
не выполняется, время выполнения SDK недоступно, а вызов должен отступить на существующий SDK.Определите класс обратного вызовов, реализуя
OutcomeReceiver
взаимодействовать с SDK во время выполнения после его загрузки. В следующем примере клиент использует обратный вызов, чтобы подождать, пока SDK не будет успешно загружен, а затем пытается отобразить веб -представление из SDK. Обратные вызовы определяются позже на этом шаге.Котлин
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()
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); }
Приложение может поделиться определенными ключами своих
SharedPreferences
по умолчанию с песочницей. Они могут сделать это, вызывая методSdkSandboxManager#addSyncedSharedPreferencesKeys(Set<String>keys)
в любом экземпляреSdkSandbox
Manager. Как только приложение уведомляетSdkSandboxManager
, которые синхронизируют клавиши к синхронизации,SdkSandboxManager
будет синхронизировать значения этих клавиш в песочницу и SDK, а затем сможет прочитать их, используяSdkSandboxController#getClientSharedPreferences
. См. Чтение SharedPreferences клиента для получения дополнительной информации.Набор синхронизированных ключей не сохраняется в результате перезапуска приложений, а данные, синхронизированные до песочницы, стерли при перезапуске из песочницы. Следовательно, важно, чтобы приложение инициировало синхронизацию, вызывая
addSyncedSharedPreferencesKeys
каждый раз, когда начинается приложение.Вы можете изменить набор клавиш, которые синхронизируются, вызывая
SdkSandboxManager#removeSyncedSharedPreferencesKeys(Set<String>keys)
для удаления клавиш. Чтобы просмотреть текущий набор синхронизированных ключей, используйтеSdkSandboxManager#getSyncedSharedPreferencesKeys()
.Мы предлагаем сохранить набор клавиш максимально маленьким, и использовать его только в случае необходимости. Если вы хотите передать информацию в SDK для общего назначения, пожалуйста, свяжитесь с SDK непосредственно, используя их интерфейс
SandboxedSdk
. Возможный сценарий для использования этих API будет, если бы приложение использовало SDK платформы управления согласия (CMP), тогда SDK в песочнице будут заинтересованы в чтении хранилищ DATA 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")); }
Чтобы справиться с случаем, когда неожиданно завершается процесс 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, эти представления должны быть загружены снова, чтобы начать новый процесс песочницы.
Добавьте зависимость от вашего SDK -модуля в
build.gradle
вашего клиента.dependencies { ... implementation project(':<your-sdk-module>') ... }
Тестируйте свои приложения
Чтобы запустить ваше клиентское приложение, установите приложение SDK и клиентское приложение на ваше тестовое устройство с помощью Android Studio или командной строки.
Развернуть через Android Studio
При развертывании через Android Studio выполните следующие шаги:
- Откройте проект Android Studio для вашего клиентского приложения.
- Перейдите, чтобы запустить> Редактировать конфигурации . Появится окно конфигурации запуска/отладки .
- Под параметрами запуска установите запуск на указанную деятельность .
- Нажмите на три тота -меню рядом с деятельностью и выберите основное действие для вашего клиента.
- Нажмите «Применить» , а затем «ОК» .
- Нажмите «Выполнить».
Чтобы установить клиентское приложение и SDK на ваше тестовое устройство.
Развернуть в командной строке
При развертывании с помощью командной строки выполните шаги в следующем списке. В этом разделе предполагается, что имя вашего модуля приложения SDK является sdk-app
и что имя вашего клиентского приложения модуля является client-app
.
Из терминала командной строки постройте Sandk Sandbox Sdk Apks: конфиденциальность:
./gradlew :client-app:buildPrivacySandboxSdkApksForDebug
Это выводит местоположение для сгенерированных APK. Эти APK подписаны с вашим локальным ключом отладки. Вам нужен этот путь в следующей команде.
Установите APK на вашем устройстве:
adb install -t /path/to/your/standalone.apk
В Android Studio нажмите «Запуск»> «Редактировать конфигурации» . Появится окно конфигурации запуска/отладки .
Под параметрами установки установите развертывание в APK по умолчанию .
Нажмите «Применить» , а затем «ОК» .
Нажмите «Запустить» , чтобы установить пакет APK на вашем тестовом устройстве.
Отлаживать свои приложения
Чтобы отлаживать приложение клиента, нажмите отладку Кнопка в Android Studio.
Чтобы отлаживать приложение SDK, перейдите , чтобы запустить> Прикрепить к процессу , что показывает вам всплывающий экран (показан ниже). Проверьте поле «Показать все процессы» . В появившемся списке ищите процесс с именем CLIENT_APP_PROCESS _sdk_sandbox
. Выберите эту опцию и добавьте точки останова в коде приложения SDK, чтобы начать отладку вашего SDK.
![Процесс приложения SDK появляется в списке, расположенном рядом с нижней частью диалога](https://developers-dot-devsite-v2-prod.appspot.com/static/privacy-sandbox/assets/images/android/debug-sdk-app.png?authuser=8&hl=ru)
Запустите и остановите время выполнения 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 в настоящее время загружаются, используя функциональность getSandboxedSdks
внутри SdkSandboxManager
.
Ограничения
Для получения списка неверных возможностей для среды выполнения SDK, просмотреть заметки о выпуске .
Примеры кода
Среда выполнения SDK и конференции по сохранению конфиденциальности API на GitHub содержит набор отдельных проектов Android Studio, которые помогут вам начать работу, включая образцы, которые демонстрируют, как инициализировать и вызвать время выполнения SDK.Сообщайте об ошибках и проблемах
Ваша отзыв является важной частью песочницей конфиденциальности на Android! Сообщите нам о любых проблемах, которые вы найдете, или идеи для улучшения конфиденциальности песочницы на Android.
{% дословно %}Рекомендуется для вас
- Примечание. Текст ссылки отображается, когда JavaScript отключен.
- Среда выполнения SDK
- Примечания к выпуску
- API защищенной аудитории в руководстве для разработчиков Android