Summary
metadata.Uid is always 0 for all connections on Android Q+. UID rules (UID,90010466,proxy) never match — traffic falls through to MATCH. PROCESS-NAME rules work correctly.
Root Cause
The Java layer calls ConnectivityManager.getConnectionOwnerUid() and gets the correct UID. It uses this UID to resolve the package name via PackageManager.getPackagesForUid(uid). However, the JNI bridge (resolve_process) only returns the package name string — the UID integer is discarded at the JNI boundary.
Java: getConnectionOwnerUid() → uid ✓ → getPackagesForUid(uid) → "com.example" ✓
JNI: resolve_process() → returns char* (package name only)
Go: metadata.Process = "com.example" ✓
metadata.Uid = 0 ← never assigned
Code path
android/service/.../VpnService.kt — resolverProcess() calls getConnectionOwnerUid(), gets real UID, resolves package name
android/core/src/main/cpp/core.cpp — JNI bridge receives UID but only passes package name string back
core/bride.go / core/bride.h — CGO bridge signature is char*, no room for UID
core/lib.go — assigns returned string to metadata.Process, metadata.Uid stays 0
core/platform/procfs.go — /proc/net/tcp fallback gated by version < 29, skipped on Android Q+
Proposed Fix
Modify the JNI bridge to return both package name and UID:
bride.h — change return type or add out-parameter for UID
core.cpp — pass UID from Java getConnectionOwnerUid() back through JNI
bride.go — CGO bridge returns struct {name string, uid uint32}
lib.go — assign UID to metadata.Uid
Reproduction
- Configure mihomo with
UID,<uid>,<proxy> rules
- Check
/connections API: all entries show "uid": 0
- UID rules never match, traffic falls to
MATCH
- PROCESS-NAME rules work correctly (confirms process resolution is functional)
Environment
- FlClash latest
- Android 15 (API 35)
Summary
metadata.Uidis always0for all connections on Android Q+. UID rules (UID,90010466,proxy) never match — traffic falls through toMATCH. PROCESS-NAME rules work correctly.Root Cause
The Java layer calls
ConnectivityManager.getConnectionOwnerUid()and gets the correct UID. It uses this UID to resolve the package name viaPackageManager.getPackagesForUid(uid). However, the JNI bridge (resolve_process) only returns the package name string — the UID integer is discarded at the JNI boundary.Code path
android/service/.../VpnService.kt—resolverProcess()callsgetConnectionOwnerUid(), gets real UID, resolves package nameandroid/core/src/main/cpp/core.cpp— JNI bridge receives UID but only passes package name string backcore/bride.go/core/bride.h— CGO bridge signature ischar*, no room for UIDcore/lib.go— assigns returned string tometadata.Process,metadata.Uidstays 0core/platform/procfs.go—/proc/net/tcpfallback gated byversion < 29, skipped on Android Q+Proposed Fix
Modify the JNI bridge to return both package name and UID:
bride.h— change return type or add out-parameter for UIDcore.cpp— pass UID from JavagetConnectionOwnerUid()back through JNIbride.go— CGO bridge returns struct{name string, uid uint32}lib.go— assign UID tometadata.UidReproduction
UID,<uid>,<proxy>rules/connectionsAPI: all entries show"uid": 0MATCHEnvironment