Android - binding to the Music app service to find currently playing song


For an Android app I’m working on I need to find out what song, if any, is playing in the background on the device. Turns out there’s no documented way to do this on Android - but it also turns out that using an open source platform is great. After a few hours of poking around I had a working proof of concept, and I’m sure someone more familiar with Android would’ve figured it out much faster.

IMPORTANT: I’m using an undocumented interface. While this works on Android 1.5R3 and T-Mobile G1 (HTC Magic), and likely on other versions/devices, there is no guarantee it’ll keep working in future releases - use at your own risk.

The default way to play music on the G1 is the Music app. Since it can play music in the background, it seemed likely there is some way to interact with the service it uses. This of course doesn’t help if the user is using some other music player, but it should be good enough.

First step was to figure out exactly what service that is - I used something like the following code to find out what services are running while I’m playing music:

		ActivityManager am = (ActivityManager)this.getSystemService(ACTIVITY_SERVICE);

		List<ActivityManager.RunningServiceInfo> rs = am.getRunningServices(50);

		for (int i=0; i<rs.size(); i++) {
			ActivityManager.RunningServiceInfo rsi = rs.get(i);
			Log.i("Service", "Process " + rsi.process + " with component " + rsi.service.getClassName());
		}

This prints up to 50 running services. The interesting line in the output is

process com.android.music with component com.android.music.MediaPlaybackService

OK, now to figure out how to interact with MediaPlaybackService. It turns out to be surprisingly simple.

First, get a copy of IMediaPlaybackService.aidl from the source code of the Music app, and include it in your Android app. The contents of this file are the methods we have access to in MediaPlaybackService. Of course, if the Music app on the device changes, and our copy of the .aidl file is inaccurate, we have a problem - hence the warning above.

Next, implement a ServiceConnection we can use to connect to MediaPlaybackService.

	private class MediaPlayerServiceConnection implements ServiceConnection {

		public IMediaPlaybackService mService;

		public void onServiceConnected(ComponentName name, IBinder service) {
			Log.i("MediaPlayerServiceConnection", "Connected! Name: " + name.getClassName());

			// This is the important line
			mService = IMediaPlaybackService.Stub.asInterface(service);

			// If all went well, now we can use the interface
			try {
				Log.i("MediaPlayerServiceConnection", "Playing track: " + mService.getTrackName());
				Log.i("MediaPlayerServiceConnection", "By artist: " + mService.getArtistName());
				if (mService.isPlaying()) {
					Log.i("MediaPlayerServiceConnection", "Music player is playing.");
				} else {
					Log.i("MediaPlayerServiceConnection", "Music player is not playing.");
				}
			} catch (Exception e) {
	    		e.printStackTrace();
	    		throw new RuntimeException(e);
			}
		}

		public void onServiceDisconnected(ComponentName name) {
			Log.i("MediaPlayerServiceConnection", "Disconnected!");
		}
	}

Finally, bind the service:

		Intent i = new Intent();
		i.setClassName("com.android.music", "com.android.music.MediaPlaybackService");
		ServiceConnection conn = new MediaPlayerServiceConnection();
		this.bindService(i, conn, 0);

That’s it! From here on it should be possible to use the MediaPlaybackService like any other service.

, ,

  1. #1 by Patrick Boos at October 3rd, 2009

    First thank you for your article! It helped me a lot! Got my program to connect just perfect to the Android Music Player.

    Question now. Do you know how to get for example the aidl file for the HTC Music player (com.htc.music)? And for other Music Players that are out there? What would be the easiest or best way to get my hands on those?

    Have a great day!

  2. #2 by alex at October 4th, 2009

    Good question. I haven’t worked with the other players, so I can’t give you a specific answer.

    If the players are open-source, it should be simple - the aidl files should be in the source…

    If not, the only option I can think of is to grab the player’s .apk file from the device (might have to root it first!), and see if you can extract the .aidl file from it somehow. I’m not sure what the legal implications would be for this method.

  3. #3 by adam at November 3rd, 2009

    Hey there Alex, great article! Helped me a lot.

    Although I’m having some trouble with my implementation. I have a MediaPlayerServiceConnection class with the above code, but instead of printing to the Log I wrote getter methods to be used by my main class, where the bind service code is in.

    The problem I have is that, every time I call any of the methods used in the aidl, i get a NullPointerException. Further investigation revealed that onServiceConnected was never called. What could I be doing wrong?

    I’m developing using Eclipse (Galileo) and android SDK 1.5 (api level 3) with ADT/emulator.

    Thanks in advanced!

  4. #4 by adam at November 4th, 2009

    Hello, please disregard the previous post. It was just a silly mistake I made. =)

  5. #5 by dghgh at November 8th, 2009

    what was the silly mistake? I keep getting the NullPointerException when i call my services method and i prettymuch followed the tutorials.

  6. #6 by ud at November 11th, 2009

    @adam..
    plz tell ur silly mistake.. i m getting the same NullPtrExp. Im calling bindService(xx) before using the methods in service. but still getting the exception.
    The main thing is that, when i call some methods on some button click, it works fine. But when I call the same methods in onStart() or onCreate() of Activity, I get NullptrExp. :(
    any help???

  7. #7 by music at November 17th, 2009

    Thanks for this article! It describes just, what I am about to try. But I ran into a problem:
    The catch clause catches a Security Exception “Binder invocation to an incorrect interface”

    I copied the aidl file to my project and changed the package to my package. When I run my test app in the emulator it connects to the service, but when I try tu use it I get the mentioned exception.

    Any idea what might be the problem? Am I using the wrong aidl file?

  8. #8 by music at November 17th, 2009

    @ud

    bindService is asynchronous. You can only use the service after onServiceConnected was called.

  9. #9 by Sathya at November 20th, 2009

    Hi,

    Thanks for the artcile..It really helped us to start the application. But when I tried the example in emulator I am getting an exception on trying to use the interface functions…It throws the exception “Binder invocation to an incorrect interface”

    The following are the steps followed
    1) Copied the AIDL file (IMediaPlayBackService.aidl) to my package folder
    2) Renamed the package in teh aidl file to my application(As it shown error when include to the package folder)
    3) Bind a service.
    4) And tried to use the service in the onServiceCOnnected callback function after getting the interface using the code
    mService = IMediaPlaybackService.Stub.asInterface((IBinder)service);

    But exception is thrown on trying to use the interface functions.

    Could you please let me know what might be the problem?

    Thanks in advance for the reply.

  10. #10 by Sathya at November 20th, 2009

    Hi,

    Thanks for the article..It really helped us to start the application. But when I tried the example in emulator I am getting an exception on trying to use the interface functions…It throws the exception “Binder invocation to an incorrect interface”

    The following are the steps followed
    1) Copied the AIDL file (IMediaPlayBackService.aidl) to my package folder
    2) Renamed the package in teh aidl file to my application(As it shown error when include to the package folder)
    3) Bind a service.
    4) And tried to use the service in the onServiceCOnnected callback function after getting the interface using the code
    mService = IMediaPlaybackService.Stub.asInterface((IBinder)service);

    But exception is thrown on trying to use the interface functions.

    Could you please let me know what might be the problem?

    Thanks in advance for the reply.

  11. #11 by MarGau at November 20th, 2009

    The problem is that the IMediaPlaybackService needs to be defined within package com.android.music you cannot change this as you then experience the security exception.
    The exception basically says that the expected interface is “com.android.music.IMediaPlaybackService” whereas you defined something else.

    In order to fix this you need to have in your source tree:
    src\com\android\music\IMediaPlaybackService.aidl
    parallel to your code. Then you don’t have to rename the package and everything works fine.

  12. #12 by Sathya at November 30th, 2009

    Hi MarGau,

    Thanks for the reply. It worked.. :-)

  13. #13 by NPS at December 5th, 2009

    Hi can you pls give us a working example ,
    What we are trying to do is tag the currently playing song so for that we need to get the info like the actual path to the song and also the artist info and all some how i cannot find the IMediaPlaybackService.aidl file in my android sdk

    could you direct me towards that

  14. #14 by Jorge Rivera at December 10th, 2009

    Hi,

    Thanks a lot for the post, it was really helpful!

    Has anyone tried to enqueue a list of songs using the .enqueue method? I have a little problem with it: I send a bunch of songs and only the first HALF of the songs get added to the list. I tried using the NOW, NEXT and LAST position options, and various sizes of lists of songs. For an odd number of songs, it adds half of (list+1).

    Any ideas?

(will not be published)
  1. No trackbacks yet.