Entwicklerleitfaden für die On-Device-Personalisierung

Die On-Device-Personalisierung (ODP) soll die Daten von Endnutzern vor Anwendungen schützen. Mit ODP können Anwendungen ihre Produkte und Dienste für Endnutzer anpassen. Die genauen Anpassungen für den Nutzer sind jedoch nicht sichtbar, es sei denn, es gibt direkte Interaktionen außerhalb von ODP zwischen der Anwendung und dem Endnutzer. Für Anwendungen mit Modellen für maschinelles Lernen oder statistischen Analysen bietet ODP eine Reihe von Diensten und Algorithmen, mit denen sichergestellt wird, dass sie mithilfe der entsprechenden Mechanismen zur Differential Privacy ordnungsgemäß anonymisiert werden. Weitere Informationen finden Sie in der Erläuterung zur personalisierten Werbung auf dem Gerät.

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

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

Die folgenden Ausgabeformate werden unterstützt:

  • Persistente Ausgabe: Diese Ausgabe kann 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 sowie die Ergebnisse der Verarbeitung 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 einer WebView innerhalb einer 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 der gerenderten HTML-Datei 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 einer SurfaceView mithilfe von ODP APIs anzuzeigen. In einer SurfaceView gerenderte Inhalte sind für die aufrufende App nicht sichtbar. Die Client-App oder das SDK kann eine andere Entität sein als die, die mit ODP entwickelt wird.

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 von Entwicklern bereitgestellten Endpunkten herunter und ruft eine Logik für die Nachverarbeitung der heruntergeladenen Daten auf. Außerdem vermittelt er die gesamte Kommunikation zwischen dem IsolatedProcess und anderen Diensten und Apps.

Client-Apps verwenden Methoden der Klasse OnDevicePersonalizationManager, um mit dem Code des Entwicklers 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 dem Namen 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 persistente Ausgabe in die Tabelle REQUESTS oder EVENTS und gibt eine opake Referenz auf die angezeigte Ausgabe an die Client-App zurück. Die Client-App kann diese opake Referenz bei einem zukünftigen requestSurfacePackage-Aufruf verwenden, um einen der angezeigten Inhalte in der Benutzeroberfläche anzuzeigen.

Dauerhafte Ausgabe

Der ODP-Dienst speichert einen Eintrag 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 wurden, und eine Liste der zurückgegebenen Rows. Jedes Row enthält eine Liste von (key, value)-Paaren. Jeder Wert ist ein Skalar, String oder Blob. Die numerischen Werte können nach der Aggregation und die String- oder Blob-Daten nach der Anwendung lokaler oder zentraler Differential Privacy erfasst werden. Entwickler können auch nachfolgende Nutzerinteraktionsereignisse in die Tabelle EVENTS schreiben. Jeder Eintrag in der Tabelle EVENTS ist einer Zeile in der Tabelle REQUESTS zugeordnet. Der ODP-Dienst protokolliert für jeden Eintrag transparent einen Zeitstempel und den Paketnamen der aufrufenden App sowie das APK des ODP-Entwicklers.

Hinweis

Bevor Sie mit der Entwicklung mit ODP beginnen, müssen Sie Ihr Paketmanifest einrichten und den Entwicklermodus aktivieren.

Einstellungen für das Paketmanifest

Für die Verwendung von ODP sind folgende Voraussetzungen 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 im Tag 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

Die im <property>-Tag angegebene XML-Ressourcendatei muss auch die Dienstklasse in einem <service>-Tag deklarieren und den URL-Endpunkt angeben, von dem ODP Inhalte herunterlädt, um die Tabelle RemoteData zu füllen, wie im folgenden Beispiel gezeigt. Wenn Sie die Funktionen für die föderierte Datenverarbeitung verwenden, müssen Sie auch den URL-Endpunkt des Föderierten-Computing-Servers angeben, mit dem der Föderierte-Computing-Client eine Verbindung herstellen 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. Folgen Sie dazu der Anleitung im Abschnitt Entwickleroptionen aktivieren der Android Studio-Dokumentation.

Einstellungen für Schalter und Flags

ODP bietet eine Reihe von Schaltern und Flags, mit denen bestimmte Funktionen gesteuert werden können:

  • _global_killswitch: der globale Schalter für alle ODP-Funktionen; auf „false“ setzen, um ODP zu verwenden
  • _federated_compute_kill_switch: _Schalter, der alle Trainingsfunktionen (föderiertes Lernen) von ODP steuert; auf „false“ setzen, um das Training zu verwenden
  • _caller_app_allowlist: Hier wird festgelegt, wer ODP aufrufen darf. Apps (Paketname, [optional] Zertifikat) können hier hinzugefügt werden oder als * festgelegt werden, um alle zuzulassen.
  • _isolated_service_allowlist: Damit wird festgelegt, welche Dienste im isolierten Dienstprozess ausgeführt werden können.

Mit den folgenden Befehlen können Sie alle Switches und Flags so 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 mit ODP entwickeln möchten, erweitern und in ihrem Paketmanifest als in einem isolierten Prozess ausgeführt 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 bearbeiten.

Entwickler müssen die Methoden der IsolatedWorker-Benutzeroberfläche implementieren, um Client-App-Anfragen, Downloadabschlüsse und Ereignisse zu verarbeiten, die durch die gerenderte HTML-Seite 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 für die Anzeige in einer SurfaceView generieren

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

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

Wenn eine App einen requestSurfacePackage-Aufruf mit einem SurfacePackageToken aus einem vorherigen OnDevicePersonalizationManager#execute-Aufruf ausführt, ruft der ODP-Dienst IsolatedWorker#onRender auf, um das HTML-Snippet abzurufen, das in einem eingegrenzten Frame gerendert werden soll. Ein Entwickler hat in dieser Phase keinen Zugriff auf LocalData oder UserData. So wird verhindert, dass der Entwickler potenziell vertrauliche UserData in Asset-Abruf-URLs in der generierten HTML-Datei einbettet. Entwickler können mit IsolatedService#getEventUrlProvider Tracking-URLs generieren, die in den generierten HTML-Code eingefügt werden. Beim Rendern des HTML-Codes 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 einbinden können. Beim Rendern des HTML-Codes 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ückgegebene EventLogRecord in der Tabelle EVENTS.

Dauerhafte Ergebnisse schreiben

Mit OnDevicePersonalizationManager#execute kann der Dienst Daten im dauerhaften Speicher (REQUESTS- und EVENTS-Tabellen) schreiben. In diese Tabellen können folgende Einträge geschrieben werden:

  • RequestLogRecord, die 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 Verweis auf eine zuvor geschriebene RequestLogRecord.

Dauerhafte Ergebnisse im Gerätespeicher können für das Modelltraining mithilfe von föderiertem Lernen verwendet werden.

On-Device-Trainingsaufgaben verwalten

Der ODP-Dienst ruft IsolatedWorker#onTrainingExample auf, wenn ein federated-compute-Trainingsjob gestartet wird und Trainingsbeispiele von Entwicklern abgerufen werden sollen, die ODP verwenden. Sie können getRemoteData(), getLocalData(), getUserData() und getLogReader() bei der Implementierung von onTrainingExample() aufrufen.

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

Bevor Sie einen neuen Job für die föderierte Datenverarbeitung planen, müssen Sie Folgendes tun:

  • Auf dem Remote-föderierten Compute-Server sollte bereits eine Aufgabe mit diesem Namen erstellt worden sein.
  • Der URL-Endpunkt des föderierten Compute-Servers sollte bereits in den Einstellungen des Paketmanifests mit dem federated-compute-settings-Tag angegeben sein.

Interaktionen mit persistenter Ausgabe

Im folgenden Abschnitt wird beschrieben, wie Sie mit persistenter 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 mithilfe von föderiertem Lernen oder für die geräteübergreifende statistische Analyse mithilfe von föderierten Analysen verwendet werden.

Interaktionen mit heruntergeladenen Inhalten

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

Inhalte von Servern herunterladen

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

Entwickler, die ODP verwenden, können auswählen, welcher Teil der heruntergeladenen Inhalte der Tabelle RemoteData hinzugefügt und welcher gelöscht 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 Tabelle LocalData nach Belieben füllen. Sie können beispielsweise einige vorab berechnete Ergebnisse im Cache speichern.

Format der Downloadanfrage

ODP ruft regelmäßig den URL-Endpunkt ab, der im Paketmanifest des Entwicklers deklariert ist, um Inhalte abzurufen und die Tabelle RemoteData zu füllen.

Der Endpunkt sollte eine JSON-Antwort zurückgeben, wie unten beschrieben. Die JSON-Antwort muss ein syncToken enthalten, das die Version der gesendeten Daten angibt, sowie eine Liste der zu befüllenden Schlüssel/Wert-Paare. Der Wert für syncToken muss ein Zeitstempel in Sekunden sein, der auf eine UTC-Stunde begrenzt ist. Im Rahmen der Downloadanfrage gibt ODP die syncToken des zuvor abgeschlossenen Downloads und das Land des Geräts als „syncToken“- und „country“-Parameter in der Download-URL an. Der Server kann die 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 Stundengrenze begrenzt ist und das „syncToken“ des vorherigen Downloads überschreiten muss. Wenn das syncToken nicht beide Anforderungen erfüllt, werden die heruntergeladenen Inhalte ohne Verarbeitung verworfen.

Das Feld „Inhalt“ ist eine Liste von Tupeln vom Typ (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 für föderierte Compute-Server interagieren.

APIs für föderierte 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-Föderierten Compute-Server erstellt wurde. In diesem Abschnitt erfahren Sie, wie Sie eine solche Aufgabe auf dem föderierten Compute-Server erstellen.

Diagramm der Client-Server-Topologie für die föderierte Datenverarbeitung.

Beim Erstellen einer neuen Aufgabe für den Task Builder sollten ODP-Entwickler zwei Dateien bereitstellen:

  1. Ein gespeichertes tff.learning.models.FunctionalModel-Modell, das über den API-Aufruf tff.learning.models.save_functional_model aufgerufen wurde. Ein Beispiel finden Sie in unserem GitHub-Repository.
  2. Eine fcp_server_config.json mit Richtlinien, der Einrichtung für die föderierte Lernen und der Einrichtung für Differential Privacy. Das folgende Beispiel zeigt 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.