অ্যান্ড্রয়েড ১০-এ স্টেবল অ্যান্ড্রয়েড ইন্টারফেস ডেফিনিশন ল্যাঙ্গুয়েজ (AIDL)-এর সাপোর্ট যুক্ত করা হয়েছে। এটি AIDL ইন্টারফেস দ্বারা প্রদত্ত অ্যাপ্লিকেশন প্রোগ্রাম ইন্টারফেস (API) এবং অ্যাপ্লিকেশন বাইনারি ইন্টারফেস (ABI)-এর হিসাব রাখার একটি নতুন উপায়। স্টেবল AIDL হুবহু AIDL-এর মতোই কাজ করে, তবে এর বিল্ড সিস্টেম ইন্টারফেসের সামঞ্জস্যতা ট্র্যাক করে এবং আপনি কী করতে পারবেন তার উপর কিছু সীমাবদ্ধতা রয়েছে:
- বিল্ড সিস্টেমে
aidl_interfacesব্যবহার করে ইন্টারফেসগুলো সংজ্ঞায়িত করা হয়। - ইন্টারফেসে শুধুমাত্র স্ট্রাকচার্ড ডেটা থাকতে পারে। পছন্দের টাইপগুলোর প্রতিনিধিত্বকারী পার্সেলেবলগুলো তাদের AIDL সংজ্ঞার উপর ভিত্তি করে স্বয়ংক্রিয়ভাবে তৈরি হয় এবং স্বয়ংক্রিয়ভাবে মার্শেল ও আনমার্শেল করা হয়।
- ইন্টারফেসগুলোকে স্থিতিশীল (পূর্ববর্তী সংস্করণের সাথে সামঞ্জস্যপূর্ণ) হিসেবে ঘোষণা করা যেতে পারে। যখন এমনটা করা হয়, তখন AIDL ইন্টারফেসের পাশের একটি ফাইলে সেগুলোর API-কে ট্র্যাক ও ভার্সন করা হয়।
কাঠামোগত বনাম স্থিতিশীল AIDL
স্ট্রাকচার্ড এআইডিএল বলতে সেইসব টাইপকে বোঝায় যা সম্পূর্ণরূপে এআইডিএল-এ সংজ্ঞায়িত। উদাহরণস্বরূপ, একটি পার্সেলএবল ডিক্লারেশন (একটি কাস্টম পার্সেলএবল) স্ট্রাকচার্ড এআইডিএল নয়। যেসব পার্সেলএবলের ফিল্ডগুলো এআইডিএল-এ সংজ্ঞায়িত, সেগুলোকে স্ট্রাকচার্ড পার্সেলএবল বলা হয়।
স্থিতিশীল AIDL-এর জন্য স্ট্রাকচার্ড AIDL প্রয়োজন, যাতে বিল্ড সিস্টেম এবং কম্পাইলার বুঝতে পারে যে পার্সেলেবলগুলিতে করা পরিবর্তনগুলি ব্যাকওয়ার্ড কম্প্যাটিবল কিনা। তবে, সব স্ট্রাকচার্ড ইন্টারফেস স্থিতিশীল নয়। স্থিতিশীল হতে হলে, একটি ইন্টারফেসকে অবশ্যই শুধুমাত্র স্ট্রাকচার্ড টাইপ ব্যবহার করতে হবে এবং এর সাথে নিম্নলিখিত ভার্সনিং বৈশিষ্ট্যগুলিও ব্যবহার করতে হবে। বিপরীতভাবে, যদি কোর বিল্ড সিস্টেম ব্যবহার করে একটি ইন্টারফেস তৈরি করা হয় অথবা যদি unstable:true সেট করা থাকে, তবে সেটি স্থিতিশীল হয় না।
একটি AIDL ইন্টারফেস সংজ্ঞায়িত করুন
aidl_interface এর সংজ্ঞাটি দেখতে এইরকম:
aidl_interface {
name: "my-aidl",
srcs: ["srcs/aidl/**/*.aidl"],
local_include_dir: "srcs/aidl",
imports: ["other-aidl"],
versions_with_info: [
{
version: "1",
imports: ["other-aidl-V1"],
},
{
version: "2",
imports: ["other-aidl-V3"],
}
],
stability: "vintf",
backend: {
java: {
enabled: true,
platform_apis: true,
},
cpp: {
enabled: true,
},
ndk: {
enabled: true,
},
rust: {
enabled: true,
},
},
}
-
name: AIDL ইন্টারফেস মডিউলের নাম যা একটি AIDL ইন্টারফেসকে অনন্যভাবে শনাক্ত করে। -
srcs: ইন্টারফেসটি গঠনকারী AIDL সোর্স ফাইলগুলির তালিকা।com.acmeপ্যাকেজে সংজ্ঞায়িতFooনামক একটি AIDL টাইপের পাথ<base_path>/com/acme/Foo.aidlহওয়া উচিত, যেখানে<base_path>হতে পারেAndroid.bpফাইলটি যে ডিরেক্টরিতে রয়েছে, তার সাথে সম্পর্কিত যেকোনো ডিরেক্টরি। পূর্ববর্তী উদাহরণে,<base_path>হলোsrcs/aidl। -
local_include_dir: যে পথ থেকে প্যাকেজের নামটি শুরু হয়। এটি উপরে বর্ণিত<base_path>এর অনুরূপ। -
imports: এই ইন্টারফেসটি যেaidl_interfaceমডিউল ব্যবহার করে, তার একটি তালিকা। যদি আপনার কোনো AIDL ইন্টারফেস অন্য কোনোaidl_interfaceথেকে কোনো ইন্টারফেস বা পার্সেলএবল ব্যবহার করে, তবে তার নামটি এখানে লিখুন। সর্বশেষ সংস্করণ বোঝাতে শুধু নামটি ব্যবহার করা যেতে পারে, অথবা কোনো নির্দিষ্ট সংস্করণ বোঝাতে নামের সাথে ভার্সন সাফিক্স (যেমন-V1) যোগ করা যেতে পারে। অ্যান্ড্রয়েড ১২ থেকে ভার্সন উল্লেখ করার সুবিধাটি সমর্থিত। -
versions: ইন্টারফেসের পূর্ববর্তী সংস্করণগুলো যাapi_dirঅধীনে সংরক্ষিত থাকে। অ্যান্ড্রয়েড ১১ থেকে,versionsaidl_api/ nameঅধীনে সংরক্ষিত হয়। যদি কোনো ইন্টারফেসের কোনো সংরক্ষিত সংস্করণ না থাকে, তবে এটি উল্লেখ করার প্রয়োজন নেই এবং কোনো সামঞ্জস্যতা যাচাই করা হবে না। অ্যান্ড্রয়েড ১৩ এবং তার পরবর্তী সংস্করণগুলোর জন্য এই ফিল্ডটিversions_with_infoদ্বারা প্রতিস্থাপিত হয়েছে। -
versions_with_info: টাপলের একটি তালিকা, যার প্রতিটিতে একটি ফ্রোজেন ভার্সনের নাম এবং অন্যান্য aidl_interface মডিউলের ভার্সন ইম্পোর্টের একটি তালিকা থাকে, যা এই aidl_interface ভার্সনটি ইম্পোর্ট করেছে। একটি AIDL ইন্টারফেস IFACE-এর ভার্সন V-এর সংজ্ঞাaidl_api/ IFACE / Vতে অবস্থিত। এই ফিল্ডটি Android 13-এ চালু করা হয়েছিল এবং এটি সরাসরিAndroid.bpতে পরিবর্তন করার কথা নয়।*-update-apiবা*-freeze-apiকল করার মাধ্যমে ফিল্ডটি যোগ বা আপডেট করা হয়। এছাড়াও, যখন কোনো ব্যবহারকারী*-update-apiবা*-freeze-apiকল করেন, তখনversionsফিল্ডটি স্বয়ংক্রিয়ভাবেversions_with_infoতে স্থানান্তরিত হয়ে যায়। -
stability: এই ইন্টারফেসের স্থিতিশীলতার প্রতিশ্রুতির জন্য এটি একটি ঐচ্ছিক ফ্ল্যাগ। এটি শুধুমাত্র"vintf"সমর্থন করে। যদিstabilityসেট করা না থাকে, তাহলে বিল্ড সিস্টেম ইন্টারফেসটি ব্যাকওয়ার্ড কম্প্যাটিবল কিনা তা পরীক্ষা করে, যদি নাunstableনির্দিষ্ট করা থাকে। সেট করা না থাকার অর্থ হলো এই কম্পাইলেশন কনটেক্সটের মধ্যে ইন্টারফেসটির স্থিতিশীলতা রয়েছে (যেমন,system.imgএবং সম্পর্কিত পার্টিশনের সমস্ত সিস্টেম সম্পর্কিত বিষয়, অথবাvendor.imgএবং সম্পর্কিত পার্টিশনের সমস্ত ভেন্ডর সম্পর্কিত বিষয়)। যদিstability"vintf"এ সেট করা থাকে, তবে এটি একটি স্থিতিশীলতার প্রতিশ্রুতিকে বোঝায়: যতক্ষণ ইন্টারফেসটি ব্যবহৃত হবে, ততক্ষণ এটিকে অবশ্যই স্থিতিশীল রাখতে হবে। -
gen_trace: ট্রেসিং চালু বা বন্ধ করার জন্য ঐচ্ছিক ফ্ল্যাগ। অ্যান্ড্রয়েড ১৪ থেকেcppএবংjavaব্যাকএন্ডের জন্য এর ডিফল্ট মানtrueথাকে। -
host_supported: এটি একটি ঐচ্ছিক ফ্ল্যাগ, যাtrueসেট করা হলে জেনারেট করা লাইব্রেরিগুলো হোস্ট এনভায়রনমেন্টে উপলব্ধ হয়। -
unstable: এই ইন্টারফেসটিকে স্থিতিশীল করার প্রয়োজন নেই, তা বোঝানোর জন্য ব্যবহৃত ঐচ্ছিক ফ্ল্যাগ। যখন এটিtrueতে সেট করা হয়, তখন বিল্ড সিস্টেম ইন্টারফেসটির জন্য এপিআই ডাম্প তৈরি করে না এবং এটিকে আপডেট করারও প্রয়োজন মনে করে না। -
frozen: এটি একটি ঐচ্ছিক ফ্ল্যাগ, যা 'trueসেট করা হলে বোঝায় যে ইন্টারফেসটির পূর্ববর্তী সংস্করণের পর থেকে এতে কোনো পরিবর্তন হয়নি। এটি আরও বেশি বিল্ড-টাইম চেক সক্ষম করে।falseসেট করা হলে এর অর্থ হলো ইন্টারফেসটি ডেভলপমেন্টের অধীনে রয়েছে এবং এতে নতুন পরিবর্তন এসেছে, তাইfoo-freeze-apiচালালে একটি নতুন সংস্করণ তৈরি হয় এবং এর মান স্বয়ংক্রিয়ভাবে 'trueতে পরিবর্তিত হয়ে যায়। এটি অ্যান্ড্রয়েড ১৪-এ চালু করা হয়েছে। -
backend.<type>.enabled: এই ফ্ল্যাগগুলো AIDL কম্পাইলার দ্বারা জেনারেট করা প্রতিটি ব্যাকএন্ডকে টগল করে। চারটি ব্যাকএন্ড সমর্থিত: Java, C++, NDK, এবং Rust। Java, C++, এবং NDK ব্যাকএন্ডগুলো ডিফল্টরূপে সক্রিয় থাকে। যদি এই তিনটি ব্যাকএন্ডের কোনোটির প্রয়োজন না হয়, তবে সেটিকে স্পষ্টভাবে নিষ্ক্রিয় করতে হবে। Android 15 পর্যন্ত Rust ডিফল্টরূপে নিষ্ক্রিয় থাকে। -
backend.<type>.apex_available: যেসব APEX নামের জন্য জেনারেট করা স্টাব লাইব্রেরিটি উপলব্ধ, তার তালিকা। -
backend.[cpp|java].gen_log: এই ঐচ্ছিক ফ্ল্যাগটি নিয়ন্ত্রণ করে যে ট্রানজ্যাকশন সম্পর্কিত তথ্য সংগ্রহের জন্য অতিরিক্ত কোড তৈরি করা হবে কিনা। -
backend.[cpp|java].vndk.enabled: এই ইন্টারফেসটিকে VNDK-এর অংশ করার জন্য এটি একটি ঐচ্ছিক ফ্ল্যাগ। ডিফল্ট মান হলোfalse। -
backend.[cpp|ndk].additional_shared_libraries: অ্যান্ড্রয়েড ১৪-এ প্রবর্তিত এই ফ্ল্যাগটি নেটিভ লাইব্রেরিতে ডিপেন্ডেন্সি যোগ করে। এই ফ্ল্যাগটিndk_headerএবংcpp_headerসাথে ব্যবহার করা যায়। -
backend.java.sdk_version: জাভা স্টাব লাইব্রেরিটি যে SDK ভার্সনের উপর ভিত্তি করে তৈরি করা হবে, তা নির্দিষ্ট করার জন্য এটি একটি ঐচ্ছিক ফ্ল্যাগ। এর ডিফল্ট মান হলো"system_current"। যখনbackend.java.platform_apistrueহয়, তখন এটি সেট করা উচিত নয়। -
backend.java.platform_apis: এটি একটি ঐচ্ছিক ফ্ল্যাগ, যা `trueসেট করতে হবে যখন জেনারেট করা লাইব্রেরিগুলো SDK-এর পরিবর্তে প্ল্যাটফর্ম API-এর উপর ভিত্তি করে বিল্ড করার প্রয়োজন হয়।
ভার্সন এবং সক্রিয় ব্যাকএন্ডগুলোর প্রতিটি সমন্বয়ের জন্য একটি স্টাব লাইব্রেরি তৈরি করা হয়। কোনো নির্দিষ্ট ব্যাকএন্ডের জন্য স্টাব লাইব্রেরির নির্দিষ্ট ভার্সনটি কীভাবে উল্লেখ করতে হয়, তা জানতে মডিউল নামকরণের নিয়মাবলী দেখুন।
AIDL ফাইল লিখুন
স্টেবল AIDL-এর ইন্টারফেসগুলো প্রচলিত ইন্টারফেসের মতোই, তবে একটি ব্যতিক্রম হলো এগুলোতে আনস্ট্রাকচার্ড পার্সেল্যাবল ব্যবহার করার অনুমতি নেই (কারণ এগুলো স্টেবল নয়! স্ট্রাকচার্ড বনাম স্টেবল AIDL দেখুন)। স্টেবল AIDL-এর প্রধান পার্থক্য হলো পার্সেল্যাবলগুলো কীভাবে সংজ্ঞায়িত করা হয়। পূর্বে, পার্সেল্যাবলগুলো ফরওয়ার্ড ডিক্লেয়ার করা হতো; স্টেবল (এবং সেই কারণে স্ট্রাকচার্ড) AIDL-এ, পার্সেল্যাবল ফিল্ড এবং ভেরিয়েবলগুলো সুস্পষ্টভাবে সংজ্ঞায়িত করা হয়।
// in a file like 'some/package/Thing.aidl'
package some.package;
parcelable SubThing {
String a = "foo";
int b;
}
boolean , char , float , double , byte , int , long , এবং String এর জন্য একটি ডিফল্ট মান সমর্থিত (কিন্তু বাধ্যতামূলক নয়)। Android 12-এ, ব্যবহারকারী-সংজ্ঞায়িত এনুমারেশনগুলির জন্যও ডিফল্ট মান সমর্থিত। যখন কোনো ডিফল্ট মান নির্দিষ্ট করা থাকে না, তখন একটি ০-এর মতো বা খালি মান ব্যবহৃত হয়। ডিফল্ট মান ছাড়া এনুমারেশনগুলি ০ দিয়ে ইনিশিয়ালাইজ করা হয়, এমনকি যদি কোনো জিরো এনুমেরেটর নাও থাকে।
স্টাব লাইব্রেরি ব্যবহার করুন
আপনার মডিউলে স্টাব লাইব্রেরিগুলোকে ডিপেন্ডেন্সি হিসেবে যুক্ত করার পর, আপনি সেগুলোকে আপনার ফাইলগুলোতে অন্তর্ভুক্ত করতে পারেন। এখানে বিল্ড সিস্টেমে স্টাব লাইব্রেরির কিছু উদাহরণ দেওয়া হলো (লেগ্যাসি মডিউল ডেফিনিশনের জন্য Android.mk ও ব্যবহার করা যেতে পারে)। উল্লেখ্য, এই উদাহরণগুলোতে ভার্সন উল্লেখ নেই, তাই এটি একটি আনস্টেবল ইন্টারফেস ব্যবহারের প্রতিনিধিত্ব করে, কিন্তু ভার্সনসহ ইন্টারফেসের নামগুলোতে অতিরিক্ত তথ্য অন্তর্ভুক্ত থাকে, এর জন্য ‘ভার্সনিং ইন্টারফেস’ দেখুন।
cc_... {
name: ...,
// use `shared_libs:` to load your library and its transitive dependencies
// dynamically
shared_libs: ["my-module-name-cpp"],
// use `static_libs:` to include the library in this binary and drop
// transitive dependencies
static_libs: ["my-module-name-cpp"],
...
}
# or
java_... {
name: ...,
// use `static_libs:` to add all jars and classes to this jar
static_libs: ["my-module-name-java"],
// use `libs:` to make these classes available during build time, but
// not add them to the jar, in case the classes are already present on the
// boot classpath (such as if it's in framework.jar) or another jar.
libs: ["my-module-name-java"],
// use `srcs:` with `-java-sources` if you want to add classes in this
// library jar directly, but you get transitive dependencies from
// somewhere else, such as the boot classpath or another jar.
srcs: ["my-module-name-java-source", ...],
...
}
# or
rust_... {
name: ...,
rustlibs: ["my-module-name-rust"],
...
}
C++ এ উদাহরণ:
#include "some/package/IFoo.h"
#include "some/package/Thing.h"
...
// use just like traditional AIDL
জাভাতে উদাহরণ:
import some.package.IFoo;
import some.package.Thing;
...
// use just like traditional AIDL
রাস্ট-এ উদাহরণ:
use aidl_interface_name::aidl::some::package::{IFoo, Thing};
...
// use just like traditional AIDL
সংস্করণ ইন্টারফেস
foo নামে একটি মডিউল ডিক্লেয়ার করলে বিল্ড সিস্টেমে একটি টার্গেটও তৈরি হয়, যা আপনি মডিউলটির এপিআই (API) ম্যানেজ করার জন্য ব্যবহার করতে পারেন। বিল্ড করার সময়, foo-freeze-api অ্যান্ড্রয়েড ভার্সনের উপর নির্ভর করে api_dir অথবা aidl_api/ name অধীনে একটি নতুন এপিআই ডেফিনিশন যোগ করে এবং একটি .hash ফাইলও যুক্ত করে, যা ইন্টারফেসটির নতুন ফ্রোজেন ভার্সনটিকে উপস্থাপন করে। foo-freeze-api অতিরিক্ত ভার্সন এবং সেই ভার্সনের জন্য imports প্রতিফলিত করতে versions_with_info প্রপার্টিটিও আপডেট করে। মূলত, versions_with_info এর imports imports ফিল্ড থেকে কপি করা হয়। কিন্তু যে ইম্পোর্টের কোনো সুস্পষ্ট ভার্সন নেই, তার জন্য versions_with_info imports সর্বশেষ স্টেবল ভার্সনটি নির্দিষ্ট করা থাকে। versions_with_info প্রপার্টিটি নির্দিষ্ট করার পর, বিল্ড সিস্টেম ফ্রোজেন ভার্সনগুলোর মধ্যে এবং টপ অফ ট্রি (ToT) ও সর্বশেষ ফ্রোজেন ভার্সনের মধ্যে কম্প্যাটিবিলিটি চেক চালায়।
এছাড়াও, আপনাকে ToT ভার্সনের API ডেফিনিশন পরিচালনা করতে হবে। যখনই কোনো API আপডেট করা হবে, তখন toT ভার্সনের API ডেফিনিশন ধারণকারী aidl_api/ name /current ফাইলটি আপডেট করার জন্য foo-update-api কমান্ডটি চালান।
একটি ইন্টারফেসের স্থিতিশীলতা বজায় রাখতে, মালিকরা নতুন কিছু যোগ করতে পারেন:
- ইন্টারফেসের শেষ প্রান্তের মেথডসমূহ (অথবা সুস্পষ্টভাবে সংজ্ঞায়িত নতুন সিরিয়ালসহ মেথডসমূহ)
- একটি পার্সেলযোগ্য উপাদানের শেষে উপাদানসমূহ (প্রতিটি উপাদানের জন্য একটি ডিফল্ট যোগ করা প্রয়োজন)
- ধ্রুবক মান
- অ্যান্ড্রয়েড ১১-এ, এনুমেটর
- অ্যান্ড্রয়েড ১২-এ, একটি ইউনিয়নের শেষে ফিল্ডগুলো
অন্য কোনো কার্যকলাপের অনুমতি নেই, এবং অন্য কেউ ইন্টারফেসটি পরিবর্তন করতে পারবে না (অন্যথায় মালিকের করা পরিবর্তনের সাথে সংঘর্ষের ঝুঁকি থাকে)।
রিলিজের জন্য সমস্ত ইন্টারফেস স্থির (ফ্রোজেন) আছে কিনা তা পরীক্ষা করতে, আপনি নিম্নলিখিত এনভায়রনমেন্ট ভেরিয়েবলগুলো সেট করে বিল্ড করতে পারেন:
-
AIDL_FROZEN_REL=true m ...- বিল্ডের জন্য সেই সমস্ত স্থিতিশীল AIDL ইন্টারফেসগুলিকে ফ্রিজ করা প্রয়োজন যেগুলিতে কোনোowner:' ফিল্ড নির্দিষ্ট করা নেই। -
AIDL_FROZEN_OWNERS="aosp test"- বিল্ডের জন্য সমস্ত স্থিতিশীল AIDL ইন্টারফেসকেowner:" ফিল্ডে "aosp" বা "test" উল্লেখ করে ফ্রিজ করা আবশ্যক।
আমদানির স্থিতিশীলতা
একটি ইন্টারফেসের ফ্রোজেন ভার্সনের জন্য ইম্পোর্টের ভার্সন আপডেট করা স্টেবল এআইডিএল লেয়ারে ব্যাকওয়ার্ড কম্প্যাটিবল। তবে, এগুলো আপডেট করার জন্য ইন্টারফেসের পূর্ববর্তী ভার্সন ব্যবহারকারী সমস্ত সার্ভার এবং ক্লায়েন্টকে আপডেট করতে হয়, এবং বিভিন্ন ভার্সনের টাইপ একসাথে ব্যবহার করলে কিছু অ্যাপ বিভ্রান্ত হতে পারে। সাধারণত, শুধুমাত্র টাইপ-ভিত্তিক বা কমন প্যাকেজগুলোর জন্য এটি নিরাপদ, কারণ আইপিসি ট্রানজ্যাকশন থেকে আসা অজানা টাইপগুলো হ্যান্ডেল করার জন্য কোড আগে থেকেই লেখা থাকে।
অ্যান্ড্রয়েড প্ল্যাটফর্ম কোডে android.hardware.graphics.common হলো এই ধরনের ভার্সন আপগ্রেডের সবচেয়ে বড় উদাহরণ।
সংস্করণযুক্ত ইন্টারফেস ব্যবহার করুন
ইন্টারফেস পদ্ধতি
রানটাইমে, পুরোনো সার্ভারে নতুন মেথড কল করার চেষ্টা করার সময়, নতুন ক্লায়েন্টরা ব্যাকএন্ডের ওপর নির্ভর করে হয় একটি এরর অথবা একটি এক্সেপশন পায়।
-
cppব্যাকএন্ড::android::UNKNOWN_TRANSACTIONপায়। -
ndkব্যাকএন্ডSTATUS_UNKNOWN_TRANSACTIONপায়। -
javaব্যাকএন্ডেandroid.os.RemoteExceptionআসছে এবং মেসেজে বলা হচ্ছে যে এপিআইটি ইমপ্লিমেন্ট করা হয়নি।
এটি মোকাবেলার কৌশলের জন্য সংস্করণ জিজ্ঞাসা করা এবং ডিফল্ট ব্যবহার করা দেখুন।
পার্সেলযোগ্য
যখন পার্সেলযোগ্য ফাইলে নতুন ফিল্ড যোগ করা হয়, তখন পুরোনো ক্লায়েন্ট এবং সার্ভারগুলো সেগুলো বাদ দিয়ে দেয়। যখন নতুন ক্লায়েন্ট এবং সার্ভারগুলো পুরোনো পার্সেলযোগ্য ফাইলগুলো গ্রহণ করে, তখন নতুন ফিল্ডগুলোর জন্য ডিফল্ট মানগুলো স্বয়ংক্রিয়ভাবে পূরণ হয়ে যায়। এর মানে হলো, একটি পার্সেলযোগ্য ফাইলের সমস্ত নতুন ফিল্ডের জন্য ডিফল্ট মান নির্দিষ্ট করে দিতে হবে।
ক্লায়েন্টদের এটা আশা করা উচিত নয় যে সার্ভার নতুন ফিল্ডগুলো ব্যবহার করবে, যদি না তারা জানে যে সার্ভারটি এমন একটি সংস্করণ প্রয়োগ করছে যেখানে ফিল্ডটি সংজ্ঞায়িত করা আছে ( সংস্করণ কোয়েরি করা দেখুন)।
এনাম এবং ধ্রুবক
একইভাবে, ক্লায়েন্ট এবং সার্ভারের উচিত পরিস্থিতি অনুযায়ী অপরিচিত ধ্রুবক মান এবং এনুমেটরগুলোকে হয় প্রত্যাখ্যান করা অথবা উপেক্ষা করা, কারণ ভবিষ্যতে আরও যুক্ত হতে পারে। উদাহরণস্বরূপ, একটি সার্ভারের এমন কোনো এনুমেটর পেলে যা তার জানা নেই, তখন তার কার্যক্রম বন্ধ করা উচিত নয়। সার্ভারের উচিত হয় এনুমেটরটিকে উপেক্ষা করা, অথবা এমন কিছু ফেরত দেওয়া যাতে ক্লায়েন্ট বুঝতে পারে যে এই বাস্তবায়নে এটি সমর্থিত নয়।
ইউনিয়ন
যদি প্রাপক পুরোনো হয় এবং ফিল্ডটি সম্পর্কে না জানে, তাহলে একটি নতুন ফিল্ড সহ ইউনিয়ন পাঠানোর চেষ্টা ব্যর্থ হয়। ইমপ্লিমেন্টেশনটি নতুন ফিল্ড সহ ইউনিয়নটি কখনোই দেখতে পাবে না। যদি এটি একটি একমুখী লেনদেন হয়, তবে এই ব্যর্থতা উপেক্ষা করা হয়; অন্যথায় ত্রুটিটি হয় BAD_VALUE (C++ বা NDK ব্যাকএন্ডের জন্য) অথবা IllegalArgumentException (জাভা ব্যাকএন্ডের জন্য)। এই ত্রুটিটি পাওয়া যায় যখন ক্লায়েন্ট একটি পুরোনো সার্ভারে নতুন ফিল্ড সহ একটি ইউনিয়ন সেট পাঠায়, অথবা যখন একটি পুরোনো ক্লায়েন্ট একটি নতুন সার্ভার থেকে ইউনিয়নটি গ্রহণ করে।
একাধিক সংস্করণ পরিচালনা করুন
অ্যান্ড্রয়েডের একটি লিঙ্কার নেমস্পেসে একটি নির্দিষ্ট aidl ইন্টারফেসের কেবল ১টি সংস্করণ থাকতে পারে, যাতে জেনারেট করা aidl টাইপগুলোর একাধিক ডেফিনিশন থাকার মতো পরিস্থিতি এড়ানো যায়। C++-এ 'ওয়ান ডেফিনিশন রুল' (One Definition Rule) রয়েছে, যা প্রতিটি সিম্বলের কেবল একটি ডেফিনিশন থাকা আবশ্যক করে।
যখন কোনো মডিউল একই aidl_interface লাইব্রেরির বিভিন্ন সংস্করণের উপর নির্ভর করে, তখন অ্যান্ড্রয়েড বিল্ড একটি ত্রুটি দেখায়। মডিউলটি এই লাইব্রেরিগুলোর উপর সরাসরি অথবা পরোক্ষভাবে তাদের ডিপেন্ডেন্সির ডিপেন্ডেন্সির মাধ্যমে নির্ভর করতে পারে। এই ত্রুটিগুলো ব্যর্থ হওয়া মডিউল থেকে aidl_interface লাইব্রেরির সাংঘর্ষিক সংস্করণগুলো পর্যন্ত ডিপেন্ডেন্সি গ্রাফটি দেখায়। এই লাইব্রেরিগুলোর একই (সাধারণত সর্বশেষ) সংস্করণ অন্তর্ভুক্ত করার জন্য সমস্ত ডিপেন্ডেন্সি আপডেট করা প্রয়োজন।
যদি ইন্টারফেস লাইব্রেরিটি বিভিন্ন মডিউল দ্বারা ব্যবহৃত হয়, তবে একই সংস্করণ ব্যবহার করতে হবে এমন লাইব্রেরি ও প্রসেসের যেকোনো গ্রুপের জন্য cc_defaults , java_defaults এবং rust_defaults তৈরি করা সহায়ক হতে পারে। ইন্টারফেসের একটি নতুন সংস্করণ চালু করার সময়, এই ডিফল্টগুলো আপডেট করা যেতে পারে এবং সেগুলো ব্যবহারকারী সমস্ত মডিউল একসাথে আপডেট হয়ে যায়, যা নিশ্চিত করে যে তারা ইন্টারফেসের ভিন্ন ভিন্ন সংস্করণ ব্যবহার করছে না।
cc_defaults {
name: "my.aidl.my-process-group-ndk-shared",
shared_libs: ["my.aidl-V3-ndk"],
...
}
cc_library {
name: "foo",
defaults: ["my.aidl.my-process-group-ndk-shared"],
...
}
cc_binary {
name: "bar",
defaults: ["my.aidl.my-process-group-ndk-shared"],
...
}
যখন aidl_interface মডিউলগুলো অন্য aidl_interface মডিউল ইম্পোর্ট করে, তখন এটি অতিরিক্ত নির্ভরতা তৈরি করে যার জন্য একসাথে ব্যবহারের ক্ষেত্রে নির্দিষ্ট সংস্করণের প্রয়োজন হয়। এই পরিস্থিতিটি পরিচালনা করা কঠিন হয়ে উঠতে পারে aidl_interface aidl_interface সাধারণ মডিউলগুলো ইম্পোর্ট করা থাকে।
aidl_interfaces_defaults ব্যবহার করে কোনো একটি aidl_interface এর নির্ভরতাগুলোর সর্বশেষ সংস্করণগুলোর একটিমাত্র সংজ্ঞা সংরক্ষণ করা যায়, যা একটি একক স্থান থেকে হালনাগাদ করা যায় এবং সেই সাধারণ ইন্টারফেসটি ইম্পোর্ট করতে ইচ্ছুক সকল aidl_interface মডিউল দ্বারা ব্যবহার করা যায়।
aidl_interface_defaults {
name: "android.popular.common-latest-defaults",
imports: ["android.popular.common-V3"],
...
}
aidl_interface {
name: "android.foo",
defaults: ["my.aidl.latest-ndk-shared"],
...
}
aidl_interface {
name: "android.bar",
defaults: ["my.aidl.latest-ndk-shared"],
...
}
পতাকা-ভিত্তিক উন্নয়ন
উন্নয়নাধীন (অপরিবর্তিত) ইন্টারফেসগুলো রিলিজ ডিভাইসে ব্যবহার করা যায় না, কারণ সেগুলো ব্যাকওয়ার্ড কম্প্যাটিবল হবে এমন কোনো নিশ্চয়তা নেই।
AIDL এই আনফ্রোজেন ইন্টারফেস লাইব্রেরিগুলোর জন্য রানটাইম ফলব্যাক সমর্থন করে, যাতে সর্বশেষ আনফ্রোজেন সংস্করণের উপর ভিত্তি করে কোড লেখা হলেও তা রিলিজ ডিভাইসে ব্যবহার করা যায়। ক্লায়েন্টদের ব্যাকওয়ার্ড-কম্প্যাটিবল আচরণ বিদ্যমান আচরণের মতোই এবং ফলব্যাকের ফলে ইমপ্লিমেন্টেশনগুলোকেও সেই আচরণগুলো অনুসরণ করতে হয়। ভার্সনযুক্ত ইন্টারফেসের ব্যবহার দেখুন।
AIDL বিল্ড ফ্ল্যাগ
যে ফ্ল্যাগটি এই আচরণ নিয়ন্ত্রণ করে তা হলো RELEASE_AIDL_USE_UNFROZEN যা build/release/build_flags.bzl ফাইলে সংজ্ঞায়িত। true মানে হলো রান টাইমে ইন্টারফেসের আনফ্রোজেন সংস্করণটি ব্যবহৃত হবে এবং false মানে হলো আনফ্রোজেন সংস্করণগুলোর লাইব্রেরিগুলো তাদের সর্বশেষ ফ্রোজেন সংস্করণের মতোই আচরণ করবে। আপনি লোকাল ডেভেলপমেন্টের জন্য ফ্ল্যাগটিকে true তে ওভাররাইড করতে পারেন, কিন্তু রিলিজের আগে অবশ্যই এটিকে false এ ফিরিয়ে আনতে হবে। সাধারণত, ডেভেলপমেন্ট এমন একটি কনফিগারেশন দিয়ে করা হয় যেখানে ফ্ল্যাগটি true তে সেট করা থাকে।
সামঞ্জস্য ম্যাট্রিক্স এবং ম্যানিফেস্ট
ভেন্ডর ইন্টারফেস অবজেক্ট (VINTF অবজেক্ট) নির্ধারণ করে যে ভেন্ডর ইন্টারফেসের উভয় দিকে কোন সংস্করণগুলো প্রত্যাশিত এবং কোন সংস্করণগুলো সরবরাহ করা হয়।
বেশিরভাগ নন-কাটলফিশ ডিভাইস ইন্টারফেসগুলো ফ্রিজ করার পরেই সর্বশেষ কম্প্যাটিবিলিটি ম্যাট্রিক্সকে টার্গেট করে, তাই RELEASE_AIDL_USE_UNFROZEN উপর ভিত্তি করে তৈরি AIDL লাইব্রেরিগুলোর মধ্যে কোনো পার্থক্য থাকে না।
ম্যাট্রিক্স
পার্টনার-মালিকানাধীন ইন্টারফেসগুলো ডিভাইস-নির্দিষ্ট বা পণ্য-নির্দিষ্ট কম্প্যাটিবিলিটি ম্যাট্রিক্সে যুক্ত করা হয়, যা ডিভাইসটি ডেভেলপমেন্টের সময় টার্গেট করে। তাই যখন কোনো ইন্টারফেসের একটি নতুন, আনফ্রোজেন সংস্করণ একটি কম্প্যাটিবিলিটি ম্যাট্রিক্সে যুক্ত করা হয়, তখন RELEASE_AIDL_USE_UNFROZEN=false এর জন্য পূর্ববর্তী ফ্রোজেন সংস্করণগুলো থাকা প্রয়োজন। আপনি বিভিন্ন RELEASE_AIDL_USE_UNFROZEN কনফিগারেশনের জন্য আলাদা কম্প্যাটিবিলিটি ম্যাট্রিক্স ফাইল ব্যবহার করে অথবা সমস্ত কনফিগারেশনে ব্যবহৃত একটিমাত্র কম্প্যাটিবিলিটি ম্যাট্রিক্স ফাইলে উভয় সংস্করণকে অনুমতি দিয়ে এটি পরিচালনা করতে পারেন।
উদাহরণস্বরূপ, আনফ্রোজেন ভার্সন ৪ যোগ করার সময় <version>3-4</version> ব্যবহার করুন।
যখন ভার্সন ৪ ফ্রিজ করা থাকে, তখন আপনি কম্প্যাটিবিলিটি ম্যাট্রিক্স থেকে ভার্সন ৩ সরিয়ে ফেলতে পারেন, কারণ RELEASE_AIDL_USE_UNFROZEN ফলস false হলে ফ্রিজ করা ভার্সন ৪ ব্যবহৃত হয়।
প্রকাশ
Android 15-এ, libvintf এ একটি পরিবর্তন আনা হয়েছে যা RELEASE_AIDL_USE_UNFROZEN এর মানের উপর ভিত্তি করে বিল্ড টাইমে ম্যানিফেস্ট ফাইলগুলোকে মডিফাই করে।
ম্যানিফেস্ট এবং ম্যানিফেস্ট ফ্র্যাগমেন্টগুলো ঘোষণা করে যে একটি সার্ভিস কোনো ইন্টারফেসের কোন সংস্করণটি ইমপ্লিমেন্ট করে। যখন কোনো ইন্টারফেসের সর্বশেষ আনফ্রোজেন সংস্করণ ব্যবহার করা হয়, তখন এই নতুন সংস্করণটি প্রতিফলিত করার জন্য ম্যানিফেস্টটি অবশ্যই আপডেট করতে হবে। যখন RELEASE_AIDL_USE_UNFROZEN=false তখন তৈরি হওয়া AIDL লাইব্রেরির পরিবর্তন প্রতিফলিত করার জন্য libvintf দ্বারা ম্যানিফেস্ট এন্ট্রিগুলো সমন্বয় করা হয়। সংস্করণটি আনফ্রোজেন সংস্করণ, N , থেকে সর্বশেষ ফ্রোজেন সংস্করণ N - 1 এ পরিবর্তিত হয়। সুতরাং, ব্যবহারকারীদের তাদের প্রতিটি সার্ভিসের জন্য একাধিক ম্যানিফেস্ট বা ম্যানিফেস্ট ফ্র্যাগমেন্ট পরিচালনা করার প্রয়োজন হয় না।
HAL ক্লায়েন্ট পরিবর্তন
HAL ক্লায়েন্ট কোডকে অবশ্যই প্রতিটি পূর্ববর্তী সমর্থিত ফ্রোজেন সংস্করণের সাথে ব্যাকওয়ার্ড কম্প্যাটিবল হতে হবে। যখন RELEASE_AIDL_USE_UNFROZEN ফলস false হয়, তখন সার্ভিসগুলো সর্বদা সর্বশেষ ফ্রোজেন সংস্করণ বা তার আগের সংস্করণের মতো দেখায় (উদাহরণস্বরূপ, নতুন আনফ্রোজেন মেথড কল করলে UNKNOWN_TRANSACTION রিটার্ন করে, অথবা নতুন parcelable ফিল্ডগুলোতে তাদের ডিফল্ট ভ্যালু থাকে)। অ্যান্ড্রয়েড ফ্রেমওয়ার্ক ক্লায়েন্টদের অতিরিক্ত পূর্ববর্তী সংস্করণগুলোর সাথেও ব্যাকওয়ার্ড কম্প্যাটিবল হতে হয়, কিন্তু এটি ভেন্ডর ক্লায়েন্ট এবং পার্টনার-মালিকানাধীন ইন্টারফেসের ক্লায়েন্টদের জন্য একটি নতুন বিষয়।
HAL বাস্তবায়নের পরিবর্তন
ফ্ল্যাগ-ভিত্তিক ডেভেলপমেন্টের সাথে HAL ডেভেলপমেন্টের সবচেয়ে বড় পার্থক্য হলো, যখন RELEASE_AIDL_USE_UNFROZEN ফলস false থাকে, তখন কাজ করার জন্য HAL ইমপ্লিমেন্টেশনগুলোকে সর্বশেষ ফ্রোজেন (frozen) ভার্সনের সাথে ব্যাকওয়ার্ড কম্প্যাটিবল (backward compatible) হতে হয়। ইমপ্লিমেন্টেশন এবং ডিভাইস কোডে ব্যাকওয়ার্ড কম্প্যাটিবিলিটি বিবেচনা করা একটি নতুন বিষয়। ভার্সনযুক্ত ইন্টারফেসের ব্যবহার (Use versioned interfaces) দেখুন।
ক্লায়েন্ট ও সার্ভার এবং ফ্রেমওয়ার্ক কোড ও ভেন্ডর কোডের ক্ষেত্রে ব্যাকওয়ার্ড কম্প্যাটিবিলিটির বিবেচ্য বিষয়গুলো সাধারণত একই থাকে, কিন্তু কিছু সূক্ষ্ম পার্থক্য রয়েছে যেগুলো সম্পর্কে আপনাকে সচেতন থাকতে হবে, কারণ আপনি এখন কার্যকরভাবে এমন দুটি সংস্করণ বাস্তবায়ন করছেন যা একই সোর্স কোড (বর্তমান, অপরিবর্তিত সংস্করণ) ব্যবহার করে।
উদাহরণ: একটি ইন্টারফেসের তিনটি ফ্রোজেন ভার্সন আছে। ইন্টারফেসটি একটি নতুন মেথড দিয়ে আপডেট করা হয়। ক্লায়েন্ট এবং সার্ভিস উভয়কেই নতুন ভার্সন ৪ লাইব্রেরি ব্যবহার করার জন্য আপডেট করা হয়। যেহেতু V4 লাইব্রেরিটি ইন্টারফেসের একটি আনফ্রোজেন ভার্সনের উপর ভিত্তি করে তৈরি, তাই RELEASE_AIDL_USE_UNFROZEN false হলে এটি সর্বশেষ ফ্রোজেন ভার্সন, অর্থাৎ ভার্সন ৩-এর মতো আচরণ করে এবং নতুন মেথডটির ব্যবহার প্রতিরোধ করে।
যখন ইন্টারফেসটি ফ্রিজ করা হয়, তখন RELEASE_AIDL_USE_UNFROZEN এর সমস্ত মান সেই ফ্রিজ করা সংস্করণটি ব্যবহার করে এবং ব্যাকওয়ার্ড কম্প্যাটিবিলিটি পরিচালনাকারী কোডটি সরিয়ে ফেলা যায়।
কলব্যাকে থাকা মেথড কল করার সময়, UNKNOWN_TRANSACTION রিটার্ন করা হলে আপনাকে অবশ্যই সেই পরিস্থিতিটি সুষ্ঠুভাবে সামাল দিতে হবে। ক্লায়েন্টরা রিলিজ কনফিগারেশনের উপর ভিত্তি করে একটি কলব্যাকের দুটি ভিন্ন সংস্করণ প্রয়োগ করতে পারে, তাই আপনি ধরে নিতে পারেন না যে ক্লায়েন্ট নতুনতম সংস্করণটিই পাঠাচ্ছে, এবং নতুন মেথডগুলো সেটিই রিটার্ন করতে পারে। এটি অনেকটা সেই পদ্ধতির মতোই, যেভাবে স্থিতিশীল AIDL ক্লায়েন্টরা সার্ভারের সাথে ব্যাকওয়ার্ড কম্প্যাটিবিলিটি বজায় রাখে, যা "Use versioned interfaces" অংশে বর্ণনা করা হয়েছে।
// Get the callback along with the version of the callback
ScopedAStatus RegisterMyCallback(const std::shared_ptr<IMyCallback>& cb) override {
mMyCallback = cb;
// Get the version of the callback for later when we call methods on it
auto status = mMyCallback->getInterfaceVersion(&mMyCallbackVersion);
return status;
}
// Example of using the callback later
void NotifyCallbackLater() {
// From the latest frozen version (V2)
mMyCallback->foo();
// Call this method from the unfrozen V3 only if the callback is at least V3
if (mMyCallbackVersion >= 3) {
mMyCallback->bar();
}
}
যখন RELEASE_AIDL_USE_UNFROZEN false হয়, তখন বিদ্যমান টাইপগুলির ( parcelable , enum , union ) নতুন ফিল্ডগুলি নাও থাকতে পারে বা তাদের ডিফল্ট মান ধারণ করতে পারে এবং একটি পরিষেবা যে নতুন ফিল্ডগুলির মান পাঠানোর চেষ্টা করে, সেগুলি প্রসেস থেকে বের হওয়ার পথে বাদ পড়ে যায়।
এই আনফ্রোজেন সংস্করণে যোগ করা নতুন টাইপগুলো ইন্টারফেসের মাধ্যমে পাঠানো বা গ্রহণ করা যাবে না।
যখন RELEASE_AIDL_USE_UNFROZEN false হয়, তখন ইমপ্লিমেন্টেশনটি কোনো ক্লায়েন্টের কাছ থেকে নতুন মেথডের জন্য কখনোই কল পায় না।
খেয়াল রাখবেন, নতুন এনুমেটরগুলো যেন শুধুমাত্র যে সংস্করণে চালু করা হয়েছে, সেটির সাথেই ব্যবহার করা হয়, পূর্ববর্তী সংস্করণের সাথে নয়।
সাধারণত, রিমোট ইন্টারফেসটি কোন ভার্সন ব্যবহার করছে তা দেখতে আপনি foo->getInterfaceVersion() ব্যবহার করেন। তবে ফ্ল্যাগ-ভিত্তিক ভার্সনিং সাপোর্টের কারণে, আপনি দুটি ভিন্ন ভার্সন ইমপ্লিমেন্ট করছেন, তাই আপনি বর্তমান ইন্টারফেসের ভার্সনটি পেতে চাইতে পারেন। আপনি বর্তমান অবজেক্টের ইন্টারফেস ভার্সনটি নিয়ে এটি করতে পারেন, যেমন, this->getInterfaceVersion() অথবা my_ver এর জন্য অন্যান্য মেথডগুলো ব্যবহার করে। আরও তথ্যের জন্য "রিমোট অবজেক্টের ইন্টারফেস ভার্সন কোয়েরি করা" দেখুন।
নতুন VINTF স্থিতিশীল ইন্টারফেস
যখন একটি নতুন AIDL ইন্টারফেস প্যাকেজ যোগ করা হয়, তখন এর কোনো সর্বশেষ ফ্রোজেন সংস্করণ থাকে না, তাই RELEASE_AIDL_USE_UNFROZEN false হলে ফিরে যাওয়ার মতো কোনো আচরণ থাকে না। এই ইন্টারফেসগুলো ব্যবহার করবেন না। যখন RELEASE_AIDL_USE_UNFROZEN false হয়, তখন সার্ভিস ম্যানেজার সার্ভিসটিকে ইন্টারফেসটি রেজিস্টার করার অনুমতি দেবে না এবং ক্লায়েন্টরা এটি খুঁজে পাবে না।
আপনি ডিভাইস মেকফাইলে RELEASE_AIDL_USE_UNFROZEN ফ্ল্যাগের মানের উপর ভিত্তি করে শর্তসাপেক্ষে সার্ভিসগুলো যোগ করতে পারেন:
ifeq ($(RELEASE_AIDL_USE_UNFROZEN),true)
PRODUCT_PACKAGES += \
android.hardware.health.storage-service
endif
যদি সার্ভিসটি একটি বৃহত্তর প্রসেসের অংশ হয় এবং আপনি শর্তসাপেক্ষে এটিকে ডিভাইসে যুক্ত করতে না পারেন, তাহলে আপনি IServiceManager::isDeclared() ব্যবহার করে সার্ভিসটি ডিক্লেয়ার করা আছে কিনা তা পরীক্ষা করতে পারেন। যদি এটি ডিক্লেয়ার করা থাকে এবং রেজিস্টার করতে ব্যর্থ হয়, তাহলে প্রসেসটি অ্যাবোর্ট করুন। আর যদি এটি ডিক্লেয়ার করা না থাকে, তাহলে এটি রেজিস্টার করতে ব্যর্থ হবে বলেই ধরে নেওয়া হয়।
নতুন VINTF স্থিতিশীল এক্সটেনশন ইন্টারফেস
নতুন এক্সটেনশন ইন্টারফেসগুলোর ফিরে যাওয়ার জন্য কোনো পূর্ববর্তী সংস্করণ থাকে না এবং যেহেতু এগুলো ServiceManager সাথে নিবন্ধিত নয় বা VINTF ম্যানিফেস্টে ঘোষিত নয়, তাই কখন এক্সটেনশন ইন্টারফেসটিকে অন্য কোনো ইন্টারফেসের সাথে সংযুক্ত করতে হবে তা নির্ধারণ করার জন্য IServiceManager::isDeclared() ব্যবহার করা যায় না।
রিলিজড ডিভাইসগুলিতে ব্যবহার এড়ানোর জন্য নতুন আনফ্রোজেন এক্সটেনশন ইন্টারফেসটিকে বিদ্যমান ইন্টারফেসের সাথে সংযুক্ত করা হবে কিনা, তা নির্ধারণ করতে RELEASE_AIDL_USE_UNFROZEN ভেরিয়েবলটি ব্যবহার করা যেতে পারে। রিলিজড ডিভাইসগুলিতে ব্যবহার করার জন্য ইন্টারফেসটিকে ফ্রোজেন করতে হবে।
vts_treble_vintf_vendor_test এবং vts_treble_vintf_framework_test VTS টেস্টগুলো কোনো রিলিজড ডিভাইসে যখন একটি আনফ্রোজেন এক্সটেনশন ইন্টারফেস ব্যবহৃত হয়, তখন তা শনাক্ত করে এবং একটি এরর থ্রো করে।
যদি এক্সটেনশন ইন্টারফেসটি নতুন না হয় এবং এর একটি পূর্ব-স্থিরীকৃত সংস্করণ থাকে, তাহলে এটি সেই পূর্ব-স্থিরীকৃত সংস্করণেই ফিরে যায় এবং এর জন্য কোনো অতিরিক্ত পদক্ষেপের প্রয়োজন হয় না।
উন্নয়ন সরঞ্জাম হিসেবে কাটলফিশ
প্রতি বছর VINTF চূড়ান্ত হওয়ার পর আমরা কাটলফিশের ফ্রেমওয়ার্ক কম্প্যাটিবিলিটি ম্যাট্রিক্স (FCM) target-level এবং PRODUCT_SHIPPING_API_LEVEL সমন্বয় করি, যাতে সেগুলো পরবর্তী বছরের রিলিজের সাথে চালু হওয়া ডিভাইসগুলোকে প্রতিফলিত করে। আমরা target-level এবং PRODUCT_SHIPPING_API_LEVEL সমন্বয় করি এটা নিশ্চিত করার জন্য যে, পরবর্তী বছরের রিলিজের জন্য পরীক্ষিত এবং নতুন প্রয়োজনীয়তা পূরণ করে এমন কোনো ডিভাইস যেন চালু হয়।
যখন RELEASE_AIDL_USE_UNFROZEN true হয়, তখন ভবিষ্যতের অ্যান্ড্রয়েড রিলিজগুলোর উন্নয়নের জন্য Cuttlefish ব্যবহৃত হয়। এটি আগামী বছরের অ্যান্ড্রয়েড রিলিজের FCM লেভেল এবং PRODUCT_SHIPPING_API_LEVEL টার্গেট করে, যার জন্য এটিকে পরবর্তী রিলিজের ভেন্ডর সফটওয়্যার রিকোয়ারমেন্টস (VSR) পূরণ করতে হয়।
যখন RELEASE_AIDL_USE_UNFROZEN false হয়, তখন Cuttlefish একটি রিলিজ ডিভাইসকে প্রতিফলিত করার জন্য পূর্ববর্তী target-level এবং PRODUCT_SHIPPING_API_LEVEL ব্যবহার করে। Android 14 এবং এর নিচের সংস্করণগুলোতে, এই পার্থক্যটি বিভিন্ন Git ব্রাঞ্চের মাধ্যমে করা হতো, যেগুলো FCM target-level , শিপিং API লেভেল বা পরবর্তী রিলিজকে লক্ষ্য করে তৈরি অন্য কোনো কোডের পরিবর্তন গ্রহণ করত না।
মডিউল নামকরণের নিয়মাবলী
অ্যান্ড্রয়েড ১১-এ, সক্রিয় করা ভার্সন এবং ব্যাকএন্ডগুলোর প্রতিটি সমন্বয়ের জন্য স্বয়ংক্রিয়ভাবে একটি স্টাব লাইব্রেরি মডিউল তৈরি হয়। লিঙ্কিংয়ের জন্য কোনো নির্দিষ্ট স্টাব লাইব্রেরি মডিউলকে উল্লেখ করতে, aidl_interface মডিউলের নামটি ব্যবহার না করে স্টাব লাইব্রেরি মডিউলের নামটি ব্যবহার করুন, যা হলো ifacename - version - backend , যেখানে
-
ifacename:aidl_interfaceমডিউলের নাম -
versionহয়- হিমায়িত সংস্করণগুলির জন্য
V version-number -
V latest-frozen-version-number + 1যা এখনো-স্থিরীকৃত-না-হওয়া-সংস্করণের-শীর্ষভাগের জন্য।
- হিমায়িত সংস্করণগুলির জন্য
-
backendহলো হয়- জাভা ব্যাকএন্ডের জন্য
java, - C++ ব্যাকএন্ডের জন্য
cpp, - NDK ব্যাকএন্ডের জন্য
ndkঅথবাndk_platform। অ্যান্ড্রয়েড ১৩ পর্যন্ত, প্রথমটি অ্যাপের জন্য এবং দ্বিতীয়টি প্ল্যাটফর্ম ব্যবহারের জন্য ছিল। অ্যান্ড্রয়েড ১৩ এবং এর পরবর্তী সংস্করণগুলোতে শুধুndkব্যবহার করুন। - রাস্ট ব্যাকএন্ডের জন্য
rust।
- জাভা ব্যাকএন্ডের জন্য
ধরে নিন, ‘foo’ নামের একটি মডিউল আছে যার সর্বশেষ সংস্করণ হলো 2 এবং এটি NDK ও C++ উভয়কেই সমর্থন করে। এক্ষেত্রে, AIDL এই মডিউলগুলো তৈরি করে:
- সংস্করণ ১ এর উপর ভিত্তি করে
-
foo-V1-(java|cpp|ndk|ndk_platform|rust)
-
- সংস্করণ ২ (সর্বশেষ স্থিতিশীল সংস্করণ) এর উপর ভিত্তি করে
-
foo-V2-(java|cpp|ndk|ndk_platform|rust)
-
- ToT সংস্করণের উপর ভিত্তি করে
-
foo-V3-(java|cpp|ndk|ndk_platform|rust)
-
অ্যান্ড্রয়েড ১১ এর তুলনায়:
-
foo- backend, যা সর্বশেষ স্থিতিশীল সংস্করণকে নির্দেশ করত, তাfoo- V2 - backendপরিণত হয়। -
foo-unstable- backend, যা ToT সংস্করণকে নির্দেশ করত, তাfoo- V3 - backendপরিণত হয়।
আউটপুট ফাইলের নামগুলো সর্বদা মডিউলের নামের মতোই হয়।
- সংস্করণ ১ এর উপর ভিত্তি করে:
foo-V1-(cpp|ndk|ndk_platform|rust).so - সংস্করণ ২ এর উপর ভিত্তি করে:
foo-V2-(cpp|ndk|ndk_platform|rust).so - ToT সংস্করণের উপর ভিত্তি করে:
foo-V3-(cpp|ndk|ndk_platform|rust).so
উল্লেখ্য যে, AIDL কম্পাইলার কোনো স্থিতিশীল AIDL ইন্টারফেসের জন্য unstable সংস্করণ মডিউল বা সংস্করণবিহীন মডিউল তৈরি করে না। অ্যান্ড্রয়েড ১২ থেকে, একটি স্থিতিশীল AIDL ইন্টারফেস থেকে তৈরি মডিউলের নামে সর্বদা এর সংস্করণ অন্তর্ভুক্ত থাকে।
নতুন মেটা ইন্টারফেস পদ্ধতি
অ্যান্ড্রয়েড ১০ স্থিতিশীল AIDL-এর জন্য বেশ কয়েকটি মেটা ইন্টারফেস মেথড যুক্ত করেছে।
রিমোট অবজেক্টের ইন্টারফেস সংস্করণটি কোয়েরি করুন
ক্লায়েন্টরা রিমোট অবজেক্টটি যে ইন্টারফেসটি ইমপ্লিমেন্ট করছে তার ভার্সন ও হ্যাশ কোয়েরি করতে পারে এবং প্রাপ্ত মানগুলোকে ক্লায়েন্টের ব্যবহৃত ইন্টারফেসের মানগুলোর সাথে তুলনা করতে পারে।
cpp ব্যাকএন্ড সহ উদাহরণ:
sp<IFoo> foo = ... // the remote object
int32_t my_ver = IFoo::VERSION;
int32_t remote_ver = foo->getInterfaceVersion();
if (remote_ver < my_ver) {
// the remote side is using an older interface
}
std::string my_hash = IFoo::HASH;
std::string remote_hash = foo->getInterfaceHash();
ndk (এবং ndk_platform ) ব্যাকএন্ডের উদাহরণ:
IFoo* foo = ... // the remote object
int32_t my_ver = IFoo::version;
int32_t remote_ver = 0;
if (foo->getInterfaceVersion(&remote_ver).isOk() && remote_ver < my_ver) {
// the remote side is using an older interface
}
std::string my_hash = IFoo::hash;
std::string remote_hash;
foo->getInterfaceHash(&remote_hash);
java ব্যাকএন্ড সহ উদাহরণ:
IFoo foo = ... // the remote object
int myVer = IFoo.VERSION;
int remoteVer = foo.getInterfaceVersion();
if (remoteVer < myVer) {
// the remote side is using an older interface
}
String myHash = IFoo.HASH;
String remoteHash = foo.getInterfaceHash();
জাভা ভাষার ক্ষেত্রে, রিমোট সাইডকে অবশ্যই getInterfaceVersion() এবং getInterfaceHash() নিম্নোক্তভাবে ইমপ্লিমেন্ট করতে হবে (কপি-পেস্টের ভুল এড়ানোর জন্য IFoo এর পরিবর্তে super ব্যবহার করা হয়েছে। javac কনফিগারেশনের উপর নির্ভর করে, ওয়ার্নিং নিষ্ক্রিয় করার জন্য @SuppressWarnings("static") অ্যানোটেশনটির প্রয়োজন হতে পারে):
class MyFoo extends IFoo.Stub {
@Override
public final int getInterfaceVersion() { return super.VERSION; }
@Override
public final String getInterfaceHash() { return super.HASH; }
}
এর কারণ হলো, জেনারেটেড ক্লাসগুলো ( IFoo , IFoo.Stub , ইত্যাদি) ক্লায়েন্ট এবং সার্ভারের মধ্যে শেয়ার করা হয় (উদাহরণস্বরূপ, ক্লাসগুলো বুট ক্লাসপাথে থাকতে পারে)। যখন ক্লাসগুলো শেয়ার করা হয়, তখন সার্ভারটিও ক্লাসগুলোর নতুনতম সংস্করণের সাথে লিঙ্ক করা থাকে, যদিও এটি ইন্টারফেসের একটি পুরোনো সংস্করণ দিয়ে বিল্ড করা হয়ে থাকতে পারে। যদি এই মেটা ইন্টারফেসটি শেয়ার করা ক্লাসে ইমপ্লিমেন্ট করা হয়, তবে এটি সর্বদা নতুনতম সংস্করণটি রিটার্ন করে। তবে, উপরে বর্ণিত পদ্ধতিতে মেথডটি ইমপ্লিমেন্ট করার মাধ্যমে, ইন্টারফেসের ভার্সন নম্বরটি সার্ভারের কোডে এমবেড হয়ে যায় (কারণ IFoo.VERSION একটি static final int যা রেফারেন্স করার সময় ইনলাইন হয়ে যায়) এবং এর ফলে মেথডটি ঠিক সেই সংস্করণটিই রিটার্ন করতে পারে যা দিয়ে সার্ভারটি বিল্ড করা হয়েছিল।
পুরানো ইন্টারফেসগুলির সাথে মোকাবিলা করুন
এমনটা হতে পারে যে, কোনো ক্লায়েন্টকে AIDL ইন্টারফেসের নতুন সংস্করণ দিয়ে আপডেট করা হয়েছে, কিন্তু সার্ভারটি পুরোনো AIDL ইন্টারফেসটিই ব্যবহার করছে। এই ধরনের ক্ষেত্রে, পুরোনো ইন্টারফেসের কোনো মেথড কল করলে UNKNOWN_TRANSACTION রিটার্ন হয়।
স্টেবল AIDL-এর মাধ্যমে ক্লায়েন্টদের আরও বেশি নিয়ন্ত্রণ থাকে। ক্লায়েন্ট সাইডে, আপনি একটি AIDL ইন্টারফেসের জন্য একটি ডিফল্ট ইমপ্লিমেন্টেশন সেট করতে পারেন। ডিফল্ট ইমপ্লিমেন্টেশনের কোনো মেথড তখনই কল করা হয়, যখন মেথডটি রিমোট সাইডে ইমপ্লিমেন্ট করা থাকে না (কারণ এটি ইন্টারফেসটির একটি পুরোনো সংস্করণ দিয়ে বিল্ড করা হয়েছিল)। যেহেতু ডিফল্টগুলো গ্লোবালি সেট করা হয়, তাই সম্ভাব্য শেয়ার্ড কনটেক্সট থেকে এগুলো ব্যবহার করা উচিত নয়।
অ্যান্ড্রয়েড ১৩ এবং পরবর্তী সংস্করণগুলিতে C++ এ উদাহরণ:
class MyDefault : public IFooDefault {
Status anAddedMethod(...) {
// do something default
}
};
// once per an interface in a process
IFoo::setDefaultImpl(::android::sp<MyDefault>::make());
foo->anAddedMethod(...); // MyDefault::anAddedMethod() will be called if the
// remote side is not implementing it
জাভাতে উদাহরণ:
IFoo.Stub.setDefaultImpl(new IFoo.Default() {
@Override
public xxx anAddedMethod(...) throws RemoteException {
// do something default
}
}); // once per an interface in a process
foo.anAddedMethod(...);
একটি AIDL ইন্টারফেসে সমস্ত মেথডের ডিফল্ট ইমপ্লিমেন্টেশন দেওয়ার প্রয়োজন নেই। যে মেথডগুলো রিমোট সাইডে ইমপ্লিমেন্ট করা হবে বলে নিশ্চিত থাকা যায় (কারণ আপনি নিশ্চিত যে মেথডগুলো AIDL ইন্টারফেসের বর্ণনায় থাকার সময়েই রিমোটটি বিল্ড করা হয়েছে), সেগুলোকে ডিফল্ট impl ক্লাসে ওভাররাইড করার প্রয়োজন নেই।
বিদ্যমান AIDL-কে কাঠামোগত বা স্থিতিশীল AIDL-এ রূপান্তর করুন
আপনার যদি আগে থেকে কোনো AIDL ইন্টারফেস এবং সেটি ব্যবহারকারী কোড থাকে, তবে ইন্টারফেসটিকে একটি স্টেবল AIDL ইন্টারফেসে রূপান্তর করতে নিম্নলিখিত ধাপগুলো অনুসরণ করুন।
আপনার ইন্টারফেসের সমস্ত নির্ভরতা শনাক্ত করুন। ইন্টারফেসটি যেসব প্যাকেজের উপর নির্ভর করে, সেগুলোর প্রত্যেকটি স্থিতিশীল AIDL-এ সংজ্ঞায়িত আছে কিনা তা নির্ধারণ করুন। যদি সংজ্ঞায়িত না থাকে, তবে প্যাকেজটিকে অবশ্যই রূপান্তর করতে হবে।
আপনার ইন্টারফেসের সমস্ত পার্সেলএবলকে স্টেবল পার্সেলএবলে রূপান্তর করুন (ইন্টারফেস ফাইলগুলো অপরিবর্তিত থাকতে পারে)। এটি করার জন্য, AIDL ফাইলে সরাসরি তাদের কাঠামো প্রকাশ করুন। এই নতুন টাইপগুলো ব্যবহার করার জন্য ম্যানেজমেন্ট ক্লাসগুলোকে নতুন করে লিখতে হবে। এটি একটি
aidl_interfaceপ্যাকেজ (নিচে) তৈরি করার আগেই করা যেতে পারে।উপরে বর্ণিত পদ্ধতি অনুসারে একটি
aidl_interfaceপ্যাকেজ তৈরি করুন, যাতে আপনার মডিউলের নাম, এর নির্ভরতা এবং আপনার প্রয়োজনীয় অন্যান্য তথ্য থাকবে। এটিকে স্থিতিশীল (শুধু কাঠামোবদ্ধ নয়) করার জন্য, এর ভার্সনিংও করা প্রয়োজন। আরও তথ্যের জন্য, ‘ইন্টারফেসের ভার্সনিং’ দেখুন।