La personalizzazione sul dispositivo (ODP) è progettata per proteggere le informazioni degli utenti finali dalle applicazioni. Le applicazioni utilizzano ODP per personalizzare i propri prodotti e servizi per gli utenti finali, ma non potranno vedere le personalizzazioni esatte apportate per l'utente (a meno che non esistano interazioni dirette al di fuori di ODP tra l'applicazione e l'utente finale). Per le applicazioni con modelli di machine learning o analisi statistiche, ODP fornisce un insieme di servizi e algoritmi per garantire che siano adeguatamente anonimizzati utilizzando i meccanismi di privacy differenziale appropriati. Per ulteriori dettagli, consulta la spiegazione sulla personalizzazione sul dispositivo.
L'ODP esegue il codice sviluppatore in un IsolatedProcess
che non ha accesso diretto alla rete, ai dischi locali o ad altri servizi in esecuzione sul dispositivo, ma ha accesso alle seguenti origini dati persistenti a livello locale:
RemoteData
: dati chiave-valore immutabili scaricati da backend remoti gestiti dagli sviluppatori, se applicabili.LocalData
: dati chiave-valore mutabili persistenti a livello locale dallo sviluppatore, se applicabile.UserData
: dati utente forniti dalla piattaforma.
Sono supportati i seguenti output:
- Output persistente: questi output possono essere utilizzati in un'elaborazione locale futura, per produrre output visualizzati, per l'addestramento dei modelli facilitato da Federated Learning o per l'analisi statistica cross-device facilitata da Federated Analytics.
- Output visualizzato:
- Gli sviluppatori possono restituire HTML visualizzato da ODP in un
WebView
all'interno di unSurfaceView
. I contenuti visualizzati lì non saranno visibili all'app che ha eseguito l'invocazione. - Gli sviluppatori possono incorporare gli URL evento forniti da ODP nell'output HTML per attivare il logging e l'elaborazione delle interazioni utente con l'HTML visualizzato. ODP intercetta le richieste a questi URL e richiama il codice per generare i dati che vengono scritti nella tabella
EVENTS
.
- Gli sviluppatori possono restituire HTML visualizzato da ODP in un
Le app client e gli SDK possono richiamare ODP per visualizzare contenuti HTML in un SurfaceView
utilizzando le API ODP. I contenuti visualizzati in un SurfaceView
non sono visibili all'app chiamante. L'app client o l'SDK può essere un'entità diversa da quella sviluppata con ODP.
Il servizio ODP gestisce l'app client che vuole richiamare ODP per mostrare contenuti personalizzati all'interno della sua UI. Scarica i contenuti dagli endpoint forniti dagli sviluppatori e richiama la logica per la post-elaborazione dei dati scaricati. Inoltre, media tutte le comunicazioni tra IsolatedProcess
e altri servizi e app.
Le app client utilizzano i metodi della classe OnDevicePersonalizationManager
per interagire con il codice dello sviluppatore in esecuzione in un IsolatedProcess
. Il codice dello sviluppatore in esecuzione in un IsolatedProcess
estende la classe IsolatedService
e implementa l'interfaccia IsolatedWorker
. IsolatedService
deve creare un'istanza di IsolatedWorker
per ogni richiesta.
Il seguente diagramma mostra la relazione tra i metodi in OnDevicePersonalizationManager
e IsolatedWorker
.
OnDevicePersonalizationManager
e IsolatedWorker
.Un'app client chiama ODP utilizzando il metodo execute
con un IsolatedService
denominato. Il servizio ODP inoltra la chiamata al metodo onExecute
di IsolatedWorker
. IsolatedWorker
restituisce i record da mantenere e i contenuti da visualizzare. Il servizio ODP scrive l'output persistente nella tabella REQUESTS
o EVENTS
e restituisce un riferimento opaco all'output visualizzato all'app client. L'app client può utilizzare questo riferimento opaco in una chiamata requestSurfacePackage
futura per visualizzare qualsiasi contenuto della visualizzazione all'interno della sua UI.
Output persistente
Il servizio ODP mantiene un record nella tabella REQUESTS
dopo il ritorno dell'implementazione di onExecute
da parte dello sviluppatore. Ogni record della tabella REQUESTS
contiene alcuni dati comuni per richiesta generati dal servizio ODP e un elenco di Rows
restituiti. Ogni Row
contiene un elenco di coppie (key, value)
. Ogni valore è scalare, stringa o blob. I valori numerici possono essere registrati dopo l'aggregazione e i dati di stringa o blob possono essere registrati dopo l'applicazione della privacy differenziale locale o centrale. Gli sviluppatori possono anche scrivere eventi di interazione utente successivi nella tabella EVENTS
: ogni record della tabella EVENTS
è associato a una riga della tabella REQUESTS
. Il servizio ODP registra in modo trasparente un timestamp e il nome del pacchetto dell'app chiamante e l'APK dello sviluppatore ODP con ogni record.
Prima di iniziare
Prima di iniziare a sviluppare con ODP, devi configurare il manifest del pacchetto e attivare la modalità sviluppatore.
Impostazioni del manifest del pacchetto
Per utilizzare ODP, sono necessari i seguenti requisiti:
- Un tag
<property>
inAndroidManifest.xml
che rimanda a una risorsa XML nel pacchetto contenente le informazioni di configurazione dell'ODP. - Un tag
<service>
inAndroidManifest.xml
che identifica la classe che estendeIsolatedService
, come mostrato nell'esempio seguente. Il servizio nel tag<service>
deve avere gli attributiexported
eisolatedProcess
impostati sutrue
. - Un tag
<service>
nella risorsa XML specificata nel passaggio 1 che identifica la classe di servizio del passaggio 2. Il tag<service>
deve includere anche impostazioni aggiuntive specifiche per ODP all'interno del tag stesso, come mostrato nel secondo esempio.
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>
Manifest specifico per ODP nella risorsa XML
Il file di risorse XML specificato nel tag <property>
deve anche dichiarare la classe di servizio in un tag <service>
e specificare l'endpoint URL da cui ODP scaricherà i contenuti per compilare la tabella RemoteData
, come mostrato nell'esempio seguente. Se utilizzi le funzionalità di calcolo federato, devi anche specificare l'endpoint URL del server di calcolo federato a cui si connetterà il client di calcolo federato.
<!-- 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>
Attivare la modalità sviluppatore
Attiva la modalità sviluppatore seguendo le istruzioni nella sezione Attivare le Opzioni sviluppatore della documentazione di Android Studio.
Impostazioni di Switch e Flag
ODP dispone di un insieme di opzioni e flag che vengono utilizzati per controllare determinate funzionalità:
- _global_killswitch: l'opzione di attivazione/disattivazione globale per tutte le funzionalità ODP; impostata su false per utilizzare ODP
- _federated_compute_kill_switch: _l'opzione di controllo di tutte le funzionalità di addestramento (apprendimento federato) di ODP; impostata su false per utilizzare l'addestramento
- _caller_app_allowlist: controlla chi è autorizzato a chiamare l'ODP. Qui è possibile aggiungere le app (nome pacchetto, certificato [facoltativo]) o impostarlo su * per consentire a tutti
- _isolated_service_allowlist: controlla quali servizi possono essere eseguiti nel processo del servizio isolato.
Puoi eseguire i seguenti comandi per configurare tutti gli switch e i flag in modo da utilizzare ODP senza restrizioni:
# 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 \"*\"
API lato dispositivo
Consulta la documentazione di riferimento dell'API Android per ODP.
Interazioni con IsolatedService
La classe IsolatedService
è una classe base astratta che tutti gli sviluppatori che intendono sviluppare in base a ODP devono estendere e dichiarare nel manifest del pacchetto come in esecuzione in un processo isolato. Il servizio ODP avvia questo servizio in un processo isolato e invia richieste. IsolatedService
riceve le richieste dal servizio ODP e crea un IsolatedWorker
per gestirle.
Gli sviluppatori devono implementare i metodi dell'interfaccia IsolatedWorker
per gestire le richieste delle app client, i completamenti dei download e gli eventi attivati dall'HTML visualizzato. Tutti questi metodi hanno implementazioni no-op predefinite, quindi gli sviluppatori possono saltare l'implementazione dei metodi che non li interessano.
La classe OnDevicePersonalizationManager
fornisce un'API per consentire ad app e SDK di interagire con un IsolatedService
implementato dallo sviluppatore in esecuzione in un processo isolato. Di seguito sono riportati alcuni casi d'uso previsti:
Generare contenuti HTML da visualizzare in un SurfaceView
Per generare contenuti da visualizzare, con OnDevicePersonalizationManager#execute
l'app chiamante può utilizzare l'oggetto SurfacePackageToken
restituito in una chiamata requestSurfacePackage
successiva per richiedere il rendering del risultato in un SurfaceView
.
In caso di esito positivo, il destinatario viene chiamato con un SurfacePackage
per una visualizzazione visualizzata dal servizio ODP. Le applicazioni client devono inserire SurfacePackage
in un SurfaceView
all'interno della gerarchia di visualizzazione.
Quando un'app effettua una chiamata requestSurfacePackage
con un SurfacePackageToken
restituito da una chiamata OnDevicePersonalizationManager#execute
precedente, il servizio ODP chiama IsolatedWorker#onRender
per recuperare lo snippet HTML da visualizzare all'interno di un frame delimitato. Uno sviluppatore non ha accesso a LocalData
o UserData
durante questa fase. In questo modo, lo sviluppatore non può incorporare UserData
potenzialmente sensibili all'interno degli URL di recupero degli asset nel codice HTML generato. Gli sviluppatori possono utilizzare IsolatedService#getEventUrlProvider
per generare URL di monitoraggio da includere nel codice HTML generato. Quando viene eseguito il rendering dell'HTML, il servizio ODP intercetta le richieste a questi URL e chiama IsolatedWorker#onEvent
. È possibile invocare getRemoteData()
durante l'implementazione di onRender()
.
Monitorare gli eventi all'interno dei contenuti HTML
La classe EventUrlProvider
fornisce API per generare URL di monitoraggio eventi che gli sviluppatori possono includere nell'output HTML. Quando il codice HTML viene visualizzato, ODP invoca IsolatedWorker#onEvent
con il payload dell'URL evento.
Il servizio ODP intercetta le richieste agli URL evento generati da ODP all'interno del codice HTML visualizzato, chiama IsolatedWorker#onEvent
e registra il EventLogRecord
restituito nella tabella EVENTS
.
Scrivere risultati permanenti
Con OnDevicePersonalizationManager#execute
, il servizio ha la possibilità di scrivere dati in un'area di archiviazione permanente (tabelle REQUESTS
e EVENTS
). Di seguito sono riportate le voci che è possibile scrivere in queste tabelle:
- un
RequestLogRecord
da aggiungere alla tabellaREQUESTS
. - un elenco di oggetti
EventLogRecord
da aggiungere alla tabellaEVENTS
, ciascuno contenente un puntatore a unRequestLogRecord
scritto in precedenza .
I risultati permanenti nello spazio di archiviazione sul dispositivo possono essere utilizzati dall'apprendimento federato per l'addestramento dei modelli.
Gestire le attività di addestramento sul dispositivo
Il servizio ODP chiama IsolatedWorker#onTrainingExample
all'avvio di un job di addestramento di calcolo federato e vuole ottenere esempi di addestramento forniti dagli sviluppatori che adottano ODP. Puoi richiamare getRemoteData()
, getLocalData()
, getUserData()
e getLogReader()
durante l'implementazione di onTrainingExample()
.
Per pianificare o annullare i job di calcolo federato, puoi utilizzare la classe FederatedComputeScheduler
che fornisce API per tutti gli ODP IsolatedService
. Ogni job di calcolo federato può essere identificato dal nome della popolazione.
Prima di pianificare un nuovo job di calcolo federato:
- Un'attività con questo nome della popolazione dovrebbe essere già stata creata sul server di calcolo federato remoto.
- L'endpoint URL del server di calcolo federato deve essere già specificato nelle impostazioni del file manifest del pacchetto con il tag
federated-compute-settings
.
Interazioni con l'output persistente
La sezione seguente descrive come interagire con l'output persistente in ODP.
Leggere le tabelle locali
La classe LogReader
fornisce API per leggere le tabelle REQUESTS
e EVENTS
. Queste tabelle contengono i dati scritti da IsolatedService
durante le chiamate onExecute()
o onEvent()
. I dati in queste tabelle possono essere utilizzati per l'addestramento dei modelli facilitato dall'apprendimento federato o per l'analisi statistica cross-device facilitata da Federated Analytics.
Interazioni con i contenuti scaricati
La sezione seguente descrive come interagire con i contenuti scaricati in ODP.
Scaricare contenuti dai server
Il servizio ODP scarica periodicamente i contenuti dall'URL dichiarato nel file manifest del pacchetto del IsolatedService
e chiama onDownloadCompleted
al termine del download. Il download è un file JSON contenente coppie chiave-valore.
Gli sviluppatori che adottano ODP possono scegliere quale sottoinsieme dei contenuti scaricati deve essere aggiunto alla tabella RemoteData
e quale deve essere eliminato. Gli sviluppatori non possono modificare i contenuti scaricati, in modo che la tabella RemoteData
non contenga dati utente. Inoltre, gli sviluppatori sono liberi di compilare la tabella LocalData
come preferiscono; ad esempio, possono memorizzare nella cache alcuni risultati precomputati.
Formato della richiesta di download
ODP esegue periodicamente il polling dell'endpoint URL dichiarato nel manifest del pacchetto dello sviluppatore per recuperare i contenuti da compilare nella tabella RemoteData
.
L'endpoint dovrebbe restituire una risposta JSON come descritto di seguito. La risposta JSON deve contenere un syncToken
che identifica la versione dei dati inviati, insieme a un elenco di coppie chiave-valore da compilare. Il valore syncToken
deve essere un timestamp in secondi, limitato a un confine orario UTC. Nell'ambito della richiesta di download, ODP fornisce il syncToken
del download completato in precedenza e il paese del dispositivo come parametri syncToken e country nell'URL di download. Il server può utilizzare il syncToken
precedente per implementare i download incrementali.
Formato file di download
Il file scaricato è un file JSON con la seguente struttura. Il file JSON deve contenere un token di sincronizzazione per identificare la versione dei dati in fase di download. syncToken deve essere un timestamp UTC bloccato a un confine di un'ora e deve superare il syncToken del download precedente. Se il token sync non soddisfa entrambi i requisiti, i contenuti scaricati vengono ignorati senza essere elaborati.
Il campo dei contenuti è un elenco di tuple (chiave, dati, codifica). key
deve essere una stringa UTF-8. Il campo encoding
è un parametro facoltativo che specifica la codifica del campo data
. Può essere impostato su "utf8" o "base64" e per impostazione predefinita si presume che sia "utf8". Il campo key
viene convertito in un oggetto String
e il campo data
viene convertito in un array di byte prima di chiamare 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"
},
// ...
]
}
API lato server
Questa sezione descrive come interagire con le API di server di calcolo federati.
API di server di calcolo federato
Per pianificare un job di calcolo federato lato client, devi avere un'attività con un nome di popolazione creato sul server di calcolo federato remoto. In questa sezione viene spiegato come creare un'attività di questo tipo sul server di calcolo federato.
Quando creano una nuova attività per Task Builder, gli sviluppatori ODP devono fornire due insiemi di file:
- Un modello tff.learning.models.FunctionalModel salvato tramite la chiamata all'API tff.learning.models.save_functional_model. Puoi trovare un esempio nel nostro repository GitHub.
- Un file fcp_server_config.json che include i criteri, la configurazione dell'apprendimento federato e la configurazione della privacy differenziale. Di seguito è riportato un esempio di 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
}
}
Puoi trovare altri esempi nel nostro repository GitHub.
Dopo aver preparato questi due input, invoca Task Builder per creare elementi e generare nuove attività. Istruzioni più dettagliate sono disponibili nel nostro repository GitHub.