FileDocCategorySizeDatePackage
MediaSessionService.javaAPI DocAndroid 5.1 API45737Thu Mar 12 22:22:42 GMT 2015com.android.server.media

MediaSessionService

public class MediaSessionService extends com.android.server.SystemService implements com.android.server.Watchdog.Monitor
System implementation of MediaSessionManager

Fields Summary
private static final String
TAG
private static final boolean
DEBUG
private static final int
WAKELOCK_TIMEOUT
final android.os.IBinder
mICallback
private final SessionManagerImpl
mSessionManagerImpl
private final MediaSessionStack
mPriorityStack
private final ArrayList
mAllSessions
private final android.util.SparseArray
mUserRecords
private final ArrayList
mSessionsListeners
private final Object
mLock
private final MessageHandler
mHandler
private final PowerManager.WakeLock
mMediaEventWakeLock
private final boolean
mUseMasterVolume
private android.app.KeyguardManager
mKeyguardManager
private android.media.IAudioService
mAudioService
private android.media.AudioManager
mAudioManager
private android.content.ContentResolver
mContentResolver
private SettingsObserver
mSettingsObserver
private int
mCurrentUserId
private android.media.IRemoteVolumeController
mRvc
Constructors Summary
public MediaSessionService(android.content.Context context)


       
        super(context);
        mSessionManagerImpl = new SessionManagerImpl();
        mPriorityStack = new MediaSessionStack();
        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
        mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
        mUseMasterVolume = context.getResources().getBoolean(
                com.android.internal.R.bool.config_useMasterVolume);
    
Methods Summary
private MediaSessionRecordcreateSessionInternal(int callerPid, int callerUid, int userId, java.lang.String callerPackageName, android.media.session.ISessionCallback cb, java.lang.String tag)

        synchronized (mLock) {
            return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
        }
    
private MediaSessionRecordcreateSessionLocked(int callerPid, int callerUid, int userId, java.lang.String callerPackageName, android.media.session.ISessionCallback cb, java.lang.String tag)


        final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
                callerPackageName, cb, tag, this, mHandler);
        try {
            cb.asBinder().linkToDeath(session, 0);
        } catch (RemoteException e) {
            throw new RuntimeException("Media Session owner died prematurely.", e);
        }

        mAllSessions.add(session);
        mPriorityStack.addSession(session);

        UserRecord user = getOrCreateUser(userId);
        user.addSessionLocked(session);

        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);

        if (DEBUG) {
            Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
        }
        return session;
    
voiddestroySession(MediaSessionRecord session)

        synchronized (mLock) {
            destroySessionLocked(session);
        }
    
private voiddestroySessionLocked(MediaSessionRecord session)

        int userId = session.getUserId();
        UserRecord user = mUserRecords.get(userId);
        if (user != null) {
            user.removeSessionLocked(session);
        }

        mPriorityStack.removeSession(session);
        mAllSessions.remove(session);

        try {
            session.getCallback().asBinder().unlinkToDeath(session, 0);
        } catch (Exception e) {
            // ignore exceptions while destroying a session.
        }
        session.onDestroy();

        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
    
private voiddestroyUserLocked(com.android.server.media.MediaSessionService$UserRecord user)
Stop the user and unbind from everything.

param
user The user to dispose of

        user.stopLocked();
        user.destroyLocked();
        mUserRecords.remove(user.mUserId);
    
private voidenforceMediaPermissions(android.content.ComponentName compName, int pid, int uid, int resolvedUserId)
Checks a caller's authorization to register an IRemoteControlDisplay. Authorization is granted if one of the following is true:
  • the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL permission
  • the caller's listener is one of the enabled notification listeners for the caller's user

        if (getContext()
                .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
                    != PackageManager.PERMISSION_GRANTED
                && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
                        resolvedUserId)) {
            throw new SecurityException("Missing permission to control media.");
        }
    
private voidenforcePackageName(java.lang.String packageName, int uid)

        if (TextUtils.isEmpty(packageName)) {
            throw new IllegalArgumentException("packageName may not be empty");
        }
        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
        final int packageCount = packages.length;
        for (int i = 0; i < packageCount; i++) {
            if (packageName.equals(packages[i])) {
                return;
            }
        }
        throw new IllegalArgumentException("packageName is not owned by the calling process");
    
protected voidenforcePhoneStatePermission(int pid, int uid)

        if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
                != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
        }
    
private voidenforceStatusBarPermission(java.lang.String action, int pid, int uid)

        if (getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
                pid, uid) != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("Only system ui may " + action);
        }
    
private intfindIndexOfSessionsListenerLocked(android.media.session.IActiveSessionsListener listener)

        for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
            if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
                return i;
            }
        }
        return -1;
    
private android.media.IAudioServicegetAudioService()

        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
        return IAudioService.Stub.asInterface(b);
    
private com.android.server.media.MediaSessionService$UserRecordgetOrCreateUser(int userId)

        UserRecord user = mUserRecords.get(userId);
        if (user == null) {
            user = new UserRecord(getContext(), userId);
            mUserRecords.put(userId, user);
        }
        return user;
    
private booleanisEnabledNotificationListener(android.content.ComponentName compName, int userId, int forUserId)
This checks if the component is an enabled notification listener for the specified user. Enabled components may only operate on behalf of the user they're running as.

param
compName The component that is enabled.
param
userId The user id of the caller.
param
forUserId The user id they're making the request on behalf of.
return
True if the component is enabled, false otherwise

        if (userId != forUserId) {
            // You may not access another user's content as an enabled listener.
            return false;
        }
        if (DEBUG) {
            Log.d(TAG, "Checking if enabled notification listener " + compName);
        }
        if (compName != null) {
            final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
                    Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
                    userId);
            if (enabledNotifListeners != null) {
                final String[] components = enabledNotifListeners.split(":");
                for (int i = 0; i < components.length; i++) {
                    final ComponentName component =
                            ComponentName.unflattenFromString(components[i]);
                    if (component != null) {
                        if (compName.equals(component)) {
                            if (DEBUG) {
                                Log.d(TAG, "ok to get sessions: " + component +
                                        " is authorized notification listener");
                            }
                            return true;
                        }
                    }
                }
            }
            if (DEBUG) {
                Log.d(TAG, "not ok to get sessions, " + compName +
                        " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
            }
        }
        return false;
    
private booleanisSessionDiscoverable(MediaSessionRecord record)

        // TODO probably want to check more than if it's active.
        return record.isActive();
    
public voidmonitor()

        synchronized (mLock) {
            // Check for deadlock
        }
    
public voidnotifyRemoteVolumeChanged(int flags, MediaSessionRecord session)
Tells the system UI that volume has changed on a remote session.

        if (mRvc == null) {
            return;
        }
        try {
            mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
        } catch (Exception e) {
            Log.wtf(TAG, "Error sending volume change to system UI.", e);
        }
    
public voidonSessionPlaybackTypeChanged(MediaSessionRecord record)

        synchronized (mLock) {
            if (!mAllSessions.contains(record)) {
                Log.d(TAG, "Unknown session changed playback type. Ignoring.");
                return;
            }
            pushRemoteVolumeUpdateLocked(record.getUserId());
        }
    
public voidonSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState)

        boolean updateSessions = false;
        synchronized (mLock) {
            if (!mAllSessions.contains(record)) {
                Log.d(TAG, "Unknown session changed playback state. Ignoring.");
                return;
            }
            updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
        }
        if (updateSessions) {
            mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
        }
    
public voidonStart()

        publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
        Watchdog.getInstance().addMonitor(this);
        updateUser();
        mKeyguardManager =
                (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
        mAudioService = getAudioService();
        mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
        mContentResolver = getContext().getContentResolver();
        mSettingsObserver = new SettingsObserver();
        mSettingsObserver.observe();
    
public voidonStartUser(int userHandle)

        updateUser();
    
public voidonStopUser(int userHandle)

        synchronized (mLock) {
            UserRecord user = mUserRecords.get(userHandle);
            if (user != null) {
                destroyUserLocked(user);
            }
        }
    
public voidonSwitchUser(int userHandle)

        updateUser();
    
private voidpushRemoteVolumeUpdateLocked(int userId)

        if (mRvc != null) {
            try {
                MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
                mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
            } catch (RemoteException e) {
                Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
            }
        }
    
private voidpushSessionsChanged(int userId)

        synchronized (mLock) {
            List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
            int size = records.size();
            if (size > 0 && records.get(0).isPlaybackActive(false)) {
                rememberMediaButtonReceiverLocked(records.get(0));
            }
            ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
            for (int i = 0; i < size; i++) {
                tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
            }
            pushRemoteVolumeUpdateLocked(userId);
            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
                SessionsListenerRecord record = mSessionsListeners.get(i);
                if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
                    try {
                        record.mListener.onActiveSessionsChanged(tokens);
                    } catch (RemoteException e) {
                        Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
                                e);
                        mSessionsListeners.remove(i);
                    }
                }
            }
        }
    
private voidrememberMediaButtonReceiverLocked(MediaSessionRecord record)

        PendingIntent receiver = record.getMediaButtonReceiver();
        UserRecord user = mUserRecords.get(record.getUserId());
        if (receiver != null && user != null) {
            user.mLastMediaButtonReceiver = receiver;
        }
    
voidsessionDied(MediaSessionRecord session)

        synchronized (mLock) {
            destroySessionLocked(session);
        }
    
private voidupdateActiveSessionListeners()

        synchronized (mLock) {
            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
                SessionsListenerRecord listener = mSessionsListeners.get(i);
                try {
                    enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
                            listener.mUserId);
                } catch (SecurityException e) {
                    Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
                            + " is no longer authorized. Disconnecting.");
                    mSessionsListeners.remove(i);
                    try {
                        listener.mListener
                                .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
                    } catch (Exception e1) {
                        // ignore
                    }
                }
            }
        }
    
public voidupdateSession(MediaSessionRecord record)

        synchronized (mLock) {
            if (!mAllSessions.contains(record)) {
                Log.d(TAG, "Unknown session updated. Ignoring.");
                return;
            }
            mPriorityStack.onSessionStateChange(record);
        }
        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
    
private voidupdateUser()

        synchronized (mLock) {
            int userId = ActivityManager.getCurrentUser();
            if (mCurrentUserId != userId) {
                final int oldUserId = mCurrentUserId;
                mCurrentUserId = userId; // do this first

                UserRecord oldUser = mUserRecords.get(oldUserId);
                if (oldUser != null) {
                    oldUser.stopLocked();
                }

                UserRecord newUser = getOrCreateUser(userId);
                newUser.startLocked();
            }
        }