Android 5.0 Media Browser APIs
When I read the release notes for the Android 5.0 APIs I was delighted to see this:
Android 5.0 introduces the ability for apps to browse the media content library of another app, through the new android.media.browse API.
I set out to try to browse the media in a variety of apps I had installed on my phone.
First I listed the apps that supported the MediaBrowserService:
private void discoverBrowseableMediaApps(Context context) {
PackageManager packageManager = context.getPackageManager();
Intent intent = new Intent(MediaBrowserService.SERVICE_INTERFACE);
List<ResolveInfo> services = packageManager.queryIntentServices(intent, 0);
for(ResolveInfo resolveInfo : services) {
if(resolveInfo.serviceInfo != null && resolveInfo.serviceInfo.applicationInfo != null) {
ApplicationInfo applicationInfo = resolveInfo.serviceInfo.applicationInfo;
String label = (String) packageManager.getApplicationLabel(applicationInfo);
Drawable icon = packageManager.getApplicationIcon(applicationInfo);
String packageName = resolveInfo.serviceInfo.packageName;
String className = resolveInfo.serviceInfo.name;
publishProgress(new AudioApp(label, packageName, className, icon));
}
}
}
The publishProgress
method updated the UI and soon I had a list of apps that supported the MediaBrowserService:
Next, I wanted to browse the media they exposed using the MediaBrowser classes:
…
public class BrowseAppMediaActivity extends ListActivity {
private static final String TAG = “BrowseAppMediaActivity”;
…
private final MediaBrowserConnectionListener mMediaBrowserListener =
new MediaBrowserConnectionListener();
private MediaBrowser mMediaBrowser;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
…
Log.d(TAG, “Connecting to “ + packageName + “ / “ + className);
ComponentName componentName = new ComponentName(packageName, className);
Log.d(TAG, “Creating media browser …”);
mMediaBrowser = new MediaBrowser(this, componentName, mMediaBrowserListener, null);
Log.d(TAG, “Connecting …”);
mMediaBrowser.connect();
}
private final class MediaBrowserConnectionListener extends MediaBrowser.ConnectionCallback {
@Override
public void onConnected() {
Log.d(TAG, “onConnected”);
super.onConnected();
String root = mMediaBrowser.getRoot();
Log.d(TAG, “Have root: “ + root);
}
@Override
public void onConnectionSuspended() {
Log.d(TAG, “onConnectionSuspended”);
super.onConnectionSuspended();
}
@Override
public void onConnectionFailed() {
Log.d(TAG, “onConnectionFailed”);
super.onConnectionFailed();
}
}
}
I’ve cut some code, but assume that the packageName
and className
are as they were when queried above. No matter what I did, and which app I queried, the onConnectionFailed
method was invoked.
Here is the log from when I tried to query the Google Music App:
29195-29195/testapp D/BrowseAppMediaActivity﹕ Connecting to com.google.android.music / com.google.android.music.browse.MediaBrowserService
29195-29195/testapp D/BrowseAppMediaActivity﹕ Creating media browser …
29195-29195/testapp D/BrowseAppMediaActivity﹕ Connecting …
16030-16030/? I/MusicPlaybackService﹕ onStartCommand null / null
16030-16030/? D/MediaBrowserService﹕ Bound to music playback service
16030-16030/? D/MediaBrowserService﹕ onGetRoot fortestapp
16030-16030/? E/MediaBrowserService﹕ package testapp is not signed by Google
16030-16030/? I/MediaBrowserService﹕ No root for client testapp from service android.service.media.MediaBrowserService$ServiceBinder$1
724-819/? I/ActivityManager﹕ Displayed testapp/.BrowseAppMediaActivity: +185ms
29195-29195/testapp E/MediaBrowser﹕ onConnectFailed for ComponentInfo{com.google.android.music/com.google.android.music.browse.MediaBrowserService}
29195-29195/testapp D/BrowseAppMediaActivity﹕ onConnectionFailed
Notice the message about my app not being signed by Google on line 7?
I’m assuming that only authorized apps are allowed to browse Google’s music app such as Google apps supporting Android Wear and Android Auto, but not arbitrary third party apps. Indeed the documentation for people implementing MediaBrowserService.onGetRoot indicates that:
The implementation should verify that the client package has permission to access browse media information before returning the root id; it should return null if the client is not allowed to access this information.
This makes sense, but it is disappointing. Just as users can grant specific apps access to notifications, it would be nice of they could also grant specific apps the right to browser other apps’ media.
Please let me know if you discover I am wrong!