Watch for changes in external USB storage for Honeycomb or later Android versions-ThrowExceptions

Exception or error:

I’ve got a DSLR camera and Samsung Galaxy Tab running Android Honeycomb. DSLR connected to a tablet using USB-cable (via USB kit enabling host functionality on a tablet). I’d like to being notified when user takes a photo using this external camera, in order to download this image to the tablet or do something else with it like showing Toast notification containing meta-information taken from the image.

As far as I get all of the existing tools (like FileObserver using underlying inotify mechanism, MediaContentProvider etc) allowing to watch for changes, demand a specific file or a filesystem path to be watched. This was good enough till we had a block layer protocol support in 2.x and earlier Android versions – when you connected device it’d been mounted somewhere on the device’s filesystem and you was able to use this mountpoint as a watch point for those tools.

Since Honeycomb Google has changed the way of accessing external USB devices to Media Transfer Protocol with PTP as a subset of this. Now when I connect external USB device to an Android device I won’t see any mountpoints for it (I’m using adb shell and subsequent mount command for getting them). Moreover, MTP implementation uses storage ids which apparently act as a higher level of abstraction and are just plain integer values. I was hoping there is a way to somehow translate these storage ids to the real paths/mountpoint/whatever but apparently there does not appear to be.

Thinking about Android MediaScanner which is already running on my device I guessed it could manage this issue with a special Intent broadcasted when there’re changes in media files accessible from the device, so I started looking for already existing and suitable Intents for being notified, but no luck – I found only ACTION_MEDIA_MOUNTED and ACTION_MEDIA_REMOVED which are broadcasted only when device is connected and disconnected respectively. That means MediaScanner can’t notice any changes on the device until you remount it (I’ve double checked it using stock Gallery app – it doesn’t see any newly created images on the camera until you unplug and then plug it into the Android device again).

Trying to get this mount path for external sdcard, I used Environment.getExternalStorageDirectory() API call but it yields emulated Galaxy’s sdcard path
which is /mnt/sdcard, not the camera’s one. So it doesn’t work for me either.

I managed to work out this issue only having launched periodic Timer event with AsyncTask acting as a TimerTask. This task does initialize usb connection, open device,
scan the whole device memory, getting only the last taken photo and then close device descriptor and usb connection.

It doesn’t look like the best and efficient way of doing that taking into account it has to do all of these actions every time which could be pretty often, say each 5 or 10 seconds. It definitely quickly drains battery out and produces unnecessary system I/O for only taking last taken photo and comparing it with the previous last taken photo (in 99% it’d the same image), but I haven’t found any better working solution for doing this. It’d much better off to have an observer mechanism with event-based notifications.

So my question is there more efficient way of being notified about changes in external USB storage for Honeycomb or later Android versions rather than one described above?

How to solve:

If you would like a more efficient way the camera would have to send out some sort of signal over usb that it has taken a photo. I guess it is not doing that.

Therefore you will have to check manually by doing the way your are discribing:

mount storage –> check for changes –> do your thing with your detected changes.

I dont know what you used to read “the MTP way” but here an example application:

https://github.com/ynakanishi/Honeycomb-MTP-sample

To not scan the entire storage every time you could save the result of read out file names for example every time you check and compare it to find the new ones. Usually the naming of the file also starts with the same number on a camera. So if you start a session with an empty sd card you know already the file name the photo will have. lets say img0001.jpg. So you just need to write a function to grab that file until it succeeds. if you want the next one img0002.jpg you can write a task/service/function to grab that file until successful, and so on.

If you want to save on battery you could implement an additional battery/power source inbetween for powering the usb port.

Instead of an Async task or timerTask you could try a ScheduledExecutorService and see if it uses less power.

Hope that gave you some new thoughts

Leave a Reply

Your email address will not be published. Required fields are marked *