Guida per gli sviluppatori di Personalizzazione sul dispositivo

La personalizzazione on-device (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 saranno in grado di visualizzare le personalizzazioni esatte apportate per l'utente (a meno che non ci siano 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 una serie di servizi e algoritmi per garantire che vengano anonimizzati correttamente 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.
    • Gli sviluppatori possono scrivere le richieste e i relativi risultati di elaborazione nella tabella locale REQUESTS.
    • Gli sviluppatori possono scrivere dati aggiuntivi associati a una richiesta precedente nella tabella EVENTS.
  • Output visualizzato:
    • Gli sviluppatori possono restituire HTML visualizzato da ODP in un WebView all'interno di un SurfaceView. I contenuti visualizzati lì non saranno visibili all'app che li ha richiamati.
    • Gli sviluppatori possono incorporare gli URL evento forniti dall'ODP nell'output HTML per attivare la registrazione e l'elaborazione delle interazioni degli utenti 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.

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 in fase di sviluppo con ODP.

Il servizio ODP gestisce l'app client che vuole richiamare ODP per mostrare i contenuti personalizzati all'interno della sua UI. Scarica i contenuti dagli endpoint forniti dallo sviluppatore e richiama la logica per la post-elaborazione dei dati scaricati. Inoltre, agisce da mediatore di tutte le comunicazioni tra IsolatedProcess e altri servizi e app.

Le app client utilizzano metodi della classe OnDevicePersonalizationManager per interagire con il codice dello sviluppatore eseguito 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.

Diagramma della relazione tra 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 permanente

Il servizio ODP mantiene un record nella tabella REQUESTS dopo il ritorno dell'implementazione di onExecute da parte dello sviluppatore. Ogni record nella tabella REQUESTS contiene alcuni dati per richiesta comuni generati dal servizio ODP, nonché un elenco di Rows restituiti. Ogni Row contiene un elenco di (key, value) coppie. Ogni valore è uno scalare, una stringa o un blob. I valori numerici possono essere riportati dopo l'aggregazione e i dati della stringa o del blob possono essere riportati 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, il nome del pacchetto dell'app chiamante e l'APK dello sviluppatore ODP per 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 file manifest del pacchetto

Per utilizzare ODP, sono necessari i seguenti requisiti:

  1. Un tag <property> in AndroidManifest.xml che punta a una risorsa XML nel pacchetto contenente informazioni sulla configurazione ODP.
  2. Un tag <service> in AndroidManifest.xml che identifica la classe che estende IsolatedService, come mostrato nell'esempio seguente. Il servizio nel tag <service> deve avere gli attributi exported e isolatedProcess impostati su true.
  3. 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 ulteriori impostazioni 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 inoltre dichiarare la classe di servizio in un tag <service> e specificare l'endpoint URL da cui ODP scaricherà i contenuti per completare 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 computing 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 riportate nella sezione Attivare le Opzioni sviluppatore della documentazione di Android Studio.

Impostazioni di Switch e Flag

ODP ha una serie di opzioni e flag utilizzati per controllare alcune funzionalità:

  • _global_killswitch: l'opzione globale per tutte le funzionalità ODP; impostala su false per utilizzare ODP
  • _federated_compute_kill_switch: _l'opzione che controlla tutte le funzionalità di addestramento (apprendimento federato) di ODP; impostata su false per utilizzare l'addestramento
  • _caller_app_allowlist: consente di stabilire chi è autorizzato a chiamare ODP. È possibile aggiungere app (nome pacchetto, certificato [facoltativo]) o impostarlo come * per consentire tutte.
  • _isolated_service_allowlist: controlla quali servizi possono essere eseguiti nel processo del servizio isolato.

Puoi eseguire questi comandi per configurare tutte le opzioni e i flag in modo che utilizzino ODP senza limitazioni:

# 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 rispetto a ODP devono estendere e dichiarare nel file 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 richieste dal servizio ODP e crea un IsolatedWorker per gestire la richiesta.

Gli sviluppatori devono implementare i metodi dall'interfaccia di IsolatedWorker per gestire le richieste dell'app client, i completamenti dei download e gli eventi attivati dal codice HTML sottoposto a rendering. 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.

Se l'operazione riesce, il ricevitore viene chiamato con 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 sensibile 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 richiamare 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. Dopo il rendering dell'HTML, ODP richiama IsolatedWorker#onEvent con il payload dell'URL dell'evento.

Il servizio ODP intercetta le richieste agli URL degli eventi 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 nell'archiviazione permanente (tabelle REQUESTS e EVENTS). Di seguito sono riportate le voci che è possibile scrivere in queste tabelle:

  • un RequestLogRecord da aggiungere alla tabella REQUESTS.
  • un elenco di oggetti EventLogRecord da aggiungere alla tabella EVENTS, ciascuno contenente un puntatore a un RequestLogRecord scritto in precedenza.

I risultati permanenti nello spazio di archiviazione sul dispositivo possono essere utilizzati dall'apprendimento federato per l'addestramento dei modelli.

Gestisci le attività di formazione sul dispositivo

Il servizio ODP chiama IsolatedWorker#onTrainingExample all'avvio di un job di addestramento di computing federato e vuole ottenere esempi di addestramento forniti dagli sviluppatori che adottano ODP. Puoi richiamare getRemoteData(), getLocalData(), getUserData() e getLogReader() quando implementi 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 computing federato deve essere già specificato nelle impostazioni del file manifest del pacchetto con il tag federated-compute-settings.

Interazioni con output permanente

La sezione seguente descrive come interagire con l'output permanente in ODP.

Lettura 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 manifest del pacchetto di 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. Questo garantisce 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 richiesta di download

ODP esegue periodicamente il polling dell'endpoint URL dichiarato nel manifest del pacchetto per sviluppatori per recuperare i contenuti da inserire nella tabella RemoteData.

L'endpoint dovrebbe restituire una risposta JSON come descritto di seguito. La risposta JSON deve contenere un valore 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, vincolato al limite di ore UTC. Nell'ambito della richiesta di download, ODP fornisce syncToken del download completato in precedenza e il paese del dispositivo come syncToken e parametri paese nell'URL di download. Il server può utilizzare l'oggetto syncToken precedente per implementare i download incrementali.

Scarica formato file

Il file scaricato è un file JSON con la seguente struttura. Il file JSON deve contenere un syncToken per identificare la versione dei dati che viene scaricato. Il syncToken deve essere un timestamp UTC limitato a un'ora e deve superare il syncToken del download precedente. Se il token di sincronizzazione 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 sul lato client, è necessaria un'attività con un nome popolato creato sul server di calcolo federato remoto. In questa sezione viene spiegato come creare un'attività di questo tipo sul server di calcolo federato.

Diagramma della topologia client-server del calcolo federato.

Quando creano una nuova attività per Task Builder, gli sviluppatori ODP devono fornire due insiemi di file:

  1. Un modello tff.learning.models.FunctionalModel salvato tramite la chiamata alla chiamata API tff.learning.models.save_functional_model. Puoi trovare un esempio nel nostro repository GitHub.
  2. Un file fcp_server_config.json che include criteri, configurazione dell'apprendimento federato e 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.