MediaSessionService.java revision 8ae0f34db936a649ddaf9cdd086c224f6514efeb
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.server.media; 18 19import android.content.Context; 20import android.media.session.IMediaSession; 21import android.media.session.IMediaSessionCallback; 22import android.media.session.IMediaSessionManager; 23import android.os.Binder; 24import android.os.Handler; 25import android.os.RemoteException; 26import android.text.TextUtils; 27import android.util.Log; 28 29import com.android.server.SystemService; 30 31import java.util.ArrayList; 32 33/** 34 * System implementation of MediaSessionManager 35 */ 36public class MediaSessionService extends SystemService { 37 private static final String TAG = "MediaSessionService"; 38 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 39 40 private final SessionManagerImpl mSessionManagerImpl; 41 42 private final ArrayList<MediaSessionRecord> mSessions 43 = new ArrayList<MediaSessionRecord>(); 44 private final Object mLock = new Object(); 45 // TODO do we want a separate thread for handling mediasession messages? 46 private final Handler mHandler = new Handler(); 47 48 public MediaSessionService(Context context) { 49 super(context); 50 mSessionManagerImpl = new SessionManagerImpl(); 51 } 52 53 @Override 54 public void onStart() { 55 publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl); 56 } 57 58 void sessionDied(MediaSessionRecord session) { 59 synchronized (mSessions) { 60 destroySessionLocked(session); 61 } 62 } 63 64 void destroySession(MediaSessionRecord session) { 65 synchronized (mSessions) { 66 destroySessionLocked(session); 67 } 68 } 69 70 private void destroySessionLocked(MediaSessionRecord session) { 71 mSessions.remove(session); 72 } 73 74 private void enforcePackageName(String packageName, int uid) { 75 if (TextUtils.isEmpty(packageName)) { 76 throw new IllegalArgumentException("packageName may not be empty"); 77 } 78 String[] packages = getContext().getPackageManager().getPackagesForUid(uid); 79 final int packageCount = packages.length; 80 for (int i = 0; i < packageCount; i++) { 81 if (packageName.equals(packages[i])) { 82 return; 83 } 84 } 85 throw new IllegalArgumentException("packageName is not owned by the calling process"); 86 } 87 88 private MediaSessionRecord createSessionInternal(int pid, String packageName, 89 IMediaSessionCallback cb, String tag) { 90 synchronized (mLock) { 91 return createSessionLocked(pid, packageName, cb, tag); 92 } 93 } 94 95 private MediaSessionRecord createSessionLocked(int pid, String packageName, 96 IMediaSessionCallback cb, String tag) { 97 final MediaSessionRecord session = new MediaSessionRecord(pid, packageName, cb, tag, this, 98 mHandler); 99 try { 100 cb.asBinder().linkToDeath(session, 0); 101 } catch (RemoteException e) { 102 throw new RuntimeException("Media Session owner died prematurely.", e); 103 } 104 synchronized (mSessions) { 105 mSessions.add(session); 106 } 107 if (DEBUG) { 108 Log.d(TAG, "Created session for package " + packageName + " with tag " + tag); 109 } 110 return session; 111 } 112 113 class SessionManagerImpl extends IMediaSessionManager.Stub { 114 @Override 115 public IMediaSession createSession(String packageName, IMediaSessionCallback cb, String tag) 116 throws RemoteException { 117 final int pid = Binder.getCallingPid(); 118 final int uid = Binder.getCallingUid(); 119 final long token = Binder.clearCallingIdentity(); 120 try { 121 enforcePackageName(packageName, uid); 122 if (cb == null) { 123 throw new IllegalArgumentException("Controller callback cannot be null"); 124 } 125 return createSessionInternal(pid, packageName, cb, tag).getSessionBinder(); 126 } finally { 127 Binder.restoreCallingIdentity(token); 128 } 129 } 130 } 131 132} 133