```mermaid %%{init: {'flowchart': { 'curve': 'linear', 'nodeSpacing': 26, 'rankSpacing': 42 }}}%% flowchart TD classDef default fill:none,stroke:#555,stroke-width:2px,color:#ffffff; classDef inject fill:none,stroke:#01579b,stroke-width:2px,color:#ffffff; classDef config fill:none,stroke:#33691e,stroke-width:2px,stroke-dasharray:5 5,color:#ffffff; classDef policy fill:none,stroke:#b71c1c,stroke-width:2px,color:#ffffff; classDef native fill:none,stroke:#4a148c,stroke-width:2px,color:#ffffff; classDef elf fill:none,stroke:#e65100,stroke-width:2px,color:#ffffff; classDef fuse fill:none,stroke:#1b5e20,stroke-width:2px,color:#ffffff; classDef cache fill:none,stroke:#ad1457,stroke-width:2px,stroke-dasharray:4 4,color:#ffffff; classDef os fill:none,stroke:#546e7a,stroke-width:2px,color:#ffffff; subgraph Phase1 ["第一层:双注入后端、仲裁与 MediaProvider 生命周期"] direction TB PostFs["Zygisk module post-fs-data.sh"] --> LspDb{"LSPosed 中 FuseHide 是否启用
且作用域包含 MediaProvider"} LspDb -->|是| LspFlag["创建 disable + lsp_scope_enabled
禁止 Zygisk 与 LSPosed 重复注入"] LspDb -->|否| ZygiskAvailable["允许 Zygisk backend"] LSP["LSPosed / libxposed"] -->|onPackageLoaded| Entry["Entry.onPackageLoaded"] Entry --> TargetLsp{"包名是否为 AOSP / Google MediaProvider"} TargetLsp -->|否| LspIgnore["忽略"] TargetLsp -->|是| LoadSo["System.loadLibrary(libfusehide.so)"] LoadSo --> LspJniOnLoad["JNI_OnLoad
保存 MediaProvider JavaVM"] Entry --> AttachHook["Hook Application.attach"] AttachHook --> MediaApp["捕获 MediaProvider Application"] MediaApp --> LspJava["MainThreadTask
Entry.registerStatusReceiver"] Zygisk["Zygisk ModuleBase"] --> ZOnLoad["onLoad
保存 Api + JavaVM"] ZOnLoad --> ZPre["preAppSpecialize"] ZPre --> TargetZ{"nice_name 是否为 MediaProvider"} TargetZ -->|否| ZClose["DLCLOSE_MODULE_LIBRARY"] TargetZ -->|是| ScopeGate{"lsp_scope_enabled 是否存在"} LspFlag -.-> ScopeGate ZygiskAvailable -.-> ScopeGate ScopeGate -->|是| ZSkip["跳过 Zygisk backend"] ScopeGate -->|否| Preload["PreloadModuleRuntime
PreloadInjectedDex + android_dlopen_ext(libfusehide.so)"] Preload --> ZPost["postAppSpecialize"] ZPost --> DlopenMonitor["InstallDlopenMonitor
DobbyHook linker do_dlopen"] DlopenMonitor --> FuseLoaded["观察 libfuse_jni.so 加载"] FuseLoaded --> ZBootstrap["JNI_OnLoad + native_init(DobbyHookAdapter)
RegisterAllNativeMethods"] ZBootstrap --> StartJava["StartInjectedJavaWhenApplicationReady"] StartJava --> ZEntry["ZygiskEntry.init(Context)"] ZEntry --> ZJava["ZygiskEntry.registerStatusReceiver"] LSP -->|native_init.list: libfusehide.so| LspNativeApi["libxposed native_init(api)
保存 api.hookFunc"] LspNativeApi -->|libxposed 通知 loadedLibrary| SharedPostInit["PostNativeInit(loadedLibrary)"] ZBootstrap --> SharedPostInit SharedPostInit -->|libfuse_jni.so| InstallHooks["InstallFuseHooks"] LspJava --> JavaRuntime["共享 Java 控制面"] ZJava --> JavaRuntime end class PostFs,LspDb,LspFlag,ZygiskAvailable,LSP,Entry,TargetLsp,LspIgnore,LoadSo,LspJniOnLoad,AttachHook,MediaApp,LspJava,Zygisk,ZOnLoad,ZPre,TargetZ,ZClose,ScopeGate,ZSkip,Preload,ZPost,DlopenMonitor,FuseLoaded,ZBootstrap,StartJava,ZEntry,ZJava,LspNativeApi,SharedPostInit,JavaRuntime inject; class InstallHooks native; subgraph Phase2 ["第二层:共享 Java 控制面与经过认证的配置同步"] direction TB UI["FuseHide App UI"] --> Store["HideConfigStore"] Store --> AppPrefs[("Device-protected SharedPreferences
saved config + reload token")] Store --> Provider["HideConfigProvider
get_hide_config"] Store --> RequestReceiver["HideConfigRequestReceiver"] Store --> ReloadBc["ACTION_RELOAD_HIDE_CONFIG
携带 reloadToken"] Store --> QueryBc["ACTION_GET_APPLIED_HIDE_CONFIG
携带 queryToken"] JavaRuntime --> ReceiverHub["注册 injected-process Receiver 集合"] ReceiverHub --> StatusRx["StatusBroadcastReceiver"] ReceiverHub --> ReloadRx["配置 reload Receiver"] ReceiverHub --> QueryRx["已应用配置 query Receiver"] ReceiverHub --> BootRx["LOCKED_BOOT / BOOT / USER_UNLOCKED
触发重试"] ReceiverHub --> PackageRx["PACKAGE_ADDED / PACKAGE_REMOVED
忽略 replacement churn"] ReceiverHub --> InitialReload["initial reload"] InitialReload --> ReloadFlow["reloadInjectedProcessConfig"] BootRx --> ReloadFlow ReloadFlow --> Snapshot["先加载 injected-process snapshot"] Snapshot --> SnapshotApply["applyBundleToNative(snapshot)
允许早期恢复策略"] SnapshotApply --> ProviderTry["读取 HideConfigProvider"] ProviderTry --> ProviderMatch{"provider token 与 snapshot
是否已经一致"} ProviderMatch -->|是| ReloadDone["无需重复 JNI apply"] ProviderMatch -->|否| ProviderApply["applyBundleToNative(provider)"] ProviderTry -->|失败| AuthFallback["requestInjectedProcessConfigBundle
随机 action + queryToken + one-shot mutable PendingIntent"] AuthFallback --> RequestReceiver RequestReceiver --> AuthGate{"校验 PendingIntent
broadcast / mutable / user / creatorPackage / creatorUid"} AuthGate -->|可信 MediaProvider| PiReply["PendingIntent.send(config + token)"] AuthGate -->|失败| Reject["拒绝请求"] PiReply --> FallbackApply["applyBundleToNative(fallback)"] ReloadBc -.-> ReloadRx ReloadRx --> ExplicitProvider["provider 优先"] ExplicitProvider --> ExplicitToken{"bundleToken == requestedToken"} ExplicitToken -->|是| ExplicitApply["applyBundleToNative"] ExplicitToken -->|否 / provider 失败| AuthFallback ProviderApply --> SaveSnapshot["saveInjectedProcessSnapshot"] FallbackApply --> SaveSnapshot ExplicitApply --> SaveSnapshot SaveSnapshot --> Ack["ACTION_SET_CONFIG_STATUS
返回 applied / message / token"] Ack -.-> UI QueryBc -.-> QueryRx QueryRx --> NativeSnapshot["从 HideConfigNativeBridge
读取 CurrentHideConfig"] NativeSnapshot --> QueryReply["ACTION_SET_APPLIED_HIDE_CONFIG"] QueryReply -.-> UI PackageRx --> PackageChanged["HideConfigNativeBridge.notifyPackageSetChanged"] end class UI,Store,AppPrefs,Provider,RequestReceiver,ReloadBc,QueryBc,ReceiverHub,StatusRx,ReloadRx,QueryRx,BootRx,PackageRx,InitialReload,ReloadFlow,Snapshot,SnapshotApply,ProviderTry,ProviderMatch,ReloadDone,ProviderApply,AuthFallback,AuthGate,PiReply,Reject,FallbackApply,ExplicitProvider,ExplicitToken,ExplicitApply,SaveSnapshot,Ack,NativeSnapshot,QueryReply,PackageChanged config; subgraph Phase3 ["第三层:Native 配置编译、UID→Package 解析与有效规则"] direction TB SnapshotApply --> JNIApply["HideConfigNativeBridge.applyHideConfig"] ProviderApply --> JNIApply FallbackApply --> JNIApply ExplicitApply --> JNIApply JNIApply --> ApplyConfig["ApplyHideConfig"] ApplyConfig --> PublishConfig["原子发布
CurrentHideConfig + CompiledHideConfig"] ApplyConfig --> ConfigGen["gHideConfigGeneration++"] ApplyConfig --> Compile["BuildCompiledHideConfig"] Compile --> GlobalFragment["globalRuleFragment
global targets + hide-all exemptions"] Compile --> PackageFragments["packageRuleFragments
[package.name] 专属规则"] Compile --> HiddenPkgSet["hiddenPackageSet
决定哪些包继承 global rule"] PackageChanged --> PackageGen["NotifyUidRulePackageSetChanged
gUidPackageSetGeneration++"] ReqUidSource["FUSE request uid"] --> ResolveUid["ResolveHideRuleForUid(uid)"] ResolveUid --> UidCache{"gUidHideRuleCache
configGeneration + packageSetGeneration 命中?"} UidCache -->|命中| EffectiveRule["CompiledHideRule
UID 的有效隐藏策略 + fingerprint"] UidCache -->|未命中| PM["PackageManager.getPackagesForUid(uid)"] PM --> Merge["排序/去重 package set
hiddenPackages 命中→合并 global
逐包合并 package-specific fragments"] GlobalFragment -.-> Merge PackageFragments -.-> Merge HiddenPkgSet -.-> Merge Merge --> BuildRule["BuildCompiledHideRule
canonical sets + relative prefixes + fingerprint"] BuildRule --> UidCacheStore["写入 UID rule cache"] UidCacheStore --> EffectiveRule end class JNIApply,ApplyConfig,PublishConfig,ConfigGen,Compile,GlobalFragment,PackageFragments,HiddenPkgSet,PackageGen,ReqUidSource,ResolveUid,UidCache,EffectiveRule,PM,Merge,BuildRule,UidCacheStore policy; subgraph Phase4 ["第四层:Native Hook 初始化、ABI Gate 与解析策略"] direction TB InstallHooks --> ModuleMap["FindTargetModule(libfuse_jni.so)
/proc/self/maps"] ModuleMap --> HookPlan["ResolveDeviceHookInstallPlan"] HookPlan --> ReqCtxResolve["解析 fuse_req_ctx
RTLD_DEFAULT → bound relocation → libfuse.so symbol"] ReqCtxResolve --> ReqCtxGate{"解析成功?"} ReqCtxGate -->|是| ReqCtxFn["Process.fuseReqCtx
RuntimeState::ReqUid 使用官方函数"] ReqCtxGate -->|否| ReqCtxFail["fail closed
不再读取未经验证的 fuse_req 内部偏移"] HookPlan --> StringGate{"libc++ string ABI
是否通过可信 profile 验证"} StringGate -->|否| SkipString["跳过需要解码/构造
std::string object 的 Hook"] StringGate -->|是| AllowString["允许 string ABI Hook"] HookPlan --> DirAbi["DetectDirectoryEntriesAbi"] DirAbi --> DirAbiGate{"shared_ptr / value / unknown"} DirAbiGate -->|shared_ptr| SharedDir["shared_ptr DirectoryEntries wrappers"] DirAbiGate -->|value| ValueDir["API 37 value-vector wrappers
缓存仅保存 name/type 语义快照"] DirAbiGate -->|unknown| SkipDir["跳过不安全的 C++ container Hook"] ModuleMap --> Embedded{"模块路径是否为 embedded APEX (!/)"} Embedded -->|否| FileElf["BuildFileElfContext
先装 minimal file-backed hooks"] FileElf --> Minimal["InstallMinimalCoreHooks
InstallMinimalDebugHooks"] Embedded -->|是| Advanced["直接进入 advanced resolver"] Minimal --> CoreComplete{"core hooks 是否完整"} CoreComplete -->|否| Advanced CoreComplete -->|是| DebugHooks["InstallAdvancedDebugHooks"] Advanced --> Sources["runtime ELF / file ELF / relocations
.gnu_debugdata / resolved anchors / trusted profile fallback"] Sources --> AdvancedInstall["InstallAdvancedCoreHooks"] AdvancedInstall --> DebugHooks LspNativeApi -.-> InstallerApi["gHookInstaller"] ZBootstrap -.-> InstallerApi InstallerApi --> HookBackend{"实际 inline hook backend"} HookBackend -->|LSPosed| LspHook["libxposed api.hookFunc"] HookBackend -->|Zygisk| DobbyHookNode["DobbyHookAdapter"] DebugHooks --> HookSet["最终 Hook 集合"] end class ModuleMap,HookPlan,ReqCtxResolve,ReqCtxGate,ReqCtxFn,ReqCtxFail,StringGate,SkipString,AllowString,DirAbi,DirAbiGate,SharedDir,ValueDir,SkipDir,InstallerApi,HookBackend,LspHook,DobbyHookNode,HookSet native; class Embedded,FileElf,Minimal,CoreComplete,Advanced,Sources,AdvancedInstall,DebugHooks elf; subgraph Phase5 ["第五层:Hook 覆盖面"] direction TB HookSet --> PolicyHooks["路径 / 特殊存储 / 缓存策略
is_app_accessible_path
is_package_owned_path / is_bpf_backing_path
ShouldNotCache / strcasecmp / EqualsIgnoreCase"] HookSet --> RequestHooks["FUSE handler
pf_lookup / lookup_postfilter / access
open / opendir / getattr
readdir / readdirplus / readdir_postfilter
do_readdir_common
mkdir / mknod / create / unlink / rmdir / rename"] HookSet --> DirHooks["目录容器
GetDirectoryEntries
addDirectoryEntriesFromLowerFs"] HookSet --> ReplyHooks["FUSE reply
fuse_reply_entry / attr / buf / err
notify_inval_entry / notify_inval_inode"] HookSet --> LibcHooks["lower-fs / libc fallback
stat / lstat / getxattr / lgetxattr
open / __open_2 / mkdir / mknod"] PolicyHooks --> AndroidSpecial["Android/data / Android/obb 特殊链
UnicodePolicy 去除 Default_Ignorable
保持 MediaProvider 专用访问控制语义"] end class PolicyHooks,RequestHooks,DirHooks,ReplyHooks,LibcHooks native; class AndroidSpecial policy; subgraph Phase6 ["第六层:FUSE 数据面——请求判定、目录过滤与错误语义"] direction TB Caller(("受限 App")) -->|syscall 访问 /sdcard| VFS["Kernel VFS"] VFS --> DevFuse["/dev/fuse"] DevFuse --> Worker["MediaProvider libfuse_jni worker"] Worker --> Session["ScopedFuseRequestSession
RememberFuseSession"] Session --> ReqUid["RuntimeState::ReqUid(req)
fuse_req_ctx(req)->uid"] ReqUid --> ReqUidSource ReqUid --> EffectiveRule EffectiveRule --> Gate{"请求类型"} Gate -->|lookup/access/open/getattr/mutation| NamedPath["ClassifyHiddenNamedTarget / ClassifyHiddenPath
exact root + descendant policy"] NamedPath --> HiddenGate{"命中隐藏策略?"} HiddenGate -->|是| HiddenReply["优先 fuse_reply_err
ENOENT / EPERM 等隐藏语义"] HiddenGate -->|否| OriginalPath["调用原始 handler / lower-fs"] HiddenReply --> ErrBridge["ReplyErrorBridge
无法直接 reply 时 arm errno remap"] ErrBridge --> ErrRemap["WrappedReplyErr
修正 EEXIST / EISDIR / ENOTEMPTY / ENOTDIR 等存在性泄漏"] Gate -->|readdir 家族| ReaddirCtx["记录 PendingReaddirContext
req.unique + uid + ino + path"] ReaddirCtx --> DirRead["WrappedGetDirectoryEntries
按 UID 读取/过滤目录"] DirRead --> LowerDir["addDirectoryEntriesFromLowerFs
再次过滤 lower-fs 追加项"] LowerDir --> ReplyBuf["WrappedReplyBuf
最终 universal wire filter"] ReplyBuf --> RecoverCtx["恢复 uid / ino / parent path
必要时使用 recent hidden parent fallback"] RecoverCtx --> WireDetect["识别 dirent / direntplus / fuse_read_out
plain / plus / postfilter / auto fallback"] WireDetect --> Rewrite["移除隐藏条目
重写 payload size / buffer"] Rewrite --> OriginalReplyBuf["调用原始 fuse_reply_buf"] OriginalPath --> PositiveReply["fuse_reply_entry / fuse_reply_attr"] PositiveReply --> LearnPath["学习 inode→path / hidden-subtree inode
记录 visible root parent"] LearnPath --> Caller OriginalReplyBuf --> Caller ErrRemap --> Caller end class Caller,VFS,DevFuse,Worker os; class Session,ReqUid,Gate,NamedPath,HiddenGate,HiddenReply,OriginalPath,ErrBridge,ErrRemap,ReaddirCtx,DirRead,LowerDir,ReplyBuf,RecoverCtx,WireDetect,Rewrite,OriginalReplyBuf,PositiveReply,LearnPath fuse; subgraph Phase7 ["第七层:缓存、Generation 与跨 UID 一致性维护"] direction TB CacheHub["运行时一致性层"] --> UidRuleCache["UID rule cache
key=uid
guard=configGeneration + packageSetGeneration"] CacheHub --> PathClassCache["HiddenPathClassification cache
key=uid + path
guard=configGeneration + packageSetGeneration"] CacheHub --> RootSnapshot["RootSnapshot cache
key=uid + effective-rule fingerprint + root path
guard=config/package/parent generations"] RootSnapshot --> CollisionGate["fingerprint 命中后再做
CompiledHideRulesSemanticallyEqual
碰撞只变 cache miss"] CacheHub --> InodeCache["inode→path cache
有上限并保护隐藏目标/祖先路径"] CacheHub --> HiddenInodes["hidden-subtree inode → rule 集合
避免跨 UID 策略串用"] CacheHub --> PendingState["pending readdir context / recent hidden parent
UID-aware fallback state"] ConfigGen --> ClearConfigCaches["清 UID rule / root snapshot / path classification
invalidate tracked hidden targets"] PackageGen --> ClearPackageCaches["清 UID 派生 cache
root snapshot / path classification
invalidate tracked targets"] ClearConfigCaches --> CacheHub ClearPackageCaches --> CacheHub DirRead --> RootSnapshot NamedPath --> PathClassCache ResolveUid --> UidRuleCache LearnPath --> InodeCache LearnPath --> HiddenInodes ReaddirCtx --> PendingState RequestHooks --> MutationTrack["root mutation request
记录 pending mutation"] MutationTrack --> MutationReply["WrappedReplyErr 确认 err==0 后
gRootSnapshotParentGeneration++"] MutationReply --> RootSnapshot ReplyHooks --> CacheControl["WrappedReplyEntry / Attr / ShouldNotCache
entry_timeout=0 / attr_timeout=0 / no-cache"] CacheControl --> Invalidate["ScheduleHiddenEntryInvalidation
ScheduleSpecificEntryInvalidation
ScheduleHiddenInodeInvalidation"] HiddenReply --> Invalidate Rewrite --> Invalidate Invalidate --> KernelCache["fuse_lowlevel_notify_inval_entry / inode
尽快压掉共享 dentry / inode cache"] Session --> SessionChange{"FUSE session 是否变化"} SessionChange -->|变化| ClearSession["清 session-scoped tracking
inode / subtree / pending / errno-remap 等状态"] ClearSession --> CacheHub end class CacheHub,UidRuleCache,PathClassCache,RootSnapshot,CollisionGate,InodeCache,HiddenInodes,PendingState,ClearConfigCaches,ClearPackageCaches,MutationTrack,MutationReply,CacheControl,Invalidate,KernelCache,SessionChange,ClearSession cache; style Phase1 fill:none,stroke:#01579b,stroke-width:2px,stroke-dasharray:5 5 style Phase2 fill:none,stroke:#33691e,stroke-width:2px,stroke-dasharray:5 5 style Phase3 fill:none,stroke:#b71c1c,stroke-width:2px,stroke-dasharray:5 5 style Phase4 fill:none,stroke:#4a148c,stroke-width:2px,stroke-dasharray:5 5 style Phase5 fill:none,stroke:#e65100,stroke-width:2px,stroke-dasharray:5 5 style Phase6 fill:none,stroke:#1b5e20,stroke-width:2px,stroke-dasharray:5 5 style Phase7 fill:none,stroke:#ad1457,stroke-width:2px,stroke-dasharray:5 5 ```