تنفيذ خصائص النظام كواجهات برمجة تطبيقات

توفّر خصائص النظام (sysprops) طريقة سهلة لمشاركة المعلومات، وعادةً ما تكون عمليات الإعداد على مستوى النظام. يمكن لكل قسم استخدام خصائص النظام الخاصة به داخليًا. قد تحدث مشكلة عند الوصول إلى سمات في أقسام مختلفة، مثل /vendor التي تصل إلى سمات محدّدة في /system. منذ الإصدار Android 8.0، يمكن ترقية بعض الأقسام، مثل /system، مع ترك /vendor بدون تغيير. وبما أنّ خصائص النظام هي مجرد قاموس عام يتضمّن أزواجًا من المفاتيح والقيم النصية بدون مخطط، يصعب تثبيت الخصائص. قد يغيّر القسم /system أو يزيل الخصائص التي يعتمد عليها القسم /vendor بدون أي إشعار.

في نظام التشغيل Android 10 والإصدارات الأحدث، يتم وضع مخطط لخصائص النظام التي يتم الوصول إليها في جميع الأقسام في ملفات وصف sysprop، ويتم إنشاء واجهات برمجة التطبيقات للوصول إلى الخصائص كدوال ملموسة للغة C++ وRust، وكفئات للغة Java. تتميّز واجهات برمجة التطبيقات هذه بسهولة الاستخدام لأنّها لا تتطلّب سلاسل سحرية (مثل ro.build.date) للوصول إليها، ولأنّه يمكن كتابة أنواعها بشكل ثابت. يتم أيضًا التحقّق من ثبات واجهة التطبيق الثنائية (ABI) في وقت الإنشاء، ويتعذّر إنشاء التطبيق إذا حدثت تغييرات غير متوافقة. يعمل هذا الفحص كواجهات محددة بوضوح بين الأقسام. ويمكن أن توفّر واجهات برمجة التطبيقات هذه أيضًا اتساقًا بين Rust وJava وC++.

تحديد سمات النظام على أنّها واجهات برمجة تطبيقات

حدِّد سمات النظام على أنّها واجهات برمجة تطبيقات باستخدام ملفات وصف سمات النظام (.sysprop)، التي تستخدم 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 هي مساحة الاسم com::android::sysprop::BuildProperties في C++‎، والفئة BuildProperties في الحزمة في com.android.sysprop في Java.
prop قائمة بالخصائص

في ما يلي معاني حقول الرسالة Property:

الحقل المعنى
api_name اسم واجهة برمجة التطبيقات التي تم إنشاؤها
type تمثّل هذه السمة نوع الموقع.
access Readonly: لإنشاء واجهة برمجة تطبيقات خاصة بالحصول على البيانات فقط
Writeonce وReadWrite: لإنشاء واجهات برمجة تطبيقات خاصة بالحصول على البيانات وتعديلها
scope Internal: يمكن للمالك فقط الوصول إلى الملف. ‫
Public: يمكن للجميع الوصول إلى هذه الحزمة، باستثناء وحدات NDK.
prop_name اسم سمة النظام الأساسية، مثل ro.build.date.
enum_values (Enum وEnumList فقط) سلسلة مفصولة بعلامة الشَرطة العمودية(|) تتألف من قيم التعداد المحتملة. على سبيل المثال، value1|value2.
integer_as_bool (Boolean وBooleanList فقط) اجعل دوال الضبط تستخدم 0 و1 بدلاً من false وtrue.
legacy_prop_name (اختيارية، لسمات Readonly فقط) الاسم القديم لسمة النظام الأساسية. عند استدعاء دالة getter، تحاول واجهة برمجة التطبيقات الخاصة بدالة getter قراءة 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>
String std::optional<std::string> Optional<String> Option<String>
Enum std::optional<{api\_name}\_values> Optional<{api\_name}\_values> Option<{ApiName}Values>
قائمة T 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_library باستخدام ملفات وصف Sysprop. تعمل sysprop_library كواجهة برمجة تطبيقات للغات C++‎ وJava وRust. ينشئ نظام الإنشاء داخليًا قيمة rust_library واحدة وقيمة java_library واحدة وقيمة cc_library واحدة لكل مثيل من sysprop_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:

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);
    }
    
}