101fe661ae5da3739215d93922412df4b24c859a2RoboErik/*
201fe661ae5da3739215d93922412df4b24c859a2RoboErik * Copyright (C) 2014 The Android Open Source Project
301fe661ae5da3739215d93922412df4b24c859a2RoboErik *
401fe661ae5da3739215d93922412df4b24c859a2RoboErik * Licensed under the Apache License, Version 2.0 (the "License");
501fe661ae5da3739215d93922412df4b24c859a2RoboErik * you may not use this file except in compliance with the License.
601fe661ae5da3739215d93922412df4b24c859a2RoboErik * You may obtain a copy of the License at
701fe661ae5da3739215d93922412df4b24c859a2RoboErik *
801fe661ae5da3739215d93922412df4b24c859a2RoboErik *      http://www.apache.org/licenses/LICENSE-2.0
901fe661ae5da3739215d93922412df4b24c859a2RoboErik *
1001fe661ae5da3739215d93922412df4b24c859a2RoboErik * Unless required by applicable law or agreed to in writing, software
1101fe661ae5da3739215d93922412df4b24c859a2RoboErik * distributed under the License is distributed on an "AS IS" BASIS,
1201fe661ae5da3739215d93922412df4b24c859a2RoboErik * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1301fe661ae5da3739215d93922412df4b24c859a2RoboErik * See the License for the specific language governing permissions and
1401fe661ae5da3739215d93922412df4b24c859a2RoboErik * limitations under the License.
1501fe661ae5da3739215d93922412df4b24c859a2RoboErik */
1601fe661ae5da3739215d93922412df4b24c859a2RoboErik
1701fe661ae5da3739215d93922412df4b24c859a2RoboErikpackage com.android.server.media;
1801fe661ae5da3739215d93922412df4b24c859a2RoboErik
19a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErikimport android.Manifest;
208a2cfc309ab9126e90022916967c65a793c034f0RoboErikimport android.app.Activity;
21e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErikimport android.app.ActivityManager;
229a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErikimport android.app.KeyguardManager;
23b214efbb9170a9f6a4991684a63ca59680074cc7RoboErikimport android.app.PendingIntent;
24b214efbb9170a9f6a4991684a63ca59680074cc7RoboErikimport android.app.PendingIntent.CanceledException;
259a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErikimport android.content.ActivityNotFoundException;
268a2cfc309ab9126e90022916967c65a793c034f0RoboErikimport android.content.BroadcastReceiver;
27e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErikimport android.content.ComponentName;
286f0e4ddd66fcdcc13944d8970d0b560e2626508bRoboErikimport android.content.ContentResolver;
2901fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.content.Context;
308a2cfc309ab9126e90022916967c65a793c034f0RoboErikimport android.content.Intent;
31a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErikimport android.content.pm.PackageManager;
327aef77bbf5b983b9f949936ed6cd174251697ca8RoboErikimport android.database.ContentObserver;
333c45c29109d23981d8b707c809b3b43ce2e20fc3RoboErikimport android.media.AudioManager;
3494c716ea2927cf7d16c66d8846b976f06bcb6460RoboErikimport android.media.AudioSystem;
35b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErikimport android.media.IAudioService;
3619c9518f6a817d53d5234de0020313cab6950b2fRoboErikimport android.media.IRemoteVolumeController;
372e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErikimport android.media.session.IActiveSessionsListener;
3807c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErikimport android.media.session.ISession;
3907c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErikimport android.media.session.ISessionCallback;
4007c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErikimport android.media.session.ISessionManager;
41d2b8c947ddfc6349a3ae6c3968b422b9cf50d7edRoboErikimport android.media.session.MediaController.PlaybackInfo;
42dba34ba35cd2042d9a8fecfda56e2abe7a680badJeff Brownimport android.media.session.MediaSession;
437c82ced4fc5b66c09a19eed9a5499039530142fbRoboErikimport android.media.session.MediaSessionManager;
447aef77bbf5b983b9f949936ed6cd174251697ca8RoboErikimport android.net.Uri;
4501fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.Binder;
468a2cfc309ab9126e90022916967c65a793c034f0RoboErikimport android.os.Bundle;
478ae0f34db936a649ddaf9cdd086c224f6514efebRoboErikimport android.os.Handler;
48e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErikimport android.os.IBinder;
492e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErikimport android.os.Message;
508a2cfc309ab9126e90022916967c65a793c034f0RoboErikimport android.os.PowerManager;
5101fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.os.RemoteException;
528a2cfc309ab9126e90022916967c65a793c034f0RoboErikimport android.os.ResultReceiver;
53b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErikimport android.os.ServiceManager;
54e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErikimport android.os.UserHandle;
55e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErikimport android.provider.Settings;
569a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErikimport android.speech.RecognizerIntent;
5701fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.text.TextUtils;
5801fe661ae5da3739215d93922412df4b24c859a2RoboErikimport android.util.Log;
594646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErikimport android.util.SparseArray;
608a2cfc309ab9126e90022916967c65a793c034f0RoboErikimport android.view.KeyEvent;
6101fe661ae5da3739215d93922412df4b24c859a2RoboErik
6201fe661ae5da3739215d93922412df4b24c859a2RoboErikimport com.android.server.SystemService;
63a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErikimport com.android.server.Watchdog;
64a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErikimport com.android.server.Watchdog.Monitor;
6501fe661ae5da3739215d93922412df4b24c859a2RoboErik
66a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErikimport java.io.FileDescriptor;
67a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErikimport java.io.PrintWriter;
6801fe661ae5da3739215d93922412df4b24c859a2RoboErikimport java.util.ArrayList;
69e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErikimport java.util.List;
7001fe661ae5da3739215d93922412df4b24c859a2RoboErik
7101fe661ae5da3739215d93922412df4b24c859a2RoboErik/**
7201fe661ae5da3739215d93922412df4b24c859a2RoboErik * System implementation of MediaSessionManager
7301fe661ae5da3739215d93922412df4b24c859a2RoboErik */
74a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErikpublic class MediaSessionService extends SystemService implements Monitor {
7501fe661ae5da3739215d93922412df4b24c859a2RoboErik    private static final String TAG = "MediaSessionService";
7601fe661ae5da3739215d93922412df4b24c859a2RoboErik    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
7701fe661ae5da3739215d93922412df4b24c859a2RoboErik
78418c10ca9df1505509afeffd558cd92fc97bc635RoboErik    private static final int WAKELOCK_TIMEOUT = 5000;
79418c10ca9df1505509afeffd558cd92fc97bc635RoboErik
802610d71251e3e376e2514f20986c81e5d55f1b55RoboErik    /* package */final IBinder mICallback = new Binder();
812610d71251e3e376e2514f20986c81e5d55f1b55RoboErik
8201fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final SessionManagerImpl mSessionManagerImpl;
83a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    private final MediaSessionStack mPriorityStack;
8401fe661ae5da3739215d93922412df4b24c859a2RoboErik
854646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    private final ArrayList<MediaSessionRecord> mAllSessions = new ArrayList<MediaSessionRecord>();
864646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    private final SparseArray<UserRecord> mUserRecords = new SparseArray<UserRecord>();
872e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik    private final ArrayList<SessionsListenerRecord> mSessionsListeners
882e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            = new ArrayList<SessionsListenerRecord>();
8901fe661ae5da3739215d93922412df4b24c859a2RoboErik    private final Object mLock = new Object();
902e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik    private final MessageHandler mHandler = new MessageHandler();
918a2cfc309ab9126e90022916967c65a793c034f0RoboErik    private final PowerManager.WakeLock mMediaEventWakeLock;
92519c7744b522aa07e12bc3244ac3de14aa2a4ad0RoboErik    private final boolean mUseMasterVolume;
9301fe661ae5da3739215d93922412df4b24c859a2RoboErik
949a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik    private KeyguardManager mKeyguardManager;
95b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik    private IAudioService mAudioService;
962610d71251e3e376e2514f20986c81e5d55f1b55RoboErik    private AudioManager mAudioManager;
976f0e4ddd66fcdcc13944d8970d0b560e2626508bRoboErik    private ContentResolver mContentResolver;
987aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik    private SettingsObserver mSettingsObserver;
999a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik
1004646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    private int mCurrentUserId = -1;
101e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik
10219c9518f6a817d53d5234de0020313cab6950b2fRoboErik    // Used to notify system UI when remote volume was changed. TODO find a
10319c9518f6a817d53d5234de0020313cab6950b2fRoboErik    // better way to handle this.
10419c9518f6a817d53d5234de0020313cab6950b2fRoboErik    private IRemoteVolumeController mRvc;
10519c9518f6a817d53d5234de0020313cab6950b2fRoboErik
10601fe661ae5da3739215d93922412df4b24c859a2RoboErik    public MediaSessionService(Context context) {
10701fe661ae5da3739215d93922412df4b24c859a2RoboErik        super(context);
10801fe661ae5da3739215d93922412df4b24c859a2RoboErik        mSessionManagerImpl = new SessionManagerImpl();
109a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mPriorityStack = new MediaSessionStack();
1108a2cfc309ab9126e90022916967c65a793c034f0RoboErik        PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
1118a2cfc309ab9126e90022916967c65a793c034f0RoboErik        mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
112519c7744b522aa07e12bc3244ac3de14aa2a4ad0RoboErik        mUseMasterVolume = context.getResources().getBoolean(
113519c7744b522aa07e12bc3244ac3de14aa2a4ad0RoboErik                com.android.internal.R.bool.config_useMasterVolume);
11401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
11501fe661ae5da3739215d93922412df4b24c859a2RoboErik
11601fe661ae5da3739215d93922412df4b24c859a2RoboErik    @Override
11701fe661ae5da3739215d93922412df4b24c859a2RoboErik    public void onStart() {
11801fe661ae5da3739215d93922412df4b24c859a2RoboErik        publishBinderService(Context.MEDIA_SESSION_SERVICE, mSessionManagerImpl);
119a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik        Watchdog.getInstance().addMonitor(this);
1204646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        updateUser();
1219a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik        mKeyguardManager =
1229a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                (KeyguardManager) getContext().getSystemService(Context.KEYGUARD_SERVICE);
123b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        mAudioService = getAudioService();
1242610d71251e3e376e2514f20986c81e5d55f1b55RoboErik        mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
1256f0e4ddd66fcdcc13944d8970d0b560e2626508bRoboErik        mContentResolver = getContext().getContentResolver();
1267aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        mSettingsObserver = new SettingsObserver();
1277aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        mSettingsObserver.observe();
128b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik    }
129b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik
130b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik    private IAudioService getAudioService() {
131b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
132b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        return IAudioService.Stub.asInterface(b);
13307c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    }
13407c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik
135a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    public void updateSession(MediaSessionRecord record) {
136e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik        synchronized (mLock) {
1374646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            if (!mAllSessions.contains(record)) {
1384646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                Log.d(TAG, "Unknown session updated. Ignoring.");
1394646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                return;
1404646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            }
141a8f951462791a16f47e8c07e552232f31dcefac5RoboErik            mPriorityStack.onSessionStateChange(record);
142e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik        }
1432e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
144e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik    }
145e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik
1469c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik    /**
1479c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik     * Tells the system UI that volume has changed on a remote session.
1489c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik     */
1499c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik    public void notifyRemoteVolumeChanged(int flags, MediaSessionRecord session) {
1509c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik        if (mRvc == null) {
1519c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik            return;
1529c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik        }
1539c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik        try {
1549c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik            mRvc.remoteVolumeChanged(session.getControllerBinder(), flags);
1559c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik        } catch (Exception e) {
1569c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik            Log.wtf(TAG, "Error sending volume change to system UI.", e);
1579c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik        }
1589c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik    }
1599c5b7cb048699237b35dad7a2a634ed6efb997c7RoboErik
160a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    public void onSessionPlaystateChange(MediaSessionRecord record, int oldState, int newState) {
1612e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        boolean updateSessions = false;
162a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        synchronized (mLock) {
1634646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            if (!mAllSessions.contains(record)) {
1644646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                Log.d(TAG, "Unknown session changed playback state. Ignoring.");
1654646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                return;
1664646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            }
1672e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            updateSessions = mPriorityStack.onPlaystateChange(record, oldState, newState);
1682e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        }
1692e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        if (updateSessions) {
1702e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, record.getUserId(), 0);
171a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        }
172a8f951462791a16f47e8c07e552232f31dcefac5RoboErik    }
173a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
17419c9518f6a817d53d5234de0020313cab6950b2fRoboErik    public void onSessionPlaybackTypeChanged(MediaSessionRecord record) {
17519c9518f6a817d53d5234de0020313cab6950b2fRoboErik        synchronized (mLock) {
17619c9518f6a817d53d5234de0020313cab6950b2fRoboErik            if (!mAllSessions.contains(record)) {
17719c9518f6a817d53d5234de0020313cab6950b2fRoboErik                Log.d(TAG, "Unknown session changed playback type. Ignoring.");
17819c9518f6a817d53d5234de0020313cab6950b2fRoboErik                return;
17919c9518f6a817d53d5234de0020313cab6950b2fRoboErik            }
18019c9518f6a817d53d5234de0020313cab6950b2fRoboErik            pushRemoteVolumeUpdateLocked(record.getUserId());
18119c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
18219c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
18319c9518f6a817d53d5234de0020313cab6950b2fRoboErik
184a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik    @Override
1854646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    public void onStartUser(int userHandle) {
1864646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        updateUser();
1874646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    }
1884646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
1894646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    @Override
1904646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    public void onSwitchUser(int userHandle) {
1914646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        updateUser();
1924646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    }
1934646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
1944646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    @Override
1954646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    public void onStopUser(int userHandle) {
1964646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        synchronized (mLock) {
1974646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            UserRecord user = mUserRecords.get(userHandle);
1984646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            if (user != null) {
1994646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                destroyUserLocked(user);
2004646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            }
2014646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
2024646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    }
2034646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
2044646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    @Override
205a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik    public void monitor() {
206a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik        synchronized (mLock) {
207a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik            // Check for deadlock
208a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik        }
209a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik    }
210a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik
2114646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    protected void enforcePhoneStatePermission(int pid, int uid) {
2124646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        if (getContext().checkPermission(android.Manifest.permission.MODIFY_PHONE_STATE, pid, uid)
2134646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                != PackageManager.PERMISSION_GRANTED) {
2144646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            throw new SecurityException("Must hold the MODIFY_PHONE_STATE permission.");
2154646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
2164646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    }
2174646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
21801fe661ae5da3739215d93922412df4b24c859a2RoboErik    void sessionDied(MediaSessionRecord session) {
219a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik        synchronized (mLock) {
22001fe661ae5da3739215d93922412df4b24c859a2RoboErik            destroySessionLocked(session);
22101fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
22201fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
22301fe661ae5da3739215d93922412df4b24c859a2RoboErik
22401fe661ae5da3739215d93922412df4b24c859a2RoboErik    void destroySession(MediaSessionRecord session) {
225a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik        synchronized (mLock) {
22601fe661ae5da3739215d93922412df4b24c859a2RoboErik            destroySessionLocked(session);
22701fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
22801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
22901fe661ae5da3739215d93922412df4b24c859a2RoboErik
2304646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    private void updateUser() {
2314646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        synchronized (mLock) {
2324646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            int userId = ActivityManager.getCurrentUser();
2334646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            if (mCurrentUserId != userId) {
2344646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                final int oldUserId = mCurrentUserId;
2354646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                mCurrentUserId = userId; // do this first
2364646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
2374646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                UserRecord oldUser = mUserRecords.get(oldUserId);
2384646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                if (oldUser != null) {
2394646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                    oldUser.stopLocked();
2404646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                }
2414646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
2424646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                UserRecord newUser = getOrCreateUser(userId);
2434646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                newUser.startLocked();
2444646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            }
2454646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
2464646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    }
2474646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
2487aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik    private void updateActiveSessionListeners() {
2497aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        synchronized (mLock) {
2507aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
2517aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                SessionsListenerRecord listener = mSessionsListeners.get(i);
2527aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                try {
2537aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                    enforceMediaPermissions(listener.mComponentName, listener.mPid, listener.mUid,
2547aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                            listener.mUserId);
2557aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                } catch (SecurityException e) {
2567aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                    Log.i(TAG, "ActiveSessionsListener " + listener.mComponentName
2577aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                            + " is no longer authorized. Disconnecting.");
2587aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                    mSessionsListeners.remove(i);
2597aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                    try {
2607aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                        listener.mListener
2617aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                                .onActiveSessionsChanged(new ArrayList<MediaSession.Token>());
2627aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                    } catch (Exception e1) {
2637aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                        // ignore
2647aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                    }
2657aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                }
2667aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik            }
2677aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        }
2687aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik    }
2697aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik
2704646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    /**
2714646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * Stop the user and unbind from everything.
2724646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     *
2734646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * @param user The user to dispose of
2744646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     */
2754646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    private void destroyUserLocked(UserRecord user) {
2764646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        user.stopLocked();
2774646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        user.destroyLocked();
2784646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        mUserRecords.remove(user.mUserId);
2794646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    }
2804646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
2814646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    /*
2824646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * When a session is removed several things need to happen.
2834646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * 1. We need to remove it from the relevant user.
2844646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * 2. We need to remove it from the priority stack.
2854646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * 3. We need to remove it from all sessions.
2864646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * 4. If this is the system priority session we need to clear it.
2874646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * 5. We need to unlink to death from the cb binder
2884646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * 6. We need to tell the session to do any final cleanup (onDestroy)
2894646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     */
29001fe661ae5da3739215d93922412df4b24c859a2RoboErik    private void destroySessionLocked(MediaSessionRecord session) {
2914646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        int userId = session.getUserId();
2924646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        UserRecord user = mUserRecords.get(userId);
2934646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        if (user != null) {
2944646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            user.removeSessionLocked(session);
2954646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
2964646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
297a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mPriorityStack.removeSession(session);
2984646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        mAllSessions.remove(session);
2994646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
3004646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        try {
3014646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            session.getCallback().asBinder().unlinkToDeath(session, 0);
3024646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        } catch (Exception e) {
3034646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            // ignore exceptions while destroying a session.
3044646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
3054646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        session.onDestroy();
3062e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
3072e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, session.getUserId(), 0);
30801fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
30901fe661ae5da3739215d93922412df4b24c859a2RoboErik
31001fe661ae5da3739215d93922412df4b24c859a2RoboErik    private void enforcePackageName(String packageName, int uid) {
31101fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (TextUtils.isEmpty(packageName)) {
31201fe661ae5da3739215d93922412df4b24c859a2RoboErik            throw new IllegalArgumentException("packageName may not be empty");
31301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
31401fe661ae5da3739215d93922412df4b24c859a2RoboErik        String[] packages = getContext().getPackageManager().getPackagesForUid(uid);
31501fe661ae5da3739215d93922412df4b24c859a2RoboErik        final int packageCount = packages.length;
31601fe661ae5da3739215d93922412df4b24c859a2RoboErik        for (int i = 0; i < packageCount; i++) {
31701fe661ae5da3739215d93922412df4b24c859a2RoboErik            if (packageName.equals(packages[i])) {
31801fe661ae5da3739215d93922412df4b24c859a2RoboErik                return;
31901fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
32001fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
32101fe661ae5da3739215d93922412df4b24c859a2RoboErik        throw new IllegalArgumentException("packageName is not owned by the calling process");
32201fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
32301fe661ae5da3739215d93922412df4b24c859a2RoboErik
324e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik    /**
325e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik     * Checks a caller's authorization to register an IRemoteControlDisplay.
326e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik     * Authorization is granted if one of the following is true:
327e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik     * <ul>
328e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik     * <li>the caller has android.Manifest.permission.MEDIA_CONTENT_CONTROL
329e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik     * permission</li>
330a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * <li>the caller's listener is one of the enabled notification listeners
331a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * for the caller's user</li>
332e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik     * </ul>
333e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik     */
334a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    private void enforceMediaPermissions(ComponentName compName, int pid, int uid,
335a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            int resolvedUserId) {
336e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik        if (getContext()
337e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                .checkPermission(android.Manifest.permission.MEDIA_CONTENT_CONTROL, pid, uid)
338e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                    != PackageManager.PERMISSION_GRANTED
339a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                && !isEnabledNotificationListener(compName, UserHandle.getUserId(uid),
340a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                        resolvedUserId)) {
341e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik            throw new SecurityException("Missing permission to control media.");
342e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik        }
343e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik    }
344e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik
34519c9518f6a817d53d5234de0020313cab6950b2fRoboErik    private void enforceStatusBarPermission(String action, int pid, int uid) {
34619c9518f6a817d53d5234de0020313cab6950b2fRoboErik        if (getContext().checkPermission(android.Manifest.permission.STATUS_BAR_SERVICE,
34719c9518f6a817d53d5234de0020313cab6950b2fRoboErik                pid, uid) != PackageManager.PERMISSION_GRANTED) {
34819c9518f6a817d53d5234de0020313cab6950b2fRoboErik            throw new SecurityException("Only system ui may " + action);
34919c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
35019c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
35119c9518f6a817d53d5234de0020313cab6950b2fRoboErik
352a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    /**
353a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * This checks if the component is an enabled notification listener for the
354a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * specified user. Enabled components may only operate on behalf of the user
355a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * they're running as.
356a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     *
357a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param compName The component that is enabled.
358a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param userId The user id of the caller.
359a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @param forUserId The user id they're making the request on behalf of.
360a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     * @return True if the component is enabled, false otherwise
361a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik     */
362a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    private boolean isEnabledNotificationListener(ComponentName compName, int userId,
363a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            int forUserId) {
364a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik        if (userId != forUserId) {
365a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            // You may not access another user's content as an enabled listener.
366a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            return false;
367a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik        }
36851fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik        if (DEBUG) {
36951fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik            Log.d(TAG, "Checking if enabled notification listener " + compName);
37051fa6bcb22a52b283f6d0756d286101f0d354f54RoboErik        }
371e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik        if (compName != null) {
3726f0e4ddd66fcdcc13944d8970d0b560e2626508bRoboErik            final String enabledNotifListeners = Settings.Secure.getStringForUser(mContentResolver,
373e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                    Settings.Secure.ENABLED_NOTIFICATION_LISTENERS,
374a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                    userId);
375e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik            if (enabledNotifListeners != null) {
376e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                final String[] components = enabledNotifListeners.split(":");
377e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                for (int i = 0; i < components.length; i++) {
378e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                    final ComponentName component =
379e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                            ComponentName.unflattenFromString(components[i]);
380e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                    if (component != null) {
381e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                        if (compName.equals(component)) {
382e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                            if (DEBUG) {
383e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                                Log.d(TAG, "ok to get sessions: " + component +
384e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                                        " is authorized notification listener");
385e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                            }
386e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                            return true;
387e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                        }
388e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                    }
389e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                }
390e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik            }
391e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik            if (DEBUG) {
392e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                Log.d(TAG, "not ok to get sessions, " + compName +
393a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                        " is not in list of ENABLED_NOTIFICATION_LISTENERS for user " + userId);
394e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik            }
395e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik        }
396e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik        return false;
397e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik    }
398e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik
399a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    private MediaSessionRecord createSessionInternal(int callerPid, int callerUid, int userId,
4004646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            String callerPackageName, ISessionCallback cb, String tag) throws RemoteException {
40101fe661ae5da3739215d93922412df4b24c859a2RoboErik        synchronized (mLock) {
402a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            return createSessionLocked(callerPid, callerUid, userId, callerPackageName, cb, tag);
40301fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
40401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
40501fe661ae5da3739215d93922412df4b24c859a2RoboErik
4064646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    /*
4074646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * When a session is created the following things need to happen.
4088a2cfc309ab9126e90022916967c65a793c034f0RoboErik     * 1. Its callback binder needs a link to death
4094646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * 2. It needs to be added to all sessions.
4104646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * 3. It needs to be added to the priority stack.
4114646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * 4. It needs to be added to the relevant user record.
4124646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     */
413a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik    private MediaSessionRecord createSessionLocked(int callerPid, int callerUid, int userId,
414a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            String callerPackageName, ISessionCallback cb, String tag) {
4154646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
416a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik        final MediaSessionRecord session = new MediaSessionRecord(callerPid, callerUid, userId,
417a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                callerPackageName, cb, tag, this, mHandler);
41801fe661ae5da3739215d93922412df4b24c859a2RoboErik        try {
41901fe661ae5da3739215d93922412df4b24c859a2RoboErik            cb.asBinder().linkToDeath(session, 0);
42001fe661ae5da3739215d93922412df4b24c859a2RoboErik        } catch (RemoteException e) {
42101fe661ae5da3739215d93922412df4b24c859a2RoboErik            throw new RuntimeException("Media Session owner died prematurely.", e);
42201fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
4234646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
4244646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        mAllSessions.add(session);
425a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        mPriorityStack.addSession(session);
4264646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
4274646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        UserRecord user = getOrCreateUser(userId);
4284646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        user.addSessionLocked(session);
4294646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
4302e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        mHandler.post(MessageHandler.MSG_SESSIONS_CHANGED, userId, 0);
4312e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
43201fe661ae5da3739215d93922412df4b24c859a2RoboErik        if (DEBUG) {
433a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik            Log.d(TAG, "Created session for package " + callerPackageName + " with tag " + tag);
43401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
43501fe661ae5da3739215d93922412df4b24c859a2RoboErik        return session;
43601fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
43701fe661ae5da3739215d93922412df4b24c859a2RoboErik
4384646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    private UserRecord getOrCreateUser(int userId) {
4394646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        UserRecord user = mUserRecords.get(userId);
4404646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        if (user == null) {
4414646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            user = new UserRecord(getContext(), userId);
4424646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            mUserRecords.put(userId, user);
4434646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
4444646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        return user;
4454646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    }
4464646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
4472e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik    private int findIndexOfSessionsListenerLocked(IActiveSessionsListener listener) {
4482e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
449a08adb2437f641087e01436293736be235fe1fdaRoboErik            if (mSessionsListeners.get(i).mListener.asBinder() == listener.asBinder()) {
4502e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                return i;
4512e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            }
4522e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        }
4532e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        return -1;
4542e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik    }
4552e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
456e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik    private boolean isSessionDiscoverable(MediaSessionRecord record) {
4574646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        // TODO probably want to check more than if it's active.
458a8f951462791a16f47e8c07e552232f31dcefac5RoboErik        return record.isActive();
459e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik    }
460e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik
4612e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik    private void pushSessionsChanged(int userId) {
4622e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        synchronized (mLock) {
4632e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            List<MediaSessionRecord> records = mPriorityStack.getActiveSessions(userId);
4642e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            int size = records.size();
465870c5a6593f723211a4a90766be9191c69f38570RoboErik            if (size > 0 && records.get(0).isPlaybackActive(false)) {
466b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                rememberMediaButtonReceiverLocked(records.get(0));
4676f0e4ddd66fcdcc13944d8970d0b560e2626508bRoboErik            }
468dba34ba35cd2042d9a8fecfda56e2abe7a680badJeff Brown            ArrayList<MediaSession.Token> tokens = new ArrayList<MediaSession.Token>();
4692e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            for (int i = 0; i < size; i++) {
470dba34ba35cd2042d9a8fecfda56e2abe7a680badJeff Brown                tokens.add(new MediaSession.Token(records.get(i).getControllerBinder()));
4712e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            }
47219c9518f6a817d53d5234de0020313cab6950b2fRoboErik            pushRemoteVolumeUpdateLocked(userId);
4732e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            for (int i = mSessionsListeners.size() - 1; i >= 0; i--) {
4742e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                SessionsListenerRecord record = mSessionsListeners.get(i);
4752e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                if (record.mUserId == UserHandle.USER_ALL || record.mUserId == userId) {
4762e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    try {
4772e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                        record.mListener.onActiveSessionsChanged(tokens);
4782e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    } catch (RemoteException e) {
4792e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                        Log.w(TAG, "Dead ActiveSessionsListener in pushSessionsChanged, removing",
4802e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                                e);
4812e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                        mSessionsListeners.remove(i);
4822e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    }
4832e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                }
4842e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            }
4852e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        }
4862e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik    }
4872e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
48819c9518f6a817d53d5234de0020313cab6950b2fRoboErik    private void pushRemoteVolumeUpdateLocked(int userId) {
48919c9518f6a817d53d5234de0020313cab6950b2fRoboErik        if (mRvc != null) {
49019c9518f6a817d53d5234de0020313cab6950b2fRoboErik            try {
49119c9518f6a817d53d5234de0020313cab6950b2fRoboErik                MediaSessionRecord record = mPriorityStack.getDefaultRemoteSession(userId);
49219c9518f6a817d53d5234de0020313cab6950b2fRoboErik                mRvc.updateRemoteController(record == null ? null : record.getControllerBinder());
49319c9518f6a817d53d5234de0020313cab6950b2fRoboErik            } catch (RemoteException e) {
49419c9518f6a817d53d5234de0020313cab6950b2fRoboErik                Log.wtf(TAG, "Error sending default remote volume to sys ui.", e);
49519c9518f6a817d53d5234de0020313cab6950b2fRoboErik            }
49619c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
49719c9518f6a817d53d5234de0020313cab6950b2fRoboErik    }
49819c9518f6a817d53d5234de0020313cab6950b2fRoboErik
499b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik    private void rememberMediaButtonReceiverLocked(MediaSessionRecord record) {
500b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik        PendingIntent receiver = record.getMediaButtonReceiver();
501b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik        UserRecord user = mUserRecords.get(record.getUserId());
502b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik        if (receiver != null && user != null) {
503b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik            user.mLastMediaButtonReceiver = receiver;
5046f0e4ddd66fcdcc13944d8970d0b560e2626508bRoboErik        }
5056f0e4ddd66fcdcc13944d8970d0b560e2626508bRoboErik    }
5066f0e4ddd66fcdcc13944d8970d0b560e2626508bRoboErik
5074646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    /**
5084646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * Information about a particular user. The contents of this object is
5094646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     * guarded by mLock.
5104646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik     */
5114646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    final class UserRecord {
5124646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        private final int mUserId;
5134646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        private final ArrayList<MediaSessionRecord> mSessions = new ArrayList<MediaSessionRecord>();
514b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik        private PendingIntent mLastMediaButtonReceiver;
5154646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
5164646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        public UserRecord(Context context, int userId) {
5174646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            mUserId = userId;
5184646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
5194646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
5204646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        public void startLocked() {
52101a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown            // nothing for now
5224646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
5234646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
5244646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        public void stopLocked() {
52501a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown            // nothing for now
5264646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
5274646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
5284646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        public void destroyLocked() {
5294646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            for (int i = mSessions.size() - 1; i >= 0; i--) {
5304646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                MediaSessionRecord session = mSessions.get(i);
5314646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                MediaSessionService.this.destroySessionLocked(session);
5324646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            }
5334646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
5344646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
5354646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        public ArrayList<MediaSessionRecord> getSessionsLocked() {
5364646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            return mSessions;
5374646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
5384646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
5394646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        public void addSessionLocked(MediaSessionRecord session) {
5404646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            mSessions.add(session);
5414646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
5424646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
5434646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        public void removeSessionLocked(MediaSessionRecord session) {
5444646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            mSessions.remove(session);
5454646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
5464646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
5474646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        public void dumpLocked(PrintWriter pw, String prefix) {
5484646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            pw.println(prefix + "Record for user " + mUserId);
5494646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            String indent = prefix + "  ";
550b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik            pw.println(indent + "MediaButtonReceiver:" + mLastMediaButtonReceiver);
55101a500ed1c6ae3fff66678144ae637aa8cad0eccJeff Brown            int size = mSessions.size();
5524646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            pw.println(indent + size + " Sessions:");
5534646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            for (int i = 0; i < size; i++) {
554aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                // Just print the short version, the full session dump will
5554646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                // already be in the list of all sessions.
556aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                pw.println(indent + mSessions.get(i).toString());
5574646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik            }
5584646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik        }
5594646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik    }
5604646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik
5612e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik    final class SessionsListenerRecord implements IBinder.DeathRecipient {
5622e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        private final IActiveSessionsListener mListener;
5637aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        private final ComponentName mComponentName;
5642e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        private final int mUserId;
5657aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        private final int mPid;
5667aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        private final int mUid;
5672e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
5687aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        public SessionsListenerRecord(IActiveSessionsListener listener,
5697aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                ComponentName componentName,
5707aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                int userId, int pid, int uid) {
5712e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            mListener = listener;
5727aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik            mComponentName = componentName;
5732e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            mUserId = userId;
5747aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik            mPid = pid;
5757aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik            mUid = uid;
5762e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        }
5772e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
5782e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        @Override
5792e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        public void binderDied() {
5802e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            synchronized (mLock) {
5812e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                mSessionsListeners.remove(this);
5822e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            }
5832e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        }
5842e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik    }
5852e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
5867aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik    final class SettingsObserver extends ContentObserver {
5877aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        private final Uri mSecureSettingsUri = Settings.Secure.getUriFor(
5887aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                Settings.Secure.ENABLED_NOTIFICATION_LISTENERS);
5897aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik
5907aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        private SettingsObserver() {
5917aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik            super(null);
5927aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        }
5937aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik
5947aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        private void observe() {
5957aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik            mContentResolver.registerContentObserver(mSecureSettingsUri,
5967aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                    false, this, UserHandle.USER_ALL);
5977aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        }
5987aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik
5997aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        @Override
6007aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        public void onChange(boolean selfChange, Uri uri) {
6017aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik            updateActiveSessionListeners();
6027aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik        }
6037aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik    }
6047aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik
60507c7077c54717dbbf2c401ea32d00fa6df6d77c6RoboErik    class SessionManagerImpl extends ISessionManager.Stub {
6068a2cfc309ab9126e90022916967c65a793c034f0RoboErik        private static final String EXTRA_WAKELOCK_ACQUIRED =
6078a2cfc309ab9126e90022916967c65a793c034f0RoboErik                "android.media.AudioService.WAKELOCK_ACQUIRED";
6088a2cfc309ab9126e90022916967c65a793c034f0RoboErik        private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; // magic number
6098a2cfc309ab9126e90022916967c65a793c034f0RoboErik
6109a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik        private boolean mVoiceButtonDown = false;
6119a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik        private boolean mVoiceButtonHandled = false;
6129a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik
61301fe661ae5da3739215d93922412df4b24c859a2RoboErik        @Override
614a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik        public ISession createSession(String packageName, ISessionCallback cb, String tag,
615a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                int userId) throws RemoteException {
61601fe661ae5da3739215d93922412df4b24c859a2RoboErik            final int pid = Binder.getCallingPid();
61701fe661ae5da3739215d93922412df4b24c859a2RoboErik            final int uid = Binder.getCallingUid();
61801fe661ae5da3739215d93922412df4b24c859a2RoboErik            final long token = Binder.clearCallingIdentity();
61901fe661ae5da3739215d93922412df4b24c859a2RoboErik            try {
62001fe661ae5da3739215d93922412df4b24c859a2RoboErik                enforcePackageName(packageName, uid);
621a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
622a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                        false /* allowAll */, true /* requireFull */, "createSession", packageName);
62301fe661ae5da3739215d93922412df4b24c859a2RoboErik                if (cb == null) {
62401fe661ae5da3739215d93922412df4b24c859a2RoboErik                    throw new IllegalArgumentException("Controller callback cannot be null");
62501fe661ae5da3739215d93922412df4b24c859a2RoboErik                }
626a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                return createSessionInternal(pid, uid, resolvedUserId, packageName, cb, tag)
627a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                        .getSessionBinder();
628e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik            } finally {
629e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                Binder.restoreCallingIdentity(token);
630e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik            }
631e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik        }
632e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik
633e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik        @Override
634a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik        public List<IBinder> getSessions(ComponentName componentName, int userId) {
635e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik            final int pid = Binder.getCallingPid();
636e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik            final int uid = Binder.getCallingUid();
637e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik            final long token = Binder.clearCallingIdentity();
638e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik
639e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik            try {
6402e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
641e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                ArrayList<IBinder> binders = new ArrayList<IBinder>();
642e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                synchronized (mLock) {
643a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    ArrayList<MediaSessionRecord> records = mPriorityStack
644a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                            .getActiveSessions(resolvedUserId);
645a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    int size = records.size();
646a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                    for (int i = 0; i < size; i++) {
647a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                        binders.add(records.get(i).getControllerBinder().asBinder());
648e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                    }
649e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                }
650e7880d8eb1903d42e4e2a90c99b58e2240e01e82RoboErik                return binders;
65101fe661ae5da3739215d93922412df4b24c859a2RoboErik            } finally {
65201fe661ae5da3739215d93922412df4b24c859a2RoboErik                Binder.restoreCallingIdentity(token);
65301fe661ae5da3739215d93922412df4b24c859a2RoboErik            }
65401fe661ae5da3739215d93922412df4b24c859a2RoboErik        }
655a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik
6562e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        @Override
6572e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        public void addSessionsListener(IActiveSessionsListener listener,
6582e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                ComponentName componentName, int userId) throws RemoteException {
6592e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            final int pid = Binder.getCallingPid();
6602e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            final int uid = Binder.getCallingUid();
6612e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            final long token = Binder.clearCallingIdentity();
6622e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
6632e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            try {
6642e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                int resolvedUserId = verifySessionsRequest(componentName, userId, pid, uid);
6652e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                synchronized (mLock) {
6662e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    int index = findIndexOfSessionsListenerLocked(listener);
6672e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    if (index != -1) {
6682e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                        Log.w(TAG, "ActiveSessionsListener is already added, ignoring");
6692e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                        return;
6702e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    }
6712e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    SessionsListenerRecord record = new SessionsListenerRecord(listener,
6727aef77bbf5b983b9f949936ed6cd174251697ca8RoboErik                            componentName, resolvedUserId, pid, uid);
6732e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    try {
6742e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                        listener.asBinder().linkToDeath(record, 0);
6752e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    } catch (RemoteException e) {
6762e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                        Log.e(TAG, "ActiveSessionsListener is dead, ignoring it", e);
6772e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                        return;
6782e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    }
6792e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    mSessionsListeners.add(record);
6802e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                }
6812e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            } finally {
6822e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                Binder.restoreCallingIdentity(token);
6832e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            }
6842e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        }
6852e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
6862e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        @Override
6872e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        public void removeSessionsListener(IActiveSessionsListener listener)
6882e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                throws RemoteException {
6892e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            synchronized (mLock) {
6902e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                int index = findIndexOfSessionsListenerLocked(listener);
6912e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                if (index != -1) {
6922e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    SessionsListenerRecord record = mSessionsListeners.remove(index);
6932e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    try {
6942e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                        record.mListener.asBinder().unlinkToDeath(record, 0);
6952e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    } catch (Exception e) {
6962e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                        // ignore exceptions, the record is being removed
6972e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    }
6982e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                }
6992e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            }
7002e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        }
7012e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
7028a2cfc309ab9126e90022916967c65a793c034f0RoboErik        /**
7038a2cfc309ab9126e90022916967c65a793c034f0RoboErik         * Handles the dispatching of the media button events to one of the
7048a2cfc309ab9126e90022916967c65a793c034f0RoboErik         * registered listeners, or if there was none, broadcast an
7058a2cfc309ab9126e90022916967c65a793c034f0RoboErik         * ACTION_MEDIA_BUTTON intent to the rest of the system.
7068a2cfc309ab9126e90022916967c65a793c034f0RoboErik         *
7078a2cfc309ab9126e90022916967c65a793c034f0RoboErik         * @param keyEvent a non-null KeyEvent whose key code is one of the
7088a2cfc309ab9126e90022916967c65a793c034f0RoboErik         *            supported media buttons
7098a2cfc309ab9126e90022916967c65a793c034f0RoboErik         * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held
7108a2cfc309ab9126e90022916967c65a793c034f0RoboErik         *            while this key event is dispatched.
7118a2cfc309ab9126e90022916967c65a793c034f0RoboErik         */
7128a2cfc309ab9126e90022916967c65a793c034f0RoboErik        @Override
7138a2cfc309ab9126e90022916967c65a793c034f0RoboErik        public void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
7148a2cfc309ab9126e90022916967c65a793c034f0RoboErik            if (keyEvent == null || !KeyEvent.isMediaKey(keyEvent.getKeyCode())) {
7158a2cfc309ab9126e90022916967c65a793c034f0RoboErik                Log.w(TAG, "Attempted to dispatch null or non-media key event.");
7168a2cfc309ab9126e90022916967c65a793c034f0RoboErik                return;
7178a2cfc309ab9126e90022916967c65a793c034f0RoboErik            }
7188a2cfc309ab9126e90022916967c65a793c034f0RoboErik            final int pid = Binder.getCallingPid();
7198a2cfc309ab9126e90022916967c65a793c034f0RoboErik            final int uid = Binder.getCallingUid();
7208a2cfc309ab9126e90022916967c65a793c034f0RoboErik            final long token = Binder.clearCallingIdentity();
7218a2cfc309ab9126e90022916967c65a793c034f0RoboErik
7228a2cfc309ab9126e90022916967c65a793c034f0RoboErik            try {
7238a2cfc309ab9126e90022916967c65a793c034f0RoboErik                synchronized (mLock) {
724870c5a6593f723211a4a90766be9191c69f38570RoboErik                    // If we don't have a media button receiver to fall back on
725870c5a6593f723211a4a90766be9191c69f38570RoboErik                    // include non-playing sessions for dispatching
726870c5a6593f723211a4a90766be9191c69f38570RoboErik                    boolean useNotPlayingSessions = mUserRecords.get(
727870c5a6593f723211a4a90766be9191c69f38570RoboErik                            ActivityManager.getCurrentUser()).mLastMediaButtonReceiver == null;
7289a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    MediaSessionRecord session = mPriorityStack
729870c5a6593f723211a4a90766be9191c69f38570RoboErik                            .getDefaultMediaButtonSession(mCurrentUserId, useNotPlayingSessions);
7309a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    if (isVoiceKey(keyEvent.getKeyCode())) {
7319a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                        handleVoiceKeyEventLocked(keyEvent, needWakeLock, session);
7328a2cfc309ab9126e90022916967c65a793c034f0RoboErik                    } else {
7339a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
7348a2cfc309ab9126e90022916967c65a793c034f0RoboErik                    }
7358a2cfc309ab9126e90022916967c65a793c034f0RoboErik                }
7368a2cfc309ab9126e90022916967c65a793c034f0RoboErik            } finally {
7378a2cfc309ab9126e90022916967c65a793c034f0RoboErik                Binder.restoreCallingIdentity(token);
7388a2cfc309ab9126e90022916967c65a793c034f0RoboErik            }
7398a2cfc309ab9126e90022916967c65a793c034f0RoboErik        }
7408a2cfc309ab9126e90022916967c65a793c034f0RoboErik
741a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik        @Override
7427c82ced4fc5b66c09a19eed9a5499039530142fbRoboErik        public void dispatchAdjustVolume(int suggestedStream, int delta, int flags) {
743b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            final int pid = Binder.getCallingPid();
744b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            final int uid = Binder.getCallingUid();
745b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            final long token = Binder.clearCallingIdentity();
746b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            try {
747b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik                synchronized (mLock) {
748b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik                    MediaSessionRecord session = mPriorityStack
749b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik                            .getDefaultVolumeSession(mCurrentUserId);
7501ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik                    dispatchAdjustVolumeLocked(suggestedStream, delta, flags, session);
751b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik                }
752b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            } finally {
753b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik                Binder.restoreCallingIdentity(token);
754b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            }
755b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        }
756b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik
757b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        @Override
75819c9518f6a817d53d5234de0020313cab6950b2fRoboErik        public void setRemoteVolumeController(IRemoteVolumeController rvc) {
75919c9518f6a817d53d5234de0020313cab6950b2fRoboErik            final int pid = Binder.getCallingPid();
76019c9518f6a817d53d5234de0020313cab6950b2fRoboErik            final int uid = Binder.getCallingUid();
76119c9518f6a817d53d5234de0020313cab6950b2fRoboErik            final long token = Binder.clearCallingIdentity();
76219c9518f6a817d53d5234de0020313cab6950b2fRoboErik            try {
76319c9518f6a817d53d5234de0020313cab6950b2fRoboErik                enforceStatusBarPermission("listen for volume changes", pid, uid);
76419c9518f6a817d53d5234de0020313cab6950b2fRoboErik                mRvc = rvc;
76519c9518f6a817d53d5234de0020313cab6950b2fRoboErik            } finally {
76619c9518f6a817d53d5234de0020313cab6950b2fRoboErik                Binder.restoreCallingIdentity(token);
76719c9518f6a817d53d5234de0020313cab6950b2fRoboErik            }
76819c9518f6a817d53d5234de0020313cab6950b2fRoboErik        }
76919c9518f6a817d53d5234de0020313cab6950b2fRoboErik
77019c9518f6a817d53d5234de0020313cab6950b2fRoboErik        @Override
771de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik        public boolean isGlobalPriorityActive() {
772de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik            return mPriorityStack.isGlobalPriorityActive();
773de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik        }
774de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik
775de9ba39c1714f5bc9e1785d8224ad26c132b6293RoboErik        @Override
776a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik        public void dump(FileDescriptor fd, final PrintWriter pw, String[] args) {
777a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik            if (getContext().checkCallingOrSelfPermission(Manifest.permission.DUMP)
778a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik                    != PackageManager.PERMISSION_GRANTED) {
779a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik                pw.println("Permission Denial: can't dump MediaSessionService from from pid="
780a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik                        + Binder.getCallingPid()
781a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik                        + ", uid=" + Binder.getCallingUid());
782a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik                return;
783a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik            }
784a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik
785a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik            pw.println("MEDIA SESSION SERVICE (dumpsys media_session)");
786a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik            pw.println();
787a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik
788a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik            synchronized (mLock) {
789a08adb2437f641087e01436293736be235fe1fdaRoboErik                pw.println(mSessionsListeners.size() + " sessions listeners.");
7904646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                int count = mAllSessions.size();
791a8f951462791a16f47e8c07e552232f31dcefac5RoboErik                pw.println(count + " Sessions:");
792a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik                for (int i = 0; i < count; i++) {
7934646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                    mAllSessions.get(i).dump(pw, "");
794a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik                    pw.println();
795a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik                }
796a5b02329209be355eafadbdf9ee685ffa58d3148RoboErik                mPriorityStack.dump(pw, "");
797a8f951462791a16f47e8c07e552232f31dcefac5RoboErik
7984646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                pw.println("User Records:");
7994646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                count = mUserRecords.size();
800a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik                for (int i = 0; i < count; i++) {
8014646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                    UserRecord user = mUserRecords.get(i);
8024646d288821d62fdfe481be67d8b7fed7d7eabd8RoboErik                    user.dumpLocked(pw, "");
803a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik                }
804a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik            }
805a278ea7cecb59a73586e5dd74ec05e85caa370c5RoboErik        }
8068a2cfc309ab9126e90022916967c65a793c034f0RoboErik
8072e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        private int verifySessionsRequest(ComponentName componentName, int userId, final int pid,
8082e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                final int uid) {
8092e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            String packageName = null;
8102e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            if (componentName != null) {
8112e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                // If they gave us a component name verify they own the
8122e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                // package
8132e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                packageName = componentName.getPackageName();
8142e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                enforcePackageName(packageName, uid);
8152e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            }
8162e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            // Check that they can make calls on behalf of the user and
8172e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            // get the final user id
8182e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            int resolvedUserId = ActivityManager.handleIncomingUser(pid, uid, userId,
8192e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    true /* allowAll */, true /* requireFull */, "getSessions", packageName);
8202e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            // Check if they have the permissions or their component is
8212e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            // enabled for the user they're calling from.
8222e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            enforceMediaPermissions(componentName, pid, uid, resolvedUserId);
8232e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            return resolvedUserId;
8242e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        }
8252e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
8261ff5b1648a051e9650614f0c0f1b3f449777db81RoboErik        private void dispatchAdjustVolumeLocked(int suggestedStream, int direction, int flags,
827b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik                MediaSessionRecord session) {
828b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            if (DEBUG) {
829aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                String description = session == null ? null : session.toString();
830aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                Log.d(TAG, "Adjusting session " + description + " by " + direction + ". flags="
831aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                        + flags + ", suggestedStream=" + suggestedStream);
832b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik
833b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            }
8349c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik            boolean preferSuggestedStream = false;
8359c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik            if (isValidLocalStreamType(suggestedStream)
8369c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik                    && AudioSystem.isStreamActive(suggestedStream, 0)) {
8379c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik                preferSuggestedStream = true;
8389c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik            }
8399c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik            if (session == null || preferSuggestedStream) {
84094c716ea2927cf7d16c66d8846b976f06bcb6460RoboErik                if ((flags & AudioManager.FLAG_ACTIVE_MEDIA_ONLY) != 0
84194c716ea2927cf7d16c66d8846b976f06bcb6460RoboErik                        && !AudioSystem.isStreamActive(AudioManager.STREAM_MUSIC, 0)) {
8423c45c29109d23981d8b707c809b3b43ce2e20fc3RoboErik                    if (DEBUG) {
8433c45c29109d23981d8b707c809b3b43ce2e20fc3RoboErik                        Log.d(TAG, "No active session to adjust, skipping media only volume event");
8443c45c29109d23981d8b707c809b3b43ce2e20fc3RoboErik                    }
845b7c014c291eff2a97f32ce1ae7e42bd8f825c74cRoboErik                    return;
8463c45c29109d23981d8b707c809b3b43ce2e20fc3RoboErik                }
8470791e1713be3df6acfc9e3dfa0dd8be55cf1a165RoboErik                try {
8482b5208c3583b5fd82564523bfd9e85b4bf44afa0Eric Laurent                    String packageName = getContext().getOpPackageName();
849519c7744b522aa07e12bc3244ac3de14aa2a4ad0RoboErik                    if (mUseMasterVolume) {
8502b5208c3583b5fd82564523bfd9e85b4bf44afa0Eric Laurent                        boolean isMasterMute = mAudioService.isMasterMute();
8517c82ced4fc5b66c09a19eed9a5499039530142fbRoboErik                        if (direction == MediaSessionManager.DIRECTION_MUTE) {
8522b5208c3583b5fd82564523bfd9e85b4bf44afa0Eric Laurent                            mAudioService.setMasterMute(!isMasterMute, flags, packageName, mICallback);
8537c82ced4fc5b66c09a19eed9a5499039530142fbRoboErik                        } else {
8542b5208c3583b5fd82564523bfd9e85b4bf44afa0Eric Laurent                            mAudioService.adjustMasterVolume(direction, flags, packageName);
855d5489220e27bc8d476b59847ae2005d68dd94864Sungsoo Lim                            // Do not call setMasterMute when direction = 0 which is used just to
856d5489220e27bc8d476b59847ae2005d68dd94864Sungsoo Lim                            // show the UI.
85748248c80e58c459d211d36164baa69354e070c07Sungsoo Lim                            if (isMasterMute && direction != 0) {
8582b5208c3583b5fd82564523bfd9e85b4bf44afa0Eric Laurent                                mAudioService.setMasterMute(false, flags, packageName, mICallback);
8592b5208c3583b5fd82564523bfd9e85b4bf44afa0Eric Laurent                            }
8607c82ced4fc5b66c09a19eed9a5499039530142fbRoboErik                        }
861519c7744b522aa07e12bc3244ac3de14aa2a4ad0RoboErik                    } else {
8622b5208c3583b5fd82564523bfd9e85b4bf44afa0Eric Laurent                        boolean isStreamMute = mAudioService.isStreamMute(suggestedStream);
8637c82ced4fc5b66c09a19eed9a5499039530142fbRoboErik                        if (direction == MediaSessionManager.DIRECTION_MUTE) {
8642610d71251e3e376e2514f20986c81e5d55f1b55RoboErik                            mAudioManager.setStreamMute(suggestedStream, !isStreamMute);
8657c82ced4fc5b66c09a19eed9a5499039530142fbRoboErik                        } else {
8667c82ced4fc5b66c09a19eed9a5499039530142fbRoboErik                            mAudioService.adjustSuggestedStreamVolume(direction, suggestedStream,
8672b5208c3583b5fd82564523bfd9e85b4bf44afa0Eric Laurent                                    flags, packageName);
868d5489220e27bc8d476b59847ae2005d68dd94864Sungsoo Lim                            // Do not call setStreamMute when direction = 0 which is used just to
869d5489220e27bc8d476b59847ae2005d68dd94864Sungsoo Lim                            // show the UI.
87048248c80e58c459d211d36164baa69354e070c07Sungsoo Lim                            if (isStreamMute && direction != 0) {
8712610d71251e3e376e2514f20986c81e5d55f1b55RoboErik                                mAudioManager.setStreamMute(suggestedStream, false);
8722b5208c3583b5fd82564523bfd9e85b4bf44afa0Eric Laurent                            }
8737c82ced4fc5b66c09a19eed9a5499039530142fbRoboErik                        }
874519c7744b522aa07e12bc3244ac3de14aa2a4ad0RoboErik                    }
8750791e1713be3df6acfc9e3dfa0dd8be55cf1a165RoboErik                } catch (RemoteException e) {
8760791e1713be3df6acfc9e3dfa0dd8be55cf1a165RoboErik                    Log.e(TAG, "Error adjusting default volume.", e);
877b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik                }
878b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            } else {
8790dac35af2c6aa42bcd181981b041747cfd1afa5fRoboErik                session.adjustVolume(direction, flags, getContext().getPackageName(),
880272e161c1a200900cb10b5b0cdab8ae1f123cabdRoboErik                        UserHandle.myUserId(), true);
881b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik            }
882b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik        }
883b69ffd4dc2c8fa85e0064151141ebeee90de471eRoboErik
8849a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik        private void handleVoiceKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
8859a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                MediaSessionRecord session) {
8869a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            if (session != null && session.hasFlag(MediaSession.FLAG_EXCLUSIVE_GLOBAL_PRIORITY)) {
8879a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                // If the phone app has priority just give it the event
8889a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
8899a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                return;
8909a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            }
8919a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            int action = keyEvent.getAction();
8929a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            boolean isLongPress = (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0;
8939a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            if (action == KeyEvent.ACTION_DOWN) {
8949a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                if (keyEvent.getRepeatCount() == 0) {
8959a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    mVoiceButtonDown = true;
8969a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    mVoiceButtonHandled = false;
8979a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                } else if (mVoiceButtonDown && !mVoiceButtonHandled && isLongPress) {
8989a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    mVoiceButtonHandled = true;
8999a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    startVoiceInput(needWakeLock);
9009a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                }
9019a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            } else if (action == KeyEvent.ACTION_UP) {
9029a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                if (mVoiceButtonDown) {
9039a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    mVoiceButtonDown = false;
9049a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
9059a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                        // Resend the down then send this event through
9069a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                        KeyEvent downEvent = KeyEvent.changeAction(keyEvent, KeyEvent.ACTION_DOWN);
9079a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                        dispatchMediaKeyEventLocked(downEvent, needWakeLock, session);
9089a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                        dispatchMediaKeyEventLocked(keyEvent, needWakeLock, session);
9099a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    }
9109a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                }
9119a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            }
9129a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik        }
9139a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik
9149a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik        private void dispatchMediaKeyEventLocked(KeyEvent keyEvent, boolean needWakeLock,
9159a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                MediaSessionRecord session) {
9169a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            if (session != null) {
9179a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                if (DEBUG) {
918aa4e23bbb36994708ba72c5f4c83255025d99e07RoboErik                    Log.d(TAG, "Sending media key to " + session.toString());
9199a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                }
9209a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                if (needWakeLock) {
9219a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    mKeyEventReceiver.aquireWakeLockLocked();
9229a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                }
9239a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                // If we don't need a wakelock use -1 as the id so we
9249a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                // won't release it later
9259a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                session.sendMediaButton(keyEvent,
9269a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                        needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
9279a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                        mKeyEventReceiver);
9289a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            } else {
929b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                // Launch the last PendingIntent we had with priority
930b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                int userId = ActivityManager.getCurrentUser();
931b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                UserRecord user = mUserRecords.get(userId);
932b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                if (user.mLastMediaButtonReceiver != null) {
933b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    if (DEBUG) {
934b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                        Log.d(TAG, "Sending media key to last known PendingIntent");
935b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    }
936b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    if (needWakeLock) {
937b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                        mKeyEventReceiver.aquireWakeLockLocked();
938b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    }
939b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
940b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    mediaButtonIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
941b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    try {
942b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                        user.mLastMediaButtonReceiver.send(getContext(),
943b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                                needWakeLock ? mKeyEventReceiver.mLastTimeoutId : -1,
944b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                                mediaButtonIntent, mKeyEventReceiver, null);
945b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    } catch (CanceledException e) {
946b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                        Log.i(TAG, "Error sending key event to media button receiver "
947b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                                + user.mLastMediaButtonReceiver, e);
948b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    }
949b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                } else {
950b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    if (DEBUG) {
951b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                        Log.d(TAG, "Sending media key ordered broadcast");
952b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    }
953b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    if (needWakeLock) {
954b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                        mMediaEventWakeLock.acquire();
955b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    }
956b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    // Fallback to legacy behavior
957b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
958b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
959b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    if (needWakeLock) {
960b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                        keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED,
961b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                                WAKELOCK_RELEASE_ON_FINISHED);
962b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    }
963b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    getContext().sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
964b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                            null, mKeyEventDone, mHandler, Activity.RESULT_OK, null, null);
9659a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                }
9669a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            }
9679a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik        }
9689a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik
9699a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik        private void startVoiceInput(boolean needWakeLock) {
9709a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            Intent voiceIntent = null;
9719a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            // select which type of search to launch:
9729a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            // - screen on and device unlocked: action is ACTION_WEB_SEARCH
9739a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            // - device locked or screen off: action is
9749a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            // ACTION_VOICE_SEARCH_HANDS_FREE
9759a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            // with EXTRA_SECURE set to true if the device is securely locked
9769a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
9779a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
9789a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            if (!isLocked && pm.isScreenOn()) {
9799a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
9809a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                Log.i(TAG, "voice-based interactions: about to use ACTION_WEB_SEARCH");
9819a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            } else {
9829a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
9839a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
9849a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                        isLocked && mKeyguardManager.isKeyguardSecure());
9859a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                Log.i(TAG, "voice-based interactions: about to use ACTION_VOICE_SEARCH_HANDS_FREE");
9869a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            }
9879a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            // start the search activity
9889a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            if (needWakeLock) {
9899a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                mMediaEventWakeLock.acquire();
9909a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            }
9919a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            try {
9929a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                if (voiceIntent != null) {
9939a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
9949a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
9959a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    getContext().startActivityAsUser(voiceIntent, UserHandle.CURRENT);
9969a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                }
9979a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            } catch (ActivityNotFoundException e) {
9989a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                Log.w(TAG, "No activity for search: " + e);
9999a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            } finally {
10009a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                if (needWakeLock) {
10019a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                    mMediaEventWakeLock.release();
10029a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik                }
10039a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            }
10049a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik        }
10059a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik
10069a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik        private boolean isVoiceKey(int keyCode) {
10079a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik            return keyCode == KeyEvent.KEYCODE_HEADSETHOOK;
10089a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik        }
10099a9d0b5f6f4be758ed6c8b837a9dd01a451bc0c0RoboErik
10109c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik        // we only handle public stream types, which are 0-5
10119c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik        private boolean isValidLocalStreamType(int streamType) {
10129c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik            return streamType >= AudioManager.STREAM_VOICE_CALL
10139c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik                    && streamType <= AudioManager.STREAM_NOTIFICATION;
10149c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik        }
10159c7854014d86a3bb2c4f2f8e3b8ca7f1e01df7dfRoboErik
1016418c10ca9df1505509afeffd558cd92fc97bc635RoboErik        private KeyEventWakeLockReceiver mKeyEventReceiver = new KeyEventWakeLockReceiver(mHandler);
1017418c10ca9df1505509afeffd558cd92fc97bc635RoboErik
1018b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik        class KeyEventWakeLockReceiver extends ResultReceiver implements Runnable,
1019b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                PendingIntent.OnFinished {
1020418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            private final Handler mHandler;
1021418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            private int mRefCount = 0;
1022418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            private int mLastTimeoutId = 0;
1023418c10ca9df1505509afeffd558cd92fc97bc635RoboErik
1024418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            public KeyEventWakeLockReceiver(Handler handler) {
1025418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                super(handler);
1026418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                mHandler = handler;
1027418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            }
1028418c10ca9df1505509afeffd558cd92fc97bc635RoboErik
1029418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            public void onTimeout() {
1030418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                synchronized (mLock) {
1031418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                    if (mRefCount == 0) {
1032418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                        // We've already released it, so just return
1033418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                        return;
1034418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                    }
1035418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                    mLastTimeoutId++;
1036418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                    mRefCount = 0;
1037418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                    releaseWakeLockLocked();
1038418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                }
1039418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            }
1040418c10ca9df1505509afeffd558cd92fc97bc635RoboErik
1041418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            public void aquireWakeLockLocked() {
1042418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                if (mRefCount == 0) {
1043418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                    mMediaEventWakeLock.acquire();
1044418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                }
1045418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                mRefCount++;
1046418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                mHandler.removeCallbacks(this);
1047418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                mHandler.postDelayed(this, WAKELOCK_TIMEOUT);
1048418c10ca9df1505509afeffd558cd92fc97bc635RoboErik
1049418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            }
1050418c10ca9df1505509afeffd558cd92fc97bc635RoboErik
1051418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            @Override
1052418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            public void run() {
1053418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                onTimeout();
1054418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            }
1055418c10ca9df1505509afeffd558cd92fc97bc635RoboErik
10568a2cfc309ab9126e90022916967c65a793c034f0RoboErik            @Override
10578a2cfc309ab9126e90022916967c65a793c034f0RoboErik            protected void onReceiveResult(int resultCode, Bundle resultData) {
1058418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                if (resultCode < mLastTimeoutId) {
1059418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                    // Ignore results from calls that were before the last
1060418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                    // timeout, just in case.
1061418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                    return;
1062418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                } else {
1063418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                    synchronized (mLock) {
1064418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                        if (mRefCount > 0) {
1065418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                            mRefCount--;
1066418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                            if (mRefCount == 0) {
1067418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                                releaseWakeLockLocked();
1068418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                            }
1069418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                        }
10708a2cfc309ab9126e90022916967c65a793c034f0RoboErik                    }
10718a2cfc309ab9126e90022916967c65a793c034f0RoboErik                }
10728a2cfc309ab9126e90022916967c65a793c034f0RoboErik            }
1073418c10ca9df1505509afeffd558cd92fc97bc635RoboErik
1074418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            private void releaseWakeLockLocked() {
1075418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                mMediaEventWakeLock.release();
1076418c10ca9df1505509afeffd558cd92fc97bc635RoboErik                mHandler.removeCallbacks(this);
1077418c10ca9df1505509afeffd558cd92fc97bc635RoboErik            }
1078b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik
1079b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik            @Override
1080b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik            public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
1081b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                    String resultData, Bundle resultExtras) {
1082b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik                onReceiveResult(resultCode, null);
1083b214efbb9170a9f6a4991684a63ca59680074cc7RoboErik            }
10848a2cfc309ab9126e90022916967c65a793c034f0RoboErik        };
10858a2cfc309ab9126e90022916967c65a793c034f0RoboErik
10868a2cfc309ab9126e90022916967c65a793c034f0RoboErik        BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
10878a2cfc309ab9126e90022916967c65a793c034f0RoboErik            @Override
10888a2cfc309ab9126e90022916967c65a793c034f0RoboErik            public void onReceive(Context context, Intent intent) {
10898a2cfc309ab9126e90022916967c65a793c034f0RoboErik                if (intent == null) {
10908a2cfc309ab9126e90022916967c65a793c034f0RoboErik                    return;
10918a2cfc309ab9126e90022916967c65a793c034f0RoboErik                }
10928a2cfc309ab9126e90022916967c65a793c034f0RoboErik                Bundle extras = intent.getExtras();
10938a2cfc309ab9126e90022916967c65a793c034f0RoboErik                if (extras == null) {
10948a2cfc309ab9126e90022916967c65a793c034f0RoboErik                    return;
10958a2cfc309ab9126e90022916967c65a793c034f0RoboErik                }
10968a2cfc309ab9126e90022916967c65a793c034f0RoboErik                synchronized (mLock) {
10978a2cfc309ab9126e90022916967c65a793c034f0RoboErik                    if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)
10988a2cfc309ab9126e90022916967c65a793c034f0RoboErik                            && mMediaEventWakeLock.isHeld()) {
10998a2cfc309ab9126e90022916967c65a793c034f0RoboErik                        mMediaEventWakeLock.release();
11008a2cfc309ab9126e90022916967c65a793c034f0RoboErik                    }
11018a2cfc309ab9126e90022916967c65a793c034f0RoboErik                }
11028a2cfc309ab9126e90022916967c65a793c034f0RoboErik            }
11038a2cfc309ab9126e90022916967c65a793c034f0RoboErik        };
110401fe661ae5da3739215d93922412df4b24c859a2RoboErik    }
110501fe661ae5da3739215d93922412df4b24c859a2RoboErik
11062e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik    final class MessageHandler extends Handler {
11072e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        private static final int MSG_SESSIONS_CHANGED = 1;
11082e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
11092e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        @Override
11102e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        public void handleMessage(Message msg) {
11112e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            switch (msg.what) {
11122e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                case MSG_SESSIONS_CHANGED:
11132e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    pushSessionsChanged(msg.arg1);
11142e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik                    break;
11152e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            }
11162e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        }
11172e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik
11182e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        public void post(int what, int arg1, int arg2) {
11192e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik            obtainMessage(what, arg1, arg2).sendToTarget();
11202e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik        }
11212e7a9167aeefeb451f8d8c769175b9a0163744f3RoboErik    }
112201fe661ae5da3739215d93922412df4b24c859a2RoboErik}
1123