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

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

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

تحديد خصائص النظام كواجهات برمجة التطبيقات (APIs).

قم بتعريف خصائص النظام كواجهات برمجة التطبيقات (APIs) مع ملفات وصف Sysprop ( .sysprop )، والتي تستخدم تنسيق TextFormat الخاص بـ protobuf، مع المخطط التالي:

// 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 : يُنشئ واجهة برمجة تطبيقات getter فقط

Writeonce و ReadWrite : يُنشئ واجهات برمجة تطبيقات getter وsetter

ملحوظة: الخصائص ذات البادئة ro. لا يجوز استخدام الوصول 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.

يكتب سي ++ جافا الصدأ
منطقية 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
التعداد std::optional<{api_name}_values> Optional<{api_name}_values> {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). للقيام بذلك، قم بإنشاء ملفات 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 التي تم إنشاؤها في وقت الإنشاء ويصدر رسالة خطأ وتعليمات لتحديث ملف 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: ["PlatformProperties"],
}

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

لاحظ أنه يتم إنشاء اسم مكتبة Rust عن طريق تحويل اسم sysprop_library إلى أحرف صغيرة، واستبدال . و - مع _ ، ثم إضافة lib وإلحاق _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);
  }

  …
}

مثال جافا:

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