Share your VPN connection over hotspot or repeater! (root required)
VPN Hotspot
Connecting things to your VPN made simple. Share your VPN connection over hotspot or repeater. (root required)
This app is useful for:
- Connecting things that don't support VPN like Chromecasts behind corporate firewalls;
- Setting up gapps behind corporate firewalls;
- Connecting to your mobile hotspot but you're not bothered to set up VPN on your device;
- Identifying, monitoring and blocking (unwanted) clients;
- Bypassing tethering limits by:
P.S. You can also do the similar on Windows, Mac, and iOS.
Features That Requires System App Installation
The following features in the app requires it to be installed under /system/priv-app since some restricted permissions are required. One way to do this is to use App systemizer for Magisk.
- (Android 8-10, since app v2.4.0)
android.permission.OVERRIDEWIFICONFIG: Read/write system Wi-Fi hotspot configuration. (#117)
android.permission.CONNECTIVITYUSERESTRICTED_NETWORKSandroid.permission.LOCALMACADDRESSandroid.permission.MANAGE_USBandroid.permission.OVERRIDEWIFICONFIGandroid.permission.READWIFICREDENTIALandroid.permission.TETHER_PRIVILEGEDandroid.permission.WRITESECURESETTINGS- Other system-app exclusive features that are not gated by permissions.
Q & A
Search the issue tracker for more.
What persistent changes can this app do to my system?
This app only indirectly write persistent changes through system APIs thus it should be generally pretty safe.
- In Supplicant mode, the repeater asks
wpasupplicantto create a persistent group on startup, which Android persists in/data/vendor/wifi/wpa/p2psupplicant.confor/data/misc/wifi/p2p_supplicant.conf. - If you edited the system Wi-Fi hotspot configuration through this app, those changes will persist.
- If you toggle tethering hardware offload through this app, the Android global
tetheroffloaddisabledsetting will persist.
Routing, firewall, addresses, and daemon/service state managed by this app are cleaned up when stopped, by Clean, or upon reboot.
No root?
Failed to create group due to internal error/repeater shuts down after a while?
This could caused by the Wi-Fi channel you selected is no longer available, due to:
- Your device doesn't support operating on this channel, or
- There is some nearby Wi-Fi direct device that broadcasted that it can't operate on the channel you picked.
Private APIs used / Assumptions for Android customizations
a.k.a. things that can go wrong if this app doesn't work.
This is a list of stuff that might impact this app's functionality if unavailable. This is only meant to be an index. You can read more in the source code. API restrictions are updated up to SHA-256 checksum 9102af02fe6ab68b92464bdff5e5b09f3bd62c65d1130aaf85d3296f17d38074.
Greylisted/blacklisted APIs or internal constants: (some constants are hardcoded or implicitly used)
- (prior to API 30)
Landroid/net/ConnectivityManager;->getLastTetherError(Ljava/lang/String;)I,max-target-r - (prior to API 30)
Landroid/net/ConnectivityManager;->EXTRAACTIVELOCAL_ONLY:Ljava/lang/String;,lo-prio,max-target-o - (prior to API 30)
Landroid/net/ConnectivityManager;->EXTRAACTIVETETHER:Ljava/lang/String;,max-target-r - (prior to API 30)
Landroid/net/ConnectivityManager;->EXTRAAVAILABLETETHER:Ljava/lang/String;,max-target-r - (prior to API 30)
Landroid/net/ConnectivityManager;->ACTIONTETHERSTATE_CHANGED:Ljava/lang/String;,max-target-r - (prior to API 30)
Landroid/net/ConnectivityManager;->EXTRAERROREDTETHER:Ljava/lang/String;,max-target-r - (since API 30)
Landroid/net/ConnectivityModuleConnector;->INPROCESSSUFFIX:Ljava/lang/String; - (since API 31)
Landroid/net/INetd$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/INetd; - (since API 31)
Landroid/net/INetd;->ipSecUpdateSecurityPolicy(IIILjava/lang/String;Ljava/lang/String;IIII)V - (since API 30)
Landroid/net/IIntResultListener$Stub;-><init>()V,blocked - (since API 30)
Landroid/net/IIntResultListener;->onResult(I)V,blocked - (since API 30)
Landroid/net/ITetheringConnector;->stopTethering(ILjava/lang/String;Landroid/net/IIntResultListener;)V,blocked - (since API 30)
Landroid/net/ITetheringConnector;->stopTethering(ILjava/lang/String;Ljava/lang/String;Landroid/net/IIntResultListener;)V,blocked - (since API 30)
Landroid/net/TetheringManager$ConnectorConsumer;->onConnectorAvailable(Landroid/net/ITetheringConnector;)V,blocked - (since API 30)
Landroid/net/TetheringManager$TetheringEventCallback;->onTetherableInterfaceRegexpsChanged(Landroid/net/TetheringManager$TetheringInterfaceRegexps;)V,blocked - (since API 31)
Landroid/net/TetheringManager$TetheringEventCallback;->onSupportedTetheringTypes(Ljava/util/Set;)V,blocked - (since API 30)
Landroid/net/TetheringManager;->getConnector(Landroid/net/TetheringManager$ConnectorConsumer;)V,blocked
Landroid/net/TetheringManager;->TETHERERROR:I,blocked
- (since API 30)
Landroid/net/TetheringManager;->TETHERING_VIRTUAL:I,blocked - (since API 30)
Landroid/net/TetheringManager;->TETHERING_WIGIG:I,blocked - (since API 31)
Landroid/net/IpSecManager;->DIRECTION_FWD:I,blocked - (since API 31)
Landroid/net/IpSecManager;->INVALIDSECURITYPARAMETER_INDEX:I,blocked - (since API 31)
Landroid/net/wifi/SoftApCapability;->getCountryCode()Ljava/lang/String;,blocked - (since API 33)
Landroid/net/wifi/SoftApConfiguration$Builder;->setRandomizedMacAddress(Landroid/net/MacAddress;)Landroid/net/wifi/SoftApConfiguration$Builder;,blocked - (since API 31)
Landroid/net/wifi/SoftApConfiguration;->BANDTYPES:I,blocked - (since API 31)
Landroid/net/wifi/SoftApInfo;->getApInstanceIdentifier()Ljava/lang/String;,blocked Landroid/net/wifi/ISoftApCallback$Stub;->asInterface(Landroid/os/IBinder;)Landroid/net/wifi/ISoftApCallback;,lo-prio,max-target-o- (since API 33)
Landroid/net/wifi/IWifiManager;->registerLocalOnlyHotspotSoftApCallback(Landroid/net/wifi/ISoftApCallback;Landroid/os/Bundle;)V,blocked - (prior to API 31)
Landroid/net/wifi/IWifiManager;->registerSoftApCallback(Landroid/os/IBinder;Landroid/net/wifi/ISoftApCallback;I)V,max-target-o - (since API 31)
Landroid/net/wifi/IWifiManager;->registerSoftApCallback(Landroid/net/wifi/ISoftApCallback;)V,blocked - (since API 33)
Landroid/net/wifi/IWifiManager;->unregisterLocalOnlyHotspotSoftApCallback(Landroid/net/wifi/ISoftApCallback;Landroid/os/Bundle;)V,blocked - (prior to API 31)
Landroid/net/wifi/IWifiManager;->unregisterSoftApCallback(I)V,max-target-o - (since API 31)
Landroid/net/wifi/IWifiManager;->unregisterSoftApCallback(Landroid/net/wifi/ISoftApCallback;)V,blocked - (since API 31)
Landroid/net/wifi/WifiClient;->getApInstanceIdentifier()Ljava/lang/String;,blocked - (prior to API 30)
Landroid/net/wifi/WifiConfiguration;->APBAND2GHZ:I,lo-prio,max-target-o - (prior to API 30)
Landroid/net/wifi/WifiConfiguration;->APBAND5GHZ:I,lo-prio,max-target-o - (prior to API 30)
Landroid/net/wifi/WifiConfiguration;->APBANDANY:I,lo-prio,max-target-o - (prior to API 30)
Landroid/net/wifi/WifiConfiguration;->apBand:I,unsupported - (prior to API 30)
Landroid/net/wifi/WifiConfiguration;->apChannel:I,unsupported - (since API 30)
Landroid/net/wifi/WifiContext;->ACTIONRESOURCESAPK:Ljava/lang/String;,blocked - (since API 30)
Landroid/net/wifi/WifiContext;-><init>(Landroid/content/Context;)V,blocked - (prior to API 30)
Landroid/net/wifi/WifiManager$SoftApCallbackProxy;-><init>(Landroid/os/Looper;Landroid/net/wifi/WifiManager$SoftApCallback;)V,max-target-o - (since API 30, prior to API 33)
Landroid/net/wifi/WifiManager$SoftApCallbackProxy;-><init>(Landroid/net/wifi/WifiManager;Ljava/util/concurrent/Executor;Landroid/net/wifi/WifiManager$SoftApCallback;)V,blocked - (since API 33)
Landroid/net/wifi/WifiManager$SoftApCallbackProxy;-><init>(Landroid/net/wifi/WifiManager;Ljava/util/concurrent/Executor;Landroid/net/wifi/WifiManager$SoftApCallback;I)V,blocked - (prior to API 30)
Landroid/net/wifi/WifiManager$SoftApCallback;->onNumClientsChanged(I)V,greylist-max-o - (since API 33)
Landroid/net/wifi/WifiManager;->EXTRAPARAMKEYATTRIBUTIONSOURCE:Ljava/lang/String;,blocked Landroid/net/wifi/WifiManager;->cancelLocalOnlyHotspotRequest()V,unsupportedLandroid/net/wifi/WifiManager;->mService:Landroid/net/wifi/IWifiManager;,unsupportedLandroid/net/wifi/p2p/WifiP2pConfig$Builder;->MACANYADDRESS:Landroid/net/MacAddress;,blockedLandroid/net/wifi/p2p/WifiP2pConfig$Builder;->mNetworkName:Ljava/lang/String;,blocked- (since API 30)
Landroid/net/wifi/p2p/WifiP2pGroup;->interfaceAddress:[B,unsupported Landroid/net/wifi/p2p/WifiP2pManager;->startWps(Landroid/net/wifi/p2p/WifiP2pManager$Channel;Landroid/net/wifi/WpsInfo;Landroid/net/wifi/p2p/WifiP2pManager$ActionListener;)V,unsupportedLandroid/hardware/wifi/supplicant/V1_0/IfaceType;->P2P:ILandroid/hardware/wifi/supplicant/V10/ISupplicant;->getService()Landroid/hardware/wifi/supplicant/V10/ISupplicant;Landroid/hardware/wifi/supplicant/V10/ISupplicant;->getInterface(Landroid/hardware/wifi/supplicant/V10/ISupplicant$IfaceInfo;Landroid/hardware/wifi/supplicant/V1_0/ISupplicant$getInterfaceCallback;)VLandroid/hardware/wifi/supplicant/V10/ISupplicant$getInterfaceCallback;->onValues(Landroid/hardware/wifi/supplicant/V10/SupplicantStatus;Landroid/hardware/wifi/supplicant/V1_0/ISupplicantIface;)VLandroid/hardware/wifi/supplicant/V10/ISupplicant;->listInterfaces(Landroid/hardware/wifi/supplicant/V10/ISupplicant$listInterfacesCallback;)VLandroid/hardware/wifi/supplicant/V1_0/ISupplicant$IfaceInfo;->name:Ljava/lang/String;Landroid/hardware/wifi/supplicant/V1_0/ISupplicant$IfaceInfo;->type:ILandroid/hardware/wifi/supplicant/V10/ISupplicant$listInterfacesCallback;->onValues(Landroid/hardware/wifi/supplicant/V10/SupplicantStatus;Ljava/util/ArrayList;)VLandroid/hardware/wifi/supplicant/V10/ISupplicantP2pIface;->asInterface(Landroid/os/IHwBinder;)Landroid/hardware/wifi/supplicant/V10/ISupplicantP2pIface;Landroid/hardware/wifi/supplicant/V1_0/SupplicantStatus;->code:ILandroid/hardware/wifi/supplicant/V1_0/SupplicantStatus;->debugMessage:Ljava/lang/String;Landroid/hardware/wifi/supplicant/V12/ISupplicantP2pIface;->addGroup12(Ljava/util/ArrayList;Ljava/lang/String;ZI[BZ)Landroid/hardware/wifi/supplicant/V10/SupplicantStatus;Landroid/hardware/wifi/supplicant/V12/ISupplicantP2pIface;->castFrom(Landroid/os/IHwInterface;)Landroid/hardware/wifi/supplicant/V12/ISupplicantP2pIface;Landroid/hardware/wifi/supplicant/V12/ISupplicantP2pIface;->setMacRandomization(Z)Landroid/hardware/wifi/supplicant/V10/SupplicantStatus;- (since API 30)
Landroid/os/ServiceManager;->waitForDeclaredService(Ljava/lang/String;)Landroid/os/IBinder;,blocked - (since API 30)
Landroid/os/ServiceManager;->waitForService(Ljava/lang/String;)Landroid/os/IBinder;,blocked - (since API 30)
Lcom/android/server/wifi/SupplicantStaIfaceHalAidlMainlineImpl;->isServiceAvailable(Landroid/content/Context;)Z - (since API 30)
Landroid/system/wifi/mainlinesupplicant/IMainlineSupplicant$Stub;->asInterface(Landroid/os/IBinder;)Landroid/system/wifi/mainlinesupplicant/IMainlineSupplicant; - (since API 30)
Landroid/system/wifi/mainline_supplicant/IMainlineSupplicant;->getVendorSupplicant()Lcom/android/wifi/x/android/hardware/wifi/supplicant/ISupplicant; - (prior to API 30)
Landroid/provider/Settings$Global;->SOFTAPTIMEOUT_ENABLED:Ljava/lang/String;,lo-prio,max-target-o - (on API 34)
Landroid/service/quicksettings/TileService;->mToken:Landroid/os/IBinder;,lo-prio,max-target-o - (prior to API 30)
Lcom/android/internal/R$array;->configtetherbluetooth_regexs:I,max-target-q - (prior to API 30)
Lcom/android/internal/R$array;->configtetherusb_regexs:I,max-target-q - (prior to API 30)
Lcom/android/internal/R$array;->configtetherwifi_regexs:I,max-target-q - (on API 29)
Lcom/android/internal/R$bool;->configwifip2pmacrandomization_supported:I,blacklist - (prior to API 30)
Lcom/android/internal/R$integer;->configwififrameworksoftaptimeoutdelay:I,greylist-max-o Lcom/android/internal/R$string;->configethernetiface_regex:I,lo-prio,max-target-o- (since API 31)
Lcom/android/server/IpSecService;->FULL_MASK:I - (since API 30)
Lcom/android/server/wifi/p2p/SupplicantP2pIfaceHalAidlBase;->HALINSTANCENAME:Ljava/lang/String; - (since API 30)
Lcom/android/server/wifi/p2p/SupplicantP2pIfaceHalAidlMainlineImpl;->MAINLINESUPPLICANTSERVICE_NAME:Ljava/lang/String; Lcom/android/server/wifi/p2p/WifiP2pServiceImpl;->ANONYMIZEDDEVICEADDRESS:Ljava/lang/String;- (since API 30)
Lcom/android/server/SystemServer;->TETHERINGCONNECTORCLASS:Ljava/lang/String; - (since API 33)
Ldalvik/system/BaseDexClassLoader;->pathList:Ldalvik/system/DexPathList;,unsupported - (since API 33)
Ldalvik/system/DexPathList;->nativeLibraryDirectories:Ljava/util/List;,unsupported - (prior to API 33)
Ljava/lang/invoke/MethodHandles$Lookup;-><init>(Ljava/lang/Class;I)V,unsupported - (prior to API 33)
Ljava/lang/invoke/MethodHandles$Lookup;->ALL_MODES:I,lo-prio,max-target-o
mobile/src/hiddenApiStubs for hidden whitelisted/system APIs as well as partial SDK-class stubs.
Nonexported system resources:
- (since API 30)
@com.android.networkstack.tethering:array/configtetherbluetooth_regexs - (since API 30)
@com.android.networkstack.tethering:array/configtetherncm_regexs - (since API 30)
@com.android.networkstack.tethering:array/configtetherusb_regexs - (since API 30)
@com.android.networkstack.tethering:array/configtetherwifip2pregexs - (since API 30)
@com.android.networkstack.tethering:array/configtetherwifi_regexs - (since API 30)
@com.android.networkstack.tethering:array/configtetherwigig_regexs - (since API 30)
@com.android.wifi.resources:bool/configwifip2pmacrandomization_supported - (since API 31)
@com.android.wifi.resources:integer/config_wifiFrameworkSoftApShutDownIdleInstanceInBridgedModeTimeoutMillisecond - (since API 30)
@com.android.wifi.resources:integer/config_wifiFrameworkSoftApShutDownTimeoutMilliseconds
- (prior to API 30) Activity
com.android.settings/.Settings$TetherSettingsActivityis assumed to be exported. - P2P HIDL fallback probes generated supplicant HIDL Java classes from Android 10
wifi-service.jar
service-wifi.jar com.android.wifi.x.* jarjar packages.
- P2P AIDL mode probes the Wi-Fi mainline service
wifimainlinesupplicantfrom live
service-wifi.jar to recover the stable vendor supplicant AIDL binder before falling back to direct
vendor HAL registration.
IPv6 NATmode depends on the iptablesTPROXYandNFQUEUEtargets and
30000
and assumes queued downstream packets expose six-byte source hardware-address
metadata through NFQA_HWADDR.
- (since API 30) Relevant tethering APEX classes used here, including
android.net.ITetheringConnector,
android.net.connectivity or com.android.connectivity.
(since API 31) Relevant netd APEX classes used here, including android.net.INetd,
may be jarjar-relocated under the optional prefixes
android.net.connectivity or com.android.connectivity.
- (since API 30) When runtime
TetheringEventCallback.onLocalOnlyInterfacesChangedis present, AOSP dispatches
executor.execute { ... } block in onCallbackStarted,
and later tether-state updates from one executor.execute { ... } block in
onTetherStatesChanged.
- The Rust DNS proxy submits upstream queries through
androidresnsend/androidresnresult.
androidresnresult as the public result
reader/closer, it waits for dnsproxyd to close the one-shot resnsend client socket before
reading the result. This assumes resnsend writes the complete resolver result before returning
and the socket receive buffer can hold that result until the framework socket listener closes the
client socket.
- For
ip rulepriorities, AOSP local-network/tethering priorities are assumed to be 17000/18000
- For route-table numbers, Android interface tables are assumed to start at ifindex + 1000;
IPv6 NAT
- Clean flushes table 900 because that table is reserved by VPNHotspot.
IPv6 NATalso adds its
local_network route table 97; Clean never flushes
that table and only deletes VPNHotspot prefixes reconstructed from current interface names.
- For packet marks, Android fwmark is assumed to use low bits for netId and routing metadata.
IPv6 NATfwmark fallback for TPROXY uses masked high reserved bits0x10000000/0x10000000.
FRAIPPROTO policy-rule support, which upstream Linux added in 4.17.
- Daemon reply sockets use the AOSP local-network protected mark
0x00030063, which assumes
LOCALNETID = 99 plus the explicitlySelected and protectedFromVpn fwmark bits.
System/root command assumptions:
The following Android system binaries are assumed to be bundled and executable:
/system/bin/dumpsys(ipsec);/system/bin/iptables-restore,/system/bin/ip6tables-restore(-w --noflush, restore input
-I, -D, -N, -nvx -L <chain>);
/system/bin/ndc(ipfwd,nat);/system/bin/settings(put global);/system/bin/linkeror/system/bin/linker64(path.zip!/program).
