Entwicklerleitfaden für die On-Device-Personalisierung

Die On-Device-Personalisierung (ODP) dient dazu, die Daten von Endnutzern vor Anwendungen zu 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 dafür gesorgt 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-Paare, die gegebenenfalls lokal vom Entwickler gespeichert werden
  • 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 schreiben, die mit einer vorherigen Anfrage in die Tabelle EVENTS verknüpft sind.
  • Angezeigte Ausgabe:
    • Entwickler können HTML-Code zurückgeben, der von ODP in einem 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 dem gerenderten HTML-Code 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 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 auf der Benutzeroberfläche anzuzeigen. Es lädt Inhalte von von Entwicklern bereitgestellten Endpunkten herunter und ruft Logik für die Nachverarbeitung der heruntergeladenen Daten auf. Sie vermittelt auch die gesamte Kommunikation zwischen der 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 in einem IsolatedProcess ausgeführte Code des Entwicklers erweitert die Klasse IsolatedService und implementiert die IsolatedWorker-Schnittstelle. 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 Client-App ruft ODP mit der Methode execute mit einem benannten IsolatedService auf. Der ODP-Dienst leitet den Aufruf an die onExecute-Methode 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 einen intransparenten Verweis auf die angezeigte Ausgabe an die Client-App zurück. Die Client-App kann diesen undurchsichtigen Verweis in einem zukünftigen requestSurfacePackage-Aufruf verwenden, um beliebige Anzeigeinhalte auf 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 Eintrag in der Tabelle REQUESTS enthält einige allgemeine anfragebezogene Daten, 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 gemeldet werden, die String- oder Blob-Daten können nach Anwendung des lokalen oder zentralen 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 transparent einen Zeitstempel und den Paketnamen der aufrufenden App sowie das APK des ODP-Entwicklers mit jedem Eintrag.

Hinweis

Bevor Sie mit der ODP-Entwicklung 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, die 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

In der XML-Ressourcendatei, die im <property>-Tag angegeben ist, muss außerdem die Dienstklasse in einem <service>-Tag deklariert werden. Außerdem muss der URL-Endpunkt angegeben werden, von dem aus ODP Inhalte zum Füllen der Tabelle RemoteData herunterlädt, wie im folgenden Beispiel gezeigt. Wenn Sie die Features für föderiertes Computing verwenden, müssen Sie auch den URL-Endpunkt des föderierten Computing-Servers angeben, zu dem der Client für föderiertes Computing eine Verbindung herstellt.

<!-- 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 Enable Developer Options (Entwickleroptionen aktivieren) in der Android Studio-Dokumentation folgen.

Schalter- und Meldeeinstellungen

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

  • _global_killswitch: Der globale Schalter für alle ODP-Funktionen; auf „false“ gesetzt, um ODP zu verwenden
  • _federated_compute_kill_switch: _Der Schalter, der alle Trainingsfunktionen (föderiertes Lernen) von ODP steuert. Stellen Sie ihn auf „false“, 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 Schalter 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 Referenzdokumentation zur Android API zu 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 stellt 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 finden Sie einige vorgesehene Anwendungsfälle:

HTML-Inhalte für die Anzeige in einer SurfaceView generieren

Um Inhalte für die 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 einem SurfacePackage für eine Ansicht aufgerufen, die vom ODP-Dienst gerendert wird. Clientanwendungen müssen das 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. Dadurch wird verhindert, dass der Entwickler eine potenziell vertrauliche UserData in die URLs zum Abrufen von Assets im generierten HTML-Code einbettet. Entwickler können mit IsolatedService#getEventUrlProvider Tracking-URLs generieren, die in den generierten HTML-Code eingefügt werden. Wenn der HTML-Code gerendert wird, fängt der ODP-Dienst Anfragen an diese URLs ab und ruft IsolatedWorker#onEvent auf. Bei der Implementierung von onRender() kann getRemoteData() aufgerufen werden.

Ereignisse in HTML-Inhalten erfassen

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

Der ODP-Dienst fängt Anfragen an über ODP generierte Ereignis-URLs im gerenderten HTML-Code ab, ruft IsolatedWorker#onEvent auf und protokolliert die zurückgegebenen EventLogRecord in die 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 und jeweils einen Verweis auf eine zuvor geschriebene RequestLogRecord enthalten .

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 bei der Implementierung von onTrainingExample() getRemoteData(), getLocalData(), getUserData() und getLogReader() 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 Rechenjob 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 Populationsnamen 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 dauerhafter Ausgabe

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

Lokale Tabellen lesen

Die Klasse LogReader stellt APIs zum Lesen der Tabellen REQUESTS und EVENTS bereit. 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 föderierte Lernen oder für die geräteübergreifende statistische Analyse verwendet werden, die durch föderiertes Lernen unterstützt wird.

Interaktionen mit heruntergeladenen Inhalten

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

Inhalte von Servern herunterladen

Der ODP-Dienst lädt regelmäßig Inhalte von der URL herunter, die im Paketmanifest von IsolatedService angegeben ist, 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, 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 fragt regelmäßig den im Paketmanifest des Entwicklers deklarierten URL-Endpunkt ab, um Inhalte abzurufen und in die Tabelle RemoteData zu übertragen.

Der Endpunkt sollte eine JSON-Antwort zurückgeben, wie unten beschrieben. Die JSON-Antwort muss ein syncToken enthalten, das die Version der gesendeten Daten identifiziert, sowie eine Liste der zu befüllenden Schlüssel/Wert-Paare. Der Wert syncToken muss ein Zeitstempel in Sekunden sein, der an eine UTC-Stundengrenze gebunden 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.

Dateiformat herunterladen

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 beschränkt ist, und muss das syncToken des vorherigen Downloads überschreiten. Wenn das syncToken beide Anforderungen nicht erfüllt, wird der heruntergeladene Inhalt 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 und ist standardmäßig „utf8“. 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 die Interaktion mit den APIs des föderierten Computing-Servers beschrieben.

Föderierte Compute Server-APIs

Zum Planen eines föderierten Computing-Jobs auf der Clientseite benötigen Sie eine Aufgabe mit einem Populationsnamen, der auf dem Remote-Server für föderierte Rechenvorgänge 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. fcp_server_config.json mit Richtlinien, Einrichtung für föderiertes Lernen und 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. Ausführlichere Anleitungen finden Sie in unserem GitHub-Repository.