เกตเวย์ยานพาหนะที่กำหนดโดยซอฟต์แวร์ (SDV) ในระบบสาระบันเทิงในรถยนต์ (IVI) ช่วยให้การสื่อสารระหว่างระบบ IVI กับบริการ SDV ระยะไกลเป็นไปได้ เกตเวย์ช่วยให้แอป Java ของ OEM และบริการที่มาพร้อมเครื่อง เช่น VHAL โต้ตอบกับบริการ SDV ได้ เกตเวย์ใช้วิธีการสื่อสารที่กำหนดไว้ ซึ่งรวมถึง การลงทะเบียนบริการ การค้นหา และ RPC
เกตเวย์นี้เป็นไปตามข้อกำหนดเฉพาะของโปรเจ็กต์ SDV ของ AAOS เช่น การเปิดใช้การติดตั้งใช้งานอ้างอิง VHAL โดยใช้ SDV Data Tunnel สำหรับข้อมูลพร็อพเพอร์ตี้ นอกจากนี้ยังช่วยให้แอป Android ที่เขียนด้วย Java และ Kotlin บน IVI ใช้สแต็ก Comms ของ SDV, ลงทะเบียนเป็นบริการ, ค้นหาบริการ SDV อื่นๆ และสื่อสารกับบริการเหล่านั้นได้ด้วย
ดูตำแหน่งโค้ดของ SDV Gateway และไคลเอ็นต์ SDV Gateway ตัวอย่างได้ที่ ตำแหน่งโค้ด
รูปแบบการผสานรวมเกตเวย์ SDV
ใช้ SDV-IVI Comms ผ่าน SDV Gateway ในสถาปัตยกรรม IVI
SDV Gateway จะโต้ตอบกับแอป Java, บริการดั้งเดิม, สแต็ก SDV Comms และเครือข่ายของยานพาหนะ รูปที่ 1 แสดงการโต้ตอบเหล่านี้
รูปที่ 1 การโต้ตอบกับเกตเวย์ SDV
ในแผนภาพระบบนี้
- แอปจะโต้ตอบกับ SDV Gateway ผ่านบริการไคลเอ็นต์
- AAOS SDV SDK มีสิ่งต่อไปนี้ให้คุณ
- ISdvGateway AIDL API สำหรับการสื่อสารระหว่างโปรเซส
- ไลบรารี Comms สำหรับการโต้ตอบในเครือข่าย
- C API ที่สะดวกสำหรับการผสานรวมบริการดั้งเดิม
- บริการและระบบย่อยของ SDV Gateway จะใช้ AIDL API ของ ISdvGateway
- บริการ SDV Gateway จะจัดการสิ่งต่อไปนี้
- Service Discovery แบบ 2 ทิศทาง
- การสื่อสารกับบริการ SDV ระยะไกล
- ตรรกะทางธุรกิจหลัก
- ระบบย่อยของเกตเวย์ SDV จะเชื่อมต่อกับเครือข่ายยานพาหนะ
- บริการเนทีฟ รวมถึงการติดตั้งใช้งาน VHAL สามารถใช้ ISdvGateway AIDL API โดยตรงหรือผ่าน C API ของ SDK
- พร็อกซี VHAL ทำหน้าที่เป็นการติดตั้งใช้งาน VHAL อ้างอิง ซึ่งรวมการผสานรวมการแมป VSIDL
รูปแบบการผสานรวมสำหรับ SDV Gateway ในบริการดั้งเดิมของ IVI
โมเดลการผสานรวมแสดงในรูปที่ 2 ดังนี้
รูปที่ 2 รูปแบบการผสานรวมเกตเวย์ SDV
ใช้ SDV Gateway ในบริการดั้งเดิมของ IVI
รูปที่ 3 แสดงการใช้เกตเวย์ SDV ใน IVI
รูปที่ 3 SDV Gateway ใน IVI
เงื่อนไขเบื้องต้น
เริ่มกลุ่มเทรด Binder
ไลบรารีของไคลเอ็นต์ SDV Gateway ต้องใช้พูลเธรด Binder ที่เริ่มต้นแล้วเพื่อรับ การเรียกกลับแบบอะซิงโครนัสจากบริการ Binder
API ที่จำเป็นต่อการสร้างไคลเอ็นต์เกตเวย์ SDV จะทำงานไม่สำเร็จเมื่อไม่ได้เริ่มพูลเธรด Binder
รวมไลบรารีของไคลเอ็นต์เกตเวย์แบบเนทีฟ
ไลบรารีของไคลเอ็นต์เกตเวย์เนทีฟแสดง C API เพิ่มอินสแตนซ์ของ
libsdvgatewayclientเป็นการอ้างอิงเพื่อใช้ C API ดังนี้
cc_binary {
name: "your_binary_name",
srcs: ["main.cpp"],
shared_libs: [
"libsdvgatewayclient",
],
}
โหลดไคลเอ็นต์เกตเวย์ดั้งเดิม
#include "libsdvgatewayclient.h"
สร้างอินสแตนซ์ไคลเอ็นต์เนทีฟ
ASDVGateway_Client* client;
ASDVGateway_StatusCode_t statusCode = ASDVGateway_Client_new(
&client, /*outStatus*/nullptr);
หลังจากสร้างไคลเอ็นต์แล้ว ไคลเอ็นต์จะทำสิ่งต่อไปนี้
เก็บสถานะสำหรับการโต้ตอบในภายหลังทั้งหมดกับบริการ Gateway
ทําหน้าที่เป็นบริบทของการโต้ตอบทั้งหมดกับบริการอื่นๆ ที่เปิดใช้ SDV และ ส่งเป็นพารามิเตอร์แรกไปยังฟังก์ชัน C API
รหัสสถานะและข้อความแสดงข้อผิดพลาด
ฟังก์ชัน C API ส่วนใหญ่มีคำจำกัดความดังนี้
ASDVGateway_StatusCode_t ApiFunctionName(..., ASDVGateway_Status_t* outStatus);
หากต้องการประเมินความสำเร็จ คุณสามารถตรวจสอบรหัสสถานะที่ส่งคืน ซึ่งมีประเภทเป็น ASDVGateway_StatusCode_t นอกจากนี้ คุณยังส่งพอยน์เตอร์ไปยังโครงสร้าง
ที่ฟังก์ชันสามารถป้อนรหัสสถานะและข้อความแสดงข้อผิดพลาดได้ด้วย ระบบจะส่งผ่านพอยน์เตอร์เป็นพารามิเตอร์สุดท้ายชื่อ outStatus ค่า Null หมายความว่าจะไม่มีการใช้
โครงสร้างเอาต์พุต
ผู้เรียกใช้ต้องจัดสรรโครงสร้างสถานะหน่วยความจำสำหรับข้อความแสดงข้อผิดพลาด โครงสร้างสถานะสามารถเก็บได้ทั้งรหัสสถานะและข้อความแสดงข้อผิดพลาด ตัวอย่าง การเรียกข้อความแสดงข้อผิดพลาดเมื่อสร้างไคลเอ็นต์ใหม่
วิธีประเมินความสำเร็จ
ตรวจสอบรหัสสถานะที่ส่งคืน ซึ่งเป็นประเภท
ASDVGateway_StatusCode_tส่ง Pointer ไปยังโครงสร้างที่ฟังก์ชันสามารถใส่รหัสสถานะและข้อความแสดงข้อผิดพลาด
- ระบบจะส่งผ่านพอยน์เตอร์เป็นพารามิเตอร์สุดท้ายชื่อ
outStatus - ค่า Null หมายความว่าจะไม่มีการใช้โครงสร้างเอาต์พุต
- ผู้เรียกใช้ต้องจัดสรรโครงสร้างสถานะหน่วยความจำสำหรับข้อความแสดงข้อผิดพลาด
- โครงสร้างสถานะสามารถเก็บรหัสสถานะและข้อความแสดงข้อผิดพลาดได้
- ระบบจะส่งผ่านพอยน์เตอร์เป็นพารามิเตอร์สุดท้ายชื่อ
ต่อไปนี้คือตัวอย่างที่แสดงวิธีดึงข้อความแสดงข้อผิดพลาดสำหรับไคลเอ็นต์ใหม่
#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;
ในตัวอย่างนี้
รหัสสถานะที่อยู่ในโครงสร้างสถานะมีค่าเหมือนกับรหัสสถานะที่ส่งคืน
ระบบจะป้อนข้อความแสดงข้อผิดพลาดจนถึง
maxErrorMessageSizeขีดจํากัดอักขระ (รวมถึงอักขระสิ้นสุดค่าว่าง\0) หากไม่มีข้อผิดพลาด (รหัสสถานะคือOK) ข้อความแสดงข้อผิดพลาดจะเป็นสตริงว่าง
การสื่อสารเริ่มต้น
Init comms จะเริ่มต้นการสื่อสารระหว่างแอปที่เรียกใช้กับแอปอื่นๆ โดยใช้ SDV Comm Stack และ SDV Gateway สามารถเรียกใช้ Init comms ได้ในบริบทต่อไปนี้
หลังจากสร้างไคลเอ็นต์แล้ว
ก่อนการโต้ตอบ Data Tunnel, RPC หรือ Service Discovery
ตัวอย่าง
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, ¶ms, &status);
// Recommended check
if (statusCode != ASDVGateway_StatusCode_OK) {
std::cerr << "Failed to init comms: " << status.errorMessage << std::endl;
}
Service Discovery
คุณจะได้รับการแจ้งเตือนเมื่อมีการลงทะเบียนหรือยกเลิกการลงทะเบียนหน่วยบริการที่มีประเภทหรือชื่อที่เฉพาะเจาะจง ระบบจะแจ้งฟังก์ชัน Callback ในเทรดที่ไคลเอ็นต์เกตเวย์เป็นเจ้าของ การเรียกกลับนี้จะทริกเกอร์ครั้งแรกโดยมีหน่วยบริการทั้งหมด ที่ลงทะเบียนทันทีหลังจากการเรียก C API หลังจากทริกเกอร์เริ่มต้น การแจ้งเตือนจะอัปเดตต่อไปจนกว่าผู้ฟังจะยกเลิกการลงทะเบียนอย่างชัดเจน
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
);
หากต้องการดึงข้อมูลหน่วยบริการที่ลงทะเบียนโดยไม่ต้องมีการแจ้งเตือนเพิ่มเติม ให้ใช้ API
ASDVGateway_Client_fetchServiceUnitsByType และ
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
);
ระบบจะทริกเกอร์การเรียกกลับแบบซิงโครนัสในเธรดของผู้เรียกใช้ระหว่างASDVGateway_Client_fetchServiceUnitsByTypeการเรียก API ใช้
ASDVGateway_Client_fetchServiceUnitsByName เพื่อรับหน่วยบริการที่ลงทะเบียน
ตามชื่อแทนที่จะตามประเภทหน่วย
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
);
โฟลว์ RPC
ไลบรารีไคลเอ็นต์ของเกตเวย์จะจัดการการตั้งค่า Transport Layer Security (TLS) สำหรับ การสื่อสารกับแอปอื่นๆ ที่เปิดใช้ SDV โดยจะดึงข้อมูลการตั้งค่า TLS ที่จำเป็นสำหรับการสื่อสาร ระบบจะพิจารณาการใช้งาน TLS ดังนี้
TLS จะใช้เมื่อโหมดการบูต SDV เป็น
LOCKEDใช้การสื่อสารไม่ปลอดภัยเมื่อโหมดการบูต SDV เป็น
UNLOCKEDเมื่อใช้ TLS ไลบรารีไคลเอ็นต์ของเกตเวย์จะสร้างคู่คีย์ที่รู้จักเฉพาะกระบวนการของแอป ไคลเอ็นต์จะดึงข้อมูลเข้าสู่ระบบ RPC เพื่อสร้าง เซิร์ฟเวอร์ RPC หรือแชแนลไคลเอ็นต์ RPC RPC ใช้ VLAN ของ SDV-RPC โดยเฉพาะ ไลบรารีไคลเอ็นต์ของเกตเวย์จะเรียกใช้ android_setprocnetwork เพื่อเปลี่ยน เครือข่ายเริ่มต้นของกระบวนการเป็น VLAN ของ SDV-RPC ไม่ว่าจะในระหว่างหรือหลังจาก การเรียกใช้ASDVGateway_Client_initComms
ความพร้อมใช้งานของ RPC
ไคลเอ็นต์ที่กำหนดค่าให้เริ่มในระหว่างการเปิดเครื่องก่อนกำหนดอาจเริ่มก่อนที่ VLAN ของ SDV-RPC จะพร้อมใช้งาน ตรวจสอบว่า RPC พร้อมใช้งานก่อนที่จะพยายามสร้างซ็อกเก็ตเซิร์ฟเวอร์หรือไคลเอ็นต์ 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
}
หรือ
// 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;
}
ตั้งค่า Listener สำหรับเหตุการณ์ไคลเอ็นต์โดยใช้
ASDVGateway_Client_setClientNotificationCallback เพื่อรับการแจ้งเตือนเมื่อสถานะความพร้อมใช้งานของ RPC เปลี่ยนแปลง
ASDVGateway_Client_isProcessBoundToSdvRpcNetworkInterface จะดีกว่าหากแอปเปลี่ยนเครือข่ายของกระบวนการ เนื่องจากจะตรวจสอบทั้งว่า RPC พร้อมใช้งานและกระบวนการเชื่อมโยงกับ VLAN ของ SDV-RPC
เปลี่ยนเครือข่ายเริ่มต้นของกระบวนการ
คุณอาจต้องเปลี่ยนจาก VLAN ของ SDV-RPC เป็นเครือข่ายเริ่มต้นของกระบวนการ
เพื่อเปิดซ็อกเก็ตสำหรับการเชื่อมต่อที่กำหนดไว้สำหรับอินเทอร์เน็ต โทรหา
ASDVGateway_Client_unbindProcessFromSdvRpcNetworkInterface และ
ASDVGateway_Client_bindProcessToSdvRpcNetworkInterface เพื่อยกเลิกการเชื่อมโยงและเชื่อมโยงกระบวนการใหม่
กับ VLAN ของ SDV-RPC การเรียกทั้ง 2 รายการจะทำหน้าที่เหมือนสวิตช์ส่วนกลาง
โดยจะสลับอินเทอร์เฟซเครือข่ายที่ซ็อกเก็ตเชื่อมโยงอยู่สำหรับทุกเธรดของ
กระบวนการ
// 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
}
การแจ้งเตือน RPC
ตั้งค่า Listener ของไคลเอ็นต์เพื่อรับการแจ้งเตือนเกี่ยวกับการเปลี่ยนแปลงความพร้อมใช้งานของ RPC และการอัปเดตใบรับรองรูทที่เซิร์ฟเวอร์ 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
);
โฟลว์เซิร์ฟเวอร์ RPC
คุณต้องใช้ข้อมูลเข้าสู่ระบบเพื่อสร้างเซิร์ฟเวอร์ 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);
}
เมื่อสร้างเซิร์ฟเวอร์ RPC และทราบพอร์ตที่ใช้ฟังแล้ว ให้ลงทะเบียน เซิร์ฟเวอร์ RPC เพื่อให้ค้นพบแอปอื่นๆ ได้
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,
¶ms,
&status
);
// Basic error handling
if (statusCode != ASDVGateway_StatusCode_OK) {
std::cerr << "Failed to register RPC server: " << status.errorMessage << std::endl;
}
เมื่อระบบอัปเดตใบรับรองรูทในขณะรันไทม์ (เช่น ระหว่างการเปลี่ยนแปลงสถานะ VM) เซิร์ฟเวอร์ RPC จะต้องรีเฟรชรายการใบรับรองที่ยอมรับ
ตั้งค่า Listener สำหรับเหตุการณ์ฝั่งไคลเอ็นต์โดยใช้
ASDVGateway_Client_setClientNotificationCallbackเพื่อรับการแจ้งเตือนเมื่อมีการอัปเดต ใบรับรองรูทโทรหา
ASDVGateway_Client_rpcCredentialsเพื่อรับใบรับรองรูทที่อัปเดต
โฟลว์ไคลเอ็นต์ RPC
ขั้นตอนสำหรับไคลเอ็นต์ 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,
¶ms,
&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);
}
สร้างผู้เผยแพร่และเผยแพร่ข้อความ
ASDVGateway_Client_createPublication API ใช้เพื่อลงทะเบียนหน่วยบริการของผู้เผยแพร่โฆษณา
กับ SDV Gateway ผ่านอินเทอร์เฟซ AIDL และสร้าง Fast
Message Queue (FMQ) ในกระบวนการของแอป Binder จะเกี่ยวข้องเฉพาะในการตั้งค่า FMQ เท่านั้น แต่จะไม่เกี่ยวข้องเมื่อเขียนข้อความ จากนั้นจะใช้ ASDVGateway_Client_publishMessages API
เพื่อเผยแพร่ข้อความไปยังสิ่งพิมพ์ที่สร้างขึ้น ซึ่งเกี่ยวข้องกับการ
เขียนไปยัง FMQ ของสื่อสิ่งพิมพ์และแจ้งเตือนว่ามีการเขียนข้อความแล้ว
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,
¶ms,
&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
);
สร้างผู้ติดตามที่มีเครื่องมือฟังการแจ้งเตือน
หากต้องการติดตามสิ่งพิมพ์และรับการแจ้งเตือน Data Tunnel (เช่น
ความพร้อมใช้งานของข้อความ) ให้ใช้ ASDVGateway_Client_subscribeToPublicationByName
API นอกจากนี้ API นี้ยังตั้งค่า FMQ สำหรับการอ่านข้อความที่เผยแพร่ด้วย คุณสามารถ
กำหนดค่าการเรียกกลับของการแจ้งเตือนได้ทั้งในระหว่างกระบวนการสมัครใช้บริการหรือ
หลังจากนั้น
#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;
เมื่อสมัครใช้บริการ คุณจะระบุตัวเลือกเพิ่มเติมควบคู่ไปกับชื่อหน่วยบริการของผู้เผยแพร่โฆษณาได้ ตัวเลือกเหล่านี้ช่วยให้คุณดึงข้อมูลข้อความที่เผยแพร่ล่าสุด ก่อนที่จะสมัครรับข้อมูล และกำหนดช่วงเวลาขั้นต่ำระหว่าง การแจ้งเตือนได้
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,
¶ms,
&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));
ใช้ ASDVGateway_Client_readAvailableMessages API เพื่ออ่านข้อความจาก
สิ่งพิมพ์
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...
}
หากต้องการตั้งค่าการเรียกกลับหลังจากสมัครใช้บริการ ให้ใช้
ASDVGateway_Client_setNotificationCallbackForPublicationId API
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
);
ตั้งค่าบริการ init
ก่อนอื่น ให้ทำตามข้อมูลประจำตัวสำหรับบริการดั้งเดิมเพื่อกำหนด ข้อมูลประจำตัวของบริการให้กับบริการผ่านรหัส Android (AID)
สมมติว่าบริการของคุณชื่อ native_sdv_gateway_client_service โดย
ไฟล์ที่เรียกใช้งานได้จะอยู่ในพาร์ติชัน /vendor ที่
/vendor/bin/native_sdv_gateway_client_service และคุณใช้
vendor_gateway_client เป็นรหัส Android (AID) สำหรับเรียกใช้บริการ
เมื่อตั้งค่าแล้ว คุณจะกำหนดบริการเริ่มต้นต่อไปนี้ได้
service native_sdv_gateway_client_service /vendor/bin/native_sdv_gateway_client_service
class core
user vendor_gateway_client
group inet
disabled
oneshot
การกำหนดค่านี้ใช้พารามิเตอร์ต่อไปนี้
vendor_gateway_clientคือ AID ที่สร้างขึ้นหรือเลือกไว้สำหรับบริการgroup inetจำเป็นสำหรับไคลเอ็นต์เกตเวย์ SDV ในการใช้อินเทอร์เฟซเครือข่ายเพื่อสื่อสารระหว่าง VM ของ SDV คุณเพิ่มกลุ่มอื่นๆ ได้เมื่อ จำเป็น- ตัวอย่างนี้ใช้
disabledและoneshotคุณอาจต้องปรับ ตัวเลือกบริการสำหรับบริการของคุณ เริ่มบริการหลังจากวันที่sdv_gateway
สร้างกฎ SELinux สำหรับบริการ
หากต้องการใช้ SDV Gateway API คุณต้องมีกฎ SELinux ต่อไปนี้สำหรับบริการ
# 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)
ในกฎ SELinux ให้ทำดังนี้
init_daemon_domainอนุญาตให้เริ่มบริการจากinitsdv_gateway_client_domainให้สิทธิ์ SELinux ที่จำเป็นทั้งหมดสำหรับการ โต้ตอบกับ SDV Gateway บรรทัดต่อไปนี้จะให้สิทธิ์กฎเหล่านี้แก่ ไฟล์ที่เรียกใช้งานได้/vendor/bin/native_sdv_gateway_client_service u:object_r:native_sdv_gateway_client_service_exec:s0
ตัวอย่างโค้ด
ดูข้อมูลเพิ่มเติมเกี่ยวกับการเรียกใช้ตัวอย่างโค้ดแบบเนทีฟที่แสดงให้เห็นว่ามีการจัดทำเอกสารเกี่ยวกับ C
APIs อย่างไรได้ที่
system/software_defined_vehicle/samples/sdv_gateway/NativeSdvGatewayTestApp/README.md
เกตเวย์ SDV ในแอป IVI Java
รูปแบบการโต้ตอบสำหรับเกตเวย์ SDV บน IVI แสดงอยู่ใน ไดอะแกรมนี้
รูปที่ 4 SDV Gateway ในแอป IVI Java
โดยโมเดลจะทำสิ่งต่อไปนี้
- ส่งการเรียกทั้งหมดไปยังเลเยอร์ JNI
- เชื่อมต่อ API ของ Java และ C
- กำหนดการโต้ตอบ AIDL กับ
ISdvGateway:- การสื่อสารเริ่มต้น
- ค้นหาหรือสร้างเซิร์ฟเวอร์ RPC
- สร้าง Pub/Sub
- ดำเนินการโต้ตอบ Data Tunnel ของ Service Discovery
- รับการแจ้งเตือน (เช่น ข้อมูลพร้อมใช้งาน)
- อ่านและเขียนข้อความ (FMQ สำหรับ Pub/Sub) เพื่อสร้างคู่คีย์และ ใบรับรองสำหรับ TLS
รวมไลบรารีของไคลเอ็นต์เกตเวย์
ไลบรารี Java และ Wrapper JNI สำหรับ libsdvgatewayclient C API จะ
ติดตั้งใน APEX บนเป้าหมาย IVI เพิ่มทรัพยากร Dependency ในเวลาคอมไพล์ไปยัง
Java Library Stub, Java Library ที่ต้องใช้ในรันไทม์ และ
APEX ที่จำเป็นซึ่งมี Java Library
android_app {
name: "YourAppName",
// ...
static_libs: [
"libsdvgatewayclient-java",
],
libs: [
"libsdvgatewayclient-java-sdk.stubs",
],
uses_libs: [
"libsdvgatewayclient-java-sdk",
],
required: [
"com.sdv.google.gateway.client",
],
// ...
}
ในกรณีของแอปที่ไม่ได้รวมกลุ่มซึ่งสร้างขึ้นนอกโครงสร้าง SDV กระบวนการจะคล้ายกัน ดังนี้
คัดลอก JAR ของ Stub ไลบรารี Java ที่สร้างขึ้นและ JAR ที่รองรับสำหรับ RPC ใน โฟลเดอร์ libs ของแอป ดูรายละเอียดได้ที่
system/software_defined_vehicle/sdv_gateway/libsdvgatewayclient_apex/README.mdเพิ่ม JAR ของ Stub เป็นทรัพยากร Dependency ที่ใช้คอมไพล์เท่านั้น ตัวอย่างเช่น อัปเดตการกำหนดค่า Gradle ให้ขึ้นอยู่กับ Stub โดยการเพิ่มรายการ
compileOnlyลงในส่วนdependenciesdependencies { // 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")) }เพิ่มไลบรารี Java ลงในส่วนแอปของไฟล์
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>
โหลดไลบรารี
สร้างออบเจ็กต์ SdvGatewayClient (จัดทำโดยไลบรารีของไคลเอ็นต์)
import google.sdv.gateway.client.SdvGatewayClient;
// --- Inside your Activity, Service, or ViewModel ---
// Initialize the SDV Gateway Client
SdvGatewayClient gatewayClient = new SdvGatewayClient();
การสื่อสารเริ่มต้น
เรียกใช้ initComms() โดยใช้ชื่อแอปเป็นชื่อแพ็กเกจบริการ
// 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);
}
Service Discovery
Java API มีเมธอดสำหรับดำเนินการต่อไปนี้
รับการแจ้งเตือนเมื่อมีการลงทะเบียนหรือยกเลิกการลงทะเบียนหน่วยบริการที่มีประเภทหน่วยที่เฉพาะเจาะจง (หรือจะจับคู่ชื่อที่เฉพาะเจาะจงก็ได้)
แสดงรายการหน่วยบริการปัจจุบันที่มีประเภทหน่วยที่เฉพาะเจาะจง (หรือจับคู่ชื่อประเภทหน่วยบริการที่เฉพาะเจาะจงแทน) หากต้องการรับการแจ้งเตือนเกี่ยวกับการเปลี่ยนแปลงหน่วยบริการ ให้สร้าง Listener โดยทำดังนี้
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
}
}
};
เมธอด addListenerForServiceUnitChangeByName Java จะแจ้งเตือน Listener ดังนี้
// 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);
}
หรือใช้เมธอด addListenerForServiceUnitChangeByType ของ Java เพื่อ
แจ้งเตือน Listener เมื่อมีการลงทะเบียน
หรือยกเลิกการลงทะเบียนบริการที่มีประเภทหน่วยที่ระบุ
// 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);
}
สำหรับ addListenerForServiceUnitChangeByName และ
addListenerForServiceUnitChangeByType หลังจากเพิ่มแล้ว ระบบจะแจ้งให้ผู้ฟังทราบถึงหน่วยบริการที่ลงทะเบียนทั้งหมด
หากต้องการรับเฉพาะหน่วยบริการที่ลงทะเบียน
ตามชื่อหรือประเภท ให้ใช้ listServiceUnitsByName และ listServiceUnitsByType
Java API ดังนี้
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...
}
โฟลว์เซิร์ฟเวอร์ RPC
ไคลเอ็นต์ SDV Gateway ภายในระบบ IVI ใช้ Google Remote Procedure Call (gRPC)
เพื่อสื่อสารกับบริการ SDV การโต้ตอบเหล่านี้อาศัยคำจำกัดความของ Proto จากแคตตาล็อก VSIDL ซึ่งสอดคล้องหรือคล้ายกับคำจำกัดความที่ใช้ใน SDV Core สำหรับแอป Java เราเลือกใช้ gRPC-Java เรามีคำจำกัดความของโปรโตคอลเซิร์ฟเวอร์ตัวอย่าง sunroof.proto สำหรับเซิร์ฟเวอร์แอป
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) {}
}
ลิงก์กับไลบรารี Proto ที่เกี่ยวข้องและกำหนดบริการ
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();
}
}
ลงทะเบียนเซิร์ฟเวอร์ gRPC ด้วยข้อมูลเข้าสู่ระบบของแชแนลที่ปลอดภัยและไม่ปลอดภัย
// 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
);
ภายใน ไลบรารีของไคลเอ็นต์จะสร้างออบเจ็กต์เซิร์ฟเวอร์ gRPC
SdvGatewayClient.java และจัดการการอัปเดตใบรับรองรูทด้วย
// 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);
โฟลว์ไคลเอ็นต์ RPC
ตัวอย่างโค้ดนี้มีคำจำกัดความของโปรโตคอลเซิร์ฟเวอร์ (tpms.proto) สำหรับเซิร์ฟเวอร์ที่แอปเชื่อมต่อในฐานะไคลเอ็นต์
/**
* 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) {}
}
ลิงก์กับไลบรารี Proto ที่เกี่ยวข้อง
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());
ภายในระบบจะเรียกใช้ ASDVGateway_Client_findRpcServerByName API เพื่อค้นหา
เซิร์ฟเวอร์ RPC หากพบเซิร์ฟเวอร์ RPC ระบบจะสร้างช่องทางที่มีการจัดการใน
โหมดที่ไม่ปลอดภัยหรือกำหนดให้ใช้การกำหนดค่า TLS เช่นเดียวกับโฟลว์เซิร์ฟเวอร์ RPC
ทั้งนี้ขึ้นอยู่กับการกำหนดค่าการค้นพบบริการ แอปสร้าง
สตับด้วยออบเจ็กต์ ManagedChannel และเรียกใช้เมธอดเซิร์ฟเวอร์
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();
เปลี่ยนเครือข่ายเริ่มต้นของกระบวนการ
คุณอาจต้องเปลี่ยนจาก VLAN ของ SDV-RPC เป็นเครือข่ายเริ่มต้นของกระบวนการ
เพื่อเปิดซ็อกเก็ตสำหรับการเชื่อมต่อที่กำหนดไว้สำหรับอินเทอร์เน็ต โทรหา
unbindProcessFromSdvRpcNetworkInterface และ
bindProcessToSdvRpcNetworkInterface เพื่อยกเลิกการเชื่อมโยงและเชื่อมโยงกระบวนการกับ
VLAN ของ SDV-RPC อีกครั้ง การเรียกใช้ทั้ง 2 รายการจะทำหน้าที่เป็นสวิตช์ส่วนกลาง ซึ่งจะสลับอินเทอร์เฟซเครือข่ายที่ซ็อกเก็ตเชื่อมโยงอยู่สำหรับทุกเธรดของกระบวนการ
// 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();
สร้างผู้เผยแพร่และเผยแพร่ข้อความ
// 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);
ภายใน Java createPublication และ API การเผยแพร่จะอิงตาม API ดั้งเดิม
ASDVGateway_Client_createPublication และ
ASDVGateway_Client_readAvailableMessages ดูข้อมูลโดยละเอียดเกี่ยวกับ C API ได้ที่ใช้ SDV Gateway ในบริการดั้งเดิมของ IVI ออบเจ็กต์
Publisher ให้บริบทสำหรับการเขียนข้อความและการจัดการวงจรการเผยแพร่
สร้างผู้ติดตามด้วยเครื่องมือฟังการแจ้งเตือน
Java API ช่วยให้ส่ง Listener เป็นพารามิเตอร์ไปยังเมธอด subscribe to
publication และแสดงผลออบเจ็กต์ Subscription ได้
Listenerจะได้รับการแจ้งเตือนเมื่อมีข้อมูลสำหรับสิ่งพิมพ์ที่สมัครรับข้อมูลSubscriptionทำหน้าที่เป็นออบเจ็กต์บริบทและใช้เพื่ออ่านข้อความ และปิดการสมัครใช้บริการได้
// 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();
ตัวอย่างโค้ด
ระบบจะเรียกใช้การเรียกกลับของผู้ฟังในเธรดเดียวที่ไคลเอ็นต์จัดการเพื่อ
ลดการประมวลผลภายในผู้ฟังเพื่อป้องกันความล่าช้าในการรับ
การแจ้งเตือนสำหรับการสมัครรับข้อมูลอื่นๆ เลเยอร์ Java ใช้ประโยชน์จาก C API สำหรับ
การจัดการการสมัครใช้บริการ การจัดการการแจ้งเตือน และการดึงข้อมูลข้อความ หากต้องการดูการสาธิตการใช้งาน API โปรดดูตัวอย่างแอป Java ที่มีให้ในไฟล์ไบนารีที่ system/software_defined_vehicle/samples/sdv_gateway/README.md
สิทธิ์ที่จำเป็น
การเรียกใช้ไคลเอ็นต์ API ของ SDV Gateway ไม่จำเป็นต้องมีสิทธิ์พิเศษ แต่คุณต้อง ใช้กฎ SELinux ที่เหมาะสมกับแอป
- สำหรับการสมัครใช้บริการและการเผยแพร่ข้อมูลผ่าน Data Tunnel คุณไม่จำเป็นต้องมีสิทธิ์ใดๆ
- สำหรับ SDV RPC คุณต้องมีสิทธิ์ต่อไปนี้
android.permission.INTERNETandroid.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS
สำหรับ android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS คุณต้องมีไฟล์รายการที่อนุญาตภายใต้ etc/permissions ในพาร์ติชันเดียวกันกับแอปด้วย เช่น สำหรับ SdvCarMonitorTestApp (ชื่อแพ็กเกจ com.android.testapp.sdvcarmonitor) ไฟล์จะมีลักษณะดังนี้
<?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>
กฎ SELinux
หากต้องการใช้ SDV Gateway API แอป Java ต้องมีสิทธิ์เช่นเดียวกับบริการดั้งเดิม
ให้สิทธิ์เหล่านี้โดยใช้sdv_gateway_client_domain()
มาโคร SELinux
sdv_gateway_client_domain(my_oem_sdv_gateway_client_app)
OEM กำหนดโดเมน my_oem_sdv_gateway_client_app สำหรับแอป Java
ที่ได้รับอนุญาตให้ใช้ SDV Gateway ใช้เกตเวย์ SDV จากแอปของระบบและแอปที่มีสิทธิ์เท่านั้น
ตำแหน่งของโค้ด
ดูซอร์สโค้ดสำหรับ SDV Gateway ได้ที่
system/software_defined_vehicle/sdv_gateway/ คุณสามารถรับตัวอย่างเกตเวย์ SDV
สำหรับสิ่งต่อไปนี้
- API ของไคลเอ็นต์ C
system/software_defined_vehicle/samples/sdv_gateway/NativeSdvGatewayTestApp/ - Java API ของไคลเอ็นต์
system/software_defined_vehicle/samples/sdv_gateway/SdvCarMonitorTestApp/