La personalización integrada en el dispositivo (ODP) está diseñada para proteger la información de los usuarios finales de las aplicaciones. Las aplicaciones usan la ODP para personalizar sus productos y servicios para los usuarios finales, pero no podrán ver las personalizaciones exactas realizadas para el usuario (a menos que existan interacciones directas fuera de la ODP entre la aplicación y el usuario final). En el caso de las aplicaciones con modelos de aprendizaje automático o análisis estadísticos, la ODP proporciona un conjunto de servicios y algoritmos para garantizar que se anonimicen correctamente mediante los mecanismos adecuados de privacidad diferencial. Para obtener más detalles, consulta la explicación sobre la personalización integrada en el dispositivo.
La ODP ejecuta el código del desarrollador en una IsolatedProcess
que no tiene acceso directo a la red, los discos locales ni otros servicios que se ejecutan en el dispositivo, pero tiene acceso a las siguientes fuentes de datos que se conservan de forma local:
RemoteData
: Datos de par clave-valor inmutables descargados de backends remotos operados por desarrolladores, si corresponde.LocalData
: Datos clave-valor mutables que el desarrollador conserva de forma local, si corresponde.UserData
: Son los datos del usuario que proporciona la plataforma.
Se admiten los siguientes resultados:
- Salida persistente: Estos resultados se pueden usar en el procesamiento local futuro, lo que genera resultados visibles, el entrenamiento de modelos facilitado por el aprendizaje federado o el análisis estadístico multidispositivo facilitado por las estadísticas federadas.
- Salida que se muestra:
- Los desarrolladores pueden mostrar HTML que la ODP renderiza en un
WebView
dentro de unSurfaceView
. La app que realiza la invocación no podrá ver el contenido que se renderiza allí. - Los desarrolladores pueden incorporar las URLs de eventos proporcionadas por el ODP en el resultado HTML para activar el registro y el procesamiento de las interacciones del usuario con el HTML renderizado. La ODP intercepta las solicitudes a esas URLs y, luego, invoca el código para generar los datos que se escriben en la tabla
EVENTS
.
- Los desarrolladores pueden mostrar HTML que la ODP renderiza en un
Las apps cliente y los SDKs pueden invocar la ODP para mostrar contenido HTML en un SurfaceView
mediante las APIs de ODP. El contenido renderizado en un SurfaceView
no es visible para la app que realiza la llamada. El SDK o la app cliente pueden ser una entidad diferente de la que se desarrolla con ODP.
El servicio de ODP administra la app cliente que desea invocar a ODP para mostrar contenido personalizado en su IU. Descarga contenido de los extremos proporcionados por el desarrollador y, luego, invoca la lógica para el procesamiento posterior de los datos descargados. También media todas las comunicaciones entre IsolatedProcess
y otros servicios y apps.
Las apps cliente usan métodos de la clase OnDevicePersonalizationManager
para interactuar con el código del desarrollador que se ejecuta en un IsolatedProcess
. El código del desarrollador que se ejecuta en un IsolatedProcess
extiende la clase IsolatedService
y, además, implementa la interfaz IsolatedWorker
. IsolatedService
debe crear una instancia de IsolatedWorker
para cada solicitud.
En el siguiente diagrama, se muestra la relación entre los métodos en OnDevicePersonalizationManager
y IsolatedWorker
.
Una app cliente llama a la ODP con el método execute
con un IsolatedService
nombrado. El servicio de ODP reenvía la llamada al método onExecute
de IsolatedWorker
. IsolatedWorker
muestra los registros que se conservarán y el contenido que se mostrará. El servicio de ODP escribe el resultado persistente en la tabla REQUESTS
o EVENTS
, y muestra una referencia opaca al resultado que se muestra en la app cliente. La app cliente puede usar esta referencia opaca en una llamada requestSurfacePackage
futura para mostrar el contenido de la pantalla dentro de su IU.
Salida persistente
El servicio de ODP conserva un registro en la tabla REQUESTS
después de que se muestra la implementación de onExecute
del desarrollador. Cada registro de la tabla REQUESTS
contiene algunos datos comunes por solicitud que genera el servicio de ODP y una lista de Rows
que se muestra. Cada Row
contiene una lista de pares (key, value)
. Cada valor es un escalar, una cadena o un objeto blob. Los valores numéricos se pueden informar después de la agregación, y los datos de cadena o BLOB se pueden informar después de aplicar la privacidad diferencial local o central. Los desarrolladores también pueden escribir eventos de interacción de usuarios posteriores en la tabla EVENTS
; cada registro de la tabla EVENTS
está asociado con una fila de la tabla REQUESTS
. El servicio de ODP registra de manera transparente una marca de tiempo y el nombre del paquete de la app que realiza la llamada y el APK del desarrollador de ODP con cada registro.
Antes de comenzar
Antes de comenzar a desarrollar con ODP, debes configurar el manifiesto de tu paquete y habilitar el modo de desarrollador.
Configuración del manifiesto del paquete
Para usar la ODP, se requiere lo siguiente:
- Una etiqueta
<property>
enAndroidManifest.xml
que apunta a un recurso XML en el paquete que contiene información de configuración de ODP. - Una etiqueta
<service>
enAndroidManifest.xml
que identifica la clase que extiendeIsolatedService
, como se muestra en el siguiente ejemplo. El servicio en la etiqueta<service>
debe tener los atributosexported
yisolatedProcess
configurados comotrue
. - Una etiqueta
<service>
en el recurso XML especificado en el paso 1 que identifica la clase de servicio del paso 2. La etiqueta<service>
también debe incluir parámetros de configuración adicionales específicos de la ODP dentro de la etiqueta, como se muestra en el segundo ejemplo.
AndroidManifest.xml
<!-- Contents of AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.odpsample" >
<application android:label="OdpSample">
<!-- XML resource that contains other ODP settings. -->
<property android:name="android.ondevicepersonalization.ON_DEVICE_PERSONALIZATION_CONFIG"
android:resource="@xml/OdpSettings"></property>
<!-- The service that ODP binds to. -->
<service android:name="com.example.odpsample.SampleService"
android:exported="true" android:isolatedProcess="true" />
</application>
</manifest>
Manifiesto específico de la ODP en el recurso XML
El archivo de recursos XML especificado en la etiqueta <property>
también debe declarar la clase de servicio en una etiqueta <service>
y especificar el extremo de URL desde el que la ODP descargará contenido para propagar la tabla RemoteData
, como se muestra en el siguiente ejemplo. Si usas las funciones de procesamiento federado, también debes especificar el extremo de la URL del servidor de procesamiento federado al que se conectará el cliente de procesamiento federado.
<!-- Contents of res/xml/OdpSettings.xml -->
<on-device-personalization>
<!-- Name of the service subclass -->
<service name="com.example.odpsample.SampleService">
<!-- If this tag is present, ODP will periodically poll this URL and
download content to populate REMOTE_DATA. Developers that do not need to
download content from their servers can skip this tag. -->
<download-settings url="https://example.com/get" />
<!-- If you want to use federated compute feature to train a model, you
need to specify this tag. -->
<federated-compute-settings url="https://fcpserver.example.com/" />
</service>
</on-device-personalization>
Habilitar el modo de desarrollador
Para habilitar el modo de desarrollador, sigue las instrucciones de la sección Habilita las opciones para desarrolladores de la documentación de Android Studio.
Cambiar y marcar configuración
La ODP tiene un conjunto de interruptores y marcas que se usan para controlar ciertas funcionalidades:
- _global_killswitch: El interruptor global para todas las funciones de ODP; se establece en falso para usar la ODP
- _federado_compute_kill_switch: _el interruptor que controla todas las funcionalidades de entrenamiento (aprendizaje federado) de la ODP; se establece en falso para usar el entrenamiento
- _caller_app_allowlist: Controla quién puede llamar a ODP. Se pueden agregar apps (nombre del paquete, certificado [opcional]) aquí o configurarlo como * para permitir todo.
- _isolated_service_allowlist: Controla qué servicios se pueden ejecutar en el proceso de servicio aislado.
Puedes ejecutar los siguientes comandos para configurar todos los interruptores y las marcas para usar la ODP sin restricciones:
# Set flags and killswitches
adb shell device_config set_sync_disabled_for_tests persistent
adb shell device_config put on_device_personalization global_kill_switch false
adb shell device_config put on_device_personalization federated_compute_kill_switch false
adb shell device_config put on_device_personalization caller_app_allow_list \"*\"
adb shell device_config put on_device_personalization isolated_service_allow_list \"*\"
APIs del dispositivo
Consulta la documentación de referencia de la API de Android para la ODP.
Interacciones con IsolatedService
La clase IsolatedService
es una clase base abstracta que todos los desarrolladores que deseen desarrollar con ODP deben extender y declarar en su manifiesto de paquetes como ejecutándose en un proceso aislado. El servicio de ODP inicia este servicio en un proceso aislado y le envía solicitudes. IsolatedService
recibe solicitudes del servicio de ODP y crea un IsolatedWorker
para controlar la solicitud.
Los desarrolladores deben implementar los métodos de la interfaz IsolatedWorker
para controlar las solicitudes de la app cliente, las finalización de descargas y los eventos activados por el HTML renderizado. Todos estos métodos tienen implementaciones predeterminadas de no operación, por lo que los desarrolladores pueden omitir la implementación de los métodos que no les interesan.
La clase OnDevicePersonalizationManager
proporciona una API para que las apps y los SDKs interactúen con un IsolatedService
implementado por el desarrollador que se ejecuta en un proceso aislado. Estos son algunos casos de uso previstos:
Cómo generar contenido HTML para mostrar en una SurfaceView
Para generar contenido para mostrar, con OnDevicePersonalizationManager#execute
, la app que realiza la llamada puede usar el objeto SurfacePackageToken
que se muestra en una llamada requestSurfacePackage
posterior para solicitar que el resultado se renderice en un SurfaceView
.
Si se realiza correctamente, se llama al receptor con un SurfacePackage
para un elemento View renderizado por el servicio de ODP. Las aplicaciones cliente deben insertar el SurfacePackage
en un SurfaceView
dentro de su jerarquía de View.
Cuando una app realiza una llamada a requestSurfacePackage
con un SurfacePackageToken
que muestra un OnDevicePersonalizationManager#execute
anterior, el servicio de ODP llama a IsolatedWorker#onRender
para recuperar el fragmento HTML que se renderizará en un marco vallado. Un desarrollador no tiene acceso a LocalData
ni a UserData
durante esta fase. Esto evita que el desarrollador incorpore UserData
potencialmente sensible en las URLs de recuperación de recursos en el HTML generado. Los desarrolladores pueden usar IsolatedService#getEventUrlProvider
para generar URLs de seguimiento para incluirlas en el código HTML generado. Cuando se renderiza el HTML, el servicio de ODP interceptará las solicitudes enviadas a estas URLs y llamará a IsolatedWorker#onEvent
. Se puede invocar a getRemoteData()
cuando se implementa onRender()
.
Seguimiento de eventos en contenido HTML
La clase EventUrlProvider
proporciona APIs para generar URLs de seguimiento de eventos que los desarrolladores pueden incluir en sus resultados HTML. Cuando se renderice el HTML, ODP invocará a IsolatedWorker#onEvent
con la carga útil de la URL del evento.
El servicio de ODP intercepta las solicitudes a las URLs de eventos generadas por la ODP dentro del HTML renderizado, llama a IsolatedWorker#onEvent
y registra el EventLogRecord
que se muestra en la tabla EVENTS
.
Cómo escribir resultados persistentes
Con OnDevicePersonalizationManager#execute
, el servicio tiene la opción de escribir datos en el almacenamiento persistente (tablas REQUESTS
y EVENTS
). Estas son las entradas que se pueden escribir en estas tablas:
- un
RequestLogRecord
que se agregará a la tablaREQUESTS
. - Una lista de objetos
EventLogRecord
que se agregarán a la tablaEVENTS
, cada uno con un puntero a un objetoRequestLogRecord
escrito anteriormente .
El aprendizaje federado puede consumir los resultados persistentes en el almacenamiento integrado en el dispositivo para el entrenamiento de modelos.
Cómo administrar tareas de entrenamiento integradas en el dispositivo
El servicio de ODP llama a IsolatedWorker#onTrainingExample
cuando se inicia un trabajo de entrenamiento de procesamiento federado y desea obtener ejemplos de entrenamiento proporcionados por desarrolladores que adoptan ODP. Puedes invocar getRemoteData()
, getLocalData()
, getUserData()
y getLogReader()
cuando implementes onTrainingExample()
.
Para programar o cancelar trabajos de procesamiento federados, puedes usar la clase FederatedComputeScheduler
, que proporciona APIs para todos los IsolatedService
de ODP. Cada trabajo de procesamiento federado se puede identificar por su nombre de población.
Antes de programar un nuevo trabajo de procesamiento federado, haz lo siguiente:
- Ya se debería haber creado una tarea con este nombre de población en el servidor de procesamiento federado remoto.
- El extremo de la URL del servidor de procesamiento federado ya debería estar especificado en la configuración del manifiesto del paquete con la etiqueta
federated-compute-settings
.
Interacciones con el resultado persistente
En la siguiente sección, se describe cómo interactuar con la salida persistente en la ODP.
Cómo leer tablas locales
La clase LogReader
proporciona APIs para leer las tablas REQUESTS
y EVENTS
. Estas tablas contienen datos que IsolatedService
escribió durante las llamadas a onExecute()
o onEvent()
. Los datos de estas tablas se pueden usar para el entrenamiento de modelos que facilita el aprendizaje federado o para el análisis estadístico multidispositivo que facilita las estadísticas federadas.
Interacciones con el contenido descargado
En la siguiente sección, se describe cómo interactuar con el contenido descargado en la ODP.
Descarga contenido de los servidores
El servicio de ODP descarga contenido periódicamente de la URL declarada en el manifiesto del paquete de IsolatedService
y llama a onDownloadCompleted
una vez que finaliza la descarga. La descarga es un archivo JSON que contiene pares clave-valor.
Los desarrolladores que adoptan la ODP pueden elegir qué subconjunto del contenido descargado se debe agregar a la tabla RemoteData
y cuál debe descartarse. Los desarrolladores no pueden modificar el contenido descargado, lo que garantiza que la tabla RemoteData
no contenga datos del usuario. Además, los desarrolladores pueden propagar la tabla LocalData
como deseen. Por ejemplo, pueden almacenar en caché algunos resultados calculados previamente.
Formato de solicitud de descarga
La ODP sondea periódicamente el extremo de URL declarado en el manifiesto del paquete del desarrollador para recuperar contenido y propagarlo en la tabla RemoteData
.
Se espera que el extremo muestre una respuesta JSON, como se describe más adelante. La respuesta JSON debe contener un syncToken
que identifique la versión de los datos que se envían, junto con una lista de pares clave-valor que se propagarán. El valor syncToken
debe ser una marca de tiempo en segundos, sujeta a un límite de hora UTC. Como parte de la solicitud de descarga, la ODP proporciona el syncToken
de la descarga completada anteriormente y el país del dispositivo como los parámetros syncToken y país en la URL de descarga. El servidor puede usar el syncToken
anterior para implementar descargas incrementales.
Formato de archivo de descarga
El archivo descargado es un archivo JSON con la siguiente estructura. Se espera que el archivo JSON contenga un syncToken para identificar la versión de los datos que se descargan. El SyncToken debe ser una marca de tiempo UTC fijada en un límite de una hora y debe exceder el syncToken de la descarga anterior. Si el syncToken no cumple con ambos requisitos, se descarta el contenido descargado sin procesarlo.
El campo de contenido es una lista de tuplas (clave, datos, codificación). Se espera que key
sea una cadena UTF-8. El campo encoding
es un parámetro opcional que especifica cómo se codifica el campo data
. Se puede establecer en "utf8" o "base64", y se supone que es "utf8" de forma predeterminada. El campo key
se convierte en un objeto String
, y el campo data
se convierte en un array de bytes antes de llamar a onDownloadCompleted().
.
{
// syncToken must be a UTC timestamp clamped to an hour boundary, and must be
// greater than the syncToken of the previously completed download.
"syncToken": <timeStampInSecRoundedToUtcHour>,
"contents": [
// List of { key, data } pairs.
{ "key": "key1",
"data": "data1"
},
{ "key": "key2",
"data": "data2",
"encoding": "base64"
},
// ...
]
}
APIs del servidor
En esta sección, se describe cómo interactuar con las APIs del servidor de procesamiento federado.
APIs de Federated Compute Server
Para programar un trabajo de procesamiento federado del cliente, necesitas una tarea con un nombre de propagación creado en el servidor de procesamiento federado remoto. En esta sección, explicamos cómo crear una tarea de este tipo en el servidor de procesamiento federado.
Al crear una tarea nueva para el Compilador de tareas, los desarrolladores de ODP deben proporcionar dos conjuntos de archivos:
- Un modelo tff.learning.models.FunctionalModel guardado a través de la llamada a la API tff.learning.models.save_functional_model. Puedes encontrar una muestra en nuestro repositorio de GitHub.
- Un fcp_server_config.json que incluye políticas, configuración de aprendizaje federado y de privacidad diferencial. El siguiente es un ejemplo de un fcp_server_config.json:
{
# Task execution mode.
mode: TRAINING_AND_EVAL
# Identifies the set of client devices that participate.
population_name: "mnist_cnn_task"
policies {
# Policy for sampling on-device examples. It is checked every
# time a device is attempting to start a new training.
min_separation_policy {
# The minimum separation required between two successful
# consective task executions. If a client successfully contributes
# to a task at index `x`, the earliest they can contribute again
# is at index `(x + minimum_separation)`. This is required by
# DP.
minimum_separation: 1
}
data_availability_policy {
# The minimum number of examples on a device to be considered
# eligible for training.
min_example_count: 1
}
# Policy for releasing training results to developers adopting ODP.
model_release_policy {
# The maximum number of training rounds.
num_max_training_rounds: 512
}
}
# Federated learning setups. They are applied inside Task Builder.
federated_learning {
# Use federated averaging to build federated learning process.
# Options you can choose:
# * FED_AVG: Federated Averaging algorithm
# (https://arxiv.org/abs/2003.00295)
# * FED_SGD: Federated SGD algorithm
# (https://arxiv.org/abs/1602.05629)
type: FED_AVG
learning_process {
# Optimizer used at client side training. Options you can choose:
# * ADAM
# * SGD
client_optimizer: SGD
# Learning rate used at client side training.
client_learning_rate: 0.02
# Optimizer used at server side training. Options you can choose:
# * ADAM
# * SGD
server_optimizer: SGD
# Learning rate used at server side training.
server_learning_rate: 1.0
runtime_config {
# Number of participating devices for each round of training.
report_goal: 2
}
metrics {
name: "sparse_categorical_accuracy"
}
}
evaluation {
# A checkpoint selector controls how checkpoints are chosen for
# evaluation. One evaluation task typically runs per training
# task, and on each round of execution, the eval task
# randomly picks one checkpoint from the past 24 hours that has
# been selected for evaluation by these rules.
# Every_k_round and every_k_hour are definitions of quantization
# buckets which each checkpoint is placed in for selection.
checkpoint_selector: "every_1_round"
# The percentage of a populate that should delicate to this
# evaluation task.
evaluation_traffic: 0.2
# Number of participating devices for each round of evaluation.
report_goal: 2
}
}
# Differential Privacy setups. They are enforced inside the Task
# Builder.
differential_privacy {
# * fixed_gaussian: DP-SGD with fixed clipping norm described in
# "Learning Differentially Private Recurrent
# Language Models"
# (https://arxiv.org/abs/1710.06963).
type: FIXED_GAUSSIAN
# The value of the clipping norm.
clip_norm: 0.1
# Noise multiplier for the Gaussian noise.
noise_multiplier: 0.1
}
}
Puedes encontrar más muestras en nuestro repositorio de GitHub.
Después de preparar estas dos entradas, invoca el Compilador de tareas para construir artefactos y generar tareas nuevas. Puedes encontrar instrucciones más detalladas en nuestro repositorio de GitHub.