Genymobile / gnirehtet

Gnirehtet provides reverse tethering for Android
Apache License 2.0
6.34k stars 581 forks source link

API < 21 support #2

Closed xstherrera1987 closed 7 years ago

xstherrera1987 commented 7 years ago

I have an API 19 tablet that for whatever reason has defective/inconsistent wifi capabilities. I'd like to fork this project to provide support for API 19 at least. My question then is why is min API set at 21, is it just for UI compatability/simplicity issue or is there some network API's on 21? Im not very familiar with low-level Android network API's so any help is appreciated, thanks.

rom1v commented 7 years ago

The min SDK version has been set to 21 due to the call to setBlocking(true).

The VPN interface is non-blocking par default, which would be great if there was a possibility to select/poll it, but it is not possible since FileChannel is not selectable. So we have a non-blocking interface without asynchronous I/O API, which is stupid. This means that when no data is available, every read would return immediately with 0 bytes…

Therefore, to avoid a busy loop with arbitrary sleep(), I had no choice but to use blocking mode and read from a different thread.

However, you could set the non-blocking mode from native code in order to avoid calling this method from API 21. I didn't do it because then, the NDK would be required for building gnirehtet (and 1 apk per architecture), which IMO would be a bit overkill for this.

xstherrera1987 commented 7 years ago

so the simplest pre-21 solution (without NDK) would probably be to create a long-running thread that just polls then sleeps (maybe every 150ms) if no data is available, ie created in RelayTunnel.open and polls RelayTunnel.receive. I also noticed that pre 22 VpnService.setUnderlyingNetworks is unavailable but have to assume its not critical. Is my understanding correct and is my workaround more or less sound?

There doesn't seem to be any related android-support libraries for VPN. If this is infeasible, I can attempt the NDK solution. I learned C/C++ in college a few years ago but haven't used either since. But seeing as it should amount to setup/config and a single syscall it may be simpler.

rom1v commented 7 years ago

Is my understanding correct and is my workaround more or less sound?

In principle, yes, using polling as a fallback when API < 21 is a good compromise I guess.

However, what is set blocking by the API 22 is the VPN interface (used for the communication between Android and gnirehtet), not the relay tunnel (used between the gnirehtet client and the relay server).

NOTE: the relay tunnel is also used in blocking mode in the client, but this design follows the fact that reading and writing are executed from separate threads, caused itself by the VPN interface beeing blocking.

Therefore, you need to replace both this blocking read and this blocking write (which calls this real blocking write) by some polling.

Tip: sleep only when the previous read/write returned 0 (e.g. don't wait if you just received data, maybe there are more available), otherwise performance would suffer too much.

vvviperrr commented 7 years ago

@xstherrera1987 u can try my tool. it works with API >= 14.

as @rom1v mentioned, the main problem is non-blocking tunnel descriptor. but i use native code to put in into blocking mode. and u dont need one apk per arch, its suitable for arm, mips and x86:

lib/x86/libsimplertjni.so
lib/armeabi-v7a/libsimplertjni.so
lib/armeabi/libsimplertjni.so
lib/mips/libsimplertjni.so
rom1v commented 7 years ago

Another solution to enable blocking mode without depending on the NDK is to call IoUtils.setBlocking(…) by reflection.

I'll probably investigate this solution.

A quick test shows that if I replace the call to setBlocking(true) by this call on the vpnInterface, it still works… To be tested on lower APIs.

rom1v commented 7 years ago

I just implemented it (branch api14, commit https://github.com/Genymobile/gnirehtet/commit/6ececf78e0035717d0f2651d015bb4e8727c1b0c, not merged yet). It should support Android >= 4.0 (API 14).

Please test it on Android 4 devices (I have no such devices available).

I am waiting for your feedbacks ;-)

rom1v commented 7 years ago

I just tried on several Android 4.x devices, unfortunately adb reverse is not implemented on Android 4:

$ adb reverse tcp:31416 tcp:31416
error: closed

So even if I may set the VPN interface in blocking mode, it still does not work… :-(

adb forward works, though, so it may be possible to "revert" the connection. But reverting the role of the relay and client in the connection initialization would add a lot a complexity: the relay server would have to monitor available devices, pick an unused local port to forward, etc.

However, it should be possible to "revert" the connection with 2 socats (one on the computer, one on the device), as I explained recently on my blog (in French, but code blocks and schema may be sufficient): http://blog.rom1v.com/2017/03/serveur-client/

Or we need another way to open a TCP connection between the client and the relay server…

rom1v commented 7 years ago

adb reverse is not implemented on Android 4

So I'm closing this issue as WONTFIX.

jayenashar commented 5 years ago

@rom1v thanks so much for the api14 branch. do we need to build the package ourselves? do you know if the api14 app works with the latest (v2.3) relay?