Search This Blog

23 January 2012

Reverse USB Tethering

I decided to give reverse usb tethering a shot. I don't know which devices easily support this, but I assume you have to be root. In my particular case, I was using an ICS device.  These steps could easily screw up your existing cell routing - so make sure your willing to risk it.

Step 0: Make sure the device is booted and connected via USB

Step 1: Enable tethering on the device side
  • Go to Settings
  • Under the Wireless & Networks, click on More
  • Click on Tethering & portable hotspot
  • Enable USB Tethering
Step 2: Get the IP Address of the device
malachi@onyx:~$ adb shell
root@android:/ # netcfg
lo       UP                                   127.0.0.1/8   0x00000049 00:00:00:00:00:00
dummy0   DOWN                                   0.0.0.0/0   0x00000082 62:bc:02:e1:d5:ea
rmnet0   DOWN                                   0.0.0.0/0   0x00000000 00:00:00:00:00:00
rmnet1   DOWN                                   0.0.0.0/0   0x00000000 00:00:00:00:00:00
rmnet2   DOWN                                   0.0.0.0/0   0x00000000 00:00:00:00:00:00
rmnet3   DOWN                                   0.0.0.0/0   0x00000000 00:00:00:00:00:00
rmnet4   DOWN                                   0.0.0.0/0   0x00000000 00:00:00:00:00:00
rmnet5   DOWN                                   0.0.0.0/0   0x00000000 00:00:00:00:00:00
rmnet6   DOWN                                   0.0.0.0/0   0x00000000 00:00:00:00:00:00
rmnet7   DOWN                                   0.0.0.0/0   0x00000000 00:00:00:00:00:00
sit0     DOWN                                   0.0.0.0/0   0x00000080 00:00:00:00:00:00
rndis0   UP                              192.168.42.129/24  0x00001043 da:02:11:22:14:af
Step 3: Get the matching IP Address of the workstation
malachi@onyx:~$ ifconfig -a usb0
usb0      Link encap:Ethernet  HWaddr e2:31:b5:d7:f3:7b 
          inet addr:192.168.42.74  Bcast:192.168.42.255  Mask:255.255.255.0
          inet6 addr: fe80::e031:b5ff:fed7:f37b/64 Scope:Link
          UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
          RX packets:1990 errors:0 dropped:0 overruns:0 frame:0
          TX packets:1188 errors:0 dropped:0 overruns:0 carrier:0
          collisions:0 txqueuelen:1000
          RX bytes:181334 (181.3 KB)  TX bytes:657452 (657.4 KB)
Step 4: Ping the device from the workstation
malachi@onyx:~$ ping 192.168.42.129
PING 192.168.42.129 (192.168.42.129) 56(84) bytes of data.
64 bytes from 192.168.42.129: icmp_req=1 ttl=64 time=1.01 ms
64 bytes from 192.168.42.129: icmp_req=2 ttl=64 time=0.605 ms

Step 5: Ping the workstation from the device
root@android:/ # ping 192.168.42.74
PING 192.168.42.74 (192.168.42.74) 56(84) bytes of data.
64 bytes from 192.168.42.74: icmp_seq=1 ttl=64 time=0.427 ms
64 bytes from 192.168.42.74: icmp_seq=2 ttl=64 time=0.397 ms
Step 6: Add workstation as the default gateway for the device
root@android:/ # route add default gw 192.168.42.74 dev rndis0
Step 7: Get a list of DNS servers from the workstation
malachi@onyx:~$ cat /etc/resolv.conf
nameserver 172.29.224.11
nameserver 8.8.8.8
Step 8: Add them to the device
root@android:/ # setprop net.dns1 172.29.224.11
root@android:/ # setprop net.dns2 8.8.8.8
Step 9: Setup forwarding from usb0 to eth1 on the workstation
[ most people probably use eth0. Just use ifconfig to check which one is active]

malachi@onyx:~$ sudo su - root
[sudo] password for malachi:
root@onyx:~# echo 1 > /proc/sys/net/ipv4/ip_forward
root@onyx:~# exit
malachi@onyx:~$ sudo iptables --flush -t nat
malachi@onyx:~$ sudo iptables --table nat --append POSTROUTING --out-interface eth1 -j MASQUERADE
malachi@onyx:~$ sudo iptables --append FORWARD --in-interface usb0 -j ACCEPT
Step 10: ping google ;)

malachi@onyx:~$ adb shell
root@android:/ # ping google.com
PING google.com (74.125.127.99) 56(84) bytes of data.
64 bytes from pz-in-f99.1e100.net (74.125.127.99): icmp_seq=1 ttl=51 time=11.8 ms
64 bytes from pz-in-f99.1e100.net (74.125.127.99): icmp_seq=2 ttl=51 time=11.9 ms
And finally, check things like the Browser...






03 January 2012

Fragments

Well, I've been needing to look into Fragments for awhile now...

First, to generate a blank project...

malachi@onyx:~/work$ mvn archetype:generate -DarchetypeCatalog=http://repository-malachid.forge.cloudbees.com/public-snapshot/archetype-catalog.xml
[INFO] Scanning for projects...
[INFO]                                                                        
[INFO] ------------------------------------------------------------------------
[INFO] Building Maven Stub Project (No POM) 1
[INFO] ------------------------------------------------------------------------
[INFO]
[INFO] >>> maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom >>>
[INFO]
[INFO] <<< maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom <<<
[INFO]
[INFO] --- maven-archetype-plugin:2.2:generate (default-cli) @ standalone-pom ---
[INFO] Generating project in Interactive mode
[INFO] No archetype defined. Using maven-archetype-quickstart (org.apache.maven.archetypes:maven-archetype-quickstart:1.0)
Choose archetype:
1: http://repository-malachid.forge.cloudbees.com/public-snapshot/archetype-catalog.xml -> org.eoti.kryten:kryten-archetype (kryten-archetype)
2: http://repository-malachid.forge.cloudbees.com/public-snapshot/archetype-catalog.xml -> org.eoti.galatea:galatea-archetype (galatea-archetype)
3: http://repository-malachid.forge.cloudbees.com/public-snapshot/archetype-catalog.xml -> org.eoti.archtest:archtest-archetype (archtest-archetype)
Choose a number or apply filter (format: [groupId:]artifactId, case sensitive contains): : 2
Define value for property 'groupId': : org.eoti.android.test.fragments
Define value for property 'artifactId': : FragmentTest
Define value for property 'version':  1.0-SNAPSHOT: :
Define value for property 'package':  org.eoti.android.test.fragments: :
Confirm properties configuration:
groupId: org.eoti.android.test.fragments
artifactId: FragmentTest
version: 1.0-SNAPSHOT
package: org.eoti.android.test.fragments
 Y: :
[INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Archetype: galatea-archetype:1.1-SNAPSHOT
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: groupId, Value: org.eoti.android.test.fragments
[INFO] Parameter: artifactId, Value: FragmentTest
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: package, Value: org.eoti.android.test.fragments
[INFO] Parameter: packageInPathFormat, Value: org/eoti/android/test/fragments
[INFO] Parameter: package, Value: org.eoti.android.test.fragments
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] Parameter: groupId, Value: org.eoti.android.test.fragments
[INFO] Parameter: artifactId, Value: FragmentTest
[WARNING] Don't override file /home/malachi/work/FragmentTest/src/main/android/res/values/strings.xml
[WARNING] Don't override file /home/malachi/work/FragmentTest/src/main/android/res/layout/main.xml
[INFO] project created from Archetype in dir: /home/malachi/work/FragmentTest
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 21.579s
[INFO] Finished at: Tue Jan 03 08:30:32 PST 2012
[INFO] Final Memory: 8M/245M
[INFO] ------------------------------------------------------------------------
malachi@onyx:~/work$ cd FragmentTest/
malachi@onyx:~/work/FragmentTest$ tree
.
├── pom.xml
└── src
    ├── AndroidManifest.xml
    └── main
        ├── android
        │   └── res
        │       ├── drawable
        │       │   └── icon.png
        │       ├── layout
        │       │   └── main.xml
        │       └── values
        │           └── strings.xml
        └── java
            └── org
                └── eoti
                    └── android
                        └── test
                            └── fragments
                                └── FragmentTestActivity.java

13 directories, 6 files


While it should be possible to use the Fragments with pre-HONEYCOMB (http://android-developers.blogspot.com/2011/03/fragments-for-all.html); but for now, let's just try it in the ICS emulator.

Based on the available jars in search.maven.org (http://search.maven.org/#artifactdetails|com.google.android|android|4.0.1.2|jar) let's set the version of the Android dependency in the pom to 4.0.1.2.

Also, change the <sdk><platform>10</platform></sdk> to <sdk><platform>14</platform></sdk>.

For now, comment out the maven-jarsigner-plugin as that requires setting up a keystore.
You'll also need to comment out the <sign /> portion of the android-maven-plugin.

Start up an ICS emulator and try 'mvn clean install' just to make sure we are able to grab the dependencies.
Launch the FragmentTest and you should see your hello world.

Let's move on to adding fragments... this part is derived from http://www.vogella.de/articles/Android/article.html#fragments_tutorial
I've modified their instructions quite a bit to fit into our Maven structure as well as to make things more clear (they had ListFragment extends ListFragment which tends to confuse people).

First, copy src/main/android/res/layout/main.xml to src/main/android/res/layout/detail.xml
On the new detail.xml, give the TextView an id:
android:id="@+id/ftDetailText"

Now, we'll deal the with portrait layout for FragmentTestActivity:
Replace the TextView in the layout/main.xml with:
    <fragment
        android:id="@+id/ftListFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        class="org.eoti.android.test.fragments.FTListFragment"
    />

Now, let's modify it for landscape...

Copy src/main/android/res/layout/main.xml to src/main/android/res/layout-land/main.xml [note: you will have to create the layout-land directory]

Now, for the layout-land/main.xml:
1. change the LinearLayout to Horizontal:
android:orientation="horizontal"
2. copy/paste the fragment so you now have 2 of them
3. for the first one. let's change the width to:
android:layout_width="300dip"
4. for the second one, change the id to:
android:id="@+id/ftDetailFragment"
5. Change the class to
class="org.eoti.android.test.fragments.FTDetailFragment"



Note: I changed the name of these classes so it would be obvious that these are our classes, and not some default Android implementation. Perhaps a redundant point since we also set the package name.


We'll need to have a separate activity for the detail portion in portrait mode, so let's create the layout for that.
Copy src/main/android/res/layout-land/main.xml to src/main/android/res/layout/details.xml [note: plural details not detail]

For the new details.xml:
1. change the LinearLayout back to Vertical:
android:orientation="vertical"
2. remove the ftListFragment <fragment />

We already have our main FragmentTestActivity.  We are missing our DetailActivity. Let's do some more copy/paste.
Copy FragmentTestActivity.java to DetailActivity.java (in the same directory)
Let's edit DetailActivity.java:
1. Change the class name to DetailActivity [I'd change the TAG to match]
2. change the setContentView to R.layout.details
3. add the following just after setContentView:
        Bundle extras = getIntent().getExtras();
        if (extras != null) {
            TextView view = (TextView) findViewById(R.id.ftDetailText);
            view.setText(System.getProperty(extras.getString("key")));
        }
4. Make sure to add the import android.widget.TextView;

We've defined two fragments in the layouts.  We need to create those.
First, we'll create our src/main/java/org/eoti/android/test/fragments/FTDetailFragment.java
This one is pretty simple:

package org.eoti.android.test.fragments;

import android.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

public class FTDetailFragment extends Fragment
{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        return inflater.inflate(R.layout.detail, container,  false);
    }

    public void setText(String key)
    {
        ((TextView)getView().findViewById(R.id.ftDetailText)).setText(System.getProperty(key));
    }

}


Next, we'll create our src/main/java/org/eoti/android/test/fragments/FTListFragment.java
This one isn't much more difficult:

package org.eoti.android.test.fragments;

import android.*;
import android.R;
import android.app.ListFragment;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import java.util.ArrayList;

public class FTListFragment extends ListFragment
{
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ArrayList keys = new ArrayList(System.getProperties().keySet());
        ArrayAdapter adapter = new ArrayAdapter(getActivity(), R.layout.simple_list_item_1, keys);
        setListAdapter(adapter);
    }

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        String key = getListAdapter().getItem(position).toString();
        FTDetailFragment fragment = (FTDetailFragment)getFragmentManager().findFragmentById(org.eoti.android.test.fragments.R.id.ftDetailFragment);
        if(fragment != null)
        {
            fragment.setText(key);
        }else{
            Intent intent = new Intent(getActivity().getApplicationContext(), DetailActivity.class);
            intent.putExtra("key", key);
            startActivity(intent);
        }
    }
}

Last, but not least, we need to add the other activity to your AndroidManifest. Put this inside your <application /> tag.
          <activity android:name=".DetailActivity" />



mvn clean install

Launch the application (in portrait mode, I assume)
Select something. You should see a new activity popup with the value of the property you chose.
Hit back, and try it a few times.
When you are done, rotate the emulator using CTRL-F11.  I'm not sure about anyone else, but I have to hit it twice quickly or it won't rotate.
Now, when you select an item on the left, the answer is on the right.
When you are done playing with it, CTRL-F12 [twice in my case] to go back to portrait.

UPDATE: If you wish to use it with older devices...
First, I used the  maven-android-sdk-deployer to create the maven-compliant compat library for me.

        <dependency>
            <groupId>android.support</groupId>
            <artifactId>compatibility-v13</artifactId>
            <version>r6</version>
        </dependency>

This allows older versions of Android to use the Fragments. In my case, I tested against an Android 2.3.3 emulator.

I made no other changes to the pom.  IE: I compiled against a newer SDK and deployed against both.

Other changes to make:
  1. make FTDetailFragment extend android.support.v4.app.Fragment
  2. make FTListFragment extend android.support.v4.app.ListFragment
  3. make DetailActivity extend android.support.v4.app.FragmentActivity
  4. make FragmentTestActivity extend android.support.v4.app.FragmentActivity
  5. In FTListFragment, instead of using getFragmentManager you need to call getSupportFragmentManager.  This is part of the FragmentActivity, so you will need to do something like:
((FragmentActivity)getActivity()).getSupportFragmentManager()...

mvn clean install and it should all work... close down the 2.3.3 emulator, start up an ICS emulator and.. it will still work ;)