Triển khai các thuộc tính hệ thống dưới dạng API

Thuộc tính hệ thống cung cấp một cách thuận tiện để chia sẻ thông tin, thường là cấu hình, trên toàn hệ thống. Mỗi phân vùng có thể sử dụng các thuộc tính hệ thống riêng của nó trong nội bộ. Sự cố có thể xảy ra khi các thuộc tính được truy cập trên các phân vùng, chẳng hạn như /vendor truy cập các thuộc tính /system xác định. Kể từ Android 8.0, một số phân vùng, chẳng hạn như /system , có thể được nâng cấp, trong khi /vendor không thay đổi. Bởi vì các thuộc tính hệ thống chỉ là một từ điển toàn cục gồm các cặp khóa/giá trị chuỗi không có lược đồ nên rất khó để ổn định các thuộc tính. Phân vùng /system có thể thay đổi hoặc loại bỏ các thuộc tính mà phân vùng /vendor phụ thuộc vào mà không cần thông báo.

Bắt đầu từ bản phát hành Android 10, các thuộc tính hệ thống được truy cập trên các phân vùng sẽ được sơ đồ hóa thành các tệp Mô tả Sysprop và các API để truy cập các thuộc tính được tạo dưới dạng các hàm cụ thể cho C++ và Rust cũng như các lớp cho Java. Các API này thuận tiện hơn khi sử dụng vì không cần chuỗi ma thuật (chẳng hạn như ro.build.date ) để truy cập và vì chúng có thể được nhập tĩnh. Độ ổn định của ABI cũng được kiểm tra tại thời điểm xây dựng và quá trình xây dựng sẽ bị hỏng nếu xảy ra những thay đổi không tương thích. Việc kiểm tra này hoạt động như các giao diện được xác định rõ ràng giữa các phân vùng. Các API này cũng có thể cung cấp tính nhất quán giữa Rust, Java và C++.

Xác định thuộc tính hệ thống dưới dạng API

Xác định các thuộc tính hệ thống dưới dạng API với các tệp Mô tả Sysprop ( .sysprop ), sử dụng TextFormat của protobuf, với lược đồ sau:

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

Một tệp Mô tả Sysprop chứa một thông báo thuộc tính, mô tả một tập hợp các thuộc tính. Ý nghĩa các trường của nó như sau.

Cánh đồng Nghĩa
owner Đặt thành phân vùng sở hữu các thuộc tính: Platform , Vendor hoặc Odm .
module Được sử dụng để tạo một không gian tên (C++) hoặc lớp cuối cùng tĩnh (Java) trong đó đặt các API đã tạo. Ví dụ: com.android.sysprop.BuildProperties sẽ là không gian tên com::android::sysprop::BuildProperties trong C++ và lớp BuildProperties trong gói trong com.android.sysprop trong Java.
prop Danh sách tài sản.

Ý nghĩa của các trường thông báo Property như sau.

Cánh đồng Nghĩa
api_name Tên của API được tạo.
type Loại tài sản này.
access Readonly : Chỉ tạo API getter

Writeonce , ReadWrite : Tạo API getter và setter

Lưu ý: Thuộc tính có tiền tố ro. không thể sử dụng quyền truy cập ReadWrite .

scope Internal : Chỉ chủ sở hữu mới có thể truy cập.

Public : Mọi người đều có thể truy cập, ngoại trừ các mô-đun NDK.

prop_name Tên của thuộc tính hệ thống cơ bản, ví dụ ro.build.date .
enum_values ( Enum , chỉ EnumList ) Một chuỗi được phân tách bằng thanh(|) bao gồm các giá trị enum có thể có. Ví dụ: value1|value2 .
integer_as_bool ( Chỉ Boolean , BooleanList ) Yêu cầu setters sử dụng 01 thay vì falsetrue .
legacy_prop_name (tùy chọn, chỉ thuộc tính Readonly ) Tên kế thừa của thuộc tính hệ thống cơ bản. Khi gọi getter, API getter sẽ cố đọc prop_name và sử dụng legacy_prop_name nếu prop_name không tồn tại. Sử dụng legacy_prop_name khi ngừng sử dụng thuộc tính hiện có và chuyển sang thuộc tính mới.

Mỗi loại thuộc tính ánh xạ tới các loại sau trong C++, Java và Rust.

Kiểu C++ Java rỉ sét
Boolean std::optional<bool> Optional<Boolean> bool
số nguyên std::optional<std::int32_t> Optional<Integer> i32
UInt std::optional<std::uint32_t> Optional<Integer> u32
Dài std::optional<std::int64_t> Optional<Long> i64
ULong std::optional<std::uint64_t> Optional<Long> u64
Gấp đôi std::optional<double> Optional<Double> f64
Sợi dây std::optional<std::string> Optional<String> String
liệt kê std::optional<{api_name}_values> Optional<{api_name}_values> {ApiName}Values
Danh sách T std::vector<std::optional<T>> List<T> Vec<T>

Dưới đây là ví dụ về tệp Mô tả Sysprop xác định ba thuộc tính:

# 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
}

Xác định thư viện thuộc tính hệ thống

Bây giờ bạn có thể xác định các mô-đun sysprop_library bằng các tệp Mô tả Sysprop. sysprop_library đóng vai trò là API cho C++, Java và Rust. Hệ thống xây dựng tạo nội bộ một rust_library , một java_library và một cc_library cho mỗi phiên bản của sysprop_library .

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

Bạn phải đưa các tệp danh sách API vào nguồn để kiểm tra API. Để thực hiện việc này, hãy tạo tệp API và thư mục api . Đặt thư mục api vào cùng thư mục với Android.bp . Tên tệp API là <module_name>-current.txt , <module_name>-latest.txt . <module_name>-current.txt chứa chữ ký API của mã nguồn hiện tại và <module_name>-latest.txt chứa chữ ký API cố định mới nhất. Hệ thống xây dựng kiểm tra xem các API có bị thay đổi hay không bằng cách so sánh các tệp API này với các tệp API được tạo tại thời điểm xây dựng và đưa ra thông báo lỗi cũng như hướng dẫn cập nhật tệp current.txt nếu current.txt không khớp với mã nguồn. Đây là một ví dụ về tổ chức thư mục và tập tin:

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

Các mô-đun máy khách Rust, Java và C++ có thể liên kết với sysprop_library để sử dụng các API được tạo. Hệ thống xây dựng tạo liên kết từ máy khách đến các thư viện C++, Java và Rust được tạo, do đó cấp cho máy khách quyền truy cập vào các API được tạo.

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"],
}

Lưu ý rằng tên thư viện Rust được tạo bằng cách chuyển đổi tên sysprop_library thành chữ thường, thay thế .- với _ , sau đó thêm lib và thêm _rust .

Trong ví dụ trên, bạn có thể truy cập các thuộc tính được xác định như sau.

Ví dụ rỉ sét:

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

  …
}

Ví dụ 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
        );
    }
    …
}
…

Ví dụ về 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);
    }
    …
}
…