Utiliser la passerelle SDV sur IVI

La passerelle SDV (Software Defined Vehicle) sur les systèmes d'infoloisirs embarqués (IVI, In-Vehicle Infotainment) facilite la communication entre les systèmes IVI et les services SDV distants. La passerelle permet aux applications OEM Java et aux services natifs, tels que VHAL, d'interagir avec les services SDV. La passerelle utilise des méthodes de communication établies, y compris l'enregistrement, la découverte et le RPC des services.

Cette passerelle répond à des exigences spécifiques du projet AAOS SDV, comme l'activation d'une implémentation de référence VHAL à l'aide de SDV Data Tunnel pour les informations sur les propriétés. Il permet également aux applications Android Java et Kotlin sur IVI d'utiliser la pile de communication SDV, de s'enregistrer en tant que services, de trouver d'autres services SDV et de communiquer avec eux.

Pour connaître les emplacements du code de la passerelle SDV et des exemples de clients de passerelle SDV, consultez Emplacements du code.

Modèles d'intégration de la passerelle SDV

Utiliser les communications SDV-IVI via l'architecture de la passerelle SDV sur IVI

La passerelle SDV interagit avec les applications Java, les services natifs, la pile de communication SDV et le réseau du véhicule. La figure 1 illustre ces interactions :

Interactions avec la passerelle SDV

Figure 1. Interactions avec la passerelle SDV.

Dans ce schéma du système :

  • Les applications interagissent avec la passerelle SDV via ses services client.
  • Le SDK AAOS SDV vous fournit les éléments suivants :
    • API AIDL ISdvGateway pour la communication inter-processus.
    • Bibliothèques de communication pour l'interaction réseau.
    • API C pratique pour l'intégration de services natifs.
  • L'API AIDL ISdvGateway est implémentée par le service et le sous-système SDV Gateway.
  • Le service SDV Gateway gère :
    • Découverte bidirectionnelle des services.
    • Communication avec les services SDV à distance.
    • Logique métier principale.
  • Le sous-système SDV Gateway se connecte au réseau du véhicule.
  • Les services natifs, y compris l'implémentation VHAL, peuvent utiliser l'API ISdvGateway AIDL directement ou via l'API C du SDK.
  • Le proxy VHAL sert d'implémentation VHAL de référence, intégrant le mappage VSIDL.

Modèle d'intégration pour la passerelle SDV sur un service natif IVI

Le modèle d'intégration est illustré dans la figure 2 :

Modèle d'intégration de la passerelle SDV

Figure 2. Modèle d'intégration de la passerelle SDV.

Utiliser la passerelle SDV sur un service natif IVI

La figure 3 illustre l'utilisation de la passerelle SDV sur IVI :

Passerelle SDV sur IVI

Figure 3. Passerelle SDV sur IVI.

Conditions préalables

Démarrez le pool de threads Binder :

  • La bibliothèque cliente SDV Gateway nécessite un pool de threads Binder démarré pour recevoir les rappels asynchrones des services Binder.

  • L'API nécessaire à la création d'un client SDV Gateway échoue lorsqu'un pool de threads Binder n'est pas démarré.

Inclure la bibliothèque cliente native Gateway

La bibliothèque cliente Native Gateway expose une API C. Ajoutez une instance de libsdvgatewayclient en tant que dépendance pour utiliser l'API C :

cc_binary {
    name: "your_binary_name",
    srcs: ["main.cpp"],
    shared_libs: [
        "libsdvgatewayclient",
    ],
}

Charger le client Gateway natif

#include "libsdvgatewayclient.h"

Créer une instance de client natif

ASDVGateway_Client* client;
ASDVGateway_StatusCode_t statusCode = ASDVGateway_Client_new(
     &client, /*outStatus*/nullptr);

Une fois le client créé, il :

  • Contient l'état de toutes les interactions ultérieures avec le service Gateway.

  • Il sert de contexte à toutes les interactions avec d'autres services compatibles avec SDV et est transmis en tant que premier paramètre aux fonctions de l'API C.

Codes d'état et messages d'erreur

La plupart des fonctions de l'API C ont la définition suivante :

ASDVGateway_StatusCode_t ApiFunctionName(..., ASDVGateway_Status_t* outStatus);

Pour évaluer la réussite, vous pouvez inspecter le code d'état renvoyé, qui est de type ASDVGateway_StatusCode_t. Vous pouvez également transmettre un pointeur vers une structure dans laquelle la fonction peut renseigner le code d'état et un message d'erreur. Le pointeur est transmis en tant que dernier paramètre, nommé outStatus. Une valeur nulle signifie que la structure de sortie n'est pas utilisée.

L'appelant doit allouer la structure d'état de la mémoire pour le message d'erreur. La structure d'état peut contenir à la fois le code d'état et un message d'erreur. Un exemple de récupération du message d'erreur lors de la création d'un client est disponible.

Pour évaluer la réussite :

  1. Inspectez le code d'état renvoyé, qui est de type ASDVGateway_StatusCode_t.

  2. Transmettez un pointeur vers une structure dans laquelle la fonction peut renseigner le code d'état et un message d'erreur.

    • Le pointeur est transmis en tant que dernier paramètre, nommé outStatus.
    • Une valeur nulle signifie que la structure de sortie n'est pas utilisée.
    • L'appelant doit allouer la structure d'état de la mémoire pour le message d'erreur.
    • La structure d'état peut contenir le code d'état et un message d'erreur.

Voici un exemple qui montre comment récupérer le message d'erreur pour un nouveau client :

#include <iostream>
#include <array>

struct StatusWithErrorMsg : ASDVGateway_Status_t {
    StatusWithErrorMsg() {
        // Ensure the base struct pointers point to our internal buffer
        errorMessage = errorMessageBuffer.data();
        maxErrorMessageSize = errorMessageBuffer.size();

        // Good practice: Zero-initialize the buffer
        errorMessageBuffer.fill(0);
    }

    std::array<char, 256> errorMessageBuffer;
};

// --- Execution ---

ASDVGateway_Client* client = nullptr;
StatusWithErrorMsg status;

// Initialize the client
ASDVGateway_StatusCode_t statusCode = ASDVGateway_Client_new(&client, &status);

// Log Results
std::cout << "Returned statusCode:    " << statusCode << std::endl;
std::cout << "Status Struct Code:     " << status.statusCode << std::endl;
std::cout << "Status Error Message:   " << status.errorMessage << std::endl;

Dans cet exemple :

  • Le code d'état contenu dans la structure d'état a la même valeur que le code d'état renvoyé.

  • Le message d'erreur n'est rempli que jusqu'à la limite de caractères maxErrorMessageSize (y compris le caractère de fin null \0). Si aucune erreur ne se produit (code d'état OK), le message d'erreur est une chaîne vide.

Communications initiales

Init comms initialise la communication entre l'application appelante et d'autres applications à l'aide de la pile de communication SDV et de la passerelle SDV. Les communications d'initialisation peuvent être appelées dans les deux contextes suivants :

  • Une fois le client créé.

  • Avant toute interaction avec Data Tunnel, RPC ou Service Discovery.

Exemple :

ASDVGateway_InitCommsParams_t params{
    .packageName         = "android.sdv.samples.gateway.client",
    .serviceBundleName   = "NativeTestApp",
    .serviceInstanceName = "default",
};

// Initialize communications and capture the status code
ASDVGateway_StatusCode_t statusCode = ASDVGateway_Client_initComms(client, &params, &status);

// Recommended check
if (statusCode != ASDVGateway_StatusCode_OK) {
    std::cerr << "Failed to init comms: " << status.errorMessage << std::endl;
}

Découverte des services

Vous pouvez recevoir des notifications lorsque des unités de service d'un type ou d'un nom spécifiques sont enregistrées ou désenregistrées. La fonction de rappel est notifiée dans un thread appartenant au client de la passerelle. Ce rappel est déclenché initialement avec toutes les unités de service enregistrées juste après l'appel de l'API C. Après le déclencheur initial, les notifications continuent d'être envoyées jusqu'à ce que l'écouteur soit explicitement désinscrit.

class NativeSdvGatewayTestApp {
public:
    static void ServiceUnitChangeListenerCallback(
        const ASDVGateway_ServiceUnitChangeEventType eventType,
        const ASDVGateway_ServiceUnitDefinition* serviceUnitDefinition,
        void* userData
    ) {
        auto* testApp = reinterpret_cast<NativeSdvGatewayTestApp*>(userData);

        if (testApp) {
            // Logic to react when services are registered or unregistered
            // e.g., testApp->handleServiceChange(eventType, serviceUnitDefinition);
        }
    }
};

// --- Listener Registration ---

// Ensure thisApp remains in scope as long as the listener is active
NativeSdvGatewayTestApp* thisApp = get_current_app_context();

ASDVGateway_UnitType_t unitType{
    .sdvPackageName     = "android.sdv.samples.sdv_gateway",
    .serviceBundleName  = "DtPublisher",
    .unitTypeName       = "TirePressure",
};

// Register the listener
ASDVGateway_Client_registerListenerForServiceUnitChangeByType(
    client,
    &unitType,
    NativeSdvGatewayTestApp::ServiceUnitChangeListenerCallback,
    static_cast<void*>(thisApp), // userData
    &listenerHandle,
    &status
);

// --- Cleanup ---

// Unregistering stops all notification to the callback function
ASDVGateway_Client_unregisterListenerForServiceUnitChangeByType(
    client,
    listenerHandle,
    &status
);

Pour récupérer les unités de service enregistrées sans recevoir d'autres notifications, utilisez les API ASDVGateway_Client_fetchServiceUnitsByType et ASDVGateway_Client_fetchServiceUnitsByName.

class NativeSdvGatewayTestApp {
public:
    /**
     * Callback triggered for each service unit found that matches the requested type.
     */
    static void FetchServiceUnitsCallback(
        const ASDVGateway_ServiceUnitDefinition* serviceUnitDefinition,
        void* userData
    ) {
        auto* app = reinterpret_cast<NativeSdvGatewayTestApp*>(userData);

        if (serviceUnitDefinition) {
            // The service unit is registered with service discovery.
            // Example: processServiceUnit(serviceUnitDefinition);
        }
    }
};

// --- Execution ---

ASDVGateway_UnitType_t unitType{
    .sdvPackageName     = "android.sdv.samples.sdv_gateway",
    .serviceBundleName  = "DtPublisher",
    .unitTypeName       = "TirePressure",
};

// Context used to differentiate between various fetchServiceUnits calls
void* userData = static_cast<void*>(thisApp);

ASDVGateway_Client_fetchServiceUnitsByType(
    client,
    &unitType,
    NativeSdvGatewayTestApp::FetchServiceUnitsCallback,
    userData,
    &status
);

Le rappel est déclenché de manière synchrone dans le thread de l'appelant lors de l'appel d'API ASDVGateway_Client_fetchServiceUnitsByType. Utilisez ASDVGateway_Client_fetchServiceUnitsByName pour obtenir les unités de service enregistrées par nom au lieu de par type d'unité :

ASDVGateway_StatusCode_t ASDVGateway_Client_fetchServiceUnitsByName(
    // [in]  Opaque pointer to a client object.
    const ASDVGateway_Client* client,

    // [in]  Pointer to a structure containing the package name,
    //       service bundle name, and service unit name.
    const ASDVGateway_UnitNameDiscoveryArgs_t* unitName,

    // [in]  Callback function to be called for each service unit
    //       definition registered. Called synchronously in the
    //       caller's thread before the fetch completes.
    ASDVGateway_FetchServiceUnitsCallback callback,

    // [in]  Optional value passed back as a parameter of the callback.
    void* userData,

    // [out] Optional pointer to status structure for result
    //       codes and error messages.
    ASDVGateway_Status_t* outStatus
);

Flux RPC

La bibliothèque cliente Gateway gère les paramètres TLS (Transport Layer Security) pour la communication avec d'autres applications compatibles avec SDV. Plus précisément, il récupère les paramètres TLS requis pour la communication. Voici comment l'utilisation de TLS est déterminée :

  • TLS est utilisé lorsque le mode Boot SDV est défini sur LOCKED.

  • La communication non sécurisée est utilisée lorsque le mode Boot SDV est défini sur UNLOCKED. Lorsque TLS est utilisé, la bibliothèque cliente Gateway génère une paire de clés connue uniquement du processus d'application. Le client récupère les identifiants RPC pour créer un serveur RPC ou un canal client RPC. RPC utilise un VLAN SDV-RPC dédié. La bibliothèque cliente Gateway appelle android_setprocnetwork pour remplacer le réseau par défaut du processus par le VLAN SDV-RPC pendant ou après l'appel ASDVGateway_Client_initComms.

Disponibilité des RPC

Un client configuré pour démarrer au début du démarrage peut démarrer avant que le VLAN SDV-RPC ne soit disponible. Vérifiez que le RPC est disponible avant d'essayer de créer des sockets de serveur ou de client RPC :

// Check if the RPC (Remote Procedure Call) service is available
bool isRpcAvailable = ASDVGateway_Client_isRpcAvailable(client);

if (isRpcAvailable) {
    // Proceed with RPC calls
} else {
    // Handle the case where RPC is not yet ready or available
}

ou

// Check if the process is correctly bound to the SDV RPC Network Interface/VLAN
bool isRpcNetworkBound = ASDVGateway_Client_isProcessBoundToSdvRpcNetworkInterface(client);

if (!isRpcNetworkBound) {
    // Usually implies the process isn't running on the correct network interface
    // or the VLAN configuration is missing.
    std::cerr << "Warning: Process is not bound to the SDV RPC VLAN." << std::endl;
}

Définissez un écouteur pour les événements client à l'aide de ASDVGateway_Client_setClientNotificationCallback afin d'être averti lorsque l'état de disponibilité du RPC change. ASDVGateway_Client_isProcessBoundToSdvRpcNetworkInterface est préférable si l'application change le réseau du processus, car il vérifie à la fois que le RPC est disponible et que le processus est lié au VLAN SDV-RPC.

Changer le réseau par défaut du processus

Vous devrez peut-être passer du VLAN SDV-RPC en tant que réseau par défaut du processus pour ouvrir des sockets pour les connexions liées à Internet. Appelez ASDVGateway_Client_unbindProcessFromSdvRpcNetworkInterface et ASDVGateway_Client_bindProcessToSdvRpcNetworkInterface pour dissocier et réassocier le processus au VLAN SVR-RPC. Les deux appels agissent comme des commutateurs globaux, en changeant l'interface réseau à laquelle les sockets sont liés pour tous les threads du processus.

// 1. Unbind: Sockets return to the "default" network interface (e.g., wlan0, eth0)
ASDVGateway_StatusCode_t unbindStatus =
    ASDVGateway_Client_unbindProcessFromSdvRpcNetworkInterface(client, &status);

// 2. Bind: Sockets are now bound to the dedicated SDV-RPC VLAN
ASDVGateway_StatusCode_t bindStatus =
    ASDVGateway_Client_bindProcessToSdvRpcNetworkInterface(client, &status);

// Validation
if (bindStatus == ASDVGateway_StatusCode_OK) {
    // Sockets are successfully bound to the SDV-RPC VLAN
}

Notifications RPC

Définissez l'écouteur client pour recevoir des notifications concernant les modifications apportées à la disponibilité RPC et les mises à jour des certificats racine qui doivent être acceptées par un serveur RPC :

class NativeSdvGatewayTestApp {
public:
    static void ClientNotificationCallback(
        ASDVGateway_ClientNotificationType_t notificationType,
        void* userData
    ) {
        auto* testApp = reinterpret_cast<NativeSdvGatewayTestApp*>(userData);
        if (!testApp) return;

        switch (notificationType) {
            case ASDVGateway_ClientNotificationType_RootCertsChanged:
                std::cout << "onClientNotification: Root Certs Changed" << std::endl;
                // Handle certificate rotation logic here
                break;

            case ASDVGateway_ClientNotificationType_RpcAvailabilityChanged:
                std::cout << "onClientNotification: RPC Availability Changed" << std::endl;
                // Handle reconnection or UI updates here
                break;

            default:
                std::cout << "onClientNotification: Received Unknown Notification ("
                          << notificationType << ")" << std::endl;
                break;
        }
    }
};

// --- Registration ---

NativeSdvGatewayTestApp* thisApp = get_current_app_context();

// Register the notification callback to monitor system-level changes
ASDVGateway_StatusCode_t statusCode = ASDVGateway_Client_setClientNotificationCallback(
    client,
    NativeSdvGatewayTestApp::ClientNotificationCallback,
    static_cast<void*>(thisApp), // userData
    &status
);

Flux du serveur RPC

Vous devez utiliser des identifiants pour créer le serveur RPC :

ASDVGateway_RpcCredentials_t* rpcCredentials = nullptr;

// Retrieve the credentials from the client
ASDVGateway_StatusCode_t statusCode = ASDVGateway_Client_rpcCredentials(client, &rpcCredentials, &status);

// Validation: Differentiating between an error and a deliberate "insecure mode"
if (status.statusCode != ASDVGateway_StatusCode_OK) {
    std::cerr << "Error retrieving credentials: " << status.errorMessage << std::endl;
    return;
}

// Logic for choosing Security Credentials
if (rpcCredentials == nullptr) {
    // If the call succeeded but credentials are null, the system expects insecure communication
    std::cout << "Configuring for Insecure Mode" << std::endl;
} else {
    std::cout << "Configuring for Secure Mode:" << std::endl;
    std::cout << "  Private Key: " << (rpcCredentials->privateKeyPem ? "Present" : "Missing") << std::endl;
    std::cout << "  RootCerts:   " << rpcCredentials->rootCertsPem << std::endl;
    std::cout << "  CertChain:   " << rpcCredentials->certChainPem << std::endl;
    std::cout << "  SAN:         " << rpcCredentials->subjectAlternativeName << std::endl;
}

// --- Cleanup ---

// Release the memory once the RPC server/client is initialized
if (rpcCredentials != nullptr) {
    ASDVGateway_Client_deleteRpcCredentials(client, rpcCredentials);
}

Une fois le serveur RPC créé et son port d'écoute connu, enregistrez le serveur RPC afin qu'il puisse découvrir d'autres applications :

ASDVGateway_RegisterRpcServerParams_t params{
    .serviceUnitName = "android-sdv-samples-sunroof-sunroof",
    .unitType = ASDVGateway_UnitType_t{
        .sdvPackageName     = "android.sdv.samples.sunroof",
        .serviceBundleName  = "SunroofServer",
        .unitTypeName       = "Sunroof",
    },
    .listeningPort = listeningPort,
    .serverUnitMetadata = ASDVGateway_ServerUnitMetadata_t{
        .version = 1,
    },
};

// Register the RPC server with the SDV Gateway
ASDVGateway_StatusCode_t statusCode = ASDVGateway_Client_registerRpcServer(
    client,
    &params,
    &status
);

// Basic error handling
if (statusCode != ASDVGateway_StatusCode_OK) {
    std::cerr << "Failed to register RPC server: " << status.errorMessage << std::endl;
}

Lorsque le système met à jour les certificats racine au moment de l'exécution (par exemple, lors des changements d'état de la VM), les serveurs RPC doivent actualiser leur liste de certificats acceptés :

  • Définissez un écouteur pour les événements client à l'aide de ASDVGateway_Client_setClientNotificationCallback afin d'être averti lorsque les certificats racine ont été mis à jour.

  • Appelez ASDVGateway_Client_rpcCredentials pour obtenir les certificats racine mis à jour.

Flux du client RPC

Voici le flux pour le client RPC :

ASDVGateway_FindRpcServerByNameParams_t params{
    .packageName        = "android.sdv.samples.cluster",
    .serviceBundleName  = "ClusterServer",
    .serviceUnitName    = "android-sdv-samples-cluster-cluster",
};

ASDVGateway_SocketAddress_t socketAddress;
ASDVGateway_RpcCredentials_t* rpcCredentials = nullptr;

// Perform the lookup to find the server's location and security requirements
ASDVGateway_StatusCode_t statusCode = ASDVGateway_Client_findRpcServerByName(
    mClient,
    &params,
    &socketAddress,
    &rpcCredentials,
    &status
);

// Mandatory check: Distinguish between system errors and intentional "Insecure Mode"
if (status.statusCode != ASDVGateway_StatusCode_OK) {
    std::cerr << "Failed to find RPC server: " << status.errorMessage << std::endl;
    return;
}

// Logic for establishing the RPC channel
if (rpcCredentials == nullptr) {
    std::cout << "Connecting via Insecure Mode to "
              << socketAddress.address << ":" << socketAddress.port << std::endl;
} else {
    std::cout << "Connecting via Secure Mode to "
              << socketAddress.address << ":" << socketAddress.port << std::endl;

    std::cout << "  RootCerts: " << rpcCredentials->rootCertsPem << std::endl;
    std::cout << "  SAN:       " << rpcCredentials->subjectAlternativeName << std::endl;
    // Note: privateKeyPem and certChainPem are typically used if the client
    // needs to perform mutual TLS (mTLS).
}

// Cleanup: Release the credential memory once the RPC channel is established
if (rpcCredentials != nullptr) {
    ASDVGateway_Client_deleteRpcCredentials(client, rpcCredentials);
}

Créer un éditeur et publier des messages

L'API ASDVGateway_Client_createPublication permet d'enregistrer une unité de service d'éditeur auprès de la passerelle SDV via son interface AIDL et de créer une file d'attente de messages rapides (FMQ) dans le processus de l'application. Binder n'est impliqué que dans la configuration de la FMQ, mais pas dans l'écriture des messages. L'API ASDVGateway_Client_publishMessages est ensuite utilisée pour publier des messages dans la publication créée. Cela implique d'écrire dans la FMQ de la publication et de notifier que des messages ont été écrits.

ASDVGateway_CreatePublicationParams_t params{
    .serviceUnitName = "mirror-position-adjust-impl-1",
    .unitType = ASDVGateway_UnitType_t{
        .sdvPackageName     = "android.sdv.samples.sdv_gateway",
        .serviceBundleName  = "DtPublisher",
        .unitTypeName       = "MirrorPositionAdjust",
    },
    .publisherUnitMetadata = ASDVGateway_PublisherUnitMetadata_t{
        .version          = 1,
        .messageSizeBytes = 64,
        .messageCount     = 16,
    },
};

ASDVGateway_PublicationMetadata_t metadata;

// 1. Create the Publication (allocates resources on the gateway)
ASDVGateway_StatusCode_t createStatus = ASDVGateway_Client_createPublication(
    client,
    &params,
    &metadata,
    &status
);

if (status.statusCode != ASDVGateway_StatusCode_OK) {
    std::cerr << "Failed to create publication: " << status.errorMessage << std::endl;
    return;
}

// 2. Publish Messages
// Note: serializedMessage should contain your encoded protobuf data
std::vector<uint8_t> serializedMessage;

ASDVGateway_Client_publishMessages(
    client,
    serializedMessage.data(),
    serializedMessage.size(),
    metadata.publicationId,
    &status
);

Créer un abonné avec un écouteur de notification

Pour vous abonner à une publication et recevoir des notifications Data Tunnel (par exemple, sur la disponibilité des messages), utilisez l'API ASDVGateway_Client_subscribeToPublicationByName. Cette API configure également la FMQ pour la lecture des messages publiés. Vous pouvez configurer un rappel de notification pendant le processus d'abonnement ou après.

#include <map>
#include <memory>
#include <iostream>

class NativeSdvGatewayTestApp {
public:
    struct SubscriptionContext {
        int32_t subscriptionId;
        std::string topicName;
        // Add other context-specific data here (e.g., counters, buffers)
    };

    /**
     * Callback triggered when new data is published to a subscribed topic.
     */
    static void SubscriptionNotificationCallback(
        const ASDVGateway_SubscriptionNotificationData_t* notification,
        void* userData
    ) {
        // The SDV Gateway client passes back the userData pointer.
        // We ensure validity by managing the lifecycle of SubscriptionContext
        // within the mSubscriptions map.
        auto* ctx = reinterpret_cast<SubscriptionContext*>(userData);

        if (ctx && notification) {
            // React to data being available for the subscription.
            // Example: handleIncomingData(notification->data, notification->size);
            std::cout << "Notification received for sub ID: " << ctx->subscriptionId << std::endl;
        }
    }

private:
    // Maps subscription handles/IDs to their respective contexts
    std::map<int32_t, std::unique_ptr<SubscriptionContext>> mSubscriptions;
};

// --- Usage ---
NativeSdvGatewayTestApp app;

Lorsque vous vous abonnez, vous pouvez spécifier des options supplémentaires en plus du nom de bloc d'annonces de l'éditeur. Ces options vous permettent de récupérer le message publié le plus récemment avant de vous abonner et de définir l'intervalle de temps minimal entre les notifications.

ASDVGateway_SubscribeToPublicationByNameParams_t params{
    .sdvVmName           = "vm1",
    .packageName         = "test.package.impl.name",
    .serviceBundleName   = "TestBundleImpl",
    .serviceUnitName     = "GrpcServerImpl",
};

ASDVGateway_Subscriber_Options_t options{
    // Ensure we receive the most recent message immediately upon subscribing
    .flags         = ASDVGATEWAY_SUBSCRIBER_OPTIONS_FLAG_FETCHLASTMESSAGE,
    .minIntervalMs = 0, // No rate limiting; receive updates as they happen
};

// 1. Prepare the context for the callback
auto subCtx = std::make_unique<SubscriptionContext>();
ASDVGateway_PublicationMetadata_t metadata{};

// 2. Perform the subscription
ASDVGateway_StatusCode_t statusCode = ASDVGateway_Client_subscribeToPublicationByName(
    client,
    &params,
    &options,
    NativeSdvGatewayTestApp::SubscriptionNotificationCallback,
    static_cast<void*>(subCtx.get()), // Pass the raw pointer as userData
    &metadata,
    &status
);

// 3. Validation and Lifecycle Management
if (status.statusCode != ASDVGateway_StatusCode_OK) {
    // Error handling: subCtx will be automatically deleted here
    std::cerr << "Subscription failed: " << status.errorMessage << std::endl;
    return;
}

// Store the context using the publicationId as the key.
// Once moved, the map owns the lifetime of subCtx.
app.mSubscriptions.emplace(metadata.publicationId, std::move(subCtx));

Utilisez l'API ASDVGateway_Client_readAvailableMessages pour lire les messages de la publication :

uint32_t messagesAvailable = 0;

// 1. Check how many messages are waiting in the queue
ASDVGateway_StatusCode_t availStatus = ASDVGateway_Client_availableToRead(
    client,
    metadata.publicationId,
    &messagesAvailable,
    &status
);

if (status.statusCode != ASDVGateway_StatusCode_OK) {
    // Error handling for availability check
    return;
}

if (messagesAvailable == 0) {
    // No messages available for this publication at this time
    return;
}

// 2. Prepare the buffer
// metadata.messageSizeBytes was provided during the publication/subscription setup
std::vector<uint8_t> bytesForMessages;
bytesForMessages.resize(metadata.messageSizeBytes * messagesAvailable);

// 3. Read the messages from the Gateway into your local buffer
uint32_t actualMessageCount = 0; // Filled by the SDK with the number of messages read
ASDVGateway_StatusCode_t readStatus = ASDVGateway_Client_readAvailableMessages(
    client,
    metadata.publicationId,
    bytesForMessages.data(),
    bytesForMessages.size(),
    &actualMessageCount,
    &status
);

if (status.statusCode == ASDVGateway_StatusCode_OK) {
    // Successfully read 'actualMessageCount' messages
    // Process bytesForMessages...
}

Pour définir le rappel après l'abonnement, utilisez l'API ASDVGateway_Client_setNotificationCallbackForPublicationId :

ASDVGateway_StatusCode_t ASDVGateway_Client_setNotificationCallbackForPublicationId(
    // [in]  Opaque pointer to the client object.
    const ASDVGateway_Client* client,

    // [in]  Identifies the specific publication to monitor.
    const int32_t publicationId,

    // [in]  Function pointer triggered when new data is available
    //       for the specified publication.
    ASDVGateway_SubscriptionNotificationCallback notificationCallback,

    // [in]  Optional user-defined context passed back to the callback.
    void* notificationCallbackUserData,

    // [out] Optional pointer to a status structure for result codes
    //       and error messages.
    ASDVGateway_Status_t* outStatus
);

Configurer le service init

Tout d'abord, suivez la section Identité pour les services natifs pour attribuer une identité de service au service via son ID Android (AID).

Supposons que votre service s'appelle native_sdv_gateway_client_service, que l'exécutable se trouve sur la partition /vendor à l'adresse /vendor/bin/native_sdv_gateway_client_service et que vous utilisez vendor_gateway_client comme ID Android (AID) pour exécuter le service. Avec cette configuration, vous pouvez définir le service d'initialisation suivant :

service native_sdv_gateway_client_service /vendor/bin/native_sdv_gateway_client_service
    class core
    user vendor_gateway_client
    group inet
    disabled
    oneshot

Cette configuration utilise les paramètres suivants :

  • vendor_gateway_client correspond à l'AID créé ou sélectionné pour le service.
  • group inet est requis pour les clients de passerelle SDV afin d'utiliser l'interface réseau pour communiquer entre les VM SDV. D'autres groupes peuvent être ajoutés si nécessaire.
  • Cet exemple utilise disabled et oneshot. Vous devrez peut-être ajuster les options de service pour votre service. Démarrez le service après sdv_gateway.

Créer des règles SELinux pour le service

Pour utiliser les API SDV Gateway, vous avez besoin des règles SELinux suivantes pour le service :

# Define the domain for the service
type native_sdv_gateway_client_service, domain;

# Define the executable file type on the vendor partition
type native_sdv_gateway_client_service_exec, exec_type, file_type, vendor_file_type;

# Macro to transition from 'init' to this service's domain upon execution
init_daemon_domain(native_sdv_gateway_client_service)

# Macro to grant the necessary permissions to communicate with the SDV Gateway
sdv_gateway_client_domain(native_sdv_gateway_client_service)

Dans les règles SELinux :

  • init_daemon_domain permet de démarrer le service à partir de init.

  • sdv_gateway_client_domain fournit toutes les autorisations SELinux nécessaires pour interagir avec la passerelle SDV. La ligne suivante accorde ces règles à l'exécutable :

    /vendor/bin/native_sdv_gateway_client_service    u:object_r:native_sdv_gateway_client_service_exec:s0
    

Exemple de code

Pour en savoir plus sur l'exécution de l'exemple de code natif qui montre comment les API C sont documentées, consultez system/software_defined_vehicle/samples/sdv_gateway/NativeSdvGatewayTestApp/README.md.

Application Java de la passerelle SDV sur IVI

Le modèle d'interaction pour une passerelle SDV sur IVI est illustré dans ce schéma :

Application Java de la passerelle SDV sur IVI

Figure 4. Application Java de la passerelle SDV sur IVI.

Plus précisément, le modèle :

  • Distribue tous les appels à la couche JNI.
  • Colle les API Java et C.
  • Définit les interactions AIDL avec ISdvGateway :
    • Init comms
    • Trouver ou créer le serveur RPC
    • Créer le Pub/Sub
    • Effectuer des interactions de découverte de services Data Tunnel
    • Recevoir des notifications (par exemple, données disponibles)
    • Lire et écrire des messages (FMQ pour Pub/Sub) pour la création de paires de clés et de certificats pour TLS

Inclure les bibliothèques clientes Gateway

La bibliothèque Java et le wrapper JNI pour l'API C libsdvgatewayclient sont installés dans un APEX sur la cible IVI. Ajoutez une dépendance au temps de compilation au stub de la bibliothèque Java, à la bibliothèque Java qui doit être utilisée au moment de l'exécution et à l'APEX requis contenant la bibliothèque Java.

android_app {
    name: "YourAppName",
    // ...
    static_libs: [
        "libsdvgatewayclient-java",
    ],
    libs: [
        "libsdvgatewayclient-java-sdk.stubs",
    ],
    uses_libs: [
        "libsdvgatewayclient-java-sdk",
    ],
    required: [
        "com.sdv.google.gateway.client",
    ],
    // ...
}

Dans le cas d'une application non groupée créée en dehors de l'arborescence SDV, le processus est similaire :

  1. Copiez le fichier JAR du stub de bibliothèque Java généré et le fichier JAR d'assistance pour RPC dans le dossier des bibliothèques d'application. Pour en savoir plus, consultez system/software_defined_vehicle/sdv_gateway/libsdvgatewayclient_apex/README.md.

  2. Ajoutez le fichier JAR des stubs en tant que dépendance de compilation uniquement. Par exemple, mettez à jour les configurations Gradle pour qu'elles dépendent des stubs en ajoutant une entrée compileOnly à la section dependencies :

    dependencies {
        // The library supporting functions for SDK RPC.
        // Statically linked into the app APK.
        implementation(files("libs/libsdvgatewayclient-java.jar"))
    
        // Stub of the SDV-Gateway client library.
        // Used only for compilation; the real implementation is provided 
        // by the com.sdv.google.gateway.client APEX at runtime.
        compileOnly(files("libs/libsdvgatewayclient-java-sdk.jar"))
    }
    
  3. Ajoutez la bibliothèque Java à la section "app" du fichier AndroidManifest.xml.

    <manifest xmlns:android="http://schemas.android.com/apk/res/android">
    
        <application>
    
            <!--
              Declares that this app requires the SDV Gateway client library.
              The 'android:required="true"' attribute ensures the app won't
              install/run if the library is missing from the system.
            -->
            <uses-library
                android:name="libsdvgatewayclient-java-sdk"
                android:required="true" />
    
        </application>
    
    </manifest>
    

Charger les bibliothèques

Créez un objet SdvGatewayClient (fourni par la bibliothèque cliente) :

import google.sdv.gateway.client.SdvGatewayClient;

// --- Inside your Activity, Service, or ViewModel ---

// Initialize the SDV Gateway Client
SdvGatewayClient gatewayClient = new SdvGatewayClient();

Communications initiales

Appelez initComms() en utilisant un nom d'application comme nom du bundle de services :

// Define a unique name for your service bundle (usually constant)
private static final String SERVICE_BUNDLE_NAME = "MySdvServiceBundle";

// ... inside an Activity or Service context ...

try {
    // Initialize communications with the SDV Gateway
    // context.getPackageName() provides "android.sdv.samples.gateway.client" or similar
    gatewayClient.initComms(context.getPackageName(), SERVICE_BUNDLE_NAME);

    Log.i("SDV_GATEWAY", "Communications initialized successfully");
} catch (Exception e) {
    // Unlike the C API which uses status codes,
    // the Java SDK often throws exceptions for initialization failures.
    Log.e("SDV_GATEWAY", "Failed to initialize communications", e);
}

Découverte des services

L'API Java contient des méthodes permettant d'effectuer les actions suivantes :

  • Recevez une notification lorsque des unités de service avec un type d'unité spécifique (ou, à la place, correspondant à un nom spécifique) sont enregistrées ou désenregistrées.

  • Répertoriez les unités de service actuelles avec un type d'unité spécifique (ou faites correspondre un nom de type d'unité de service spécifique). Pour être averti des modifications apportées aux unités de service, créez un écouteur :

import google.sdv.gateway.client.ServiceUnitChangeListener;
import google.sdv.gateway.client.ServiceUnitChangeEventType;
import google.sdv.gateway.client.ServiceUnitDefinition;

// --- Implementation ---

ServiceUnitChangeListener listener = new ServiceUnitChangeListener() {
    @Override
    public void onServiceUnitChanged(
        ServiceUnitChangeEventType eventType,
        ServiceUnitDefinition serviceUnitDefinition
    ) {
        // This is triggered when services matching your criteria
        // are registered or unregistered on the vehicle network.
        if (eventType == ServiceUnitChangeEventType.REGISTERED) {
            // Handle new service discovery
        } else if (eventType == ServiceUnitChangeEventType.UNREGISTERED) {
            // Handle service removal
        }
    }
};

La méthode Java addListenerForServiceUnitChangeByName avertit l'écouteur :

// 1. Register the listener for a specific service unit by name
AutoCloseable handle = gatewayClient.addListenerForServiceUnitChangeByName(
    new UnitNameDiscoveryArgs(
        "",                                   // sdvVmName (empty for local/auto-discovery)
        "android.sdv.samples.cluster",        // sdvPackageName
        "ClusterServer",                      // serviceBundleName
        "android-sdv-samples-cluster-cluster" // serviceUnitName
    ),
    listener
);

// --- Later in the application lifecycle ---

// 2. To stop receiving notifications and clean up resources, close the handle.
try {
    if (handle != null) {
        handle.close();
    }
} catch (Exception e) {
    Log.e("SDV_GATEWAY", "Error while closing the listener handle", e);
}

Vous pouvez également utiliser la méthode Java addListenerForServiceUnitChangeByType pour notifier l'écouteur lorsque des services avec le type d'unité spécifié sont enregistrés ou désenregistrés :

// 1. Register a listener based on the Service Unit Type
AutoCloseable handle = gatewayClient.addListenerForServiceUnitChangeByType(
    new UnitType(
        "com.android.testapp.sdvcarmonitor", // sdvPackageName
        "SunroofRpcServer",                  // serviceBundleName
        "Sunroof"                            // unitTypeName
    ),
    listener
);

// --- Execution Loop / Lifecycle ---

// 2. To stop receiving notifications and clean up memory, close the handle.
// This effectively unregisters the listener from the SDV Gateway.
try {
    if (handle != null) {
        handle.close();
    }
} catch (Exception e) {
    Log.e("SDV_GATEWAY", "Failed to close the service unit listener handle", e);
}

Pour addListenerForServiceUnitChangeByName et addListenerForServiceUnitChangeByType, une fois l'écouteur ajouté, il est averti de toutes les unités de service enregistrées. Pour n'obtenir que les unités de service enregistrées par nom ou par type, utilisez les API Java listServiceUnitsByName et listServiceUnitsByType :

import google.sdv.gateway.client.ServiceUnitDefinition;
import google.sdv.gateway.client.UnitNameDiscoveryArgs;
import google.sdv.gateway.client.UnitType;

// --- 1. Synchronous Lookup by Specific Name ---
ServiceUnitDefinition[] definitionsByName = gatewayClient.listServiceUnitsByName(
    new UnitNameDiscoveryArgs(
        "",                                   // sdvVmName
        "android.sdv.samples.cluster",        // sdvPackageName
        "ClusterServer",                      // serviceBundleName
        "android-sdv-samples-cluster-cluster" // serviceUnitName
    )
);

// --- 2. Synchronous Lookup by Service Type ---
ServiceUnitDefinition[] definitionsByType = gatewayClient.listServiceUnitsByType(
    new UnitType(
        "com.android.testapp.sdvcarmonitor", // sdvPackageName
        "SunroofRpcServer",                  // serviceBundleName
        "Sunroof"                            // unitTypeName
    )
);

// Example processing
if (definitionsByType.length > 0) {
    ServiceUnitDefinition firstSunroof = definitionsByType[0];
    // Proceed to connect...
}

Flux du serveur RPC

Les clients de la passerelle SDV dans les systèmes IVI utilisent l'appel de procédure à distance (gRPC) de Google pour communiquer avec les services SDV. Ces interactions s'appuient sur des définitions de proto du catalogue VSIDL, qui sont cohérentes ou similaires à celles utilisées dans SDV Core. Pour les applications Java, l'implémentation choisie est gRPC-Java. Un exemple de définition proto de serveur, sunroof.proto, est fourni pour un serveur d'application.

service Sunroof {
    /**
     * Retrieves the current state of the sunroof (e.g., position, tilt, status).
     *
     * @param .google.protobuf.Empty - No input parameters required.
     * @return SunroofStateResponse - The current telemetry data for the sunroof.
     */
    rpc GetSunroofState(.google.protobuf.Empty) returns (SunroofStateResponse) {}
}

Associez-le à la bibliothèque proto correspondante et définissez le service :

import com.android.sdv.sdvgrpclibrary.SunroofGrpc;
import com.android.sdv.sdvgrpclibrary.SunroofStateResponse; // Assuming this is the generated class
import com.google.protobuf.Empty;
import io.grpc.stub.StreamObserver;

/**
 * Implementation of the Sunroof gRPC service.
 * This class handles the logic for the RPCs defined in your .proto file.
 */
static class SunroofGrpcImpl extends SunroofGrpc.SunroofImplBase {

    @Override
    public void getSunroofState(Empty request, StreamObserver<SunroofStateResponse> responseObserver) {
        // 1. Fetch current sunroof data (e.g., from a Hardware Abstraction Layer)
        int currentPosition = 50; // Example value: 50% open

        // 2. Build the Protobuf response message
        SunroofStateResponse response = SunroofStateResponse.newBuilder()
                .setPercentageOpen(currentPosition)
                .build();

        // 3. Send the response to the client using the observer
        responseObserver.onNext(response);

        // 4. Close the stream to signal that the RPC is finished
        responseObserver.onCompleted();
    }
}

Enregistrez le serveur gRPC avec des identifiants de canal sécurisés et non sécurisés :

// 1. Define the type signature for the service
UnitType unitType = new UnitType(
    "com.android.testapp.sdvcarmonitor", // sdvPackageName
    "SunroofRpcServer",                  // serviceBundleName
    "Sunroof"                            // typeName (Unit Type)
);

// 2. Register the RPC server with the Gateway
// The gateway creates a mapping between the ServiceUnitName and your implementation.
server = gatewayClient.registerRpcServer(
    "SunroofRpcServerImpl-1",                       // serviceUnitName (Unique instance name)
    unitType,                                       // unitType defined earlier
    "SUNROOF_GRPC_SERVER_VALUE_HOLDER".getBytes(),  // appMetadataValueHolder (Static discovery data)
    1,                                              // appMetadataVersion
    new SunroofGrpcImpl()                           // The actual gRPC service implementation
);

En interne, la bibliothèque cliente crée l'objet serveur gRPC, SdvGatewayClient.java, et gère également les mises à jour des certificats racine :

// 1. Initialize credentials (Insecure for dev, TLS for production)
ServerCredentials serverCredentials = InsecureServerCredentials.create();

// 2. Build and start the OkHttp-based gRPC server
final int bindAnyPort = 0;
final Server server = OkHttpServerBuilder
    .forPort(bindAnyPort, serverCredentials)
    .addService(gRpcServerImplementation) // Your SunroofGrpcImpl
    .build()
    .start();

// The assigned port can now be retrieved using server.getPort()
int actualPort = server.getPort();

// 3. Prepare the JNI data structure
// This object mirrors the ASDVGateway_ServiceUnitDefinition_t C struct
JniServiceUnitDefinition definition = new JniServiceUnitDefinition();

// Fill the RPC service definition params (Port, Name, Type, etc.)
definition.setPort(actualPort);
definition.setServiceUnitName("SunroofRpcServerImpl-1");

// 4. Perform the cross-language call
// This jumps from Java -> JNI -> ASDVGateway_Client_registerRpcServer (C API)
mJniClient.nativeRegisterRpcServer(definition);

Flux du client RPC

Cet exemple de code fournit la définition proto du serveur (tpms.proto) pour le serveur auquel l'application se connecte en tant que client :

/**
 * The TPMS service provides real-time pressure and temperature
 * data for all tires on the vehicle.
 */
service Tpms {
    /**
     * Returns the full state of all monitored tires.
     */
    rpc GetTpmsState(.google.protobuf.Empty) returns (TpmsStateResponse) {}

    /**
     * A filtered query that returns only the tires
     * below the recommended pressure threshold.
     */
    rpc GetLowTires(.google.protobuf.Empty) returns (LowTiresResponse) {}
}

Associez-le à la bibliothèque proto correspondante :

import com.android.sdv.sdvgrpclibrary.TpmsGrpc;
import io.grpc.ManagedChannel;

// --- Inside your Client Application ---

// 1. Request a ManagedChannel from the Gateway for a specific service unit
ManagedChannel managedChannel = gatewayClient.connectToRpcServerByName(
    "",                                     // sdvVmName (empty for local/auto-lookup)
    "android.sdv.samples.cluster",          // packageName
    "ClusterServer",                        // serviceBundleName
    "android-sdv-samples-cluster-cluster"  // serviceUnitName
);

// 2. Use the channel to create a gRPC stub (e.g., for the TPMS service)
TpmsGrpc.TpmsBlockingStub tpmsStub = TpmsGrpc.newBlockingStub(managedChannel);

// 3. Now you can call RPC methods directly
// TpmsStateResponse response = tpmsStub.getTpmsState(Empty.getDefaultInstance());

En interne, l'API ASDVGateway_Client_findRpcServerByName est appelée pour trouver le serveur RPC. Si le serveur RPC est trouvé, le canal géré est créé en mode non sécurisé ou désigné pour utiliser la configuration TLS, de la même manière que le flux du serveur RPC, en fonction de la configuration Service Discovery. L'application crée les stubs avec l'objet ManagedChannel et appelle les méthodes du serveur :

import com.android.sdv.sdvgrpclibrary.TpmsGrpc;
import com.android.sdv.sdvgrpclibrary.TpmsStateResponse;
import com.google.protobuf.Empty;
import io.grpc.stub.MetadataUtils;

// 1. Create a "Blocking Stub" from the existing ManagedChannel.
// We apply an interceptor to attach mandatory metadata (headers)
// required by the SDV Gateway for authorization.
TpmsGrpc.TpmsBlockingStub tpmsStub = TpmsGrpc.newBlockingStub(managedChannel)
    .withInterceptors(MetadataUtils.newAttachHeadersInterceptor(mMetadata));

// 2. Execute the RPC call.
// Because this is a "BlockingStub," the thread will wait here until
// the vehicle service responds or times out.
TpmsStateResponse tpmsStateResponse = tpmsStub.getTpmsState(Empty.getDefaultInstance());

// 3. Extract the domain-specific state object from the Protobuf response.
// 'newState' can now be used to update your UI or application logic.
newState = tpmsStateResponse.getTpmsState();

Changer le réseau par défaut du processus

Vous devrez peut-être passer du VLAN SDV-RPC en tant que réseau par défaut du processus pour ouvrir des sockets pour les connexions liées à Internet. Appelez unbindProcessFromSdvRpcNetworkInterface et bindProcessToSdvRpcNetworkInterface pour dissocier et réassocier le processus au VLAN SDV-RPC. Les deux appels agissent comme des commutateurs globaux, en changeant l'interface réseau à laquelle les sockets sont liés pour tous les threads du processus.

// 1. Redirect all socket traffic from this process to the SDV-RPC VLAN.
// This call is required for making RPC calls or hosting RPC services for SDV.
gatewayClient.bindProcessToSdvRpcNetworkInterface();

// --- Process is now communicating over the SDV-RPC interface ---

// 2. Revert the process network binding back to the "default" interface.
// This allows the app to access internet resources again.
gatewayClient.unbindProcessFromSdvRpcNetworkInterface();

Créer un éditeur et publier des messages

// 1. Define the interface and type for the publication
UnitType unitType = new UnitType(
    "android.sdv.samples.tires.interface", // sdvPackageName
    "TirePressurePublisherInterface",      // serviceBundleName
    "TirePressure"                         // typeName
);

// 2. Configure the Publisher's buffer and message constraints
PublisherUnitMetadata publisherUnitMetadata = new PublisherUnitMetadata(
    1,   // version
    64,  // message size in bytes (fixed size for performance)
    128  // max message count (buffer depth)
);

// 3. Create the Publication instance through the Gateway
Publisher tirePublisher = gatewayClient.createPublication(
    "tire-pressure-service-unit-name",
    unitType,
    publisherUnitMetadata
);

// 4. Prepare and publish a message
// In a real app, you would encode your sensor data into this byte array
byte[] msg = new byte[publisherUnitMetadata.messageSizeBytes];

// ... fill msg with data ...

tirePublisher.publish(msg);

En interne, les API Java createPublication et de publication s'appuient sur les API natives ASDVGateway_Client_createPublication et ASDVGateway_Client_readAvailableMessages. Pour en savoir plus sur l'API C, consultez Utiliser SDV Gateway sur un service natif IVI. L'objet Publisher fournit un contexte pour la rédaction de messages et la gestion du cycle de vie de la publication.

Créer un abonné avec un outil d'écoute des notifications

L'API Java permet de transmettre un écouteur en tant que paramètre à la méthode d'abonnement à la publication et renvoie un objet Subscription.

  • Listener reçoit une notification lorsque des données sont disponibles pour la publication à laquelle il est abonné.

  • Subscription sert d'objet de contexte et peut être utilisé pour lire des messages et fermer l'abonnement.

// 1. Define the Listener to handle incoming data notifications
SubscriptionNotificationListener listener = new SubscriptionNotificationListener() {
    @Override
    public void onSubscriptionNotification(
        Subscription subscription,
        SubscriptionNotificationType notificationType
    ) {
        // Only process if the notification indicates new data is ready
        if (notificationType != SubscriptionNotificationType.DataAvailable) {
            return;
        }

        // Read the message from the subscription buffer
        byte[] content = subscription.readMessage();

        // Note: This callback often runs on a background thread provided by the SDK.
        // If you need to update the UI, use a Handler or View.post().
        processTireData(content);
    }
};

// 2. Subscribe to the publication by its unique name
Subscription tireSubscription = gatewayClient.subscribeToPublicationByName(
    "",                                  // sdvVmName (empty for auto-lookup)
    "android.sdv.samples.dt_publisher",  // packageName
    "SdvGatewayDtPublisher",             // serviceBundleName
    "tire",                              // serviceUnitName
    listener
);

// 3. Read Messages
// You can poll or register callbacks for new messages.
// When polling the message, call this method in a loop.
// When registering callbacks, call this method to get the message payload.
byte[] manualContent = tireSubscription.readMessage();

Exemple de code

Les rappels d'écouteur sont appelés sur un seul thread géré par le client pour minimiser le traitement dans les écouteurs afin d'éviter les retards dans la réception des notifications pour les autres abonnements. La couche Java utilise une API C pour la gestion des abonnements, le traitement des notifications et la récupération des messages. Pour une démonstration de l'utilisation de l'API, consultez l'exemple d'application Java fourni dans le fichier binaire à l'adresse system/software_defined_vehicle/samples/sdv_gateway/README.md.

Autorisations requises

L'appel de l'API client SDV Gateway ne nécessite pas d'autorisations spéciales, mais vous devez appliquer les règles SELinux appropriées pour vos applications.

  • Pour les abonnements et les publications de tunnels de données, vous n'avez besoin d'aucune autorisation.
  • Pour SDV RPC, vous devez disposer des autorisations suivantes :
    • android.permission.INTERNET
    • android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS

Pour android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS, vous avez également besoin d'un fichier de liste d'autorisation sous etc/permissions dans la même partition que votre application. Par exemple, pour SdvCarMonitorTestApp (nom du package com.android.testapp.sdvcarmonitor), le fichier se présente comme suit :

<?xml version="1.0" encoding="utf-8"?>

<permissions>
    <privapp-permissions package="com.android.testapp.sdvcarmonitor">
        <permission name="android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS"/>
    </privapp-permissions>
</permissions>

Règles SELinux

Pour utiliser les API SDV Gateway, les applications Java nécessitent les mêmes autorisations que les services natifs. Accordez ces autorisations à l'aide de la macro SELinux sdv_gateway_client_domain() :

sdv_gateway_client_domain(my_oem_sdv_gateway_client_app)

L'OEM définit le domaine my_oem_sdv_gateway_client_app pour les applications Java autorisées à utiliser la passerelle SDV. Utilisez la passerelle SDV uniquement à partir des applications système et privilégiées.

Emplacements de code

Obtenez le code source de la passerelle SDV sur system/software_defined_vehicle/sdv_gateway/. Vous pouvez obtenir des exemples de passerelle SDV pour :

  • API C du client : system/software_defined_vehicle/samples/sdv_gateway/NativeSdvGatewayTestApp/
  • API Java du client : system/software_defined_vehicle/samples/sdv_gateway/SdvCarMonitorTestApp/