API হিসেবে সিস্টেম প্রপার্টি প্রয়োগ করা

সিস্টেম প্রপার্টি (sysprops) হল তথ্য শেয়ার করার একটি সুবিধাজনক উপায়, সাধারণত কনফিগারেশন, সিস্টেম-ওয়াইড। প্রতিটি পার্টিশন ইন্টার্নালি নিজস্ব সিস্টেম প্রপার্টি ব্যবহার করতে পারে। /vendor-এর মতো পার্টিশন জুড়ে অ্যাক্সেস করা হলে, /system-এর মতো প্রপার্টিতে সমস্যা হতে পারে। Android 8.0 থেকে, কিছু পার্টিশন, যেমন /system, আপগ্রেড করা যেতে পারে, যদিও /vendor অপরিবর্তিত থাকে। সিস্টেম প্রপার্টি হল কোনও স্কিমা ছাড়াই স্ট্রিং কী-ভ্যালু পেয়ারের একটি গ্লোবাল ডিকশনারি, তাই প্রপার্টি স্থিতিশীল করা কঠিন। /system পার্টিশনটি কোনও বিজ্ঞপ্তি ছাড়াই এমন প্রপার্টি পরিবর্তন বা সরিয়ে দিতে পারে যেটির উপর /vendor পার্টিশন নির্ভর করে।

Android 10 ও তার পরবর্তী যেকোনও ভার্সনে, পার্টিশন জুড়ে অ্যাক্সেস করা সিস্টেম প্রপার্টি sysprop বিবরণ ফাইলে স্কিমাটাইজ করা হয় এবং প্রপার্টি অ্যাক্সেস করার API, C++ ও Rust-এর জন্য কনক্রিট ফাংশন হিসেবে জেনারেট করা হয় এবং Java-এর জন্য ক্লাস হিসেবে জেনারেট করা হয়। এইসব API ব্যবহার করা আরও সুবিধাজনক কারণ অ্যাক্সেসের জন্য কোনও ম্যাজিক স্ট্রিংয়ের (যেমন ro.build.date) প্রয়োজন হয় না এবং এগুলি স্ট্যাটিক্যালি টাইপ করা যায়। বিল্ডের সময় ABI স্টেবিলিটিও চেক করা হয় এবং অসামঞ্জস্যপূর্ণ পরিবর্তন হলে বিল্ড ভেঙে যায়। এই চেকটি পার্টিশনের মধ্যে স্পষ্টভাবে সংজ্ঞায়িত ইন্টারফেস হিসেবে কাজ করে। এছাড়াও, এই API-গুলি Rust, Java ও C++-এর মধ্যে সামঞ্জস্য প্রদান করতে পারে।

API হিসেবে সিস্টেম প্রপার্টি সংজ্ঞায়িত করুন

Sysprop বিবরণ ফাইল (.sysprop) সহ API হিসেবে সিস্টেম প্রপার্টিকে সংজ্ঞায়িত করুন, যা নিম্নলিখিত স্কিমা সহ protobuf-এর TextFormat ব্যবহার করে:

// File: sysprop.proto

syntax = "proto3";

package sysprop;

enum Access {
  Readonly = 0;
  Writeonce = 1;
  ReadWrite = 2;
}

enum Owner {
  Platform = 0;
  Vendor = 1;
  Odm = 2;
}

enum Scope {
  Public = 0;
  Internal = 2;
}

enum Type {
  Boolean = 0;
  Integer = 1;
  Long = 2;
  Double = 3;
  String = 4;
  Enum = 5;
  UInt = 6;
  ULong = 7;

  BooleanList = 20;
  IntegerList = 21;
  LongList = 22;
  DoubleList = 23;
  StringList = 24;
  EnumList = 25;
  UIntList = 26;
  ULongList = 27;
}

message Property {
  string api_name = 1;
  Type type = 2;
  Access access = 3;
  Scope scope = 4;
  string prop_name = 5;
  string enum_values = 6;
  bool integer_as_bool = 7;
  string legacy_prop_name = 8;
}

message Properties {
  Owner owner = 1;
  string module = 2;
  repeated Property prop = 3;
}

sysprop বিবরণ ফাইলে একটি প্রপার্টি মেসেজ থাকে, যা প্রপার্টির সেট বর্ণনা করে। এর ফিল্ডের অর্থ নিচে দেওয়া হল:

ফিল্ড অর্থ
owner যে পার্টিশনের প্রপার্টি আছে সেটিতে সেট করুন: platform, vendor অথবা odm.
module যেখানে জেনারেট করা API রাখা হয়, সেখানে নেমস্পেস (C++) বা স্ট্যাটিক ফাইনাল ক্লাস (Java) তৈরি করতে ব্যবহৃত হয়। যেমন, com.android.sysprop.BuildProperties হল C++-এ নেমস্পেস com::android::sysprop::BuildProperties, এবং Java-তে com.android.sysprop-এর প্যাকেজে BuildProperties ক্লাস।
prop প্রপার্টির তালিকা।

Property মেসেজ ফিল্ডের অর্থ নিচে দেওয়া হল:

ফিল্ড অর্থ
api_name জেনারেট করা API-এর নাম।
type এই প্রপার্টির ধরন।
access Readonly: শুধুমাত্র গেটার API তৈরি করে
Writeonce, ReadWrite: গেটার ও সেটার API তৈরি করে
scope Internal: শুধু মালিক অ্যাক্সেস করতে পারবেন।
Public: NDK মডিউল ছাড়া, প্রত্যেকে অ্যাক্সেস করতে পারবেন।
prop_name অন্তর্নিহিত সিস্টেম প্রপার্টির নাম, যেমন ro.build.date.
enum_values (Enum, শুধুমাত্র EnumList) সম্ভাব্য enum ভ্যালু নিয়ে গঠিত একটি বার(|) দ্বারা আলাদা করা স্ট্রিং। যেমন, value1|value2।
integer_as_bool (শুধুমাত্র Boolean, BooleanList) সেটারদের false ও true-এর পরিবর্তে 0 ও 1 ব্যবহার করতে বলুন।
legacy_prop_name (ঐচ্ছিক, শুধুমাত্র Readonly প্রপার্টি) অন্তর্নিহিত সিস্টেম প্রপার্টির পুরনো নাম। গেটারকে কল করার সময়, গেটার API prop_name রিড করার চেষ্টা করে এবং legacy_prop_name ব্যবহার করে যদি prop_name না থাকে। আগে থেকে থাকা প্রপার্টি সরিয়ে দিয়ে নতুন প্রপার্টি ব্যবহার করার সময় legacy_prop_name ব্যবহার করুন।

C++, Java ও Rust-এ প্রতিটি প্রপার্টির ধরন নিম্নলিখিত ধরনের সাথে ম্যাপ করা হয়:

ধরন C++ (নালেবেল) Java (নালেবেল) Rust (ঐচ্ছিক বা নাল হতে পারে)
বুলিয়ান std::optional<bool> Optional<Boolean> Option<bool>
পূর্ণসংখ্যা std::optional<std::int32_t> Optional<Integer> Option<i32>
UInt std::optional<std::uint32_t> Optional<Integer> Option<u32>
দীর্ঘ std::optional<std::int64_t> Optional<Long> Option<i64>
ULong std::optional<std::uint64_t> Optional<Long> Option<u64>
ডবল std::optional<double> Optional<Double> Option<f64>
স্ট্রিং std::optional<std::string> Optional<String> Option<String>
Enum std::optional<{api\_name}\_values> Optional<{api\_name}\_values> Option<{ApiName}Values>
T List std::vector<std::optional<T>> List<T> Vec<T>

এখানে Sysprop বিবরণ ফাইলের একটি উদাহরণ দেওয়া হল যা তিনটি প্রপার্টি সংজ্ঞায়িত করে:

# File: android/sysprop/PlatformProperties.sysprop

owner: Platform
module: "android.sysprop.PlatformProperties"
prop {
    api_name: "build_date"
    type: String
    prop_name: "ro.build.date"
    scope: Public
    access: Readonly
}
prop {
    api_name: "date_utc"
    type: Integer
    prop_name: "ro.build.date_utc"
    scope: Internal
    access: Readonly
}
prop {
    api_name: "device_status"
    type: Enum
    enum_values: "on|off|unknown"
    prop_name: "device.status"
    scope: Public
    access: ReadWrite
}

সিস্টেম প্রপার্টি লাইব্রেরি সম্পর্কে সংজ্ঞা দাও

আপনি Sysprop বিবরণ ফাইলের মাধ্যমে sysprop_library মডিউল সংজ্ঞায়িত করতে পারবেন। sysprop_library হল C++, Java ও Rust-এর জন্য API। বিল্ড সিস্টেম sysprop_library-এর প্রতিটি ইনস্ট্যান্সের জন্য ইন্টার্নালি একটি rust_library, একটি java_library এবং একটি cc_library জেনারেট করে।

// File: Android.bp
sysprop_library {
    name: "PlatformProperties",
    srcs: ["android/sysprop/PlatformProperties.sysprop"],
    property_owner: "Platform",
    vendor_available: true,
}

API চেক করার জন্য সোর্সে API তালিকা ফাইল যোগ করতে হবে। এটি করতে, API ফাইল ও api ডিরেক্টরি তৈরি করুন। api ডিরেক্টরিকে Android.bp-এর মতো একই ডিরেক্টরিতে রাখুন। API ফাইলের নাম হল <module_name>-current.txt এবং <module_name>-latest.txt। <module_name>-current.txt-এ বর্তমান সোর্স কোডের API সিগনেচার থাকে এবং <module_name>-latest.txt-এ লেটেস্ট ফ্রোজেন API সিগনেচার থাকে। বিল্ড সিস্টেম চেক করে দেখে যে API পরিবর্তন করা হয়েছে কিনা। বিল্ড করার সময় জেনারেট করা API ফাইলের সাথে এই API ফাইল তুলনা করে এটি করা হয়। current.txt সোর্স কোডের সাথে ম্যাচ না করলে, এটি একটি ত্রুটি মেসেজ এবং current.txt ফাইল আপডেট করার নির্দেশাবলী দেখায়। এখানে ডিরেক্টরি ও ফাইল সংগঠনের একটি উদাহরণ দেওয়া হল:

├── api
│   ├── PlatformProperties-current.txt
│   └── PlatformProperties-latest.txt
└── Android.bp

জেনারেট করা API ব্যবহার করতে Rust, Java ও C++ ক্লায়েন্ট মডিউল sysprop_library-এর সাথে লিঙ্ক করা যায়। বিল্ড সিস্টেম ক্লায়েন্ট থেকে জেনারেট করা C++, Java ও Rust লাইব্রেরিতে লিঙ্ক তৈরি করে, যার ফলে ক্লায়েন্ট জেনারেট করা API-তে অ্যাক্সেস পায়।

java_library {
    name: "JavaClient",
    srcs: ["foo/bar.java"],
    libs: ["PlatformProperties"],
}

cc_binary {
    name: "cc_client",
    srcs: ["baz.cpp"],
    shared_libs: ["libPlatformProperties"],
}

rust_binary {
    name: "rust_client",
    srcs: ["src/main.rs"],
    rustlibs: ["libplatformproperties_rust"],
}

আগের উদাহরণে, আপনি নিম্নলিখিত উপায়ে নির্দিষ্ট করা প্রপার্টি অ্যাক্সেস করতে পারবেন।

Rust-এর উদাহরণ:

use platformproperties::DeviceStatusValues;

fn foo() -> Result<(), Error> {
  // Read "ro.build.date_utc". default value is -1.
  let date_utc = platformproperties::date_utc()?.unwrap_or_else(-1);

  // set "device.status" to "unknown" if "ro.build.date" is not set.
  if platformproperties::build_date()?.is_none() {
    platformproperties::set_device_status(DeviceStatusValues::UNKNOWN);
  }

  …
}

Java-র উদাহরণ:

import android.sysprop.PlatformProperties;

…

static void foo() {
    …
    // read "ro.build.date_utc". default value is -1
    Integer dateUtc = PlatformProperties.date_utc().orElse(-1);

    // set "device.status" to "unknown" if "ro.build.date" is not set
    if (!PlatformProperties.build_date().isPresent()) {
        PlatformProperties.device_status(
            PlatformProperties.device_status_values.UNKNOWN
        );
    }
    …
}
…

C++ উদাহরণ:

#include <android/sysprop/PlatformProperties.sysprop.h>
using namespace android::sysprop;

…

void bar() {
    …
    // read "ro.build.date". default value is "(unknown)"
    std::string build_date = PlatformProperties::build_date().value_or("(unknown)");

    // set "device.status" to "on" if it's "unknown" or not set
    using PlatformProperties::device_status_values;
    auto status = PlatformProperties::device_status();
    if (!status.has_value() || status.value() == device_status_values::UNKNOWN) {
        PlatformProperties::device_status(device_status_values::ON);
    }
    …
}
…