Skip to content

Latest commit

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

EverywhereCore-Android

Go core (mihomo + sing-box + xray-core, glued by gomobile) packaged as an Android .aar. Consumed by the Everywhere Android app's VpnService via Gradle. Sibling of EverywhereCore (iOS/macOS xcframework) and EverywhereCore-Windows (c-shared DLL) — same wrapper design, same upstream pins.

Each core runs its own TUN inbound directly on the file descriptor returned by VpnService.Builder.establish() — there is no userland tun→socks shim in between.

The three core dependencies are not vendored. go/go.mod pins them by Go-module-compatible semver; the GitHub Actions workflow at .github/workflows/upstream-watch.yml polls the Go proxy daily and auto-cuts a new release whenever any upstream moves.

Layout

go/ gomobile entry package; *.go + go.mod
Scripts/build.sh gomobile bind → EverywhereCore.aar
.github/workflows/
upstream-watch.yml daily upstream poll + auto-release

Building locally

git clone https://github.com/NodePassProject/EverywhereCore-Android
cd EverywhereCore-Android
Scripts/build.sh # gomobile bind, deps fetched from Go proxy

Requires an Android SDK with an NDK (ANDROID_HOME or ANDROID_NDK_HOME). Produces ./EverywhereCore.aar with arm64-v8a, armeabi-v7a, x86_64, and x86 slices, built against -androidapi 26 (override with ANDROID_API=<n>).

Consuming from an Android project

Drop the .aar (from a local build or a GitHub Release asset) into app/libs/ and add:

dependencies {
implementation(files("libs/EverywhereCore.aar"))
}

The bindings land in the com.argsment.everywhere.evcore package as a static-method facade named Evcore.

Calling from a VpnService

classEverywhereVpnService : VpnService() {
privateinnerclassServiceProtector : Protector {
overridefunprotect(fd:Int): Boolean=this@EverywhereVpnService.protect(fd)
}
privatevar tunFd:ParcelFileDescriptor?=nullfunstartTunnel(coreType:String, config:String) {
val builder =Builder()
.setMtu(MTU)
.addAddress("172.19.0.1", 30)
.addRoute("0.0.0.0", 0)
.addDnsServer("172.19.0.2")
val pfd = builder.establish() ?: error("VPN permission revoked")
tunFd = pfd
Evcore.setSocketProtector(ServiceProtector()) // BEFORE startCoreEvcore.setResourcesPath(noBackupFilesDir.absolutePath)
Evcore.startCore(coreType, config, pfd.fd.toLong(), MTU.toLong())
}
funstopTunnel() {
Evcore.stopAll()
tunFd?.close() // unblocks any reader still inside the cores
tunFd =null
}
}

coreType is "xray", "singbox", or "mihomo". The config string must declare a TUN inbound for the active core (the app-side ConfigNormalizer takes care of that): a tun inbound for Xray, a type: tun inbound for sing-box, an enabled tun: block for mihomo. The fd itself is injected by this framework — never write it into the config text.

setSocketProtector is mandatory. Android routes every socket the VPN process creates back into its own TUN unless VpnService.protect(fd) is called on it — without the protector, core outbound traffic loops through the tunnel and nothing connects. (iOS has no equivalent because an NE's sockets bypass the tunnel by design.)

Feed network changes from a ConnectivityManager.NetworkCallback:

overridefunonCapabilitiesChanged(network:Network, caps:NetworkCapabilities) {
val link = connectivityManager.getLinkProperties(network) ?:returnval index =NetworkInterface.getByName(link.interfaceName)?.index ?:-1Evcore.updateDefaultInterface(
link.interfaceName ?:"", index,
!caps.hasCapability(NET_CAPABILITY_NOT_METERED), false)
}
overridefunonLost(network:Network) {
Evcore.updateDefaultInterface("", -1, false, false)
}

How releases happen

.github/workflows/upstream-watch.yml runs daily at 08:00 UTC and on manual workflow_dispatch. Each run:

  1. Queries proxy.golang.org/<module>/@latest for mihomo, sing-box, and xray-core (stable tags only — no pre-releases).
  2. Compares each against the version currently pinned in go/go.mod.
  3. If at least one is newer (or if dispatched with force_release: true):
    • go get each to its latest, go mod tidy
    • Scripts/build.sh to build the aar
    • Compute SHA256, commit + tag vYYYY.MM.DD
    • Append .1, .2, … to the tag if multiple runs land same day
    • Push tag + main; gh release create with the aar attached
  4. Otherwise: no-op (logged as a notice).

To bootstrap the first release after pushing this repo, run the workflow from the Actions tab with force_release: true.

Pinning a specific upstream version manually

Edit go/go.mod, push to main. The next cron run will detect the manual pin is current (or stale) and act accordingly. If you want a release for a manual bump immediately, dispatch the workflow with force_release: true.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages