IVI-তে SDV গেটওয়ে ব্যবহার করুন

ইন-ভেহিকেল ইনফোটেইনমেন্ট (IVI) সিস্টেমে থাকা সফটওয়্যার ডিফাইন্ড ভেহিকেল (SDV) গেটওয়ে, IVI সিস্টেম এবং রিমোট SDV সার্ভিসগুলোর মধ্যে যোগাযোগ সহজ করে। এই গেটওয়ে OEM জাভা অ্যাপ এবং নেটিভ সার্ভিস, যেমন VHAL-কে, SDV সার্ভিসগুলোর সাথে ইন্টারঅ্যাক্ট করতে দেয়। গেটওয়েটি সার্ভিস রেজিস্ট্রেশন, ডিসকভারি এবং RPC সহ বিভিন্ন প্রতিষ্ঠিত যোগাযোগ পদ্ধতি ব্যবহার করে।

এই গেটওয়েটি AAOS SDV প্রকল্পের নির্দিষ্ট কিছু প্রয়োজনীয়তা পূরণ করে, যেমন প্রপার্টি তথ্যের জন্য SDV ডেটা টানেল ব্যবহার করে একটি VHAL রেফারেন্স ইমপ্লিমেন্টেশন সক্ষম করা। এটি IVI-তে থাকা জাভা এবং কোটলিন অ্যান্ড্রয়েড অ্যাপগুলোকে SDV কমস স্ট্যাক ব্যবহার করতে, সার্ভিস হিসেবে রেজিস্টার করতে, অন্যান্য SDV সার্ভিস খুঁজে বের করতে এবং সেগুলোর সাথে যোগাযোগ করতেও সক্ষম করে।

SDV গেটওয়ে কোডের অবস্থান এবং উদাহরণস্বরূপ SDV গেটওয়ে ক্লায়েন্টগুলোর জন্য, কোডের অবস্থান দেখুন।

এসডিভি গেটওয়ে ইন্টিগ্রেশন মডেল

IVI আর্কিটেকচারে SDV গেটওয়ের মাধ্যমে SDV-IVI Comms ব্যবহার করুন।

এসডিভি গেটওয়ে জাভা অ্যাপস, নেটিভ সার্ভিস, এসডিভি কমস স্ট্যাক এবং ভেহিকল নেটওয়ার্কের সাথে যোগাযোগ স্থাপন করে। চিত্র ১-এ এই যোগাযোগগুলো দেখানো হয়েছে:

এসডিভি গেটওয়ে মিথস্ক্রিয়া

চিত্র ১. এসডিভি গেটওয়ের মিথস্ক্রিয়া।

এই সিস্টেম ডায়াগ্রামে:

  • অ্যাপগুলো এসডিভি গেটওয়ের ক্লায়েন্ট সার্ভিসের মাধ্যমে এর সাথে যোগাযোগ করে।
  • AAOS SDV SDK আপনাকে প্রদান করে:
    • ISdvGateway হলো আন্তঃপ্রক্রিয়া যোগাযোগের জন্য একটি AIDL API।
    • নেটওয়ার্ক যোগাযোগের জন্য কমিউনিকেশন লাইব্রেরি।
    • নেটিভ সার্ভিস ইন্টিগ্রেশনের জন্য সুবিধাজনক সি এপিআই।
  • ISdvGateway AIDL API-টি SDV Gateway পরিষেবা এবং সাবসিস্টেম দ্বারা বাস্তবায়িত হয়।
  • এসডিভি গেটওয়ে পরিষেবা নিম্নলিখিত বিষয়গুলো পরিচালনা করে:
    • দ্বিমুখী পরিষেবা আবিষ্কার।
    • দূরবর্তী এসডিভি পরিষেবাগুলির সাথে যোগাযোগ।
    • মূল ব্যবসায়িক যুক্তি।
  • এসডিভি গেটওয়ে সাবসিস্টেমটি যানবাহন নেটওয়ার্কের সাথে সংযুক্ত হয়।
  • VHAL ইমপ্লিমেন্টেশন সহ নেটিভ সার্ভিসগুলো সরাসরি অথবা SDK-এর C API-এর মাধ্যমে ISdvGateway AIDL API ব্যবহার করতে পারে।
  • VHAL প্রক্সিটি একটি রেফারেন্স VHAL ইমপ্লিমেন্টেশন হিসেবে কাজ করে, যাতে VSIDL ম্যাপিং ইন্টিগ্রেশন অন্তর্ভুক্ত রয়েছে।

একটি IVI নেটিভ সার্ভিসে SDV গেটওয়ের জন্য ইন্টিগ্রেশন মডেল

একত্রীকরণ মডেলটি চিত্র ২-এ দেখানো হয়েছে:

এসডিভি গেটওয়ে ইন্টিগ্রেশন মডেল

চিত্র ২. এসডিভি গেটওয়ে ইন্টিগ্রেশন মডেল।

একটি IVI নেটিভ সার্ভিসে SDV গেটওয়ে ব্যবহার করুন

চিত্র ৩-এ IVI-তে SDV গেটওয়ের ব্যবহার দেখানো হয়েছে:

IVI-তে SDV গেটওয়ে

চিত্র ৩. IVI-তে SDV গেটওয়ে।

পূর্বশর্ত

বাইন্ডার থ্রেড পুল শুরু করুন:

  • বাইন্ডার সার্ভিসগুলো থেকে অ্যাসিঙ্ক্রোনাস কলব্যাক গ্রহণ করার জন্য এসডিভি গেটওয়ে ক্লায়েন্ট লাইব্রেরির একটি চালু বাইন্ডার থ্রেড পুল প্রয়োজন।

  • যখন একটি বাইন্ডার থ্রেড পুল চালু করা হয় না, তখন এসডিভি গেটওয়ে ক্লায়েন্ট তৈরি করার জন্য প্রয়োজনীয় এপিআইটি ব্যর্থ হয়।

নেটিভ গেটওয়ে ক্লায়েন্ট লাইব্রেরি অন্তর্ভুক্ত করুন

নেটিভ গেটওয়ে ক্লায়েন্ট লাইব্রেরি একটি C API প্রদান করে। C API ব্যবহার করার জন্য libsdvgatewayclient এর একটি ইনস্ট্যান্সকে ডিপেন্ডেন্সি হিসেবে যোগ করুন:

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);

ক্লায়েন্ট তৈরি হওয়ার পরে, ক্লায়েন্ট:

  • গেটওয়ে পরিষেবার সাথে পরবর্তী সকল যোগাযোগের জন্য অবস্থা ধরে রাখে।

  • এটি অন্যান্য SDV-সক্ষম পরিষেবাগুলির সাথে সমস্ত ইন্টারঅ্যাকশনের প্রেক্ষাপট হিসাবে কাজ করে এবং C API ফাংশনগুলিতে প্রথম প্যারামিটার হিসাবে পাস করা হয়।

স্ট্যাটাস কোড এবং ত্রুটির বার্তা

বেশিরভাগ C API ফাংশনের সংজ্ঞাটি হলো:

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

সফলতা মূল্যায়ন করতে, আপনি ফেরত আসা স্ট্যাটাস কোডটি পরীক্ষা করতে পারেন, যা ASDVGateway_StatusCode_t টাইপের হয়ে থাকে। এছাড়াও আপনি একটি স্ট্রাকচারের পয়েন্টার পাস করতে পারেন, যেখানে ফাংশনটি স্ট্যাটাস কোড এবং একটি ত্রুটির বার্তা পূরণ করতে পারে। পয়েন্টারটি outStatus নামের শেষ প্যারামিটার হিসেবে পাস করা হয়। null ভ্যালুর অর্থ হলো আউটপুট স্ট্রাকচারটি ব্যবহৃত হচ্ছে না।

কলারকে অবশ্যই এরর মেসেজের জন্য মেমরি স্ট্যাটাস স্ট্রাকচারটি বরাদ্দ করতে হবে। স্ট্যাটাস স্ট্রাকচারটি স্ট্যাটাস কোড এবং এরর মেসেজ উভয়ই ধারণ করতে পারে। নতুন ক্লায়েন্ট তৈরি করার সময় এরর মেসেজটি কীভাবে পেতে হয়, তার একটি উদাহরণ দেওয়া আছে।

সাফল্য মূল্যায়ন করতে:

  1. ফেরত আসা স্ট্যাটাস কোডটি যাচাই করুন, যা ASDVGateway_StatusCode_t টাইপের।

  2. একটি স্ট্রাকচারের পয়েন্টার পাস করুন যেখানে ফাংশনটি স্ট্যাটাস কোড এবং একটি ত্রুটির বার্তা পূরণ করতে পারবে।

    • পয়েন্টারটি শেষ প্যারামিটার হিসেবে পাস করা হয়, যার নাম outStatus
    • নাল মানের অর্থ হলো আউটপুট স্ট্রাকচারটি ব্যবহৃত হচ্ছে না।
    • ত্রুটির বার্তার জন্য কলারকে অবশ্যই মেমরি স্ট্যাটাস স্ট্রাকচারটি বরাদ্দ করতে হবে।
    • স্ট্যাটাস স্ট্রাকচারটিতে স্ট্যাটাস কোড এবং একটি এরর মেসেজ থাকতে পারে।

এখানে একটি নমুনা দেওয়া হলো, যা দেখায় কীভাবে একজন নতুন ক্লায়েন্টের জন্য ত্রুটির বার্তাটি পুনরুদ্ধার করতে হয়:

#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 নিম্নলিখিত উভয় প্রেক্ষাপটেই কল করা যেতে পারে:

  • ক্লায়েন্ট তৈরি হওয়ার পরে

  • যেকোনো ডেটা টানেল, আরপিসি, বা সার্ভিস ডিসকভারি ইন্টারঅ্যাকশনের আগে

এখানে একটি উদাহরণ দেওয়া হলো:

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;
}

পরিষেবা আবিষ্কার

একটি নির্দিষ্ট প্রকার বা নামের সার্ভিস ইউনিট নিবন্ধিত বা অনিবন্ধিত হলে আপনি বিজ্ঞপ্তি পেতে পারেন। কলব্যাক ফাংশনটিকে গেটওয়ে ক্লায়েন্টের মালিকানাধীন একটি থ্রেডে অবহিত করা হয়। 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
);

অতিরিক্ত বিজ্ঞপ্তি ছাড়াই নিবন্ধিত পরিষেবা ইউনিটগুলি পুনরুদ্ধার করতে, ASDVGateway_Client_fetchServiceUnitsByType এবং ASDVGateway_Client_fetchServiceUnitsByName API গুলি ব্যবহার করুন।

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
);

আরপিসি প্রবাহ

গেটওয়ে ক্লায়েন্ট লাইব্রেরি অন্যান্য SDV-সক্ষম অ্যাপের সাথে যোগাযোগের জন্য ট্রান্সপোর্ট লেয়ার সিকিউরিটি (TLS) সেটিংস পরিচালনা করে। বিশেষত, এটি যোগাযোগের জন্য প্রয়োজনীয় TLS সেটিংস সংগ্রহ করে। TLS ব্যবহার যেভাবে নির্ধারণ করা হয় তা নিচে দেওয়া হলো:

  • যখন SDV বুট মোড LOCKED থাকে, তখন TLS ব্যবহৃত হয়।

  • যখন SDV বুট মোড UNLOCKED থাকে, তখন অসুরক্ষিত যোগাযোগ ব্যবহৃত হয়। যখন TLS ব্যবহৃত হয়, তখন গেটওয়ে ক্লায়েন্ট লাইব্রেরি একটি কী পেয়ার তৈরি করে যা শুধুমাত্র অ্যাপ প্রসেসের কাছেই পরিচিত থাকে। ক্লায়েন্ট একটি RPC সার্ভার বা একটি RPC ক্লায়েন্ট চ্যানেল তৈরি করার জন্য RPC ক্রেডেনশিয়ালগুলো সংগ্রহ করে। RPC একটি ডেডিকেটেড SDV-RPC VLAN ব্যবহার করে। গেটওয়ে ক্লায়েন্ট লাইব্রেরি, ASDVGateway_Client_initComms কল চলাকালীন বা তার পরে, প্রসেসের ডিফল্ট নেটওয়ার্ককে SDV-RPC VLAN-এ পরিবর্তন করার জন্য android_setprocnetwork ফাংশনটিকে কল করে।

আরপিসি প্রাপ্যতা

আর্লি বুটে চালু হওয়ার জন্য কনফিগার করা কোনো ক্লায়েন্ট SDV-RPC VLAN উপলব্ধ হওয়ার আগেই চালু হয়ে যেতে পারে। যেকোনো 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;
}

RPC প্রাপ্যতা স্থিতি পরিবর্তিত হলে বিজ্ঞপ্তি পেতে ASDVGateway_Client_setClientNotificationCallback ব্যবহার করে ক্লায়েন্ট ইভেন্টের জন্য একটি লিসেনার সেট করুন। যদি অ্যাপটি প্রসেসের নেটওয়ার্ক পরিবর্তন করে, তবে ASDVGateway_Client_isProcessBoundToSdvRpcNetworkInterface ব্যবহার করা শ্রেয়, কারণ এটি RPC উপলব্ধ আছে কিনা এবং প্রসেসটি SDV-RPC VLAN-এর সাথে আবদ্ধ আছে কিনা, উভয়ই পরীক্ষা করে।

প্রসেসটির ডিফল্ট নেটওয়ার্ক পরিবর্তন করুন

ইন্টারনেট-সংযুক্ত সংযোগের জন্য সকেট খুলতে, প্রসেসটির ডিফল্ট নেটওয়ার্ক হিসেবে SDV-RPC VLAN থেকে সরে আসার প্রয়োজন হতে পারে। প্রসেসটিকে SDV-RPC VLAN থেকে আনবাইন্ড এবং রিবাইন্ড করতে ASDVGateway_Client_unbindProcessFromSdvRpcNetworkInterface এবং ASDVGateway_Client_bindProcessToSdvRpcNetworkInterface কল করুন। এই দুটি কল গ্লোবাল সুইচের মতো কাজ করে, যা প্রসেসের সমস্ত থ্রেডের জন্য সকেট সংযুক্ত থাকা নেটওয়ার্ক ইন্টারফেস পরিবর্তন করে দেয়।

// 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 প্রাপ্যতার পরিবর্তন এবং 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 সার্ভার তৈরি করতে আপনাকে অবশ্যই ক্রেডেনশিয়াল ব্যবহার করতে হবে:

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,
    &params,
    &status
);

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

যখন সিস্টেম রান টাইমে রুট সার্টিফিকেট আপডেট করে (উদাহরণস্বরূপ, ভিএম-এর অবস্থা পরিবর্তনের সময়), তখন আরপিসি সার্ভারগুলিকে অবশ্যই তাদের গৃহীত সার্টিফিকেটের তালিকা রিফ্রেশ করতে হবে:

  • রুট সার্টিফিকেট আপডেট হলে বিজ্ঞপ্তি পেতে ASDVGateway_Client_setClientNotificationCallback ব্যবহার করে ক্লায়েন্ট ইভেন্টের জন্য একটি লিসেনার সেট করুন।

  • আপডেট করা রুট সার্টিফিকেটগুলো পেতে ASDVGateway_Client_rpcCredentials কল করুন।

আরপিসি ক্লায়েন্ট প্রবাহ

আরপিসি ক্লায়েন্টের কার্যপ্রবাহটি নিচে দেওয়া হলো:

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);
}

প্রকাশক তৈরি করুন এবং বার্তা প্রকাশ করুন

ASDVGateway_Client_createPublication API-টি একটি পাবলিশার সার্ভিস ইউনিটকে তার AIDL ইন্টারফেসের মাধ্যমে SDV Gateway-তে রেজিস্টার করতে এবং অ্যাপ প্রসেসে একটি ফাস্ট মেসেজ কিউ (FMQ) তৈরি করতে ব্যবহৃত হয়। বাইন্ডার শুধুমাত্র 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,
    &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
);

নোটিফিকেশন লিসেনার সহ একজন সাবস্ক্রাইবার তৈরি করুন

কোনো পাবলিকেশনে সাবস্ক্রাইব করতে এবং ডেটা টানেল নোটিফিকেশন (যেমন মেসেজের প্রাপ্যতা) পেতে, 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,
    &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));

পাবলিকেশন থেকে মেসেজ পড়ার জন্য 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
);

ইনিট পরিষেবা সেট আপ করুন

প্রথমে, সার্ভিসটির অ্যান্ড্রয়েড আইডি (AID)- এর মাধ্যমে সেটিকে একটি সার্ভিস আইডেন্টিটি নির্ধারণ করার জন্য 'আইডেন্টিটি ফর নেটিভ সার্ভিসেস' অনুসরণ করুন।

ধরে নিন আপনার সার্ভিসের নাম native_sdv_gateway_client_service , এক্সিকিউটেবলটি /vendor পার্টিশনে /vendor/bin/native_sdv_gateway_client_service এ অবস্থিত, এবং সার্ভিসটি চালানোর জন্য আপনি অ্যান্ড্রয়েড আইডি (AID) হিসেবে vendor_gateway_client ব্যবহার করেন। এই সেটআপের মাধ্যমে, আপনি নিম্নলিখিত ইনিট সার্ভিসটি সংজ্ঞায়িত করতে পারেন:

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।
  • SDV VM-গুলোর মধ্যে যোগাযোগের জন্য নেটওয়ার্ক ইন্টারফেস ব্যবহার করতে SDV গেটওয়ে ক্লায়েন্টদের জন্য group inet আবশ্যক। প্রয়োজন অনুযায়ী অন্যান্য গ্রুপ যোগ করা যেতে পারে।
  • এই উদাহরণে 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 init থেকে সার্ভিসটি শুরু করার সুযোগ দেয়।

  • sdv_gateway_client_domain SDV গেটওয়ের সাথে যোগাযোগের জন্য প্রয়োজনীয় সকল SELinux অনুমতি প্রদান করে। নিম্নলিখিত লাইনটি এক্সিকিউটেবল ফাইলটিকে এই নিয়মগুলো প্রদান করে:

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

কোডের নমুনা

সি এপিআই (C API)-গুলো কীভাবে নথিভুক্ত করা হয়েছে তা প্রদর্শনকারী নেটিভ কোড স্যাম্পলটি চালানো সম্পর্কে আরও জানতে, system/software_defined_vehicle/samples/sdv_gateway/NativeSdvGatewayTestApp/README.md দেখুন।

IVI জাভা অ্যাপে SDV গেটওয়ে

IVI-তে একটি SDV গেটওয়ের ইন্টারঅ্যাকশন মডেলটি এই ডায়াগ্রামে চিত্রিত করা হয়েছে:

IVI জাভা অ্যাপে SDV গেটওয়ে

চিত্র ৪. IVI জাভা অ্যাপে SDV গেটওয়ে।

বিশেষত, মডেলটি:

  • সমস্ত কল JNI লেয়ারে প্রেরণ করে।
  • জাভা এবং সি এপিআই-কে সংযুক্ত করে।
  • ISdvGateway এর সাথে AIDL-এর মিথস্ক্রিয়া সংজ্ঞায়িত করে:
    • যোগাযোগ শুরু করুন
    • RPC সার্ভার খুঁজুন বা তৈরি করুন
    • পাব/সাব তৈরি করুন
    • পরিষেবা আবিষ্কার সম্পাদন করুন ডেটা টানেল মিথস্ক্রিয়া
    • বিজ্ঞপ্তি গ্রহণ করুন (যেমন, ডেটা উপলব্ধ হলে)
    • কী পেয়ার তৈরি এবং TLS-এর জন্য সার্টিফিকেট প্রস্তুত করতে মেসেজ পড়া ও লেখা (পাব/সাব-এর জন্য FMQ)।

গেটওয়ে ক্লায়েন্ট লাইব্রেরি অন্তর্ভুক্ত করুন

libsdvgatewayclient C API-এর জন্য জাভা লাইব্রেরি এবং JNI র‍্যাপারটি IVI টার্গেটের একটি APEX-এ ইনস্টল করা আছে। জাভা লাইব্রেরি স্টাব, রান টাইমে অবশ্যই ব্যবহার করতে হবে এমন জাভা লাইব্রেরি এবং জাভা লাইব্রেরি ধারণকারী প্রয়োজনীয় APEX-এ একটি কম্পাইল টাইম ডিপেন্ডেন্সি যোগ করুন।

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 ট্রি-এর বাইরে তৈরি একটি আনবান্ডেলড অ্যাপের ক্ষেত্রেও প্রক্রিয়াটি একই রকম:

  1. তৈরি হওয়া জাভা লাইব্রেরি স্টাব JAR এবং RPC-এর জন্য সাপোর্টিং JAR ফাইলটি অ্যাপের libs ফোল্ডারে কপি করুন। বিস্তারিত জানতে system/software_defined_vehicle/sdv_gateway/libsdvgatewayclient_apex/README.md দেখুন।

  2. স্টাবস JAR-টিকে একটি কম্পাইল-অনলি ডিপেন্ডেন্সি হিসেবে যোগ করুন। উদাহরণস্বরূপ, dependencies সেকশনে একটি compileOnly এন্ট্রি যোগ করে স্টাবস-এর উপর নির্ভর করার জন্য গ্রেডল কনফিগারেশন আপডেট করুন:

    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. 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);
}

পরিষেবা আবিষ্কার

জাভা এপিআই-তে নিম্নলিখিত পদ্ধতিগুলো রয়েছে:

  • একটি নির্দিষ্ট ইউনিট টাইপের (অথবা একটি নির্দিষ্ট নামের) সার্ভিস ইউনিট নিবন্ধিত বা অনিবন্ধিত হলে বিজ্ঞপ্তি পান।

  • একটি নির্দিষ্ট ইউনিট টাইপ সহ বর্তমান পরিষেবা ইউনিটগুলির তালিকা করুন (অথবা বিকল্পভাবে একটি নির্দিষ্ট পরিষেবা ইউনিট টাইপের নাম মেলান)। পরিষেবা ইউনিটের পরিবর্তন সম্পর্কে বিজ্ঞপ্তি পেতে, একটি লিসেনার তৈরি করুন:

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 জাভা মেথডটি লিসেনারকে অবহিত করে:

// 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 জাভা মেথডটি ব্যবহার করুন:

// 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 জাভা এপিআইগুলো ব্যবহার করুন:

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...
}

আরপিসি সার্ভার প্রবাহ

IVI সিস্টেমের মধ্যে থাকা SDV গেটওয়ে ক্লায়েন্টরা SDV পরিষেবাগুলির সাথে যোগাযোগের জন্য গুগল রিমোট প্রসিডিউর কল (gRPC) ব্যবহার করে। এই মিথস্ক্রিয়াগুলি VSIDL ক্যাটালগের প্রোটো ডেফিনিশনের উপর নির্ভর করে, যা SDV কোরে ব্যবহৃত ডেফিনিশনগুলির সাথে সামঞ্জস্যপূর্ণ বা অনুরূপ। জাভা অ্যাপের জন্য, 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) {}
}

সংশ্লিষ্ট প্রোটো লাইব্রেরির সাথে লিঙ্ক করুন এবং পরিষেবাটি সংজ্ঞায়িত করুন:

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
);

অভ্যন্তরীণভাবে, ক্লায়েন্ট লাইব্রেরিটি SdvGatewayClient.java নামক gRPC সার্ভার অবজেক্টটি তৈরি করে এবং রুট সার্টিফিকেটগুলির আপডেটও পরিচালনা করে:

// 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);

আরপিসি ক্লায়েন্ট প্রবাহ

এই কোড নমুনাটি সেই সার্ভারের সার্ভার প্রোটো সংজ্ঞা ( 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) {}
}

সংশ্লিষ্ট প্রোটো লাইব্রেরির সাথে লিঙ্ক করুন:

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());

অভ্যন্তরীণভাবে, RPC সার্ভার খুঁজে বের করার জন্য ASDVGateway_Client_findRpcServerByName API-টি কল করা হয়। যদি RPC সার্ভারটি খুঁজে পাওয়া যায়, তাহলে সার্ভিস ডিসকভারি কনফিগারেশনের উপর নির্ভর করে, RPC সার্ভার ফ্লো-এর মতোই ম্যানেজড চ্যানেলটি ইনসিকিউর মোডে তৈরি করা হয় অথবা TLS কনফিগারেশন ব্যবহার করার জন্য নির্দিষ্ট করা হয়। অ্যাপটি 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();

প্রসেসটির ডিফল্ট নেটওয়ার্ক পরিবর্তন করুন

ইন্টারনেট-সংযুক্ত সংযোগের জন্য সকেট খুলতে, প্রসেসটির ডিফল্ট নেটওয়ার্ক হিসেবে SDV-RPC VLAN থেকে সরে আসার প্রয়োজন হতে পারে। প্রসেসটিকে SDV-RPC VLAN থেকে আনবাইন্ড এবং রিবাইন্ড করতে unbindProcessFromSdvRpcNetworkInterface এবং bindProcessToSdvRpcNetworkInterface কল করুন। এই দুটি কল গ্লোবাল সুইচ হিসেবে কাজ করে, যা প্রসেসের সমস্ত থ্রেডের জন্য সকেটগুলো যে নেটওয়ার্ক ইন্টারফেসে বাইন্ড করা থাকে, তা পরিবর্তন করে দেয়।

// 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);

অভ্যন্তরীণভাবে, জাভার createPublication এবং publish API-গুলো নেটিভ ASDVGateway_Client_createPublication এবং ASDVGateway_Client_readAvailableMessages API-গুলোর উপর নির্ভর করে। C API সম্পর্কে বিস্তারিত তথ্যের জন্য, "Use SDV Gateway on an IVI native service" দেখুন। Publisher অবজেক্টটি মেসেজ লেখার এবং পাবলিকেশনের লাইফসাইকেল পরিচালনা করার জন্য একটি কনটেক্সট প্রদান করে।

নোটিফিকেশন লিসেনার সহ সাবস্ক্রাইবার তৈরি করুন

জাভা এপিআই অনুযায়ী, `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();

কোডের নমুনা

অন্যান্য সাবস্ক্রিপশনের জন্য নোটিফিকেশন পেতে বিলম্ব রোধ করতে, লিসেনারের অভ্যন্তরীণ প্রসেসিং কমানোর উদ্দেশ্যে ক্লায়েন্ট দ্বারা পরিচালিত একটিমাত্র থ্রেডে লিসেনার কলব্যাকগুলো আহ্বান করা হয়। জাভা লেয়ারটি সাবস্ক্রিপশন ব্যবস্থাপনা, নোটিফিকেশন হ্যান্ডলিং এবং মেসেজ পুনরুদ্ধারের জন্য একটি C API ব্যবহার করে। API ব্যবহারের একটি প্রদর্শনের জন্য, system/software_defined_vehicle/samples/sdv_gateway/README.md এ থাকা বাইনারি ফাইলে প্রদত্ত জাভা অ্যাপের নমুনাটি দেখুন।

প্রয়োজনীয় অনুমতি

SDV গেটওয়ে ক্লায়েন্ট API কল করার জন্য বিশেষ অনুমতির প্রয়োজন হয় না, তবে আপনার অ্যাপগুলোর জন্য যথাযথ SELinux নিয়ম প্রয়োগ করতে হবে।

  • ডেটা টানেল সাবস্ক্রিপশন এবং পাবলিকেশনের জন্য আপনার কোনো অনুমতির প্রয়োজন নেই।
  • SDV RPC-এর জন্য আপনার নিম্নলিখিত অনুমতিগুলো প্রয়োজন:
    • android.permission.INTERNET
    • android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS

android.permission.CONNECTIVITY_USE_RESTRICTED_NETWORKS এর জন্য, আপনার অ্যাপের পার্টিশনেই etc/permissions এর অধীনে একটি allowlist ফাইলও প্রয়োজন হবে। উদাহরণস্বরূপ, 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)

SDV গেটওয়ে ব্যবহারের অনুমতিপ্রাপ্ত জাভা অ্যাপগুলোর জন্য OEM my_oem_sdv_gateway_client_app ’ ডোমেইনটি নির্ধারণ করে দেয়। শুধুমাত্র সিস্টেম এবং বিশেষাধিকারপ্রাপ্ত অ্যাপগুলো থেকে SDV গেটওয়ে ব্যবহার করুন।

কোড অবস্থান

SDV গেটওয়ের সোর্স কোড system/software_defined_vehicle/sdv_gateway/ থেকে পাওয়া যাবে। আপনি নিম্নলিখিতগুলোর জন্য SDV গেটওয়ের স্যাম্পল পেতে পারেন:

  • ক্লায়েন্ট সি এপিআই: system/software_defined_vehicle/samples/sdv_gateway/NativeSdvGatewayTestApp/
  • ক্লায়েন্ট জাভা এপিআই: system/software_defined_vehicle/samples/sdv_gateway/SdvCarMonitorTestApp/