Entwicklerleitfaden für die On-Device-Personalisierung

Die On-Device-Personalisierung (ODP) soll die Informationen von Endnutzern vor Anwendungen schützen. Anwendungen verwenden ODP, um ihre Produkte und Dienste für Endnutzer anzupassen. Sie können jedoch nicht die genauen Anpassungen sehen, die für den Nutzer vorgenommen wurden, es sei denn, es gibt direkte Interaktionen außerhalb von ODP zwischen der Anwendung und dem Endnutzer. Für Anwendungen mit Machine-Learning-Modellen oder statistischen Analysen bietet ODP eine Reihe von Diensten und Algorithmen, um sicherzustellen, dass sie mit den entsprechenden Differential Privacy-Mechanismen ordnungsgemäß anonymisiert werden. Weitere Informationen finden Sie in der Erläuterung zur On-Device-Personalisierung.

ODP führt Entwicklercode in einer IsolatedProcess aus, die keinen direkten Zugriff auf das Netzwerk, lokale Festplatten oder andere auf dem Gerät ausgeführte Dienste hat, aber Zugriff auf die folgenden lokal gespeicherten Datenquellen hat:

  • RemoteData: Unveränderliche Schlüssel/Wert-Daten, die gegebenenfalls von Remote-Backends heruntergeladen werden, die vom Entwickler betrieben werden.
  • LocalData: Änderbare Schlüssel/Wert-Daten, die vom Entwickler lokal gespeichert werden, sofern zutreffend.
  • UserData – Von der Plattform bereitgestellte Nutzerdaten.

Die folgenden Ausgaben werden unterstützt:

  • Persistente Ausgabe: Diese Ausgaben können für die zukünftige lokale Verarbeitung verwendet werden, um angezeigte Ausgaben, durch föderiertes Lernen unterstütztes Modelltraining oder durch föderierte Analysen unterstützte geräteübergreifende statistische Analysen zu erstellen.
    • Entwickler können Anfragen und ihre Verarbeitungsergebnisse in die lokale Tabelle REQUESTS schreiben.
    • Entwickler können zusätzliche Daten, die mit einer früheren Anfrage verknüpft sind, in die Tabelle EVENTS schreiben.
  • Angezeigte Ausgabe:
    • Entwickler können HTML zurückgeben, das von ODP in einem WebView innerhalb eines SurfaceView gerendert wird. Die dort gerenderten Inhalte sind für die aufrufende App nicht sichtbar.
    • Entwickler können von ODP bereitgestellte Ereignis-URLs in die HTML-Ausgabe einbetten, um die Protokollierung und Verarbeitung von Nutzerinteraktionen mit dem gerenderten HTML auszulösen. ODP fängt Anfragen an diese URLs ab und ruft Code auf, um Daten zu generieren, die in die Tabelle EVENTS geschrieben werden.

Client-Apps und SDKs können ODP aufrufen, um HTML-Inhalte in einem SurfaceView mithilfe von ODP-APIs anzuzeigen. In einem SurfaceView gerenderte Inhalte sind für die aufrufende App nicht sichtbar. Die Client-App oder das SDK können von einem anderen Unternehmen als dem, das ODP verwendet, entwickelt werden.

Der ODP-Dienst verwaltet die Client-App, die ODP aufrufen möchte, um personalisierte Inhalte in ihrer Benutzeroberfläche anzuzeigen. Es lädt Inhalte von den vom Entwickler bereitgestellten Endpunkten herunter und ruft die Logik für die Nachbearbeitung der heruntergeladenen Daten auf. Außerdem wird die gesamte Kommunikation zwischen dem IsolatedProcess und anderen Diensten und Apps vermittelt.

Client-Apps verwenden Methoden in der Klasse OnDevicePersonalizationManager, um mit dem Entwicklercode zu interagieren, der in einem IsolatedProcess ausgeführt wird. Der Code des Entwicklers, der in einem IsolatedProcess ausgeführt wird, erweitert die Klasse IsolatedService und implementiert die Schnittstelle IsolatedWorker. Die IsolatedService muss für jede Anfrage eine Instanz von IsolatedWorker erstellen.

Das folgende Diagramm zeigt die Beziehung zwischen den Methoden in OnDevicePersonalizationManager und IsolatedWorker.

Diagramm der Beziehung zwischen OnDevicePersonalizationManager und IsolatedWorker.

Eine Clientanwendung ruft ODP mit der Methode execute mit einem benannten IsolatedService auf. Der ODP-Dienst leitet den Aufruf an die Methode onExecute des IsolatedWorker weiter. Die IsolatedWorker gibt Datensätze zurück, die gespeichert werden sollen, und Inhalte, die angezeigt werden sollen. Der ODP-Dienst schreibt die persistenten Ausgaben in die Tabelle REQUESTS oder EVENTS und gibt einen undurchsichtigen Verweis auf die angezeigten Ausgaben an die Client-App zurück. Die Client-App kann diesen undurchsichtigen Verweis in einem zukünftigen requestSurfacePackage-Aufruf verwenden, um beliebige der angezeigten Inhalte in der Benutzeroberfläche darzustellen.

Dauerhafte Ausgabe

Der ODP-Dienst speichert einen Datensatz in der Tabelle REQUESTS, nachdem die Implementierung von onExecute durch den Entwickler zurückgegeben wurde. Jeder Datensatz in der Tabelle REQUESTS enthält einige allgemeine Daten pro Anfrage, die vom ODP-Dienst generiert werden, sowie eine Liste der zurückgegebenen Rows. Jedes Row enthält eine Liste von (key, value)-Paaren. Jeder Wert ist ein Skalar, ein String oder ein Blob. Die numerischen Werte können nach der Aggregation und die String- oder Blob-Daten nach Anwendung von lokalem oder zentralem Differential Privacy gemeldet werden. Entwickler können auch nachfolgende Nutzerinteraktionsereignisse in die Tabelle EVENTS schreiben. Jeder Datensatz in der Tabelle EVENTS ist einer Zeile in der Tabelle REQUESTS zugeordnet. Der ODP-Dienst protokolliert mit jedem Datensatz transparent einen Zeitstempel und den Paketnamen der aufrufenden App sowie das APK des ODP-Entwicklers.

Hinweis

Bevor Sie mit der Entwicklung mit ODP beginnen können, müssen Sie das Paketmanifest einrichten und den Entwicklermodus aktivieren.

Einstellungen für das Paketmanifest

Für die Verwendung von ODP ist Folgendes erforderlich:

  1. Ein <property>-Tag in AndroidManifest.xml, das auf eine XML-Ressource im Paket verweist, die ODP-Konfigurationsinformationen enthält.
  2. Ein <service>-Tag in AndroidManifest.xml, das die Klasse identifiziert, die IsolatedService erweitert, wie im folgenden Beispiel gezeigt. Für den Dienst im <service>-Tag müssen die Attribute exported und isolatedProcess auf true festgelegt sein.
  3. Ein <service>-Tag in der in Schritt 1 angegebenen XML-Ressource, das die Dienstklasse aus Schritt 2 identifiziert. Das <service>-Tag muss außerdem zusätzliche ODP-spezifische Einstellungen innerhalb des Tags selbst enthalten, wie im zweiten Beispiel gezeigt.

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>

ODP-spezifisches Manifest in XML-Ressource

In der XML-Ressourcendatei, die im Tag <property> angegeben ist, muss die Dienstklasse auch in einem Tag <service> deklariert und der URL-Endpunkt angegeben werden, von dem ODP Inhalte zum Füllen der Tabelle RemoteData herunterlädt, wie im folgenden Beispiel gezeigt. Wenn Sie die Funktionen für föderiertes Computing verwenden, müssen Sie auch den URL-Endpunkt des Servers für föderiertes Computing angeben, mit dem sich der Client für föderiertes Computing verbinden soll.

<!-- 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>

Entwicklermodus aktivieren

Aktivieren Sie den Entwicklermodus, indem Sie der Anleitung im Abschnitt Entwickleroptionen aktivieren der Android Studio-Dokumentation folgen.

Einstellungen für Schalter und Flags

ODP verfügt über eine Reihe von Schaltern und Flags, mit denen bestimmte Funktionen gesteuert werden:

  • _global_killswitch: Der globale Schalter für alle ODP-Funktionen. Wenn er auf „false“ gesetzt ist, wird ODP verwendet.
  • _federated_compute_kill_switch: _Der Schalter, mit dem alle Trainingsfunktionen (Föderiertes Lernen) von ODP gesteuert werden. Auf „false“ gesetzt, um das Training zu verwenden.
  • _caller_app_allowlist: Steuert, wer ODP aufrufen darf. Hier können Apps (Paketname, [optional] Zertifikat) hinzugefügt oder als * festgelegt werden, um alle zuzulassen.
  • _isolated_service_allowlist: Steuert, welche Dienste im Prozess „Isolated Service“ ausgeführt werden können.

Sie können die folgenden Befehle ausführen, um alle Schalter und Flags so zu konfigurieren, dass ODP ohne Einschränkungen verwendet wird:

# 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 \"*\"

Geräteseitige APIs

Weitere Informationen finden Sie in der Android API-Referenzdokumentation für ODP.

Interaktionen mit IsolatedService

Die Klasse IsolatedService ist eine abstrakte Basisklasse, die alle Entwickler, die ODP verwenden möchten, erweitern und in ihrem Paketmanifest als in einem isolierten Prozess laufend deklarieren müssen. Der ODP-Dienst startet diesen Dienst in einem isolierten Prozess und sendet Anfragen an ihn. Der IsolatedService empfängt Anfragen vom ODP-Dienst und erstellt einen IsolatedWorker, um die Anfrage zu verarbeiten.

Entwickler müssen die Methoden der IsolatedWorker-Schnittstelle implementieren, um Anfragen von Client-Apps, abgeschlossene Downloads und Ereignisse zu verarbeiten, die durch das gerenderte HTML ausgelöst werden. Alle diese Methoden haben standardmäßige No-Op-Implementierungen, sodass Entwickler die Implementierung der Methoden überspringen können, die sie nicht benötigen.

Die Klasse OnDevicePersonalizationManager bietet eine API für Apps und SDKs, um mit einem vom Entwickler implementierten IsolatedService zu interagieren, der in einem isolierten Prozess ausgeführt wird. Im Folgenden sind einige Anwendungsfälle aufgeführt:

HTML-Inhalte generieren, die in einer SurfaceView angezeigt werden

Um mit OnDevicePersonalizationManager#execute Inhalte zu generieren, die angezeigt werden sollen, kann die aufrufende App das zurückgegebene SurfacePackageToken-Objekt in einem nachfolgenden requestSurfacePackage-Aufruf verwenden, um das Ergebnis in einem SurfaceView rendern zu lassen .

Bei Erfolg wird der Empfänger mit einem SurfacePackage für eine vom ODP-Dienst gerenderte Ansicht aufgerufen. Clientanwendungen müssen das SurfacePackage in ein SurfaceView in ihrer Ansichtshierarchie einfügen.

Wenn eine App einen requestSurfacePackage-Aufruf mit einem SurfacePackageToken ausführt, der von einem vorherigen OnDevicePersonalizationManager#execute-Aufruf zurückgegeben wurde, ruft der ODP-Dienst IsolatedWorker#onRender auf, um das HTML-Snippet abzurufen, das in einem umzäunten Frame gerendert werden soll. Entwickler haben in dieser Phase keinen Zugriff auf LocalData oder UserData. Dadurch wird verhindert, dass der Entwickler potenziell vertrauliche UserData in Asset-Abruf-URLs im generierten HTML einbettet. Entwickler können IsolatedService#getEventUrlProvider verwenden, um Tracking-URLs zu generieren, die in den generierten HTML-Code eingefügt werden. Wenn das HTML gerendert wird, fängt der ODP-Dienst Anfragen an diese URLs ab und ruft IsolatedWorker#onEvent auf. getRemoteData() kann bei der Implementierung von onRender() aufgerufen werden.

Ereignisse in HTML-Inhalten erfassen

Die Klasse EventUrlProvider bietet APIs zum Generieren von Ereignis-Tracking-URLs, die Entwickler in ihre HTML-Ausgabe einfügen können. Wenn das HTML gerendert wird, ruft ODP IsolatedWorker#onEvent mit der Nutzlast der Ereignis-URL auf.

Der ODP-Dienst fängt Anfragen an ODP-generierte Ereignis-URLs im gerenderten HTML ab, ruft IsolatedWorker#onEvent auf und protokolliert die zurückgegebenen EventLogRecord in der Tabelle EVENTS.

Nichtflüchtige Ergebnisse schreiben

Mit OnDevicePersonalizationManager#execute kann der Dienst Daten in persistenten Speicher (REQUESTS- und EVENTS-Tabellen) schreiben. Hier sind die Einträge, die in diese Tabellen geschrieben werden können:

  • ein RequestLogRecord, das der Tabelle REQUESTS hinzugefügt werden soll.
  • Eine Liste von EventLogRecord-Objekten, die der Tabelle EVENTS hinzugefügt werden sollen. Jedes Objekt enthält einen Zeiger auf ein zuvor geschriebenes RequestLogRecord.

Permanente Ergebnisse im On-Device-Speicher können von Federated Learning für das Modelltraining verwendet werden.

Aufgaben für das Training auf dem Gerät verwalten

Der ODP-Dienst ruft IsolatedWorker#onTrainingExample auf, wenn ein föderierter Trainingsjob für die Berechnung gestartet wird und Trainingsbeispiele von Entwicklern abrufen möchte, die ODP verwenden. Sie können getRemoteData(), getLocalData(), getUserData() und getLogReader() aufrufen, wenn Sie onTrainingExample() implementieren.

Zum Planen oder Abbrechen von föderierten Compute-Jobs können Sie die Klasse FederatedComputeScheduler verwenden, die APIs für alle ODP IsolatedService bietet. Jeder föderierte Rechenjob kann anhand seines Populationsnamens identifiziert werden.

Bevor Sie einen neuen föderierten Berechnungsjob planen, müssen Sie Folgendes tun:

  • Eine Aufgabe mit diesem Populationsnamen sollte bereits auf dem Remote-Server für föderiertes Computing erstellt worden sein.
  • Der Endpunkt der URL des föderierten Rechenservers sollte bereits in den Paketmanifesteinstellungen mit dem Tag federated-compute-settings angegeben sein.

Interaktionen mit persistenten Ausgaben

Im folgenden Abschnitt wird beschrieben, wie Sie mit der persistenten Ausgabe in ODP interagieren.

Lokale Tabellen lesen

Die Klasse LogReader bietet APIs zum Lesen der Tabellen REQUESTS und EVENTS. Diese Tabellen enthalten Daten, die von IsolatedService während onExecute()- oder onEvent()-Aufrufen geschrieben wurden. Die Daten in diesen Tabellen können für das Modelltraining mit föderiertem Lernen oder für geräteübergreifende statistische Analysen mit föderierter Analyse verwendet werden.

Interaktionen mit heruntergeladenen Inhalten

Im folgenden Abschnitt wird beschrieben, wie Sie in ODP mit heruntergeladenen Inhalten interagieren.

Inhalte von Servern herunterladen

Der ODP-Dienst lädt regelmäßig Inhalte von der im Paketmanifest des IsolatedService deklarierten URL herunter und ruft onDownloadCompleted auf, nachdem der Download abgeschlossen ist. Der Download ist eine JSON-Datei mit Schlüssel/Wert-Paaren.

Entwickler, die ODP verwenden, können auswählen, welche Teilmenge der heruntergeladenen Inhalte der Tabelle RemoteData hinzugefügt und welche verworfen werden soll. Entwickler können die heruntergeladenen Inhalte nicht ändern. So wird sichergestellt, dass die Tabelle RemoteData keine Nutzerdaten enthält. Außerdem können Entwickler die LocalData-Tabelle nach Belieben füllen, z. B. durch das Zwischenspeichern einiger vorab berechneter Ergebnisse.

Format der Downloadanfrage

ODP fragt regelmäßig den im Paketmanifest des Entwicklers deklarierten URL-Endpunkt ab, um Inhalte für die Tabelle RemoteData abzurufen.

Der Endpunkt sollte eine JSON-Antwort zurückgeben, wie später beschrieben. Die JSON-Antwort muss einen syncToken enthalten, der die Version der gesendeten Daten angibt, sowie eine Liste der Schlüssel/Wert-Paare, die eingefügt werden sollen. Der syncToken-Wert muss ein Zeitstempel in Sekunden sein, der auf eine UTC-Stundengrenze begrenzt ist. Im Rahmen der Downloadanfrage stellt ODP die syncToken des zuvor abgeschlossenen Downloads und das Geräte-Land als syncToken- und country-Parameter in der Download-URL bereit. Der Server kann das vorherige syncToken verwenden, um inkrementelle Downloads zu implementieren.

Download-Dateiformat

Die heruntergeladene Datei ist eine JSON-Datei mit der folgenden Struktur. Die JSON-Datei muss ein syncToken enthalten, um die Version der heruntergeladenen Daten zu identifizieren. Das „syncToken“ muss ein UTC-Zeitstempel sein, der auf eine Stunde begrenzt ist und den „syncToken“ des vorherigen Downloads überschreiten muss. Wenn das syncToken nicht beide Anforderungen erfüllt, werden die heruntergeladenen Inhalte ohne Verarbeitung verworfen.

Das Feld „contents“ ist eine Liste von Tupeln aus (Schlüssel, Daten, Codierung). key muss ein UTF-8-String sein. Das Feld encoding ist ein optionaler Parameter, der angibt, wie das Feld data codiert wird. Es kann entweder auf „utf8“ oder „base64“ festgelegt werden. Standardmäßig wird „utf8“ angenommen. Das Feld key wird in ein String-Objekt und das Feld data in ein Byte-Array konvertiert, bevor onDownloadCompleted(). aufgerufen wird.

{
  // 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"
    },
    // ...
  ]
}

Serverseitige APIs

In diesem Abschnitt wird beschrieben, wie Sie mit den APIs des föderierten Rechenservers interagieren.

APIs für Federated Compute Server

Wenn Sie einen föderierten Compute-Job auf der Clientseite planen möchten, benötigen Sie eine Aufgabe mit einem Populationsnamen, die auf dem Remote-Server für föderiertes Computing erstellt wurde. In diesem Abschnitt wird beschrieben, wie Sie eine solche Aufgabe auf dem föderierten Compute-Server erstellen.

Diagramm der Client-Server-Topologie für föderiertes Computing.

Wenn ODP-Entwickler eine neue Aufgabe für den Task Builder erstellen, sollten sie zwei Dateisätze bereitstellen:

  1. Ein gespeichertes tff.learning.models.FunctionalModel-Modell durch Aufrufen des API-Aufrufs tff.learning.models.save_functional_model. Ein Beispiel finden Sie in unserem GitHub-Repository.
  2. Eine fcp_server_config.json-Datei mit Richtlinien, Einrichtung des föderierten Lernens und Einrichtung von Differential Privacy. Hier ist ein Beispiel für eine 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
  }
}

Weitere Beispiele finden Sie in unserem GitHub-Repository.

Nachdem Sie diese beiden Eingaben vorbereitet haben, rufen Sie den Task Builder auf, um Artefakte zu erstellen und neue Aufgaben zu generieren. Eine ausführlichere Anleitung finden Sie in unserem GitHub-Repository.