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