Thread-Netzwerk mit Thread-Anmeldedaten von Google teilen

1. Hinweis

In unserem Thread Border Router (TBR)-Codelab zeigen wir, wie Sie einen Thread Border Router auf Basis eines Raspberry Pi erstellen. In diesem Codelab

  • Bidirektionale IP-Verbindung zwischen Thread- und WLAN-/Ethernet-Netzwerken herstellen.
  • Bidirektionale Service Discovery über mDNS (über WLAN-/Ethernet-Verbindung) und SRP (über Thread-Netzwerk) bereitstellen.

Dieses Codelab baut auf dem vorherigen auf und behandelt, wie Ihr eigener Border Router und Ihre App mit Google-APIs interagieren können, um ein einzelnes Thread-Netzwerk zu erstellen. Die Zusammenführung von Thread-Anmeldedaten ist wichtig, da sie die Robustheit des Netzwerks erhöht und die Nutzerinteraktion mit Anwendungen vereinfacht, die auf Thread basieren.

Vorbereitung

  • Führen Sie das OTBR-Codelab aus.
  • Grundkenntnisse in Linux, Android/Kotlin und Thread-Netzwerken

Lerninhalte

  • Anmeldedatensätze mit den Thread Sharing APIs abrufen und festlegen
  • Eigenen OpenThread-Border-Router mit denselben Anmeldedaten wie das Google-Netzwerk einrichten

Voraussetzungen

  • Raspberry Pi 4 oder ein anderes Linux-basiertes Board mit Open Thread Border Router (OTBR)
  • Platine, die IEEE 802.15.4-Konnektivität als Radio Co-Processor (RCP) bietet. Eine Liste der Repositories verschiedener SoC-Anbieter und deren Anleitungen finden Sie auf der OpenThread-GitHub-Seite.

2. HTTP-Dienst einrichten

Der erste Baustein, den wir benötigen, ist eine Schnittstelle, mit der wir aktive Anmeldedaten lesen und ausstehende Anmeldedaten in Ihren OTBR schreiben können. Verwenden Sie beim Erstellen einer TBR Ihre eigenen proprietären Mechanismen, wie hier anhand von zwei Beispielen gezeigt. Die erste Option zeigt, wie Sie lokal über DBUS mit dem OTBR-Agent interagieren, während bei der zweiten Option die REST API verwendet wird, die auf dem OTBR aufgebaut werden kann.

Keine der beiden Methoden ist sicher und sollte nicht unverändert in einer Produktionsumgebung verwendet werden. Ein Anbieter kann jedoch eine Verschlüsselung für eine der beiden Methoden entwickeln, um sie in einer Produktionsumgebung zu verwenden. Alternativ können Sie Ihren eigenen Überwachungsdienst erweitern, um Loopback-HTTP- oder inhärent lokale DBUS-Aufrufe auszugeben.

Option 1: DBUS- und HTTP-API in Python-Script

91e5fdeed83e9354.png

In diesem Schritt wird ein einfacher HTTP-Dienst erstellt, der zwei Endpunkte zum Lesen und Festlegen von Anmeldedaten bereitstellt und letztendlich DBUS-Befehle aufruft.

Installieren Sie auf dem RPi, der als OTBR dienen soll, die Python 3-Abhängigkeiten:

$ pip install dbus-python shlex json

Führen Sie das Skript als:

$  sudo python credentials_server.py 8081
serving at port 8081

Im Beispiel wird ein HTTP-Server auf Port 8081 eingerichtet, der auf dem Stammverzeichnis entweder auf eine GET-Anfrage zum Abrufen von Thread-Anmeldedaten oder auf eine POST-Anfrage zum Festlegen von Thread-Anmeldedaten wartet. Die Nutzlast ist immer eine JSON-Struktur mit dem TLV.

Mit der folgenden PUT-Anfrage werden neue ausstehende Thread-Anmeldedaten für den OTBR über den Pfad /node/dataset/pending festgelegt. In diesem Fall werden die ausstehenden Anmeldedaten innerhalb von 10 Sekunden angewendet:

PUT /node/dataset/pending
Host: <IP>:8081
ContentType: "application/json"
acceptMimeType: "application/json"
...
{
        "ActiveDataset": "<TLV encoded new Thread Dataset>"
"PendingTimestamp": {
        "Seconds": <Unix timestamp in seconds>,
        "Ticks": 0,
        "Authoritative": false
},
"Delay": 10000 // in milliseconds
}

Mit einer GET-Anfrage an /node/dataset/active werden die derzeit aktiven Anmeldedaten abgerufen.

GET /node/dataset/active
Host: <IP>:8081
ContentType = "application/json"
acceptMimeType = "text/plain"
...
<TLV encoded Thread Dataset>

Das Skript ruft DBUS-Lese-/Schreibbefehle für den Buspfad io.openthread.BorderRouter.wpan0 und den Objektpfad /io/openthread/BorderRouter/wpan0 auf:

# D-BUS interface
def call_dbus_method(interface, method_name, *arguments):
    bus = dbus.SystemBus()
    obj = bus.get_object('io.openthread.BorderRouter.wpan0', '/io/openthread/BorderRouter/wpan0')
    iface = dbus.Interface(obj, interface)
    method = getattr(iface, method_name)
    res = method(*arguments)
    return res

def get_dbus_property(property_name):
    return call_dbus_method('org.freedesktop.DBus.Properties', 'Get', 'io.openthread.BorderRouter',
                                 property_name)

def set_dbus_property(property_name, property_value):
    return call_dbus_method('org.freedesktop.DBus.Properties', 'Set', 'io.openthread.BorderRouter',
                                 property_name, property_value)                               

DBUS ermöglicht die Introspektion seiner Funktionen. Dazu haben Sie folgende Möglichkeiten:

$ sudo dbus-send --system --dest=io.openthread.BorderRouter.wpan0 \
        --type=method_call --print-reply /io/openthread/BorderRouter/wpan0 \
        org.freedesktop.DBus.Introspectable.Introspect

Hier finden Sie eine Dokumentation der unterstützten Funktionen.

Option 2: Native HTTP-REST-API des OTBR-Agents

c748ca5151b6cacb.png

Die OpenThread Border Router-Builds werden standardmäßig mit dem Flag REST_API=1 erstellt, wodurch die REST API aktiviert wird. Falls die REST API bei Ihrem Build aus einem vorherigen Codelab nicht aktiviert wurde, müssen Sie OTBR auf Ihrem RPi mit diesem Flag erstellen:

$ REST_API=1 INFRA_IF_NAME=wlan0 ./script/setup

Ein OTBR-Agent kann mit folgendem Befehl neu gestartet werden:

$ sudo systemctl restart otbr-agent.service

Der Agent startet einen HTTP-Server auf Port 8081. Dieser Server ermöglicht es einem Nutzer oder Monitorprogramm, viele Aufgaben im OTBR auszuführen (hier dokumentiert). Sie können den Inhalt mit Ihrem Browser, curl oder wget prüfen. Zu den vielen unterstützten Pfaden gehören die oben beschriebenen Anwendungsfälle mit dem Verb GET auf /node/dataset/active und dem Verb PUT auf /node/dataset/pending.

3. Credential Framework auf Android einrichten

Bevorzugte Anmeldedaten

Die Google Play-Dienste unter Android ermöglichen und erwarten die Registrierung von Anmeldedaten für alle TBRs in Ihrem Netzwerk. Jeder wird durch seine Border Router Agent ID (BAID) identifiziert. Für diese Aufgabe verwenden Sie die Methode addCredentials() der Schnittstelle ThreadNetworkClient. Die bevorzugten Anmeldedaten für dieses Mobilgerät werden durch das erste TBR bestimmt, das dem Speicher der Google Play-Dienste hinzugefügt wird.

Die App, die ihrem BAID eine Reihe von Anmeldedaten für das Thread-Netzwerk hinzufügt, wird zum Inhaber der Anmeldedaten und hat vollen Zugriff darauf. Wenn Sie versuchen, auf Anmeldedaten zuzugreifen, die von anderen Apps hinzugefügt wurden, erhalten Sie den Fehler PERMISSION_DENIED. Die bevorzugten Anmeldedaten sind jedoch immer für jede App verfügbar, wenn der Nutzer seine Einwilligung erteilt. Wir empfehlen, Anmeldedaten, die in Google Play-Diensten gespeichert sind, auf dem neuesten Stand zu halten, wenn das Thread Border Router-Netzwerk aktualisiert wird. Diese Informationen werden derzeit nicht verwendet, aber wir werden in Zukunft möglicherweise verbesserte Routen anbieten.

Auch wenn der erste TBR später ausgeschlossen wird, bleiben die bevorzugten Anmeldedaten auf dem Android-Gerät erhalten. Sobald die Anmeldedaten festgelegt sind, können andere Apps, die Thread-Anmeldedaten verwalten, die Anmeldedaten über einen getPreferredCredentials()-Aufruf abrufen.

Google TBR Sync

Android-Geräte werden automatisch mit Google TBRs synchronisiert. Wenn auf Android keine Anmeldedaten vorhanden sind, werden sie von den Google TBRs in Ihrem Netzwerk abgerufen und werden zu den bevorzugten Anmeldedaten. Die Synchronisierung zwischen TBRs und dem Android-Gerät erfolgt nur, wenn der TBR mit einem einzelnen Nutzer oder mit zwei Nutzern gekoppelt ist, die sich im selben Smart Home (Structure) befinden.

Dieser Vorgang wird auch ausgeführt, wenn sich ein anderer Google-Nutzer in derselben Struktur befindet und GHA für Android oder GHA für iOS verwendet. Bei GHA für iOS werden die bevorzugten Anmeldedaten im iOS-Speicher festgelegt, sofern keine bevorzugten Anmeldedaten vorhanden sind.

Wenn sich zwei Android-Geräte (oder Android + iGHA) mit unterschiedlichen bevorzugten Anmeldedaten im selben Netzwerk befinden, wird das Gerät, auf dem die TBR ursprünglich konfiguriert wurde, für die TBR verwendet.

TBR-Onboarding von Drittanbietern

Der Speicherort der Anmeldedaten ist derzeit nicht auf das Smart-Home-Gerät (Structure) des Nutzers beschränkt. Jedes Android-Gerät hat seinen eigenen BAID-Speicher. Sobald jedoch ein Google-TBR im Netzwerk vorhanden ist, werden andere Android-Geräte und iOS-Geräte, auf denen die Google Home App für iOS ausgeführt wird, mit diesem TBR synchronisiert und versuchen, lokale Anmeldedaten im Telefonspeicher festzulegen.

Bevor ein neues OOB-TBR ein Netzwerk erstellt, muss geprüft werden, ob bereits ein bevorzugtes Netzwerk im Android-Speicher vorhanden ist.

  • Wenn ein bevorzugtes Netzwerk vorhanden ist, sollte der Anbieter es verwenden. So wird dafür gesorgt, dass Thread-Geräte nach Möglichkeit mit einem einzigen Thread-Netzwerk verbunden sind.
  • Wenn kein bevorzugtes Netzwerk vorhanden ist, erstellen Sie einen neuen Anmeldedatensatz und weisen Sie ihn Ihrem TBR in Google Play-Diensten zu. Android verwendet diese Anmeldedaten als Standardanmeldedaten für alle Google-basierten TBRs. Andere Anbieter können die Reichweite und Robustheit Ihres Mesh-Netzwerks mit zusätzlichen Geräten verbessern.

cd8bc726f67b1fa1.png

4. Android-App klonen und ändern

Wir haben eine Android-App erstellt, die die wichtigsten möglichen Aufrufe der Thread API zeigt. Sie können diese Muster in Ihrer App verwenden. In diesem Codelab klonen wir die Google Home Sample App for Matter von GitHub.

Der gesamte hier gezeigte Quellcode ist bereits in der Beispiel-App enthalten. Sie können ihn nach Bedarf ändern. Sie können die App aber auch einfach klonen oder die vorkompilierten Binärdateien ausführen, um die Funktionalität zu prüfen.

  1. Klonen Sie es mit:
$ git clone https://github.com/google-home/sample-apps-for-matter-android.git
  1. Laden Sie Android Studio herunter und öffnen Sie es.
  2. Klicken Sie auf „File“ > „Open“ (Datei > Öffnen) und wählen Sie das geklonte Repository aus.
  3. Aktivieren Sie den Entwicklermodus auf Ihrem Android-Smartphone.
  4. Verbinden Sie es über ein USB-Kabel mit Ihrem Computer.
  5. App über Android Studio mit <Cmd+R> (OS X) oder <Ctrl+R> (Windows, Linux) ausführen
  6. Gehe zum Zahnrad –> „Entwicklertools“ –> „Thread-Netzwerk“.
  7. Interagieren Sie mit den verschiedenen verfügbaren Optionen. In den folgenden Abschnitten wird der Code erläutert, der für jede Schaltfläche ausgeführt wird.

Gibt es bevorzugte Anmeldedaten?

Der erste Schritt für einen TBR-Hersteller sollte sein, Google zu fragen, ob auf dem Gerät bereits bevorzugte Anmeldedaten vorhanden sind. Dies sollte der Ausgangspunkt Ihres Flows sein. Mit dem folgenden Code wird das GPS-Team nach dem Vorhandensein von Anmeldedaten gefragt. Es wird keine Nutzereinwilligung eingeholt, da keine Anmeldedaten weitergegeben werden.

/**
* Prompts whether credentials exist in storage or not. Consent from user is not necessary
*/

fun doGPSPreferredCredsExist(activity: FragmentActivity) {
 try {
   // Uses the ThreadNetwork interface for the preferred credentials, adding
   // a listener that will receive an intentSenderResult. If that is NULL, 
   // preferred credentials don't exist. If that isn't NULL, they exist.
   // In this case we'll not use it.

   ThreadNetwork.getClient(activity).preferredCredentials.addOnSuccessListener { intentSenderResult ->
     intentSenderResult.intentSender?.let { intentSender ->
       ToastTimber.d("threadClient: preferred credentials exist", activity)
       // don't post the intent on `threadClientIntentSender` as we do when
       // we really want to know which are the credentials. That will prompt a
       // user consent. In this case we just want to know whether they exist
     } ?: ToastTimber.d(
       "threadClient: no preferred credentials found, or no thread module found", activity
     )
   }.addOnFailureListener { e: Exception ->
     Timber.d("ERROR: [${e}]")
   }
 } catch (e: Exception) {
   ToastTimber.e("Error $e", activity)
 }
}

Bevorzugte Anmeldedaten für GPS abrufen

Falls sie vorhanden sind, möchten Sie die Anmeldedaten lesen. Der einzige Unterschied zum vorherigen Code besteht darin, dass Sie nach dem Empfang von intentSenderResult einen Intent mit diesem Ergebnis vom Absender erstellen und starten möchten.

In unserem Code verwenden wir aus organisatorischen/architektonischen Gründen ein MutableLiveData<IntentSender?>, da sich der ursprüngliche Code im ViewModel (ThreadViewModel.kt) und die Intent-Observer im Activity-Fragment (ThreadFragment.kt) befinden. Sobald intentSenderResult in den Live-Daten veröffentlicht wird, führen wir den Inhalt dieses Observers aus:

viewModel.threadClientIntentSender.observe(viewLifecycleOwner) { sender ->
 Timber.d(
   "threadClient: intent observe is called with [${intentSenderToString(sender)}]"
 )
 if (sender != null) {
   Timber.d("threadClient: Launch GPS activity to get ThreadClient")
   threadClientLauncher.launch(IntentSenderRequest.Builder(sender).build())
   viewModel.consumeThreadClientIntentSender()
 }
}

Dadurch wird die Einwilligung des Nutzers zum Teilen von Anmeldedaten ausgelöst. Wenn die Einwilligung erteilt wird, werden Inhalte über Folgendes zurückgegeben:

threadClientLauncher =
 registerForActivityResult(ActivityResultContracts.StartIntentSenderForResult()) { result ->
   if (result.resultCode == RESULT_OK) {
     val threadNetworkCredentials =
       ThreadNetworkCredentials.fromIntentSenderResultData(result.data!!)
     viewModel.threadPreferredCredentialsOperationalDataset.postValue(
       threadNetworkCredentials
     )
   } else {
     val error = "User denied request."
     Timber.d(error)
     updateThreadInfo(null, "")
   }
 }

Das Posten der Anmeldedaten in MutableLiveData<ThreadNetworkCredentials?> wird unten beschrieben.

GPS-Anmeldedaten festlegen

Unabhängig davon, ob sie vorhanden sind oder nicht, sollten Sie Ihre TBR in Google Play-Diensten registrieren. Ihre App kann als einzige die Anmeldedaten lesen, die mit der Border Agent-ID Ihres TBR verknüpft sind. Wenn Ihr TBR jedoch der erste ist, der sich registriert, werden diese Anmeldedaten in den Satz „Bevorzugte Anmeldedaten“ kopiert. Diese Informationen sind für jede App auf dem Smartphone zugänglich, sofern der Nutzer dies autorisiert.

/**
* Last step in setting the GPS thread credentials of a TBR
*/
private fun associateGPSThreadCredentialsToThreadBorderRouterAgent(
 credentials: ThreadNetworkCredentials?,
 activity: FragmentActivity,
 threadBorderAgent: ThreadBorderAgent,
) {
 credentials?.let {
   ThreadNetwork.getClient(activity).addCredentials(threadBorderAgent, credentials)
     .addOnSuccessListener {
       ToastTimber.d("threadClient: Credentials added", activity)
     }.addOnFailureListener { e: Exception ->
       ToastTimber.e("threadClient: Error adding the new credentials: $e", activity)
     }
 }
}

Anmeldedaten für Ihr TBR-Produkt festlegen

Dieser Teil ist für jeden Anbieter proprietär. In diesem Codelab wird er entweder über DBUS+Python HTTP Rest Server oder den nativen HTTP Rest Server von OTBR implementiert.

/**
* Creates credentials in the format used by the OTBR HTTP server. See its documentation in
* https://github.com/openthread/ot-br-posix/blob/main/src/rest/openapi.yaml#L215
*/
fun createJsonCredentialsObject(newCredentials: ThreadNetworkCredentials): JSONObject {
 val jsonTimestamp = JSONObject()
 jsonTimestamp.put("Seconds", System.currentTimeMillis() / 1000)
 jsonTimestamp.put("Ticks", 0)
 jsonTimestamp.put("Authoritative", false)

 val jsonQuery = JSONObject()
 jsonQuery.put(
   "ActiveDataset",
   BaseEncoding.base16().encode(newCredentials.activeOperationalDataset)
 )
 jsonQuery.put("PendingTimestamp", jsonTimestamp)
 // delay of committing the pending set into active set: 10000ms
 jsonQuery.put("Delay", 10000)

 Timber.d(jsonQuery.toString())

 return jsonQuery
}

//(...)

var response = OtbrHttpClient.createJsonHttpRequest(
 URL("http://$ipAddress:$otbrPort$otbrDatasetPendingEndpoint"),
 activity,
 OtbrHttpClient.Verbs.PUT,
 jsonQuery.toString()
)

Anmeldedaten für Ihr TBR-Produkt abrufen

Wie bereits gezeigt, verwenden Sie das HTTP-Verb GET, um die Anmeldedaten aus Ihrem TBR abzurufen. Beispiel für ein Python-Skript

Build und Importe

Wenn Sie Ihre Android-App erstellen, müssen Sie Änderungen an Ihrem Manifest, Build und Ihren Importen vornehmen, um das Google Play-Dienste-Thread-Modul zu unterstützen. Die folgenden drei Snippets fassen die meisten Ergänzungen zusammen.

Unsere Beispiel-App wurde hauptsächlich für die Matter-Inbetriebnahme entwickelt. Daher sind die Manifest- und Gradle-Dateien komplexer als die Ergänzungen, die nur für die Verwendung von Thread-Anmeldedaten erforderlich sind.

Manifeständerungen

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    (...)
    <!-- usesCleartextTraffic needed for OTBR local unencrypted communication -->
    <!-- Not needed for Thread Module, only used for HTTP -->
    <uses-feature
    (...)
        android:usesCleartextTraffic="true">

    <application>
    (...)
    <!-- GPS automatically downloads scanner module when app is installed -->
    <!-- Not needed for Thread Module, only used for scanning QR Codes -->
    <meta-data
        android:name="com.google.mlkit.vision.DEPENDENCIES"
        android:value="barcode_ui"/>
    </application>
</manifest>

Build.gradle

// Thread Network
implementation 'com.google.android.gms:play-services-threadnetwork:16.0.0'
// Thread QR Code Scanning
implementation 'com.google.android.gms:play-services-code-scanner:16.0.0'
// Thread QR Code Generation
implementation 'com.journeyapps:zxing-android-embedded:4.1.0'
// Needed for using BaseEncoding class
implementation 'com.google.guava:guava:31.1-jre'

Relevante Importe

// Thread Network Module
import com.google.android.gms.threadnetwork.ThreadNetworkCredentials
import com.google.android.gms.threadnetwork.ThreadBorderAgent
import com.google.android.gms.threadnetwork.ThreadNetwork

// Conversion of credentials to/fro Base16 (hex)
import com.google.common.io.BaseEncoding

// HTTP
import java.io.BufferedInputStream
import java.io.InputStream
import java.net.HttpURLConnection
import java.net.URL
import java.nio.charset.StandardCharsets

// Co-routines for HTTP calls
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch


// JSON
import org.json.JSONObject

// Logs
import timber.log.Timber

// mDNS/SD
import android.net.nsd.NsdServiceInfo

// QR Code reader / writer
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.codescanner.GmsBarcodeScannerOptions
import com.google.mlkit.vision.codescanner.GmsBarcodeScanning
import com.google.zxing.BarcodeFormat
import com.google.zxing.MultiFormatWriter
import com.journeyapps.barcodescanner.BarcodeEncoder

5. mDNS-/SD-Erkennung

In unserer Beispiel-App wird mDNS/SD verwendet, um eine Liste der verfügbaren Thread-Border-Router im Netzwerk sowie deren jeweilige BAIDs zu erstellen.

Das ist sehr hilfreich, wenn Sie die Informationen Ihres TBR in den Speicher der GPS-Anmeldedaten eingeben. Die Verwendung wird in diesem Codelab jedoch nicht behandelt. Wir verwenden die Android Service Discovery-Bibliothek NSDManager. Der vollständige Quellcode ist in der Beispiel-App unter ServiceDiscovery.kt verfügbar.

6. Zusammenfassung

Sobald Sie diese Aufrufe implementiert haben oder die Beispiel-App verwenden, können Sie Ihr RPi OTBR vollständig einrichten. Unsere Beispiel-App bietet 8 Schaltflächen:

91979bf065e9673d.png

So kannst du deine TBR-Liste einrichten:

  1. Abfrage, ob bevorzugte Anmeldedaten vorhanden sind (blau, 1. Zeile)
  2. Je nach Antwort
  3. GPS-Anmeldedaten abrufen (blau, 2. Zeile)
  4. TBR-Anmeldedaten in GPS festlegen (blau, 3. Zeile) –> TBR auswählen –> Zufällig erstellen –> Netzwerkname eingeben –> OK
  5. Nachdem Sie bevorzugte Anmeldedaten haben, legen Sie sie mit Set RPi OTBR credentials (OTBR-Anmeldedaten für RPi festlegen) für Ihren OTBR fest. Dadurch werden diese Anmeldedaten auf den ausstehenden Satz angewendet.

In der Beispiel-App wird standardmäßig eine Verzögerung von 10 Sekunden verwendet. Nach diesem Zeitraum werden die Anmeldedaten Ihres RPi TBR (und anderer Knoten, die sich möglicherweise in seinem Netzwerk befinden) in das neue Dataset migriert.

7. Fazit

In diesem Codelab haben wir eine Beispiel-Android-App geklont und mehrere Code-Snippets analysiert, in denen die Thread Storage APIs der Google Play-Dienste verwendet werden. Wir haben diese APIs verwendet, um ein gemeinsames Dataset zu erstellen, das wir auf einem RPi-TBR einbinden können, um den TBR eines Anbieters zu präsentieren.

Wenn sich alle TBR eines Nutzers im selben Netzwerk befinden, wird die Stabilität und Reichweite des Thread-Netzwerks verbessert. Außerdem werden fehlerhafte Nutzeraktionen verhindert, bei denen Apps keine Thread-Geräte einrichten können, weil sie keinen Zugriff auf Anmeldedaten haben.

Wir hoffen, dass dieses Codelab und die Beispiel-Apps Ihnen helfen, Ihre eigene App und Ihr Thread Border Router-Produkt zu entwickeln.

8. Verweise

RCP-Coprozessor

DBUS