सिस्टम प्रॉपर्टी को एपीआई के तौर पर लागू करना

सिस्टम प्रॉपर्टी, सिस्टम-वाइड कॉन्फ़िगरेशन जैसी जानकारी शेयर करने का एक आसान तरीका उपलब्ध कराती हैं. हर पार्टीशन, अपनी सिस्टम प्रॉपर्टी का इस्तेमाल अंदरूनी तौर पर कर सकता है. जब प्रॉपर्टी को अलग-अलग पार्टीशन में ऐक्सेस किया जाता है, तो समस्या हो सकती है. जैसे, /vendor से तय की गई प्रॉपर्टी ऐक्सेस करना./system Android 8.0 के बाद, /system जैसे कुछ पार्टीशन को अपग्रेड किया जा सकता है. हालांकि, /vendor में कोई बदलाव नहीं किया जाता. सिस्टम प्रॉपर्टी, स्ट्रिंग की वैल्यू के जोड़े की एक ग्लोबल डिक्शनरी होती हैं. इनमें कोई स्कीमा नहीं होता. इसलिए, प्रॉपर्टी को स्थिर करना मुश्किल होता है. /system partition, /vendor partition पर निर्भर प्रॉपर्टी को बिना किसी सूचना के बदल सकता है या हटा सकता है.

Android 10 रिलीज़ से, सभी पार्टीशन में ऐक्सेस की गई सिस्टम प्रॉपर्टी को Sysprop Description फ़ाइलों में स्कीमा के तौर पर सेट किया जाता है. साथ ही, प्रॉपर्टी को ऐक्सेस करने के लिए, C++ और Rust के लिए कंसक्रिट फ़ंक्शन और Java के लिए क्लास के तौर पर एपीआई जनरेट किए जाते हैं. इन एपीआई को इस्तेमाल करना ज़्यादा आसान होता है, क्योंकि इन्हें ऐक्सेस करने के लिए ro.build.date जैसी कोई मैजिक स्ट्रिंग ज़रूरी नहीं होती. साथ ही, इन्हें स्टैटिक टाइप किया जा सकता है. बिल्ड के समय, एबीआई के स्थिर होने की जांच भी की जाती है. साथ ही, अगर कोई ऐसा बदलाव होता है जो काम नहीं करता है, तो बिल्ड रुक जाता है. यह जांच, अलग-अलग सेगमेंट के बीच साफ़ तौर पर तय किए गए इंटरफ़ेस के तौर पर काम करती है. ये एपीआई, Rust, Java, और C++ के बीच भी एक जैसी सुविधाएं दे सकते हैं.

सिस्टम प्रॉपर्टी को एपीआई के तौर पर तय करना

Sysprop Description फ़ाइलों (.sysprop) की मदद से, सिस्टम प्रॉपर्टी को एपीआई के तौर पर तय करें. ये फ़ाइलें, 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 इसका इस्तेमाल नेमस्पेस (C++) या स्टैटिक फ़ाइनल क्लास (Java) बनाने के लिए किया जाता है, जिसमें जनरेट किए गए एपीआई डाले जाते हैं. उदाहरण के लिए, com.android.sysprop.BuildProperties C++ में नेमस्पेस com::android::sysprop::BuildProperties होगा, और Java में पैकेज में BuildProperties क्लास, com.android.sysprop होगी.
prop प्रॉपर्टी की सूची.

Property मैसेज फ़ील्ड का मतलब यहां बताया गया है.

फ़ील्ड मतलब
api_name जनरेट किए गए एपीआई का नाम.
type इस प्रॉपर्टी का टाइप.
access Readonly: सिर्फ़ गेट्टर एपीआई जनरेट करता है
Writeonce, ReadWrite: गेट्टर और सेटर एपीआई जनरेट करता है
ध्यान दें: ro. प्रीफ़िक्स वाली प्रॉपर्टी, ReadWrite ऐक्सेस का इस्तेमाल नहीं कर सकतीं.
scope Internal: सिर्फ़ मालिक ही ऐक्सेस कर सकता है.
Public: एनडीके मॉड्यूल को छोड़कर, सभी लोग ऐक्सेस कर सकते हैं.
prop_name सिस्टम प्रॉपर्टी का नाम, जैसे कि ro.build.date.
enum_values (सिर्फ़ Enum, EnumList) बार(|) से अलग की गई स्ट्रिंग, जिसमें EnumList के लिए वैल्यू शामिल होती हैं. उदाहरण के लिए, value1|value2.
integer_as_bool (सिर्फ़ Boolean, BooleanList के लिए) सेटर को false और true के बजाय, 0 और 1 का इस्तेमाल करने के लिए कहें.
legacy_prop_name (ज़रूरी नहीं, सिर्फ़ Readonly प्रॉपर्टी के लिए) मौजूदा सिस्टम प्रॉपर्टी का लेगसी नाम. getter को कॉल करते समय, getter API, prop_name को पढ़ने की कोशिश करता है. अगर prop_name मौजूद नहीं है, तो legacy_prop_name का इस्तेमाल किया जाता है. किसी मौजूदा प्रॉपर्टी को बंद करके, नई प्रॉपर्टी पर स्विच करते समय legacy_prop_name का इस्तेमाल करें.

हर तरह की प्रॉपर्टी, C++, Java, और Rust में इन टाइप से मैप होती है.

टाइप C++ Java रस्ट
बूलियन std::optional<bool> Optional<Boolean> bool
पूर्णांक std::optional<std::int32_t> Optional<Integer> i32
UInt std::optional<std::uint32_t> Optional<Integer> u32
ज़्यादा समय के लिए std::optional<std::int64_t> Optional<Long> i64
ULong std::optional<std::uint64_t> Optional<Long> u64
डबल std::optional<double> Optional<Double> f64
स्ट्रिंग std::optional<std::string> Optional<String> String
Enum std::optional<{api_name}_values> Optional<{api_name}_values> {ApiName}Values
टी सूची std::vector<std::optional<T>> List<T> Vec<T>

यहां तीन प्रॉपर्टी की जानकारी देने वाली Sysprop Description फ़ाइल का उदाहरण दिया गया है:

# 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 के लिए एपीआई के तौर पर काम करता है. बिल्ड सिस्टम, 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 डायरेक्ट्री को Android.bp डायरेक्ट्री में डालें. एपीआई के फ़ाइल नाम <module_name>-current.txt, <module_name>-latest.txt हैं. <module_name>-current.txt में मौजूदा सोर्स कोड के एपीआई हस्ताक्षर होते हैं और <module_name>-latest.txt में, फ़्रीज़ किए गए नए एपीआई हस्ताक्षर होते हैं. बिल्ड सिस्टम, इन एपीआई फ़ाइलों की तुलना जनरेट की गई एपीआई फ़ाइलों से करके यह जांच करता है कि एपीआई में बदलाव हुआ है या नहीं. साथ ही, अगर current.txt, सोर्स कोड से मेल नहीं खाता है, तो गड़बड़ी का मैसेज और current.txt फ़ाइल को अपडेट करने के निर्देश दिखाता है. यहां डायरेक्ट्री और फ़ाइल व्यवस्थित करने का उदाहरण दिया गया है:

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

जनरेट किए गए एपीआई का इस्तेमाल करने के लिए, Rust, Java, और C++ क्लाइंट मॉड्यूल, sysprop_library के साथ लिंक किए जा सकते हैं. बिल्ड सिस्टम, क्लाइंट से जनरेट की गई C++, Java, और Rust लाइब्रेरी को लिंक करता है. इससे क्लाइंट को जनरेट किए गए एपीआई का ऐक्सेस मिलता है.

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 लाइब्रेरी का नाम, sysprop_library नाम को छोटे अक्षरों में बदलकर जनरेट किया जाता है. इसके बाद, . और - को _ से बदला जाता है. इसके बाद, lib को पहले और _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);
    }
    
}