Các thuộc tính hệ thống là 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 phân vù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 chung gồm các cặp khoá-giá trị chuỗi không có giản đồ, 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.
Kể 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 lập sơ đồ thành các tệp Sysprop Description và các API để truy cập vào các thuộc tính sẽ đượ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 đặc biệ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 tạo và quá trình tạo sẽ bị gián đoạn nếu có các thay đổi không tương thích. Thao tác kiểm tra này đóng vai trò là 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ể đảm bảo 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 tệp Sysprop Description (.sysprop
), sử dụng TextFormat của protobuf, với giản đồ 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 Sysprop Description chứa một thông báo về các 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 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 một không gian tên (C++) hoặc lớp tĩnh cuối cùng (Java) mà trong đó các API được tạo sẽ được đặt. 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 các cơ sở lưu trú. |
Ý 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 tài sản này. |
access
|
Readonly : Chỉ tạo API getter
Writeonce , ReadWrite : Tạo API getter và setter
Lưu ý: Các thuộc tính có tiền tố ro. có thể không 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
|
(Chỉ Enum , EnumList ) Một chuỗi được phân tách bằng dấu gạch dọc(|) bao gồm các giá trị enum có thể có. Ví dụ: value1|value2 .
|
integer_as_bool
|
(Chỉ Boolean , BooleanList ) Hãy để 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ó thuộc tính Readonly ) Tên cũ của thuộc tính hệ thống cơ bản. Khi gọi phương thức 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 không dùng một tài sản hiện có nữa và chuyển sang một tài sản mới.
|
Mỗi loại thuộc tính tương ứng với các loại sau trong C++, Java và Rust.
Loại | C++ | Java | Rust |
---|---|---|---|
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
|
Đôi | std::optional<double>
|
Optional<Double>
|
f64
|
Chuỗi | std::optional<std::string>
|
Optional<String>
|
String
|
Enum | std::optional<{api_name}_values>
|
Optional<{api_name}_values>
|
{ApiName}Values
|
T List | std::vector<std::optional<T>>
|
List<T>
|
Vec<T>
|
Sau đây là ví dụ về tệp Sysprop Description (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
Giờ đây, bạn có thể xác định các mô-đun sysprop_library
bằng tệp Mô tả Sysprop.
sysprop_library
đóng vai trò là một API cho C++, Java và Rust. Hệ thống xây dựng sẽ 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 thêm 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à một thư mục api
. Đặt thư mục api
trong cùng một 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 bản 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, đồng thời phát ra thông báo lỗi và 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ề cách sắp xếp 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 đã tạo. Hệ thống bản dựng tạo các đường liên kết từ ứng dụng đến các thư viện C++, Java và Rust đã tạo, nhờ đó cho phép ứng dụng truy cập vào các API đã 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"],
}
Xin 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à -
bằng _
, sau đó thêm lib
vào đầu và _rust
vào cuối.
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);
}
…
}
…