19 June, 2010

Launching the Android Application Manager

I recently had a need in an Android app to launch the Application Manager. After poking through the source, it turned out to be pretty easy. As you would expect, you simply create an intent and start the activity.

Here is the code to do it:

private static final String ANDROID_SETTINGS_PKG = "com.android.settings";
private static final String ANDROID_SETTINGS_INSTALLED_APP_DTL = ANDROID_SETTINGS_PKG + ".InstalledAppDetails";
private static final String ANDROID_SETTINGS_APP_PKG_NAME = ANDROID_SETTINGS_PKG + ".ApplicationPkgName";

...

Intent intent = new Intent();
intent.setClassName(ANDROID_SETTINGS_PKG, ANDROID_SETTINGS_INSTALLED_APP_DTL);
intent.putExtra(ANDROID_SETTINGS_APP_PKG_NAME, "com.your.package");
startActivity(intent);

...

This will launch the Application manager for the "com.your.package" app (as defined in its AndroidManifest.xml).

Creating a custom USB Mass Storage Driver

In my previous post I described how to get a USB mass storage (UMS) device recognized. This is not always sufficient, as was the case for me recently. To get around this, it was necessary to pull down the kernel source and modify and rebuild the usb mass storage driver. This blog entry describes what I did.

In my case, the device was also not forthright with it's protocol and transport, so the driver was rejecting the device. The usb-storage driver includes a header file of exception cases for just such usb devices. If you modify this file for your device, you can get around this last hurdle.

You will need to pull down the kernel source, modify one of th usb-storage driver files, recompile the kernel, and install the driver.

Step 1: Download the kernel source. Using Ubuntu, you can use synaptic or apt-get install to pull down the source. Using apt-get for instance, get it using:

apt-get install linux-source

On Ubuntu, the source will be placed in /usr/src as a tar.bz2. You'll need to unzip this. Unzipping in the same location is fine, as this is where the header files are placed anyway.

Step 2: Open "unusual_devs.h" in an editor. It should be under "drivers/usb/storage" directory.

Step 3: Add an entry for your device into "unusual_devs.h". Each entry looks like the following:

UNUSUAL_DEV( 0xbbbb, 0xb001, 0x0001, 0x0001,
"Mfg Name",
"Product Name",
US_SC_SCSI, US_PR_BULK, NULL,
US_FL_FIX_CAPACITY ),

I don't fully understand what all the values mean, but here is a break down of what I do know:

Field Purpose Possible Values
1 USB Vendor Id Hexadecimal
2 USB Product Id Hexadecimal
3 Device Min Hexadecimal
4 Device Max Hexadecimal
5 Vendor Name String
6 Product Name String
7 Protocol US_SC_DEVICE - Use the protocol specified by the device
US_SC_SCSI - Transparent SCSI
US_SC_RBC - Reduced Block Commands (RBC)
US_SC_8020 - 8020i
US_SC_8070 - 8070i
US_SC_QIC - QIC-157
8 Transport US_PR_DEVICE - Use the transport specified by the device
US_PR_CB - Control/Bulk
US_PR_CBI - Control/Bulk/Interrupt
US_PR_BULK - Bulk
9 Init function The name of the function for initialization, or NULL if there is none.
10 Flags US_FL_SANE_SENSE
US_FL_BAD_SENSE
US_FL_FIX_CAPACITY
US_FL_CAPACITY_HEURISTICS
US_FL_IGNORE_DEVICE
US_FL_NOT_LOCKABLE
US_FL_MAX_SECTORS_64
US_FL_CAPACITY_OK
US_FL_IGNORE_RESIDUE
US_FL_SINGLE_LUN
US_FL_NO_WP_DETECT

The key values here are the Protocol, Transport and Flags. For the Protocol and Transport, chances are the auto-detect values won't work, otherwise you would not need to do this. In my case I simply guessed at the values (SCSI is a common protocol, for instance).

Step 4: Compile. Run make as root from the base kernel directory. You may need to install additional compilation tools. Verify that you have a usb-storage.ko file under drivers/usb/storage.

Step 5: Determine your running kernel version. Run "uname -r". This will return the name of your kernel.

Step 6: Install your new kernel module. While you can use the whole kernel, I simply installed the one driver. Rename the existing driver to usb-storage.ko.orig, and copy over the new driver to /lib/modules/<kernel-name>/kernel/drivers/usb/storage, where <kernel-name> is the value returned in step 5.

If all works according to plan, your new device should be recognized when you plug it in.

16 March, 2010

Getting a USB Mass Storage (UMS) device to work in Ubuntu

I recently had a need to get an unrecognized USB mass storage (UMS) device to work in Ubuntu (my favorite desktop operating system). While this is seldom needed anymore due to the rich driver support built into Ubuntu/Linux, I had a USB mass storage device that was not recognized. In this case, I knew that it was indeed a ums device, as it had a custom MS Windows driver. This post will step through what I did in case anyone else might benefit.


Step 1: Determine the vendor and product id of the device

Run lsusb to list all devices. If you don't have lsusb installed, install usbutils via apt-get or synaptic.

The output from lsusb should look something like this:

Bus 005 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 002 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 001 Device 002: ID bbbb:b001 Unrecognized Device
Bus 001 Device 001: ID 1d6b:0002 Linux Foundation 2.0 root hub
Bus 004 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub
Bus 003 Device 002: ID 046d:c521 Logitech, Inc. MX620 Laser Cordless Mouse
Bus 003 Device 001: ID 1d6b:0001 Linux Foundation 1.1 root hub

lsusb will list all usb devices present on the system, including usb hubs. As you can see I have 2 client devices plugged in - a mouse and the "Unrecognized Device". The ID of the device is what is important here. The name of the device is immaterial. Now I note the ID of the device "bbbb:b001". The first part of the ID is the vendor ID, while the second part is the device ID.


Step 2: Add the device to the usb module map.

The usb module map is used to map device IDs to the appropriate usb module. In Ubuntu 9.10, this map is stored in /lib/modules/<kernel>/modules.usbmap, where kernel is the name of the kernel you are currently running. Run "uname -r" to get the kernel name for your system.

Once you have located the modules.usbmap file, add a line to the end of the file for your device. The line we add in this case is:

usb-storage 0x000f 0xbbbb 0xb001 0x0001 0x0001
0x00 0x00 0x00 0x00 0x00
0x00 0x0

I must confess that I have not bothered to fully understand what all of the values here mean. That being said, the relevant values are the 1st, 3rd and 4th values. The first value indicates the driver module to load. For a ums device, the module is usb-storage. The 3rd and 4th values are the vendor and device IDs we noted in step 1.

Once this is complete, run depmod, which will regenerate the binary files for the kernel to load.


Step 3. Add a custom rules.dev entry to set the permissions.

At this point, you may have enough for your device to be recognized, but it won't have the permissions that you'll need to use it as a normal user. To get past this, we're going to add a simple custom udev entry.

We'll create a new file in /etc/udev/rules.d to modify the permissions. Udev loads rules files based on alphabetical order. The default rules are 50-udev-default.rules. We'll name our rules file 51-unrecognized.rules with the following content:

SUBSYSTEM=="usb", SYSFS{idVendor}=="bbbb", MODE="0666"

The vendor ID from step 1 is placed in the SYSFS{idVendor} field.

Step 4. Test it out!

At this point you can try to connect your device. If it works, you're done. If not, you'll need to re-compile the usb-storage driver as I did. In my next post I'll break down the steps necessary to accomplish that.

15 January, 2010

Android Market Lacks an Educational Category

As I recently wrote, I uploaded "Suzuki Twinkle" to the Android Market. What I was disappointed to learn was that there is no "Educational" category, either as an App or a Game. I ended up selecting Application --> Reference, for lack of a better category.

Suzuki Twinkle is an educational program. While I am sure there may be plenty who may argue for their on specialized category, I hardly think that having an "Educational" category would be considered specialized.

Google - Please fix this and add an "Educational" category to the Market.

My first published Android App - "Suzuki Twinkle"


I just published my first Android app last night - "Suzuki Twinkle". It's meant to help beginning Suzuki violin students learn their notes, rhythm and pitch. Suzuki Twinkle was pretty rewarding to write, particularly because I was able to involve my kids. If you try it, you'll hear some clapping and cheering when you get something right - those are my kids. The screen you see is for pitch, where the student is expected to tell if the second note was higher or lower than the first. The next release will add score, a little more beauty and hopefully a smaller download size.

From a technical perspective, I ended up trying a good chunk of the Media API. The app plays midi and ogg content. I dabbled with the MediaPlayer for both the midi and ogg, but ended up settling on JetPlayer for the midi, and SoundPool for the ogg. JetPlayer gives you some control over your midi files, although you have to repackage them into a .jet file beforehand. I use the mute capability to mute tracks when playing back notes. This allows me to pack all of the single notes into a single midi file. It probably isn't necessary, seeing as how that is partially the point of Jet, but it does make for less source files when all is said and done.

My original plan for the midi content was to generate the midi notes on the fly using something similar to the midi API in Java SE. Android unfortunately does not expose such an API. So I then pursued AudioTrack, which allows you to stream audio directly. unfortunately the data needs to be PCM encoded, so the level of effort to create the notes was just too high. Thus, I went the JetPlayer route.

For the ogg content, I gave MediaPlayer more of a shot. It mostly worked, but I found that it's lifecycle was too complex for what I wanted (2-3 second playback), and despite my efforts, I could not get it to play well if the previous playback was interrupted. Enter SoundPool. SoundPool was much easier to use. It has a simple lifecycle, and was designed for what I had in mind anyway.

I currently have all this managed by a single Player class that manages all this so that the midi sounds don't clobber the applause and vice versa.

This was actually a pretty simple app overall to implement, as it took just a few days to complete while working a day job and learning about midi from scratch.

Open Source Notes:
This whole app was written with open source software, I am proud to say. I wrote the app on Ubuntu Karmic 9.10 with the following tools:
* Eclipse
* Android SDK
* Rosegarden
* JetCreator
* Gimp

I expect to be posting the source code soon as GPL.