Brief introduction
There are already solutions to share the mobile network connections of Android phone by the host PC via USB cable, so that browser in the host PC can surf Internet by using Android’s mobile network subscription. This is called tethering. However, today I’m going to present a complete solution to connect Android with the host PC by using ethernet via USB and enable Andriod to access the Internet through the Internet connection of the host PC.
I’m using the Beagle Board (http://www.beagleboard.org) as the platform to run Android (http://labs.embinux.org/index.php/Android_Porting_Guide_to_Beagle_Board).
Enable ethernet over USB in the linux kernel
- go to the “kernel” directory in the android source, use menuconfig (CC_PATH is mentioned in the link above)
make CROSS_COMPILE=$CC_PATH menuconfig
- enable usb-eth by choosing “Ethernet Gadget (with CDC Ethernet support)” for “Device Drivers -> USB support -> USB Gadget Support -> USB Gadget Drivers”, exit and save
- re-build the uImage
make CROSS_COMPILE=$CC_PATH uImage
the previous built uImage might need to be removed first
rm arch/arm/boot/uImage
- copy the uImage to the first partition of the sdcard
- make sure the kernel on the host machine is built with usbnet (“Device Drivers -> Network device support -> USB Network Adapters -> Multi-purpose USB Networking Framework” in the menuconfig). Most GNU Linux distributions already come with usbnet built-in, including Ubuntu in my case
Config ethernet over USB
- refer to http://benno.id.au/blog/2007/11/14/android-busybox to download and install the static-linked busybox built for ARM. It’ll be assumed busybox is installed at “/data/busybox” below
- in minicom connected with Beagle Board, wait until Android is fully booted up
- use ifconfig from busybox mentioned above
/data/busybox/ifconfig usb0 192.168.1.2 up
- config the gateway as well
route add default gw 192.168.1.1 dev usb0
- similarly, type the following command in the shell of the host machine
sudo ifconfig usb0 192.168.1.1 up
Use ADB
- the ADB daemon may require to be terminated first if already running:
ADBHOST=192.168.1.2 adb kill-server
- ADBHOST requires to be set if ADB works over ethernet
ADBHOST=192.168.1.2 adb shell
Config the host machine to act as NAT
- load necessary kernel modules
sudo modprobe ip_tables sudo modprobe iptable_nat sudo modprobe ip_nat_ftp sudo modprobe ip_nat_irc
- enable IP forwarding
echo "1" | sudo dd of=/proc/sys/net/ipv4/ip_forward
- in case the IP address is assigned dynamically
echo "1" | sudo dd of=/proc/sys/net/ipv4/ip_dynaddr
- enable SNAT functionality on the corresponding network interface
sudo iptables -t nat -A POSTROUTING -o [NETWORK_INTERFACE] -j MASQUERADE
Note: [NETWORK_INTERFACE] is the corresponding network interface your host is using to access the Internet.
Proxy
It’s not uncommon that computer access Internet via network proxy. But WIFI and other network connections in Android require direct network connection and network proxy is not supported.
The code handling network proxy in Android is mainly in “frameworks/base/core/java/android/net/http/RequestQueue.java”. In the function setProxyConfig(), getHost() from “frameworks/base/core/java/android/net/Proxy.java is invoked to get the proxy host. However, as illustrated in “frameworks/base/core/java/android/net/ConnectivityManager.java”, Android seems to only support WIFI and mobile network as network connection type. WIFI happens to be the default connectivity type, the reason of which is shown below taken from the file “[ANDROID_SRC_DIR]/frameworks/base/core/java/android/net/http/RequestQueue.java”:
/*
* Initializing the network type is really unnecessary,
* since as soon as we register with the NCL, we'll
* get a CONNECTED event for the active network, and
* we'll configure the HTTP proxy accordingly. However,
* as a fallback in case that doesn't happen for some
* reason, initializing to type WIFI would mean that
* we'd start out without a proxy. This seems better
* than thinking we have a proxy (which is probably
* private to the carrier network and therefore
* unreachable outside of that network) when we really
* shouldn't.
*/
Then no proxy is set in case of WIFI network, which means WIFI requires direct Internet connection as shown below also taken from the file “frameworks/base/core/java/android/net/http/RequestQueue.java”. The way to solve it is to comment out the “if (mNetworkStateTracker.getCurrentNetworkType() == ConnectivityManager.TYPE_WIFI)” statement:
private synchronized void setProxyConfig() {
/* get rid of this branch */
if (mNetworkStateTracker.getCurrentNetworkType() == ConnectivityManager.TYPE_WIFI) {
mProxyHost = null;
} else {
String host = Proxy.getHost(mContext);
if (HttpLog.LOGV) HttpLog.v("RequestQueue.setProxyConfig " + host);
if (host == null) {
mProxyHost = null;
} else {
mActivePool.disablePersistence();
mProxyHost = new HttpHost(host, Proxy.getPort(mContext), "http");
}
}
}
Still in “frameworks/base/core/java/android/net/http/RequestQueue.java”, setProxyConfig() is only invoked when the NetworkStateTracker sends out message that the network state is changed. And setProxyConfig() is not called during initialization because as soon as Android registers with the NCL, a CONNECTED event is received for the active network. However, this is only true to mobile network, not for WIFI or wired networks. The way to fix this is to invoke setProxyConfig() at the end of enablePlatformNotifications(), so that proxy is properly configured at initialization regardless of what network type it is. The corresonding code is illustrated below:
public synchronized void enablePlatformNotifications() {
if (HttpLog.LOGV) HttpLog.v("RequestQueue.enablePlatformNotifications() network");
if (mProxyChangeReceiver == null) {
mProxyChangeReceiver =
new BroadcastReceiver() {
@Override
public void onReceive(Context ctx, Intent intent) {
setProxyConfig();
}
};
mContext.registerReceiver(mProxyChangeReceiver,
new IntentFilter(Proxy.PROXY_CHANGE_ACTION));
}
/* Network state notification is broken on the simulator
don't register for notifications on SIM */
String device = SystemProperties.get("ro.product.device");
boolean simulation = TextUtils.isEmpty(device);
if (!simulation) {
if (mNetworkStateTracker == null) {
mNetworkStateTracker = new NetworkStateTracker(mContext);
}
mNetworkStateTracker.enable();
}
/* call setProxyConfig() as part of the initialization.
Otherwise, setProxyConfig() will only be invoked in case of
network state change, and only mobile network get CONNECTED
event after registration with NCL */
setProxyConfig();
}
Then in Proxy.java, it uses “frameworks/base/core/java/android/provider/Settings.java”, which is actually an utility wrapper of sqlite database to get the proxy and port settings. The corresponding table is “secure” from “/data/data/com.android.providers.settings/databases/settings.db”. The “name”, “value” pair should be “http_proxy”, “[HTTP_PROXY_HOST]:[HTTP_PROXY_PORT]“. The way to append the record is:
adb shell sqlite3 /data/data/com.android.providers.settings/databases/settings.db "INSERT INTO secure VALUES(99,'http_proxy','[HTTP_PROXY_HOST]:[HTTP_PROXY_PORT]');"
If nothing is got from the above database, then the system property “net.gprs.http-proxy” is used instead. The way to put it is to add the following command in “on boot” trigger in “init.rc”:
setprop net.gprs.http-proxy [HTTP_PROXY_HOST]:[HTTP_PROXY_PORT]
One more thing to mention is DNS has to be configured to resolve domain names. The way to do that is to add the following commands in “on boot” trigger in “init.rc” as well:
setprop net.dns1 [DNS_SERVER_1_IP] setprop net.dns2 [DNS_SERVER_2_IP]
Very good article and useful infomation.
I just bought my first android phone, and am starting to learn the code, as well as the building process by enabling the wifi proxy.
I look forward to more good articles on your blog.
And BTW, it’s cool for you to use this .se domain. At first I thought you were Swedish. Lol.
Thanks for your comment. I’m a Chinese working and living in Sweden. However, it’s not practical for me to get a .cn domain since it has already over 12 million registrations (second most common domain, only after .com, oops~).
Great article. Do you have this confirmed as working? Also is it possible to set/use credentials for authenticated proxies?
Yes, it’s confirmed to work. I didn’t use credentials in my case. But I bet it’s possible, since all proxy functionalities are existing in Android, which is only shed away in case of WiFi connection.
For god’s sake Song…
Do not you have enough Android at E******* in your working time?
So what’s your girlfriend going to think now that she’s there?? You have to take more care of her!!
保重!
再見
[...] This post was mentioned on Twitter by microjo. microjo said: @iBeyond 第一个地址是这个:http://zi.mu/d19 又发现了一个分析的:http://zi.mu/d1a 真不知道怎么说,为什么wifi不支持代理设置,Google应该很重视网络的啊~~~ [...]
Hello Yuan Song,
This proxy thing seems useful solution to me. Is it possible to make a patch to use it on a non-rooted Desire? If you could make it thousands of users would be happy.
Hi eref,
If you are talking about a code patch then http://www.cyanogenmod.com has it’s distributions that include proxy setting feature, although you would need rooted device to flash their distributions.
/Song
Hi, I did this over one year ago, but I believe you can find your answer in the source code of http://www.cyanogenmod.com distributions.
@Yuan…Thanks a lot… But I dont have any technical knowledge to do this
This is working in FROYO?? I have a HTC Desire with Froyo….and my school is having proxy…so I badly need a fix…Can you release it as an app or a patch…My phone is rooted by the way….Please help!!
Thanks a lot