मल्टी-डिसप्ले कम्यूनिकेशन एपीआई

मल्टी-डिसप्ले कम्यूनिकेशन एपीआई का इस्तेमाल, AAOS में सिस्टम की खास सुविधाओं वाले ऐप्लिकेशन के लिए किया जा सकता है. इससे, कार के किसी दूसरे ऑक्यूपंट ज़ोन में चल रहे उसी ऐप्लिकेशन (एक ही पैकेज का नाम) से कम्यूनिकेट किया जा सकता है. इस पेज पर, एपीआई को इंटिग्रेट करने का तरीका बताया गया है. ज़्यादा जानने के लिए, CarOccupantZoneManager.OccupantZoneInfo देखें.

ऑक्यूपंट ज़ोन

ऑक्युपेंट ज़ोन का कॉन्सेप्ट, किसी उपयोगकर्ता को डिसप्ले के सेट से मैप करता है. हर ऑक्युपेंट ज़ोन में एक डिसप्ले होता है, जिसका टाइप DISPLAY_TYPE_MAIN होता है. ऑक्युपेंट ज़ोन में क्लस्टर डिसप्ले जैसे अन्य डिसप्ले भी हो सकते हैं. हर ऑक्युपेंट ज़ोन को एक Android उपयोगकर्ता असाइन किया जाता है. हर उपयोगकर्ता के पास अपने खाते और ऐप्लिकेशन होते हैं.

हार्डवेयर कॉन्फ़िगरेशन

Comms API सिर्फ़ एक SoC के साथ काम करता है. एक SoC मॉडल में, सभी ऑक्युपेंटज़ोन और उपयोगकर्ता एक ही SoC पर काम करते हैं. Comms API में तीन कॉम्पोनेंट होते हैं:

  • पावर मैनेजमेंट एपीआई की मदद से, क्लाइंट, ऑक्युपेंट ज़ोन में डिसप्ले की पावर को मैनेज कर सकता है.

  • डिस्कवरी एपीआई की मदद से, क्लाइंट कार में मौजूद अन्य ऑक्यूपंट ज़ोन की स्थितियों को मॉनिटर कर सकता है. साथ ही, उन ऑक्यूपंट ज़ोन में मौजूद मिलते-जुलते क्लाइंट को मॉनिटर कर सकता है. Connection API का इस्तेमाल करने से पहले, डिस्कवरी एपीआई का इस्तेमाल करें.

  • Connection API की मदद से, क्लाइंट किसी दूसरे ऑक्युपेंट ज़ोन में मौजूद अपने पीयर क्लाइंट से कनेक्ट कर सकता है और पीयर क्लाइंट को पेलोड भेज सकता है.

कनेक्शन के लिए, डिस्कवरी एपीआई और कनेक्शन एपीआई की ज़रूरत होती है. Power management API का इस्तेमाल करना ज़रूरी नहीं है.

Comms API, अलग-अलग ऐप्लिकेशन के बीच कम्यूनिकेशन की सुविधा नहीं देता. इसके बजाय, इसे सिर्फ़ एक ही पैकेज नाम वाले ऐप्लिकेशन के बीच कम्यूनिकेशन के लिए डिज़ाइन किया गया है. साथ ही, इसका इस्तेमाल सिर्फ़ अलग-अलग दिखने वाले उपयोगकर्ताओं के बीच कम्यूनिकेशन के लिए किया जाता है.

इंटिग्रेशन गाइड

AbstractReceiverService को लागू करना

Payload पाने के लिए, AbstractReceiverService में बताए गए एब्स्ट्रैक्ट तरीके, ऐप्लिकेशन में लागू होने चाहिए. उदाहरण के लिए:

public class MyReceiverService extends AbstractReceiverService {

    @Override
    public void onConnectionInitiated(@NonNull OccupantZoneInfo senderZone) {
    }

    @Override
    public void onPayloadReceived(@NonNull OccupantZoneInfo senderZone,
            @NonNull Payload payload) {
    }
}

onConnectionInitiated() तब चालू होता है, जब भेजने वाला क्लाइंट, इस रिसीवर क्लाइंट से कनेक्शन का अनुरोध करता है. अगर कनेक्शन बनाने के लिए उपयोगकर्ता की पुष्टि ज़रूरी है, तो अनुमति ऐक्टिविटी को लॉन्च करने के लिए, MyReceiverService इस तरीके को ओवरराइड कर सकता है. साथ ही, नतीजे के आधार पर acceptConnection() या rejectConnection() को कॉल कर सकता है. इसके अलावा, MyReceiverService सिर्फ़ acceptConnection() को कॉल कर सकता है.

onPayloadReceived() तब चालू होता है, जब MyReceiverService को भेजने वाले क्लाइंट से Payload मिलता है. MyReceiverService इस तरीके को बदल सकता है, ताकि:

  • अगर कोई है, तो Payload को उससे जुड़े ईंडपॉइंट पर फ़ॉरवर्ड करें. रजिस्टर किए गए रिसीवर एंडपॉइंट पाने के लिए, getAllReceiverEndpoints() को कॉल करें. Payload को किसी खास पाने वाले एंडपॉइंट पर फ़ॉरवर्ड करने के लिए, forwardPayload() को कॉल करें

या,

  • Payload को कैश मेमोरी में सेव करें और उसे तब भेजें, जब ईमेल पाने वाले का एंडपॉइंट रजिस्टर हो जाए. इसकी सूचना MyReceiverService को onReceiverRegistered() के ज़रिए दी जाती है

AbstractReceiverService का एलान करना

रिसीवर ऐप्लिकेशन को अपनी मेनिफ़ेस्ट फ़ाइल में, लागू किए गए AbstractReceiverService के बारे में एलान करना होगा. साथ ही, इस सेवा के लिए कार्रवाई android.car.intent.action.RECEIVER_SERVICE के साथ इंटेंट फ़िल्टर जोड़ना होगा और android.car.occupantconnection.permission.BIND_RECEIVER_SERVICE अनुमति की ज़रूरत होगी:

<service android:name=".MyReceiverService"
         android:permission="android.car.occupantconnection.permission.BIND_RECEIVER_SERVICE"
         android:exported="true">
    <intent-filter>
        <action android:name="android.car.intent.action.RECEIVER_SERVICE" />
    </intent-filter>
</service>

android.car.occupantconnection.permission.BIND_RECEIVER_SERVICEअनुमति से यह पक्का होता है कि सिर्फ़ फ़्रेमवर्क ही इस सेवा से बंधा हो. अगर इस सेवा को अनुमति की ज़रूरत नहीं है, तो कोई दूसरा ऐप्लिकेशन इस सेवा से कनेक्ट करके, सीधे तौर पर Payload भेज सकता है.

अनुमति का एलान करना

क्लाइंट ऐप्लिकेशन को अपनी मेनिफ़ेस्ट फ़ाइल में अनुमतियों का एलान करना होगा.

<!-- This permission is needed for connection API -->
<uses-permission android:name="android.car.permission.MANAGE_OCCUPANT_CONNECTION"/>
<!-- This permission is needed for discovery API -->
<uses-permission android:name="android.car.permission.MANAGE_REMOTE_DEVICE"/>
<!-- This permission is needed if the client app calls CarRemoteDeviceManager#setOccupantZonePower() -->
<uses-permission android:name="android.car.permission.CAR_POWER"/>

ऊपर दी गई तीनों अनुमतियां, खास अनुमतियां हैं. इन्हें अनुमति वाली सूची में शामिल फ़ाइलों से पहले ही देना ज़रूरी है. उदाहरण के लिए, यहां MultiDisplayTest ऐप्लिकेशन की अनुमति वाली फ़ाइल दी गई है:

// packages/services/Car/data/etc/com.google.android.car.multidisplaytest.xml
<permissions>
    <privapp-permissions package="com.google.android.car.multidisplaytest">
         
        <permission name="android.car.permission.MANAGE_OCCUPANT_CONNECTION"/>
        <permission name="android.car.permission.MANAGE_REMOTE_DEVICE"/>
        <permission name="android.car.permission.CAR_POWER"/>
    </privapp-permissions>
</permissions>

कार मैनेजर पाना

एपीआई का इस्तेमाल करने के लिए, क्लाइंट ऐप्लिकेशन को CarServiceLifecycleListener रजिस्टर करना होगा, ताकि उससे जुड़े कार मैनेजर मिल सकें:

private CarRemoteDeviceManager mRemoteDeviceManager;
private CarOccupantConnectionManager mOccupantConnectionManager;

private final Car.CarServiceLifecycleListener mCarServiceLifecycleListener = (car, ready) -> {
   if (!ready) {
       Log.w(TAG, "Car service crashed");
       mRemoteDeviceManager = null;
       mOccupantConnectionManager = null;
       return;
   }
   mRemoteDeviceManager = car.getCarManager(CarRemoteDeviceManager.class);
   mOccupantConnectionManager = car.getCarManager(CarOccupantConnectionManager.class);
};

Car.createCar(getContext(), /* handler= */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER,
       mCarServiceLifecycleListener);

(भेजने वाला) डिस्कवर

भेजने वाले क्लाइंट को, पाने वाले क्लाइंट से कनेक्ट करने से पहले, CarRemoteDeviceManager.StateCallback रजिस्टर करके, पाने वाले क्लाइंट का पता लगाना चाहिए:

// The maps are accessed by the main thread only, so there is no multi-thread issue.
private final ArrayMap<OccupantZoneInfo, Integer> mOccupantZoneStateMap = new ArrayMap<>();
private final ArrayMap<OccupantZoneInfo, Integer> mAppStateMap = new ArrayMap<>();

private final StateCallback mStateCallback = new StateCallback() {
        @Override
        public void onOccupantZoneStateChanged(
                @androidx.annotation.NonNull OccupantZoneInfo occupantZone,
                int occupantZoneStates) {
            mOccupantZoneStateMap.put(occupantZone, occupantZoneStates);
        }
        @Override
        public void onAppStateChanged(
                @androidx.annotation.NonNull OccupantZoneInfo occupantZone,
                int appStates) {
            mAppStateMap.put(occupantZone, appStates);
        }
    };

if (mRemoteDeviceManager != null) {
   mRemoteDeviceManager.registerStateCallback(getActivity().getMainExecutor(),
           mStateCallback);
}

रिसीवर से कनेक्शन का अनुरोध करने से पहले, भेजने वाले को यह पक्का करना चाहिए कि रिसीवर के ऑक्युपेंट ज़ोन और रिसीवर ऐप्लिकेशन के सभी फ़्लैग सेट हों. ऐसा न करने पर, गड़बड़ियां हो सकती हैं. उदाहरण के लिए:

private boolean canRequestConnectionToReceiver(OccupantZoneInfo receiverZone) {
    Integer zoneState = mOccupantZoneStateMap.get(receiverZone);
    if ((zoneState == null) || (zoneState.intValue() & (FLAG_OCCUPANT_ZONE_POWER_ON
            // FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED is not implemented yet. Right now
            // just ignore this flag.
            //  | FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED
            | FLAG_OCCUPANT_ZONE_CONNECTION_READY)) == 0) {
        return false;
    }
    Integer appState = mAppStateMap.get(receiverZone);
    if ((appState == null) ||
        (appState.intValue() & (FLAG_CLIENT_INSTALLED
            | FLAG_CLIENT_SAME_LONG_VERSION | FLAG_CLIENT_SAME_SIGNATURE
            | FLAG_CLIENT_RUNNING | FLAG_CLIENT_IN_FOREGROUND)) == 0) {
        return false;
    }
    return true;
}

हमारा सुझाव है कि ईमेल भेजने वाला व्यक्ति, ईमेल पाने वाले व्यक्ति से कनेक्शन का अनुरोध सिर्फ़ तब करे, जब ईमेल पाने वाले व्यक्ति के सभी फ़्लैग सेट हों. हालांकि, इसके कुछ अपवाद हैं:

  • FLAG_OCCUPANT_ZONE_CONNECTION_READY और FLAG_CLIENT_INSTALLED, कनेक्शन बनाने के लिए ज़रूरी शर्तें हैं.

  • अगर रिसीवर ऐप्लिकेशन को कनेक्शन के लिए उपयोगकर्ता की अनुमति पाने के लिए यूज़र इंटरफ़ेस (यूआई) दिखाना है, तो FLAG_OCCUPANT_ZONE_POWER_ON और FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED अतिरिक्त ज़रूरी शर्तें बन जाती हैं. बेहतर उपयोगकर्ता अनुभव के लिए, FLAG_CLIENT_RUNNING और FLAG_CLIENT_IN_FOREGROUND का भी सुझाव दिया जाता है. ऐसा न करने पर, उपयोगकर्ता को आश्चर्य हो सकता है.

  • फ़िलहाल (Android 15), FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED लागू नहीं है. क्लाइंट ऐप्लिकेशन, इस 'एट्रिब्यूट' को अनदेखा कर सकता है.

  • फ़िलहाल (Android 15), Comms API सिर्फ़ एक ही Android इंस्टेंस पर कई उपयोगकर्ताओं के साथ काम करता है, ताकि पीयर ऐप्लिकेशन में एक ही लंबा वर्शन कोड (FLAG_CLIENT_SAME_LONG_VERSION) और सिग्नेचर (FLAG_CLIENT_SAME_SIGNATURE) हो. इस वजह से, ऐप्लिकेशन को यह पुष्टि करने की ज़रूरत नहीं होती कि दोनों वैल्यू एक जैसी हैं.

बेहतर उपयोगकर्ता अनुभव के लिए, अगर कोई फ़्लैग सेट नहीं है, तो ईमेल भेजने वाला क्लाइंट यूज़र इंटरफ़ेस (यूआई) दिखा सकता है. उदाहरण के लिए, अगर FLAG_OCCUPANT_ZONE_SCREEN_UNLOCKED सेट नहीं है, तो भेजने वाला व्यक्ति, उपयोगकर्ता को रिसीवर के ऑक्युपेंट ज़ोन की स्क्रीन अनलॉक करने के लिए, टॉस्ट या डायलॉग दिखा सकता है.

जब डिवाइस को रिसीवर ढूंढने की ज़रूरत नहीं होती, तो वह डिवाइस डिस्कवरी की प्रोसेस को रोक सकता है. उदाहरण के लिए, जब उसे सभी रिसीवर मिल जाते हैं और कनेक्टिविटी सेट अप हो जाती है या वह डिवाइस बंद हो जाता है.

if (mRemoteDeviceManager != null) {
    mRemoteDeviceManager.unregisterStateCallback();
}

डिस्कवरी बंद होने पर, मौजूदा कनेक्शन पर कोई असर नहीं पड़ता. भेजने वाला व्यक्ति, कनेक्ट किए गए रिसीवर को Payload भेजना जारी रख सकता है.

(ईमेल भेजने वाला) कनेक्शन का अनुरोध करना

जब रिसीवर के सभी फ़्लैग सेट होते हैं, तो भेजने वाला व्यक्ति रिसीवर से कनेक्शन का अनुरोध कर सकता है:

    private final ConnectionRequestCallback mRequestCallback = new ConnectionRequestCallback() {
        @Override
        public void onConnected(OccupantZoneInfo receiverZone) {
        }

        @Override
        public void onFailed(OccupantZoneInfo receiverZone, int connectionError) {
        }

        @Override
        public void onDisconnected(OccupantZoneInfo receiverZone) {
        }
    };

if (mOccupantConnectionManager != null && canRequestConnectionToReceiver(receiverZone)) {
    mOccupantConnectionManager.requestConnection(receiverZone,
                getActivity().getMainExecutor(), mRequestCallback);
}

(रिसीवर सेवा) कनेक्शन स्वीकार करना

जब भेजने वाला व्यक्ति, पाने वाले व्यक्ति से कनेक्ट करने का अनुरोध करता है, तो पाने वाले व्यक्ति के ऐप्लिकेशन में मौजूद AbstractReceiverService को कार सेवा से बंधा दिया जाएगा. साथ ही, AbstractReceiverService.onConnectionInitiated() को चालू कर दिया जाएगा. (Sender) Request Connection में बताए गए तरीके के मुताबिक, onConnectionInitiated() एक एब्स्ट्रैक्टेड तरीका है. इसे क्लाइंट ऐप्लिकेशन को लागू करना ज़रूरी है.

जब डिवाइस इस्तेमाल करने वाला व्यक्ति, कनेक्ट करने का अनुरोध स्वीकार करता है, तो भेजने वाले व्यक्ति का ConnectionRequestCallback.onConnected() ट्रिगर हो जाएगा. इसके बाद, कनेक्शन स्थापित हो जाएगा.

(पहला पक्ष) पेलोड भेजना

कनेक्शन बन जाने के बाद, भेजने वाला व्यक्ति, पाने वाले को Payload भेज सकता है:

if (mOccupantConnectionManager != null) {
    Payload payload = ...;
    try {
        mOccupantConnectionManager.sendPayload(receiverZone, payload);
    } catch (CarOccupantConnectionManager.PayloadTransferException e) {
        Log.e(TAG, "Failed to send Payload to " + receiverZone);
    }
}

भेजने वाला व्यक्ति, Payload में Binder ऑब्जेक्ट या बाइट कलेक्शन डाल सकता है. अगर भेजने वाले को किसी दूसरे डेटा टाइप को भेजना है, तो उसे डेटा को बाइट कलेक्शन में सीरियलाइज़ करना होगा. साथ ही, Payload ऑब्जेक्ट बनाने के लिए बाइट कलेक्शन का इस्तेमाल करना होगा और Payload भेजना होगा. इसके बाद, रिसीवर क्लाइंट को मिले Payload से बाइट कलेक्शन मिलता है. साथ ही, बाइट कलेक्शन को उम्मीद के मुताबिक डेटा ऑब्जेक्ट में बदल दिया जाता है. उदाहरण के लिए, अगर भेजने वाला व्यक्ति, आईडी FragmentB वाले पाने वाले एंडपॉइंट को स्ट्रिंग hello भेजना चाहता है, तो वह डेटा टाइप तय करने के लिए, Proto Buffers का इस्तेमाल कर सकता है. जैसे:

message MyData {
  required string receiver_endpoint_id = 1;
  required string data = 2;
}

पहली इमेज में Payload फ़्लो को दिखाया गया है:

पेलोड भेजना

पहली इमेज. पेलोड भेजें.

(Receiver service) Receive and dispatch the payload

रिसीवर ऐप्लिकेशन को Payload मिलने के बाद, उसका AbstractReceiverService.onPayloadReceived() ट्रिगर हो जाएगा. पेलोड भेजें में बताए गए तरीके के मुताबिक, onPayloadReceived() एक ऐसा तरीका है जिसे क्लाइंट ऐप्लिकेशन को लागू करना ज़रूरी है. इस तरीके में, क्लाइंट Payload को उससे जुड़े रिसीवर एंडपॉइंट पर फ़ॉरवर्ड कर सकता है या Payload को कैश मेमोरी में सेव कर सकता है. इसके बाद, रिसीवर एंडपॉइंट रजिस्टर होने पर उसे भेज सकता है.

(रिसीवर एंडपॉइंट) रजिस्टर और अनरजिस्टर करना

रिसीवर ऐप्लिकेशन को रिसीवर एंडपॉइंट रजिस्टर करने के लिए, registerReceiver() को कॉल करना चाहिए. इस्तेमाल का एक सामान्य उदाहरण यह है कि किसी फ़्रैगमेंट को Payload पाने की ज़रूरत है, इसलिए वह रिसीवर एंडपॉइंट रजिस्टर करता है:

private final PayloadCallback mPayloadCallback = (senderZone, payload) -> {
    
};

if (mOccupantConnectionManager != null) {
    mOccupantConnectionManager.registerReceiver("FragmentB",
                getActivity().getMainExecutor(), mPayloadCallback);
}

जब रिसीवर क्लाइंट में मौजूद AbstractReceiverService, रिसीवर एंडपॉइंट पर Payload भेजता है, तो उससे जुड़ा PayloadCallback ट्रिगर हो जाएगा.

क्लाइंट ऐप्लिकेशन, एक से ज़्यादा रिसीवर एंडपॉइंट रजिस्टर कर सकता है. हालांकि, इसके लिए ज़रूरी है कि क्लाइंट ऐप्लिकेशन में उनके receiverEndpointId यूनीक हों. AbstractReceiverService, receiverEndpointId का इस्तेमाल करके यह तय करेगा कि पल्यलोड को किस रिसीवर एंडपॉइंट पर भेजना है. उदाहरण के लिए:

  • मैसेज भेजने वाले ने Payload में receiver_endpoint_id:FragmentB की जानकारी दी है. Payload पाने पर, रिसीवर में मौजूद AbstractReceiverService, FragmentB को पेलोड भेजने के लिए forwardPayload("FragmentB", payload) को कॉल करता है
  • मैसेज भेजने वाले ने Payload में data_type:VOLUME_CONTROL की जानकारी दी है. Payload पाने पर, रिसीवर में मौजूद AbstractReceiverService को पता होता है कि इस तरह के Payload को FragmentB को भेजा जाना चाहिए. इसलिए, वह forwardPayload("FragmentB", payload) को कॉल करता है
if (mOccupantConnectionManager != null) {
    mOccupantConnectionManager.unregisterReceiver("FragmentB");
}

(भेजने वाला) कनेक्शन खत्म करना

जब भेजने वाले को पाने वाले को Payload भेजने की ज़रूरत नहीं होती (उदाहरण के लिए, वह बंद हो जाता है), तो उसे कनेक्शन बंद कर देना चाहिए.

if (mOccupantConnectionManager != null) {
    mOccupantConnectionManager.disconnect(receiverZone);
}

डिसकनेक्ट होने के बाद, ईमेल भेजने वाला व्यक्ति ईमेल पाने वाले व्यक्ति को Payload नहीं भेज सकता.

कनेक्शन फ़्लो

कनेक्शन फ़्लो को दूसरी इमेज में दिखाया गया है.

कनेक्शन फ़्लो

दूसरी इमेज. कनेक्शन फ़्लो.

समस्या का हल

लॉग देखना

उससे जुड़े लॉग देखने के लिए:

  1. लॉगिंग के लिए यह कमांड चलाएं:

    adb shell setprop log.tag.CarRemoteDeviceService VERBOSE && adb shell setprop log.tag.CarOccupantConnectionService VERBOSE && adb logcat -s "AbstractReceiverService","CarOccupantConnectionManager","CarRemoteDeviceManager","CarRemoteDeviceService","CarOccupantConnectionService"
  2. CarRemoteDeviceService और CarOccupantConnectionService की इंटरनल स्टेटस को डंप करने के लिए:

    adb shell dumpsys car_service --services CarRemoteDeviceService && adb shell dumpsys car_service --services CarOccupantConnectionService

CarRemoteDeviceManager और CarOccupantConnectionManager को शून्य पर सेट करना

इसकी ये मुख्य वजहें हो सकती हैं:

  1. कार सेवा क्रैश हो गई. जैसा कि पहले बताया गया है, कार सेवा के क्रैश होने पर, दोनों मैनेजर को null पर जान-बूझकर रीसेट किया जाता है. कार सेवा को रीस्टार्ट करने पर, दोनों मैनेजर को नॉन-शून्य वैल्यू पर सेट किया जाता है.

  2. CarRemoteDeviceService या CarOccupantConnectionService में से कोई एक प्रॉपर्टी चालू नहीं है. यह पता करने के लिए कि इनमें से कोई एक या दोनों चालू हैं या नहीं, यह चलाएं:

    adb shell dumpsys car_service --services CarFeatureController
    • mDefaultEnabledFeaturesFromConfig ढूंढें, जिसमें car_remote_device_service और car_occupant_connection_service शामिल होने चाहिए. उदाहरण के लिए:

      mDefaultEnabledFeaturesFromConfig:[car_evs_service, car_navigation_service, car_occupant_connection_service, car_remote_device_service, car_telemetry_service, cluster_home_service, com.android.car.user.CarUserNoticeService, diagnostic, storage_monitoring, vehicle_map_service]
      
    • डिफ़ॉल्ट रूप से, ये दोनों सेवाएं बंद रहती हैं. अगर कोई डिवाइस कई डिसप्ले के साथ काम करता है, तो आपको इस कॉन्फ़िगरेशन फ़ाइल को ओवरले करना होगा. कॉन्फ़िगरेशन फ़ाइल में, दोनों सेवाओं को चालू किया जा सकता है:

      // packages/services/Car/service/res/values/config.xml
      <string-array translatable="false" name="config_allowed_optional_car_features">
           <item>car_occupant_connection_service</item>
           <item>car_remote_device_service</item>
            
      </string-array>
      

एपीआई को कॉल करते समय अपवाद

अगर क्लाइंट ऐप्लिकेशन, एपीआई का सही तरीके से इस्तेमाल नहीं कर रहा है, तो कोई अपवाद हो सकता है. इस मामले में, क्लाइंट ऐप्लिकेशन समस्या को हल करने के लिए, अपवाद और क्रैश स्टैक में मैसेज देख सकता है. एपीआई के गलत इस्तेमाल के उदाहरण:

  • registerStateCallback() इस क्लाइंट ने पहले ही StateCallback रजिस्टर कर लिया है.
  • unregisterStateCallback() इस CarRemoteDeviceManager इंस्टेंस से कोई StateCallback रजिस्टर नहीं किया गया था.
  • registerReceiver() receiverEndpointId पहले से रजिस्टर है.
  • unregisterReceiver() receiverEndpointId को रजिस्टर नहीं किया गया है.
  • requestConnection() कोई कनेक्शन पहले से मौजूद है या उसे मंज़ूरी मिलना बाकी है.
  • cancelConnection() रद्द करने के लिए कोई कनेक्शन नहीं है.
  • sendPayload() कोई कनेक्शन नहीं है.
  • disconnect() कोई कनेक्शन नहीं है.

क्लाइंट 1, क्लाइंट 2 को पेलोड भेज सकता है, लेकिन क्लाइंट 2, क्लाइंट 1 को पेलोड नहीं भेज सकता

डिज़ाइन के हिसाब से, यह कनेक्शन एकतरफा है. दो-तरफ़ा कनेक्शन बनाने के लिए, client1 और client2, दोनों को एक-दूसरे से कनेक्शन का अनुरोध करना होगा और फिर अनुमति लेनी होगी.