システム プロパティの追加

このページでは、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」という用語を使用します。これは、audiotelephony と同じように使用されるサブシステム名であることが意図されています。syssystemdevdefaultconfig など、あいまいな用語や複数の意味を持つ用語は使用しないでください

一般的には、システム プロパティに対する排他的な読み取りまたは書き込みアクセス権を持つ、ドメインタイプのプロセスの名前を使用することをおすすめします。たとえば、vold プロセスが書き込み権限を持つシステム プロパティの場合、vold(ドメインタイプのプロセスの名前)をグループ名として使用するのが一般的です。

必要に応じて、プロパティをさらに分類するために subgroup を追加した場合でも、その要素を説明するためにあいまいな用語や複数の意味を持つ用語は使用しないでください(複数の subgroup を使用することもできます)。

事前定義されているグループ名が複数存在します。system/sepolicy/private/property_contexts ファイルで既存のグループ名を確認し、可能であれば、新しいグループ名を作成するのではなく、既存のグループ名を確認してください。次の表に、よく使用されるグループ名の例を示します。

ドメイン グループ(およびサブグループ)
Bluetooth 関連 bluetooth
カーネル cmdline の sysprops boot
ビルドを識別する sysprop build
テレフォニー関連 telephony
オーディオ関連 audio
グラフィック関連 graphics
vold 関連 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: タイムアウト値(ミリ秒単位)のシステム プロパティである場合に使用します。

例:

  • persist.radio.multisim.config
  • drm.service.enabled

プロパティ コンテキスト

新しい SELinux プロパティ コンテキスト スキームでは、さらに詳細かつわかりやすい名前を使用できます。プロパティ名に使用されるものと同様に、AOSP では以下の形式が推奨されます。

{group}[_{subgroup}]*_prop

用語は以下のように定義されます。

groupsubgroup の意味は、前述の正規表現の例で定義したものと同じです。たとえば、vold_config_prop はベンダーによって構成され、vendor_init によって設定されるプロパティを示します。vold_status_prop または単に vold_prop は、vold の現在の状態を公開するプロパティを意味します。

プロパティ コンテキストに名前を付ける際は、プロパティの一般的な使用方法が反映された名前にします。特に、次のような用語は使用しないでください。

  • あまりにも一般的であいまいな用語(syssystemdefault など)。
  • アクセシビリティを直接エンコードする用語(exportedapponlyropublicprivate など)。

exported_vold_propvold_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 ファイルとして指定します。詳しくは、ステップ 3property_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.imgproduct.img などの非システム パーティションに存在するコード(プロセスではない)との間で読み書きされることが意図されていますか?「はい」の場合、システム プロパティは安定している必要があります。
  • このシステム プロパティは、複数の Mainline モジュール間で、または 1 つの Mainline モジュールとプラットフォームの更新可能ではない部分にまたがってアクセスされますか?「はい」の場合、システム プロパティは安定している必要があります。

安定しているシステム プロパティは、それぞれを API として正式に定義し、その API を使用してシステム プロパティにアクセスします。ステップ 6 をご覧ください。

ステップ 5: ビルド時にプロパティを設定する

ビルド時に、makefile 変数を使用してプロパティを設定します。技術的には、値は {partition}/build.prop に書き込まれます。次に、init{partition}/build.prop を読み取り、プロパティを設定します。このような変数には、PRODUCT_{PARTITION}_PROPERTIESTARGET_{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 として使用します。

libclibbaselibcutils は、C++ システム プロパティ関数を提供します。libc には基礎となる API があり、libbase 関数と libcutils 関数はラッパーです。可能であれば、libbase sysprop 関数を使用します。これは、この関数が最も使いやすく、ホストバイナリは libbase 関数を使用できるためです。詳しくは、sys/system_properties.hlibc)、android-base/properties.hlibbase)、cutils/properties.hlibcutils)をご覧ください。

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 パーティションはベンダーに依存できないため、systemsystem-extproduct パーティションからはベンダー プロパティにアクセスできないようにしてください。

付録: 既存のプロパティ名の変更

あるプロパティを廃止して新しいプロパティに移行する場合は、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 が書き込み可能である場合、その名前を変更することはできません。