Thuộc tính hệ thống (sysprop) cung cấp một cách thuận tiện để chia sẻ thông tin, thường là các 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 ở bên trong. Vấn đề 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 vào các thuộc tính do /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 vẫn không thay đổ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 khoá-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 xoá 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.
Trong Android 10 trở lên, các thuộc tính hệ thống được truy cập trên các phân vùng được lập sơ đồ thành các tệp mô tả sysprop và các API để truy cập vào 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. 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ị gián đoạn nếu xảy ra các thay đổi không tương thích. Quá trình 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 các 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 bằng 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;
}
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ủa các trường trong tệp này như sau:
| Trườ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
|
Dùng để tạo không gian tên (C++) hoặc lớp cuối cùng tĩnh (Java) nơi
đặt các API được tạo. Ví dụ:
com.android.sysprop.BuildProperties
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 các thuộc tính. |
Ý nghĩa của các trường thông báo Property như sau:
| Trường | Ý nghĩa |
|---|---|
api_name
|
Tên của API được tạo. |
type
|
Loại thuộc tính này. |
access
|
Readonly: Chỉ tạo API getter
Writeonce, ReadWrite: Tạo API getter và setter
|
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, EnumList chỉ) Một chuỗi được phân tách bằng dấu gạch đứng(|)
bao gồm các giá trị enum có thể có. Ví dụ: value1|value2.
|
integer_as_bool
|
(Boolean, BooleanList chỉ) Tạo các setter sử dụng
0 và 1 thay vì false và
true.
|
legacy_prop_name
|
(không bắt buộc, chỉ các thuộc tính Readonly ) Tên cũ của thuộc tính hệ thống cơ bản. Khi gọi getter, API getter sẽ cố gắng đọ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 một thuộc tính hiện có và chuyển sang một thuộc tính mới.
|
Mỗi loại thuộc tính sẽ ánh xạ đến các loại sau trong C++, Java và Rust:
| Loại | C++ (có thể rỗng) | Java (có thể rỗng) | Rust (không bắt buộc hoặc có thể rỗng) |
|---|---|---|---|
| Boolean | std::optional<bool> |
Optional<Boolean> |
Option<bool> |
| Số nguyên | std::optional<std::int32_t> |
Optional<Integer> |
Option<i32> |
| UInt | std::optional<std::uint32_t> |
Optional<Integer> |
Option<u32> |
| Long | std::optional<std::int64_t> |
Optional<Long> |
Option<i64> |
| ULong | std::optional<std::uint64_t> |
Optional<Long> |
Option<u64> |
| Double | 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> |
| Danh sách T | std::vector<std::optional<T>> |
List<T> |
Vec<T> |
Sau đây là ví dụ về tệp Mô tả sysprop xác định 3 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 các thư viện thuộc tính hệ thống
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 sẽ tạo một rust_library, một java_library và một cc_library cho mỗi thực thể của sysprop_library ở bên trong.
// 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. Để làm việc này, hãy tạo các tệp API và thư mục api. Đặt thư mục api trong cùng thư mục với Android.bp. Tên tệp API là <module_name>-current.txt và
<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 mới nhất đã được cố định. Hệ thống xây dựng sẽ kiểm tra xem các API có 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. Sau đây là ví dụ về tổ chức thư mục và tệp:
├── api
│ ├── PlatformProperties-current.txt
│ └── PlatformProperties-latest.txt
└── Android.bp
Các mô-đun ứng dụng 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 sẽ tạo các đường liên kết từ ứng dụng đến các thư viện C++, Java và Rust được tạo, nhờ đó cho phép ứng dụng 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: ["libPlatformProperties"],
}
rust_binary {
name: "rust_client",
srcs: ["src/main.rs"],
rustlibs: ["libplatformproperties_rust"],
}
Trong ví dụ trước, bạn có thể truy cập vào các thuộc tính đã xác định như sau.
Ví dụ về 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);
}
…
}
Ví dụ về 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);
}
…
}
…