Wednesday, December 10, 2008

CellFinder released!

CellFinder has been posted to the Android Market, so if you have a T-Mobile G1, check it out in the Applications/Tools section. The source code is available at the Codetastrophe Google Code project page. There's also some documentation. Some of the new features since the initial post:
  • Improved format for data display at top of screen
  • Friendler direction/distance display with configurable units
  • Configurable location refresh time
  • Full-screen map option ("Show cell" data in the Settings menu)
  • A few bug fixes
There are a few features I'm planning on adding in the near future:
  • Auto update notification
  • Keep database of cell tower locations and display those on the map
  • GPS satellite locations (see this post)
  • Signal strength is some unit other than "asu"
  • Redirect user to settings page if GPS or Network location service is disabled
  • Help page to show full documentation for all features
So anyways, I was pretty surprised how fast CellFinder showed up on the Marketplace and how soon people starting e-mailing me. I got a lot of positive comments and a few negative ones too. I uploaded it to the Marketplace around 10pm last night, and as of 8:43AM, my stats are:
  • 110 reviews, 4/5 stars
  • 2235 total installs
  • 1823 active installs (81%)

Tuesday, December 9, 2008

Writing a cross-platform ELF binary

Just for fun (actually, I was bored at work) I came up with the crazy idea of writing a cross-platform ELF binary that I could run on Linux, FreeBSD (without the Linux compatibility module), and potentially other Unix. I'm sure at the time I justified it by thinking that it would somehow be easier than writing portable C that I just compiled on different platforms, though in retrospect I was just looking for an excuse to do something a bit weird.

My early experiments with making portable ELF files weren't going so well. Trying to run a Linux executable on FreeBSD (without Linux compat) was harder than I thought. The first problem is the ABI version in the ELF header. The first byte of the padding in the ELF header is interpreted as an ABI version under FreeBSD. Linux just sets it to 0 (and ignores any other value you set it to), but FreeBSD recognizes:
> brandelf -l
known ELF types are: FreeBSD(9) Linux(3) Solaris(6) SVR4(0)
...and refuses to run an ELF type of 0. Supposedly, setting to 3 works if you have the Linux compat library installed, but I was avoiding that.

Before I gave up, I got the idea that if I could strip the program down to the basics, I'd have a better chance of making it work. I found a great article on making tiny ELF files, which reduced the problem down to:
  • Define _start instead of main
  • Make the exit system call (without calling _exit)
It turns out that an even bigger problem than the ELF header is making system calls cross-platform. FreeBSD system calls use a different calling convention: arguments are passed in the stack instead of registers like on Linux. Fortunately, I'm only making one system call (exit). Setting the registers AND pushing values on to the stack will work on both platforms! Doing something complicated that requires more system calls could get messy rather quickly, though.

The final piece is just doing something useful. I found an article describing using the SLDT instruction on x86 to detect virtualization. In summary, if both the high and low bytes returned by the SLDT instruction are nonzero, there's a high probability you're running in a VM. There are lots of other ways to detect virtualization, but SLDT is easy.

The code I wrote is probably non-optimal (I haven't written much x86 assembly), but at least is easy enough to read. Anyway, the "main" assembly part (not including ELF headers, etc) of the program is as follows:

_start:
    mov     eax, 1      ; system call 1 (exit)
    mov     ebx, 0      ; default return value of 0
    sldt    edx         ; SLDT to detect VMWare
    cmp     dl, 0       ; if first byte is 0
    je      .L2         ; jump to end (return 0)
    cmp     dh, 0       ; if second byte is 0
    je      .L2         ; jump to end (return 0)
    mov     ebx, 2      ; otherwise, set return value of 2
.L2:
    push    ebx         ; push return value arg onto stack
    push    eax         ; push syscall number onto stack
    int     0x80        ; syscall interrupt
This gives a return value of 2 if the machine is running in a VM, otherwise 0 (not using 1 because that might be the return value if it doesn't execute properly). It should run on any UNIX platform if the ABI in the ELF header is set correctly, though I've only tested on Linux and FreeBSD. Full assembly on our Google Code project (direct download link here). Since I was following the tiny ELF file article, the assembly includes the ELF header and is 116 bytes when assembled. Build with nasm -f bin -o vmware_small vmware_small.asm. It runs on Linux as-is, but you need to run brandelf -t FreeBSD vmware_small on FreeBSD to set the ABI byte to 9 and make it run there (afterwords, it can still run on Linux!).

Sunday, December 7, 2008

Accessing hidden System Service APIs in Android

Android's SDK allows developers to do a lot with the platform, but there are some interesting capabilities of the system that aren't accessible through the public API. It's still possible to access these capabilities with a little bit of work and the Android source code, available from http://source.android.com. The source code I wrote is here.

The capability I wanted to expose is collecting GPS satellite information - data on the satellites (SVs - space vehicles) themselves. There are hidden methods in the LocationManager service API to set up callbacks to collect this data, but they aren't accessible through the public API. I considered the various ways I could get this data and the easiest one I came up with is to send messages directly to the LocationManager service itself using the IPC interface.

LocationManager and LocationManagerService

The LocationManager API in the android.location namespace is the public interface to the LocationManagerService, in com.android.server. The service runs in a separate process, and the API communicates with it using Android's IPC mechanism (Binder). When an Android application wants to communicate with the LocationManagerService, it uses this API call to get a reference to the API object:

LocationManager mLocationManager = (LocationManager)getSystemService(Context.LOCATION_SERVICE);
Normally, an application will then use the LocationManager API to register for location events (via requestLocationUpdates()) and then use the data to do really fun and interesting things, like this.
If you dig into the LocationManager source code, you'll see that there's a method called registerGpsStatusListener(). This sets up a listener that's able to get updates on GPS satellite information – PRNs, elevations, azimuths, etc, of each of the satellites in view of the GPS. This is what I want. Unfortunately, this method, as well as the argument type (GpsStatusListenerTransport), aren't visible in the android.jar that comes with the SDK. It's still possible to get at this information by communicating directly with the LocationManagerService itself.

The APIs available in the Android SDK communicate with system services using IPC. Specifically, they use interfaces defined with AIDL to communicate with the service. The AIDL specifications for the service IPC API's isn't available in the SDK, but you can get them from the Android source code. The AIDL is used to generate a client stub that Java classes can use to send messages to and receive messages from the service.

Accessing LocationManagerService directly

The class in LocationManager that communicates with the LocationManagerService is GpsStatusListenerTransport. This is an extension of the IGpsStatusListener.Stub class, which was generated from an AIDL specification – IGpsStatusListener.aidl. It's possible to copy IGpsStatusListener.aidl from the Android source code and add to your project to generate the IGpsListenerStatus.Stub class. This project can then use this to communicate directly with the service that implements that interface.
IGpsStatusListener isn't the only AIDL interface we'll need – ILocationManager.aidl, ILocationListener.aidl, and Address.aidl are also needed to do what we want. Once these classes are available in our project (I put them in the android.location namespace to avoid changing the code, but it doesn't really matter which namespace they belong to). Once these interfaces are available to our project, it's really easy to get what we want.

While it is possible to write a totally new client for the LocationManagerService using the AIDL interfaces, it's actually easier for us to re-use an existing client. Setting up a new connection involves a bunch of lines of code and my carpal tunnels hate when I write too much code. The easy way is to use the Java reflection API to get the handle to the system service:
Class c = Class.forName(mLocationManager.getClass().getName());
Field f = c.getDeclaredField("mService");
f.setAccessible(true);
ILocationManager mILM = (ILocationManager)f.get(mLocationManager);
Accessing private fields like this in Java is generally frowned upon in production code, but we're hackers and we want the data and we can do whatever we want to get it and the establishment can't stop us.

So now that we have access to the ILocationManager API, we can see that there is an addGpsStatusListener() method we can call to add a listener that retrieves the GPS satellite status updates. This takes an IGpsStatusListener object, which we can create by instantiating a class that extends IGpsStatusListener.stub. Passing that object to addGpsStatusListener() will result in us getting onSvStatusChanged() callbacks with the satellite status. Nice.

Before we get any of this spiffy satellite info, the GPS must be enabled. In my sample app, I used the regular LocationManager API to do this by adding a LocationListener. The result of all of my efforts is this useless and boring application that shows some information about some satellites:


Oops...

If you start digging into the service code, you might notice that no permissions are required to add a listener for GPS status updates. This is a minor and low-risk information leak vulnerability. Applications shouldn't have access to GPS location data unless they have the correct permission. If an application creates a service that listens for GPS status updates (but not GPS location updates, which require the ACCESS_FINE_LOCATION permission), it will get these updates when any other application turns on the GPS. It's possible, with some fancy math, to determine the location of the phone with this data. You'll need to know the exact time, the relative locations of the GPS satellites to the user, and the absolute locations of the GPS satellites in space at that time. The time and relative locations can come from the phone, and the precise locations of the satellites can be found at http://www.navcen.uscg.gov/GPS/almanacs.htm. I'll leave doing the fancy math as an exercise to the reader.
Update: Proposed patch to add permission check, http://review.source.android.com/5124.

Tuesday, December 2, 2008

Using the AndroidDebugBridge API to get screenshots

I needed a way to get periodic screenshots of the Android device connected to my computer through the USB port. DDMS has the ability to take screenshots on-demand, but not automatically. I dug through the DDMS source code to figure out how screenshots were taken, then a small utility in Java to do what I want. The code uses the AndroidDebugBridge API into the adb service. The jar that contains this API is ddmlib.jar, in the tools\lib directory of the Android SDK. The code is available here. It has a few limitations:
  • If there are more than one Android device connected (including emulators), it only gets screenshots from the first one we find. Ideally there would be a way to list devices and select which one we want to use.
  • Not at all configurable.
Here's what the program does: 1. Initialize the adb interface and get a handle to the bridge:
AndroidDebugBridge.init(false);
AndroidDebugBridge adb = AndroidDebugBridge.createBridge();
2. Set up a listener (an implementation of the IDeviceChangeListener interface) for device changes and add it:
Screenshot ss = new Screenshot();
AndroidDebugBridge.addDeviceChangeListener(ss);
3. Wait for a device to be connected
4. Get a list of connected devices:
Device[] devices = adb.getDevices();
5. In a loop, grab a raw image from the device, then save it as a PNG:
raw = device.getScreenshot();
PaletteData pal = new PaletteData(0xf800, 0x07e0, 0x001f);
ImageData image = new ImageData(raw.width, raw.height, raw.bpp, pal, 1, raw.data);

ImageLoader loader = new ImageLoader();
loader.data = new ImageData[] { image };
loader.save(filename, SWT.IMAGE_PNG);

And that's it. There's a lot more functionality in ddmlib.jar that could be useful for automating tasks like this.

Sunday, November 30, 2008

CellFinder

CellFinder is an Android (T-Mobile G1) cell phone application that takes advantage of the two available location services provided by the platform to determine where your cellular tower is in relation to you. It shows some information about the cell network you're on (Operator name, MCC, MNC), the signal strenth (in "asu", whatever that is). It also shows the CID (Cell ID) and LAC (Location area code) on the GSM network. The bearing from your location to the tower is displayed in degrees east of true north. The distance is in meters. The latitude and longitude of each of the location points is shown in degrees and minutes. Your location, as determined by GPS, is indicated by a circular reticle. The cell tower location is indicated by a star. There is a friendly green line connecting the two on the map. The map updates as often as the phone gets location updates. There are several options available for configuring how the map is displayed:
  • Auto Zoom: Determines if CellFinder should automatically determine the correct zoom level to show both points on the map. Off or on.
  • Satellite: Satellite view or not. Turning this option off shows the street view.
  • Auto Center: This decides how the map changes it's center as each of the locations changes. There are four options:
    GPS centers the map on the GPS coordinate.
    Network centers the map on the Network coordinate.
    Midpoint centers the map on the point between the two.
    None doesn't let the map center itself.
With Auto Zoom and Auto Center disabled, it's possible to use the standard map controls to navigate around. That's all there is to the functionality. It started out as a programming exercise to learn more about the Google Android development platform, but as I added features it made sense to share it. I'll be submitting to the Android Marketplace soon and will eventually post the source code here.