新增系統屬性

本頁面提供在 Android 中新增或定義系統屬性的標準方法,並提供重構現有系統屬性的指南。重構時請務必遵循準則,除非有強烈 也可能引發相容性問題。

步驟 1:定義系統屬性

新增系統屬性時,請決定屬性名稱,並將其與 SELinux 屬性內容關聯。如果沒有適當的現有情境,請建立新的情境。存取屬性時會使用這個名稱。房源 也就是用於控制 SELinux 的無障礙程度名稱可以是任何字串,但 AOSP 建議您遵循結構化格式,以便清楚表示名稱。

屬性名稱

請使用 snake_case 大小寫的格式:

[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]

針對 prefix 元素,請使用「"」(省略)、ro (僅設定一次的屬性) 或 persist (重新啟動後仍保留的屬性)。

注意事項

只有在確定日後不需要 prefix 可寫入時,才使用 ro。**請勿指定 ro 前置字元**。請改用 sepolicy,將 prefix 設為唯讀 (也就是只能由 init 寫入)。

只有在您確定值必須在重新啟動時保留,且使用系統屬性是唯一選項時,才使用 persist

Google 會嚴格審查含有 ropersist 屬性的系統資源。

group 一詞用於匯總相關的屬性。這個名稱應類似於 audiotelephony 的子系統名稱。不使用 模稜兩可或超載的字詞,例如 syssystemdevdefaultconfig

常見的做法是將網域名稱做為 對系統屬性擁有專屬讀取或寫入權限的程序。舉例來說,如果 vold 程序具有寫入存取權的系統屬性,通常會使用 vold (程序的網域類型名稱) 做為群組名稱。

如有需要,請新增 subgroup 進一步分類屬性,但請避免使用模糊或過度負載的字詞來描述此元素。您也可以 多個 subgroup)。

已定義許多群組名稱。請檢查 system/sepolicy/private/property_contexts 檔案,並盡可能使用現有的群組名稱,而非建立新群組名稱。下表列出常用的群組名稱示例。

網域 群組 (和子群組)
藍牙相關 bluetooth
來自核心 cmdline 的 sysprops boot
用於識別建構作業的 sysprops build
電話相關 telephony
音訊相關 audio
與圖像相關 graphics
伏特相關 vold

以下定義前述規則運算式範例nametype 的用法。

[{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

這些條款的定義如下:

groupsubgroup 的含義與前一個定義相同 規則運算式範例。舉例來說,vold_config_prop 代表 資源是由供應商提供的設定 vendor_initvold_status_propvold_prop 則代表屬性 用來公開 vold 目前的狀態

命名資源內容時,請選擇反映資源一般用法的名稱。請特別避免使用下列類型的字詞:

  • 太過籠統且模稜兩可的字詞,例如 syssystemdefault
  • 直接為無障礙功能編碼的字詞,例如 exportedapponlyropublicprivate

偏好使用 vold_config_prop 等名稱功能,優先使用 exported_vold_prop。 或 vold_vendor_writable_prop

類型

屬性類型可以是下表中的其中一種。

類型 定義
布林值 true1 代表 true,false0 代表 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 下),以及額外的 allowneverallow 規則 瞭解可以讀取或寫入哪些程序

首先,請在 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}.tesystem/sepolicy/private/{domain}.te 檔案中使用 set_propget_prop 巨集來授予存取權。盡可能使用 private;只有在 set_propget_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.imgproduct.img 等非系統分割區中存在的程式碼 (而非處理程序)?如果是,則必須是穩定的網路。
  • 這個系統屬性是否跨 Mainline 模組或整個 Mainline 存取? 以及平台中無法更新的部分?如果是,則必須是穩定的網路。

針對穩定的系統屬性,請正式將每個屬性定義為 API,並使用 API 存取系統屬性,如步驟 6所述。

步驟 5:在建構期間設定屬性

使用 makefile 變數在建構期間設定屬性。從技術層面來說,這些值已內建於 {partition}/build.prop 中。接著,init 會讀取 {partition}/build.prop 來設定屬性。我們有兩種方法 變數:PRODUCT_{PARTITION}_PROPERTIESTARGET_{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 殼層指令

您可以分別使用 getpropsetprop 殼層指令來讀取或 寫入屬性如需更多詳細資訊,請叫用 getprop --helpsetprop --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 方法

libclibbaselibcutils 提供 C++ 系統屬性函式。libc 具有基礎 API,而 libbaselibcutils 函式則是包裝函式。如有可能,請使用 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

供應商資源限制

由於系統和產品分區無法依賴廠商,因此絕對不會 允許從 systemsystem-extproduct 個分區。

附錄:重新命名現有資源

如果您必須淘汰屬性並移至新屬性,請使用 Sysprop 做為 API 重新命名現有屬性。這樣一來,您就能透過新技術 指定舊版名稱和新屬性名稱。具體來說,您可以透過 .sysprop 檔案中的 legacy_prop_name 欄位設定舊版名稱。產生的 API 會嘗試讀取 prop_name,如果 prop_name 不存在,則會使用 legacy_prop_name

舉例來說,下列步驟重新命名 awesome_feature_foo_enabledfoo.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 是可寫入的,您無法重新命名。