একটি এআইডিএল ব্যাকএন্ড হল স্টাব কোড তৈরির লক্ষ্য। একটি নির্দিষ্ট রানটাইম সহ একটি নির্দিষ্ট ভাষায় সর্বদা AIDL ফাইলগুলি ব্যবহার করুন। প্রেক্ষাপটের উপর নির্ভর করে, আপনার বিভিন্ন AIDL ব্যাকএন্ড ব্যবহার করা উচিত।
নিম্নলিখিত সারণীতে, API পৃষ্ঠের স্থায়িত্ব বলতে এই API পৃষ্ঠের বিরুদ্ধে কোড কম্পাইল করার ক্ষমতাকে বোঝায় যাতে কোডটি system.img
libbinder.so
বাইনারি থেকে স্বাধীনভাবে বিতরণ করা যায়।
AIDL এর নিম্নলিখিত ব্যাকএন্ড রয়েছে:
ব্যাকএন্ড | ভাষা | API পৃষ্ঠ | সিস্টেম তৈরি করুন |
---|---|---|---|
জাভা | জাভা | SDK বা SystemApi (স্থিতিশীল*) | সব |
এনডিকে | সি++ | libbinder_ndk (স্থিতিশীল*) | aidl_interface |
সিপিপি | সি++ | libbinder (অস্থির) | সব |
মরিচা | মরিচা | libbinder_rs (স্থিতিশীল*) | aidl_interface |
- এই API পৃষ্ঠতলগুলি স্থিতিশীল, কিন্তু অনেক API, যেমন পরিষেবা পরিচালনার জন্য, অভ্যন্তরীণ প্ল্যাটফর্ম ব্যবহারের জন্য সংরক্ষিত এবং অ্যাপগুলিতে উপলব্ধ নয়। অ্যাপে এআইডিএল কীভাবে ব্যবহার করবেন সে সম্পর্কে আরও তথ্যের জন্য, অ্যান্ড্রয়েড ইন্টারফেস ডেফিনিশন ল্যাঙ্গুয়েজ (এআইডিএল) দেখুন।
- মরিচা ব্যাকএন্ড অ্যান্ড্রয়েড 12 এ চালু করা হয়েছিল; এনডিকে ব্যাকএন্ড অ্যান্ড্রয়েড 10 হিসাবে উপলব্ধ।
- মরিচা ক্রেটটি
libbinder_ndk
এর উপরে নির্মিত, যা এটিকে স্থিতিশীল এবং বহনযোগ্য হতে দেয়। APEXes সিস্টেমের দিকে স্ট্যান্ডার্ড উপায়ে বাইন্ডার ক্রেট ব্যবহার করে। মরিচা অংশটি একটি এপেক্সে বান্ডিল করা হয় এবং এর ভিতরে পাঠানো হয়। এই অংশটিlibbinder_ndk.so
সিস্টেম পার্টিশনের উপর নির্ভর করে।
সিস্টেম তৈরি করুন
ব্যাকএন্ডের উপর নির্ভর করে, স্টাব কোডে AIDL কম্পাইল করার দুটি উপায় রয়েছে। বিল্ড সিস্টেম সম্পর্কে আরো বিস্তারিত জানার জন্য, সুং মডিউল রেফারেন্স দেখুন।
কোর বিল্ড সিস্টেম
যেকোন cc_
বা java_
Android.bp module
(বা তাদের Android.mk
সমতুল্য), আপনি AIDL ( .aidl
) ফাইলগুলিকে সোর্স ফাইল হিসেবে নির্দিষ্ট করতে পারেন। এই ক্ষেত্রে, AIDL এর জাভা বা CPP ব্যাকএন্ড ব্যবহার করা হয় (NDK ব্যাকএন্ড নয়), এবং সংশ্লিষ্ট AIDL ফাইলগুলি ব্যবহার করার জন্য ক্লাসগুলি স্বয়ংক্রিয়ভাবে মডিউলে যোগ করা হয়। আপনি একটি aidl:
গ্রুপের অধীনে এই মডিউলগুলিতে local_include_dirs
(যা বিল্ড সিস্টেমকে সেই মডিউলে AIDL ফাইলের রুট পাথ বলে) মত বিকল্পগুলি নির্দিষ্ট করতে পারেন।
মরিচা ব্যাকএন্ড শুধুমাত্র মরিচা ব্যবহার করার জন্য। rust_
মডিউলগুলি ভিন্নভাবে পরিচালনা করা হয় যাতে AIDL ফাইলগুলি উত্স ফাইল হিসাবে নির্দিষ্ট করা হয় না। পরিবর্তে, aidl_interface
মডিউলটি aidl_interface_name -rust
নামক একটি rustlib
তৈরি করে, যার সাথে লিঙ্ক করা যেতে পারে। বিস্তারিত জানার জন্য, মরিচা এআইডিএল উদাহরণ দেখুন।
aidl_interface
aidl_interface
বিল্ড সিস্টেমের সাথে ব্যবহৃত প্রকারগুলি অবশ্যই কাঠামোগত হতে হবে। কাঠামোবদ্ধ হওয়ার জন্য, পার্সেবলে অবশ্যই সরাসরি ক্ষেত্র থাকতে হবে এবং লক্ষ্য ভাষায় সরাসরি সংজ্ঞায়িত প্রকারের ঘোষণা হবে না। স্থিতিশীল AIDL-এর সাথে কীভাবে কাঠামোগত AIDL ফিট করে, দেখুন স্ট্রাকচার্ড বনাম স্থিতিশীল AIDL ।
প্রকারভেদ
প্রকারের জন্য একটি রেফারেন্স বাস্তবায়ন হিসাবে aidl
কম্পাইলার বিবেচনা করুন। যখন আপনি একটি ইন্টারফেস তৈরি করেন, তখন aidl --lang=<backend> ...
ব্যবহার করুন ফলে ইন্টারফেস ফাইলটি দেখতে। আপনি যখন aidl_interface
মডিউল ব্যবহার করেন, আপনি out/soong/.intermediates/ <path to module> /
এ আউটপুট দেখতে পারেন।
জাভা বা এআইডিএল টাইপ | C++ প্রকার | NDK প্রকার | মরিচা ধরন |
---|---|---|---|
boolean | bool | bool | bool |
byte 8 | int8_t | int8_t | i8 |
char | char16_t | char16_t | u16 |
int | int32_t | int32_t | i32 |
long | int64_t | int64_t | i64 |
float | float | float | f32 |
double | double | double | f64 |
String | android::String16 | std::string | ইন: &str আউট: String |
android.os.Parcelable | android::Parcelable | N/A | N/A |
IBinder | android::IBinder | ndk::SpAIBinder | binder::SpIBinder |
T[] | std::vector<T> | std::vector<T> | মধ্যে: &[T] আউট: Vec<T> |
byte[] | std::vector | std::vector 1 | মধ্যে: &[u8] আউট: Vec<u8> |
List<T> | std::vector<T> 2 | std::vector<T> 3 | In: In: &[T] 4আউট: Vec<T> |
FileDescriptor | android::base::unique_fd | N/A | N/A |
ParcelFileDescriptor | android::os::ParcelFileDescriptor | ndk::ScopedFileDescriptor | binder::parcel::ParcelFileDescriptor |
ইন্টারফেসের ধরন ( T ) | android::sp<T> | std::shared_ptr<T> 7 | binder::Strong |
পার্সেলযোগ্য প্রকার ( T ) | T | T | T |
ইউনিয়নের ধরন ( T ) 5 | T | T | T |
T[N] 6 | std::array<T, N> | std::array<T, N> | [T; N] |
1. Android 12 বা উচ্চতর সংস্করণে, বাইট অ্যারেগুলি সামঞ্জস্যের কারণে int8_t
এর পরিবর্তে uint8_t
ব্যবহার করে৷
2. C++ ব্যাকএন্ড List<T>
সমর্থন করে যেখানে T
হল String
, IBinder
, ParcelFileDescriptor
বা parcelable এর মধ্যে একটি। অ্যান্ড্রয়েড 13 বা উচ্চতর, T
অ্যারে ব্যতীত যেকোন নন-প্রিমিটিভ টাইপ (ইন্টারফেসের ধরন সহ) হতে পারে। AOSP T[]
মত অ্যারে প্রকার ব্যবহার করার পরামর্শ দেয়, কারণ তারা সমস্ত ব্যাকএন্ডে কাজ করে।
3. NDK ব্যাকএন্ড List<T>
সমর্থন করে যেখানে T
হল String
, ParcelFileDescriptor
বা পার্সেলেবলের একটি। অ্যান্ড্রয়েড 13 বা উচ্চতর সংস্করণে, T
অ্যারে ব্যতীত যেকোন নন-প্রিমিটিভ টাইপ হতে পারে।
4. রাস্ট কোডের জন্য প্রকারগুলি ভিন্নভাবে পাস করা হয় তা নির্ভর করে সেগুলি ইনপুট (একটি যুক্তি), বা একটি আউটপুট (একটি ফেরত মান)।
5. ইউনিয়ন প্রকারগুলি Android 12 এবং উচ্চতর সংস্করণে সমর্থিত।
6. অ্যান্ড্রয়েড 13 বা উচ্চতর, স্থির আকারের অ্যারে সমর্থিত। স্থির-আকারের অ্যারেতে একাধিক মাত্রা থাকতে পারে (উদাহরণস্বরূপ, int[3][4]
)। জাভা ব্যাকএন্ডে, স্থির-আকারের অ্যারেগুলিকে অ্যারের প্রকার হিসাবে উপস্থাপন করা হয়।
7. একটি বাইন্ডার SharedRefBase
অবজেক্ট ইনস্ট্যান্ট করতে, SharedRefBase::make\<My\>(... args ...)
ব্যবহার করুন। এই ফাংশনটি একটি std::shared_ptr\<T\>
অবজেক্ট তৈরি করে, যা অভ্যন্তরীণভাবেও পরিচালিত হয়, যদি বাইন্ডার অন্য প্রক্রিয়ার মালিকানাধীন হয়। বস্তুটি অন্য উপায়ে তৈরি করা দ্বিগুণ মালিকানার কারণ।
8. এছাড়াও Java বা AIDL টাইপ byte[]
।
দিকনির্দেশনা (ইন, আউট এবং ইনআউট)
ফাংশনে আর্গুমেন্টের ধরন নির্দিষ্ট করার সময়, আপনি এগুলিকে in
, out
, বা inout
হিসাবে নির্দিষ্ট করতে পারেন। এটি আইপিসি কলের জন্য তথ্য পাঠানোর দিকটিকে নিয়ন্ত্রণ করে।
in
আর্গুমেন্ট স্পেসিফায়ার নির্দেশ করে যে কলারের কাছ থেকে কলকারীর কাছে ডেটা চলে গেছে।in
স্পেসিফায়ার হল ডিফল্ট দিক, কিন্তু যদি ডেটা প্রকারগুলিওout
হতে পারে, তাহলে আপনাকে অবশ্যই দিকটি নির্দিষ্ট করতে হবে।out
আর্গুমেন্ট স্পেসিফায়ার মানে কলে থেকে কলারের কাছে ডাটা চলে যায়।inout
আর্গুমেন্ট স্পেসিফায়ার হল এই দুটির সমন্বয়। যাইহোক, আমরাinout
আর্গুমেন্ট স্পেসিফায়ার ব্যবহার এড়ানোর পরামর্শ দিচ্ছি। আপনি যদি একটি সংস্করণযুক্ত ইন্টারফেস এবং একটি পুরানো কলির সাথেinout
ব্যবহার করেন, তবে অতিরিক্ত ক্ষেত্রগুলি যেগুলি শুধুমাত্র কলারে উপস্থিত থাকে সেগুলি তাদের ডিফল্ট মানগুলিতে পুনরায় সেট করা হয়। Rust এর ক্ষেত্রে, একটি সাধারণinout
প্রকার&mut T
গ্রহণ করে এবং একটি তালিকাinout
প্রকার&mut Vec<T>
গ্রহণ করে।
interface IRepeatExamples {
MyParcelable RepeatParcelable(MyParcelable token); // implicitly 'in'
MyParcelable RepeatParcelableWithIn(in MyParcelable token);
void RepeatParcelableWithInAndOut(in MyParcelable param, out MyParcelable result);
void RepeatParcelableWithInOut(inout MyParcelable param);
}
UTF-8 এবং UTF-16
CPP ব্যাকএন্ডের সাহায্যে, আপনি স্ট্রিংগুলি UTF-8 বা UTF-16 কিনা তা চয়ন করতে পারেন৷ স্বয়ংক্রিয়ভাবে UTF-8 এ রূপান্তর করতে AIDL-এ স্ট্রিংগুলিকে @utf8InCpp String
হিসাবে ঘোষণা করুন। NDK এবং মরিচা ব্যাকএন্ড সবসময় UTF-8 স্ট্রিং ব্যবহার করে। utf8InCpp
টীকা সম্পর্কে আরও তথ্যের জন্য, utf8InCpp দেখুন।
শূন্যতা
আপনি @nullable
এর সাথে নাল হতে পারে এমন ধরনের টীকা দিতে পারেন। nullable
টীকা সম্পর্কে আরও তথ্যের জন্য, nullable দেখুন।
কাস্টম পার্সেবল
একটি কাস্টম পার্সেলেবল হল একটি পার্সেবল যা একটি টার্গেট ব্যাকএন্ডে ম্যানুয়ালি প্রয়োগ করা হয়। শুধুমাত্র তখনই কাস্টম পার্সেবল ব্যবহার করুন যখন আপনি কোনো বিদ্যমান কাস্টম পার্সেলেবলের জন্য অন্য ভাষায় সমর্থন যোগ করার চেষ্টা করছেন যা পরিবর্তন করা যাবে না।
এখানে একটি AIDL পার্সেলযোগ্য ঘোষণার একটি উদাহরণ:
package my.pack.age;
parcelable Foo;
ডিফল্টরূপে, এটি একটি জাভা পার্সেলেবল ঘোষণা করে যেখানে my.pack.age.Foo
হল একটি জাভা ক্লাস যা Parcelable
ইন্টারফেস বাস্তবায়ন করে।
AIDL-এ একটি কাস্টম CPP ব্যাকএন্ড পার্সেলযোগ্য ঘোষণার জন্য, cpp_header
ব্যবহার করুন:
package my.pack.age;
parcelable Foo cpp_header "my/pack/age/Foo.h";
my/pack/age/Foo.h
এ C++ বাস্তবায়ন এইরকম দেখাচ্ছে:
#include <binder/Parcelable.h>
class MyCustomParcelable : public android::Parcelable {
public:
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
AIDL-এ একটি কাস্টম NDK পার্সেলযোগ্য ঘোষণার জন্য, ndk_header
ব্যবহার করুন:
package my.pack.age;
parcelable Foo ndk_header "android/pack/age/Foo.h";
android/pack/age/Foo.h
এ NDK বাস্তবায়ন এইরকম দেখাচ্ছে:
#include <android/binder_parcel.h>
class MyCustomParcelable {
public:
binder_status_t writeToParcel(AParcel* _Nonnull parcel) const;
binder_status_t readFromParcel(const AParcel* _Nonnull parcel);
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
অ্যান্ড্রয়েড 15-এ, AIDL-এ একটি কাস্টম মরিচা পার্সেলযোগ্য ঘোষণার জন্য, rust_type
ব্যবহার করুন:
package my.pack.age;
@RustOnlyStableParcelable parcelable Foo rust_type "rust_crate::Foo";
rust_crate/src/lib.rs
এ মরিচা বাস্তবায়ন এইরকম দেখাচ্ছে:
use binder::{
binder_impl::{BorrowedParcel, UnstructuredParcelable},
impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
StatusCode,
};
#[derive(Clone, Debug, Eq, PartialEq)]
struct Foo {
pub bar: String,
}
impl UnstructuredParcelable for Foo {
fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
parcel.write(&self.bar)?;
Ok(())
}
fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
let bar = parcel.read()?;
Ok(Self { bar })
}
}
impl_deserialize_for_unstructured_parcelable!(Foo);
impl_serialize_for_unstructured_parcelable!(Foo);
তারপরে আপনি এই পার্সেলেবলটিকে এআইডিএল ফাইলগুলিতে একটি টাইপ হিসাবে ব্যবহার করতে পারেন, তবে এটি এআইডিএল দ্বারা তৈরি করা হবে না। <
এবং ==
CPP এবং NDK ব্যাকএন্ড কাস্টম পার্সেলেবলের জন্য অপারেটরদেরকে সেগুলিকে union
ব্যবহার করার জন্য প্রদান করুন।
ডিফল্ট মান
স্ট্রাকচার্ড পার্সেবলগুলি আদিম, String
ক্ষেত্র এবং এই ধরণের অ্যারের জন্য প্রতি-ক্ষেত্র ডিফল্ট মান ঘোষণা করতে পারে।
parcelable Foo {
int numField = 42;
String stringField = "string value";
char charValue = 'a';
...
}
জাভা ব্যাকএন্ডে, যখন ডিফল্ট মান অনুপস্থিত থাকে, ক্ষেত্রগুলি আদিম প্রকারের জন্য শূন্য মান হিসাবে আরম্ভ করা হয় এবং ননপ্রিমিটিভ প্রকারের জন্য null
।
অন্যান্য ব্যাকএন্ডে, ক্ষেত্রগুলি ডিফল্ট প্রারম্ভিক মানগুলির সাথে আরম্ভ করা হয় যখন ডিফল্ট মানগুলি সংজ্ঞায়িত করা হয় না। উদাহরণস্বরূপ, C++ ব্যাকএন্ডে, String
ক্ষেত্রগুলি একটি খালি স্ট্রিং হিসাবে আরম্ভ করা হয় এবং List<T>
ক্ষেত্রগুলি একটি খালি vector<T>
>। @nullable
ক্ষেত্রগুলি null-value ক্ষেত্র হিসাবে আরম্ভ করা হয়।
ইউনিয়ন
AIDL ইউনিয়নগুলিকে ট্যাগ করা হয়েছে এবং তাদের বৈশিষ্ট্যগুলি সমস্ত ব্যাকএন্ডে একই রকম৷ এগুলি প্রথম ক্ষেত্রের ডিফল্ট মান অনুসারে তৈরি করা হয়েছে এবং তাদের সাথে যোগাযোগ করার একটি ভাষা-নির্দিষ্ট উপায় রয়েছে:
union Foo {
int intField;
long longField;
String stringField;
MyParcelable parcelableField;
...
}
জাভা উদাহরণ
Foo u = Foo.intField(42); // construct
if (u.getTag() == Foo.intField) { // tag query
// use u.getIntField() // getter
}
u.setSringField("abc"); // setter
C++ এবং NDK উদাহরণ
Foo u; // default constructor
assert (u.getTag() == Foo::intField); // tag query
assert (u.get<Foo::intField>() == 0); // getter
u.set<Foo::stringField>("abc"); // setter
assert (u == Foo::make<Foo::stringField>("abc")); // make<tag>(value)
মরিচা উদাহরণ
মরিচায়, ইউনিয়নগুলি enums হিসাবে প্রয়োগ করা হয় এবং সুস্পষ্ট গেটার এবং সেটার নেই।
let mut u = Foo::Default(); // default constructor
match u { // tag match + get
Foo::IntField(x) => assert!(x == 0);
Foo::LongField(x) => panic!("Default constructed to first field");
Foo::StringField(x) => panic!("Default constructed to first field");
Foo::ParcelableField(x) => panic!("Default constructed to first field");
...
}
u = Foo::StringField("abc".to_string()); // set
ত্রুটি হ্যান্ডলিং
অ্যান্ড্রয়েড ওএস ত্রুটি রিপোর্ট করার সময় পরিষেবাগুলির জন্য অন্তর্নির্মিত ত্রুটির প্রকারগুলি সরবরাহ করে৷ এগুলি বাইন্ডার দ্বারা ব্যবহৃত হয় এবং বাইন্ডার ইন্টারফেস বাস্তবায়নকারী যেকোন পরিষেবা দ্বারা ব্যবহার করা যেতে পারে। এআইডিএল সংজ্ঞায় তাদের ব্যবহার ভালভাবে নথিভুক্ত করা হয়েছে এবং তাদের কোনো ব্যবহারকারী-সংজ্ঞায়িত অবস্থা বা রিটার্ন টাইপের প্রয়োজন নেই।
ত্রুটি সহ আউটপুট পরামিতি
যখন একটি এআইডিএল ফাংশন একটি ত্রুটি রিপোর্ট করে, তখন ফাংশনটি আউটপুট প্যারামিটারগুলি শুরু বা সংশোধন করতে পারে না। বিশেষত, আউটপুট প্যারামিটার পরিবর্তন করা হতে পারে যদি ত্রুটিটি আনপার্সেলিংয়ের সময় ঘটে, যেমনটি লেনদেনের প্রক্রিয়াকরণের সময় ঘটতে পারে না। সাধারণভাবে, যখন একটি AIDL ফাংশন থেকে একটি ত্রুটি পাওয়া যায়, তখন সমস্ত inout
এবং out
প্যারামিটারের পাশাপাশি রিটার্ন মান (যা কিছু ব্যাকএন্ডে একটি out
প্যারামিটারের মতো কাজ করে) একটি অনির্দিষ্ট অবস্থায় বিবেচনা করা উচিত।
কোন ত্রুটি মান ব্যবহার করতে হবে
বিল্ট-ইন ত্রুটির মানগুলির অনেকগুলি যেকোন এআইডিএল ইন্টারফেসে ব্যবহার করা যেতে পারে, তবে কিছু একটি বিশেষ উপায়ে চিকিত্সা করা হয়। উদাহরণস্বরূপ, EX_UNSUPPORTED_OPERATION
এবং EX_ILLEGAL_ARGUMENT
ত্রুটির অবস্থা বর্ণনা করার সময় ব্যবহার করা ঠিক, কিন্তু EX_TRANSACTION_FAILED
ব্যবহার করা উচিত নয় কারণ এটি অন্তর্নিহিত অবকাঠামো দ্বারা বিশেষভাবে চিকিত্সা করা হয়৷ এই অন্তর্নির্মিত মান সম্পর্কে আরও তথ্যের জন্য ব্যাকএন্ড নির্দিষ্ট সংজ্ঞা পরীক্ষা করুন।
যদি AIDL ইন্টারফেসের জন্য অতিরিক্ত ত্রুটির মান প্রয়োজন যা অন্তর্নির্মিত ত্রুটির ধরন দ্বারা আচ্ছাদিত না হয়, তবে তারা বিশেষ পরিষেবা-নির্দিষ্ট অন্তর্নির্মিত ত্রুটি ব্যবহার করতে পারে যা ব্যবহারকারীর দ্বারা সংজ্ঞায়িত পরিষেবা-নির্দিষ্ট ত্রুটি মান অন্তর্ভুক্ত করার অনুমতি দেয়৷ এই পরিষেবা-নির্দিষ্ট ত্রুটিগুলি সাধারণত AIDL ইন্টারফেসে একটি const int
বা int
-backed enum
হিসাবে সংজ্ঞায়িত করা হয় এবং বাইন্ডার দ্বারা পার্স করা হয় না।
জাভাতে, ত্রুটিগুলি ব্যতিক্রমগুলিতে ম্যাপ করে, যেমন android.os.RemoteException
। পরিষেবা-নির্দিষ্ট ব্যতিক্রমগুলির জন্য, Java ব্যবহারকারী-সংজ্ঞায়িত ত্রুটি সহ android.os.ServiceSpecificException
ব্যবহার করে।
অ্যান্ড্রয়েডে নেটিভ কোড ব্যতিক্রম ব্যবহার করে না। CPP ব্যাকএন্ড android::binder::Status
ব্যবহার করে। NDK ব্যাকএন্ড ndk::ScopedAStatus
ব্যবহার করে। এআইডিএল দ্বারা উত্পন্ন প্রতিটি পদ্ধতি এইগুলির মধ্যে একটি প্রদান করে, পদ্ধতির অবস্থা উপস্থাপন করে। মরিচা ব্যাকএন্ড NDK-এর মতো একই ব্যতিক্রম কোড মান ব্যবহার করে, কিন্তু ব্যবহারকারীর কাছে সেগুলি সরবরাহ করার আগে সেগুলিকে নেটিভ রাস্ট ত্রুটি ( StatusCode
, ExceptionCode
) এ রূপান্তরিত করে। পরিষেবা-নির্দিষ্ট ত্রুটির জন্য, প্রত্যাবর্তিত Status
বা ScopedAStatus
ব্যবহারকারী-সংজ্ঞায়িত ত্রুটি সহ EX_SERVICE_SPECIFIC
ব্যবহার করে৷
অন্তর্নির্মিত ত্রুটি প্রকারগুলি নিম্নলিখিত ফাইলগুলিতে পাওয়া যাবে:
ব্যাকএন্ড | সংজ্ঞা |
---|---|
জাভা | android/os/Parcel.java |
সিপিপি | binder/Status.h |
এনডিকে | android/binder_status.h |
মরিচা | android/binder_status.h |
বিভিন্ন ব্যাকএন্ড ব্যবহার করুন
এই নির্দেশাবলী Android প্ল্যাটফর্ম কোডের জন্য নির্দিষ্ট। এই উদাহরণগুলি একটি সংজ্ঞায়িত প্রকার, my.package.IFoo
ব্যবহার করে। মরিচা ব্যাকএন্ড কীভাবে ব্যবহার করবেন তার নির্দেশাবলীর জন্য, Android Rust প্যাটার্নে Rust AIDL উদাহরণ দেখুন।
আমদানি প্রকার
সংজ্ঞায়িত টাইপটি একটি ইন্টারফেস, পার্সেলেবল বা ইউনিয়ন হোক না কেন, আপনি এটি জাভাতে আমদানি করতে পারেন:
import my.package.IFoo;
অথবা CPP ব্যাকএন্ডে:
#include <my/package/IFoo.h>
অথবা NDK ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <aidl/my/package/IFoo.h>
অথবা মরিচা ব্যাকএন্ডে:
use my_package::aidl::my::package::IFoo;
যদিও আপনি জাভাতে একটি নেস্টেড টাইপ আমদানি করতে পারেন, CPP এবং NDK ব্যাকএন্ডে আপনাকে অবশ্যই এর রুট টাইপের জন্য হেডার অন্তর্ভুক্ত করতে হবে। উদাহরণস্বরূপ, my/package/IFoo.aidl
এ সংজ্ঞায়িত একটি নেস্টেড টাইপ Bar
আমদানি করার সময় ( IFoo
হল ফাইলের রুট টাইপ) আপনাকে অবশ্যই CPP ব্যাকএন্ডের জন্য <my/package/IFoo.h>
(বা NDK ব্যাকএন্ডের জন্য <aidl/my/package/IFoo.h>
) অন্তর্ভুক্ত করতে হবে।
একটি ইন্টারফেস প্রয়োগ করুন
একটি ইন্টারফেস বাস্তবায়ন করতে, আপনাকে অবশ্যই নেটিভ স্টাব ক্লাস থেকে উত্তরাধিকারসূত্রে প্রাপ্ত হতে হবে। একটি ইন্টারফেসের বাস্তবায়নকে প্রায়শই একটি পরিষেবা বলা হয় যখন এটি পরিষেবা পরিচালক বা android.app.ActivityManager
সাথে নিবন্ধিত হয় এবং যখন এটি কোনও পরিষেবার ক্লায়েন্ট দ্বারা নিবন্ধিত হয় তখন একটি কলব্যাক বলা হয়৷ যাইহোক, সঠিক ব্যবহারের উপর নির্ভর করে ইন্টারফেস বাস্তবায়ন বর্ণনা করতে বিভিন্ন নাম ব্যবহার করা হয়। স্টাব ক্লাস বাইন্ডার ড্রাইভারের কাছ থেকে কমান্ড পড়ে এবং আপনি যে পদ্ধতিগুলি প্রয়োগ করেন তা কার্যকর করে। কল্পনা করুন যে আপনার কাছে এইরকম একটি AIDL ফাইল আছে:
package my.package;
interface IFoo {
int doFoo();
}
জাভাতে, আপনাকে অবশ্যই জেনারেট করা Stub
ক্লাস থেকে প্রসারিত করতে হবে:
import my.package.IFoo;
public class MyFoo extends IFoo.Stub {
@Override
int doFoo() { ... }
}
CPP ব্যাকএন্ডে:
#include <my/package/BnFoo.h>
class MyFoo : public my::package::BnFoo {
android::binder::Status doFoo(int32_t* out) override;
}
NDK ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <aidl/my/package/BnFoo.h>
class MyFoo : public aidl::my::package::BnFoo {
ndk::ScopedAStatus doFoo(int32_t* out) override;
}
মরিচা ব্যাকএন্ডে:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFoo};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
impl IFoo for MyFoo {
fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
অথবা অ্যাসিঙ্ক মরিচা সহ:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFooAsyncServer};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
#[async_trait]
impl IFooAsyncServer for MyFoo {
async fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
নিবন্ধন করুন এবং পরিষেবা পান
প্ল্যাটফর্ম অ্যান্ড্রয়েডের পরিষেবাগুলি সাধারণত servicemanager
প্রক্রিয়ার সাথে নিবন্ধিত হয়। নিম্নলিখিত APIগুলি ছাড়াও, কিছু API পরিষেবাটি পরীক্ষা করে (অর্থাৎ পরিষেবাটি উপলব্ধ না হলে তারা অবিলম্বে ফিরে আসে)। সঠিক বিবরণের জন্য সংশ্লিষ্ট servicemanager
ইন্টারফেস চেক করুন। প্ল্যাটফর্ম অ্যান্ড্রয়েডের বিরুদ্ধে কম্পাইল করার সময় আপনি এই অপারেশনগুলি সম্পাদন করতে পারেন৷
জাভাতে:
import android.os.ServiceManager;
// registering
ServiceManager.addService("service-name", myService);
// return if service is started now
myService = IFoo.Stub.asInterface(ServiceManager.checkService("service-name"));
// waiting until service comes up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));
CPP ব্যাকএন্ডে:
#include <binder/IServiceManager.h>
// registering
defaultServiceManager()->addService(String16("service-name"), myService);
// return if service is started now
status_t err = checkService<IFoo>(String16("service-name"), &myService);
// waiting until service comes up (new in Android 11)
myService = waitForService<IFoo>(String16("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = waitForDeclaredService<IFoo>(String16("service-name"));
NDK ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <android/binder_manager.h>
// registering
binder_exception_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
// return if service is started now
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_checkService("service-name")));
// is a service declared in the VINTF manifest
// VINTF services have the type in the interface instance name.
bool isDeclared = AServiceManager_isDeclared("android.hardware.light.ILights/default");
// wait until a service is available (if isDeclared or you know it's available)
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService("service-name")));
মরিচা ব্যাকএন্ডে:
use myfoo::MyFoo;
use binder;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_binder(
my_service,
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Does not return - spawn or perform any work you mean to do before this call.
binder::ProcessState::join_thread_pool()
}
অ্যাসিঙ্ক মরিচা ব্যাকএন্ডে, একটি একক-থ্রেডেড রানটাইম সহ:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "current_thread")]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleeps forever, but does not join the binder threadpool.
// Spawned tasks run on this thread.
std::future::pending().await
}
অন্যান্য বিকল্পগুলির থেকে একটি গুরুত্বপূর্ণ পার্থক্য হল যে আপনি async Rust এবং একটি একক-থ্রেডেড রানটাইম ব্যবহার করার সময় join_thread_pool
কল করবেন না । এর কারণ হল আপনাকে টোকিওকে একটি থ্রেড দিতে হবে যেখানে এটি তৈরি করা কাজগুলি সম্পাদন করতে পারে। নিম্নলিখিত উদাহরণে, মূল থ্রেড সেই উদ্দেশ্যটি পরিবেশন করে। প্রধান থ্রেডে tokio::spawn
execute ব্যবহার করে যে কোনো কাজ তৈরি করা হয়।
অ্যাসিঙ্ক মরিচা ব্যাকএন্ডে, একটি মাল্টিথ্রেডেড রানটাইম সহ:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleep forever.
tokio::task::block_in_place(|| {
binder::ProcessState::join_thread_pool();
});
}
মাল্টিথ্রেডেড টোকিও রানটাইম সহ, তৈরি করা কাজগুলি মূল থ্রেডে কার্যকর হয় না। তাই, প্রধান থ্রেডে join_thread_pool
কল করা আরও বোধগম্য হয় যাতে মূল থ্রেডটি নিষ্ক্রিয় না হয়। অ্যাসিঙ্ক প্রসঙ্গ ছেড়ে যেতে আপনাকে অবশ্যই block_in_place
এ কলটি মুড়ে দিতে হবে।
মৃত্যুর সাথে লিঙ্ক
যখন একটি বাইন্ডার হোস্টিং পরিষেবা মারা যায় তখন আপনি একটি বিজ্ঞপ্তি পাওয়ার জন্য অনুরোধ করতে পারেন৷ এটি কলব্যাক প্রক্সি ফাঁস এড়াতে বা ত্রুটি পুনরুদ্ধারে সহায়তা করতে পারে৷ বাইন্ডার প্রক্সি অবজেক্টে এই কলগুলি করুন।
- জাভাতে,
android.os.IBinder::linkToDeath
ব্যবহার করুন। - CPP ব্যাকএন্ডে,
android::IBinder::linkToDeath
ব্যবহার করুন। - NDK ব্যাকএন্ডে,
AIBinder_linkToDeath
ব্যবহার করুন। - মরিচা ব্যাকএন্ডে, একটি
DeathRecipient
অবজেক্ট তৈরি করুন, তারপরmy_binder.link_to_death(&mut my_death_recipient)
কল করুন। নোট করুন যেDeathRecipient
কলব্যাকের মালিক, তাই যতক্ষণ আপনি বিজ্ঞপ্তি পেতে চান ততক্ষণ আপনাকে সেই বস্তুটিকে জীবিত রাখতে হবে।
কলারের তথ্য
একটি কার্নেল বাইন্ডার কল গ্রহণ করার সময়, কলার তথ্য বিভিন্ন API-এ উপলব্ধ। প্রসেস আইডি (পিআইডি) একটি লেনদেন পাঠানোর প্রক্রিয়ার Linux প্রসেস আইডিকে বোঝায়। ইউজার আইডি (UI) বলতে লিনাক্স ইউজার আইডি বোঝায়। একটি একমুখী কল গ্রহণ করার সময়, কলিং পিআইডি 0 হয়। একটি বাইন্ডার লেনদেন প্রসঙ্গের বাইরে, এই ফাংশনগুলি বর্তমান প্রক্রিয়ার পিআইডি এবং ইউআইডি ফিরিয়ে দেয়।
জাভা ব্যাকএন্ডে:
... = Binder.getCallingPid();
... = Binder.getCallingUid();
CPP ব্যাকএন্ডে:
... = IPCThreadState::self()->getCallingPid();
... = IPCThreadState::self()->getCallingUid();
NDK ব্যাকএন্ডে:
... = AIBinder_getCallingPid();
... = AIBinder_getCallingUid();
মরিচা ব্যাকএন্ডে, ইন্টারফেস প্রয়োগ করার সময়, নিম্নলিখিতটি নির্দিষ্ট করুন (এটিকে ডিফল্ট করার অনুমতি দেওয়ার পরিবর্তে):
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
বাগ রিপোর্ট এবং পরিষেবার জন্য ডিবাগিং API
যখন বাগ রিপোর্ট চালানো হয় (উদাহরণস্বরূপ, adb bugreport
সহ), তারা বিভিন্ন সমস্যা ডিবাগ করতে সহায়তা করার জন্য সিস্টেমের চারপাশ থেকে তথ্য সংগ্রহ করে। AIDL পরিষেবাগুলির জন্য, বাগ রিপোর্টগুলি বাগ রিপোর্টে তাদের তথ্য ডাম্প করতে পরিষেবা পরিচালকের সাথে নিবন্ধিত সমস্ত পরিষেবাগুলিতে বাইনারি dumpsys
ব্যবহার করে৷ এছাড়াও আপনি dumpsys SERVICE [ARGS]
এর সাথে একটি পরিষেবা থেকে তথ্য পেতে কমান্ড লাইনে dumpsys
ব্যবহার করতে পারেন। C++ এবং Java ব্যাকএন্ডে, addService
এ অতিরিক্ত আর্গুমেন্ট ব্যবহার করে আপনি কোন ক্রমে পরিষেবাগুলি ডাম্প করা হবে তা নিয়ন্ত্রণ করতে পারেন। ডিবাগ করার সময় একটি পরিষেবার পিআইডি পেতে আপনি dumpsys --pid SERVICE
ব্যবহার করতে পারেন।
আপনার পরিষেবাতে কাস্টম আউটপুট যোগ করতে, আপনার সার্ভার অবজেক্টে dump
পদ্ধতিটি ওভাররাইড করুন যেমন আপনি একটি AIDL ফাইলে সংজ্ঞায়িত অন্য কোনো IPC পদ্ধতি প্রয়োগ করছেন। এটি করার সময়, অ্যাপের অনুমতি android.permission.DUMP
এ ডাম্পিং সীমাবদ্ধ করুন বা নির্দিষ্ট UID-তে ডাম্পিং সীমাবদ্ধ করুন।
জাভা ব্যাকএন্ডে:
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {...}
CPP ব্যাকএন্ডে:
status_t dump(int, const android::android::Vector<android::String16>&) override;
NDK ব্যাকএন্ডে:
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
মরিচা ব্যাকএন্ডে, ইন্টারফেস প্রয়োগ করার সময়, নিম্নলিখিতটি নির্দিষ্ট করুন (এটিকে ডিফল্ট করার অনুমতি দেওয়ার পরিবর্তে):
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
দুর্বল পয়েন্টার ব্যবহার করুন
আপনি একটি বাইন্ডার বস্তুর একটি দুর্বল রেফারেন্স ধরে রাখতে পারেন।
যদিও জাভা WeakReference
সমর্থন করে, এটি নেটিভ লেয়ারে দুর্বল বাইন্ডার রেফারেন্স সমর্থন করে না।
CPP ব্যাকএন্ডে, দুর্বল টাইপ হল wp<IFoo>
।
NDK ব্যাকএন্ডে, ScopedAIBinder_Weak
ব্যবহার করুন:
#include <android/binder_auto_utils.h>
AIBinder* binder = ...;
ScopedAIBinder_Weak myWeakReference = ScopedAIBinder_Weak(AIBinder_Weak_new(binder));
মরিচা ব্যাকএন্ডে, WpIBinder
বা Weak<IFoo>
ব্যবহার করুন :
let weak_interface = myIface.downgrade();
let weak_binder = myIface.as_binder().downgrade();
গতিশীলভাবে ইন্টারফেস বর্ণনাকারী পান
ইন্টারফেস বর্ণনাকারী একটি ইন্টারফেসের ধরন সনাক্ত করে। এটি ডিবাগ করার সময় বা আপনার কাছে একটি অজানা বাইন্ডার থাকলে দরকারী।
জাভাতে, আপনি কোড সহ ইন্টারফেস বর্ণনাকারী পেতে পারেন যেমন:
service = /* get ahold of service object */
... = service.asBinder().getInterfaceDescriptor();
CPP ব্যাকএন্ডে:
service = /* get ahold of service object */
... = IInterface::asBinder(service)->getInterfaceDescriptor();
NDK এবং মরিচা ব্যাকএন্ড এই ক্ষমতা সমর্থন করে না।
স্থিরভাবে ইন্টারফেস বর্ণনাকারী পান
কখনও কখনও (যেমন @VintfStability
পরিষেবাগুলি নিবন্ধন করার সময়), আপনাকে জানতে হবে ইন্টারফেস বর্ণনাকারীটি স্থিরভাবে কী। জাভাতে, আপনি কোড যোগ করে বর্ণনাকারী পেতে পারেন যেমন:
import my.package.IFoo;
... IFoo.DESCRIPTOR
CPP ব্যাকএন্ডে:
#include <my/package/BnFoo.h>
... my::package::BnFoo::descriptor
NDK ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <aidl/my/package/BnFoo.h>
... aidl::my::package::BnFoo::descriptor
মরিচা ব্যাকএন্ডে:
aidl::my::package::BnFoo::get_descriptor()
Enum পরিসীমা
নেটিভ ব্যাকএন্ডে, আপনি একটি enum গ্রহণ করতে পারে এমন সম্ভাব্য মানগুলির উপর পুনরাবৃত্তি করতে পারেন। কোড আকার বিবেচনার কারণে, এটি জাভাতে সমর্থিত নয়।
AIDL-এ সংজ্ঞায়িত একটি enum MyEnum
জন্য, পুনরাবৃত্তি নিম্নরূপ প্রদান করা হয়।
CPP ব্যাকএন্ডে:
::android::enum_range<MyEnum>()
NDK ব্যাকএন্ডে:
::ndk::enum_range<MyEnum>()
মরিচা ব্যাকএন্ডে:
MyEnum::enum_values()
থ্রেড ব্যবস্থাপনা
একটি প্রক্রিয়ায় libbinder
প্রতিটি উদাহরণ একটি থ্রেডপুল বজায় রাখে। বেশিরভাগ ব্যবহারের ক্ষেত্রে, এটি ঠিক একটি থ্রেডপুল হওয়া উচিত, সমস্ত ব্যাকএন্ড জুড়ে ভাগ করা। একমাত্র ব্যতিক্রম যদি বিক্রেতা কোড /dev/vndbinder
সাথে কথা বলার জন্য libbinder
এর অন্য একটি অনুলিপি লোড করে। এটি একটি পৃথক বাইন্ডার নোডে রয়েছে, তাই থ্রেডপুল ভাগ করা হয় না।
জাভা ব্যাকএন্ডের জন্য, থ্রেডপুল শুধুমাত্র আকারে বৃদ্ধি করতে পারে (কারণ এটি ইতিমধ্যেই শুরু হয়েছে):
BinderInternal.setMaxThreads(<new larger value>);
CPP ব্যাকএন্ডের জন্য, নিম্নলিখিত ক্রিয়াকলাপগুলি উপলব্ধ:
// set max threadpool count (default is 15)
status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(numThreads);
// create threadpool
ProcessState::self()->startThreadPool();
// add current thread to threadpool (adds thread to max thread count)
IPCThreadState::self()->joinThreadPool();
একইভাবে, NDK ব্যাকএন্ডে:
bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
ABinderProcess_startThreadPool();
ABinderProcess_joinThreadPool();
মরিচা ব্যাকএন্ডে:
binder::ProcessState::start_thread_pool();
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
binder::ProcessState::join_thread_pool();
অ্যাসিঙ্ক রাস্ট ব্যাকএন্ডের সাথে, আপনার দুটি থ্রেডপুল দরকার: বাইন্ডার এবং টোকিও। এর মানে হল যে অ্যাসিঙ্ক মরিচা ব্যবহার করা অ্যাপগুলির বিশেষ বিবেচনার প্রয়োজন, বিশেষ করে যখন এটি join_thread_pool
ব্যবহারের ক্ষেত্রে আসে। এই বিষয়ে আরও তথ্যের জন্য পরিষেবা নিবন্ধন সংক্রান্ত বিভাগটি দেখুন।
সংরক্ষিত নাম
C++, Java, এবং Rust কিছু নাম কীওয়ার্ড হিসেবে বা ভাষা-নির্দিষ্ট ব্যবহারের জন্য সংরক্ষণ করে। যদিও AIDL ভাষার নিয়মের উপর ভিত্তি করে বিধিনিষেধ প্রয়োগ করে না, একটি সংরক্ষিত নামের সাথে মেলে এমন ক্ষেত্র বা টাইপ নাম ব্যবহার করে C++ বা Java এর জন্য একটি সংকলন ব্যর্থ হতে পারে। মরিচা-এর জন্য, ক্ষেত্র বা প্রকারের নামকরণ করা হয়েছে কাঁচা শনাক্তকারী সিনট্যাক্স ব্যবহার করে, যা r#
উপসর্গ ব্যবহার করে অ্যাক্সেসযোগ্য।
আমরা আপনার AIDL সংজ্ঞাগুলিতে সংরক্ষিত নামগুলি ব্যবহার করা এড়ানোর পরামর্শ দিই যেখানে সম্ভব unergonomic বাইন্ডিং বা সম্পূর্ণ সংকলন ব্যর্থতা এড়াতে।
আপনার যদি ইতিমধ্যেই আপনার AIDL সংজ্ঞাগুলিতে সংরক্ষিত নাম থাকে, তবে প্রোটোকল সামঞ্জস্যপূর্ণ থাকা অবস্থায় আপনি নিরাপদে ক্ষেত্রগুলির নাম পরিবর্তন করতে পারেন। বিল্ডিং চালিয়ে যাওয়ার জন্য আপনাকে আপনার কোড আপডেট করতে হতে পারে, কিন্তু যেকোনও ইতিমধ্যে নির্মিত প্রোগ্রাম ইন্টারঅপারেটিং চালিয়ে যেতে পারে।
এড়ানোর জন্য নাম:
,একটি এআইডিএল ব্যাকএন্ড হল স্টাব কোড তৈরির লক্ষ্য। একটি নির্দিষ্ট রানটাইম সহ একটি নির্দিষ্ট ভাষায় সর্বদা AIDL ফাইলগুলি ব্যবহার করুন। প্রেক্ষাপটের উপর নির্ভর করে, আপনার বিভিন্ন AIDL ব্যাকএন্ড ব্যবহার করা উচিত।
নিম্নলিখিত সারণীতে, API পৃষ্ঠের স্থায়িত্ব বলতে এই API পৃষ্ঠের বিরুদ্ধে কোড কম্পাইল করার ক্ষমতাকে বোঝায় যাতে কোডটি system.img
libbinder.so
বাইনারি থেকে স্বাধীনভাবে বিতরণ করা যায়।
AIDL এর নিম্নলিখিত ব্যাকএন্ড রয়েছে:
ব্যাকএন্ড | ভাষা | API পৃষ্ঠ | সিস্টেম তৈরি করুন |
---|---|---|---|
জাভা | জাভা | SDK বা SystemApi (স্থিতিশীল*) | সব |
এনডিকে | সি++ | libbinder_ndk (স্থিতিশীল*) | aidl_interface |
সিপিপি | সি++ | libbinder (অস্থির) | সব |
মরিচা | মরিচা | libbinder_rs (স্থিতিশীল*) | aidl_interface |
- এই API পৃষ্ঠতলগুলি স্থিতিশীল, কিন্তু অনেক API, যেমন পরিষেবা পরিচালনার জন্য, অভ্যন্তরীণ প্ল্যাটফর্ম ব্যবহারের জন্য সংরক্ষিত এবং অ্যাপগুলিতে উপলব্ধ নয়। অ্যাপে এআইডিএল কীভাবে ব্যবহার করবেন সে সম্পর্কে আরও তথ্যের জন্য, অ্যান্ড্রয়েড ইন্টারফেস ডেফিনিশন ল্যাঙ্গুয়েজ (এআইডিএল) দেখুন।
- মরিচা ব্যাকএন্ড অ্যান্ড্রয়েড 12 এ চালু করা হয়েছিল; এনডিকে ব্যাকএন্ড অ্যান্ড্রয়েড 10 হিসাবে উপলব্ধ।
- মরিচা ক্রেটটি
libbinder_ndk
এর উপরে নির্মিত, যা এটিকে স্থিতিশীল এবং বহনযোগ্য হতে দেয়। APEXes সিস্টেমের দিকে স্ট্যান্ডার্ড উপায়ে বাইন্ডার ক্রেট ব্যবহার করে। মরিচা অংশটি একটি এপেক্সে বান্ডিল করা হয় এবং এর ভিতরে পাঠানো হয়। এই অংশটিlibbinder_ndk.so
সিস্টেম পার্টিশনের উপর নির্ভর করে।
সিস্টেম তৈরি করুন
ব্যাকএন্ডের উপর নির্ভর করে, স্টাব কোডে AIDL কম্পাইল করার দুটি উপায় রয়েছে। বিল্ড সিস্টেম সম্পর্কে আরো বিস্তারিত জানার জন্য, সুং মডিউল রেফারেন্স দেখুন।
কোর বিল্ড সিস্টেম
যেকোন cc_
বা java_
Android.bp module
(বা তাদের Android.mk
সমতুল্য), আপনি AIDL ( .aidl
) ফাইলগুলিকে সোর্স ফাইল হিসেবে নির্দিষ্ট করতে পারেন। এই ক্ষেত্রে, AIDL এর জাভা বা CPP ব্যাকএন্ড ব্যবহার করা হয় (NDK ব্যাকএন্ড নয়), এবং সংশ্লিষ্ট AIDL ফাইলগুলি ব্যবহার করার জন্য ক্লাসগুলি স্বয়ংক্রিয়ভাবে মডিউলে যোগ করা হয়। আপনি একটি aidl:
গ্রুপের অধীনে এই মডিউলগুলিতে local_include_dirs
(যা বিল্ড সিস্টেমকে সেই মডিউলে AIDL ফাইলের রুট পাথ বলে) মত বিকল্পগুলি নির্দিষ্ট করতে পারেন।
মরিচা ব্যাকএন্ড শুধুমাত্র মরিচা ব্যবহার করার জন্য। rust_
মডিউলগুলি ভিন্নভাবে পরিচালনা করা হয় যাতে AIDL ফাইলগুলি উত্স ফাইল হিসাবে নির্দিষ্ট করা হয় না। পরিবর্তে, aidl_interface
মডিউলটি aidl_interface_name -rust
নামক একটি rustlib
তৈরি করে, যার সাথে লিঙ্ক করা যেতে পারে। বিস্তারিত জানার জন্য, মরিচা এআইডিএল উদাহরণ দেখুন।
aidl_interface
aidl_interface
বিল্ড সিস্টেমের সাথে ব্যবহৃত প্রকারগুলি অবশ্যই কাঠামোগত হতে হবে। কাঠামোবদ্ধ হওয়ার জন্য, পার্সেবলে অবশ্যই সরাসরি ক্ষেত্র থাকতে হবে এবং লক্ষ্য ভাষায় সরাসরি সংজ্ঞায়িত প্রকারের ঘোষণা হবে না। স্থিতিশীল AIDL-এর সাথে কীভাবে কাঠামোগত AIDL ফিট করে, দেখুন স্ট্রাকচার্ড বনাম স্থিতিশীল AIDL ।
প্রকারভেদ
প্রকারের জন্য একটি রেফারেন্স বাস্তবায়ন হিসাবে aidl
কম্পাইলার বিবেচনা করুন। যখন আপনি একটি ইন্টারফেস তৈরি করেন, তখন aidl --lang=<backend> ...
ব্যবহার করুন ফলে ইন্টারফেস ফাইলটি দেখতে। আপনি যখন aidl_interface
মডিউল ব্যবহার করেন, আপনি out/soong/.intermediates/ <path to module> /
এ আউটপুট দেখতে পারেন।
জাভা বা এআইডিএল টাইপ | C++ প্রকার | NDK প্রকার | মরিচা ধরন |
---|---|---|---|
boolean | bool | bool | bool |
byte 8 | int8_t | int8_t | i8 |
char | char16_t | char16_t | u16 |
int | int32_t | int32_t | i32 |
long | int64_t | int64_t | i64 |
float | float | float | f32 |
double | double | double | f64 |
String | android::String16 | std::string | ইন: &str আউট: String |
android.os.Parcelable | android::Parcelable | N/A | N/A |
IBinder | android::IBinder | ndk::SpAIBinder | binder::SpIBinder |
T[] | std::vector<T> | std::vector<T> | ইন: &[T] আউট: Vec<T> |
byte[] | std::vector | std::vector 1 | মধ্যে: &[u8] আউট: Vec<u8> |
List<T> | std::vector<T> 2 | std::vector<T> 3 | In: In: &[T] 4আউট: Vec<T> |
FileDescriptor | android::base::unique_fd | N/A | N/A |
ParcelFileDescriptor | android::os::ParcelFileDescriptor | ndk::ScopedFileDescriptor | binder::parcel::ParcelFileDescriptor |
ইন্টারফেসের ধরন ( T ) | android::sp<T> | std::shared_ptr<T> 7 | binder::Strong |
পার্সেলযোগ্য প্রকার ( T ) | T | T | T |
ইউনিয়নের ধরন ( T ) 5 | T | T | T |
T[N] 6 | std::array<T, N> | std::array<T, N> | [T; N] |
1. Android 12 বা উচ্চতর সংস্করণে, বাইট অ্যারেগুলি সামঞ্জস্যের কারণে int8_t
এর পরিবর্তে uint8_t
ব্যবহার করে৷
2. C++ ব্যাকএন্ড List<T>
সমর্থন করে যেখানে T
হল String
, IBinder
, ParcelFileDescriptor
বা parcelable এর মধ্যে একটি। অ্যান্ড্রয়েড 13 বা উচ্চতর, T
অ্যারে ব্যতীত যেকোন নন-প্রিমিটিভ টাইপ (ইন্টারফেসের ধরন সহ) হতে পারে। AOSP T[]
মত অ্যারে প্রকার ব্যবহার করার পরামর্শ দেয়, কারণ তারা সমস্ত ব্যাকএন্ডে কাজ করে।
3. NDK ব্যাকএন্ড List<T>
সমর্থন করে যেখানে T
হল String
, ParcelFileDescriptor
বা পার্সেলেবলের একটি। অ্যান্ড্রয়েড 13 বা উচ্চতর সংস্করণে, T
অ্যারে ব্যতীত যেকোন নন-প্রিমিটিভ টাইপ হতে পারে।
4. রাস্ট কোডের জন্য প্রকারগুলি ভিন্নভাবে পাস করা হয় তা নির্ভর করে সেগুলি ইনপুট (একটি যুক্তি), বা একটি আউটপুট (একটি ফেরত মান)।
5. ইউনিয়ন প্রকারগুলি Android 12 এবং উচ্চতর সংস্করণে সমর্থিত।
6. অ্যান্ড্রয়েড 13 বা উচ্চতর, স্থির আকারের অ্যারে সমর্থিত। স্থির-আকারের অ্যারেতে একাধিক মাত্রা থাকতে পারে (উদাহরণস্বরূপ, int[3][4]
)। জাভা ব্যাকএন্ডে, স্থির-আকারের অ্যারেগুলিকে অ্যারের প্রকার হিসাবে উপস্থাপন করা হয়।
7. একটি বাইন্ডার SharedRefBase
অবজেক্ট ইনস্ট্যান্ট করতে, SharedRefBase::make\<My\>(... args ...)
ব্যবহার করুন। এই ফাংশনটি একটি std::shared_ptr\<T\>
অবজেক্ট তৈরি করে, যা অভ্যন্তরীণভাবেও পরিচালিত হয়, যদি বাইন্ডার অন্য প্রক্রিয়ার মালিকানাধীন হয়। বস্তুটি অন্য উপায়ে তৈরি করা দ্বিগুণ মালিকানার কারণ।
8. এছাড়াও Java বা AIDL টাইপ byte[]
।
দিকনির্দেশনা (ইন, আউট এবং ইনআউট)
ফাংশনে আর্গুমেন্টের ধরন নির্দিষ্ট করার সময়, আপনি এগুলিকে in
, out
, বা inout
হিসাবে নির্দিষ্ট করতে পারেন। এটি আইপিসি কলের জন্য তথ্য পাঠানোর দিকটিকে নিয়ন্ত্রণ করে।
in
আর্গুমেন্ট স্পেসিফায়ার নির্দেশ করে যে কলারের কাছ থেকে কলকারীর কাছে ডেটা চলে গেছে।in
স্পেসিফায়ার হল ডিফল্ট দিক, কিন্তু যদি ডেটা প্রকারগুলিওout
হতে পারে, তাহলে আপনাকে অবশ্যই দিকটি নির্দিষ্ট করতে হবে।out
আর্গুমেন্ট স্পেসিফায়ার মানে কলে থেকে কলারের কাছে ডাটা চলে যায়।inout
আর্গুমেন্ট স্পেসিফায়ার হল এই দুটির সমন্বয়। যাইহোক, আমরাinout
আর্গুমেন্ট স্পেসিফায়ার ব্যবহার এড়ানোর পরামর্শ দিচ্ছি। আপনি যদি একটি সংস্করণযুক্ত ইন্টারফেস এবং একটি পুরানো কলির সাথেinout
ব্যবহার করেন, তবে অতিরিক্ত ক্ষেত্রগুলি যেগুলি শুধুমাত্র কলারে উপস্থিত থাকে সেগুলি তাদের ডিফল্ট মানগুলিতে পুনরায় সেট করা হয়। Rust এর ক্ষেত্রে, একটি সাধারণinout
প্রকার&mut T
গ্রহণ করে এবং একটি তালিকাinout
প্রকার&mut Vec<T>
গ্রহণ করে।
interface IRepeatExamples {
MyParcelable RepeatParcelable(MyParcelable token); // implicitly 'in'
MyParcelable RepeatParcelableWithIn(in MyParcelable token);
void RepeatParcelableWithInAndOut(in MyParcelable param, out MyParcelable result);
void RepeatParcelableWithInOut(inout MyParcelable param);
}
UTF-8 এবং UTF-16
CPP ব্যাকএন্ডের সাহায্যে, আপনি স্ট্রিংগুলি UTF-8 বা UTF-16 কিনা তা চয়ন করতে পারেন৷ স্বয়ংক্রিয়ভাবে UTF-8 এ রূপান্তর করতে AIDL-এ স্ট্রিংগুলিকে @utf8InCpp String
হিসাবে ঘোষণা করুন। NDK এবং মরিচা ব্যাকএন্ড সবসময় UTF-8 স্ট্রিং ব্যবহার করে। utf8InCpp
টীকা সম্পর্কে আরও তথ্যের জন্য, utf8InCpp দেখুন।
শূন্যতা
আপনি @nullable
এর সাথে নাল হতে পারে এমন ধরনের টীকা দিতে পারেন। nullable
টীকা সম্পর্কে আরও তথ্যের জন্য, nullable দেখুন।
কাস্টম পার্সেবল
একটি কাস্টম পার্সেলেবল হল একটি পার্সেবল যা একটি টার্গেট ব্যাকএন্ডে ম্যানুয়ালি প্রয়োগ করা হয়। শুধুমাত্র তখনই কাস্টম পার্সেবল ব্যবহার করুন যখন আপনি কোনো বিদ্যমান কাস্টম পার্সেলেবলের জন্য অন্য ভাষায় সমর্থন যোগ করার চেষ্টা করছেন যা পরিবর্তন করা যাবে না।
এখানে একটি AIDL পার্সেলযোগ্য ঘোষণার একটি উদাহরণ:
package my.pack.age;
parcelable Foo;
ডিফল্টরূপে, এটি একটি জাভা পার্সেলেবল ঘোষণা করে যেখানে my.pack.age.Foo
হল একটি জাভা ক্লাস যা Parcelable
ইন্টারফেস বাস্তবায়ন করে।
AIDL-এ একটি কাস্টম CPP ব্যাকএন্ড পার্সেলযোগ্য ঘোষণার জন্য, cpp_header
ব্যবহার করুন:
package my.pack.age;
parcelable Foo cpp_header "my/pack/age/Foo.h";
my/pack/age/Foo.h
এ C++ বাস্তবায়ন এইরকম দেখাচ্ছে:
#include <binder/Parcelable.h>
class MyCustomParcelable : public android::Parcelable {
public:
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
AIDL-এ একটি কাস্টম NDK পার্সেলযোগ্য ঘোষণার জন্য, ndk_header
ব্যবহার করুন:
package my.pack.age;
parcelable Foo ndk_header "android/pack/age/Foo.h";
android/pack/age/Foo.h
এ NDK বাস্তবায়ন এইরকম দেখাচ্ছে:
#include <android/binder_parcel.h>
class MyCustomParcelable {
public:
binder_status_t writeToParcel(AParcel* _Nonnull parcel) const;
binder_status_t readFromParcel(const AParcel* _Nonnull parcel);
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
অ্যান্ড্রয়েড 15-এ, AIDL-এ একটি কাস্টম মরিচা পার্সেলযোগ্য ঘোষণার জন্য, rust_type
ব্যবহার করুন:
package my.pack.age;
@RustOnlyStableParcelable parcelable Foo rust_type "rust_crate::Foo";
rust_crate/src/lib.rs
এ মরিচা বাস্তবায়ন এইরকম দেখাচ্ছে:
use binder::{
binder_impl::{BorrowedParcel, UnstructuredParcelable},
impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
StatusCode,
};
#[derive(Clone, Debug, Eq, PartialEq)]
struct Foo {
pub bar: String,
}
impl UnstructuredParcelable for Foo {
fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
parcel.write(&self.bar)?;
Ok(())
}
fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
let bar = parcel.read()?;
Ok(Self { bar })
}
}
impl_deserialize_for_unstructured_parcelable!(Foo);
impl_serialize_for_unstructured_parcelable!(Foo);
তারপরে আপনি এই পার্সেলেবলটিকে এআইডিএল ফাইলগুলিতে একটি টাইপ হিসাবে ব্যবহার করতে পারেন, তবে এটি এআইডিএল দ্বারা তৈরি করা হবে না। <
এবং ==
CPP এবং NDK ব্যাকএন্ড কাস্টম পার্সেলেবলের জন্য অপারেটরদেরকে সেগুলিকে union
ব্যবহার করার জন্য প্রদান করুন।
ডিফল্ট মান
স্ট্রাকচার্ড পার্সেবলগুলি আদিম, String
ক্ষেত্র এবং এই ধরণের অ্যারের জন্য প্রতি-ক্ষেত্র ডিফল্ট মান ঘোষণা করতে পারে।
parcelable Foo {
int numField = 42;
String stringField = "string value";
char charValue = 'a';
...
}
জাভা ব্যাকএন্ডে, যখন ডিফল্ট মান অনুপস্থিত থাকে, ক্ষেত্রগুলি আদিম প্রকারের জন্য শূন্য মান হিসাবে আরম্ভ করা হয় এবং ননপ্রিমিটিভ প্রকারের জন্য null
।
অন্যান্য ব্যাকএন্ডে, ক্ষেত্রগুলি ডিফল্ট প্রারম্ভিক মানগুলির সাথে আরম্ভ করা হয় যখন ডিফল্ট মানগুলি সংজ্ঞায়িত করা হয় না। উদাহরণস্বরূপ, C++ ব্যাকএন্ডে, String
ক্ষেত্রগুলি একটি খালি স্ট্রিং হিসাবে আরম্ভ করা হয় এবং List<T>
ক্ষেত্রগুলি একটি খালি vector<T>
>। @nullable
ক্ষেত্রগুলি null-value ক্ষেত্র হিসাবে আরম্ভ করা হয়।
ইউনিয়ন
AIDL ইউনিয়নগুলিকে ট্যাগ করা হয়েছে এবং তাদের বৈশিষ্ট্যগুলি সমস্ত ব্যাকএন্ডে একই রকম৷ এগুলি প্রথম ক্ষেত্রের ডিফল্ট মান অনুসারে তৈরি করা হয়েছে এবং তাদের সাথে যোগাযোগ করার একটি ভাষা-নির্দিষ্ট উপায় রয়েছে:
union Foo {
int intField;
long longField;
String stringField;
MyParcelable parcelableField;
...
}
জাভা উদাহরণ
Foo u = Foo.intField(42); // construct
if (u.getTag() == Foo.intField) { // tag query
// use u.getIntField() // getter
}
u.setSringField("abc"); // setter
C++ এবং NDK উদাহরণ
Foo u; // default constructor
assert (u.getTag() == Foo::intField); // tag query
assert (u.get<Foo::intField>() == 0); // getter
u.set<Foo::stringField>("abc"); // setter
assert (u == Foo::make<Foo::stringField>("abc")); // make<tag>(value)
মরিচা উদাহরণ
মরিচায়, ইউনিয়নগুলি enums হিসাবে প্রয়োগ করা হয় এবং সুস্পষ্ট গেটার এবং সেটার নেই।
let mut u = Foo::Default(); // default constructor
match u { // tag match + get
Foo::IntField(x) => assert!(x == 0);
Foo::LongField(x) => panic!("Default constructed to first field");
Foo::StringField(x) => panic!("Default constructed to first field");
Foo::ParcelableField(x) => panic!("Default constructed to first field");
...
}
u = Foo::StringField("abc".to_string()); // set
ত্রুটি হ্যান্ডলিং
অ্যান্ড্রয়েড ওএস ত্রুটি রিপোর্ট করার সময় পরিষেবাগুলির জন্য অন্তর্নির্মিত ত্রুটির প্রকারগুলি সরবরাহ করে৷ এগুলি বাইন্ডার দ্বারা ব্যবহৃত হয় এবং বাইন্ডার ইন্টারফেস বাস্তবায়নকারী যেকোন পরিষেবা দ্বারা ব্যবহার করা যেতে পারে। এআইডিএল সংজ্ঞায় তাদের ব্যবহার ভালভাবে নথিভুক্ত করা হয়েছে এবং তাদের কোনো ব্যবহারকারী-সংজ্ঞায়িত অবস্থা বা রিটার্ন টাইপের প্রয়োজন নেই।
ত্রুটি সহ আউটপুট পরামিতি
যখন একটি এআইডিএল ফাংশন একটি ত্রুটি রিপোর্ট করে, তখন ফাংশনটি আউটপুট প্যারামিটারগুলি শুরু বা সংশোধন করতে পারে না। বিশেষত, আউটপুট প্যারামিটার পরিবর্তন করা হতে পারে যদি ত্রুটিটি আনপার্সেলিংয়ের সময় ঘটে, যেমনটি লেনদেনের প্রক্রিয়াকরণের সময় ঘটতে পারে না। সাধারণভাবে, যখন একটি AIDL ফাংশন থেকে একটি ত্রুটি পাওয়া যায়, তখন সমস্ত inout
এবং out
প্যারামিটারের পাশাপাশি রিটার্ন মান (যা কিছু ব্যাকএন্ডে একটি out
প্যারামিটারের মতো কাজ করে) একটি অনির্দিষ্ট অবস্থায় বিবেচনা করা উচিত।
কোন ত্রুটি মান ব্যবহার করতে হবে
বিল্ট-ইন ত্রুটির মানগুলির অনেকগুলি যেকোন এআইডিএল ইন্টারফেসে ব্যবহার করা যেতে পারে, তবে কিছু একটি বিশেষ উপায়ে চিকিত্সা করা হয়। উদাহরণস্বরূপ, EX_UNSUPPORTED_OPERATION
এবং EX_ILLEGAL_ARGUMENT
ত্রুটির অবস্থা বর্ণনা করার সময় ব্যবহার করা ঠিক, কিন্তু EX_TRANSACTION_FAILED
ব্যবহার করা উচিত নয় কারণ এটি অন্তর্নিহিত অবকাঠামো দ্বারা বিশেষভাবে চিকিত্সা করা হয়৷ এই অন্তর্নির্মিত মান সম্পর্কে আরও তথ্যের জন্য ব্যাকএন্ড নির্দিষ্ট সংজ্ঞা পরীক্ষা করুন।
যদি এইডএল ইন্টারফেসের জন্য অতিরিক্ত ত্রুটি মানগুলির প্রয়োজন হয় যা অন্তর্নির্মিত ত্রুটির প্রকারের দ্বারা আচ্ছাদিত নয়, তারা বিশেষ পরিষেবা-নির্দিষ্ট অন্তর্নির্মিত ত্রুটি ব্যবহার করতে পারে যা ব্যবহারকারী দ্বারা সংজ্ঞায়িত একটি পরিষেবা-নির্দিষ্ট ত্রুটি মান অন্তর্ভুক্ত করার অনুমতি দেয়। এই পরিষেবা -নির্দিষ্ট ত্রুটিগুলি সাধারণত এইডএল ইন্টারফেসে একটি const int
বা int
-ব্যাকড enum
হিসাবে সংজ্ঞায়িত করা হয় এবং বাইন্ডার দ্বারা পার্স করা হয় না।
জাভাতে, ত্রুটিগুলি ব্যতিক্রমগুলিতে মানচিত্র যেমন android.os.RemoteException
। পরিষেবা-নির্দিষ্ট ব্যতিক্রমগুলির জন্য, জাভা ব্যবহারকারী-সংজ্ঞায়িত ত্রুটির সাথে android.os.ServiceSpecificException
ব্যবহার করে।
অ্যান্ড্রয়েডে নেটিভ কোড ব্যতিক্রম ব্যবহার করে না। সিপিপি ব্যাকএন্ড android::binder::Status
ব্যবহার করে। এনডিকে ব্যাকএন্ড ndk::ScopedAStatus
ব্যবহার করে। এইডএল দ্বারা উত্পাদিত প্রতিটি পদ্ধতি পদ্ধতির স্থিতি উপস্থাপন করে এর মধ্যে একটিকে ফেরত দেয়। মরিচা ব্যাকএন্ড এনডিকে হিসাবে একই ব্যতিক্রম কোডের মানগুলি ব্যবহার করে তবে তাদের ব্যবহারকারীর কাছে পৌঁছে দেওয়ার আগে তাদের দেশীয় মরিচা ত্রুটি ( StatusCode
, ExceptionCode
) এ রূপান্তর করে। পরিষেবা-নির্দিষ্ট ত্রুটির জন্য, প্রত্যাবর্তিত Status
বা ScopedAStatus
ব্যবহারকারী-সংজ্ঞায়িত ত্রুটির সাথে EX_SERVICE_SPECIFIC
ব্যবহার করে।
অন্তর্নির্মিত ত্রুটির প্রকারগুলি নিম্নলিখিত ফাইলগুলিতে পাওয়া যাবে:
ব্যাকএন্ড | সংজ্ঞা |
---|---|
জাভা | android/os/Parcel.java |
সিপিপি | binder/Status.h |
এনডিকে | android/binder_status.h |
মরিচা | android/binder_status.h |
বিভিন্ন ব্যাকেন্ড ব্যবহার করুন
এই নির্দেশাবলী অ্যান্ড্রয়েড প্ল্যাটফর্ম কোডের জন্য নির্দিষ্ট। এই উদাহরণগুলি একটি সংজ্ঞায়িত প্রকার, my.package.IFoo
ব্যবহার করে। মরিচা ব্যাকএন্ড কীভাবে ব্যবহার করবেন সে সম্পর্কে নির্দেশাবলীর জন্য, অ্যান্ড্রয়েড মরিচা নিদর্শনগুলিতে মরিচা এইডেলের উদাহরণ দেখুন।
আমদানি প্রকার
সংজ্ঞায়িত প্রকারটি কোনও ইন্টারফেস, পার্সেলেবল বা ইউনিয়ন হোক না কেন, আপনি এটি জাভাতে আমদানি করতে পারেন:
import my.package.IFoo;
বা সিপিপি ব্যাকএন্ডে:
#include <my/package/IFoo.h>
বা এনডিকে ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <aidl/my/package/IFoo.h>
বা মরিচা ব্যাকএন্ডে:
use my_package::aidl::my::package::IFoo;
যদিও আপনি জাভাতে একটি নেস্টেড টাইপ আমদানি করতে পারেন, সিপিপি এবং এনডিকে ব্যাকেন্ডগুলিতে আপনাকে অবশ্যই এর মূল ধরণের জন্য শিরোনামটি অন্তর্ভুক্ত করতে হবে। উদাহরণস্বরূপ, my/package/IFoo.aidl
( IFoo
ফাইলের মূল ধরণ) এ সংজ্ঞায়িত নেস্টেড টাইপ Bar
আমদানি করার সময় আপনাকে অবশ্যই সিপিপি ব্যাকএন্ডের জন্য <my/package/IFoo.h>
অন্তর্ভুক্ত করতে হবে (বা এনডিকে ব্যাকএন্ডের জন্য বা <aidl/my/package/IFoo.h>
)।
একটি ইন্টারফেস প্রয়োগ করুন
একটি ইন্টারফেস বাস্তবায়নের জন্য আপনাকে অবশ্যই নেটিভ স্টাব শ্রেণি থেকে উত্তরাধিকারী হতে হবে। কোনও ইন্টারফেসের বাস্তবায়নকে প্রায়শই পরিষেবা বলা হয় যখন এটি পরিষেবা পরিচালক বা android.app.ActivityManager
সাথে নিবন্ধিত হয় এবং যখন কোনও পরিষেবার ক্লায়েন্ট দ্বারা নিবন্ধিত হয় তখন একটি কলব্যাক বলা হয়। যাইহোক, সঠিক ব্যবহারের উপর নির্ভর করে ইন্টারফেস বাস্তবায়নগুলি বর্ণনা করতে বিভিন্ন নাম ব্যবহার করা হয়। স্টাব ক্লাসটি বাইন্ডার ড্রাইভারের কাছ থেকে কমান্ডগুলি পড়ে এবং আপনি যে পদ্ধতিগুলি প্রয়োগ করেন তা কার্যকর করে। কল্পনা করুন যে আপনার কাছে এর মতো একটি এইডএল ফাইল রয়েছে:
package my.package;
interface IFoo {
int doFoo();
}
জাভাতে, আপনাকে অবশ্যই উত্পন্ন Stub
ক্লাস থেকে প্রসারিত করতে হবে:
import my.package.IFoo;
public class MyFoo extends IFoo.Stub {
@Override
int doFoo() { ... }
}
সিপিপি ব্যাকএন্ডে:
#include <my/package/BnFoo.h>
class MyFoo : public my::package::BnFoo {
android::binder::Status doFoo(int32_t* out) override;
}
এনডিকে ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <aidl/my/package/BnFoo.h>
class MyFoo : public aidl::my::package::BnFoo {
ndk::ScopedAStatus doFoo(int32_t* out) override;
}
মরিচা ব্যাকএন্ডে:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFoo};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
impl IFoo for MyFoo {
fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
বা অ্যাসিঙ্ক মরিচা সহ:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFooAsyncServer};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
#[async_trait]
impl IFooAsyncServer for MyFoo {
async fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
নিবন্ধন করুন এবং পরিষেবা পান
প্ল্যাটফর্ম অ্যান্ড্রয়েডের পরিষেবাগুলি সাধারণত servicemanager
প্রক্রিয়াটিতে নিবন্ধিত হয়। নিম্নলিখিত এপিআই ছাড়াও, কিছু এপিআই পরিষেবাটি পরীক্ষা করে (যার অর্থ পরিষেবাটি উপলব্ধ না হলে তারা তাত্ক্ষণিকভাবে ফিরে আসে)। সঠিক বিশদের জন্য সংশ্লিষ্ট servicemanager
ইন্টারফেসটি পরীক্ষা করুন। প্ল্যাটফর্ম অ্যান্ড্রয়েডের বিরুদ্ধে সংকলন করার সময় আপনি এই ক্রিয়াকলাপগুলি সম্পাদন করতে পারেন।
জাভাতে:
import android.os.ServiceManager;
// registering
ServiceManager.addService("service-name", myService);
// return if service is started now
myService = IFoo.Stub.asInterface(ServiceManager.checkService("service-name"));
// waiting until service comes up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));
সিপিপি ব্যাকএন্ডে:
#include <binder/IServiceManager.h>
// registering
defaultServiceManager()->addService(String16("service-name"), myService);
// return if service is started now
status_t err = checkService<IFoo>(String16("service-name"), &myService);
// waiting until service comes up (new in Android 11)
myService = waitForService<IFoo>(String16("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = waitForDeclaredService<IFoo>(String16("service-name"));
এনডিকে ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <android/binder_manager.h>
// registering
binder_exception_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
// return if service is started now
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_checkService("service-name")));
// is a service declared in the VINTF manifest
// VINTF services have the type in the interface instance name.
bool isDeclared = AServiceManager_isDeclared("android.hardware.light.ILights/default");
// wait until a service is available (if isDeclared or you know it's available)
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService("service-name")));
মরিচা ব্যাকএন্ডে:
use myfoo::MyFoo;
use binder;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_binder(
my_service,
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Does not return - spawn or perform any work you mean to do before this call.
binder::ProcessState::join_thread_pool()
}
অ্যাসিঙ্ক মরিচা ব্যাকএন্ডে, একক থ্রেডযুক্ত রানটাইম সহ:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "current_thread")]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleeps forever, but does not join the binder threadpool.
// Spawned tasks run on this thread.
std::future::pending().await
}
অন্যান্য বিকল্পগুলির একটি গুরুত্বপূর্ণ পার্থক্য হ'ল অ্যাসিঙ্ক মরিচা এবং একক থ্রেডেড রানটাইম ব্যবহার করার সময় আপনি join_thread_pool
কল করবেন না । এটি কারণ আপনাকে টোকিওকে এমন একটি থ্রেড দিতে হবে যেখানে এটি তৈরি করা কাজগুলি কার্যকর করতে পারে। নিম্নলিখিত উদাহরণে, মূল থ্রেডটি সেই উদ্দেশ্যে কাজ করে। tokio::spawn
মূল থ্রেডে কার্যকর করে এমন কোনও কাজ তৈরি করা হয়েছে।
অ্যাসিঙ্ক মরিচা ব্যাকএন্ডে, একটি মাল্টিথ্রেডড রানটাইম সহ:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleep forever.
tokio::task::block_in_place(|| {
binder::ProcessState::join_thread_pool();
});
}
মাল্টিথ্রেডেড টোকিও রানটাইম সহ, স্প্যানড টাস্কগুলি মূল থ্রেডে কার্যকর হয় না। অতএব, মূল থ্রেডে join_thread_pool
কল করা আরও বেশি অর্থবোধ করে যাতে মূল থ্রেডটি নিষ্ক্রিয় না হয়। অ্যাসিঙ্ক প্রসঙ্গটি ছাড়তে আপনাকে অবশ্যই কলটি block_in_place
আবৃত করতে হবে।
মৃত্যুর লিঙ্ক
আপনি যখন কোনও বাইন্ডার হোস্টিং পরিষেবা মারা যায় তার জন্য একটি বিজ্ঞপ্তি পেতে অনুরোধ করতে পারেন। এটি কলব্যাক প্রক্সিগুলি ফাঁস এড়াতে বা ত্রুটি পুনরুদ্ধারে সহায়তা করতে সহায়তা করতে পারে। বাইন্ডার প্রক্সি অবজেক্টগুলিতে এই কলগুলি তৈরি করুন।
- জাভাতে,
android.os.IBinder::linkToDeath
ব্যবহার করুন। - সিপিপি ব্যাকএন্ডে,
android::IBinder::linkToDeath
। - এনডিকে ব্যাকএন্ডে,
AIBinder_linkToDeath
ব্যবহার করুন। - মরিচা ব্যাকএন্ডে, একটি
DeathRecipient
অবজেক্ট তৈরি করুন, তারপরেmy_binder.link_to_death(&mut my_death_recipient)
কল করুন। মনে রাখবেন যেDeathRecipient
কলব্যাকের মালিকানাধীন, যতক্ষণ না আপনি বিজ্ঞপ্তিগুলি পেতে চান ততক্ষণ আপনাকে অবশ্যই সেই বস্তুটিকে বাঁচিয়ে রাখতে হবে।
কলারের তথ্য
কার্নেল বাইন্ডার কল পাওয়ার সময়, কলার তথ্য বেশ কয়েকটি এপিআইতে পাওয়া যায়। প্রক্রিয়া আইডি (পিআইডি) বোঝায় যে প্রক্রিয়াটির লিনাক্স প্রক্রিয়া আইডি যা লেনদেন প্রেরণ করছে। ব্যবহারকারী আইডি (ইউআই) লিনাক্স ব্যবহারকারী আইডি বোঝায়। একমুখী কল পাওয়ার সময়, কলিং পিআইডি 0 0 হয় বাইন্ডার লেনদেনের প্রসঙ্গের বাইরে, এই ফাংশনগুলি বর্তমান প্রক্রিয়াটির পিআইডি এবং ইউআইডি ফেরত দেয়।
জাভা ব্যাকএন্ডে:
... = Binder.getCallingPid();
... = Binder.getCallingUid();
সিপিপি ব্যাকএন্ডে:
... = IPCThreadState::self()->getCallingPid();
... = IPCThreadState::self()->getCallingUid();
এনডিকে ব্যাকএন্ডে:
... = AIBinder_getCallingPid();
... = AIBinder_getCallingUid();
মরিচা ব্যাকএন্ডে, ইন্টারফেসটি প্রয়োগ করার সময়, নিম্নলিখিতগুলি নির্দিষ্ট করুন (পরিবর্তে এটি ডিফল্ট করার অনুমতি দেওয়ার পরিবর্তে):
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
পরিষেবাগুলির জন্য বাগ রিপোর্ট এবং ডিবাগিং এপিআই
যখন বাগের প্রতিবেদনগুলি চলে (উদাহরণস্বরূপ, adb bugreport
সহ), তারা বিভিন্ন সমস্যা ডিবাগ করতে সহায়তা করার জন্য সিস্টেমের চারপাশে থেকে তথ্য সংগ্রহ করে। এইডএল পরিষেবাগুলির জন্য, বাগ রিপোর্টগুলি পরিষেবা পরিচালকের সাথে নিবন্ধিত সমস্ত পরিষেবাগুলিতে বাইনারি dumpsys
ব্যবহার করে তাদের তথ্য বাগ রিপোর্টে ফেলে দেয়। আপনি dumpsys SERVICE [ARGS]
সহ কোনও পরিষেবা থেকে তথ্য পেতে কমান্ড লাইনে dumpsys
ব্যবহার করতে পারেন। সি ++ এবং জাভা ব্যাকএন্ডগুলিতে, আপনি addService
অতিরিক্ত আর্গুমেন্ট ব্যবহার করে পরিষেবাগুলি যে ক্রমটি ফেলে দেওয়া হয় তা নিয়ন্ত্রণ করতে পারেন। ডিবাগিংয়ের সময় কোনও পরিষেবার পিআইডি পেতে আপনি dumpsys --pid SERVICE
ব্যবহার করতে পারেন।
আপনার পরিষেবাতে কাস্টম আউটপুট যুক্ত করতে, আপনার সার্ভার অবজেক্টে dump
পদ্ধতিটি ওভাররাইড করুন যেমন আপনি কোনও এইডএল ফাইলটিতে সংজ্ঞায়িত অন্য কোনও আইপিসি পদ্ধতি বাস্তবায়ন করছেন। এটি করার সময়, অ্যাপের অনুমতিটি android.permission.DUMP
ডাম্পিংকে সীমাবদ্ধ করুন বা নির্দিষ্ট ইউআইডিগুলিতে ডাম্পিংকে সীমাবদ্ধ করুন।
জাভা ব্যাকএন্ডে:
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {...}
সিপিপি ব্যাকএন্ডে:
status_t dump(int, const android::android::Vector<android::String16>&) override;
এনডিকে ব্যাকএন্ডে:
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
মরিচা ব্যাকএন্ডে, ইন্টারফেসটি প্রয়োগ করার সময়, নিম্নলিখিতগুলি নির্দিষ্ট করুন (পরিবর্তে এটি ডিফল্ট করার অনুমতি দেওয়ার পরিবর্তে):
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
দুর্বল পয়েন্টার ব্যবহার করুন
আপনি একটি বাইন্ডার অবজেক্টের দুর্বল রেফারেন্স রাখতে পারেন।
যদিও জাভা WeakReference
সমর্থন করে, এটি দেশীয় স্তরে দুর্বল বাইন্ডার রেফারেন্সগুলিকে সমর্থন করে না।
সিপিপি ব্যাকএন্ডে, দুর্বল প্রকারটি wp<IFoo>
।
এনডিকে ব্যাকএন্ডে, ScopedAIBinder_Weak
ব্যবহার করুন:
#include <android/binder_auto_utils.h>
AIBinder* binder = ...;
ScopedAIBinder_Weak myWeakReference = ScopedAIBinder_Weak(AIBinder_Weak_new(binder));
মরিচা ব্যাকএন্ডে, WpIBinder
বা Weak<IFoo>
:
let weak_interface = myIface.downgrade();
let weak_binder = myIface.as_binder().downgrade();
গতিশীলভাবে ইন্টারফেস বর্ণনাকারী পান
ইন্টারফেস বর্ণনাকারী একটি ইন্টারফেসের ধরণ চিহ্নিত করে। ডিবাগ করার সময় বা যখন আপনার অজানা বাইন্ডার থাকে তখন এটি কার্যকর।
জাভাতে, আপনি কোড সহ ইন্টারফেস বর্ণনাকারী পেতে পারেন:
service = /* get ahold of service object */
... = service.asBinder().getInterfaceDescriptor();
সিপিপি ব্যাকএন্ডে:
service = /* get ahold of service object */
... = IInterface::asBinder(service)->getInterfaceDescriptor();
এনডিকে এবং মরিচা ব্যাকেন্ডস এই ক্ষমতাটিকে সমর্থন করে না।
স্ট্যাটিক্যালি ইন্টারফেস বর্ণনাকারী পান
কখনও কখনও (যেমন @VintfStability
পরিষেবাগুলি নিবন্ধভুক্ত করার সময়), আপনাকে ইন্টারফেস বর্ণনাকারী কী তা জানতে হবে। জাভাতে, আপনি কোড যুক্ত করে বর্ণনাকারী পেতে পারেন:
import my.package.IFoo;
... IFoo.DESCRIPTOR
সিপিপি ব্যাকএন্ডে:
#include <my/package/BnFoo.h>
... my::package::BnFoo::descriptor
এনডিকে ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <aidl/my/package/BnFoo.h>
... aidl::my::package::BnFoo::descriptor
মরিচা ব্যাকএন্ডে:
aidl::my::package::BnFoo::get_descriptor()
এনাম রেঞ্জ
নেটিভ ব্যাকেন্ডগুলিতে, আপনি কোনও এনাম গ্রহণ করতে পারে এমন সম্ভাব্য মানগুলির উপর পুনরাবৃত্তি করতে পারেন। কোড আকারের বিবেচনার কারণে, এটি জাভাতে সমর্থিত নয়।
এইডএলে সংজ্ঞায়িত একটি এনাম MyEnum
জন্য, নিম্নলিখিত হিসাবে পুনরাবৃত্তি সরবরাহ করা হয়।
সিপিপি ব্যাকএন্ডে:
::android::enum_range<MyEnum>()
এনডিকে ব্যাকএন্ডে:
::ndk::enum_range<MyEnum>()
মরিচা ব্যাকএন্ডে:
MyEnum::enum_values()
থ্রেড ব্যবস্থাপনা
একটি প্রক্রিয়াতে libbinder
প্রতিটি উদাহরণ একটি থ্রেডপুল বজায় রাখে। বেশিরভাগ ব্যবহারের ক্ষেত্রে, এটি হুবহু একটি থ্রেডপুল হওয়া উচিত, সমস্ত ব্যাকেন্ড জুড়ে ভাগ করা। একমাত্র ব্যতিক্রম যদি বিক্রেতার কোড /dev/vndbinder
সাথে কথা বলতে libbinder
অন্য একটি অনুলিপি লোড করে। এটি একটি পৃথক বাইন্ডার নোডে রয়েছে, সুতরাং থ্রেডপুলটি ভাগ করা হয়নি।
জাভা ব্যাকএন্ডের জন্য, থ্রেডপুলটি কেবল আকারে বাড়তে পারে (কারণ এটি ইতিমধ্যে শুরু হয়েছে):
BinderInternal.setMaxThreads(<new larger value>);
সিপিপি ব্যাকএন্ডের জন্য, নিম্নলিখিত অপারেশনগুলি উপলব্ধ:
// set max threadpool count (default is 15)
status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(numThreads);
// create threadpool
ProcessState::self()->startThreadPool();
// add current thread to threadpool (adds thread to max thread count)
IPCThreadState::self()->joinThreadPool();
একইভাবে, এনডিকে ব্যাকএন্ডে:
bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
ABinderProcess_startThreadPool();
ABinderProcess_joinThreadPool();
মরিচা ব্যাকএন্ডে:
binder::ProcessState::start_thread_pool();
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
binder::ProcessState::join_thread_pool();
অ্যাসিঙ্ক মরিচা ব্যাকএন্ডের সাহায্যে আপনার দুটি থ্রেডপুল দরকার: বাইন্ডার এবং টোকিও। এর অর্থ হ'ল অ্যাসিঙ্ক মরিচা ব্যবহার করা অ্যাপ্লিকেশনগুলির বিশেষ বিবেচনার প্রয়োজন, বিশেষত যখন এটি join_thread_pool
ব্যবহারের ক্ষেত্রে আসে। এ সম্পর্কে আরও তথ্যের জন্য নিবন্ধকরণ পরিষেবাদি বিভাগটি দেখুন।
সংরক্ষিত নাম
সি ++, জাভা এবং মরিচা কিছু নাম কীওয়ার্ড হিসাবে বা ভাষা-নির্দিষ্ট ব্যবহারের জন্য সংরক্ষণ করে। যদিও এইডএলটি ভাষার নিয়মের উপর ভিত্তি করে বিধিনিষেধগুলি প্রয়োগ করে না, তবে ক্ষেত্র বা টাইপ নামগুলি ব্যবহার করে যা সংরক্ষিত নামের সাথে মেলে তার ফলে সি ++ বা জাভাগুলির জন্য সংকলন ব্যর্থতা হতে পারে। মরিচা জন্য, ক্ষেত্র বা প্রকারটি কাঁচা আইডেন্টিফায়ার সিনট্যাক্স ব্যবহার করে নামকরণ করা হয়, r#
উপসর্গ ব্যবহার করে অ্যাক্সেসযোগ্য।
আমরা আপনার এইডএল সংজ্ঞাগুলিতে সংরক্ষিত নামগুলি ব্যবহার করা এড়ানোর পরামর্শ দিই যেখানে সম্ভব যেখানে আনারগোনমিক বাইন্ডিং বা সরাসরি সংকলন ব্যর্থতা এড়াতে সম্ভব।
আপনার যদি ইতিমধ্যে আপনার এইডএল সংজ্ঞাগুলিতে নাম সংরক্ষণ করে থাকে তবে প্রোটোকল সামঞ্জস্যপূর্ণ থাকাকালীন আপনি নিরাপদে ক্ষেত্রগুলির নাম পরিবর্তন করতে পারেন। বিল্ডিং চালিয়ে যেতে আপনার কোড আপডেট করতে হবে, তবে ইতিমধ্যে নির্মিত যে কোনও প্রোগ্রাম আন্তঃসংযোগ অব্যাহত রয়েছে।
নাম এড়াতে:
,একটি এইডএল ব্যাকএন্ড স্টাব কোড জেনারেশনের জন্য একটি লক্ষ্য। সর্বদা একটি নির্দিষ্ট রানটাইম সহ একটি নির্দিষ্ট ভাষায় সহায়তা ফাইলগুলি ব্যবহার করুন। প্রসঙ্গে নির্ভর করে আপনার বিভিন্ন এইডেল ব্যাকেন্ড ব্যবহার করা উচিত।
নিম্নলিখিত সারণীতে, এপিআই পৃষ্ঠের স্থায়িত্ব এই এপিআই পৃষ্ঠের বিপরীতে কোড সংকলন করার ক্ষমতা বোঝায় যাতে কোডটি system.img
থেকে স্বাধীনভাবে বিতরণ করা যায়। আইএমজি libbinder.so
বাইনারি।
এইডেলের নিম্নলিখিত ব্যাকেন্ড রয়েছে:
ব্যাকএন্ড | ভাষা | এপিআই পৃষ্ঠ | সিস্টেম তৈরি করুন |
---|---|---|---|
জাভা | জাভা | এসডিকে বা SystemApi (স্থিতিশীল*) | সব |
এনডিকে | সি++ | libbinder_ndk (স্থিতিশীল*) | aidl_interface |
সিপিপি | সি++ | libbinder (অস্থির) | সব |
মরিচা | মরিচা | libbinder_rs (স্থিতিশীল*) | aidl_interface |
- এই এপিআই পৃষ্ঠগুলি স্থিতিশীল, তবে পরিষেবা পরিচালনার জন্য অনেকগুলি এপিআই যেমন অভ্যন্তরীণ প্ল্যাটফর্ম ব্যবহারের জন্য সংরক্ষিত এবং অ্যাপ্লিকেশনগুলিতে উপলভ্য নয়। অ্যাপ্লিকেশনগুলিতে কীভাবে সহায়তা ব্যবহার করবেন সে সম্পর্কে আরও তথ্যের জন্য, অ্যান্ড্রয়েড ইন্টারফেস সংজ্ঞা ভাষা (এইডএল) দেখুন।
- মরিচা ব্যাকএন্ডটি অ্যান্ড্রয়েড 12 এ চালু হয়েছিল; এনডিকে ব্যাকএন্ড অ্যান্ড্রয়েড 10 হিসাবে উপলব্ধ।
- মরিচা ক্রেটটি
libbinder_ndk
এর উপরে নির্মিত, যা এটি স্থিতিশীল এবং বহনযোগ্য হতে দেয়। শীর্ষগুলি সিস্টেমের পাশের স্ট্যান্ডার্ড উপায়ে বাইন্ডার ক্রেট ব্যবহার করে। মরিচা অংশটি একটি শীর্ষে বান্ডিল করা হয় এবং এর ভিতরে প্রেরণ করা হয়। এই অংশটি সিস্টেম পার্টিশনেlibbinder_ndk.so
এর উপর নির্ভর করে।
সিস্টেম তৈরি করুন
ব্যাকএন্ডের উপর নির্ভর করে স্টাব কোডে সহায়তা সংকলন করার দুটি উপায় রয়েছে। বিল্ড সিস্টেমগুলি সম্পর্কে আরও তথ্যের জন্য, SOONG মডিউল রেফারেন্স দেখুন।
কোর বিল্ড সিস্টেম
যে কোনও cc_
বা java_
Android.bp module
(বা তাদের Android.mk
সমতুল্য) এ, আপনি উত্স ফাইল হিসাবে এইডএল ( .aidl
) ফাইলগুলি নির্দিষ্ট করতে পারেন। এই ক্ষেত্রে, এইডেলের জাভা বা সিপিপি ব্যাকেন্ডগুলি ব্যবহৃত হয় (এনডিকে ব্যাকএন্ড নয়), এবং সংশ্লিষ্ট এইডএল ফাইলগুলি ব্যবহারের জন্য ক্লাসগুলি মডিউলে স্বয়ংক্রিয়ভাবে যুক্ত হয়। আপনি local_include_dirs
(যা বিল্ড সিস্টেমকে সেই মডিউলটিতে ফাইলের সহায়তা ফাইলের মূল পথটি বলে) এর মতো বিকল্পগুলি নির্দিষ্ট করতে পারেন aidl:
গ্রুপের অধীনে এই মডিউলগুলিতে।
মরিচা ব্যাকএন্ড কেবল মরিচা দিয়ে ব্যবহারের জন্য। rust_
মডিউলগুলি আলাদাভাবে পরিচালনা করা হয় যে এইডএল ফাইলগুলি উত্স ফাইল হিসাবে নির্দিষ্ট করা হয় না। পরিবর্তে, aidl_interface
মডিউলটি aidl_interface_name -rust
নামে একটি rustlib
তৈরি করে, যার বিরুদ্ধে লিঙ্ক করা যেতে পারে। বিশদ জন্য, মরিচা এইডেল উদাহরণ দেখুন।
এইডল_ইন্টারফেস
aidl_interface
বিল্ড সিস্টেমের সাথে ব্যবহৃত প্রকারগুলি অবশ্যই কাঠামোগত হতে হবে। কাঠামোগত হওয়ার জন্য, পার্সেলেবলগুলিতে অবশ্যই সরাসরি ক্ষেত্রগুলি থাকতে হবে এবং লক্ষ্য ভাষায় সরাসরি সংজ্ঞায়িত প্রকারের ঘোষণা হতে হবে না। স্ট্রাকচার্ড এইডল কীভাবে স্থিতিশীল এইডেলের সাথে ফিট করে, তার জন্য কাঠামোগত বনাম স্থিতিশীল সহায়তা দেখুন।
প্রকারভেদ
প্রকারের জন্য একটি রেফারেন্স বাস্তবায়ন হিসাবে aidl
সংকলকটিকে বিবেচনা করুন। আপনি যখন কোনও ইন্টারফেস তৈরি করেন, ফলাফল ইন্টারফেস ফাইলটি দেখতে aidl --lang=<backend> ...
আহ্বান করুন। আপনি যখন aidl_interface
মডিউলটি ব্যবহার করেন, আপনি আউটপুটটি আউটপুটটি দেখতে পারেন out/soong/.intermediates/ <path to module> /
।
জাভা বা এইডেল প্রকার | সি ++ প্রকার | এনডিকে টাইপ | মরিচা টাইপ |
---|---|---|---|
boolean | bool | bool | bool |
byte 8 | int8_t | int8_t | i8 |
char | char16_t | char16_t | u16 |
int | int32_t | int32_t | i32 |
long | int64_t | int64_t | i64 |
float | float | float | f32 |
double | double | double | f64 |
String | android::String16 | std::string | ইন: &str আউট: String |
android.os.Parcelable | android::Parcelable | N/A | N/A |
IBinder | android::IBinder | ndk::SpAIBinder | binder::SpIBinder |
T[] | std::vector<T> | std::vector<T> | ইন: &[T] আউট: Vec<T> |
byte[] | std::vector | std::vector 1 | ইন: &[u8] আউট: Vec<u8> |
List<T> | std::vector<T> 2 | std::vector<T> 3 | ইন: In: &[T] 4আউট: Vec<T> |
FileDescriptor | android::base::unique_fd | N/A | N/A |
ParcelFileDescriptor | android::os::ParcelFileDescriptor | ndk::ScopedFileDescriptor | binder::parcel::ParcelFileDescriptor |
ইন্টারফেসের ধরণ ( T ) | android::sp<T> | std::shared_ptr<T> 7 | binder::Strong |
পার্সেলেবল টাইপ ( T ) | T | T | T |
ইউনিয়ন প্রকার ( T ) 5 | T | T | T |
T[N] 6 | std::array<T, N> | std::array<T, N> | [T; N] |
1। অ্যান্ড্রয়েড 12 বা উচ্চতর ক্ষেত্রে, বাইট অ্যারে সামঞ্জস্যতার কারণে int8_t
এর পরিবর্তে uint8_t
ব্যবহার করে।
2। সি ++ ব্যাকএন্ড List<T>
সমর্থন করে যেখানে T
String
, IBinder
, ParcelFileDescriptor
বা পার্সেলেবলের একটি। অ্যান্ড্রয়েড 13 বা উচ্চতর ক্ষেত্রে, T
অ্যারে ব্যতীত কোনও অ -প্রিমিটিভ টাইপ (ইন্টারফেসের ধরণ সহ) হতে পারে। এওএসপি T[]
মতো অ্যারে প্রকারগুলি ব্যবহার করার পরামর্শ দেয়, কারণ তারা সমস্ত ব্যাকেন্ডে কাজ করে।
3। এনডিকে ব্যাকএন্ড List<T>
যেখানে T
String
, ParcelFileDescriptor
বা পার্সেলেবলের একটি। অ্যান্ড্রয়েড 13 বা উচ্চতর ক্ষেত্রে, T
অ্যারে ব্যতীত যে কোনও অ -প্রিমিটিভ টাইপ হতে পারে।
4। প্রকারগুলি ইনপুট (একটি যুক্তি), বা আউটপুট (একটি ফেরত মান) কিনা তার উপর নির্ভর করে মরিচা কোডের জন্য আলাদাভাবে পাস করা হয়।
5। ইউনিয়ন প্রকারগুলি অ্যান্ড্রয়েড 12 এবং উচ্চতর সমর্থিত।
6। অ্যান্ড্রয়েড 13 বা উচ্চতর, স্থির আকারের অ্যারেগুলি সমর্থিত। স্থির-আকারের অ্যারে একাধিক মাত্রা থাকতে পারে (উদাহরণস্বরূপ, int[3][4]
)। জাভা ব্যাকএন্ডে, স্থির আকারের অ্যারেগুলি অ্যারে প্রকার হিসাবে উপস্থাপিত হয়।
SharedRefBase
SharedRefBase::make\<My\>(... args ...)
এই ফাংশনটি একটি std::shared_ptr\<T\>
অবজেক্ট তৈরি করে, যা অভ্যন্তরীণভাবে পরিচালিত হয়, যদি বাইন্ডারটি অন্য প্রক্রিয়াটির মালিকানাধীন হয়। অন্যান্য উপায়গুলি তৈরি করা দ্বিগুণ মালিকানা সৃষ্টি করে।
8। জাভা বা এইডেল টাইপ byte[]
দেখুন।
দিকনির্দেশ (ইন, আউট এবং ইনআউট)
ফাংশনগুলিতে যুক্তিগুলির প্রকারগুলি নির্দিষ্ট করার সময়, আপনি সেগুলি in
, out
বা inout
হিসাবে নির্দিষ্ট করতে পারেন। এটি আইপিসি কলের জন্য তথ্যটি যে দিকটি পাস করা হয়েছে তা নিয়ন্ত্রণ করে।
in
আর্গুমেন্ট স্পেসিফায়ার ইঙ্গিত দেয় যে কলার থেকে কলির কাছে ডেটা পাস করা হয়।in
হ'ল ডিফল্ট দিক, তবে যদি ডেটা প্রকারগুলিওout
থাকতে পারে তবে আপনাকে অবশ্যই দিকটি নির্দিষ্ট করতে হবে।out
আর্গুমেন্টের নির্দিষ্ট অর্থ হ'ল কলি থেকে কলারের কাছে ডেটা পাস করা হয়।inout
আর্গুমেন্ট স্পেসিফায়ার হ'ল এই উভয়ের সংমিশ্রণ। তবে, আমরা আর্গুমেন্ট স্পেসিফায়ারinout
ব্যবহার করা এড়ানোর পরামর্শ দিই। আপনি যদি কোনও সংস্করণযুক্ত ইন্টারফেস এবং একটি পুরানো কলির সাথেinout
ব্যবহার করেন তবে কেবল কলারে উপস্থিত অতিরিক্ত ক্ষেত্রগুলি তাদের ডিফল্ট মানগুলিতে পুনরায় সেট করুন। মরিচা প্রতি শ্রদ্ধার সাথে, একটি সাধারণinout
টাইপ গ্রহণ করে&mut T
, এবং একটি তালিকাinout
টাইপ গ্রহণ করে&mut Vec<T>
>।
interface IRepeatExamples {
MyParcelable RepeatParcelable(MyParcelable token); // implicitly 'in'
MyParcelable RepeatParcelableWithIn(in MyParcelable token);
void RepeatParcelableWithInAndOut(in MyParcelable param, out MyParcelable result);
void RepeatParcelableWithInOut(inout MyParcelable param);
}
ইউটিএফ -8 এবং ইউটিএফ -16
সিপিপি ব্যাকএন্ডের সাহায্যে আপনি স্ট্রিংগুলি ইউটিএফ -8 বা ইউটিএফ -16 কিনা তা চয়ন করতে পারেন। এডিএলএলে @utf8InCpp String
হিসাবে স্ট্রিংগুলি ঘোষণা করুন তাদের স্বয়ংক্রিয়ভাবে ইউটিএফ -8 এ রূপান্তর করতে। এনডিকে এবং মরিচা ব্যাকেন্ডগুলি সর্বদা ইউটিএফ -8 স্ট্রিং ব্যবহার করে। utf8InCpp
টীকা সম্পর্কে আরও তথ্যের জন্য, ইউটিএফ 8 আইএনসিপি দেখুন।
শূন্যতা
আপনি @nullable
সাথে নাল হতে পারে এমন প্রকারগুলি টীকা দিতে পারেন। nullable
টীকা সম্পর্কে আরও তথ্যের জন্য, বাতিলযোগ্য দেখুন।
কাস্টম পার্সেলেবল
একটি কাস্টম পার্সেলেবল একটি পার্সেলেবল যা লক্ষ্য ব্যাকএন্ডে ম্যানুয়ালি প্রয়োগ করা হয়। আপনি যখন বিদ্যমান কাস্টম পার্সেলেবলের জন্য অন্যান্য ভাষায় সমর্থন যুক্ত করার চেষ্টা করছেন তখনই কাস্টম পার্সেলেবলগুলি ব্যবহার করুন যা পরিবর্তন করা যায় না।
এখানে একটি এইডেল পার্সেলেবল ঘোষণার উদাহরণ:
package my.pack.age;
parcelable Foo;
ডিফল্টরূপে, এটি একটি জাভা পার্সেলেবল ঘোষণা করে যেখানে my.pack.age.Foo
Parcelable
ইন্টারফেস বাস্তবায়নকারী একটি জাভা শ্রেণি।
এইডলে একটি কাস্টম সিপিপি ব্যাকএন্ড পার্সেলেবল ঘোষণার জন্য, cpp_header
ব্যবহার করুন:
package my.pack.age;
parcelable Foo cpp_header "my/pack/age/Foo.h";
my/pack/age/Foo.h
এ সি ++ বাস্তবায়ন এর মতো দেখাচ্ছে:
#include <binder/Parcelable.h>
class MyCustomParcelable : public android::Parcelable {
public:
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
এইডলে একটি কাস্টম এনডিকে পার্সেলেবল ঘোষণার জন্য, ndk_header
ব্যবহার করুন:
package my.pack.age;
parcelable Foo ndk_header "android/pack/age/Foo.h";
android/pack/age/Foo.h
এ এনডিকে বাস্তবায়ন এর মতো দেখাচ্ছে:
#include <android/binder_parcel.h>
class MyCustomParcelable {
public:
binder_status_t writeToParcel(AParcel* _Nonnull parcel) const;
binder_status_t readFromParcel(const AParcel* _Nonnull parcel);
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
অ্যান্ড্রয়েড 15 -এ, এইডলে একটি কাস্টম মরিচা পার্সেলেবল ঘোষণার জন্য, rust_type
ব্যবহার করুন:
package my.pack.age;
@RustOnlyStableParcelable parcelable Foo rust_type "rust_crate::Foo";
rust_crate/src/lib.rs
এ মরিচা বাস্তবায়ন এর মতো দেখাচ্ছে:
use binder::{
binder_impl::{BorrowedParcel, UnstructuredParcelable},
impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
StatusCode,
};
#[derive(Clone, Debug, Eq, PartialEq)]
struct Foo {
pub bar: String,
}
impl UnstructuredParcelable for Foo {
fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
parcel.write(&self.bar)?;
Ok(())
}
fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
let bar = parcel.read()?;
Ok(Self { bar })
}
}
impl_deserialize_for_unstructured_parcelable!(Foo);
impl_serialize_for_unstructured_parcelable!(Foo);
তারপরে আপনি এই পার্সেলেবলকে এইডএল ফাইলগুলিতে টাইপ হিসাবে ব্যবহার করতে পারেন তবে এটি এইডএল দ্বারা উত্পাদিত হবে না। সিপিপি এবং এনডিকে ব্যাকএন্ড কাস্টম পার্সেলেবলগুলির জন্য তাদের union
ব্যবহার করার জন্য <
এবং ==
অপারেটর সরবরাহ করুন।
ডিফল্ট মান
কাঠামোগত পার্সেলেবলগুলি এই ধরণের প্রিমিটিভ, String
ক্ষেত্র এবং অ্যারেগুলির জন্য প্রতি ক্ষেত্রের ডিফল্ট মানগুলি ঘোষণা করতে পারে।
parcelable Foo {
int numField = 42;
String stringField = "string value";
char charValue = 'a';
...
}
জাভা ব্যাকএন্ডে, যখন ডিফল্ট মানগুলি অনুপস্থিত থাকে, ক্ষেত্রগুলি আদিম ধরণের জন্য শূন্য মান এবং অ -প্রিমিটিভ ধরণের জন্য null
হিসাবে শুরু করা হয়।
অন্যান্য ব্যাকেন্ডগুলিতে, ডিফল্ট মানগুলি সংজ্ঞায়িত না করা হলে ক্ষেত্রগুলি ডিফল্ট আরম্ভিত মানগুলির সাথে শুরু করা হয়। উদাহরণস্বরূপ, সি ++ ব্যাকএন্ডে, String
ক্ষেত্রগুলি একটি খালি স্ট্রিং হিসাবে শুরু করা হয় এবং List<T>
ক্ষেত্রগুলি খালি vector<T>
হিসাবে শুরু করা হয়। @nullable
ক্ষেত্রগুলি নাল-মান ক্ষেত্র হিসাবে আরম্ভ করা হয়।
ইউনিয়ন
এইডএল ইউনিয়নগুলি ট্যাগ করা হয় এবং তাদের বৈশিষ্ট্যগুলি সমস্ত ব্যাকেন্ডে একই রকম। এগুলি প্রথম ক্ষেত্রের ডিফল্ট মানটিতে নির্মিত এবং তাদের সাথে ইন্টারঅ্যাক্ট করার জন্য তাদের একটি ভাষা-নির্দিষ্ট উপায় রয়েছে:
union Foo {
int intField;
long longField;
String stringField;
MyParcelable parcelableField;
...
}
জাভা উদাহরণ
Foo u = Foo.intField(42); // construct
if (u.getTag() == Foo.intField) { // tag query
// use u.getIntField() // getter
}
u.setSringField("abc"); // setter
সি ++ এবং এনডিকে উদাহরণ
Foo u; // default constructor
assert (u.getTag() == Foo::intField); // tag query
assert (u.get<Foo::intField>() == 0); // getter
u.set<Foo::stringField>("abc"); // setter
assert (u == Foo::make<Foo::stringField>("abc")); // make<tag>(value)
মরিচা উদাহরণ
মরিচাগুলিতে, ইউনিয়নগুলি এনাম হিসাবে প্রয়োগ করা হয় এবং সুস্পষ্ট গেটার এবং সেটার নেই।
let mut u = Foo::Default(); // default constructor
match u { // tag match + get
Foo::IntField(x) => assert!(x == 0);
Foo::LongField(x) => panic!("Default constructed to first field");
Foo::StringField(x) => panic!("Default constructed to first field");
Foo::ParcelableField(x) => panic!("Default constructed to first field");
...
}
u = Foo::StringField("abc".to_string()); // set
ত্রুটি হ্যান্ডলিং
অ্যান্ড্রয়েড ওএস ত্রুটিগুলি প্রতিবেদন করার সময় পরিষেবাগুলি ব্যবহারের জন্য অন্তর্নির্মিত ত্রুটির প্রকারগুলি সরবরাহ করে। এগুলি বাইন্ডার দ্বারা ব্যবহৃত হয় এবং কোনও বাইন্ডার ইন্টারফেস বাস্তবায়নের যে কোনও পরিষেবা দ্বারা ব্যবহার করা যেতে পারে। তাদের ব্যবহার এইডএল সংজ্ঞায় ভালভাবে নথিভুক্ত করা হয়েছে এবং তাদের কোনও ব্যবহারকারী-সংজ্ঞায়িত স্থিতি বা রিটার্ন ধরণের প্রয়োজন হয় না।
ত্রুটি সহ আউটপুট পরামিতি
যখন কোনও এইডএল ফাংশন কোনও ত্রুটির প্রতিবেদন করে, তখন ফাংশনটি আউটপুট পরামিতিগুলি আরম্ভ বা সংশোধন করতে পারে না। বিশেষত, লেনদেনের প্রক্রিয়া চলাকালীন ঘটনার বিপরীতে, ত্রুটিটি অবিচ্ছিন্ন সময় ত্রুটিটি ঘটে থাকলে আউটপুট প্যারামিটারগুলি সংশোধন করা যেতে পারে। সাধারণভাবে, যখন কোনও এইডএলএল ফাংশন থেকে ত্রুটি পাওয়া যায়, তখন সমস্ত inout
এবং out
প্যারামিটারগুলির পাশাপাশি রিটার্ন মান (যা কিছু ব্যাকএন্ডগুলিতে out
প্যারামিটারের মতো কাজ করে) অনির্দিষ্টকালের অবস্থায় বিবেচনা করা উচিত।
কোন ত্রুটি মান ব্যবহার করতে
অন্তর্নির্মিত ত্রুটি মানগুলির অনেকগুলি যে কোনও এইডএল ইন্টারফেসে ব্যবহার করা যেতে পারে তবে কিছু কিছু একটি বিশেষ উপায়ে চিকিত্সা করা হয়। উদাহরণস্বরূপ, EX_UNSUPPORTED_OPERATION
এবং EX_ILLEGAL_ARGUMENT
যখন তারা ত্রুটির শর্তটি বর্ণনা করে তখন তারা ব্যবহার করা ঠিক আছে, তবে EX_TRANSACTION_FAILED
অবশ্যই ব্যবহার করা উচিত নয় কারণ এটি অন্তর্নিহিত অবকাঠামো দ্বারা বিশেষভাবে চিকিত্সা করা হয়। এই অন্তর্নির্মিত মানগুলি সম্পর্কে আরও তথ্যের জন্য ব্যাকএন্ড নির্দিষ্ট সংজ্ঞাগুলি পরীক্ষা করুন।
যদি এইডএল ইন্টারফেসের জন্য অতিরিক্ত ত্রুটি মানগুলির প্রয়োজন হয় যা অন্তর্নির্মিত ত্রুটির প্রকারের দ্বারা আচ্ছাদিত নয়, তারা বিশেষ পরিষেবা-নির্দিষ্ট অন্তর্নির্মিত ত্রুটি ব্যবহার করতে পারে যা ব্যবহারকারী দ্বারা সংজ্ঞায়িত একটি পরিষেবা-নির্দিষ্ট ত্রুটি মান অন্তর্ভুক্ত করার অনুমতি দেয়। এই পরিষেবা -নির্দিষ্ট ত্রুটিগুলি সাধারণত এইডএল ইন্টারফেসে একটি const int
বা int
-ব্যাকড enum
হিসাবে সংজ্ঞায়িত করা হয় এবং বাইন্ডার দ্বারা পার্স করা হয় না।
জাভাতে, ত্রুটিগুলি ব্যতিক্রমগুলিতে মানচিত্র যেমন android.os.RemoteException
। পরিষেবা-নির্দিষ্ট ব্যতিক্রমগুলির জন্য, জাভা ব্যবহারকারী-সংজ্ঞায়িত ত্রুটির সাথে android.os.ServiceSpecificException
ব্যবহার করে।
অ্যান্ড্রয়েডে নেটিভ কোড ব্যতিক্রম ব্যবহার করে না। সিপিপি ব্যাকএন্ড android::binder::Status
ব্যবহার করে। এনডিকে ব্যাকএন্ড ndk::ScopedAStatus
ব্যবহার করে। এইডএল দ্বারা উত্পাদিত প্রতিটি পদ্ধতি পদ্ধতির স্থিতি উপস্থাপন করে এর মধ্যে একটিকে ফেরত দেয়। মরিচা ব্যাকএন্ড এনডিকে হিসাবে একই ব্যতিক্রম কোডের মানগুলি ব্যবহার করে তবে তাদের ব্যবহারকারীর কাছে পৌঁছে দেওয়ার আগে তাদের দেশীয় মরিচা ত্রুটি ( StatusCode
, ExceptionCode
) এ রূপান্তর করে। পরিষেবা-নির্দিষ্ট ত্রুটির জন্য, প্রত্যাবর্তিত Status
বা ScopedAStatus
ব্যবহারকারী-সংজ্ঞায়িত ত্রুটির সাথে EX_SERVICE_SPECIFIC
ব্যবহার করে।
অন্তর্নির্মিত ত্রুটির প্রকারগুলি নিম্নলিখিত ফাইলগুলিতে পাওয়া যাবে:
ব্যাকএন্ড | সংজ্ঞা |
---|---|
জাভা | android/os/Parcel.java |
সিপিপি | binder/Status.h |
এনডিকে | android/binder_status.h |
মরিচা | android/binder_status.h |
বিভিন্ন ব্যাকেন্ড ব্যবহার করুন
এই নির্দেশাবলী অ্যান্ড্রয়েড প্ল্যাটফর্ম কোডের জন্য নির্দিষ্ট। এই উদাহরণগুলি একটি সংজ্ঞায়িত প্রকার, my.package.IFoo
ব্যবহার করে। মরিচা ব্যাকএন্ড কীভাবে ব্যবহার করবেন সে সম্পর্কে নির্দেশাবলীর জন্য, অ্যান্ড্রয়েড মরিচা নিদর্শনগুলিতে মরিচা এইডেলের উদাহরণ দেখুন।
আমদানি প্রকার
সংজ্ঞায়িত প্রকারটি কোনও ইন্টারফেস, পার্সেলেবল বা ইউনিয়ন হোক না কেন, আপনি এটি জাভাতে আমদানি করতে পারেন:
import my.package.IFoo;
বা সিপিপি ব্যাকএন্ডে:
#include <my/package/IFoo.h>
বা এনডিকে ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <aidl/my/package/IFoo.h>
বা মরিচা ব্যাকএন্ডে:
use my_package::aidl::my::package::IFoo;
যদিও আপনি জাভাতে একটি নেস্টেড টাইপ আমদানি করতে পারেন, সিপিপি এবং এনডিকে ব্যাকেন্ডগুলিতে আপনাকে অবশ্যই এর মূল প্রকারের জন্য শিরোনামটি অন্তর্ভুক্ত করতে হবে। উদাহরণস্বরূপ, my/package/IFoo.aidl
( IFoo
ফাইলের মূল ধরণ) এ সংজ্ঞায়িত নেস্টেড টাইপ Bar
আমদানি করার সময় আপনাকে অবশ্যই সিপিপি ব্যাকএন্ডের জন্য <my/package/IFoo.h>
অন্তর্ভুক্ত করতে হবে (বা এনডিকে ব্যাকএন্ডের জন্য বা <aidl/my/package/IFoo.h>
)।
একটি ইন্টারফেস প্রয়োগ করুন
একটি ইন্টারফেস বাস্তবায়নের জন্য আপনাকে অবশ্যই নেটিভ স্টাব শ্রেণি থেকে উত্তরাধিকারী হতে হবে। কোনও ইন্টারফেসের বাস্তবায়নকে প্রায়শই পরিষেবা বলা হয় যখন এটি পরিষেবা পরিচালক বা android.app.ActivityManager
সাথে নিবন্ধিত হয় এবং যখন কোনও পরিষেবার ক্লায়েন্ট দ্বারা নিবন্ধিত হয় তখন একটি কলব্যাক বলা হয়। যাইহোক, সঠিক ব্যবহারের উপর নির্ভর করে ইন্টারফেস বাস্তবায়নগুলি বর্ণনা করতে বিভিন্ন নাম ব্যবহার করা হয়। স্টাব ক্লাসটি বাইন্ডার ড্রাইভারের কাছ থেকে কমান্ডগুলি পড়ে এবং আপনি যে পদ্ধতিগুলি প্রয়োগ করেন তা কার্যকর করে। কল্পনা করুন যে আপনার কাছে এর মতো একটি এইডএল ফাইল রয়েছে:
package my.package;
interface IFoo {
int doFoo();
}
জাভাতে, আপনাকে অবশ্যই উত্পন্ন Stub
ক্লাস থেকে প্রসারিত করতে হবে:
import my.package.IFoo;
public class MyFoo extends IFoo.Stub {
@Override
int doFoo() { ... }
}
সিপিপি ব্যাকএন্ডে:
#include <my/package/BnFoo.h>
class MyFoo : public my::package::BnFoo {
android::binder::Status doFoo(int32_t* out) override;
}
এনডিকে ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <aidl/my/package/BnFoo.h>
class MyFoo : public aidl::my::package::BnFoo {
ndk::ScopedAStatus doFoo(int32_t* out) override;
}
মরিচা ব্যাকএন্ডে:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFoo};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
impl IFoo for MyFoo {
fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
বা অ্যাসিঙ্ক মরিচা সহ:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFooAsyncServer};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
#[async_trait]
impl IFooAsyncServer for MyFoo {
async fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
নিবন্ধন করুন এবং পরিষেবা পান
প্ল্যাটফর্ম অ্যান্ড্রয়েডের পরিষেবাগুলি সাধারণত servicemanager
প্রক্রিয়াটিতে নিবন্ধিত হয়। নিম্নলিখিত এপিআই ছাড়াও, কিছু এপিআই পরিষেবাটি পরীক্ষা করে (যার অর্থ পরিষেবাটি উপলব্ধ না হলে তারা তাত্ক্ষণিকভাবে ফিরে আসে)। সঠিক বিশদের জন্য সংশ্লিষ্ট servicemanager
ইন্টারফেসটি পরীক্ষা করুন। প্ল্যাটফর্ম অ্যান্ড্রয়েডের বিরুদ্ধে সংকলন করার সময় আপনি এই ক্রিয়াকলাপগুলি সম্পাদন করতে পারেন।
জাভাতে:
import android.os.ServiceManager;
// registering
ServiceManager.addService("service-name", myService);
// return if service is started now
myService = IFoo.Stub.asInterface(ServiceManager.checkService("service-name"));
// waiting until service comes up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));
সিপিপি ব্যাকএন্ডে:
#include <binder/IServiceManager.h>
// registering
defaultServiceManager()->addService(String16("service-name"), myService);
// return if service is started now
status_t err = checkService<IFoo>(String16("service-name"), &myService);
// waiting until service comes up (new in Android 11)
myService = waitForService<IFoo>(String16("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = waitForDeclaredService<IFoo>(String16("service-name"));
এনডিকে ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <android/binder_manager.h>
// registering
binder_exception_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
// return if service is started now
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_checkService("service-name")));
// is a service declared in the VINTF manifest
// VINTF services have the type in the interface instance name.
bool isDeclared = AServiceManager_isDeclared("android.hardware.light.ILights/default");
// wait until a service is available (if isDeclared or you know it's available)
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService("service-name")));
মরিচা ব্যাকএন্ডে:
use myfoo::MyFoo;
use binder;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_binder(
my_service,
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Does not return - spawn or perform any work you mean to do before this call.
binder::ProcessState::join_thread_pool()
}
অ্যাসিঙ্ক মরিচা ব্যাকএন্ডে, একক থ্রেডযুক্ত রানটাইম সহ:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "current_thread")]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleeps forever, but does not join the binder threadpool.
// Spawned tasks run on this thread.
std::future::pending().await
}
অন্যান্য বিকল্পগুলির একটি গুরুত্বপূর্ণ পার্থক্য হ'ল অ্যাসিঙ্ক মরিচা এবং একক থ্রেডেড রানটাইম ব্যবহার করার সময় আপনি join_thread_pool
কল করবেন না । এটি কারণ আপনাকে টোকিওকে এমন একটি থ্রেড দিতে হবে যেখানে এটি তৈরি করা কাজগুলি কার্যকর করতে পারে। নিম্নলিখিত উদাহরণে, মূল থ্রেডটি সেই উদ্দেশ্যে কাজ করে। tokio::spawn
মূল থ্রেডে কার্যকর করে এমন কোনও কাজ তৈরি করা হয়েছে।
অ্যাসিঙ্ক মরিচা ব্যাকএন্ডে, একটি মাল্টিথ্রেডড রানটাইম সহ:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleep forever.
tokio::task::block_in_place(|| {
binder::ProcessState::join_thread_pool();
});
}
মাল্টিথ্রেডেড টোকিও রানটাইম সহ, স্প্যানড টাস্কগুলি মূল থ্রেডে কার্যকর হয় না। অতএব, মূল থ্রেডে join_thread_pool
কল করা আরও বেশি অর্থবোধ করে যাতে মূল থ্রেডটি নিষ্ক্রিয় না হয়। অ্যাসিঙ্ক প্রসঙ্গটি ছাড়তে আপনাকে অবশ্যই কলটি block_in_place
আবৃত করতে হবে।
মৃত্যুর লিঙ্ক
আপনি যখন কোনও বাইন্ডার হোস্টিং পরিষেবা মারা যায় তার জন্য একটি বিজ্ঞপ্তি পেতে অনুরোধ করতে পারেন। এটি কলব্যাক প্রক্সিগুলি ফাঁস এড়াতে বা ত্রুটি পুনরুদ্ধারে সহায়তা করতে সহায়তা করতে পারে। বাইন্ডার প্রক্সি অবজেক্টগুলিতে এই কলগুলি তৈরি করুন।
- জাভাতে,
android.os.IBinder::linkToDeath
ব্যবহার করুন। - সিপিপি ব্যাকএন্ডে,
android::IBinder::linkToDeath
। - এনডিকে ব্যাকএন্ডে,
AIBinder_linkToDeath
ব্যবহার করুন। - মরিচা ব্যাকএন্ডে, একটি
DeathRecipient
অবজেক্ট তৈরি করুন, তারপরেmy_binder.link_to_death(&mut my_death_recipient)
কল করুন। মনে রাখবেন যেDeathRecipient
কলব্যাকের মালিকানাধীন, যতক্ষণ না আপনি বিজ্ঞপ্তিগুলি পেতে চান ততক্ষণ আপনাকে অবশ্যই সেই বস্তুটিকে বাঁচিয়ে রাখতে হবে।
কলারের তথ্য
কার্নেল বাইন্ডার কল পাওয়ার সময়, কলার তথ্য বেশ কয়েকটি এপিআইতে পাওয়া যায়। প্রক্রিয়া আইডি (পিআইডি) বোঝায় যে প্রক্রিয়াটির লিনাক্স প্রক্রিয়া আইডি যা লেনদেন প্রেরণ করছে। ব্যবহারকারী আইডি (ইউআই) লিনাক্স ব্যবহারকারী আইডি বোঝায়। একমুখী কল পাওয়ার সময়, কলিং পিআইডি 0 0 হয় বাইন্ডার লেনদেনের প্রসঙ্গের বাইরে, এই ফাংশনগুলি বর্তমান প্রক্রিয়াটির পিআইডি এবং ইউআইডি ফেরত দেয়।
জাভা ব্যাকএন্ডে:
... = Binder.getCallingPid();
... = Binder.getCallingUid();
সিপিপি ব্যাকএন্ডে:
... = IPCThreadState::self()->getCallingPid();
... = IPCThreadState::self()->getCallingUid();
এনডিকে ব্যাকএন্ডে:
... = AIBinder_getCallingPid();
... = AIBinder_getCallingUid();
মরিচা ব্যাকএন্ডে, ইন্টারফেসটি প্রয়োগ করার সময়, নিম্নলিখিতগুলি নির্দিষ্ট করুন (পরিবর্তে এটি ডিফল্ট করার অনুমতি দেওয়ার পরিবর্তে):
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
পরিষেবাগুলির জন্য বাগ রিপোর্ট এবং ডিবাগিং এপিআই
যখন বাগের প্রতিবেদনগুলি চলে (উদাহরণস্বরূপ, adb bugreport
সহ), তারা বিভিন্ন সমস্যা ডিবাগ করতে সহায়তা করার জন্য সিস্টেমের চারপাশে থেকে তথ্য সংগ্রহ করে। এইডএল পরিষেবাগুলির জন্য, বাগ রিপোর্টগুলি পরিষেবা পরিচালকের সাথে নিবন্ধিত সমস্ত পরিষেবাগুলিতে বাইনারি dumpsys
ব্যবহার করে তাদের তথ্য বাগ রিপোর্টে ফেলে দেয়। আপনি dumpsys SERVICE [ARGS]
সহ কোনও পরিষেবা থেকে তথ্য পেতে কমান্ড লাইনে dumpsys
ব্যবহার করতে পারেন। সি ++ এবং জাভা ব্যাকএন্ডগুলিতে, আপনি addService
অতিরিক্ত আর্গুমেন্ট ব্যবহার করে পরিষেবাগুলি যে ক্রমটি ফেলে দেওয়া হয় তা নিয়ন্ত্রণ করতে পারেন। ডিবাগিংয়ের সময় কোনও পরিষেবার পিআইডি পেতে আপনি dumpsys --pid SERVICE
ব্যবহার করতে পারেন।
আপনার পরিষেবাতে কাস্টম আউটপুট যুক্ত করতে, আপনার সার্ভার অবজেক্টে dump
পদ্ধতিটি ওভাররাইড করুন যেমন আপনি কোনও এইডএল ফাইলটিতে সংজ্ঞায়িত অন্য কোনও আইপিসি পদ্ধতি বাস্তবায়ন করছেন। এটি করার সময়, অ্যাপের অনুমতিটি android.permission.DUMP
ডাম্পিংকে সীমাবদ্ধ করুন বা নির্দিষ্ট ইউআইডিগুলিতে ডাম্পিংকে সীমাবদ্ধ করুন।
জাভা ব্যাকএন্ডে:
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {...}
সিপিপি ব্যাকএন্ডে:
status_t dump(int, const android::android::Vector<android::String16>&) override;
এনডিকে ব্যাকএন্ডে:
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
মরিচা ব্যাকএন্ডে, ইন্টারফেসটি প্রয়োগ করার সময়, নিম্নলিখিতগুলি নির্দিষ্ট করুন (পরিবর্তে এটি ডিফল্ট করার অনুমতি দেওয়ার পরিবর্তে):
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
দুর্বল পয়েন্টার ব্যবহার করুন
আপনি একটি বাইন্ডার অবজেক্টের দুর্বল রেফারেন্স রাখতে পারেন।
যদিও জাভা WeakReference
সমর্থন করে, এটি দেশীয় স্তরে দুর্বল বাইন্ডার রেফারেন্সগুলিকে সমর্থন করে না।
সিপিপি ব্যাকএন্ডে, দুর্বল প্রকারটি wp<IFoo>
।
এনডিকে ব্যাকএন্ডে, ScopedAIBinder_Weak
ব্যবহার করুন:
#include <android/binder_auto_utils.h>
AIBinder* binder = ...;
ScopedAIBinder_Weak myWeakReference = ScopedAIBinder_Weak(AIBinder_Weak_new(binder));
মরিচা ব্যাকএন্ডে, WpIBinder
বা Weak<IFoo>
:
let weak_interface = myIface.downgrade();
let weak_binder = myIface.as_binder().downgrade();
গতিশীলভাবে ইন্টারফেস বর্ণনাকারী পান
ইন্টারফেস বর্ণনাকারী একটি ইন্টারফেসের ধরণ চিহ্নিত করে। ডিবাগ করার সময় বা যখন আপনার অজানা বাইন্ডার থাকে তখন এটি কার্যকর।
জাভাতে, আপনি কোড সহ ইন্টারফেস বর্ণনাকারী পেতে পারেন:
service = /* get ahold of service object */
... = service.asBinder().getInterfaceDescriptor();
সিপিপি ব্যাকএন্ডে:
service = /* get ahold of service object */
... = IInterface::asBinder(service)->getInterfaceDescriptor();
এনডিকে এবং মরিচা ব্যাকেন্ডস এই ক্ষমতাটিকে সমর্থন করে না।
স্ট্যাটিক্যালি ইন্টারফেস বর্ণনাকারী পান
কখনও কখনও (যেমন @VintfStability
পরিষেবাগুলি নিবন্ধভুক্ত করার সময়), আপনাকে ইন্টারফেস বর্ণনাকারী কী তা জানতে হবে। জাভাতে, আপনি কোড যুক্ত করে বর্ণনাকারী পেতে পারেন:
import my.package.IFoo;
... IFoo.DESCRIPTOR
সিপিপি ব্যাকএন্ডে:
#include <my/package/BnFoo.h>
... my::package::BnFoo::descriptor
এনডিকে ব্যাকএন্ডে (অতিরিক্ত aidl
নেমস্পেসটি লক্ষ্য করুন):
#include <aidl/my/package/BnFoo.h>
... aidl::my::package::BnFoo::descriptor
মরিচা ব্যাকএন্ডে:
aidl::my::package::BnFoo::get_descriptor()
এনাম রেঞ্জ
নেটিভ ব্যাকেন্ডগুলিতে, আপনি কোনও এনাম গ্রহণ করতে পারে এমন সম্ভাব্য মানগুলির উপর পুনরাবৃত্তি করতে পারেন। কোড আকারের বিবেচনার কারণে, এটি জাভাতে সমর্থিত নয়।
এইডএলে সংজ্ঞায়িত একটি এনাম MyEnum
জন্য, নিম্নলিখিত হিসাবে পুনরাবৃত্তি সরবরাহ করা হয়।
সিপিপি ব্যাকএন্ডে:
::android::enum_range<MyEnum>()
এনডিকে ব্যাকএন্ডে:
::ndk::enum_range<MyEnum>()
মরিচা ব্যাকএন্ডে:
MyEnum::enum_values()
থ্রেড ব্যবস্থাপনা
একটি প্রক্রিয়াতে libbinder
প্রতিটি উদাহরণ একটি থ্রেডপুল বজায় রাখে। বেশিরভাগ ব্যবহারের ক্ষেত্রে, এটি হুবহু একটি থ্রেডপুল হওয়া উচিত, সমস্ত ব্যাকেন্ড জুড়ে ভাগ করা। একমাত্র ব্যতিক্রম যদি বিক্রেতার কোড /dev/vndbinder
সাথে কথা বলতে libbinder
অন্য একটি অনুলিপি লোড করে। এটি একটি পৃথক বাইন্ডার নোডে রয়েছে, সুতরাং থ্রেডপুলটি ভাগ করা হয়নি।
জাভা ব্যাকএন্ডের জন্য, থ্রেডপুলটি কেবল আকারে বাড়তে পারে (কারণ এটি ইতিমধ্যে শুরু হয়েছে):
BinderInternal.setMaxThreads(<new larger value>);
সিপিপি ব্যাকএন্ডের জন্য, নিম্নলিখিত অপারেশনগুলি উপলব্ধ:
// set max threadpool count (default is 15)
status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(numThreads);
// create threadpool
ProcessState::self()->startThreadPool();
// add current thread to threadpool (adds thread to max thread count)
IPCThreadState::self()->joinThreadPool();
একইভাবে, এনডিকে ব্যাকএন্ডে:
bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
ABinderProcess_startThreadPool();
ABinderProcess_joinThreadPool();
মরিচা ব্যাকএন্ডে:
binder::ProcessState::start_thread_pool();
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
binder::ProcessState::join_thread_pool();
অ্যাসিঙ্ক মরিচা ব্যাকএন্ডের সাহায্যে আপনার দুটি থ্রেডপুল দরকার: বাইন্ডার এবং টোকিও। এর অর্থ হ'ল অ্যাসিঙ্ক মরিচা ব্যবহার করা অ্যাপ্লিকেশনগুলির বিশেষ বিবেচনার প্রয়োজন, বিশেষত যখন এটি join_thread_pool
ব্যবহারের ক্ষেত্রে আসে। এ সম্পর্কে আরও তথ্যের জন্য নিবন্ধকরণ পরিষেবাদি বিভাগটি দেখুন।
সংরক্ষিত নাম
সি ++, জাভা এবং মরিচা কিছু নাম কীওয়ার্ড হিসাবে বা ভাষা-নির্দিষ্ট ব্যবহারের জন্য সংরক্ষণ করে। যদিও এইডএলটি ভাষার নিয়মের উপর ভিত্তি করে বিধিনিষেধগুলি প্রয়োগ করে না, তবে ক্ষেত্র বা টাইপ নামগুলি ব্যবহার করে যা সংরক্ষিত নামের সাথে মেলে তার ফলে সি ++ বা জাভাগুলির জন্য সংকলন ব্যর্থতা হতে পারে। মরিচা জন্য, ক্ষেত্র বা প্রকারটি কাঁচা আইডেন্টিফায়ার সিনট্যাক্স ব্যবহার করে নামকরণ করা হয়, r#
উপসর্গ ব্যবহার করে অ্যাক্সেসযোগ্য।
আমরা আপনার এইডএল সংজ্ঞাগুলিতে সংরক্ষিত নামগুলি ব্যবহার করা এড়ানোর পরামর্শ দিই যেখানে সম্ভব যেখানে আনারগোনমিক বাইন্ডিং বা সরাসরি সংকলন ব্যর্থতা এড়াতে সম্ভব।
আপনার যদি ইতিমধ্যে আপনার এইডএল সংজ্ঞাগুলিতে নাম সংরক্ষণ করে থাকে তবে প্রোটোকল সামঞ্জস্যপূর্ণ থাকাকালীন আপনি নিরাপদে ক্ষেত্রগুলির নাম পরিবর্তন করতে পারেন। বিল্ডিং চালিয়ে যেতে আপনার কোড আপডেট করতে হবে, তবে ইতিমধ্যে নির্মিত যে কোনও প্রোগ্রাম আন্তঃসংযোগ অব্যাহত রয়েছে।
নাম এড়াতে:
,একটি এইডএল ব্যাকএন্ড স্টাব কোড জেনারেশনের জন্য একটি লক্ষ্য। সর্বদা একটি নির্দিষ্ট রানটাইম সহ একটি নির্দিষ্ট ভাষায় সহায়তা ফাইলগুলি ব্যবহার করুন। প্রসঙ্গে নির্ভর করে আপনার বিভিন্ন এইডেল ব্যাকেন্ড ব্যবহার করা উচিত।
নিম্নলিখিত সারণীতে, এপিআই পৃষ্ঠের স্থায়িত্ব এই এপিআই পৃষ্ঠের বিপরীতে কোড সংকলন করার ক্ষমতা বোঝায় যাতে কোডটি system.img
থেকে স্বাধীনভাবে বিতরণ করা যায়। আইএমজি libbinder.so
বাইনারি।
এইডেলের নিম্নলিখিত ব্যাকেন্ড রয়েছে:
ব্যাকএন্ড | ভাষা | এপিআই পৃষ্ঠ | সিস্টেম তৈরি করুন |
---|---|---|---|
জাভা | জাভা | এসডিকে বা SystemApi (স্থিতিশীল*) | সব |
এনডিকে | সি++ | libbinder_ndk (স্থিতিশীল*) | aidl_interface |
সিপিপি | সি++ | libbinder (অস্থির) | সব |
মরিচা | মরিচা | libbinder_rs (স্থিতিশীল*) | aidl_interface |
- এই এপিআই পৃষ্ঠগুলি স্থিতিশীল, তবে পরিষেবা পরিচালনার জন্য অনেকগুলি এপিআই যেমন অভ্যন্তরীণ প্ল্যাটফর্ম ব্যবহারের জন্য সংরক্ষিত এবং অ্যাপ্লিকেশনগুলিতে উপলভ্য নয়। For more information on how to use AIDL in apps, see Android Interface Definition Language (AIDL) .
- The Rust backend was introduced in Android 12; the NDK backend has been available as of Android 10.
- The Rust crate is built on top of
libbinder_ndk
, which lets it be stable and portable. APEXes use the binder crate in the standard way on the system side. The Rust portion is bundled into an APEX and shipped inside it. This portion depends on thelibbinder_ndk.so
on the system partition.
সিস্টেম তৈরি করুন
Depending on the backend, there are two ways to compile AIDL into stub code. For more details on the build systems, see Soong Modules Reference .
Core build system
In any cc_
or java_
Android.bp module
(or in their Android.mk
equivalents), you can specify AIDL ( .aidl
) files as source files. In this case, the Java or CPP backends of AIDL are used (not the NDK backend), and the classes to use the corresponding AIDL files are added to the module automatically. You can specify options such as local_include_dirs
(which tells the build system the root path to AIDL files in that module) in these modules under an aidl:
group.
The Rust backend is only for use with Rust. rust_
modules are handled differently in that AIDL files aren't specified as source files. Instead, the aidl_interface
module produces a rustlib
called aidl_interface_name -rust
, which can be linked against. For details, see the Rust AIDL example .
aidl_interface
Types used with the aidl_interface
build system must be structured. In order to be structured, parcelables must contain fields directly and not be declarations of types defined directly in target languages. For how structured AIDL fits in with stable AIDL, see Structured versus stable AIDL .
প্রকারভেদ
Consider the aidl
compiler as a reference implementation for types. When you create an interface, invoke aidl --lang=<backend> ...
to see the resulting interface file. When you use the aidl_interface
module, you can view the output in out/soong/.intermediates/ <path to module> /
.
Java or AIDL type | C++ type | NDK type | Rust type |
---|---|---|---|
boolean | bool | bool | bool |
byte 8 | int8_t | int8_t | i8 |
char | char16_t | char16_t | u16 |
int | int32_t | int32_t | i32 |
long | int64_t | int64_t | i64 |
float | float | float | f32 |
double | double | double | f64 |
String | android::String16 | std::string | In: &str Out: String |
android.os.Parcelable | android::Parcelable | N/A | N/A |
IBinder | android::IBinder | ndk::SpAIBinder | binder::SpIBinder |
T[] | std::vector<T> | std::vector<T> | In: &[T] Out: Vec<T> |
byte[] | std::vector | std::vector 1 | In: &[u8] Out: Vec<u8> |
List<T> | std::vector<T> 2 | std::vector<T> 3 | In: In: &[T] 4Out: Vec<T> |
FileDescriptor | android::base::unique_fd | N/A | N/A |
ParcelFileDescriptor | android::os::ParcelFileDescriptor | ndk::ScopedFileDescriptor | binder::parcel::ParcelFileDescriptor |
Interface type ( T ) | android::sp<T> | std::shared_ptr<T> 7 | binder::Strong |
Parcelable type ( T ) | T | T | T |
Union type ( T ) 5 | T | T | T |
T[N] 6 | std::array<T, N> | std::array<T, N> | [T; N] |
1. In Android 12 or higher, byte arrays use uint8_t
instead of int8_t
for compatibility reasons.
2. The C++ backend supports List<T>
where T
is one of String
, IBinder
, ParcelFileDescriptor
or parcelable. In Android 13 or higher, T
can be any nonprimitive type (including interface types) except arrays. AOSP recommends using array types like T[]
, because they work in all backends.
3. The NDK backend supports List<T>
where T
is one of String
, ParcelFileDescriptor
or parcelable. In Android 13 or higher, T
can be any nonprimitive type except arrays.
4. Types are passed differently for Rust code depending on whether they are input (an argument), or an output (a returned value).
5. Union types are supported in Android 12 and higher.
6. In Android 13 or higher, fixed-size arrays are supported. Fixed-size arrays can have multiple dimensions (for example, int[3][4]
). In the Java backend, fixed-size arrays are represented as array types.
7. To instantiate a binder SharedRefBase
object, use SharedRefBase::make\<My\>(... args ...)
. This function creates a std::shared_ptr\<T\>
object, which is also managed internally, in case the binder is owned by another process. Creating the object other ways causes double ownership.
8. See also Java or AIDL type byte[]
.
Directionality (in, out, and inout)
When specifying the types of the arguments to functions, you can specify them as in
, out
, or inout
. This controls the direction that information is passed for an IPC call.
The
in
argument specifier indicates data is passed from the caller to the callee. Thein
specifier is the default direction, but if data types can also beout
, then you must specify the direction.The
out
argument specifier means that data is passed from the callee to the caller.The
inout
argument specifier is the combination of both of these. However, we recommend avoiding using the argument specifierinout
. If you useinout
with a versioned interface and an older callee, the additional fields that are present only in the caller get reset to their default values. With respect to Rust, a normalinout
type receives&mut T
, and a listinout
type receives&mut Vec<T>
.
interface IRepeatExamples {
MyParcelable RepeatParcelable(MyParcelable token); // implicitly 'in'
MyParcelable RepeatParcelableWithIn(in MyParcelable token);
void RepeatParcelableWithInAndOut(in MyParcelable param, out MyParcelable result);
void RepeatParcelableWithInOut(inout MyParcelable param);
}
UTF-8 and UTF-16
With the CPP backend, you can choose whether strings are UTF-8 or UTF-16. Declare strings as @utf8InCpp String
in AIDL to automatically convert them to UTF-8. The NDK and Rust backends always use UTF-8 strings. For more information about the utf8InCpp
annotation, see utf8InCpp .
শূন্যতা
You can annotate types that can be null with @nullable
. For more information about the nullable
annotation, see nullable .
Custom parcelables
A custom parcelable is a parcelable that's implemented manually in a target backend. Use custom parcelables only when you're trying to add support to other languages for an existing custom parcelable which can't be changed.
Here's an example of an AIDL parcelable declaration:
package my.pack.age;
parcelable Foo;
By default, this declares a Java parcelable where my.pack.age.Foo
is a Java class implementing the Parcelable
interface.
For a declaration of a custom CPP backend parcelable in AIDL, use cpp_header
:
package my.pack.age;
parcelable Foo cpp_header "my/pack/age/Foo.h";
The C++ implementation in my/pack/age/Foo.h
looks like this:
#include <binder/Parcelable.h>
class MyCustomParcelable : public android::Parcelable {
public:
status_t writeToParcel(Parcel* parcel) const override;
status_t readFromParcel(const Parcel* parcel) override;
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
For a declaration of a custom NDK parcelable in AIDL, use ndk_header
:
package my.pack.age;
parcelable Foo ndk_header "android/pack/age/Foo.h";
The NDK implementation in android/pack/age/Foo.h
looks like this:
#include <android/binder_parcel.h>
class MyCustomParcelable {
public:
binder_status_t writeToParcel(AParcel* _Nonnull parcel) const;
binder_status_t readFromParcel(const AParcel* _Nonnull parcel);
std::string toString() const;
friend bool operator==(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
friend bool operator!=(const MyCustomParcelable& lhs, const MyCustomParcelable& rhs);
};
In Android 15, for declaration of a custom Rust parcelable in AIDL, use rust_type
:
package my.pack.age;
@RustOnlyStableParcelable parcelable Foo rust_type "rust_crate::Foo";
The Rust implementation in rust_crate/src/lib.rs
looks like this:
use binder::{
binder_impl::{BorrowedParcel, UnstructuredParcelable},
impl_deserialize_for_unstructured_parcelable, impl_serialize_for_unstructured_parcelable,
StatusCode,
};
#[derive(Clone, Debug, Eq, PartialEq)]
struct Foo {
pub bar: String,
}
impl UnstructuredParcelable for Foo {
fn write_to_parcel(&self, parcel: &mut BorrowedParcel) -> Result<(), StatusCode> {
parcel.write(&self.bar)?;
Ok(())
}
fn from_parcel(parcel: &BorrowedParcel) -> Result<Self, StatusCode> {
let bar = parcel.read()?;
Ok(Self { bar })
}
}
impl_deserialize_for_unstructured_parcelable!(Foo);
impl_serialize_for_unstructured_parcelable!(Foo);
Then you can use this parcelable as a type in AIDL files, but it won't be generated by AIDL. Provide <
and ==
operators for CPP and NDK backend custom parcelables to use them in union
.
ডিফল্ট মান
Structured parcelables can declare per-field default values for primitives, String
fields, and arrays of these types.
parcelable Foo {
int numField = 42;
String stringField = "string value";
char charValue = 'a';
...
}
In the Java backend, when default values are missing, fields are initialized as zero values for primitive types and null
for nonprimitive types.
In other backends, fields are initialized with default initialized values when default values aren't defined. For example, in the C++ backend, String
fields are initialized as an empty string and List<T>
fields are initialized as an empty vector<T>
. @nullable
fields are initialized as null-value fields.
ইউনিয়ন
AIDL unions are tagged and their features are similar in all backends. They're constructed to the first field's default value and they have a language-specific way to interact with them:
union Foo {
int intField;
long longField;
String stringField;
MyParcelable parcelableField;
...
}
জাভা উদাহরণ
Foo u = Foo.intField(42); // construct
if (u.getTag() == Foo.intField) { // tag query
// use u.getIntField() // getter
}
u.setSringField("abc"); // setter
C++ and NDK example
Foo u; // default constructor
assert (u.getTag() == Foo::intField); // tag query
assert (u.get<Foo::intField>() == 0); // getter
u.set<Foo::stringField>("abc"); // setter
assert (u == Foo::make<Foo::stringField>("abc")); // make<tag>(value)
মরিচা উদাহরণ
In Rust, unions are implemented as enums and don't have explicit getters and setters.
let mut u = Foo::Default(); // default constructor
match u { // tag match + get
Foo::IntField(x) => assert!(x == 0);
Foo::LongField(x) => panic!("Default constructed to first field");
Foo::StringField(x) => panic!("Default constructed to first field");
Foo::ParcelableField(x) => panic!("Default constructed to first field");
...
}
u = Foo::StringField("abc".to_string()); // set
ত্রুটি হ্যান্ডলিং
The Android OS provides built-in error types for services to use when reporting errors. These are used by binders and can be used by any services implementing a binder interface. Their use is well documented in the AIDL definition and they don't require any user-defined status or return type.
Output parameters with errors
When an AIDL function reports an error, the function might not initialize or modify output parameters. Specifically, output parameters might be modified if the error occurs during unparceling, as opposed to happening during the processing of the transaction itself. In general, when getting an error from an AIDL function, all inout
and out
parameters as well as the return value (which acts like an out
parameter in some backends) should be considered to be in an indefinite state.
Which error values to use
Many of the built-in error values can be used in any AIDL interfaces, but some are treated in a special way. For example, EX_UNSUPPORTED_OPERATION
and EX_ILLEGAL_ARGUMENT
are OK to use when they describe the error condition, but EX_TRANSACTION_FAILED
must not be used because it's treated specially by the underlying infrastructure. Check the backend specific definitions for more information on these built-in values.
If the AIDL interface requires additional error values that aren't covered by the built-in error types, they can use the special service-specific built-in error that allows the inclusion of a service-specific error value that's defined by the user. These service-specific errors are typically defined in the AIDL interface as a const int
or int
-backed enum
and aren't parsed by binder.
In Java, errors map to exceptions, such as android.os.RemoteException
. For service-specific exceptions, Java uses android.os.ServiceSpecificException
along with the user-defined error.
Native code in Android doesn't use exceptions. The CPP backend uses android::binder::Status
. The NDK backend uses ndk::ScopedAStatus
. Every method generated by AIDL returns one of these, representing the status of the method. The Rust backend uses the same exception code values as the NDK, but converts them into native Rust errors ( StatusCode
, ExceptionCode
) before delivering them to the user. For service-specific errors, the returned Status
or ScopedAStatus
uses EX_SERVICE_SPECIFIC
along with the user-defined error.
The built-in error types can be found in the following files:
ব্যাকএন্ড | সংজ্ঞা |
---|---|
জাভা | android/os/Parcel.java |
সিপিপি | binder/Status.h |
এনডিকে | android/binder_status.h |
মরিচা | android/binder_status.h |
Use various backends
These instructions are specific to Android platform code. These examples use a defined type, my.package.IFoo
. For instructions on how to use the Rust backend, see the Rust AIDL example in Android Rust patterns .
আমদানি প্রকার
Whether the defined type is an interface, parcelable, or union, you can import it in Java:
import my.package.IFoo;
Or in the CPP backend:
#include <my/package/IFoo.h>
Or in the NDK backend (notice the extra aidl
namespace):
#include <aidl/my/package/IFoo.h>
Or in the Rust backend:
use my_package::aidl::my::package::IFoo;
Although you can import a nested type in Java, in the CPP and NDK backends you must include the header for its root type. For example, when importing a nested type Bar
defined in my/package/IFoo.aidl
( IFoo
is the root type of the file) you must include <my/package/IFoo.h>
for the CPP backend (or <aidl/my/package/IFoo.h>
for the NDK backend).
একটি ইন্টারফেস প্রয়োগ করুন
To implement an interface, you must inherit from the native stub class. An implementation of an interface is often called a service when it's registered with the service manager or android.app.ActivityManager
and called a callback when it's registered by a client of a service. However, a variety of names are used to describe interface implementations depending on the exact usage. The stub class reads commands from the binder driver and executes the methods that you implement. Imagine that you have an AIDL file like this:
package my.package;
interface IFoo {
int doFoo();
}
In Java, you must extend from the generated Stub
class:
import my.package.IFoo;
public class MyFoo extends IFoo.Stub {
@Override
int doFoo() { ... }
}
In the CPP backend:
#include <my/package/BnFoo.h>
class MyFoo : public my::package::BnFoo {
android::binder::Status doFoo(int32_t* out) override;
}
In the NDK backend (notice the extra aidl
namespace):
#include <aidl/my/package/BnFoo.h>
class MyFoo : public aidl::my::package::BnFoo {
ndk::ScopedAStatus doFoo(int32_t* out) override;
}
In the Rust backend:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFoo};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
impl IFoo for MyFoo {
fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
Or with async Rust:
use aidl_interface_name::aidl::my::package::IFoo::{BnFoo, IFooAsyncServer};
use binder;
/// This struct is defined to implement IRemoteService AIDL interface.
pub struct MyFoo;
impl Interface for MyFoo {}
#[async_trait]
impl IFooAsyncServer for MyFoo {
async fn doFoo(&self) -> binder::Result<()> {
...
Ok(())
}
}
Register and get services
Services in platform Android are usually registered with the servicemanager
process. In addition to the following APIs, some APIs check the service (meaning they return immediately if the service isn't available). Check the corresponding servicemanager
interface for exact details. You can perform these operations only when compiling against platform Android.
জাভাতে:
import android.os.ServiceManager;
// registering
ServiceManager.addService("service-name", myService);
// return if service is started now
myService = IFoo.Stub.asInterface(ServiceManager.checkService("service-name"));
// waiting until service comes up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForService("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = IFoo.Stub.asInterface(ServiceManager.waitForDeclaredService("service-name"));
In the CPP backend:
#include <binder/IServiceManager.h>
// registering
defaultServiceManager()->addService(String16("service-name"), myService);
// return if service is started now
status_t err = checkService<IFoo>(String16("service-name"), &myService);
// waiting until service comes up (new in Android 11)
myService = waitForService<IFoo>(String16("service-name"));
// waiting for declared (VINTF) service to come up (new in Android 11)
myService = waitForDeclaredService<IFoo>(String16("service-name"));
In the NDK backend (notice the extra aidl
namespace):
#include <android/binder_manager.h>
// registering
binder_exception_t err = AServiceManager_addService(myService->asBinder().get(), "service-name");
// return if service is started now
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_checkService("service-name")));
// is a service declared in the VINTF manifest
// VINTF services have the type in the interface instance name.
bool isDeclared = AServiceManager_isDeclared("android.hardware.light.ILights/default");
// wait until a service is available (if isDeclared or you know it's available)
myService = IFoo::fromBinder(ndk::SpAIBinder(AServiceManager_waitForService("service-name")));
In the Rust backend:
use myfoo::MyFoo;
use binder;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_binder(
my_service,
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Does not return - spawn or perform any work you mean to do before this call.
binder::ProcessState::join_thread_pool()
}
In the async Rust backend, with a single-threaded runtime:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "current_thread")]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleeps forever, but does not join the binder threadpool.
// Spawned tasks run on this thread.
std::future::pending().await
}
One important difference from the other options is that you don't call join_thread_pool
when using async Rust and a single-threaded runtime. This is because you need to give Tokio a thread where it can execute spawned tasks. In the following example, the main thread serves that purpose. Any tasks spawned using tokio::spawn
execute on the main thread.
In the async Rust backend, with a multithreaded runtime:
use myfoo::MyFoo;
use binder;
use binder_tokio::TokioRuntime;
use aidl_interface_name::aidl::my::package::IFoo::BnFoo;
#[tokio::main(flavor = "multi_thread", worker_threads = 2)]
async fn main() {
binder::ProcessState::start_thread_pool();
// [...]
let my_service = MyFoo;
let my_service_binder = BnFoo::new_async_binder(
my_service,
TokioRuntime(Handle::current()),
BinderFeatures::default(),
);
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
// Sleep forever.
tokio::task::block_in_place(|| {
binder::ProcessState::join_thread_pool();
});
}
With the multithreaded Tokio runtime, spawned tasks don't execute on the main thread. Therefore, it makes more sense to call join_thread_pool
on the main thread so that the main thread isn't idle. You must wrap the call in block_in_place
to leave the async context.
Link to death
You can request to get a notification for when a service hosting a binder dies. This can help to avoid leaking callback proxies or assist in error recovery. Make these calls on binder proxy objects.
- In Java, use
android.os.IBinder::linkToDeath
. - In the CPP backend, use
android::IBinder::linkToDeath
. - In the NDK backend, use
AIBinder_linkToDeath
. - In the Rust backend, create a
DeathRecipient
object, then callmy_binder.link_to_death(&mut my_death_recipient)
. Note that becauseDeathRecipient
owns the callback, you must keep that object alive as long as you want to receive notifications.
কলারের তথ্য
When receiving a kernel binder call, caller information is available in several APIs. The process ID (PID) refers to the Linux process ID of the process that's sending a transaction. The user ID (UI) refers to the Linux user ID. When receiving a one-way call, the calling PID is 0. Outside of a binder transaction context, these functions return the PID and UID of the current process.
In the Java backend:
... = Binder.getCallingPid();
... = Binder.getCallingUid();
In the CPP backend:
... = IPCThreadState::self()->getCallingPid();
... = IPCThreadState::self()->getCallingUid();
In the NDK backend:
... = AIBinder_getCallingPid();
... = AIBinder_getCallingUid();
In the Rust backend, when implementing the interface, specify the following (instead of allowing it to default):
... = ThreadState::get_calling_pid();
... = ThreadState::get_calling_uid();
Bug reports and debugging API for services
When bug reports run (for example, with adb bugreport
), they collect information from all around the system to aid with debugging various issues. For AIDL services, bug reports use the binary dumpsys
on all services registered with the service manager to dump their information into the bug report. You can also use dumpsys
on the command line to get information from a service with dumpsys SERVICE [ARGS]
. In the C++ and Java backends, you can control the order in which services get dumped by using additional arguments to addService
. You can also use dumpsys --pid SERVICE
to get the PID of a service while debugging.
To add custom output to your service, override the dump
method in your server object like you're implementing any other IPC method defined in an AIDL file. When doing this, restrict dumping to the app permission android.permission.DUMP
or restrict dumping to specific UIDs.
In the Java backend:
@Override
protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
@Nullable String[] args) {...}
In the CPP backend:
status_t dump(int, const android::android::Vector<android::String16>&) override;
In the NDK backend:
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
In the Rust backend, when implementing the interface, specify the following (instead of allowing it to default):
fn dump(&self, mut file: &File, args: &[&CStr]) -> binder::Result<()>
Use weak pointers
You can hold a weak reference to a binder object.
While Java supports WeakReference
, it doesn't support weak binder references at the native layer.
In the CPP backend, the weak type is wp<IFoo>
.
In the NDK backend, use ScopedAIBinder_Weak
:
#include <android/binder_auto_utils.h>
AIBinder* binder = ...;
ScopedAIBinder_Weak myWeakReference = ScopedAIBinder_Weak(AIBinder_Weak_new(binder));
In the Rust backend, use WpIBinder
or Weak<IFoo>
:
let weak_interface = myIface.downgrade();
let weak_binder = myIface.as_binder().downgrade();
Dynamically get interface descriptor
The interface descriptor identifies the type of an interface. This is useful when debugging or when you have an unknown binder.
In Java, you can get the interface descriptor with code such as:
service = /* get ahold of service object */
... = service.asBinder().getInterfaceDescriptor();
In the CPP backend:
service = /* get ahold of service object */
... = IInterface::asBinder(service)->getInterfaceDescriptor();
The NDK and Rust backends don't support this capability.
Statically get interface descriptor
Sometimes (such as when registering @VintfStability
services), you need to know what the interface descriptor is statically. In Java, you can get the descriptor by adding code such as:
import my.package.IFoo;
... IFoo.DESCRIPTOR
In the CPP backend:
#include <my/package/BnFoo.h>
... my::package::BnFoo::descriptor
In the NDK backend (notice the extra aidl
namespace):
#include <aidl/my/package/BnFoo.h>
... aidl::my::package::BnFoo::descriptor
In the Rust backend:
aidl::my::package::BnFoo::get_descriptor()
Enum range
In native backends, you can iterate over the possible values an enum can take on. Due to code size considerations, this isn't supported in Java.
For an enum MyEnum
defined in AIDL, iteration is provided as follows.
In the CPP backend:
::android::enum_range<MyEnum>()
In the NDK backend:
::ndk::enum_range<MyEnum>()
In the Rust backend:
MyEnum::enum_values()
থ্রেড ব্যবস্থাপনা
Every instance of libbinder
in a process maintains one threadpool. For most use cases, this should be exactly one threadpool, shared across all backends. The only exception is if vendor code loads another copy of libbinder
to talk to /dev/vndbinder
. This is on a separate binder node, so the threadpool isn't shared.
For the Java backend, the threadpool can only increase in size (because it's already started):
BinderInternal.setMaxThreads(<new larger value>);
For the CPP backend, the following operations are available:
// set max threadpool count (default is 15)
status_t err = ProcessState::self()->setThreadPoolMaxThreadCount(numThreads);
// create threadpool
ProcessState::self()->startThreadPool();
// add current thread to threadpool (adds thread to max thread count)
IPCThreadState::self()->joinThreadPool();
Similarly, in the NDK backend:
bool success = ABinderProcess_setThreadPoolMaxThreadCount(numThreads);
ABinderProcess_startThreadPool();
ABinderProcess_joinThreadPool();
In the Rust backend:
binder::ProcessState::start_thread_pool();
binder::add_service("myservice", my_service_binder).expect("Failed to register service?");
binder::ProcessState::join_thread_pool();
With the async Rust backend, you need two threadpools: binder and Tokio. This means that apps using async Rust need special considerations, especially when it comes to the use of join_thread_pool
. See the section on registering services for more information on this.
সংরক্ষিত নাম
C++, Java, and Rust reserve some names as keywords or for language-specific use. While AIDL doesn't enforce restrictions based on language rules, using field or type names that match a reserved name can result in a compilation failure for C++ or Java. For Rust, the field or type is renamed using the raw identifier syntax, accessible using the r#
prefix.
We recommend avoiding using reserved names in your AIDL definitions where possible to avoid unergonomic bindings or outright compilation failure.
If you already have reserved names in your AIDL definitions, you can safely rename fields while remaining protocol compatible. You might need to update your code to continue building, but any already built programs continue to interoperate.
Names to avoid: