本頁面提供在 Android 中新增或定義系統屬性的標準方法,並提供重構現有系統屬性的指南。重構時請務必遵循準則,除非有強烈 也可能引發相容性問題。
步驟 1:定義系統屬性
新增系統屬性時,請決定屬性名稱,並將其與 SELinux 屬性內容關聯。如果沒有適當的現有情境,請建立新的情境。存取屬性時會使用這個名稱。房源 也就是用於控制 SELinux 的無障礙程度名稱可以是任何字串,但 AOSP 建議您遵循結構化格式,以便清楚表示名稱。
屬性名稱
請使用 snake_case 大小寫的格式:
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
針對 prefix
元素,請使用「"」(省略)、ro
(僅設定一次的屬性) 或 persist
(重新啟動後仍保留的屬性)。
注意事項
只有在確定日後不需要 prefix
可寫入時,才使用 ro
。**請勿指定 ro
前置字元**。請改用 sepolicy,將 prefix
設為唯讀 (也就是只能由 init
寫入)。
只有在您確定值必須在重新啟動時保留,且使用系統屬性是唯一選項時,才使用 persist
。
Google 會嚴格審查含有 ro
或 persist
屬性的系統資源。
group
一詞用於匯總相關的屬性。這個名稱應類似於 audio
或 telephony
的子系統名稱。不使用
模稜兩可或超載的字詞,例如 sys
、system
、dev
、default
或
config
。
常見的做法是將網域名稱做為
對系統屬性擁有專屬讀取或寫入權限的程序。舉例來說,如果 vold
程序具有寫入存取權的系統屬性,通常會使用 vold
(程序的網域類型名稱) 做為群組名稱。
如有需要,請新增 subgroup
進一步分類屬性,但請避免使用模糊或過度負載的字詞來描述此元素。您也可以
多個 subgroup
)。
已定義許多群組名稱。請檢查 system/sepolicy/private/property_contexts
檔案,並盡可能使用現有的群組名稱,而非建立新群組名稱。下表列出常用的群組名稱示例。
網域 | 群組 (和子群組) |
---|---|
藍牙相關 | bluetooth |
來自核心 cmdline 的 sysprops | boot |
用於識別建構作業的 sysprops | build
|
電話相關 | telephony |
音訊相關 | audio |
與圖像相關 | graphics |
伏特相關 | vold |
以下定義前述規則運算式範例中 name
和 type
的用法。
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
name
可識別群組中的系統屬性。type
是選用元素,用來闡明 系統屬性舉例來說,與其將 sysprop 命名為audio.awesome_feature_enabled
或只有audio.awesome_feature
,請重新命名為audio.awesome_feature.enabled
反映系統屬性類型和意圖。
我們並未特別規定類型,這些用量 最佳化建議:
enabled
:如果類型是用來開啟或關閉功能的布林值系統屬性,請使用此值。config
:如果意圖是為了說明系統屬性「並非」代表系統的動態狀態,而是代表預先設定的值 (例如唯讀項目),請使用此屬性。List
:如果是系統屬性,且其值是清單,請使用這個欄位。Timeoutmillis
:如果是用於表示逾時值的系統屬性,則以 ms 為單位。
例如:
persist.radio.multisim.config
drm.service.enabled
資源情境
新的 SELinux 屬性內容架構可提供更精細的細節和更具描述性的名稱。與屬性名稱的做法類似,AOSP 建議使用以下格式:
{group}[_{subgroup}]*_prop
這些條款的定義如下:
group
和 subgroup
的含義與前一個定義相同
規則運算式範例。舉例來說,vold_config_prop
代表
資源是由供應商提供的設定
vendor_init
,vold_status_prop
或 vold_prop
則代表屬性
用來公開 vold
目前的狀態
命名資源內容時,請選擇反映資源一般用法的名稱。請特別避免使用下列類型的字詞:
- 太過籠統且模稜兩可的字詞,例如
sys
、system
、default
。 - 直接為無障礙功能編碼的字詞,例如
exported
、apponly
、ro
、public
、private
。
偏好使用 vold_config_prop
等名稱功能,優先使用 exported_vold_prop
。
或 vold_vendor_writable_prop
。
類型
屬性類型可以是下表中的其中一種。
類型 | 定義 |
---|---|
布林值 | true 或 1 代表 true,false 或 0 代表 false |
整數 | 帶正負號 64 位元整數 |
無號整數 | 無正負號的 64 位元整數 |
雙項 | 雙精度浮點 |
字串 | 任何有效的 UTF-8 字串 |
列舉 | 值可以是任何不含空白字元的有效 UTF-8 字串 |
上列清單 | 使用半形逗號 (, ) 做為分隔符號整數清單 [1, 2, 3] 會儲存為 1,2,3 |
在內部,所有屬性都會以字串的形式儲存。如要強制執行類型
指定為 property_contexts
檔案若需更多資訊,請參閲
步驟 3 中的 property_contexts
。
步驟 2:決定必要的無障礙層級
有四個輔助巨集可用來定義屬性。
無障礙類型 | 意義 |
---|---|
system_internal_prop |
只在 /system 中使用的屬性 |
system_restricted_prop |
在 /system 外讀取但未寫入的屬性 |
system_vendor_config_prop |
僅在 /system 之外讀取,且僅由 vendor_init 寫入的屬性 |
system_public_prop |
可在 /system 之外讀取及寫入的屬性 |
請盡可能縮小系統資源的存取權範圍。過去 廣泛的存取權導致應用程式損壞和安全漏洞。您可以考慮使用 設定範圍時,請留意下列問題:
- 需要保留這項系統屬性嗎?(如果是,原因為何?)
- 哪個程序應具備此屬性的讀取權限?
- 哪個程序應具備這項資源的寫入權限?
請使用上一個問題和 決策樹狀圖做為工具 來決定適當的存取範圍
圖 1. 用來判斷系統屬性存取權範圍的決策樹
步驟 3:新增至 system/sepolicy
存取 sysprop 時,SELinux 會控製程序的存取。更新後
由您決定所需的無障礙程度、定義屬性的背景資訊
(位於 system/sepolicy
下),以及額外的 allow 和 neverallow 規則
瞭解可以讀取或寫入哪些程序
首先,請在 system/sepolicy/public/property.te
中定義屬性結構定義
檔案。如果屬性是系統內部屬性,請在 system/sepolicy/private/property.te
檔案中定義。請使用其中一個 system_[accessibility]_prop([context])
巨集,為系統屬性提供必要的無障礙功能。以下是 system/sepolicy/public/property.te
檔案的範例:
system_public_prop(audio_foo_prop)
system_vendor_config_prop(audio_bar_prop)
可新增至 system/sepolicy/private/property.te
檔案的範例:
system_internal_prop(audio_baz_prop)
其次,授予資源內容的讀取和/或寫入權限。在 system/sepolicy/public/{domain}.te
或 system/sepolicy/private/{domain}.te
檔案中使用 set_prop
和 get_prop
巨集來授予存取權。盡可能使用 private
;只有在 set_prop
或 get_prop
巨集影響核心網域以外的任何網域時,public
才適合使用。
例如,在 system/sepolicy/private/audio.te
檔案中:
set_prop(audio, audio_foo_prop)
set_prop(audio, audio_bar_prop)
例如,在 system/sepolicy/public/domain.te
檔案中:
get_prop(domain, audio_bar_prop)
第三,新增一些永不允許的規則,進一步降低
限定在巨集的範圍舉例來說,假設您使用
system_restricted_prop
,因為供應商必須讀取你的系統資源
作業。如果並非所有供應商程序都需要讀取權限,而只有特定程序 (例如 vendor_init
) 需要讀取權限,請禁止不需要讀取權限的供應商程序。
請使用下列語法限制寫入及讀取權限:
如何限制寫入權限:
neverallow [domain] [context]:property_service set;
如何限制讀取權限:
neverallow [domain] [context]:file no_rw_file_perms;
如果 neverallow 規則已繫結至特定網域,請將 neverallow 規則放在 system/sepolicy/private/{domain}.te
檔案中。如要使用更廣泛的永不允許規則,請在適當情況下使用下列一般網域:
system/sepolicy/private/property.te
system/sepolicy/private/coredomain.te
system/sepolicy/private/domain.te
在 system/sepolicy/private/audio.te
檔案中,加入以下內容:
neverallow {
domain -init -audio
} {audio_foo_prop audio_bar_prop}:property_service set;
在 system/sepolicy/private/property.te
檔案中放入以下內容:
neverallow {
domain -coredomain -vendor_init
} audio_prop:file no_rw_file_perms;
請注意,{domain -coredomain}
會擷取所有供應商程序。因此,{domain -coredomain -vendor_init}
代表「所有供應商程序 (vendor_init
除外)」。
最後,將系統屬性與屬性結構定義建立關聯。這樣可確保系統將授予的存取權和套用至資源內容的 neverallow 規則套用至實際資源。如要這麼做,請在 property_contexts
檔案中新增項目。這個檔案會說明系統屬性和屬性內容之間的對應關係。在這個檔案中,您可以指定
屬性,或是要對應至情境的屬性前置字元。
以下是對應單一資源的語法:
[property_name] u:object_r:[context_name]:s0 exact [type]
以下是對應前置字串的語法:
[property_name_prefix] u:object_r:[context_name]:s0 prefix [type]
您可以選擇指定資源的類型,可選的類型如下:
bool
int
uint
double
enum [list of possible values...]
string
(清單屬性請使用string
)。
請確定每個項目都有指定類型,例如 type
會在設定 property
時強制執行以下範例說明如何編寫
對應:
# binds a boolean property "ro.audio.status.enabled"
# to the context "audio_foo_prop"
ro.audio.status.enabled u:object_r:audio_foo_prop:s0 exact bool
# binds a boolean property "vold.decrypt.status"
# to the context "vold_foo_prop"
# The property can only be set to one of these: on, off, unknown
vold.decrypt.status u:object_r:vold_foo_prop:s0 exact enum on off unknown
# binds any properties starting with "ro.audio.status."
# to the context "audio_bar_prop", such as
# "ro.audio.status.foo", or "ro.audio.status.bar.baz", and so on.
ro.audio.status. u:object_r:audio_bar_prop:s0 prefix
當完全相符項目和前置字串項目發生衝突時,完全相符項目會優先採用。如需更多範例,請參閱 system/sepolicy/private/property_contexts
。
步驟 4:決定穩定性需求
穩定性是系統屬性的另一個面向,與無障礙性不同。穩定性是指系統屬性日後是否可以變更 (例如重新命名或甚至移除)。隨著 Android 作業系統採用模組化設計,這點就顯得格外重要。有了 Treble 這個系統 和產品分區可以獨立更新。取代為 Mainline,部分 OS 的模組化為可更新的模組 (在 APEXs 中) 或 APK)。
舉例來說,假設系統屬性適用於可更新的軟體元件 各個系統和供應商分區之間必須保持穩定不過,如果只在特定 Mainline 模組中使用,您可以變更名稱、類型或屬性內容,甚至移除該模組。
請思考以下問題,判斷系統屬性的穩定性:
- 這個系統資源是否由合作夥伴設定 (或已設定) 每種裝置的不同情況)?如果是,則必須是穩定的網路。
- 這個 AOSP 定義的系統屬性是否會寫入或讀取
vendor.img
或product.img
等非系統分割區中存在的程式碼 (而非處理程序)?如果是,則必須是穩定的網路。 - 這個系統屬性是否跨 Mainline 模組或整個 Mainline 存取? 以及平台中無法更新的部分?如果是,則必須是穩定的網路。
針對穩定的系統屬性,請正式將每個屬性定義為 API,並使用 API 存取系統屬性,如步驟 6所述。
步驟 5:在建構期間設定屬性
使用 makefile 變數在建構期間設定屬性。從技術層面來說,這些值已內建於 {partition}/build.prop
中。接著,init
會讀取 {partition}/build.prop
來設定屬性。我們有兩種方法
變數:PRODUCT_{PARTITION}_PROPERTIES
和 TARGET_{PARTITION}_PROP
。
PRODUCT_{PARTITION}_PROPERTIES
包含屬性值清單。語法為 {prop}={value}
或 {prop}?={value}
。
{prop}={value}
是一般指派,可確保 {prop}
設為 {value}
;每個屬性只能有一個這樣的指派。
{prop}?={value}
是選用指派;只有在沒有任何 {prop}={value}
指派時,{prop}
才會設為 {value}
。如果有多個選用指派,系統會採用第一個指派。
# sets persist.traced.enable to 1 with system/build.prop
PRODUCT_SYSTEM_PROPERTIES += persist.traced.enable=1
# sets ro.zygote to zygote32 with system/build.prop
# but only when there are no other assignments to ro.zygote
# optional are useful when giving a default value to a property
PRODUCT_SYSTEM_PROPERTIES += ro.zygote?=zygote32
# sets ro.config.low_ram to true with vendor/build.prop
PRODUCT_VENDOR_PROPERTIES += ro.config.low_ram=true
TARGET_{PARTITION}_PROP
包含檔案清單,會直接傳送至 {partition}/build.prop
。每個檔案都包含 {prop}={value}
組合的清單。
# example.prop
ro.cp_system_other_odex=0
ro.adb.secure=0
ro.control_privapp_permissions=disable
# emits example.prop to system/build.prop
TARGET_SYSTEM_PROP += example.prop
詳情請參閱
build/make/core/sysprop.mk
。
步驟 6:在執行階段存取屬性
您可以在執行階段讀取及寫入屬性。
初始化指令碼
Init 指令碼檔案 (通常是 *.rc 檔案) 可由 ${prop}
或
${prop:-default}
,可以設定在屬性變為
特定值,且可以使用 setprop
指令寫入屬性。
# when persist.device_config.global_settings.sys_traced becomes 1,
# set persist.traced.enable to 1
on property:persist.device_config.global_settings.sys_traced=1
setprop persist.traced.enable 1
# when security.perf_harden becomes 0,
# write /proc/sys/kernel/sample_rate to the value of
# debug.sample_rate. If it's empty, write -100000 instead
on property:security.perf_harden=0
write /proc/sys/kernel/sample_rate ${debug.sample_rate:-100000}
getprop 和 setprop 殼層指令
您可以分別使用 getprop
或 setprop
殼層指令來讀取或
寫入屬性如需更多詳細資訊,請叫用 getprop --help
或 setprop --help
。
$ adb shell getprop ro.vndk.version
$
$ adb shell setprop security.perf_harden 0
Sysprop 做為 C++/Java/Rust 的 API
使用 sysprop 做為 API,您可以定義系統屬性,並使用具體且有型別的自動產生 API。使用 Public
設定 scope
後,產生的 API 也會提供給跨界限的模組,並確保 API 穩定性。請參考
.sysprop
檔案、Android.bp
模組以及 C++、Java 和 Rust 程式碼的範例
# AudioProps.sysprop
# module becomes static class (Java) / namespace (C++) for serving API
module: "android.sysprop.AudioProps"
# owner can be Platform or Vendor or Odm
owner: Platform
# one prop defines one property
prop {
prop_name: "ro.audio.volume.level"
type: Integer
scope: Public
access: ReadWrite
api_name: "volume_level"
}
…
// Android.bp
sysprop_library {
name: "AudioProps",
srcs: ["android/sysprop/AudioProps.sysprop"],
property_owner: "Platform",
}
// Rust, Java and C++ modules can link against the sysprop_library
rust_binary {
rustlibs: ["libaudioprops_rust"],
…
}
java_library {
static_libs: ["AudioProps"],
…
}
cc_binary {
static_libs: ["libAudioProps"],
…
}
// Rust code accessing generated API.
// Get volume. Use 50 as the default value.
let vol = audioprops::volume_level()?.unwrap_or_else(50);
// Java codes accessing generated API
// get volume. use 50 as the default value.
int vol = android.sysprop.AudioProps.volume_level().orElse(50);
// add 10 to the volume level.
android.sysprop.AudioProps.volume_level(vol + 10);
// C++ codes accessing generated API
// get volume. use 50 as the default value.
int vol = android::sysprop::AudioProps::volume_level().value_or(50);
// add 10 to the volume level.
android::sysprop::AudioProps::volume_level(vol + 10);
詳情請參閱「將系統屬性做為 API 實作」。
C/C++、Java 和 Rust 低階屬性函式和方法
盡可能使用 Sysprop 做為 API (即使低階 C/C++ 或 Rust 函式) 也可以使用低階 Java 方法
libc
、libbase
和 libcutils
提供 C++ 系統屬性函式。libc
具有基礎 API,而 libbase
和 libcutils
函式則是包裝函式。如有可能,請使用 libbase
sysprop 函式,因為這類函式最方便,而且主機二進位檔可以使用 libbase
函式。詳情請參閱 sys/system_properties.h
(libc
)、android-base/properties.h
(libbase
) 和 cutils/properties.h
(libcutils
)。
android.os.SystemProperties
類別提供 Java 系統屬性方法。
rustutils::system_properties
模組提供 Rust 系統屬性函式和類型。
附錄:新增供應商專屬屬性
合作夥伴 (包括針對 Pixel 開發工作的 Google 員工) 想要達成的目標
定義硬體專屬的 (或裝置專用) 系統屬性。
供應商專屬屬性是合作夥伴擁有的屬性,屬性僅適用於合作夥伴的硬體或裝置,而非平台。因為這是硬體或裝置
相依,適合在 /vendor
或 /odm
分區中使用。
自從 Treble 以來,平台屬性和供應商屬性 以免發生衝突以下說明如何定義供應商屬性,並說明必須一律使用的供應商屬性。
屬性和內容名稱的命名空間
所有供應商屬性都必須以下列其中一個前置字元開頭,以免與其他區隔的屬性發生衝突。
ctl.odm.
ctl.vendor.
ctl.start$odm.
ctl.start$vendor.
ctl.stop$odm.
ctl.stop$vendor.
init.svc.odm.
init.svc.vendor.
ro.odm.
ro.vendor.
odm.
persist.odm.
persist.vendor.
vendor.
請注意,ro.hardware.
可做為前置字串,但只適用於相容性。
請勿用於一般屬性。
以下範例都使用上述任一前置字串:
vendor.display.primary_red
persist.vendor.faceauth.use_disk_cache
ro.odm.hardware.platform
所有供應商屬性內容都必須以 vendor_
開頭。同樣適用於
相容性。以下是一些範例:
vendor_radio_prop
。vendor_faceauth_prop
。vendor_usb_prop
。
命名及維護資源是供應商的責任,因此除了供應商的命名空間規定之外,請遵循步驟 2 建議的格式。
供應商專屬的 SEPolicy 規則和 property_contexts
供應商屬性可透過 vendor_internal_prop
巨集定義。在
您在 BOARD_VENDOR_SEPOLICY_DIRS
目錄中定義的供應商專屬規則。
舉例來說,假設您在 coral 中定義供應商 faceauth 屬性。
在 BoardConfig.mk
檔案 (或任何 BoardConfig.mk
的包含) 中加入
包括:
BOARD_VENDOR_SEPOLICY_DIRS := device/google/coral-sepolicy
在 device/google/coral-sepolicy/private/property.te
檔案中,放入以下內容:
vendor_internal_prop(vendor_faceauth_prop)
在 device/google/coral-sepolicy/private/property_contexts
檔案中,放入以下內容:
vendor.faceauth.trace u:object_r:vendor_faceauth_prop:s0 exact bool
供應商資源限制
由於系統和產品分區無法依賴廠商,因此絕對不會
允許從 system
、system-ext
或
product
個分區。
附錄:重新命名現有資源
如果您必須淘汰屬性並移至新屬性,請使用 Sysprop 做為 API
重新命名現有屬性。這樣一來,您就能透過新技術
指定舊版名稱和新屬性名稱。具體來說,您可以透過 .sysprop
檔案中的 legacy_prop_name
欄位設定舊版名稱。產生的 API 會嘗試讀取 prop_name
,如果 prop_name
不存在,則會使用 legacy_prop_name
。
舉例來說,下列步驟重新命名 awesome_feature_foo_enabled
至 foo.awesome_feature.enabled
。
在 foo.sysprop
檔案中
module: "android.sysprop.foo"
owner: Platform
prop {
api_name: "is_awesome_feature_enabled"
type: Boolean
scope: Public
access: Readonly
prop_name: "foo.awesome_feature.enabled"
legacy_prop_name: "awesome_feature_foo_enabled"
}
在 C++ 程式碼中
// is_awesome_feature_enabled() reads "foo.awesome_feature.enabled".
// If it doesn't exist, reads "awesome_feature_foo_enabled" instead
using android::sysprop::foo;
bool enabled = foo::is_awesome_feature_enabled().value_or(false);
請注意下列事項:
首先,您無法變更 sysprop 的類型。舉例來說,您無法將
int
道具變更為string
道具,只能變更名稱。第二,只有讀取 API 會改回使用舊版名稱。Write API 不會 回溯。如果 sysprop 是可寫入的,您無法重新命名。