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

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

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

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

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

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

  • Power Management API का इस्तेमाल करके, क्लाइंट ऑक्यूपंट ज़ोन में दिखाया जाता है.

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

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

कनेक्शन के लिए, Discovery API और Connection API की ज़रूरत होती है. द पावर management API ज़रूरी नहीं है.

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

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

AbstrackReceiverService को लागू करें

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()is invoked whenMyReceiverServicehas received aPayloadfrom the sender client.MyReceiverService` को इसे बदल सकता है करने का तरीका:

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

या,

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

AbsडेटReceiverService का एलान करें

पाने वाले ऐप्लिकेशन को, लागू किए गए 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() शुरू हो जाएगा. जैसे इसकी जानकारी (भेजने वाले) अनुरोध कनेक्शन में दी गई है, 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, और बाइट अरे को उम्मीद के मुताबिक डेटा ऑब्जेक्ट में डीसीरियलाइज़ करता है. उदाहरण के लिए, अगर ईमेल भेजने वाला व्यक्ति, पाने वाले को स्ट्रिंग hello भेजना चाहता है FragmentB आईडी वाला एंडपॉइंट, डेटा टाइप तय करने के लिए प्रोटो बफ़र का इस्तेमाल कर सकता है इस तरह:

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

पहली इमेज में 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 सबसे अलग होते हैं. receiverEndpointId AbstractReceiverService इसका इस्तेमाल करके यह तय करेगा कि कौनसा रिसीवर पेलोड को भेजने के लिए एंडपॉइंट. उदाहरण के लिए:

  • भेजने वाले ने Payload में receiver_endpoint_id:FragmentB के बारे में बताया है. टास्क कब शुरू होगा Payload, रिसीवर कॉल में AbstractReceiverService पेलोड इस पते पर भेजने के लिए forwardPayload("FragmentB", payload) FragmentB
  • भेजने वाले ने 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
    

Null Car RemoteDeviceManager और 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() इस ईमेल पते से, किसी भी StateCallback को रजिस्टर नहीं किया गया है CarRemoteDeviceManager इंस्टेंस.
  • registerReceiver() receiverEndpointId पहले से रजिस्टर है.
  • unregisterReceiver() receiverEndpointId रजिस्टर नहीं किया गया है.
  • requestConnection() ऐसा कनेक्शन पहले से मौजूद है जिसे मंज़ूरी मिलना बाकी है या पहले से मौजूद है.
  • cancelConnection() रद्द करने के लिए कोई कनेक्शन बाकी नहीं है.
  • sendPayload() कोई स्थापित कनेक्शन नहीं है.
  • disconnect() कोई स्थापित कनेक्शन नहीं है.

Client1, Client2 को पेलोड भेज सकता है, लेकिन उसका उलटा नहीं

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