このページでは、Android でシステム プロパティを追加または定義するための正規のメソッドと、既存のシステム プロパティのリファクタリングに関するガイドラインについて説明します。他の方法を取らなければならないような深刻な互換性の問題がない限り、必ずこれらのガイドラインに沿ってリファクタリングを行ってください。
ステップ 1: システム プロパティを定義する
システム プロパティを追加するときは、プロパティの名前を決定し、それを SELinux プロパティ コンテキストに関連付けます。適切な既存のコンテキストがない場合は、新しいコンテキストを作成します。この名前は、プロパティへのアクセス時に使用されます。プロパティ コンテキストは、SELinux に従ってアクセシビリティを制御するために使用されます。名前には任意の文字列を使用できますが、AOSP では構造化形式に沿って明確な名前にすることをおすすめします。
プロパティ名
この形式は、snake_case ケースで使用します。
[{prefix}.]{group}[.{subgroup}]*.{name}[.{type}]
要素 prefix
には、""(省略)、ro
(一度だけ設定できるプロパティに使用)、persist
(再起動後も保持されるプロパティに使用)のいずれかを使用します。
注意点
ro
は、今後、書き込み可能な prefix
を必要としないことが明らかな場合にのみ使用してください。** ro
接頭辞を指定しないでください。** prefix
を読み取り専用(つまり、init
によってのみ書き込み可能)にするには、代わりに sepolicy を使用してください。
再起動後も値を保持する必要があることが明らかであり、システム プロパティの使用が唯一の手段である場合にのみ、persist
を使用してください(詳しくは、準備のセクションをご覧ください)。
Google では、ro
または persist
のいずれかのプロパティを持つシステム プロパティを厳重に審査しています。
関連するプロパティを集約するには、「group
」という用語を使用します。これは、audio
や telephony
と同じように使用されるサブシステム名であることが意図されています。sys
、system
、dev
、default
、config
など、あいまいな用語や複数の意味を持つ用語は使用しないでください。
一般的には、システム プロパティに対する排他的な読み取りまたは書き込みアクセス権を持つ、ドメインタイプのプロセスの名前を使用することをおすすめします。たとえば、vold
プロセスが書き込み権限を持つシステム プロパティの場合、vold
(ドメインタイプのプロセスの名前)をグループ名として使用するのが一般的です。
必要に応じて、プロパティをさらに分類するために subgroup
を追加した場合でも、その要素を説明するためにあいまいな用語や複数の意味を持つ用語は使用しないでください(複数の subgroup
を使用することもできます)。
事前定義されているグループ名が複数存在します。system/sepolicy/private/property_contexts
ファイルで既存のグループ名を確認し、可能であれば、新しいグループ名を作成するのではなく、既存のグループ名を確認してください。次の表に、よく使用されるグループ名の例を示します。
ドメイン | グループ(およびサブグループ) |
---|---|
Bluetooth 関連 | bluetooth |
カーネル cmdline の sysprops | boot |
ビルドを識別する sysprop | build
|
テレフォニー関連 | telephony |
オーディオ関連 | audio |
グラフィック関連 | graphics |
vold 関連 | 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
: タイムアウト値(ミリ秒単位)のシステム プロパティである場合に使用します。
例:
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
など)。
exported_vold_prop
や vold_vendor_writable_prop
よりも、vold_config_prop
などの名前を使用してください。
型
プロパティ型は、以下の表に記載されている型のいずれかです。
型 | 定義 |
---|---|
ブール値 | true の場合は true または 1 、false の場合は false または 0 |
整数 | 64 ビット符号付き整数 |
符号なし整数 | 64 ビット符号なし整数 |
倍精度 | 倍精度浮動小数点数 |
文字列 | 任意の有効な UTF-8 文字列 |
列挙型 | 値には、空白文字を含まない任意の有効な UTF-8 文字列を使用できます |
上記のリスト | カンマ(, )は区切り文字として使用されます整数リスト [1, 2, 3] は 1,2,3 として保存されます |
内部的には、すべてのプロパティは文字列として保存されます。タイプを適用するには、property_contexts
ファイルとして指定します。詳しくは、ステップ 3 の property_contexts
をご覧ください。
ステップ 2: 必要なアクセシビリティ レベルを決定する
プロパティを定義する 4 つのヘルパーマクロを以下に示します。
アクセシビリティのタイプ | 説明 |
---|---|
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)
次に、プロパティ コンテキストに対する読み取りアクセス権と書き込みアクセス権の両方(またはいずれか)を付与します。set_prop
マクロと get_prop
マクロを使用して、system/sepolicy/public/{domain}.te
ファイルまたは system/sepolicy/private/{domain}.te
ファイルのいずれかでアクセス権を付与します。可能な限り private
を使用します。public
が適切なのは、set_prop
マクロまたは get_prop
マクロがコアドメイン外のドメインに影響を及ぼす場合のみです。
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)
次に、マクロで範囲設定されているアクセシビリティをさらに狭めるための neverallow ルールを追加します。たとえば、システム プロパティはベンダー プロセスによって読み取られる必要があるので、system_restricted_prop
を使用することを想定します。読み取りアクセスがすべてのベンダー プロセスで必要ではなく、特定のプロセスのセット(vendor_init
など)でのみ必要となる場合、読み取りアクセスを必要としないベンダー プロセスを禁止します。
書き込みおよび読み取りアクセスを制限するには、次の構文を使用します。
書き込みアクセスを制限する場合:
neverallow [domain] [context]:property_service set;
読み取りアクセスを制限する場合:
neverallow [domain] [context]:file no_rw_file_perms;
neverallow ルールが特定のドメインにバインドされている場合、system/sepolicy/private/{domain}.te
ファイルに neverallow ルールを追加します。広範囲の neverallow ルールでは、必要に応じて、次のような一般的なドメインを使用します。
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
を使用します。)
property
を設定するときに type
が適用されるため、可能な限り、すべてのエントリが指定されたタイプを持つことを確認してください。以下の例は、マッピングを作成する方法を示しています。
# 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 OS はモジュール化されるため、この要素は非常に重要です。Treble を使用すると、system パーティション、vendor パーティション、product パーティションをそれぞれ個別に更新できます。Mainline を使用すると、OS の一部は更新可能なモジュール(APEX または APK)にモジュール化されます。
システム プロパティが更新可能なソフトウェア全体に使用される(たとえば system パーティションと vendor パーティションをまたいで使用される)場合、そのシステム プロパティは安定している必要があります。ただし、たとえば、特定の Mainline モジュール内のみで使用される場合は、その名前、タイプ、プロパティ コンテキストを変更したり、さらには削除したりできます。
以下の質問を検討して、システム プロパティの安定性についての決定を行います。
- このシステム プロパティは、パートナーによって構成される(またはデバイスごとに異なる方法で構成される)ことが意図されていますか?「はい」の場合、システム プロパティは安定している必要があります。
- この AOSP によって定義されたシステム プロパティは、
vendor.img
やproduct.img
などの非システム パーティションに存在するコード(プロセスではない)との間で読み書きされることが意図されていますか?「はい」の場合、システム プロパティは安定している必要があります。 - このシステム プロパティは、複数の Mainline モジュール間で、または 1 つの Mainline モジュールとプラットフォームの更新可能ではない部分にまたがってアクセスされますか?「はい」の場合、システム プロパティは安定している必要があります。
安定しているシステム プロパティは、それぞれを API として正式に定義し、その API を使用してシステム プロパティにアクセスします。ステップ 6 をご覧ください。
ステップ 5: ビルド時にプロパティを設定する
ビルド時に、makefile 変数を使用してプロパティを設定します。技術的には、値は {partition}/build.prop
に書き込まれます。次に、init
は {partition}/build.prop
を読み取り、プロパティを設定します。このような変数には、PRODUCT_{PARTITION}_PROPERTIES
と TARGET_{PARTITION}_PROP
の 2 つのセットがあります。
PRODUCT_{PARTITION}_PROPERTIES
には、プロパティ値のリストが含まれます。構文は {prop}={value}
または {prop}?={value}
です。
{prop}={value}
は、通常の割り当てであり、{prop}
が {value}
に設定されます。このような割り当ては 1 つのプロパティに対して 1 つのみ許容されます。
{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 スクリプト
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
C++ / Java / Rust 用の API としての Sysprop
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 の下位レベルのプロパティ関数とメソッド
下位レベルの C/C++ 関数や Rust 関数、または下位レベルの Java メソッドも使用できる場合でも、可能であれば Sysprop を API として使用します。
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.
は接頭辞として使用できますが、互換性の確保のみを目的としています。通常のプロパティには使用しないでください。
以下の例では、上記の接頭辞のうちの 1 つを使用します。
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 にベンダーの顔認証プロパティを定義するとします。
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 パーティションと poduct パーティションはベンダーに依存できないため、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
prop をstring
prop にすることはできません。変更できるのは名前だけです。次に、読み取り API のみが以前の名にフォールバックします。書き込み API がフォールバックすることはありません。sysprop が書き込み可能である場合、その名前を変更することはできません。