মাল্টি-ডিসপ্লে কমিউনিকেশন API

মাল্টি-ডিসপ্লে কমিউনিকেশন এপিআই AAOS-এর একটি সিস্টেমের সুবিধাপ্রাপ্ত অ্যাপ ব্যবহার করতে পারে একটি গাড়িতে ভিন্ন অকুপ্যান্ট জোনে চলমান একই অ্যাপের (একই প্যাকেজের নাম) সাথে যোগাযোগ করতে। এই পৃষ্ঠাটি বর্ণনা করে কিভাবে API একত্রিত করতে হয়। আরও জানতে, আপনি CarOccupantZoneManager.OccupantZoneInfo দেখতে পারেন।

দখলদার অঞ্চল

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

হার্ডওয়্যার কনফিগারেশন

Comms API শুধুমাত্র একটি একক SoC সমর্থন করে। একক SoC মডেলে, সমস্ত দখলকারী অঞ্চল এবং ব্যবহারকারীরা একই SoC-তে চলে। Comms API তিনটি উপাদান নিয়ে গঠিত:

  • পাওয়ার ম্যানেজমেন্ট API ক্লায়েন্টকে অকুপ্যান্ট জোনে প্রদর্শনের শক্তি পরিচালনা করতে দেয়।

  • ডিসকভারি এপিআই ক্লায়েন্টকে গাড়িতে থাকা অন্যান্য অকুপ্যান্ট জোনের অবস্থা পর্যবেক্ষণ করতে এবং সেই অকুপ্যান্ট জোনে পিয়ার ক্লায়েন্টদের নিরীক্ষণ করতে দেয়। সংযোগ API ব্যবহার করার আগে আবিষ্কার API ব্যবহার করুন.

  • কানেকশন এপিআই ক্লায়েন্টকে তার পিয়ার ক্লায়েন্টের সাথে অন্য অকুপ্যান্ট জোনে সংযোগ করতে এবং পিয়ার ক্লায়েন্টকে একটি পেলোড পাঠাতে দেয়।

সংযোগের জন্য ডিসকভারি API এবং সংযোগ API প্রয়োজন৷ পাওয়ার ম্যানেজমেন্ট 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() is invoked when MyReceiverService from the sender client. has received a । 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>

কার ম্যানেজার পান

API ব্যবহার করার জন্য, ক্লায়েন্ট অ্যাপটিকে অবশ্যই একটি 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 হল একটি সংযোগ স্থাপনের জন্য প্রয়োজনীয় নূন্যতম প্রয়োজনীয়তা৷

  • সংযোগের ব্যবহারকারীর অনুমোদন পেতে যদি রিসিভার অ্যাপটিকে একটি UI প্রদর্শন করতে হয়, তাহলে 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 শুধুমাত্র একই অ্যান্ড্রয়েড ইন্সট্যান্সে একাধিক ব্যবহারকারীকে সমর্থন করে যাতে পিয়ার অ্যাপগুলির একই দীর্ঘ সংস্করণ কোড ( FLAG_CLIENT_SAME_LONG_VERSION ) এবং স্বাক্ষর ( FLAG_CLIENT_SAME_SIGNATURE ) থাকতে পারে৷ ফলস্বরূপ, অ্যাপগুলিকে যাচাই করতে হবে না যে দুটি মান একমত।

একটি ভাল ব্যবহারকারীর অভিজ্ঞতার জন্য, প্রেরক ক্লায়েন্ট একটি UI দেখাতে পারে যদি একটি পতাকা সেট না থাকে৷ উদাহরণস্বরূপ, যদি 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 থেকে বাইট অ্যারে পায় এবং প্রত্যাশিত ডেটা অবজেক্টে বাইট অ্যারেকে ডিসিরিয়ালাইজ করে। উদাহরণস্বরূপ, যদি প্রেরক আইডি FragmentB সহ রিসিভার এন্ডপয়েন্টে একটি স্ট্রিং hello পাঠাতে চান, তাহলে এটি এই ধরনের ডেটা টাইপ সংজ্ঞায়িত করতে প্রোটো বাফার ব্যবহার করতে পারে:

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

চিত্র 1 Payload প্রবাহকে চিত্রিত করে:

পেলোড পাঠান

চিত্র 1. পেলোড পাঠান।

(রিসিভার পরিষেবা) পেলোড গ্রহণ করুন এবং প্রেরণ করুন

একবার রিসিভার অ্যাপ Payload গ্রহণ করলে, এর AbstractReceiverService.onPayloadReceived() চালু করা হবে। Send the payload- এ যেমন ব্যাখ্যা করা হয়েছে, 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 পাঠাতে পারবেন না।

সংযোগ প্রবাহ

একটি সংযোগ প্রবাহ চিত্র 2 এ চিত্রিত করা হয়েছে।

সংযোগ প্রবাহ

চিত্র 2. সংযোগ প্রবাহ।

সমস্যা সমাধান

লগ চেক করুন

সংশ্লিষ্ট লগ চেক করতে:

  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>
      

API কল করার সময় ব্যতিক্রম

যদি ক্লায়েন্ট অ্যাপটি উদ্দেশ্য অনুযায়ী API ব্যবহার না করে, তাহলে একটি ব্যতিক্রম ঘটতে পারে। এই ক্ষেত্রে, ক্লায়েন্ট অ্যাপ সমস্যাটি সমাধান করতে ব্যতিক্রম এবং ক্র্যাশ স্ট্যাকের বার্তাটি পরীক্ষা করতে পারে। API অপব্যবহারের উদাহরণ হল:

  • registerStateCallback() এই ক্লায়েন্টটি ইতিমধ্যে একটি StateCallback নিবন্ধন করেছে।
  • unregisterStateCallback() এই CarRemoteDeviceManager দৃষ্টান্ত দ্বারা কোন StateCallback নিবন্ধিত হয়নি।
  • registerReceiver() receiverEndpointId ইতিমধ্যেই নিবন্ধিত।
  • unregisterReceiver() receiverEndpointId নিবন্ধিত নয়।
  • requestConnection() একটি মুলতুবি বা প্রতিষ্ঠিত সংযোগ ইতিমধ্যেই বিদ্যমান।
  • cancelConnection() বাতিল করার জন্য কোনো মুলতুবি সংযোগ নেই।
  • sendPayload() কোনো প্রতিষ্ঠিত সংযোগ নেই।
  • disconnect() কোনো প্রতিষ্ঠিত সংযোগ নেই।

ক্লায়েন্ট 1 পেলোড ক্লায়েন্ট 2 এ পাঠাতে পারে, তবে অন্যভাবে নয়

সংযোগ নকশা দ্বারা এক উপায়. দ্বি-মুখী সংযোগ স্থাপনের জন্য, client1 এবং client2 উভয়কেই একে অপরের সাথে সংযোগের অনুরোধ করতে হবে এবং তারপরে অনুমোদন পেতে হবে।