AGENTS.md — selectionfwk
OpenHarmony Selection Service subsystem (@ohos/selectionfwk, part_name selectionfwk, subsystem systemabilitymgr). Implements SystemAbility ID 8500 (selection_service process) that globally captures user-selected text and drives selection extension panels. Not buildable standalone — lives inside the OpenHarmony source tree at foundation/systemabilitymgr/selectionfwk.
This file routes an agent to the right files; it is not an architecture reference. When a section says "read X", read X before editing.
Build
The build is GN + Ninja driven by the top-level OpenHarmony build.sh, run from the workspace root (the OpenHarmony tree root, not this repo):
- Full (after editing any
BUILD.gn):./build.sh --product-name rk3568 --ccache - Full (no
BUILD.gnchanges):./build.sh --product-name rk3568 --ccache --fast-rebuild - Component only:
./build.sh --product-name rk3568 --ccache --build-target selectionfwk - Tests only:
./build.sh --product-name rk3568 --build-target selectionfwk_test
GN entry: bundle.json registers the component; selection_service.gni (imported by every BUILD.gn) defines selection_fwk_root_path and system_type = "default". Add new declare_args() switches (e.g. word_selection_feature_test_one) in selection_service.gni, not in BUILD.gn files.
There is no separate lint step. Static analysis (CFI, UBSan, integer_overflow, boundary_sanitize) is enforced at build time via the sanitize block in every production target — a clean build is the lint gate.
Layout / what builds what
| Path | Output | Notes |
|---|---|---|
service/ |
libselection_service.z.so |
Core SA. service/src, service/include, service/focus_monitor, service/plugins |
service/plugins/ |
libselection_plugins_impl.z.so + selection_config_static |
Decoupled pasteboard / database / ability plugins. plugin_exports.cpp exports extern "C" symbols with explicit visibility("default") (everything else hidden) |
frameworks/native/selection_ability |
selection_ability |
Native selection-ability impl |
frameworks/native/selection_extension |
selection_extension_ability_native |
Native extension impl |
frameworks/native/selection_client |
(compiled into inner_kits) | Source shared with interfaces/inner_kits/selection_client |
interfaces/inner_kits/selection_client |
libselection_client.z.so |
Public inner-api; version_script = selection_client.versionscript; innerapi_tags = ["platformsdk"] |
frameworks/js/napi/selection_ability |
selectionmanager_napi |
The selectionManager JS module |
frameworks/js/napi/selection_panel |
selectionpanel_napi |
The selectionPanel JS module |
frameworks/js/napi/selection_extension_ability / _context |
selectionextensionactivity_napi / ...context_napi |
|
frameworks/ets/ets/ |
selection_extension_ability_etc |
ETS API .ets declaration files |
frameworks/ets/taihe/ |
selection_taihe_group |
Taihe FFI codegen (dep taihe_ffi_gen); IDL sources under SelectionManager/idl, SelectionPanel/idl |
interfaces/idl/ |
selection_service_proxy/stub, selection_listener_proxy/stub |
IDL codegen via idl_tool (idl_gen_interface); generated *_proxy.cpp/*_stub.cpp land in ${target_gen_dir} — do not edit |
common/ |
libselection_common.a (static) |
Shared utils, NAPI helpers |
sa_profile/8500.json |
SA profile | SA 8500, libselection_service.z.so, boot phase BootStartPhase, start-on-demand on sys.selection.switch=on and usual.event.USER_SWITCHED |
etc/init/selection_service.cfg |
init config | uid/gid sysselection, SELinux u:r:selection_service:s0, apl: system_basic; creates /data/service/el1/public/selection_service |
etc/para/selection.para |
system params | sys.selection.switch, sys.selection.trigger, sys.selection.app, sys.selection.uid |
sysevent/ / hiappevent_agent/ |
hisysevent / hiappevent adapters | Domain SELECTIONFWK (see hisysevent-SELECTIONFWK.yaml) |
utils/ |
selection_timer |
Linked into service + plugins |
Frequent-change paths (by recent commit frequency): service/src, service/include, service/plugins, frameworks/native, frameworks/js, test/unittest. Timer state / lock-safety changes recur often in service/src/selection_input_monitor.cpp and utils/src/selection_timer.cpp — check for existing lock guards before adding concurrency code there.
Where to look for common tasks
| Task | First files to read |
|---|---|
| Add/change a selection service behavior | service/include/selection_service.h, service/src/selection_service.cpp |
| Input event handling / state machine | service/include/selection_input_monitor.h, service/src/selection_input_monitor.cpp |
| Pasteboard / DB / ability plugin logic | service/plugins/src/{database,pasteboard,ability}/, service/plugins/include/ |
| Public C++ inner-API surface | interfaces/inner_kits/selection_client/include/selection_client.h, frameworks/native/selection_client/selection_client.cpp |
| IPC wire format / proxy-stub | interfaces/idl/ISelectionService.idl, ISelectionListener.idl (then ${target_gen_dir}/*_proxy.cpp) |
| JS/ETS API (selectionManager / selectionPanel) | frameworks/js/napi/selection_ability/, frameworks/js/napi/selection_panel/, frameworks/ets/ets/ |
| SA on-demand / boot / SELinux / permissions | sa_profile/8500.json, etc/init/selection_service.cfg, etc/para/selection.para(.dac) |
| DFX (hisysevent / hiappevent) | sysevent/hisysevent_adapter.{h,cpp}, hiappevent_agent/, hisysevent-SELECTIONFWK.yaml |
| Config persistence / user-switch | service/src/sys_selection_config_repository.cpp, service/src/selection_config_comparator.cpp |
| Tests for any of the above | test/unittest/<matching>_test.cpp |
Knowledge routing
Task-based routing
- Public API change (anything touching
libselection_client.z.so): readinterfaces/inner_kits/selection_client/selection_client.versionscriptfirst — only symbols matching*SelectionClient*are exported; everything else islocal:*. A new public symbol that does not match this pattern will be silently hidden. - IPC protocol / cross-process change: edit the
.idlininterfaces/idl/, never the generated*_proxy.cpp/*_stub.cpp. Rebuild to regenerate. - Permission / SELinux / on-demand behavior: read
sa_profile/8500.json,etc/init/selection_service.cfg,etc/para/selection.para(.dac)together — they are coupled (switch param + common event + SA profile + init uid/gid/secon). - DFX / fault attribution: read
hisysevent-SELECTIONFWK.yamlfor the event schema (SELECTION_PROCESS_ABNORMAL,SELECTION_STATISTIC) before adding/renaming a hisysevent field; domain isSELECTIONFWK. - Window manager dep: see the sceneboard conditional below before adding any
window_manager:*dep. - Plugin boundary change: the SA must stay decoupled from
ability_runtime:ability_manager— read the comment inservice/BUILD.gnand route that dep throughservice/plugins/instead.
Path-based routing
- Editing anything under
service/plugins/→ also re-checkservice/plugins/BUILD.gnCFI config (must matchrelational_store:native_rdb). - Editing
frameworks/ets/taihe/*/idl/→ expect regenerated glue underframeworks/ets/taihe/*/src; treatsrcas partly generated. - Editing
interfaces/idl/*.idl→ regenerate happens at build time into${target_gen_dir}; do not commit generated proxy/stub. - Adding a new
BUILD.gnfor a shared lib → copy the fullsanitize+cflags_cc+ldflagsblock from an existing target (see "Build hardening").
Vocabulary routing
| Term | Meaning / where to read more |
|---|---|
| SA 8500 | SystemAbility ID 8500 = this service. Profile: sa_profile/8500.json. |
selection_service (process) |
The SA's runtime process name, runs under uid/gid sysselection. |
APL system_basic |
Access permission level of the process — etc/init/selection_service.cfg. Higher than normal apps; governs which permissions the process can hold. |
| inner_kits / innerapi | The public C++ API surface (interfaces/inner_kits/selection_client), tagged platformsdk and version-scripted. Changes need compatibility review. |
| IDL proxy/stub | IPC codegen from interfaces/idl/*.idl via idl_gen_interface. Outputs in ${target_gen_dir}. |
| Taihe FFI | C/Rust-style FFI codegen (dep taihe_ffi_gen) for the selectionManager/selectionPanel JS modules. IDL in frameworks/ets/taihe/*/idl/. |
| sceneboard | Global GN switch window_manager_use_sceneboard; picks libwm_lite vs libwm and defines SCENE_BOARD_ENABLE. |
| CFI / UBSan / integer_overflow | Build-time sanitizers in every sanitize block. CFI cross-DSO requires matching configs across DSO boundaries. |
sys.selection.* |
System parameters driving switch/trigger/app/uid. Defaults in etc/para/selection.para. |
BootStartPhase |
SA boot phase for 8500; starts on-demand, not at init. |
usual.event.USER_SWITCHED |
Common event that also triggers SA 8500 load (multi-user config switch). |
Tests
Tests are registered in bundle.json under component.build.test and run via the OpenHarmony test runner.
- Unit tests —
test/unittest/:ohos_unittest("selection_service_unit_test"), groupselection_manager_ut. gtest/gmock only, usesHWTEST_F(Fixture, Name, TestSize.LevelN)macro (not rawTEST_F). Private/protected members are exposed via-Dprivate=public -Dprotected=publiccflags. - Mock tests —
test/unittest/mock/: separateohos_unittest("selection_service_unit_mock_test")(uses localmock_parameter.cpp). - Performance test —
test/unittest/PerformanceTest/:ohos_app("performanceTest"), a separate HAP, not part of the gtest suite. - Fuzz tests —
test/fuzztest/{selectioninputability_fuzzer,selectioninputlistener_fuzzer}: groupselection_service_fuzztest,ohos_fuzztest(...)withmodule_out_path = "selectionfwk/selectionfwk". - XTS —
test/xts/*.test.ets(declarative API conformance). - Autotests —
test/autotest/: scenario-driven Python.py+.jsonpairs (e.g.selectionfwk_normal_select,selectionfwk_kill_service,selectionfwk_user_switch,selectionfwk_lock_screen). Run on-device, not via GN unittest.
To run a focused test target from the workspace root:
./build.sh --product-name rk3568 --build-target selectionfwk_test
# then run the specific ohos_unittest binary on device, e.g.:
hdc shell /data/local/tmp/selection_service_unit_test
Constraints and boundaries
Do not
- Do not hand-edit generated files:
${target_gen_dir}/*_proxy.cpp,${target_gen_dir}/*_stub.cpp(frominterfaces/idl/*.idl), andframeworks/ets/taihe/*/srcglue (fromtaihe_ffi_gen). Change the.idl/source and rebuild. - Do not re-add
ability_runtime:ability_managertoservice/BUILD.gn— the SA is deliberately decoupled; that dep belongs inservice/plugins/only. A comment inservice/BUILD.gnwarns about this. - Do not drop the
sanitize/branch_protector_ret = "pac_ret"/cflags_ccvisibility /ldflags--exclude-libs,ALLblocks when copy-pasting a newBUILD.gnfor a production target (see "Build hardening"). - Do not add a new exported symbol to
libselection_client.z.sowithout confirming it matches*SelectionClient*inselection_client.versionscript(or updating the versionscript deliberately). - Do not modify the SA's exported C++ surface casually —
service/selection_service.mapis an explicit symbol whitelist (SelectionService::*,BaseSelectionInputMonitor::*,SelectionConfig::*, etc.). New exports need a map update. - Do not change
sys.selection.*defaults inetc/para/selection.paraor the SA on-demand profile (sa_profile/8500.json) without escalation — it changes when SA 8500 loads on real devices and affects user-switch behavior. - Do not bypass the
window_manager_use_sceneboardbranch when adding window_manager deps; runtime code paths are guarded bySCENE_BOARD_ENABLE.
Ask before
- Any change to
interfaces/inner_kits/selection_client/include/selection_client.h(public inner-api,platformsdk) — needs API compatibility review. - Any change to
etc/init/selection_service.cfg(uid/gid/secon/permissions) oretc/para/selection.para.dac(DAC) — security/permission boundary. - Any change to
hisysevent-SELECTIONFWK.yamlevent schema — DFX consumers depend on field names/types. - Any new
external_depsentry — must be inbundle.jsoncomponent.deps.componentsalready, or the build will fail; adding a new component dep is a cross-component boundary change.
Invariants
- C++17, namespace
OHOS::SelectionFwk; service code usesusing namespace MMI;. - License header: Apache 2.0 with copyright line — keep the year current; production files 2025, tests 2026.
- PC/2-in-1 + external keyboard/mouse only; selection content capped at 6,000 bytes; no cross-device use.
- Plugins export only via
plugin_exports.cppextern "C"+visibility("default"); everything else stays hidden.
Build hardening — keep consistent when adding/editing libs
All production targets set (do not drop these when copy-pasting a new BUILD.gn):
branch_protector_ret = "pac_ret"
sanitize = {
boundary_sanitize = true
cfi = true
cfi_cross_dso = true
cfi_vcall_icall_only = true
debug = false
integer_overflow = true
ubsan = true
}
Plus cflags_cc with -fvisibility=hidden/-fvisibility-inlines-hidden and ldflags with -Wl,--exclude-libs,ALL for shared libs. The plugins BUILD.gn documents why: CFI config must match native_rdb to avoid cross-DSO CFI failures, and ldflags += [ "-Wl,-z,now" ] is required so CFI type-checks complete at load time.
CFI blocklist for tests lives at test/unittest/ipc_blocklist.txt — add new test-only source paths there if they trip CFI (gtest/gmock and test/unittest/* are already excluded).
Conditional: sceneboard window manager
Several BUILD.gn files branch on the global window_manager_use_sceneboard:
if (window_manager_use_sceneboard) {
external_deps += [ "window_manager:libwm_lite" ]
defines += [ "SCENE_BOARD_ENABLE" ]
} else {
external_deps += [ "window_manager:libwm" ]
}
Keep this branch in sync when adding window_manager deps; SCENE_BOARD_ENABLE guards runtime code paths.
Codegen to be aware of
- IDL (
interfaces/idl/*.idl→*_proxy.cpp/*_stub.cppin${target_gen_dir}): never hand-edit generated files; change the.idland rebuild. - Taihe FFI (
frameworks/ets/taihe/*/idl/): thetaihe_ffi_gencomponent generates glue forSelectionManager/SelectionPanel. Treatframeworks/ets/taihe/*/srcpartly as generated.
When editing SA on-demand behavior
sa_profile/8500.json controls start/stop on demand via sys.selection.switch and the usual.event.USER_SWITCHED common event. etc/para/selection.para sets defaults; etc/para/selection.para.dac sets DAC permissions. Changing switch defaults here affects when SA 8500 loads on real devices.
Verification loop (how to prove the task is done)
- Build the component clean:
./build.sh --product-name rk3568 --ccache --build-target selectionfwk— a clean build is the sanitizer/static-analysis gate; if it fails on CFI/UBSan, fix the code, do not weaken thesanitizeblock. - If you added a test or touched
service//service/plugins//interfaces/, build and run tests:./build.sh --product-name rk3568 --build-target selectionfwk_test, thenhdc shell /data/local/tmp/selection_service_unit_teston device. - If you touched a public header under
interfaces/inner_kits/selection_client/include/, confirmselection_client.versionscriptstill matches the exported symbols and there is no ABI break. - If you touched
interfaces/idl/*.idl, confirm the regenerated proxy/stub builds (it lands in${target_gen_dir}).
Done definition
A task is done when: (a) --build-target selectionfwk succeeds with no new sanitizer errors; (b) relevant *_test.cpp for the touched path builds and passes; (c) generated code (IDL/Taihe) was regenerated, not hand-edited; (d) no Do not rule was violated; (e) any public-API, permission, or on-demand change flagged an Ask before item.
Final response expectations
Report: the files changed (with file:line for the key edits), the exact build/test commands run and their pass/fail result, which Do not/Ask before items were considered, and any Ask before item that needs human escalation.
Fallback if validation cannot run
If the OpenHarmony tree is not available (cannot run build.sh), say so explicitly, do not claim success, and at minimum: compile-check the changed BUILD.gn for the required sanitize/visibility/ldflags blocks, verify no generated file was hand-edited, and flag that on-device build/test validation is still pending.
External OpenCode config
None — no opencode.json, CLAUDE.md, or .cursorrules exist in this repo at time of writing.