2 * Copyright (C) 2006 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package android.media;
19 import static android.Manifest.permission.REMOTE_AUDIO_PLAYBACK;
20 import static android.media.AudioManager.RINGER_MODE_NORMAL;
21 import static android.media.AudioManager.RINGER_MODE_SILENT;
22 import static android.media.AudioManager.RINGER_MODE_VIBRATE;
24 import android.app.Activity;
25 import android.app.ActivityManagerNative;
26 import android.app.KeyguardManager;
27 import android.app.PendingIntent;
28 import android.app.PendingIntent.CanceledException;
29 import android.app.PendingIntent.OnFinished;
30 import android.bluetooth.BluetoothA2dp;
31 import android.bluetooth.BluetoothAdapter;
32 import android.bluetooth.BluetoothClass;
33 import android.bluetooth.BluetoothDevice;
34 import android.bluetooth.BluetoothHeadset;
35 import android.bluetooth.BluetoothProfile;
36 import android.content.ActivityNotFoundException;
37 import android.content.BroadcastReceiver;
38 import android.content.ComponentName;
39 import android.content.ContentResolver;
40 import android.content.Context;
41 import android.content.Intent;
42 import android.content.IntentFilter;
43 import android.content.pm.PackageManager;
44 import android.content.res.Configuration;
45 import android.database.ContentObserver;
46 import android.media.MediaPlayer.OnCompletionListener;
47 import android.media.MediaPlayer.OnErrorListener;
48 import android.os.Binder;
49 import android.os.Bundle;
50 import android.os.Environment;
51 import android.os.Handler;
52 import android.os.IBinder;
53 import android.os.Looper;
54 import android.os.Message;
55 import android.os.PowerManager;
56 import android.os.RemoteCallbackList;
57 import android.os.RemoteException;
58 import android.os.ServiceManager;
59 import android.os.SystemProperties;
60 import android.os.Vibrator;
61 import android.provider.Settings;
62 import android.provider.Settings.System;
63 import android.speech.RecognizerIntent;
64 import android.telephony.PhoneStateListener;
65 import android.telephony.TelephonyManager;
66 import android.text.TextUtils;
67 import android.util.Log;
68 import android.view.KeyEvent;
69 import android.view.VolumePanel;
71 import com.android.internal.telephony.ITelephony;
73 import java.io.FileDescriptor;
74 import java.io.IOException;
75 import java.io.PrintWriter;
76 import java.util.ArrayList;
77 import java.util.concurrent.ConcurrentHashMap;
78 import java.util.HashMap;
79 import java.util.Iterator;
80 import java.util.List;
82 import java.util.NoSuchElementException;
84 import java.util.Stack;
87 * The implementation of the volume manager service.
89 * This implementation focuses on delivering a responsive UI. Most methods are
90 * asynchronous to external calls. For example, the task of setting a volume
91 * will update our internal state, but in a separate thread will set the system
92 * volume and later persist to the database. Similarly, setting the ringer mode
93 * will update the state and broadcast a change and in a separate thread later
94 * persist the ringer mode.
98 public class AudioService extends IAudioService.Stub implements OnFinished {
100 private static final String TAG = "AudioService";
102 /** Debug remote control client/display feature */
103 protected static final boolean DEBUG_RC = false;
105 protected static final boolean DEBUG_VOL = false;
107 /** How long to delay before persisting a change in volume/ringer mode. */
108 private static final int PERSIST_DELAY = 500;
110 private Context mContext;
111 private ContentResolver mContentResolver;
112 private boolean mVoiceCapable;
115 private VolumePanel mVolumePanel;
118 /** If the msg is already queued, replace it with this one. */
119 private static final int SENDMSG_REPLACE = 0;
120 /** If the msg is already queued, ignore this one and leave the old. */
121 private static final int SENDMSG_NOOP = 1;
122 /** If the msg is already queued, queue this one and leave the old. */
123 private static final int SENDMSG_QUEUE = 2;
125 // AudioHandler messages
126 private static final int MSG_SET_DEVICE_VOLUME = 0;
127 private static final int MSG_PERSIST_VOLUME = 1;
128 private static final int MSG_PERSIST_MASTER_VOLUME = 2;
129 private static final int MSG_PERSIST_RINGER_MODE = 3;
130 private static final int MSG_MEDIA_SERVER_DIED = 4;
131 private static final int MSG_MEDIA_SERVER_STARTED = 5;
132 private static final int MSG_PLAY_SOUND_EFFECT = 6;
133 private static final int MSG_BTA2DP_DOCK_TIMEOUT = 7;
134 private static final int MSG_LOAD_SOUND_EFFECTS = 8;
135 private static final int MSG_SET_FORCE_USE = 9;
136 private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 10;
137 private static final int MSG_BT_HEADSET_CNCT_FAILED = 11;
138 private static final int MSG_RCDISPLAY_CLEAR = 12;
139 private static final int MSG_RCDISPLAY_UPDATE = 13;
140 private static final int MSG_SET_ALL_VOLUMES = 14;
141 private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15;
142 private static final int MSG_REPORT_NEW_ROUTES = 16;
143 private static final int MSG_REEVALUATE_REMOTE = 17;
144 private static final int MSG_RCC_NEW_PLAYBACK_INFO = 18;
145 private static final int MSG_RCC_NEW_VOLUME_OBS = 19;
146 private static final int MSG_SET_FORCE_BT_A2DP_USE = 20;
147 // start of messages handled under wakelock
148 // these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
149 // and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
150 private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 21;
151 private static final int MSG_SET_A2DP_CONNECTION_STATE = 22;
152 // end of messages handled under wakelock
154 // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
156 private static final int PERSIST_CURRENT = 0x1;
157 private static final int PERSIST_LAST_AUDIBLE = 0x2;
159 private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
160 // Timeout for connection to bluetooth headset service
161 private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
163 /** @see AudioSystemThread */
164 private AudioSystemThread mAudioSystemThread;
165 /** @see AudioHandler */
166 private AudioHandler mAudioHandler;
167 /** @see VolumeStreamState */
168 private VolumeStreamState[] mStreamStates;
169 private SettingsObserver mSettingsObserver;
172 // protects mRingerMode
173 private final Object mSettingsLock = new Object();
175 private boolean mMediaServerOk;
177 private SoundPool mSoundPool;
178 private final Object mSoundEffectsLock = new Object();
179 private static final int NUM_SOUNDPOOL_CHANNELS = 4;
181 // Internally master volume is a float in the 0.0 - 1.0 range,
182 // but to support integer based AudioManager API we translate it to 0 - 100
183 private static final int MAX_MASTER_VOLUME = 100;
185 // Maximum volume adjust steps allowed in a single batch call.
186 private static final int MAX_BATCH_VOLUME_ADJUST_STEPS = 4;
188 /* Sound effect file names */
189 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
190 private static final String[] SOUND_EFFECT_FILES = new String[] {
192 "KeypressStandard.ogg",
193 "KeypressSpacebar.ogg",
194 "KeypressDelete.ogg",
198 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
199 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
200 * uses soundpool (second column) */
201 private final int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
202 {0, -1}, // FX_KEY_CLICK
203 {0, -1}, // FX_FOCUS_NAVIGATION_UP
204 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN
205 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT
206 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT
207 {1, -1}, // FX_KEYPRESS_STANDARD
208 {2, -1}, // FX_KEYPRESS_SPACEBAR
209 {3, -1}, // FX_FOCUS_DELETE
210 {4, -1} // FX_FOCUS_RETURN
213 /** @hide Maximum volume index values for audio streams */
214 private final int[] MAX_STREAM_VOLUME = new int[] {
215 5, // STREAM_VOICE_CALL
220 7, // STREAM_NOTIFICATION
221 15, // STREAM_BLUETOOTH_SCO
222 7, // STREAM_SYSTEM_ENFORCED
226 /* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings
227 * of another stream: This avoids multiplying the volume settings for hidden
228 * stream types that follow other stream behavior for volume settings
229 * NOTE: do not create loops in aliases!
230 * Some streams alias to different streams according to device category (phone or tablet) or
231 * use case (in call s off call...).See updateStreamVolumeAlias() for more details
232 * mStreamVolumeAlias contains the default aliases for a voice capable device (phone) and
233 * STREAM_VOLUME_ALIAS_NON_VOICE for a non voice capable device (tablet).*/
234 private final int[] STREAM_VOLUME_ALIAS = new int[] {
235 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
236 AudioSystem.STREAM_RING, // STREAM_SYSTEM
237 AudioSystem.STREAM_RING, // STREAM_RING
238 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
239 AudioSystem.STREAM_ALARM, // STREAM_ALARM
240 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
241 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
242 AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED
243 AudioSystem.STREAM_RING, // STREAM_DTMF
244 AudioSystem.STREAM_MUSIC // STREAM_TTS
246 private final int[] STREAM_VOLUME_ALIAS_NON_VOICE = new int[] {
247 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
248 AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM
249 AudioSystem.STREAM_RING, // STREAM_RING
250 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
251 AudioSystem.STREAM_ALARM, // STREAM_ALARM
252 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION
253 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
254 AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM_ENFORCED
255 AudioSystem.STREAM_MUSIC, // STREAM_DTMF
256 AudioSystem.STREAM_MUSIC // STREAM_TTS
258 private int[] mStreamVolumeAlias;
260 // stream names used by dumpStreamStates()
261 private final String[] STREAM_NAMES = new String[] {
267 "STREAM_NOTIFICATION",
268 "STREAM_BLUETOOTH_SCO",
269 "STREAM_SYSTEM_ENFORCED",
274 private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
275 public void onError(int error) {
277 case AudioSystem.AUDIO_STATUS_SERVER_DIED:
278 if (mMediaServerOk) {
279 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
281 mMediaServerOk = false;
284 case AudioSystem.AUDIO_STATUS_OK:
285 if (!mMediaServerOk) {
286 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SENDMSG_NOOP, 0, 0,
288 mMediaServerOk = true;
298 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
299 * {@link AudioManager#RINGER_MODE_SILENT}, or
300 * {@link AudioManager#RINGER_MODE_VIBRATE}.
302 // protected by mSettingsLock
303 private int mRingerMode;
305 /** @see System#MODE_RINGER_STREAMS_AFFECTED */
306 private int mRingerModeAffectedStreams;
308 // Streams currently muted by ringer mode
309 private int mRingerModeMutedStreams;
311 /** @see System#MUTE_STREAMS_AFFECTED */
312 private int mMuteAffectedStreams;
315 * NOTE: setVibrateSetting(), getVibrateSetting(), shouldVibrate() are deprecated.
316 * mVibrateSetting is just maintained during deprecation period but vibration policy is
317 * now only controlled by mHasVibrator and mRingerMode
319 private int mVibrateSetting;
321 // Is there a vibrator
322 private final boolean mHasVibrator;
324 // Broadcast receiver for device connections intent broadcasts
325 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
327 // Used to alter media button redirection when the phone is ringing.
328 private boolean mIsRinging = false;
330 // Devices currently connected
331 private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
333 // Forced device usage for communications
334 private int mForcedUseForComm;
336 // True if we have master volume support
337 private final boolean mUseMasterVolume;
339 private final int[] mMasterVolumeRamp;
341 // List of binder death handlers for setMode() client processes.
342 // The last process to have called setMode() is at the top of the list.
343 private final ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
345 // List of clients having issued a SCO start request
346 private final ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
348 // BluetoothHeadset API to control SCO connection
349 private BluetoothHeadset mBluetoothHeadset;
351 // Bluetooth headset device
352 private BluetoothDevice mBluetoothHeadsetDevice;
354 // Indicate if SCO audio connection is currently active and if the initiator is
355 // audio service (internal) or bluetooth headset (external)
356 private int mScoAudioState;
357 // SCO audio state is not active
358 private static final int SCO_STATE_INACTIVE = 0;
359 // SCO audio activation request waiting for headset service to connect
360 private static final int SCO_STATE_ACTIVATE_REQ = 1;
361 // SCO audio state is active or starting due to a local request to start a virtual call
362 private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
363 // SCO audio deactivation request waiting for headset service to connect
364 private static final int SCO_STATE_DEACTIVATE_REQ = 5;
366 // SCO audio state is active due to an action in BT handsfree (either voice recognition or
368 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
369 // Deactivation request for all SCO connections (initiated by audio mode change)
370 // waiting for headset service to connect
371 private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
373 // Current connection state indicated by bluetooth headset
374 private int mScoConnectionState;
376 // true if boot sequence has been completed
377 private boolean mBootCompleted;
378 // listener for SoundPool sample load completion indication
379 private SoundPoolCallback mSoundPoolCallBack;
380 // thread for SoundPool listener
381 private SoundPoolListenerThread mSoundPoolListenerThread;
382 // message looper for SoundPool listener
383 private Looper mSoundPoolLooper = null;
384 // volume applied to sound played with playSoundEffect()
385 private static int sSoundEffectVolumeDb;
386 // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification
388 private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
389 // previous volume adjustment direction received by checkForRingerModeChange()
390 private int mPrevVolDirection = AudioManager.ADJUST_SAME;
391 // Keyguard manager proxy
392 private KeyguardManager mKeyguardManager;
393 // mVolumeControlStream is set by VolumePanel to temporarily force the stream type which volume
394 // is controlled by Vol keys.
395 private int mVolumeControlStream = -1;
396 private final Object mForceControlStreamLock = new Object();
397 // VolumePanel is currently the only client of forceVolumeControlStream() and runs in system
398 // server process so in theory it is not necessary to monitor the client death.
399 // However it is good to be ready for future evolutions.
400 private ForceControlStreamClient mForceControlStreamClient = null;
401 // Used to play ringtones outside system_server
402 private volatile IRingtonePlayer mRingtonePlayer;
404 private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED;
406 // Request to override default use of A2DP for media.
407 private boolean mBluetoothA2dpEnabled;
408 private final Object mBluetoothA2dpEnabledLock = new Object();
410 // Monitoring of audio routes. Protected by mCurAudioRoutes.
411 final AudioRoutesInfo mCurAudioRoutes = new AudioRoutesInfo();
412 final RemoteCallbackList<IAudioRoutesObserver> mRoutesObservers
413 = new RemoteCallbackList<IAudioRoutesObserver>();
416 * A fake stream type to match the notion of remote media playback
418 public final static int STREAM_REMOTE_MUSIC = -200;
420 ///////////////////////////////////////////////////////////////////////////
422 ///////////////////////////////////////////////////////////////////////////
425 public AudioService(Context context) {
427 mContentResolver = context.getContentResolver();
428 mVoiceCapable = mContext.getResources().getBoolean(
429 com.android.internal.R.bool.config_voice_capable);
431 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
432 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
434 Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
435 mHasVibrator = vibrator == null ? false : vibrator.hasVibrator();
438 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
439 "ro.config.vc_call_vol_steps",
440 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
442 sSoundEffectVolumeDb = context.getResources().getInteger(
443 com.android.internal.R.integer.config_soundEffectVolumeDb);
445 mVolumePanel = new VolumePanel(context, this);
446 mMode = AudioSystem.MODE_NORMAL;
447 mForcedUseForComm = AudioSystem.FORCE_NONE;
448 createAudioSystemThread();
449 readPersistedSettings();
450 mSettingsObserver = new SettingsObserver();
451 updateStreamVolumeAlias(false /*updateVolumes*/);
452 createStreamStates();
454 mMediaServerOk = true;
456 // Call setRingerModeInt() to apply correct mute
457 // state on streams affected by ringer mode.
458 mRingerModeMutedStreams = 0;
459 setRingerModeInt(getRingerMode(), false);
461 AudioSystem.setErrorCallback(mAudioSystemCallback);
463 // Register for device connection intent broadcasts.
464 IntentFilter intentFilter =
465 new IntentFilter(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
466 intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
467 intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
468 intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG);
469 intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG);
470 intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
471 intentFilter.addAction(Intent.ACTION_SCREEN_ON);
472 intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
474 // Register a configuration change listener only if requested by system properties
475 // to monitor orientation changes (off by default)
476 if (SystemProperties.getBoolean("ro.audio.monitorOrientation", false)) {
477 Log.v(TAG, "monitoring device orientation");
478 intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
479 // initialize orientation in AudioSystem
480 setOrientationForAudioSystem();
483 context.registerReceiver(mReceiver, intentFilter);
485 // Register for package removal intent broadcasts for media button receiver persistence
486 IntentFilter pkgFilter = new IntentFilter();
487 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
488 pkgFilter.addDataScheme("package");
489 context.registerReceiver(mReceiver, pkgFilter);
491 // Register for phone state monitoring
492 TelephonyManager tmgr = (TelephonyManager)
493 context.getSystemService(Context.TELEPHONY_SERVICE);
494 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
496 mUseMasterVolume = context.getResources().getBoolean(
497 com.android.internal.R.bool.config_useMasterVolume);
498 restoreMasterVolume();
500 mMasterVolumeRamp = context.getResources().getIntArray(
501 com.android.internal.R.array.config_masterVolumeRamp);
503 mMainRemote = new RemotePlaybackState(-1, MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC],
504 MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
505 mHasRemotePlayback = false;
506 mMainRemoteIsActive = false;
507 postReevaluateRemote();
510 private void createAudioSystemThread() {
511 mAudioSystemThread = new AudioSystemThread();
512 mAudioSystemThread.start();
513 waitForAudioHandlerCreation();
516 /** Waits for the volume handler to be created by the other thread. */
517 private void waitForAudioHandlerCreation() {
519 while (mAudioHandler == null) {
521 // Wait for mAudioHandler to be set by the other thread
523 } catch (InterruptedException e) {
524 Log.e(TAG, "Interrupted while waiting on volume handler.");
530 private void checkAllAliasStreamVolumes() {
531 int numStreamTypes = AudioSystem.getNumStreamTypes();
532 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
533 if (streamType != mStreamVolumeAlias[streamType]) {
534 mStreamStates[streamType].
535 setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]],
536 false /*lastAudible*/);
537 mStreamStates[streamType].
538 setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]],
539 true /*lastAudible*/);
541 // apply stream volume
542 if (mStreamStates[streamType].muteCount() == 0) {
543 mStreamStates[streamType].applyAllVolumes();
548 private void createStreamStates() {
549 int numStreamTypes = AudioSystem.getNumStreamTypes();
550 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
552 for (int i = 0; i < numStreamTypes; i++) {
553 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i);
556 checkAllAliasStreamVolumes();
559 private void dumpStreamStates(PrintWriter pw) {
560 pw.println("\nStream volumes (device: index)");
561 int numStreamTypes = AudioSystem.getNumStreamTypes();
562 for (int i = 0; i < numStreamTypes; i++) {
563 pw.println("- "+STREAM_NAMES[i]+":");
564 mStreamStates[i].dump(pw);
570 private void updateStreamVolumeAlias(boolean updateVolumes) {
573 mStreamVolumeAlias = STREAM_VOLUME_ALIAS;
574 dtmfStreamAlias = AudioSystem.STREAM_RING;
576 mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NON_VOICE;
577 dtmfStreamAlias = AudioSystem.STREAM_MUSIC;
579 if (isInCommunication()) {
580 dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL;
582 mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias;
584 mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
585 false /*lastAudible*/);
586 mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias],
587 true /*lastAudible*/);
588 sendMsg(mAudioHandler,
593 mStreamStates[AudioSystem.STREAM_DTMF], 0);
597 private void readPersistedSettings() {
598 final ContentResolver cr = mContentResolver;
600 int ringerModeFromSettings =
601 System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
602 int ringerMode = ringerModeFromSettings;
603 // sanity check in case the settings are restored from a device with incompatible
605 if (!AudioManager.isValidRingerMode(ringerMode)) {
606 ringerMode = AudioManager.RINGER_MODE_NORMAL;
608 if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
609 ringerMode = AudioManager.RINGER_MODE_SILENT;
611 if (ringerMode != ringerModeFromSettings) {
612 System.putInt(cr, System.MODE_RINGER, ringerMode);
614 synchronized(mSettingsLock) {
615 mRingerMode = ringerMode;
618 // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting
619 // are still needed while setVibrateSetting() and getVibrateSetting() are being deprecated.
620 mVibrateSetting = getValueForVibrateSetting(0,
621 AudioManager.VIBRATE_TYPE_NOTIFICATION,
622 mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
623 : AudioManager.VIBRATE_SETTING_OFF);
624 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting,
625 AudioManager.VIBRATE_TYPE_RINGER,
626 mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
627 : AudioManager.VIBRATE_SETTING_OFF);
629 // make sure settings for ringer mode are consistent with device type: non voice capable
630 // devices (tablets) include media stream in silent mode whereas phones don't.
631 mRingerModeAffectedStreams = Settings.System.getInt(cr,
632 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
633 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
634 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
636 mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
638 mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
640 Settings.System.putInt(cr,
641 Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams);
643 mMuteAffectedStreams = System.getInt(cr,
644 System.MUTE_STREAMS_AFFECTED,
645 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
647 boolean masterMute = System.getInt(cr, System.VOLUME_MASTER_MUTE, 0) == 1;
648 AudioSystem.setMasterMute(masterMute);
649 broadcastMasterMuteStatus(masterMute);
651 // Each stream will read its own persisted settings
653 // Broadcast the sticky intent
654 broadcastRingerMode(ringerMode);
656 // Broadcast vibrate settings
657 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
658 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
660 // Restore the default media button receiver from the system settings
661 restoreMediaButtonReceiver();
664 private int rescaleIndex(int index, int srcStream, int dstStream) {
665 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
668 ///////////////////////////////////////////////////////////////////////////
670 ///////////////////////////////////////////////////////////////////////////
672 /** @see AudioManager#adjustVolume(int, int) */
673 public void adjustVolume(int direction, int flags) {
674 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
677 /** @see AudioManager#adjustLocalOrRemoteStreamVolume(int, int) with current assumption
678 * on streamType: fixed to STREAM_MUSIC */
679 public void adjustLocalOrRemoteStreamVolume(int streamType, int direction) {
680 if (DEBUG_VOL) Log.d(TAG, "adjustLocalOrRemoteStreamVolume(dir="+direction+")");
681 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
682 adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, 0);
683 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
684 adjustStreamVolume(AudioSystem.STREAM_MUSIC, direction, 0);
688 /** @see AudioManager#adjustVolume(int, int, int) */
689 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
690 if (DEBUG_VOL) Log.d(TAG, "adjustSuggestedStreamVolume() stream="+suggestedStreamType);
692 if (mVolumeControlStream != -1) {
693 streamType = mVolumeControlStream;
695 streamType = getActiveStreamType(suggestedStreamType);
698 // Play sounds on STREAM_RING only and if lock screen is not on.
699 if ((streamType != STREAM_REMOTE_MUSIC) &&
700 (flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
701 ((mStreamVolumeAlias[streamType] != AudioSystem.STREAM_RING)
702 || (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
703 flags &= ~AudioManager.FLAG_PLAY_SOUND;
706 if (streamType == STREAM_REMOTE_MUSIC) {
707 // don't play sounds for remote
708 flags &= ~AudioManager.FLAG_PLAY_SOUND;
709 //if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()");
710 adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
712 adjustStreamVolume(streamType, direction, flags);
716 /** @see AudioManager#adjustStreamVolume(int, int, int) */
717 public void adjustStreamVolume(int streamType, int direction, int flags) {
718 if (DEBUG_VOL) Log.d(TAG, "adjustStreamVolume() stream="+streamType+", dir="+direction);
720 ensureValidDirection(direction);
721 ensureValidStreamType(streamType);
723 // use stream type alias here so that streams with same alias have the same behavior,
724 // including with regard to silent mode control (e.g the use of STREAM_RING below and in
725 // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
726 int streamTypeAlias = mStreamVolumeAlias[streamType];
727 VolumeStreamState streamState = mStreamStates[streamTypeAlias];
729 final int device = getDeviceForStream(streamTypeAlias);
730 // get last audible index if stream is muted, current index otherwise
731 final int aliasIndex = streamState.getIndex(device,
732 (streamState.muteCount() != 0) /* lastAudible */);
733 boolean adjustVolume = true;
735 // convert one UI step (+/-1) into a number of internal units on the stream alias
736 int step = rescaleIndex(10, streamType, streamTypeAlias);
738 // If either the client forces allowing ringer modes for this adjustment,
739 // or the stream type is one that is affected by ringer modes
740 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
741 (streamTypeAlias == getMasterStreamType())) {
742 int ringerMode = getRingerMode();
743 // do not vibrate if already in vibrate mode
744 if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
745 flags &= ~AudioManager.FLAG_VIBRATE;
747 // Check if the ringer mode changes with this volume adjustment. If
748 // it does, it will handle adjusting the volume, so we won't below
749 adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);
750 if ((streamTypeAlias == getMasterStreamType()) &&
751 (mRingerMode == AudioManager.RINGER_MODE_SILENT)) {
752 streamState.setLastAudibleIndex(0, device);
756 // If stream is muted, adjust last audible index only
758 final int oldIndex = mStreamStates[streamType].getIndex(device,
759 (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
761 if (streamState.muteCount() != 0) {
763 // Post a persist volume msg
764 // no need to persist volume on all streams sharing the same alias
765 streamState.adjustLastAudibleIndex(direction * step, device);
766 sendMsg(mAudioHandler,
769 PERSIST_LAST_AUDIBLE,
774 index = mStreamStates[streamType].getIndex(device, true /* lastAudible */);
776 if (adjustVolume && streamState.adjustIndex(direction * step, device)) {
777 // Post message to set system volume (it in turn will post a message
778 // to persist). Do not change volume if stream is muted.
779 sendMsg(mAudioHandler,
780 MSG_SET_DEVICE_VOLUME,
787 index = mStreamStates[streamType].getIndex(device, false /* lastAudible */);
790 sendVolumeUpdate(streamType, oldIndex, index, flags);
793 /** @see AudioManager#adjustMasterVolume(int) */
794 public void adjustMasterVolume(int steps, int flags) {
795 ensureValidSteps(steps);
796 int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
798 int numSteps = Math.abs(steps);
799 int direction = steps > 0 ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER;
800 for (int i = 0; i < numSteps; ++i) {
801 delta = findVolumeDelta(direction, volume);
805 //Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps);
806 setMasterVolume(volume, flags);
809 /** @see AudioManager#setStreamVolume(int, int, int) */
810 public void setStreamVolume(int streamType, int index, int flags) {
811 ensureValidStreamType(streamType);
812 VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]];
814 final int device = getDeviceForStream(streamType);
815 // get last audible index if stream is muted, current index otherwise
816 final int oldIndex = streamState.getIndex(device,
817 (streamState.muteCount() != 0) /* lastAudible */);
819 index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]);
821 // setting volume on master stream type also controls silent mode
822 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
823 (mStreamVolumeAlias[streamType] == getMasterStreamType())) {
826 newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
827 : AudioManager.RINGER_MODE_SILENT;
828 setStreamVolumeInt(mStreamVolumeAlias[streamType],
834 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
836 setRingerMode(newRingerMode);
839 setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false, true);
840 // get last audible index if stream is muted, current index otherwise
841 index = mStreamStates[streamType].getIndex(device,
842 (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
844 sendVolumeUpdate(streamType, oldIndex, index, flags);
847 /** @see AudioManager#forceVolumeControlStream(int) */
848 public void forceVolumeControlStream(int streamType, IBinder cb) {
849 synchronized(mForceControlStreamLock) {
850 mVolumeControlStream = streamType;
851 if (mVolumeControlStream == -1) {
852 if (mForceControlStreamClient != null) {
853 mForceControlStreamClient.release();
854 mForceControlStreamClient = null;
857 mForceControlStreamClient = new ForceControlStreamClient(cb);
862 private class ForceControlStreamClient implements IBinder.DeathRecipient {
863 private IBinder mCb; // To be notified of client's death
865 ForceControlStreamClient(IBinder cb) {
868 cb.linkToDeath(this, 0);
869 } catch (RemoteException e) {
871 Log.w(TAG, "ForceControlStreamClient() could not link to "+cb+" binder death");
878 public void binderDied() {
879 synchronized(mForceControlStreamLock) {
880 Log.w(TAG, "SCO client died");
881 if (mForceControlStreamClient != this) {
882 Log.w(TAG, "unregistered control stream client died");
884 mForceControlStreamClient = null;
885 mVolumeControlStream = -1;
890 public void release() {
892 mCb.unlinkToDeath(this, 0);
898 private int findVolumeDelta(int direction, int volume) {
900 if (direction == AudioManager.ADJUST_RAISE) {
901 if (volume == MAX_MASTER_VOLUME) {
904 // This is the default value if we make it to the end
905 delta = mMasterVolumeRamp[1];
906 // If we're raising the volume move down the ramp array until we
907 // find the volume we're above and use that groups delta.
908 for (int i = mMasterVolumeRamp.length - 1; i > 1; i -= 2) {
909 if (volume >= mMasterVolumeRamp[i - 1]) {
910 delta = mMasterVolumeRamp[i];
914 } else if (direction == AudioManager.ADJUST_LOWER){
918 int length = mMasterVolumeRamp.length;
919 // This is the default value if we make it to the end
920 delta = -mMasterVolumeRamp[length - 1];
921 // If we're lowering the volume move up the ramp array until we
922 // find the volume we're below and use the group below it's delta
923 for (int i = 2; i < length; i += 2) {
924 if (volume <= mMasterVolumeRamp[i]) {
925 delta = -mMasterVolumeRamp[i - 1];
933 // UI update and Broadcast Intent
934 private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
935 if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
936 streamType = AudioSystem.STREAM_NOTIFICATION;
939 mVolumePanel.postVolumeChanged(streamType, flags);
941 oldIndex = (oldIndex + 5) / 10;
942 index = (index + 5) / 10;
943 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
944 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
945 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
946 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
947 mContext.sendBroadcast(intent);
950 // UI update and Broadcast Intent
951 private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) {
952 mVolumePanel.postMasterVolumeChanged(flags);
954 Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
955 intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
956 intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
957 mContext.sendBroadcast(intent);
960 // UI update and Broadcast Intent
961 private void sendMasterMuteUpdate(boolean muted, int flags) {
962 mVolumePanel.postMasterMuteChanged(flags);
963 broadcastMasterMuteStatus(muted);
966 private void broadcastMasterMuteStatus(boolean muted) {
967 Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
968 intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
969 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
970 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
971 long origCallerIdentityToken = Binder.clearCallingIdentity();
972 mContext.sendStickyBroadcast(intent);
973 Binder.restoreCallingIdentity(origCallerIdentityToken);
977 * Sets the stream state's index, and posts a message to set system volume.
978 * This will not call out to the UI. Assumes a valid stream type.
980 * @param streamType Type of the stream
981 * @param index Desired volume index of the stream
982 * @param device the device whose volume must be changed
983 * @param force If true, set the volume even if the desired volume is same
984 * as the current volume.
985 * @param lastAudible If true, stores new index as last audible one
987 private void setStreamVolumeInt(int streamType,
991 boolean lastAudible) {
992 VolumeStreamState streamState = mStreamStates[streamType];
994 // If stream is muted, set last audible index only
995 if (streamState.muteCount() != 0) {
996 // Do not allow last audible index to be 0
998 streamState.setLastAudibleIndex(index, device);
999 // Post a persist volume msg
1000 sendMsg(mAudioHandler,
1003 PERSIST_LAST_AUDIBLE,
1009 if (streamState.setIndex(index, device, lastAudible) || force) {
1010 // Post message to set system volume (it in turn will post a message
1012 sendMsg(mAudioHandler,
1013 MSG_SET_DEVICE_VOLUME,
1023 /** @see AudioManager#setStreamSolo(int, boolean) */
1024 public void setStreamSolo(int streamType, boolean state, IBinder cb) {
1025 for (int stream = 0; stream < mStreamStates.length; stream++) {
1026 if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
1027 // Bring back last audible volume
1028 mStreamStates[stream].mute(cb, state);
1032 /** @see AudioManager#setStreamMute(int, boolean) */
1033 public void setStreamMute(int streamType, boolean state, IBinder cb) {
1034 if (isStreamAffectedByMute(streamType)) {
1035 mStreamStates[streamType].mute(cb, state);
1039 /** get stream mute state. */
1040 public boolean isStreamMute(int streamType) {
1041 return (mStreamStates[streamType].muteCount() != 0);
1044 /** @see AudioManager#setMasterMute(boolean, IBinder) */
1045 public void setMasterMute(boolean state, int flags, IBinder cb) {
1046 if (state != AudioSystem.getMasterMute()) {
1047 AudioSystem.setMasterMute(state);
1048 // Post a persist master volume msg
1049 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1
1050 : 0, 0, null, PERSIST_DELAY);
1051 sendMasterMuteUpdate(state, flags);
1055 /** get master mute state. */
1056 public boolean isMasterMute() {
1057 return AudioSystem.getMasterMute();
1060 /** @see AudioManager#getStreamVolume(int) */
1061 public int getStreamVolume(int streamType) {
1062 ensureValidStreamType(streamType);
1063 int device = getDeviceForStream(streamType);
1064 return (mStreamStates[streamType].getIndex(device, false /* lastAudible */) + 5) / 10;
1067 public int getMasterVolume() {
1068 if (isMasterMute()) return 0;
1069 return getLastAudibleMasterVolume();
1072 public void setMasterVolume(int volume, int flags) {
1075 } else if (volume > MAX_MASTER_VOLUME) {
1076 volume = MAX_MASTER_VOLUME;
1078 doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags);
1081 private void doSetMasterVolume(float volume, int flags) {
1082 // don't allow changing master volume when muted
1083 if (!AudioSystem.getMasterMute()) {
1084 int oldVolume = getMasterVolume();
1085 AudioSystem.setMasterVolume(volume);
1087 int newVolume = getMasterVolume();
1088 if (newVolume != oldVolume) {
1089 // Post a persist master volume msg
1090 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE,
1091 Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
1093 // Send the volume update regardless whether there was a change.
1094 sendMasterVolumeUpdate(flags, oldVolume, newVolume);
1098 /** @see AudioManager#getStreamMaxVolume(int) */
1099 public int getStreamMaxVolume(int streamType) {
1100 ensureValidStreamType(streamType);
1101 return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
1104 public int getMasterMaxVolume() {
1105 return MAX_MASTER_VOLUME;
1108 /** Get last audible volume before stream was muted. */
1109 public int getLastAudibleStreamVolume(int streamType) {
1110 ensureValidStreamType(streamType);
1111 int device = getDeviceForStream(streamType);
1112 return (mStreamStates[streamType].getIndex(device, true /* lastAudible */) + 5) / 10;
1115 /** Get last audible master volume before it was muted. */
1116 public int getLastAudibleMasterVolume() {
1117 return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
1120 /** @see AudioManager#getMasterStreamType(int) */
1121 public int getMasterStreamType() {
1122 if (mVoiceCapable) {
1123 return AudioSystem.STREAM_RING;
1125 return AudioSystem.STREAM_MUSIC;
1129 /** @see AudioManager#getRingerMode() */
1130 public int getRingerMode() {
1131 synchronized(mSettingsLock) {
1136 private void ensureValidRingerMode(int ringerMode) {
1137 if (!AudioManager.isValidRingerMode(ringerMode)) {
1138 throw new IllegalArgumentException("Bad ringer mode " + ringerMode);
1142 /** @see AudioManager#setRingerMode(int) */
1143 public void setRingerMode(int ringerMode) {
1144 if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) {
1145 ringerMode = AudioManager.RINGER_MODE_SILENT;
1147 if (ringerMode != getRingerMode()) {
1148 setRingerModeInt(ringerMode, true);
1149 // Send sticky broadcast
1150 broadcastRingerMode(ringerMode);
1154 private void setRingerModeInt(int ringerMode, boolean persist) {
1155 synchronized(mSettingsLock) {
1156 mRingerMode = ringerMode;
1159 // Mute stream if not previously muted by ringer mode and ringer mode
1160 // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
1161 // Unmute stream if previously muted by ringer mode and ringer mode
1162 // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
1163 int numStreamTypes = AudioSystem.getNumStreamTypes();
1164 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1165 if (isStreamMutedByRingerMode(streamType)) {
1166 if (!isStreamAffectedByRingerMode(streamType) ||
1167 ringerMode == AudioManager.RINGER_MODE_NORMAL) {
1168 // ring and notifications volume should never be 0 when not silenced
1169 // on voice capable devices
1170 if (mVoiceCapable &&
1171 mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) {
1172 synchronized (mStreamStates[streamType]) {
1173 Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet();
1174 Iterator i = set.iterator();
1175 while (i.hasNext()) {
1176 Map.Entry entry = (Map.Entry)i.next();
1177 if ((Integer)entry.getValue() == 0) {
1183 mStreamStates[streamType].mute(null, false);
1184 mRingerModeMutedStreams &= ~(1 << streamType);
1187 if (isStreamAffectedByRingerMode(streamType) &&
1188 ringerMode != AudioManager.RINGER_MODE_NORMAL) {
1189 mStreamStates[streamType].mute(null, true);
1190 mRingerModeMutedStreams |= (1 << streamType);
1195 // Post a persist ringer mode msg
1197 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE,
1198 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
1202 private void restoreMasterVolume() {
1203 if (mUseMasterVolume) {
1204 float volume = Settings.System.getFloat(mContentResolver,
1205 Settings.System.VOLUME_MASTER, -1.0f);
1206 if (volume >= 0.0f) {
1207 AudioSystem.setMasterVolume(volume);
1212 /** @see AudioManager#shouldVibrate(int) */
1213 public boolean shouldVibrate(int vibrateType) {
1214 if (!mHasVibrator) return false;
1216 switch (getVibrateSetting(vibrateType)) {
1218 case AudioManager.VIBRATE_SETTING_ON:
1219 return getRingerMode() != AudioManager.RINGER_MODE_SILENT;
1221 case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
1222 return getRingerMode() == AudioManager.RINGER_MODE_VIBRATE;
1224 case AudioManager.VIBRATE_SETTING_OFF:
1225 // return false, even for incoming calls
1233 /** @see AudioManager#getVibrateSetting(int) */
1234 public int getVibrateSetting(int vibrateType) {
1235 if (!mHasVibrator) return AudioManager.VIBRATE_SETTING_OFF;
1236 return (mVibrateSetting >> (vibrateType * 2)) & 3;
1239 /** @see AudioManager#setVibrateSetting(int, int) */
1240 public void setVibrateSetting(int vibrateType, int vibrateSetting) {
1242 if (!mHasVibrator) return;
1244 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
1247 broadcastVibrateSetting(vibrateType);
1252 * @see #setVibrateSetting(int, int)
1254 public static int getValueForVibrateSetting(int existingValue, int vibrateType,
1255 int vibrateSetting) {
1257 // First clear the existing setting. Each vibrate type has two bits in
1258 // the value. Note '3' is '11' in binary.
1259 existingValue &= ~(3 << (vibrateType * 2));
1261 // Set into the old value
1262 existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
1264 return existingValue;
1267 private class SetModeDeathHandler implements IBinder.DeathRecipient {
1268 private IBinder mCb; // To be notified of client's death
1270 private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
1272 SetModeDeathHandler(IBinder cb, int pid) {
1277 public void binderDied() {
1278 int newModeOwnerPid = 0;
1279 synchronized(mSetModeDeathHandlers) {
1280 Log.w(TAG, "setMode() client died");
1281 int index = mSetModeDeathHandlers.indexOf(this);
1283 Log.w(TAG, "unregistered setMode() client died");
1285 newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid);
1288 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
1289 // SCO connections not started by the application changing the mode
1290 if (newModeOwnerPid != 0) {
1291 disconnectBluetoothSco(newModeOwnerPid);
1295 public int getPid() {
1299 public void setMode(int mode) {
1303 public int getMode() {
1307 public IBinder getBinder() {
1312 /** @see AudioManager#setMode(int) */
1313 public void setMode(int mode, IBinder cb) {
1314 if (!checkAudioSettingsPermission("setMode()")) {
1318 if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
1322 int newModeOwnerPid = 0;
1323 synchronized(mSetModeDeathHandlers) {
1324 if (mode == AudioSystem.MODE_CURRENT) {
1327 newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid());
1329 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
1330 // SCO connections not started by the application changing the mode
1331 if (newModeOwnerPid != 0) {
1332 disconnectBluetoothSco(newModeOwnerPid);
1336 // must be called synchronized on mSetModeDeathHandlers
1337 // setModeInt() returns a valid PID if the audio mode was successfully set to
1338 // any mode other than NORMAL.
1339 int setModeInt(int mode, IBinder cb, int pid) {
1340 int newModeOwnerPid = 0;
1342 Log.e(TAG, "setModeInt() called with null binder");
1343 return newModeOwnerPid;
1346 SetModeDeathHandler hdlr = null;
1347 Iterator iter = mSetModeDeathHandlers.iterator();
1348 while (iter.hasNext()) {
1349 SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
1350 if (h.getPid() == pid) {
1352 // Remove from client list so that it is re-inserted at top of list
1354 hdlr.getBinder().unlinkToDeath(hdlr, 0);
1358 int status = AudioSystem.AUDIO_STATUS_OK;
1360 if (mode == AudioSystem.MODE_NORMAL) {
1361 // get new mode from client at top the list if any
1362 if (!mSetModeDeathHandlers.isEmpty()) {
1363 hdlr = mSetModeDeathHandlers.get(0);
1364 cb = hdlr.getBinder();
1365 mode = hdlr.getMode();
1369 hdlr = new SetModeDeathHandler(cb, pid);
1371 // Register for client death notification
1373 cb.linkToDeath(hdlr, 0);
1374 } catch (RemoteException e) {
1376 Log.w(TAG, "setMode() could not link to "+cb+" binder death");
1379 // Last client to call setMode() is always at top of client list
1380 // as required by SetModeDeathHandler.binderDied()
1381 mSetModeDeathHandlers.add(0, hdlr);
1385 if (mode != mMode) {
1386 status = AudioSystem.setPhoneState(mode);
1387 if (status == AudioSystem.AUDIO_STATUS_OK) {
1391 mSetModeDeathHandlers.remove(hdlr);
1392 cb.unlinkToDeath(hdlr, 0);
1394 // force reading new top of mSetModeDeathHandlers stack
1395 mode = AudioSystem.MODE_NORMAL;
1398 status = AudioSystem.AUDIO_STATUS_OK;
1400 } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
1402 if (status == AudioSystem.AUDIO_STATUS_OK) {
1403 if (mode != AudioSystem.MODE_NORMAL) {
1404 if (mSetModeDeathHandlers.isEmpty()) {
1405 Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
1407 newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
1410 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
1411 if (streamType == STREAM_REMOTE_MUSIC) {
1412 // here handle remote media playback the same way as local playback
1413 streamType = AudioManager.STREAM_MUSIC;
1415 int device = getDeviceForStream(streamType);
1416 int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device, false);
1417 setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, false);
1419 updateStreamVolumeAlias(true /*updateVolumes*/);
1421 return newModeOwnerPid;
1424 /** @see AudioManager#getMode() */
1425 public int getMode() {
1429 /** @see AudioManager#playSoundEffect(int) */
1430 public void playSoundEffect(int effectType) {
1431 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
1432 effectType, -1, null, 0);
1435 /** @see AudioManager#playSoundEffect(int, float) */
1436 public void playSoundEffectVolume(int effectType, float volume) {
1438 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP,
1439 effectType, (int) (volume * 1000), null, 0);
1443 * Loads samples into the soundpool.
1444 * This method must be called at first when sound effects are enabled
1446 public boolean loadSoundEffects() {
1449 synchronized (mSoundEffectsLock) {
1450 if (!mBootCompleted) {
1451 Log.w(TAG, "loadSoundEffects() called before boot complete");
1455 if (mSoundPool != null) {
1458 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
1461 mSoundPoolCallBack = null;
1462 mSoundPoolListenerThread = new SoundPoolListenerThread();
1463 mSoundPoolListenerThread.start();
1464 // Wait for mSoundPoolCallBack to be set by the other thread
1465 mSoundEffectsLock.wait();
1466 } catch (InterruptedException e) {
1467 Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
1470 if (mSoundPoolCallBack == null) {
1471 Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
1472 if (mSoundPoolLooper != null) {
1473 mSoundPoolLooper.quit();
1474 mSoundPoolLooper = null;
1476 mSoundPoolListenerThread = null;
1477 mSoundPool.release();
1482 * poolId table: The value -1 in this table indicates that corresponding
1483 * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
1484 * Once loaded, the value in poolId is the sample ID and the same
1485 * sample can be reused for another effect using the same file.
1487 int[] poolId = new int[SOUND_EFFECT_FILES.length];
1488 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1489 poolId[fileIdx] = -1;
1492 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
1493 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
1494 * this indicates we have a valid sample loaded for this effect.
1498 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1499 // Do not load sample if this effect uses the MediaPlayer
1500 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
1503 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
1504 String filePath = Environment.getRootDirectory()
1505 + SOUND_EFFECTS_PATH
1506 + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
1507 int sampleId = mSoundPool.load(filePath, 0);
1508 if (sampleId <= 0) {
1509 Log.w(TAG, "Soundpool could not load file: "+filePath);
1511 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
1512 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
1513 lastSample = sampleId;
1516 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
1519 // wait for all samples to be loaded
1520 if (lastSample != 0) {
1521 mSoundPoolCallBack.setLastSample(lastSample);
1524 mSoundEffectsLock.wait();
1525 status = mSoundPoolCallBack.status();
1526 } catch (java.lang.InterruptedException e) {
1527 Log.w(TAG, "Interrupted while waiting sound pool callback.");
1534 if (mSoundPoolLooper != null) {
1535 mSoundPoolLooper.quit();
1536 mSoundPoolLooper = null;
1538 mSoundPoolListenerThread = null;
1541 "loadSoundEffects(), Error "
1542 + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
1543 + " while loading samples");
1544 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1545 if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
1546 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1550 mSoundPool.release();
1554 return (status == 0);
1558 * Unloads samples from the sound pool.
1559 * This method can be called to free some memory when
1560 * sound effects are disabled.
1562 public void unloadSoundEffects() {
1563 synchronized (mSoundEffectsLock) {
1564 if (mSoundPool == null) {
1568 mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
1569 mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
1571 int[] poolId = new int[SOUND_EFFECT_FILES.length];
1572 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1573 poolId[fileIdx] = 0;
1576 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1577 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
1580 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
1581 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
1582 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1583 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
1586 mSoundPool.release();
1591 class SoundPoolListenerThread extends Thread {
1592 public SoundPoolListenerThread() {
1593 super("SoundPoolListenerThread");
1600 mSoundPoolLooper = Looper.myLooper();
1602 synchronized (mSoundEffectsLock) {
1603 if (mSoundPool != null) {
1604 mSoundPoolCallBack = new SoundPoolCallback();
1605 mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
1607 mSoundEffectsLock.notify();
1613 private final class SoundPoolCallback implements
1614 android.media.SoundPool.OnLoadCompleteListener {
1619 public int status() {
1623 public void setLastSample(int sample) {
1624 mLastSample = sample;
1627 public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
1628 synchronized (mSoundEffectsLock) {
1632 if (sampleId == mLastSample) {
1633 mSoundEffectsLock.notify();
1639 /** @see AudioManager#reloadAudioSettings() */
1640 public void reloadAudioSettings() {
1641 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
1642 readPersistedSettings();
1644 // restore volume settings
1645 int numStreamTypes = AudioSystem.getNumStreamTypes();
1646 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
1647 VolumeStreamState streamState = mStreamStates[streamType];
1649 synchronized (streamState) {
1650 streamState.readSettings();
1652 // unmute stream that was muted but is not affect by mute anymore
1653 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
1654 int size = streamState.mDeathHandlers.size();
1655 for (int i = 0; i < size; i++) {
1656 streamState.mDeathHandlers.get(i).mMuteCount = 1;
1657 streamState.mDeathHandlers.get(i).mute(false);
1663 checkAllAliasStreamVolumes();
1665 // apply new ringer mode
1666 setRingerModeInt(getRingerMode(), false);
1669 /** @see AudioManager#setSpeakerphoneOn() */
1670 public void setSpeakerphoneOn(boolean on){
1671 if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
1674 mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
1676 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
1677 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
1680 /** @see AudioManager#isSpeakerphoneOn() */
1681 public boolean isSpeakerphoneOn() {
1682 return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
1685 /** @see AudioManager#setBluetoothScoOn() */
1686 public void setBluetoothScoOn(boolean on){
1687 if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
1690 mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
1692 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
1693 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
1694 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
1695 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
1698 /** @see AudioManager#isBluetoothScoOn() */
1699 public boolean isBluetoothScoOn() {
1700 return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
1703 /** @see AudioManager#setBluetoothA2dpOn() */
1704 public void setBluetoothA2dpOn(boolean on) {
1705 synchronized (mBluetoothA2dpEnabledLock) {
1706 mBluetoothA2dpEnabled = on;
1707 sendMsg(mAudioHandler, MSG_SET_FORCE_BT_A2DP_USE, SENDMSG_QUEUE,
1708 AudioSystem.FOR_MEDIA,
1709 mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
1714 /** @see AudioManager#isBluetoothA2dpOn() */
1715 public boolean isBluetoothA2dpOn() {
1716 synchronized (mBluetoothA2dpEnabledLock) {
1717 return mBluetoothA2dpEnabled;
1721 /** @see AudioManager#startBluetoothSco() */
1722 public void startBluetoothSco(IBinder cb){
1723 if (!checkAudioSettingsPermission("startBluetoothSco()") ||
1727 ScoClient client = getScoClient(cb, true);
1731 /** @see AudioManager#stopBluetoothSco() */
1732 public void stopBluetoothSco(IBinder cb){
1733 if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
1737 ScoClient client = getScoClient(cb, false);
1738 if (client != null) {
1744 private class ScoClient implements IBinder.DeathRecipient {
1745 private IBinder mCb; // To be notified of client's death
1746 private int mCreatorPid;
1747 private int mStartcount; // number of SCO connections started by this client
1749 ScoClient(IBinder cb) {
1751 mCreatorPid = Binder.getCallingPid();
1755 public void binderDied() {
1756 synchronized(mScoClients) {
1757 Log.w(TAG, "SCO client died");
1758 int index = mScoClients.indexOf(this);
1760 Log.w(TAG, "unregistered SCO client died");
1763 mScoClients.remove(this);
1768 public void incCount() {
1769 synchronized(mScoClients) {
1770 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
1771 if (mStartcount == 0) {
1773 mCb.linkToDeath(this, 0);
1774 } catch (RemoteException e) {
1775 // client has already died!
1776 Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death");
1783 public void decCount() {
1784 synchronized(mScoClients) {
1785 if (mStartcount == 0) {
1786 Log.w(TAG, "ScoClient.decCount() already 0");
1789 if (mStartcount == 0) {
1791 mCb.unlinkToDeath(this, 0);
1792 } catch (NoSuchElementException e) {
1793 Log.w(TAG, "decCount() going to 0 but not registered to binder");
1796 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1801 public void clearCount(boolean stopSco) {
1802 synchronized(mScoClients) {
1803 if (mStartcount != 0) {
1805 mCb.unlinkToDeath(this, 0);
1806 } catch (NoSuchElementException e) {
1807 Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
1812 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1817 public int getCount() {
1821 public IBinder getBinder() {
1825 public int getPid() {
1829 public int totalCount() {
1830 synchronized(mScoClients) {
1832 int size = mScoClients.size();
1833 for (int i = 0; i < size; i++) {
1834 count += mScoClients.get(i).getCount();
1840 private void requestScoState(int state) {
1841 checkScoAudioState();
1842 if (totalCount() == 0) {
1843 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
1844 // Make sure that the state transitions to CONNECTING even if we cannot initiate
1846 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
1847 // Accept SCO audio activation only in NORMAL audio mode or if the mode is
1848 // currently controlled by the same client process.
1849 synchronized(mSetModeDeathHandlers) {
1850 if ((mSetModeDeathHandlers.isEmpty() ||
1851 mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
1852 (mScoAudioState == SCO_STATE_INACTIVE ||
1853 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
1854 if (mScoAudioState == SCO_STATE_INACTIVE) {
1855 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1856 if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1857 mBluetoothHeadsetDevice)) {
1858 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1860 broadcastScoConnectionState(
1861 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1863 } else if (getBluetoothHeadset()) {
1864 mScoAudioState = SCO_STATE_ACTIVATE_REQ;
1867 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1868 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
1871 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1874 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
1875 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
1876 mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
1877 if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
1878 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1879 if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
1880 mBluetoothHeadsetDevice)) {
1881 mScoAudioState = SCO_STATE_INACTIVE;
1882 broadcastScoConnectionState(
1883 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1885 } else if (getBluetoothHeadset()) {
1886 mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
1889 mScoAudioState = SCO_STATE_INACTIVE;
1890 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1897 private void checkScoAudioState() {
1898 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
1899 mScoAudioState == SCO_STATE_INACTIVE &&
1900 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
1901 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1902 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
1906 private ScoClient getScoClient(IBinder cb, boolean create) {
1907 synchronized(mScoClients) {
1908 ScoClient client = null;
1909 int size = mScoClients.size();
1910 for (int i = 0; i < size; i++) {
1911 client = mScoClients.get(i);
1912 if (client.getBinder() == cb)
1916 client = new ScoClient(cb);
1917 mScoClients.add(client);
1923 public void clearAllScoClients(int exceptPid, boolean stopSco) {
1924 synchronized(mScoClients) {
1925 ScoClient savedClient = null;
1926 int size = mScoClients.size();
1927 for (int i = 0; i < size; i++) {
1928 ScoClient cl = mScoClients.get(i);
1929 if (cl.getPid() != exceptPid) {
1930 cl.clearCount(stopSco);
1935 mScoClients.clear();
1936 if (savedClient != null) {
1937 mScoClients.add(savedClient);
1942 private boolean getBluetoothHeadset() {
1943 boolean result = false;
1944 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1945 if (adapter != null) {
1946 result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
1947 BluetoothProfile.HEADSET);
1949 // If we could not get a bluetooth headset proxy, send a failure message
1950 // without delay to reset the SCO audio state and clear SCO clients.
1951 // If we could get a proxy, send a delayed failure message that will reset our state
1952 // in case we don't receive onServiceConnected().
1953 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
1954 SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
1958 private void disconnectBluetoothSco(int exceptPid) {
1959 synchronized(mScoClients) {
1960 checkScoAudioState();
1961 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
1962 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
1963 if (mBluetoothHeadsetDevice != null) {
1964 if (mBluetoothHeadset != null) {
1965 if (!mBluetoothHeadset.stopVoiceRecognition(
1966 mBluetoothHeadsetDevice)) {
1967 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
1968 SENDMSG_REPLACE, 0, 0, null, 0);
1970 } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
1971 getBluetoothHeadset()) {
1972 mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
1976 clearAllScoClients(exceptPid, true);
1981 private void resetBluetoothSco() {
1982 synchronized(mScoClients) {
1983 clearAllScoClients(0, false);
1984 mScoAudioState = SCO_STATE_INACTIVE;
1985 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1989 private void broadcastScoConnectionState(int state) {
1990 if (state != mScoConnectionState) {
1991 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
1992 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
1993 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
1994 mScoConnectionState);
1995 mContext.sendStickyBroadcast(newIntent);
1996 mScoConnectionState = state;
2000 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
2001 new BluetoothProfile.ServiceListener() {
2002 public void onServiceConnected(int profile, BluetoothProfile proxy) {
2003 BluetoothDevice btDevice;
2004 List<BluetoothDevice> deviceList;
2006 case BluetoothProfile.A2DP:
2007 BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
2008 deviceList = a2dp.getConnectedDevices();
2009 if (deviceList.size() > 0) {
2010 btDevice = deviceList.get(0);
2011 synchronized (mConnectedDevices) {
2012 int state = a2dp.getConnectionState(btDevice);
2013 int delay = checkSendBecomingNoisyIntent(
2014 AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2015 (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
2016 queueMsgUnderWakeLock(mAudioHandler,
2017 MSG_SET_A2DP_CONNECTION_STATE,
2026 case BluetoothProfile.HEADSET:
2027 synchronized (mScoClients) {
2028 // Discard timeout message
2029 mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
2030 mBluetoothHeadset = (BluetoothHeadset) proxy;
2031 deviceList = mBluetoothHeadset.getConnectedDevices();
2032 if (deviceList.size() > 0) {
2033 mBluetoothHeadsetDevice = deviceList.get(0);
2035 mBluetoothHeadsetDevice = null;
2037 // Refresh SCO audio state
2038 checkScoAudioState();
2039 // Continue pending action if any
2040 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
2041 mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
2042 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
2043 boolean status = false;
2044 if (mBluetoothHeadsetDevice != null) {
2045 switch (mScoAudioState) {
2046 case SCO_STATE_ACTIVATE_REQ:
2047 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
2048 status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
2049 mBluetoothHeadsetDevice);
2051 case SCO_STATE_DEACTIVATE_REQ:
2052 status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
2053 mBluetoothHeadsetDevice);
2055 case SCO_STATE_DEACTIVATE_EXT_REQ:
2056 status = mBluetoothHeadset.stopVoiceRecognition(
2057 mBluetoothHeadsetDevice);
2061 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
2062 SENDMSG_REPLACE, 0, 0, null, 0);
2072 public void onServiceDisconnected(int profile) {
2074 case BluetoothProfile.A2DP:
2075 synchronized (mConnectedDevices) {
2076 if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
2077 makeA2dpDeviceUnavailableNow(
2078 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
2083 case BluetoothProfile.HEADSET:
2084 synchronized (mScoClients) {
2085 mBluetoothHeadset = null;
2095 ///////////////////////////////////////////////////////////////////////////
2097 ///////////////////////////////////////////////////////////////////////////
2100 * Checks if the adjustment should change ringer mode instead of just
2101 * adjusting volume. If so, this will set the proper ringer mode and volume
2102 * indices on the stream states.
2104 private boolean checkForRingerModeChange(int oldIndex, int direction, int step) {
2105 boolean adjustVolumeIndex = true;
2106 int ringerMode = getRingerMode();
2108 switch (ringerMode) {
2109 case RINGER_MODE_NORMAL:
2110 if (direction == AudioManager.ADJUST_LOWER) {
2112 // "step" is the delta in internal index units corresponding to a
2113 // change of 1 in UI index units.
2114 // Because of rounding when rescaling from one stream index range to its alias
2115 // index range, we cannot simply test oldIndex == step:
2116 // (step <= oldIndex < 2 * step) is equivalent to: (old UI index == 1)
2117 if (step <= oldIndex && oldIndex < 2 * step) {
2118 ringerMode = RINGER_MODE_VIBRATE;
2121 // (oldIndex < step) is equivalent to (old UI index == 0)
2122 if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
2123 ringerMode = RINGER_MODE_SILENT;
2128 case RINGER_MODE_VIBRATE:
2129 if (!mHasVibrator) {
2130 Log.e(TAG, "checkForRingerModeChange() current ringer mode is vibrate" +
2131 "but no vibrator is present");
2134 if ((direction == AudioManager.ADJUST_LOWER)) {
2135 if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
2136 ringerMode = RINGER_MODE_SILENT;
2138 } else if (direction == AudioManager.ADJUST_RAISE) {
2139 ringerMode = RINGER_MODE_NORMAL;
2141 adjustVolumeIndex = false;
2143 case RINGER_MODE_SILENT:
2144 if (direction == AudioManager.ADJUST_RAISE) {
2146 ringerMode = RINGER_MODE_VIBRATE;
2148 ringerMode = RINGER_MODE_NORMAL;
2151 adjustVolumeIndex = false;
2154 Log.e(TAG, "checkForRingerModeChange() wrong ringer mode: "+ringerMode);
2158 setRingerMode(ringerMode);
2160 mPrevVolDirection = direction;
2162 return adjustVolumeIndex;
2165 public boolean isStreamAffectedByRingerMode(int streamType) {
2166 return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
2169 private boolean isStreamMutedByRingerMode(int streamType) {
2170 return (mRingerModeMutedStreams & (1 << streamType)) != 0;
2173 public boolean isStreamAffectedByMute(int streamType) {
2174 return (mMuteAffectedStreams & (1 << streamType)) != 0;
2177 private void ensureValidDirection(int direction) {
2178 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
2179 throw new IllegalArgumentException("Bad direction " + direction);
2183 private void ensureValidSteps(int steps) {
2184 if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) {
2185 throw new IllegalArgumentException("Bad volume adjust steps " + steps);
2189 private void ensureValidStreamType(int streamType) {
2190 if (streamType < 0 || streamType >= mStreamStates.length) {
2191 throw new IllegalArgumentException("Bad stream type " + streamType);
2195 private boolean isInCommunication() {
2196 boolean isOffhook = false;
2198 if (mVoiceCapable) {
2200 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
2201 if (phone != null) isOffhook = phone.isOffhook();
2202 } catch (RemoteException e) {
2203 Log.w(TAG, "Couldn't connect to phone service", e);
2206 return (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION);
2209 private int getActiveStreamType(int suggestedStreamType) {
2210 if (mVoiceCapable) {
2211 if (isInCommunication()) {
2212 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
2213 == AudioSystem.FORCE_BT_SCO) {
2214 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
2215 return AudioSystem.STREAM_BLUETOOTH_SCO;
2217 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
2218 return AudioSystem.STREAM_VOICE_CALL;
2220 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
2221 // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
2222 // volume can have priority over STREAM_MUSIC
2223 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
2225 Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
2226 return STREAM_REMOTE_MUSIC;
2227 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
2229 Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
2230 return AudioSystem.STREAM_MUSIC;
2233 Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
2234 return AudioSystem.STREAM_RING;
2236 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
2238 Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
2239 return AudioSystem.STREAM_MUSIC;
2241 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
2242 + suggestedStreamType);
2243 return suggestedStreamType;
2246 if (isInCommunication()) {
2247 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
2248 == AudioSystem.FORCE_BT_SCO) {
2249 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
2250 return AudioSystem.STREAM_BLUETOOTH_SCO;
2252 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
2253 return AudioSystem.STREAM_VOICE_CALL;
2255 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
2256 NOTIFICATION_VOLUME_DELAY_MS) ||
2257 AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
2258 NOTIFICATION_VOLUME_DELAY_MS)) {
2259 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
2260 return AudioSystem.STREAM_NOTIFICATION;
2261 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
2262 if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
2263 // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
2264 // volume can have priority over STREAM_MUSIC
2265 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
2266 return STREAM_REMOTE_MUSIC;
2269 Log.v(TAG, "getActiveStreamType: using STREAM_MUSIC as default");
2270 return AudioSystem.STREAM_MUSIC;
2273 if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
2274 + suggestedStreamType);
2275 return suggestedStreamType;
2280 private void broadcastRingerMode(int ringerMode) {
2281 // Send sticky broadcast
2282 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
2283 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
2284 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
2285 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
2286 long origCallerIdentityToken = Binder.clearCallingIdentity();
2287 mContext.sendStickyBroadcast(broadcast);
2288 Binder.restoreCallingIdentity(origCallerIdentityToken);
2291 private void broadcastVibrateSetting(int vibrateType) {
2293 if (ActivityManagerNative.isSystemReady()) {
2294 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
2295 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
2296 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
2297 mContext.sendBroadcast(broadcast);
2301 // Message helper methods
2303 * Queue a message on the given handler's message queue, after acquiring the service wake lock.
2304 * Note that the wake lock needs to be released after the message has been handled.
2306 private void queueMsgUnderWakeLock(Handler handler, int msg,
2307 int arg1, int arg2, Object obj, int delay) {
2308 mMediaEventWakeLock.acquire();
2309 sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
2312 private static void sendMsg(Handler handler, int msg,
2313 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
2315 if (existingMsgPolicy == SENDMSG_REPLACE) {
2316 handler.removeMessages(msg);
2317 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
2321 handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
2324 boolean checkAudioSettingsPermission(String method) {
2325 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
2326 == PackageManager.PERMISSION_GRANTED) {
2329 String msg = "Audio Settings Permission Denial: " + method + " from pid="
2330 + Binder.getCallingPid()
2331 + ", uid=" + Binder.getCallingUid();
2336 private int getDeviceForStream(int stream) {
2337 int device = AudioSystem.getDevicesForStream(stream);
2338 if ((device & (device - 1)) != 0) {
2339 // Multiple device selection is either:
2340 // - speaker + one other device: give priority to speaker in this case.
2341 // - one A2DP device + another device: happens with duplicated output. In this case
2342 // retain the device on the A2DP output as the other must not correspond to an active
2343 // selection if not the speaker.
2344 if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
2345 device = AudioSystem.DEVICE_OUT_SPEAKER;
2347 device &= AudioSystem.DEVICE_OUT_ALL_A2DP;
2353 public void setWiredDeviceConnectionState(int device, int state, String name) {
2354 synchronized (mConnectedDevices) {
2355 int delay = checkSendBecomingNoisyIntent(device, state);
2356 queueMsgUnderWakeLock(mAudioHandler,
2357 MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
2365 public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state)
2368 synchronized (mConnectedDevices) {
2369 delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2370 (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
2371 queueMsgUnderWakeLock(mAudioHandler,
2372 MSG_SET_A2DP_CONNECTION_STATE,
2381 ///////////////////////////////////////////////////////////////////////////
2383 ///////////////////////////////////////////////////////////////////////////
2385 public class VolumeStreamState {
2386 private final int mStreamType;
2388 private String mVolumeIndexSettingName;
2389 private String mLastAudibleVolumeIndexSettingName;
2390 private int mIndexMax;
2391 private final ConcurrentHashMap<Integer, Integer> mIndex =
2392 new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
2393 private final ConcurrentHashMap<Integer, Integer> mLastAudibleIndex =
2394 new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
2395 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
2397 private VolumeStreamState(String settingName, int streamType) {
2399 mVolumeIndexSettingName = settingName;
2400 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
2402 mStreamType = streamType;
2403 mIndexMax = MAX_STREAM_VOLUME[streamType];
2404 AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
2409 mDeathHandlers = new ArrayList<VolumeDeathHandler>();
2412 public String getSettingNameForDevice(boolean lastAudible, int device) {
2413 String name = lastAudible ?
2414 mLastAudibleVolumeIndexSettingName :
2415 mVolumeIndexSettingName;
2416 String suffix = AudioSystem.getDeviceName(device);
2417 if (suffix.isEmpty()) {
2420 return name + "_" + suffix;
2423 public synchronized void readSettings() {
2424 int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
2426 for (int i = 0; remainingDevices != 0; i++) {
2427 int device = (1 << i);
2428 if ((device & remainingDevices) == 0) {
2431 remainingDevices &= ~device;
2433 // retrieve current volume for device
2434 String name = getSettingNameForDevice(false /* lastAudible */, device);
2435 // if no volume stored for current stream and device, use default volume if default
2436 // device, continue otherwise
2437 int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
2438 AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
2439 int index = Settings.System.getInt(mContentResolver, name, defaultIndex);
2444 // retrieve last audible volume for device
2445 name = getSettingNameForDevice(true /* lastAudible */, device);
2446 // use stored last audible index if present, otherwise use current index if not 0
2448 defaultIndex = (index > 0) ?
2449 index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
2450 int lastAudibleIndex = Settings.System.getInt(mContentResolver, name, defaultIndex);
2452 // a last audible index of 0 should never be stored for ring and notification
2453 // streams on phones (voice capable devices).
2454 // same for system stream on phones and tablets
2455 if ((lastAudibleIndex == 0) &&
2457 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
2458 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
2459 lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
2460 // Correct the data base
2461 sendMsg(mAudioHandler,
2464 PERSIST_LAST_AUDIBLE,
2469 mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex));
2470 // the initial index should never be 0 for ring and notification streams on phones
2471 // (voice capable devices) if not in silent or vibrate mode.
2472 // same for system stream on phones and tablets
2473 if ((index == 0) && (mRingerMode == AudioManager.RINGER_MODE_NORMAL) &&
2475 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
2476 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
2477 index = lastAudibleIndex;
2478 // Correct the data base
2479 sendMsg(mAudioHandler,
2487 mIndex.put(device, getValidIndex(10 * index));
2491 public void applyDeviceVolume(int device) {
2492 AudioSystem.setStreamVolumeIndex(mStreamType,
2493 (getIndex(device, false /* lastAudible */) + 5)/10,
2497 public synchronized void applyAllVolumes() {
2498 // apply default volume first: by convention this will reset all
2499 // devices volumes in audio policy manager to the supplied value
2500 AudioSystem.setStreamVolumeIndex(mStreamType,
2501 (getIndex(AudioSystem.DEVICE_OUT_DEFAULT, false /* lastAudible */) + 5)/10,
2502 AudioSystem.DEVICE_OUT_DEFAULT);
2503 // then apply device specific volumes
2504 Set set = mIndex.entrySet();
2505 Iterator i = set.iterator();
2506 while (i.hasNext()) {
2507 Map.Entry entry = (Map.Entry)i.next();
2508 int device = ((Integer)entry.getKey()).intValue();
2509 if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
2510 AudioSystem.setStreamVolumeIndex(mStreamType,
2511 ((Integer)entry.getValue() + 5)/10,
2517 public boolean adjustIndex(int deltaIndex, int device) {
2518 return setIndex(getIndex(device,
2519 false /* lastAudible */) + deltaIndex,
2521 true /* lastAudible */);
2524 public synchronized boolean setIndex(int index, int device, boolean lastAudible) {
2525 int oldIndex = getIndex(device, false /* lastAudible */);
2526 index = getValidIndex(index);
2527 mIndex.put(device, getValidIndex(index));
2529 if (oldIndex != index) {
2531 mLastAudibleIndex.put(device, index);
2533 // Apply change to all streams using this one as alias
2534 // if changing volume of current device, also change volume of current
2535 // device on aliased stream
2536 boolean currentDevice = (device == getDeviceForStream(mStreamType));
2537 int numStreamTypes = AudioSystem.getNumStreamTypes();
2538 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2539 if (streamType != mStreamType &&
2540 mStreamVolumeAlias[streamType] == mStreamType) {
2541 int scaledIndex = rescaleIndex(index, mStreamType, streamType);
2542 mStreamStates[streamType].setIndex(scaledIndex,
2545 if (currentDevice) {
2546 mStreamStates[streamType].setIndex(scaledIndex,
2547 getDeviceForStream(streamType),
2558 public synchronized int getIndex(int device, boolean lastAudible) {
2559 ConcurrentHashMap <Integer, Integer> indexes;
2561 indexes = mLastAudibleIndex;
2565 Integer index = indexes.get(device);
2566 if (index == null) {
2567 // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
2568 index = indexes.get(AudioSystem.DEVICE_OUT_DEFAULT);
2570 return index.intValue();
2573 public synchronized void setLastAudibleIndex(int index, int device) {
2574 // Apply change to all streams using this one as alias
2575 // if changing volume of current device, also change volume of current
2576 // device on aliased stream
2577 boolean currentDevice = (device == getDeviceForStream(mStreamType));
2578 int numStreamTypes = AudioSystem.getNumStreamTypes();
2579 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2580 if (streamType != mStreamType &&
2581 mStreamVolumeAlias[streamType] == mStreamType) {
2582 int scaledIndex = rescaleIndex(index, mStreamType, streamType);
2583 mStreamStates[streamType].setLastAudibleIndex(scaledIndex, device);
2584 if (currentDevice) {
2585 mStreamStates[streamType].setLastAudibleIndex(scaledIndex,
2586 getDeviceForStream(streamType));
2590 mLastAudibleIndex.put(device, getValidIndex(index));
2593 public synchronized void adjustLastAudibleIndex(int deltaIndex, int device) {
2594 setLastAudibleIndex(getIndex(device,
2595 true /* lastAudible */) + deltaIndex,
2599 public int getMaxIndex() {
2603 // only called by setAllIndexes() which is already synchronized
2604 public ConcurrentHashMap <Integer, Integer> getAllIndexes(boolean lastAudible) {
2606 return mLastAudibleIndex;
2612 public synchronized void setAllIndexes(VolumeStreamState srcStream, boolean lastAudible) {
2613 ConcurrentHashMap <Integer, Integer> indexes = srcStream.getAllIndexes(lastAudible);
2614 Set set = indexes.entrySet();
2615 Iterator i = set.iterator();
2616 while (i.hasNext()) {
2617 Map.Entry entry = (Map.Entry)i.next();
2618 int device = ((Integer)entry.getKey()).intValue();
2619 int index = ((Integer)entry.getValue()).intValue();
2620 index = rescaleIndex(index, srcStream.getStreamType(), mStreamType);
2621 setIndex(index, device, lastAudible);
2625 public synchronized void mute(IBinder cb, boolean state) {
2626 VolumeDeathHandler handler = getDeathHandler(cb, state);
2627 if (handler == null) {
2628 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
2631 handler.mute(state);
2634 public int getStreamType() {
2638 private int getValidIndex(int index) {
2641 } else if (index > mIndexMax) {
2648 private class VolumeDeathHandler implements IBinder.DeathRecipient {
2649 private IBinder mICallback; // To be notified of client's death
2650 private int mMuteCount; // Number of active mutes for this client
2652 VolumeDeathHandler(IBinder cb) {
2656 // must be called while synchronized on parent VolumeStreamState
2657 public void mute(boolean state) {
2659 if (mMuteCount == 0) {
2660 // Register for client death notification
2662 // mICallback can be 0 if muted by AudioService
2663 if (mICallback != null) {
2664 mICallback.linkToDeath(this, 0);
2666 mDeathHandlers.add(this);
2667 // If the stream is not yet muted by any client, set level to 0
2668 if (muteCount() == 0) {
2669 Set set = mIndex.entrySet();
2670 Iterator i = set.iterator();
2671 while (i.hasNext()) {
2672 Map.Entry entry = (Map.Entry)i.next();
2673 int device = ((Integer)entry.getKey()).intValue();
2674 setIndex(0, device, false /* lastAudible */);
2676 sendMsg(mAudioHandler,
2677 MSG_SET_ALL_VOLUMES,
2681 VolumeStreamState.this, 0);
2683 } catch (RemoteException e) {
2689 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
2693 if (mMuteCount == 0) {
2694 Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
2697 if (mMuteCount == 0) {
2698 // Unregister from client death notification
2699 mDeathHandlers.remove(this);
2700 // mICallback can be 0 if muted by AudioService
2701 if (mICallback != null) {
2702 mICallback.unlinkToDeath(this, 0);
2704 if (muteCount() == 0) {
2705 // If the stream is not muted any more, restore its volume if
2706 // ringer mode allows it
2707 if (!isStreamAffectedByRingerMode(mStreamType) ||
2708 mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
2709 Set set = mIndex.entrySet();
2710 Iterator i = set.iterator();
2711 while (i.hasNext()) {
2712 Map.Entry entry = (Map.Entry)i.next();
2713 int device = ((Integer)entry.getKey()).intValue();
2714 setIndex(getIndex(device,
2715 true /* lastAudible */),
2717 false /* lastAudible */);
2719 sendMsg(mAudioHandler,
2720 MSG_SET_ALL_VOLUMES,
2724 VolumeStreamState.this, 0);
2732 public void binderDied() {
2733 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
2734 if (mMuteCount != 0) {
2735 // Reset all active mute requests from this client.
2742 private synchronized int muteCount() {
2744 int size = mDeathHandlers.size();
2745 for (int i = 0; i < size; i++) {
2746 count += mDeathHandlers.get(i).mMuteCount;
2751 // only called by mute() which is already synchronized
2752 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
2753 VolumeDeathHandler handler;
2754 int size = mDeathHandlers.size();
2755 for (int i = 0; i < size; i++) {
2756 handler = mDeathHandlers.get(i);
2757 if (cb == handler.mICallback) {
2761 // If this is the first mute request for this client, create a new
2762 // client death handler. Otherwise, it is an out of sequence unmute request.
2764 handler = new VolumeDeathHandler(cb);
2766 Log.w(TAG, "stream was not muted by this client");
2772 private void dump(PrintWriter pw) {
2773 pw.print(" Current: ");
2774 Set set = mIndex.entrySet();
2775 Iterator i = set.iterator();
2776 while (i.hasNext()) {
2777 Map.Entry entry = (Map.Entry)i.next();
2778 pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
2779 + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
2781 pw.print("\n Last audible: ");
2782 set = mLastAudibleIndex.entrySet();
2784 while (i.hasNext()) {
2785 Map.Entry entry = (Map.Entry)i.next();
2786 pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
2787 + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
2792 /** Thread that handles native AudioSystem control. */
2793 private class AudioSystemThread extends Thread {
2794 AudioSystemThread() {
2795 super("AudioService");
2800 // Set this thread up so the handler will work on it
2803 synchronized(AudioService.this) {
2804 mAudioHandler = new AudioHandler();
2806 // Notify that the handler has been created
2807 AudioService.this.notify();
2810 // Listen for volume change requests that are set by VolumePanel
2815 /** Handles internal volume messages in separate volume thread. */
2816 private class AudioHandler extends Handler {
2818 private void setDeviceVolume(VolumeStreamState streamState, int device) {
2821 streamState.applyDeviceVolume(device);
2823 // Apply change to all streams using this one as alias
2824 int numStreamTypes = AudioSystem.getNumStreamTypes();
2825 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2826 if (streamType != streamState.mStreamType &&
2827 mStreamVolumeAlias[streamType] == streamState.mStreamType) {
2828 mStreamStates[streamType].applyDeviceVolume(getDeviceForStream(streamType));
2832 // Post a persist volume msg
2833 sendMsg(mAudioHandler,
2836 PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
2843 private void setAllVolumes(VolumeStreamState streamState) {
2846 streamState.applyAllVolumes();
2848 // Apply change to all streams using this one as alias
2849 int numStreamTypes = AudioSystem.getNumStreamTypes();
2850 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2851 if (streamType != streamState.mStreamType &&
2852 mStreamVolumeAlias[streamType] == streamState.mStreamType) {
2853 mStreamStates[streamType].applyAllVolumes();
2858 private void persistVolume(VolumeStreamState streamState,
2861 if ((persistType & PERSIST_CURRENT) != 0) {
2862 System.putInt(mContentResolver,
2863 streamState.getSettingNameForDevice(false /* lastAudible */, device),
2864 (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10);
2866 if ((persistType & PERSIST_LAST_AUDIBLE) != 0) {
2867 System.putInt(mContentResolver,
2868 streamState.getSettingNameForDevice(true /* lastAudible */, device),
2869 (streamState.getIndex(device, true /* lastAudible */) + 5) / 10);
2873 private void persistRingerMode(int ringerMode) {
2874 System.putInt(mContentResolver, System.MODE_RINGER, ringerMode);
2877 private void playSoundEffect(int effectType, int volume) {
2878 synchronized (mSoundEffectsLock) {
2879 if (mSoundPool == null) {
2883 // use default if volume is not specified by caller
2885 volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20);
2887 volFloat = (float) volume / 1000.0f;
2890 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
2891 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
2893 MediaPlayer mediaPlayer = new MediaPlayer();
2895 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
2896 mediaPlayer.setDataSource(filePath);
2897 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
2898 mediaPlayer.prepare();
2899 mediaPlayer.setVolume(volFloat, volFloat);
2900 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
2901 public void onCompletion(MediaPlayer mp) {
2905 mediaPlayer.setOnErrorListener(new OnErrorListener() {
2906 public boolean onError(MediaPlayer mp, int what, int extra) {
2911 mediaPlayer.start();
2912 } catch (IOException ex) {
2913 Log.w(TAG, "MediaPlayer IOException: "+ex);
2914 } catch (IllegalArgumentException ex) {
2915 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
2916 } catch (IllegalStateException ex) {
2917 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2923 private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
2924 Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
2925 receiver == null ? "" : receiver.flattenToString());
2928 private void cleanupPlayer(MediaPlayer mp) {
2933 } catch (IllegalStateException ex) {
2934 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2939 private void setForceUse(int usage, int config) {
2940 AudioSystem.setForceUse(usage, config);
2944 public void handleMessage(Message msg) {
2948 case MSG_SET_DEVICE_VOLUME:
2949 setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
2952 case MSG_SET_ALL_VOLUMES:
2953 setAllVolumes((VolumeStreamState) msg.obj);
2956 case MSG_PERSIST_VOLUME:
2957 persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2);
2960 case MSG_PERSIST_MASTER_VOLUME:
2961 Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER,
2962 (float)msg.arg1 / (float)1000.0);
2965 case MSG_PERSIST_MASTER_VOLUME_MUTE:
2966 Settings.System.putInt(mContentResolver, Settings.System.VOLUME_MASTER_MUTE,
2970 case MSG_PERSIST_RINGER_MODE:
2971 // note that the value persisted is the current ringer mode, not the
2972 // value of ringer mode as of the time the request was made to persist
2973 persistRingerMode(getRingerMode());
2976 case MSG_MEDIA_SERVER_DIED:
2977 if (!mMediaServerOk) {
2978 Log.e(TAG, "Media server died.");
2979 // Force creation of new IAudioFlinger interface so that we are notified
2980 // when new media_server process is back to life.
2981 AudioSystem.setErrorCallback(mAudioSystemCallback);
2982 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
2987 case MSG_MEDIA_SERVER_STARTED:
2988 Log.e(TAG, "Media server started.");
2989 // indicate to audio HAL that we start the reconfiguration phase after a media
2991 // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
2992 // process restarts after a crash, not the first time it is started.
2993 AudioSystem.setParameters("restarting=true");
2995 // Restore device connection states
2996 synchronized (mConnectedDevices) {
2997 Set set = mConnectedDevices.entrySet();
2998 Iterator i = set.iterator();
2999 while (i.hasNext()) {
3000 Map.Entry device = (Map.Entry)i.next();
3001 AudioSystem.setDeviceConnectionState(
3002 ((Integer)device.getKey()).intValue(),
3003 AudioSystem.DEVICE_STATE_AVAILABLE,
3004 (String)device.getValue());
3007 // Restore call state
3008 AudioSystem.setPhoneState(mMode);
3010 // Restore forced usage for communcations and record
3011 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
3012 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
3014 // Restore stream volumes
3015 int numStreamTypes = AudioSystem.getNumStreamTypes();
3016 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
3017 VolumeStreamState streamState = mStreamStates[streamType];
3018 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
3020 streamState.applyAllVolumes();
3023 // Restore ringer mode
3024 setRingerModeInt(getRingerMode(), false);
3026 // Restore master volume
3027 restoreMasterVolume();
3029 // Reset device orientation (if monitored for this device)
3030 if (SystemProperties.getBoolean("ro.audio.monitorOrientation", false)) {
3031 setOrientationForAudioSystem();
3034 synchronized (mBluetoothA2dpEnabledLock) {
3035 AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
3036 mBluetoothA2dpEnabled ?
3037 AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
3039 // indicate the end of reconfiguration phase to audio HAL
3040 AudioSystem.setParameters("restarting=false");
3043 case MSG_LOAD_SOUND_EFFECTS:
3047 case MSG_PLAY_SOUND_EFFECT:
3048 playSoundEffect(msg.arg1, msg.arg2);
3051 case MSG_BTA2DP_DOCK_TIMEOUT:
3052 // msg.obj == address of BTA2DP device
3053 synchronized (mConnectedDevices) {
3054 makeA2dpDeviceUnavailableNow( (String) msg.obj );
3058 case MSG_SET_FORCE_USE:
3059 case MSG_SET_FORCE_BT_A2DP_USE:
3060 setForceUse(msg.arg1, msg.arg2);
3063 case MSG_PERSIST_MEDIABUTTONRECEIVER:
3064 onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
3067 case MSG_RCDISPLAY_CLEAR:
3071 case MSG_RCDISPLAY_UPDATE:
3072 // msg.obj is guaranteed to be non null
3073 onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
3076 case MSG_BT_HEADSET_CNCT_FAILED:
3077 resetBluetoothSco();
3080 case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
3081 onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
3082 mMediaEventWakeLock.release();
3085 case MSG_SET_A2DP_CONNECTION_STATE:
3086 onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
3087 mMediaEventWakeLock.release();
3090 case MSG_REPORT_NEW_ROUTES: {
3091 int N = mRoutesObservers.beginBroadcast();
3093 AudioRoutesInfo routes;
3094 synchronized (mCurAudioRoutes) {
3095 routes = new AudioRoutesInfo(mCurAudioRoutes);
3099 IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(N);
3101 obs.dispatchAudioRoutesChanged(routes);
3102 } catch (RemoteException e) {
3106 mRoutesObservers.finishBroadcast();
3110 case MSG_REEVALUATE_REMOTE:
3111 onReevaluateRemote();
3114 case MSG_RCC_NEW_PLAYBACK_INFO:
3115 onNewPlaybackInfoForRcc(msg.arg1 /* rccId */, msg.arg2 /* key */,
3116 ((Integer)msg.obj).intValue() /* value */);
3118 case MSG_RCC_NEW_VOLUME_OBS:
3119 onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
3120 (IRemoteVolumeObserver)msg.obj /* rvo */);
3126 private class SettingsObserver extends ContentObserver {
3128 SettingsObserver() {
3129 super(new Handler());
3130 mContentResolver.registerContentObserver(Settings.System.getUriFor(
3131 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
3135 public void onChange(boolean selfChange) {
3136 super.onChange(selfChange);
3137 // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode.
3138 // However there appear to be some missing locks around mRingerModeMutedStreams
3139 // and mRingerModeAffectedStreams, so will leave this synchronized for now.
3140 // mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once).
3141 synchronized (mSettingsLock) {
3142 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
3143 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
3144 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
3145 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
3146 if (mVoiceCapable) {
3147 ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
3149 ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
3151 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
3153 * Ensure all stream types that should be affected by ringer mode
3154 * are in the proper state.
3156 mRingerModeAffectedStreams = ringerModeAffectedStreams;
3157 setRingerModeInt(getRingerMode(), false);
3163 // must be called synchronized on mConnectedDevices
3164 private void makeA2dpDeviceAvailable(String address) {
3165 // enable A2DP before notifying A2DP connection to avoid unecessary processing in
3166 // audio policy manager
3167 setBluetoothA2dpOnInt(true);
3168 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
3169 AudioSystem.DEVICE_STATE_AVAILABLE,
3171 // Reset A2DP suspend state each time a new sink is connected
3172 AudioSystem.setParameters("A2dpSuspended=false");
3173 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
3177 private void sendBecomingNoisyIntent() {
3178 mContext.sendBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
3181 // must be called synchronized on mConnectedDevices
3182 private void makeA2dpDeviceUnavailableNow(String address) {
3183 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
3184 AudioSystem.DEVICE_STATE_UNAVAILABLE,
3186 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
3189 // must be called synchronized on mConnectedDevices
3190 private void makeA2dpDeviceUnavailableLater(String address) {
3191 // prevent any activity on the A2DP audio output to avoid unwanted
3192 // reconnection of the sink.
3193 AudioSystem.setParameters("A2dpSuspended=true");
3194 // the device will be made unavailable later, so consider it disconnected right away
3195 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
3196 // send the delayed message to make the device unavailable later
3197 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
3198 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
3202 // must be called synchronized on mConnectedDevices
3203 private void cancelA2dpDeviceTimeout() {
3204 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
3207 // must be called synchronized on mConnectedDevices
3208 private boolean hasScheduledA2dpDockTimeout() {
3209 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
3212 private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state)
3214 if (btDevice == null) {
3217 String address = btDevice.getAddress();
3218 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
3221 synchronized (mConnectedDevices) {
3222 boolean isConnected =
3223 (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
3224 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
3226 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
3227 if (btDevice.isBluetoothDock()) {
3228 if (state == BluetoothProfile.STATE_DISCONNECTED) {
3229 // introduction of a delay for transient disconnections of docks when
3230 // power is rapidly turned off/on, this message will be canceled if
3231 // we reconnect the dock under a preset delay
3232 makeA2dpDeviceUnavailableLater(address);
3233 // the next time isConnected is evaluated, it will be false for the dock
3236 makeA2dpDeviceUnavailableNow(address);
3238 synchronized (mCurAudioRoutes) {
3239 if (mCurAudioRoutes.mBluetoothName != null) {
3240 mCurAudioRoutes.mBluetoothName = null;
3241 sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3242 SENDMSG_NOOP, 0, 0, null, 0);
3245 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
3246 if (btDevice.isBluetoothDock()) {
3247 // this could be a reconnection after a transient disconnection
3248 cancelA2dpDeviceTimeout();
3249 mDockAddress = address;
3251 // this could be a connection of another A2DP device before the timeout of
3252 // a dock: cancel the dock timeout, and make the dock unavailable now
3253 if(hasScheduledA2dpDockTimeout()) {
3254 cancelA2dpDeviceTimeout();
3255 makeA2dpDeviceUnavailableNow(mDockAddress);
3258 makeA2dpDeviceAvailable(address);
3259 synchronized (mCurAudioRoutes) {
3260 String name = btDevice.getAliasName();
3261 if (!TextUtils.equals(mCurAudioRoutes.mBluetoothName, name)) {
3262 mCurAudioRoutes.mBluetoothName = name;
3263 sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3264 SENDMSG_NOOP, 0, 0, null, 0);
3271 private boolean handleDeviceConnection(boolean connected, int device, String params) {
3272 synchronized (mConnectedDevices) {
3273 boolean isConnected = (mConnectedDevices.containsKey(device) &&
3274 (params.isEmpty() || mConnectedDevices.get(device).equals(params)));
3276 if (isConnected && !connected) {
3277 AudioSystem.setDeviceConnectionState(device,
3278 AudioSystem.DEVICE_STATE_UNAVAILABLE,
3279 mConnectedDevices.get(device));
3280 mConnectedDevices.remove(device);
3282 } else if (!isConnected && connected) {
3283 AudioSystem.setDeviceConnectionState(device,
3284 AudioSystem.DEVICE_STATE_AVAILABLE,
3286 mConnectedDevices.put(new Integer(device), params);
3293 // Devices which removal triggers intent ACTION_AUDIO_BECOMING_NOISY. The intent is only
3294 // sent if none of these devices is connected.
3295 int mBecomingNoisyIntentDevices =
3296 AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
3297 AudioSystem.DEVICE_OUT_ALL_A2DP;
3299 // must be called before removing the device from mConnectedDevices
3300 private int checkSendBecomingNoisyIntent(int device, int state) {
3302 if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
3304 for (int dev : mConnectedDevices.keySet()) {
3305 if ((dev & mBecomingNoisyIntentDevices) != 0) {
3309 if (devices == device) {
3311 sendBecomingNoisyIntent();
3315 if (mAudioHandler.hasMessages(MSG_SET_A2DP_CONNECTION_STATE) ||
3316 mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
3322 private void sendDeviceConnectionIntent(int device, int state, String name)
3324 Intent intent = new Intent();
3326 intent.putExtra("state", state);
3327 intent.putExtra("name", name);
3328 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
3332 if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
3333 connType = AudioRoutesInfo.MAIN_HEADSET;
3334 intent.setAction(Intent.ACTION_HEADSET_PLUG);
3335 intent.putExtra("microphone", 1);
3336 } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
3337 connType = AudioRoutesInfo.MAIN_HEADPHONES;
3338 intent.setAction(Intent.ACTION_HEADSET_PLUG);
3339 intent.putExtra("microphone", 0);
3340 } else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) {
3341 connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
3342 intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
3343 } else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
3344 connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
3345 intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
3346 } else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
3347 connType = AudioRoutesInfo.MAIN_HDMI;
3348 intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
3351 synchronized (mCurAudioRoutes) {
3352 if (connType != 0) {
3353 int newConn = mCurAudioRoutes.mMainType;
3355 newConn |= connType;
3357 newConn &= ~connType;
3359 if (newConn != mCurAudioRoutes.mMainType) {
3360 mCurAudioRoutes.mMainType = newConn;
3361 sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3362 SENDMSG_NOOP, 0, 0, null, 0);
3367 ActivityManagerNative.broadcastStickyIntent(intent, null);
3370 private void onSetWiredDeviceConnectionState(int device, int state, String name)
3372 synchronized (mConnectedDevices) {
3373 if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
3374 (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
3375 setBluetoothA2dpOnInt(true);
3377 handleDeviceConnection((state == 1), device, "");
3378 if ((state != 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
3379 (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
3380 setBluetoothA2dpOnInt(false);
3382 sendDeviceConnectionIntent(device, state, name);
3386 /* cache of the address of the last dock the device was connected to */
3387 private String mDockAddress;
3390 * Receiver for misc intent broadcasts the Phone app cares about.
3392 private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
3394 public void onReceive(Context context, Intent intent) {
3395 String action = intent.getAction();
3399 if (action.equals(Intent.ACTION_DOCK_EVENT)) {
3400 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
3401 Intent.EXTRA_DOCK_STATE_UNDOCKED);
3403 switch (dockState) {
3404 case Intent.EXTRA_DOCK_STATE_DESK:
3405 config = AudioSystem.FORCE_BT_DESK_DOCK;
3407 case Intent.EXTRA_DOCK_STATE_CAR:
3408 config = AudioSystem.FORCE_BT_CAR_DOCK;
3410 case Intent.EXTRA_DOCK_STATE_LE_DESK:
3411 config = AudioSystem.FORCE_ANALOG_DOCK;
3413 case Intent.EXTRA_DOCK_STATE_HE_DESK:
3414 config = AudioSystem.FORCE_DIGITAL_DOCK;
3416 case Intent.EXTRA_DOCK_STATE_UNDOCKED:
3418 config = AudioSystem.FORCE_NONE;
3420 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
3421 } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
3422 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
3423 BluetoothProfile.STATE_DISCONNECTED);
3424 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
3425 String address = null;
3427 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
3428 if (btDevice == null) {
3432 address = btDevice.getAddress();
3433 BluetoothClass btClass = btDevice.getBluetoothClass();
3434 if (btClass != null) {
3435 switch (btClass.getDeviceClass()) {
3436 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
3437 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
3438 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
3440 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
3441 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
3446 if (!BluetoothAdapter.checkBluetoothAddress(address)) {
3450 boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
3451 if (handleDeviceConnection(connected, device, address)) {
3452 synchronized (mScoClients) {
3454 mBluetoothHeadsetDevice = btDevice;
3456 mBluetoothHeadsetDevice = null;
3457 resetBluetoothSco();
3461 } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ||
3462 action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
3463 state = intent.getIntExtra("state", 0);
3464 int alsaCard = intent.getIntExtra("card", -1);
3465 int alsaDevice = intent.getIntExtra("device", -1);
3466 String params = (alsaCard == -1 && alsaDevice == -1 ? ""
3467 : "card=" + alsaCard + ";device=" + alsaDevice);
3468 device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
3469 AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE;
3470 Log.v(TAG, "Broadcast Receiver: Got "
3471 + (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
3472 "ACTION_USB_AUDIO_ACCESSORY_PLUG" : "ACTION_USB_AUDIO_DEVICE_PLUG")
3473 + ", state = " + state + ", card: " + alsaCard + ", device: " + alsaDevice);
3474 handleDeviceConnection((state == 1), device, params);
3475 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
3476 boolean broadcast = false;
3477 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
3478 synchronized (mScoClients) {
3479 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
3480 // broadcast intent if the connection was initated by AudioService
3481 if (!mScoClients.isEmpty() &&
3482 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
3483 mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
3484 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
3488 case BluetoothHeadset.STATE_AUDIO_CONNECTED:
3489 scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
3490 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
3491 mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
3492 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
3493 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
3496 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
3497 scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
3498 mScoAudioState = SCO_STATE_INACTIVE;
3499 clearAllScoClients(0, false);
3501 case BluetoothHeadset.STATE_AUDIO_CONNECTING:
3502 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
3503 mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
3504 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
3505 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
3508 // do not broadcast CONNECTING or invalid state
3514 broadcastScoConnectionState(scoAudioState);
3515 //FIXME: this is to maintain compatibility with deprecated intent
3516 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
3517 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
3518 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
3519 mContext.sendStickyBroadcast(newIntent);
3521 } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
3522 mBootCompleted = true;
3523 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP,
3527 (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
3528 mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
3529 resetBluetoothSco();
3530 getBluetoothHeadset();
3531 //FIXME: this is to maintain compatibility with deprecated intent
3532 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
3533 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
3534 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
3535 AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
3536 mContext.sendStickyBroadcast(newIntent);
3538 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
3539 if (adapter != null) {
3540 adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
3541 BluetoothProfile.A2DP);
3543 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
3544 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
3545 // a package is being removed, not replaced
3546 String packageName = intent.getData().getSchemeSpecificPart();
3547 if (packageName != null) {
3548 removeMediaButtonReceiverForPackage(packageName);
3551 } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
3552 AudioSystem.setParameters("screen_state=on");
3553 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
3554 AudioSystem.setParameters("screen_state=off");
3555 } else if (action.equalsIgnoreCase(Intent.ACTION_CONFIGURATION_CHANGED)) {
3556 handleConfigurationChanged(context);
3561 //==========================================================================================
3563 //==========================================================================================
3565 /* constant to identify focus stack entry that is used to hold the focus while the phone
3566 * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
3567 * entering and exiting calls.
3569 public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
3571 private final static Object mAudioFocusLock = new Object();
3573 private final static Object mRingingLock = new Object();
3575 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
3577 public void onCallStateChanged(int state, String incomingNumber) {
3578 if (state == TelephonyManager.CALL_STATE_RINGING) {
3579 //Log.v(TAG, " CALL_STATE_RINGING");
3580 synchronized(mRingingLock) {
3583 } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
3584 || (state == TelephonyManager.CALL_STATE_IDLE)) {
3585 synchronized(mRingingLock) {
3592 private void notifyTopOfAudioFocusStack() {
3593 // notify the top of the stack it gained focus
3594 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
3595 if (canReassignAudioFocus()) {
3597 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
3598 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
3599 } catch (RemoteException e) {
3600 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
3601 e.printStackTrace();
3607 private static class FocusStackEntry {
3608 public int mStreamType = -1;// no stream type
3609 public IAudioFocusDispatcher mFocusDispatcher = null;
3610 public IBinder mSourceRef = null;
3611 public String mClientId;
3612 public int mFocusChangeType;
3613 public AudioFocusDeathHandler mHandler;
3614 public String mPackageName;
3615 public int mCallingUid;
3617 public FocusStackEntry() {
3620 public FocusStackEntry(int streamType, int duration,
3621 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
3622 String pn, int uid) {
3623 mStreamType = streamType;
3624 mFocusDispatcher = afl;
3625 mSourceRef = source;
3627 mFocusChangeType = duration;
3633 public void unlinkToDeath() {
3635 if (mSourceRef != null && mHandler != null) {
3636 mSourceRef.unlinkToDeath(mHandler, 0);
3639 } catch (java.util.NoSuchElementException e) {
3640 Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
3645 protected void finalize() throws Throwable {
3646 unlinkToDeath(); // unlink exception handled inside method
3651 private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
3655 * Display in the log the current entries in the audio focus stack
3657 private void dumpFocusStack(PrintWriter pw) {
3658 pw.println("\nAudio Focus stack entries:");
3659 synchronized(mAudioFocusLock) {
3660 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3661 while(stackIterator.hasNext()) {
3662 FocusStackEntry fse = stackIterator.next();
3663 pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
3664 + " -- duration: " + fse.mFocusChangeType
3665 + " -- uid: " + fse.mCallingUid);
3672 * Called synchronized on mAudioFocusLock
3673 * Remove a focus listener from the focus stack.
3674 * @param focusListenerToRemove the focus listener
3675 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
3676 * focus, notify the next item in the stack it gained focus.
3678 private void removeFocusStackEntry(String clientToRemove, boolean signal) {
3679 // is the current top of the focus stack abandoning focus? (because of request, not death)
3680 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
3682 //Log.i(TAG, " removeFocusStackEntry() removing top of stack");
3683 FocusStackEntry fse = mFocusStack.pop();
3684 fse.unlinkToDeath();
3686 // notify the new top of the stack it gained focus
3687 notifyTopOfAudioFocusStack();
3688 // there's a new top of the stack, let the remote control know
3689 synchronized(mRCStack) {
3690 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3694 // focus is abandoned by a client that's not at the top of the stack,
3695 // no need to update focus.
3696 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3697 while(stackIterator.hasNext()) {
3698 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
3699 if(fse.mClientId.equals(clientToRemove)) {
3700 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
3702 stackIterator.remove();
3703 fse.unlinkToDeath();
3711 * Called synchronized on mAudioFocusLock
3712 * Remove focus listeners from the focus stack for a particular client when it has died.
3714 private void removeFocusStackEntryForClient(IBinder cb) {
3715 // is the owner of the audio focus part of the client to remove?
3716 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
3717 mFocusStack.peek().mSourceRef.equals(cb);
3718 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3719 while(stackIterator.hasNext()) {
3720 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
3721 if(fse.mSourceRef.equals(cb)) {
3722 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
3724 stackIterator.remove();
3725 // the client just died, no need to unlink to its death
3728 if (isTopOfStackForClientToRemove) {
3729 // we removed an entry at the top of the stack:
3730 // notify the new top of the stack it gained focus.
3731 notifyTopOfAudioFocusStack();
3732 // there's a new top of the stack, let the remote control know
3733 synchronized(mRCStack) {
3734 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3741 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
3743 private boolean canReassignAudioFocus() {
3744 // focus requests are rejected during a phone call or when the phone is ringing
3745 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
3746 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
3753 * Inner class to monitor audio focus client deaths, and remove them from the audio focus
3754 * stack if necessary.
3756 private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
3757 private IBinder mCb; // To be notified of client's death
3759 AudioFocusDeathHandler(IBinder cb) {
3763 public void binderDied() {
3764 synchronized(mAudioFocusLock) {
3765 Log.w(TAG, " AudioFocus audio focus client died");
3766 removeFocusStackEntryForClient(mCb);
3770 public IBinder getBinder() {
3776 /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
3777 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
3778 IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
3779 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId);
3780 // the main stream type for the audio focus request is currently not used. It may
3781 // potentially be used to handle multiple stream type-dependent audio focuses.
3783 // we need a valid binder callback for clients
3784 if (!cb.pingBinder()) {
3785 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
3786 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3789 synchronized(mAudioFocusLock) {
3790 if (!canReassignAudioFocus()) {
3791 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3794 // handle the potential premature death of the new holder of the focus
3795 // (premature death == death before abandoning focus)
3796 // Register for client death notification
3797 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
3799 cb.linkToDeath(afdh, 0);
3800 } catch (RemoteException e) {
3801 // client has already died!
3802 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
3803 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3806 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
3807 // if focus is already owned by this client and the reason for acquiring the focus
3808 // hasn't changed, don't do anything
3809 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
3810 // unlink death handler so it can be gc'ed.
3811 // linkToDeath() creates a JNI global reference preventing collection.
3812 cb.unlinkToDeath(afdh, 0);
3813 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3815 // the reason for the audio focus request has changed: remove the current top of
3816 // stack and respond as if we had a new focus owner
3817 FocusStackEntry fse = mFocusStack.pop();
3818 fse.unlinkToDeath();
3821 // notify current top of stack it is losing focus
3822 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
3824 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
3825 -1 * focusChangeHint, // loss and gain codes are inverse of each other
3826 mFocusStack.peek().mClientId);
3827 } catch (RemoteException e) {
3828 Log.e(TAG, " Failure to signal loss of focus due to "+ e);
3829 e.printStackTrace();
3833 // focus requester might already be somewhere below in the stack, remove it
3834 removeFocusStackEntry(clientId, false /* signal */);
3836 // push focus requester at the top of the audio focus stack
3837 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
3838 clientId, afdh, callingPackageName, Binder.getCallingUid()));
3840 // there's a new top of the stack, let the remote control know
3841 synchronized(mRCStack) {
3842 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3844 }//synchronized(mAudioFocusLock)
3846 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3849 /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
3850 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
3851 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
3853 // this will take care of notifying the new focus owner if needed
3854 synchronized(mAudioFocusLock) {
3855 removeFocusStackEntry(clientId, true);
3857 } catch (java.util.ConcurrentModificationException cme) {
3858 // Catching this exception here is temporary. It is here just to prevent
3859 // a crash seen when the "Silent" notification is played. This is believed to be fixed
3860 // but this try catch block is left just to be safe.
3861 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme);
3862 cme.printStackTrace();
3865 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3869 public void unregisterAudioFocusClient(String clientId) {
3870 synchronized(mAudioFocusLock) {
3871 removeFocusStackEntry(clientId, false);
3876 //==========================================================================================
3878 //==========================================================================================
3879 public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
3880 filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
3883 public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
3884 filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
3887 private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
3888 // sanity check on the incoming key event
3889 if (!isValidMediaKeyEvent(keyEvent)) {
3890 Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
3893 // event filtering for telephony
3894 synchronized(mRingingLock) {
3895 synchronized(mRCStack) {
3896 if ((mMediaReceiverForCalls != null) &&
3897 (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL))) {
3898 dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
3903 // event filtering based on voice-based interactions
3904 if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
3905 filterVoiceInputKeyEvent(keyEvent, needWakeLock);
3907 dispatchMediaKeyEvent(keyEvent, needWakeLock);
3912 * Handles the dispatching of the media button events to the telephony package.
3913 * Precondition: mMediaReceiverForCalls != null
3914 * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
3915 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
3918 private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
3919 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
3920 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
3921 keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
3923 mMediaEventWakeLock.acquire();
3924 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
3926 mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
3927 mAudioHandler, Activity.RESULT_OK, null, null);
3931 * Handles the dispatching of the media button events to one of the registered listeners,
3932 * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
3933 * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
3934 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
3937 private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
3939 mMediaEventWakeLock.acquire();
3941 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
3942 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
3943 synchronized(mRCStack) {
3944 if (!mRCStack.empty()) {
3945 // send the intent that was registered by the client
3947 mRCStack.peek().mMediaIntent.send(mContext,
3948 needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
3949 keyIntent, AudioService.this, mAudioHandler);
3950 } catch (CanceledException e) {
3951 Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
3952 e.printStackTrace();
3955 // legacy behavior when nobody registered their media button event receiver
3956 // through AudioManager
3958 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
3960 mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
3961 mAudioHandler, Activity.RESULT_OK, null, null);
3967 * The different actions performed in response to a voice button key event.
3969 private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
3970 private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
3971 private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
3973 private final Object mVoiceEventLock = new Object();
3974 private boolean mVoiceButtonDown;
3975 private boolean mVoiceButtonHandled;
3978 * Filter key events that may be used for voice-based interactions
3979 * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
3980 * media buttons that can be used to trigger voice-based interactions.
3981 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
3984 private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
3986 Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
3989 int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
3990 int keyAction = keyEvent.getAction();
3991 synchronized (mVoiceEventLock) {
3992 if (keyAction == KeyEvent.ACTION_DOWN) {
3993 if (keyEvent.getRepeatCount() == 0) {
3995 mVoiceButtonDown = true;
3996 mVoiceButtonHandled = false;
3997 } else if (mVoiceButtonDown && !mVoiceButtonHandled
3998 && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
3999 // long-press, start voice-based interactions
4000 mVoiceButtonHandled = true;
4001 voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
4003 } else if (keyAction == KeyEvent.ACTION_UP) {
4004 if (mVoiceButtonDown) {
4006 mVoiceButtonDown = false;
4007 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
4008 voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
4012 }//synchronized (mVoiceEventLock)
4014 // take action after media button event filtering for voice-based interactions
4015 switch (voiceButtonAction) {
4016 case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
4017 if (DEBUG_RC) Log.v(TAG, " ignore key event");
4019 case VOICEBUTTON_ACTION_START_VOICE_INPUT:
4020 if (DEBUG_RC) Log.v(TAG, " start voice-based interactions");
4021 // then start the voice-based interactions
4022 startVoiceBasedInteractions(needWakeLock);
4024 case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
4025 if (DEBUG_RC) Log.v(TAG, " send simulated key event");
4026 sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
4031 private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
4033 KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
4034 dispatchMediaKeyEvent(keyEvent, needWakeLock);
4036 keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
4037 dispatchMediaKeyEvent(keyEvent, needWakeLock);
4042 private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
4043 if (keyEvent == null) {
4046 final int keyCode = keyEvent.getKeyCode();
4048 case KeyEvent.KEYCODE_MUTE:
4049 case KeyEvent.KEYCODE_HEADSETHOOK:
4050 case KeyEvent.KEYCODE_MEDIA_PLAY:
4051 case KeyEvent.KEYCODE_MEDIA_PAUSE:
4052 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
4053 case KeyEvent.KEYCODE_MEDIA_STOP:
4054 case KeyEvent.KEYCODE_MEDIA_NEXT:
4055 case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
4056 case KeyEvent.KEYCODE_MEDIA_REWIND:
4057 case KeyEvent.KEYCODE_MEDIA_RECORD:
4058 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
4059 case KeyEvent.KEYCODE_MEDIA_CLOSE:
4060 case KeyEvent.KEYCODE_MEDIA_EJECT:
4069 * Checks whether the given key code is one that can trigger the launch of voice-based
4071 * @param keyCode the key code associated with the key event
4072 * @return true if the key is one of the supported voice-based interaction triggers
4074 private static boolean isValidVoiceInputKeyCode(int keyCode) {
4075 if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
4083 * Tell the system to start voice-based interactions / voice commands
4085 private void startVoiceBasedInteractions(boolean needWakeLock) {
4086 Intent voiceIntent = null;
4087 // select which type of search to launch:
4088 // - screen on and device unlocked: action is ACTION_WEB_SEARCH
4089 // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
4090 // with EXTRA_SECURE set to true if the device is securely locked
4091 PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
4092 boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
4093 if (!isLocked && pm.isScreenOn()) {
4094 voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
4096 voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
4097 voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
4098 isLocked && mKeyguardManager.isKeyguardSecure());
4100 // start the search activity
4102 mMediaEventWakeLock.acquire();
4105 if (voiceIntent != null) {
4106 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
4107 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
4108 mContext.startActivity(voiceIntent);
4110 } catch (ActivityNotFoundException e) {
4111 Log.w(TAG, "No activity for search: " + e);
4114 mMediaEventWakeLock.release();
4119 private PowerManager.WakeLock mMediaEventWakeLock;
4121 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
4123 // only set when wakelock was acquired, no need to check value when received
4124 private static final String EXTRA_WAKELOCK_ACQUIRED =
4125 "android.media.AudioService.WAKELOCK_ACQUIRED";
4127 public void onSendFinished(PendingIntent pendingIntent, Intent intent,
4128 int resultCode, String resultData, Bundle resultExtras) {
4129 if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
4130 mMediaEventWakeLock.release();
4134 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
4135 public void onReceive(Context context, Intent intent) {
4136 if (intent == null) {
4139 Bundle extras = intent.getExtras();
4140 if (extras == null) {
4143 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
4144 mMediaEventWakeLock.release();
4149 private final Object mCurrentRcLock = new Object();
4151 * The one remote control client which will receive a request for display information.
4152 * This object may be null.
4153 * Access protected by mCurrentRcLock.
4155 private IRemoteControlClient mCurrentRcClient = null;
4157 private final static int RC_INFO_NONE = 0;
4158 private final static int RC_INFO_ALL =
4159 RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
4160 RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
4161 RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
4162 RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
4165 * A monotonically increasing generation counter for mCurrentRcClient.
4166 * Only accessed with a lock on mCurrentRcLock.
4167 * No value wrap-around issues as we only act on equal values.
4169 private int mCurrentRcClientGen = 0;
4172 * Inner class to monitor remote control client deaths, and remove the client for the
4173 * remote control stack if necessary.
4175 private class RcClientDeathHandler implements IBinder.DeathRecipient {
4176 private IBinder mCb; // To be notified of client's death
4177 private PendingIntent mMediaIntent;
4179 RcClientDeathHandler(IBinder cb, PendingIntent pi) {
4184 public void binderDied() {
4185 Log.w(TAG, " RemoteControlClient died");
4186 // remote control client died, make sure the displays don't use it anymore
4187 // by setting its remote control client to null
4188 registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
4189 // the dead client was maybe handling remote playback, reevaluate
4190 postReevaluateRemote();
4193 public IBinder getBinder() {
4199 * A global counter for RemoteControlClient identifiers
4201 private static int sLastRccId = 0;
4203 private class RemotePlaybackState {
4207 int mVolumeHandling;
4209 private RemotePlaybackState(int id, int vol, int volMax) {
4212 mVolumeMax = volMax;
4213 mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
4218 * Internal cache for the playback information of the RemoteControlClient whose volume gets to
4219 * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
4220 * every time we need this info.
4222 private RemotePlaybackState mMainRemote;
4224 * Indicates whether the "main" RemoteControlClient is considered active.
4225 * Use synchronized on mMainRemote.
4227 private boolean mMainRemoteIsActive;
4229 * Indicates whether there is remote playback going on. True even if there is no "active"
4230 * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
4231 * handles remote playback.
4232 * Use synchronized on mMainRemote.
4234 private boolean mHasRemotePlayback;
4236 private static class RemoteControlStackEntry {
4237 public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
4239 * The target for the ACTION_MEDIA_BUTTON events.
4242 public PendingIntent mMediaIntent;
4244 * The registered media button event receiver.
4247 public ComponentName mReceiverComponent;
4248 public String mCallingPackageName;
4249 public int mCallingUid;
4251 * Provides access to the information to display on the remote control.
4252 * May be null (when a media button event receiver is registered,
4253 * but no remote control client has been registered) */
4254 public IRemoteControlClient mRcClient;
4255 public RcClientDeathHandler mRcClientDeathHandler;
4257 * Information only used for non-local playback
4259 public int mPlaybackType;
4260 public int mPlaybackVolume;
4261 public int mPlaybackVolumeMax;
4262 public int mPlaybackVolumeHandling;
4263 public int mPlaybackStream;
4264 public int mPlaybackState;
4265 public IRemoteVolumeObserver mRemoteVolumeObs;
4267 public void resetPlaybackInfo() {
4268 mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
4269 mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
4270 mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
4271 mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
4272 mPlaybackStream = AudioManager.STREAM_MUSIC;
4273 mPlaybackState = RemoteControlClient.PLAYSTATE_STOPPED;
4274 mRemoteVolumeObs = null;
4277 /** precondition: mediaIntent != null, eventReceiver != null */
4278 public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
4279 mMediaIntent = mediaIntent;
4280 mReceiverComponent = eventReceiver;
4283 mRccId = ++sLastRccId;
4285 resetPlaybackInfo();
4288 public void unlinkToRcClientDeath() {
4289 if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
4291 mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
4292 mRcClientDeathHandler = null;
4293 } catch (java.util.NoSuchElementException e) {
4294 // not much we can do here
4295 Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
4296 e.printStackTrace();
4302 protected void finalize() throws Throwable {
4303 unlinkToRcClientDeath();// unlink exception handled inside method
4309 * The stack of remote control event receivers.
4310 * Code sections and methods that modify the remote control event receiver stack are
4311 * synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
4312 * stack, audio focus or RC, can lead to a change in the remote control display
4314 private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
4317 * The component the telephony package can register so telephony calls have priority to
4318 * handle media button events
4320 private ComponentName mMediaReceiverForCalls = null;
4324 * Display in the log the current entries in the remote control focus stack
4326 private void dumpRCStack(PrintWriter pw) {
4327 pw.println("\nRemote Control stack entries:");
4328 synchronized(mRCStack) {
4329 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4330 while(stackIterator.hasNext()) {
4331 RemoteControlStackEntry rcse = stackIterator.next();
4332 pw.println(" pi: " + rcse.mMediaIntent +
4333 " -- ercvr: " + rcse.mReceiverComponent +
4334 " -- client: " + rcse.mRcClient +
4335 " -- uid: " + rcse.mCallingUid +
4336 " -- type: " + rcse.mPlaybackType +
4337 " state: " + rcse.mPlaybackState);
4344 * Display in the log the current entries in the remote control stack, focusing
4345 * on RemoteControlClient data
4347 private void dumpRCCStack(PrintWriter pw) {
4348 pw.println("\nRemote Control Client stack entries:");
4349 synchronized(mRCStack) {
4350 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4351 while(stackIterator.hasNext()) {
4352 RemoteControlStackEntry rcse = stackIterator.next();
4353 pw.println(" uid: " + rcse.mCallingUid +
4354 " -- id: " + rcse.mRccId +
4355 " -- type: " + rcse.mPlaybackType +
4356 " -- state: " + rcse.mPlaybackState +
4357 " -- vol handling: " + rcse.mPlaybackVolumeHandling +
4358 " -- vol: " + rcse.mPlaybackVolume +
4359 " -- volMax: " + rcse.mPlaybackVolumeMax +
4360 " -- volObs: " + rcse.mRemoteVolumeObs);
4364 synchronized (mMainRemote) {
4365 pw.println("\nRemote Volume State:");
4366 pw.println(" has remote: " + mHasRemotePlayback);
4367 pw.println(" is remote active: " + mMainRemoteIsActive);
4368 pw.println(" rccId: " + mMainRemote.mRccId);
4369 pw.println(" volume handling: "
4370 + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
4371 "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
4372 pw.println(" volume: " + mMainRemote.mVolume);
4373 pw.println(" volume steps: " + mMainRemote.mVolumeMax);
4379 * Remove any entry in the remote control stack that has the same package name as packageName
4380 * Pre-condition: packageName != null
4382 private void removeMediaButtonReceiverForPackage(String packageName) {
4383 synchronized(mRCStack) {
4384 if (mRCStack.empty()) {
4387 RemoteControlStackEntry oldTop = mRCStack.peek();
4388 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4389 // iterate over the stack entries
4390 while(stackIterator.hasNext()) {
4391 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
4392 if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
4393 // a stack entry is from the package being removed, remove it from the stack
4394 stackIterator.remove();
4395 rcse.unlinkToRcClientDeath();
4398 if (mRCStack.empty()) {
4399 // no saved media button receiver
4400 mAudioHandler.sendMessage(
4401 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
4403 } else if (oldTop != mRCStack.peek()) {
4404 // the top of the stack has changed, save it in the system settings
4405 // by posting a message to persist it
4406 mAudioHandler.sendMessage(
4407 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
4408 mRCStack.peek().mReceiverComponent));
4416 * Restore remote control receiver from the system settings.
4418 private void restoreMediaButtonReceiver() {
4419 String receiverName = Settings.System.getString(mContentResolver,
4420 Settings.System.MEDIA_BUTTON_RECEIVER);
4421 if ((null != receiverName) && !receiverName.isEmpty()) {
4422 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
4423 // construct a PendingIntent targeted to the restored component name
4424 // for the media button and register it
4425 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
4426 // the associated intent will be handled by the component being registered
4427 mediaButtonIntent.setComponent(eventReceiver);
4428 PendingIntent pi = PendingIntent.getBroadcast(mContext,
4429 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
4430 registerMediaButtonIntent(pi, eventReceiver);
4436 * Set the new remote control receiver at the top of the RC focus stack.
4437 * precondition: mediaIntent != null, target != null
4439 private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
4440 // already at top of stack?
4441 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
4444 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4445 RemoteControlStackEntry rcse = null;
4446 boolean wasInsideStack = false;
4447 while(stackIterator.hasNext()) {
4448 rcse = (RemoteControlStackEntry)stackIterator.next();
4449 if(rcse.mMediaIntent.equals(mediaIntent)) {
4450 wasInsideStack = true;
4451 stackIterator.remove();
4455 if (!wasInsideStack) {
4456 rcse = new RemoteControlStackEntry(mediaIntent, target);
4458 mRCStack.push(rcse);
4460 // post message to persist the default media button receiver
4461 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
4462 MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
4467 * Remove the remote control receiver from the RC focus stack.
4468 * precondition: pi != null
4470 private void removeMediaButtonReceiver(PendingIntent pi) {
4471 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4472 while(stackIterator.hasNext()) {
4473 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
4474 if(rcse.mMediaIntent.equals(pi)) {
4475 stackIterator.remove();
4476 rcse.unlinkToRcClientDeath();
4484 * Called synchronized on mRCStack
4486 private boolean isCurrentRcController(PendingIntent pi) {
4487 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
4493 //==========================================================================================
4494 // Remote control display / client
4495 //==========================================================================================
4497 * Update the remote control displays with the new "focused" client generation
4499 private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
4500 PendingIntent newMediaIntent, boolean clearing) {
4501 // NOTE: Only one IRemoteControlDisplay supported in this implementation
4502 if (mRcDisplay != null) {
4504 mRcDisplay.setCurrentClientId(
4505 newClientGeneration, newMediaIntent, clearing);
4506 } catch (RemoteException e) {
4507 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
4508 // if we had a display before, stop monitoring its death
4509 rcDisplay_stopDeathMonitor_syncRcStack();
4516 * Update the remote control clients with the new "focused" client generation
4518 private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
4519 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4520 while(stackIterator.hasNext()) {
4521 RemoteControlStackEntry se = stackIterator.next();
4522 if ((se != null) && (se.mRcClient != null)) {
4524 se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
4525 } catch (RemoteException e) {
4526 Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e);
4527 stackIterator.remove();
4528 se.unlinkToRcClientDeath();
4535 * Update the displays and clients with the new "focused" client generation and name
4536 * @param newClientGeneration the new generation value matching a client update
4537 * @param newClientEventReceiver the media button event receiver associated with the client.
4538 * May be null, which implies there is no registered media button event receiver.
4539 * @param clearing true if the new client generation value maps to a remote control update
4540 * where the display should be cleared.
4542 private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
4543 PendingIntent newMediaIntent, boolean clearing) {
4544 // send the new valid client generation ID to all displays
4545 setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
4546 // send the new valid client generation ID to all clients
4547 setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
4551 * Called when processing MSG_RCDISPLAY_CLEAR event
4553 private void onRcDisplayClear() {
4554 if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
4556 synchronized(mRCStack) {
4557 synchronized(mCurrentRcLock) {
4558 mCurrentRcClientGen++;
4559 // synchronously update the displays and clients with the new client generation
4560 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
4561 null /*newMediaIntent*/, true /*clearing*/);
4567 * Called when processing MSG_RCDISPLAY_UPDATE event
4569 private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
4570 synchronized(mRCStack) {
4571 synchronized(mCurrentRcLock) {
4572 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
4573 if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
4575 mCurrentRcClientGen++;
4576 // synchronously update the displays and clients with
4577 // the new client generation
4578 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
4579 rcse.mMediaIntent /*newMediaIntent*/,
4580 false /*clearing*/);
4582 // tell the current client that it needs to send info
4584 mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
4585 flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
4586 } catch (RemoteException e) {
4587 Log.e(TAG, "Current valid remote client is dead: "+e);
4588 mCurrentRcClient = null;
4591 // the remote control display owner has changed between the
4592 // the message to update the display was sent, and the time it
4593 // gets to be processed (now)
4602 * Called synchronized on mRCStack
4604 private void clearRemoteControlDisplay_syncAfRcs() {
4605 synchronized(mCurrentRcLock) {
4606 mCurrentRcClient = null;
4608 // will cause onRcDisplayClear() to be called in AudioService's handler thread
4609 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
4613 * Helper function for code readability: only to be called from
4614 * checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
4617 * - called synchronized mAudioFocusLock then on mRCStack
4618 * - mRCStack.isEmpty() is false
4620 private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
4621 RemoteControlStackEntry rcse = mRCStack.peek();
4622 int infoFlagsAboutToBeUsed = infoChangedFlags;
4623 // this is where we enforce opt-in for information display on the remote controls
4624 // with the new AudioManager.registerRemoteControlClient() API
4625 if (rcse.mRcClient == null) {
4626 //Log.w(TAG, "Can't update remote control display with null remote control client");
4627 clearRemoteControlDisplay_syncAfRcs();
4630 synchronized(mCurrentRcLock) {
4631 if (!rcse.mRcClient.equals(mCurrentRcClient)) {
4632 // new RC client, assume every type of information shall be queried
4633 infoFlagsAboutToBeUsed = RC_INFO_ALL;
4635 mCurrentRcClient = rcse.mRcClient;
4637 // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
4638 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
4639 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
4644 * Called synchronized on mAudioFocusLock, then mRCStack
4645 * Check whether the remote control display should be updated, triggers the update if required
4646 * @param infoChangedFlags the flags corresponding to the remote control client information
4647 * that has changed, if applicable (checking for the update conditions might trigger a
4648 * clear, rather than an update event).
4650 private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
4651 // determine whether the remote control display should be refreshed
4652 // if either stack is empty, there is a mismatch, so clear the RC display
4653 if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
4654 clearRemoteControlDisplay_syncAfRcs();
4657 // if the top of the two stacks belong to different packages, there is a mismatch, clear
4658 if ((mRCStack.peek().mCallingPackageName != null)
4659 && (mFocusStack.peek().mPackageName != null)
4660 && !(mRCStack.peek().mCallingPackageName.compareTo(
4661 mFocusStack.peek().mPackageName) == 0)) {
4662 clearRemoteControlDisplay_syncAfRcs();
4665 // if the audio focus didn't originate from the same Uid as the one in which the remote
4666 // control information will be retrieved, clear
4667 if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
4668 clearRemoteControlDisplay_syncAfRcs();
4671 // refresh conditions were verified: update the remote controls
4672 // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
4673 updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
4677 * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
4678 * precondition: mediaIntent != null, target != null
4680 public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
4681 Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent);
4683 synchronized(mAudioFocusLock) {
4684 synchronized(mRCStack) {
4685 pushMediaButtonReceiver(mediaIntent, eventReceiver);
4686 // new RC client, assume every type of information shall be queried
4687 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
4693 * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
4694 * precondition: mediaIntent != null, eventReceiver != null
4696 public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
4698 Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent);
4700 synchronized(mAudioFocusLock) {
4701 synchronized(mRCStack) {
4702 boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
4703 removeMediaButtonReceiver(mediaIntent);
4704 if (topOfStackWillChange) {
4705 // current RC client will change, assume every type of info needs to be queried
4706 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
4713 * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
4714 * precondition: c != null
4716 public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
4717 if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
4718 != PackageManager.PERMISSION_GRANTED) {
4719 Log.e(TAG, "Invalid permissions to register media button receiver for calls");
4722 synchronized(mRCStack) {
4723 mMediaReceiverForCalls = c;
4728 * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
4730 public void unregisterMediaButtonEventReceiverForCalls() {
4731 if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
4732 != PackageManager.PERMISSION_GRANTED) {
4733 Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
4736 synchronized(mRCStack) {
4737 mMediaReceiverForCalls = null;
4742 * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
4743 * @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient
4744 * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
4745 * without modifying the RC stack, but while still causing the display to refresh (will
4746 * become blank as a result of this)
4748 public int registerRemoteControlClient(PendingIntent mediaIntent,
4749 IRemoteControlClient rcClient, String callingPackageName) {
4750 if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
4751 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
4752 synchronized(mAudioFocusLock) {
4753 synchronized(mRCStack) {
4754 // store the new display information
4755 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4756 while(stackIterator.hasNext()) {
4757 RemoteControlStackEntry rcse = stackIterator.next();
4758 if(rcse.mMediaIntent.equals(mediaIntent)) {
4759 // already had a remote control client?
4760 if (rcse.mRcClientDeathHandler != null) {
4761 // stop monitoring the old client's death
4762 rcse.unlinkToRcClientDeath();
4764 // save the new remote control client
4765 rcse.mRcClient = rcClient;
4766 rcse.mCallingPackageName = callingPackageName;
4767 rcse.mCallingUid = Binder.getCallingUid();
4768 if (rcClient == null) {
4769 // here rcse.mRcClientDeathHandler is null;
4770 rcse.resetPlaybackInfo();
4773 rccId = rcse.mRccId;
4775 // there is a new (non-null) client:
4776 // 1/ give the new client the current display (if any)
4777 if (mRcDisplay != null) {
4779 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
4780 } catch (RemoteException e) {
4781 Log.e(TAG, "Error connecting remote control display to client: "+e);
4782 e.printStackTrace();
4785 // 2/ monitor the new client's death
4786 IBinder b = rcse.mRcClient.asBinder();
4787 RcClientDeathHandler rcdh =
4788 new RcClientDeathHandler(b, rcse.mMediaIntent);
4790 b.linkToDeath(rcdh, 0);
4791 } catch (RemoteException e) {
4792 // remote control client is DOA, disqualify it
4793 Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
4794 rcse.mRcClient = null;
4796 rcse.mRcClientDeathHandler = rcdh;
4800 // if the eventReceiver is at the top of the stack
4801 // then check for potential refresh of the remote controls
4802 if (isCurrentRcController(mediaIntent)) {
4803 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
4811 * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
4812 * rcClient is guaranteed non-null
4814 public void unregisterRemoteControlClient(PendingIntent mediaIntent,
4815 IRemoteControlClient rcClient) {
4816 synchronized(mAudioFocusLock) {
4817 synchronized(mRCStack) {
4818 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4819 while(stackIterator.hasNext()) {
4820 RemoteControlStackEntry rcse = stackIterator.next();
4821 if ((rcse.mMediaIntent.equals(mediaIntent))
4822 && rcClient.equals(rcse.mRcClient)) {
4823 // we found the IRemoteControlClient to unregister
4824 // stop monitoring its death
4825 rcse.unlinkToRcClientDeath();
4826 // reset the client-related fields
4827 rcse.mRcClient = null;
4828 rcse.mCallingPackageName = null;
4836 * The remote control displays.
4837 * Access synchronized on mRCStack
4838 * NOTE: Only one IRemoteControlDisplay supported in this implementation
4840 private IRemoteControlDisplay mRcDisplay;
4841 private RcDisplayDeathHandler mRcDisplayDeathHandler;
4842 private int mArtworkExpectedWidth = -1;
4843 private int mArtworkExpectedHeight = -1;
4845 * Inner class to monitor remote control display deaths, and unregister them from the list
4846 * of displays if necessary.
4848 private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
4849 private IBinder mCb; // To be notified of client's death
4851 public RcDisplayDeathHandler(IBinder b) {
4852 if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b);
4856 public void binderDied() {
4857 synchronized(mRCStack) {
4858 Log.w(TAG, "RemoteControl: display died");
4863 public void unlinkToRcDisplayDeath() {
4864 if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb);
4866 mCb.unlinkToDeath(this, 0);
4867 } catch (java.util.NoSuchElementException e) {
4868 // not much we can do here, the display was being unregistered anyway
4869 Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()");
4870 e.printStackTrace();
4876 private void rcDisplay_stopDeathMonitor_syncRcStack() {
4877 if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null)
4878 // we had a display before, stop monitoring its death
4879 mRcDisplayDeathHandler.unlinkToRcDisplayDeath();
4883 private void rcDisplay_startDeathMonitor_syncRcStack() {
4884 if (mRcDisplay != null) {
4885 // new non-null display, monitor its death
4886 IBinder b = mRcDisplay.asBinder();
4887 mRcDisplayDeathHandler = new RcDisplayDeathHandler(b);
4889 b.linkToDeath(mRcDisplayDeathHandler, 0);
4890 } catch (RemoteException e) {
4891 // remote control display is DOA, disqualify it
4892 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
4899 * Register an IRemoteControlDisplay.
4900 * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
4901 * at the top of the stack to update the new display with its information.
4902 * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
4903 * @param rcd the IRemoteControlDisplay to register. No effect if null.
4905 public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
4906 if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
4907 synchronized(mAudioFocusLock) {
4908 synchronized(mRCStack) {
4909 if ((mRcDisplay == rcd) || (rcd == null)) {
4912 // if we had a display before, stop monitoring its death
4913 rcDisplay_stopDeathMonitor_syncRcStack();
4915 // new display, start monitoring its death
4916 rcDisplay_startDeathMonitor_syncRcStack();
4918 // let all the remote control clients there is a new display
4919 // no need to unplug the previous because we only support one display
4920 // and the clients don't track the death of the display
4921 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4922 while(stackIterator.hasNext()) {
4923 RemoteControlStackEntry rcse = stackIterator.next();
4924 if(rcse.mRcClient != null) {
4926 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
4927 } catch (RemoteException e) {
4928 Log.e(TAG, "Error connecting remote control display to client: " + e);
4929 e.printStackTrace();
4934 // we have a new display, of which all the clients are now aware: have it be updated
4935 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
4941 * Unregister an IRemoteControlDisplay.
4942 * Since only one IRemoteControlDisplay is supported, this has no effect if the one to
4943 * unregister is not the current one.
4944 * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
4946 public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
4947 if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
4948 synchronized(mRCStack) {
4949 // only one display here, so you can only unregister the current display
4950 if ((rcd == null) || (rcd != mRcDisplay)) {
4951 if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD");
4954 // if we had a display before, stop monitoring its death
4955 rcDisplay_stopDeathMonitor_syncRcStack();
4958 // disconnect this remote control display from all the clients
4959 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4960 while(stackIterator.hasNext()) {
4961 RemoteControlStackEntry rcse = stackIterator.next();
4962 if(rcse.mRcClient != null) {
4964 rcse.mRcClient.unplugRemoteControlDisplay(rcd);
4965 } catch (RemoteException e) {
4966 Log.e(TAG, "Error disconnecting remote control display to client: " + e);
4967 e.printStackTrace();
4974 public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
4975 synchronized(mRCStack) {
4976 // NOTE: Only one IRemoteControlDisplay supported in this implementation
4977 mArtworkExpectedWidth = w;
4978 mArtworkExpectedHeight = h;
4982 public void setPlaybackInfoForRcc(int rccId, int what, int value) {
4983 sendMsg(mAudioHandler, MSG_RCC_NEW_PLAYBACK_INFO, SENDMSG_QUEUE,
4984 rccId /* arg1 */, what /* arg2 */, Integer.valueOf(value) /* obj */, 0 /* delay */);
4987 // handler for MSG_RCC_NEW_PLAYBACK_INFO
4988 private void onNewPlaybackInfoForRcc(int rccId, int key, int value) {
4989 if(DEBUG_RC) Log.d(TAG, "onNewPlaybackInfoForRcc(id=" + rccId +
4990 ", what=" + key + ",val=" + value + ")");
4991 synchronized(mRCStack) {
4992 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4993 while(stackIterator.hasNext()) {
4994 RemoteControlStackEntry rcse = stackIterator.next();
4995 if (rcse.mRccId == rccId) {
4997 case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
4998 rcse.mPlaybackType = value;
4999 postReevaluateRemote();
5001 case RemoteControlClient.PLAYBACKINFO_VOLUME:
5002 rcse.mPlaybackVolume = value;
5003 synchronized (mMainRemote) {
5004 if (rccId == mMainRemote.mRccId) {
5005 mMainRemote.mVolume = value;
5006 mVolumePanel.postHasNewRemotePlaybackInfo();
5010 case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
5011 rcse.mPlaybackVolumeMax = value;
5012 synchronized (mMainRemote) {
5013 if (rccId == mMainRemote.mRccId) {
5014 mMainRemote.mVolumeMax = value;
5015 mVolumePanel.postHasNewRemotePlaybackInfo();
5019 case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
5020 rcse.mPlaybackVolumeHandling = value;
5021 synchronized (mMainRemote) {
5022 if (rccId == mMainRemote.mRccId) {
5023 mMainRemote.mVolumeHandling = value;
5024 mVolumePanel.postHasNewRemotePlaybackInfo();
5028 case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
5029 rcse.mPlaybackStream = value;
5031 case RemoteControlClient.PLAYBACKINFO_PLAYSTATE:
5032 rcse.mPlaybackState = value;
5033 synchronized (mMainRemote) {
5034 if (rccId == mMainRemote.mRccId) {
5035 mMainRemoteIsActive = isPlaystateActive(value);
5036 postReevaluateRemote();
5041 Log.e(TAG, "unhandled key " + key + " for RCC " + rccId);
5050 public void registerRemoteVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
5051 sendMsg(mAudioHandler, MSG_RCC_NEW_VOLUME_OBS, SENDMSG_QUEUE,
5052 rccId /* arg1 */, 0, rvo /* obj */, 0 /* delay */);
5055 // handler for MSG_RCC_NEW_VOLUME_OBS
5056 private void onRegisterVolumeObserverForRcc(int rccId, IRemoteVolumeObserver rvo) {
5057 synchronized(mRCStack) {
5058 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5059 while(stackIterator.hasNext()) {
5060 RemoteControlStackEntry rcse = stackIterator.next();
5061 if (rcse.mRccId == rccId) {
5062 rcse.mRemoteVolumeObs = rvo;
5070 * Checks if a remote client is active on the supplied stream type. Update the remote stream
5071 * volume state if found and playing
5073 * @return false if no remote playing is currently playing
5075 private boolean checkUpdateRemoteStateIfActive(int streamType) {
5076 synchronized(mRCStack) {
5077 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5078 while(stackIterator.hasNext()) {
5079 RemoteControlStackEntry rcse = stackIterator.next();
5080 if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
5081 && isPlaystateActive(rcse.mPlaybackState)
5082 && (rcse.mPlaybackStream == streamType)) {
5083 if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
5084 + ", vol =" + rcse.mPlaybackVolume);
5085 synchronized (mMainRemote) {
5086 mMainRemote.mRccId = rcse.mRccId;
5087 mMainRemote.mVolume = rcse.mPlaybackVolume;
5088 mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax;
5089 mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling;
5090 mMainRemoteIsActive = true;
5096 synchronized (mMainRemote) {
5097 mMainRemoteIsActive = false;
5103 * Returns true if the given playback state is considered "active", i.e. it describes a state
5104 * where playback is happening, or about to
5105 * @param playState the playback state to evaluate
5106 * @return true if active, false otherwise (inactive or unknown)
5108 private static boolean isPlaystateActive(int playState) {
5109 switch (playState) {
5110 case RemoteControlClient.PLAYSTATE_PLAYING:
5111 case RemoteControlClient.PLAYSTATE_BUFFERING:
5112 case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
5113 case RemoteControlClient.PLAYSTATE_REWINDING:
5114 case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
5115 case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
5122 private void adjustRemoteVolume(int streamType, int direction, int flags) {
5123 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
5124 boolean volFixed = false;
5125 synchronized (mMainRemote) {
5126 if (!mMainRemoteIsActive) {
5127 if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
5130 rccId = mMainRemote.mRccId;
5131 volFixed = (mMainRemote.mVolumeHandling ==
5132 RemoteControlClient.PLAYBACK_VOLUME_FIXED);
5134 // unlike "local" stream volumes, we can't compute the new volume based on the direction,
5135 // we can only notify the remote that volume needs to be updated, and we'll get an async'
5136 // update through setPlaybackInfoForRcc()
5138 sendVolumeUpdateToRemote(rccId, direction);
5142 mVolumePanel.postRemoteVolumeChanged(streamType, flags);
5145 private void sendVolumeUpdateToRemote(int rccId, int direction) {
5146 if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
5147 if (direction == 0) {
5148 // only handling discrete events
5151 IRemoteVolumeObserver rvo = null;
5152 synchronized (mRCStack) {
5153 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5154 while(stackIterator.hasNext()) {
5155 RemoteControlStackEntry rcse = stackIterator.next();
5156 //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
5157 if (rcse.mRccId == rccId) {
5158 rvo = rcse.mRemoteVolumeObs;
5165 rvo.dispatchRemoteVolumeUpdate(direction, -1);
5166 } catch (RemoteException e) {
5167 Log.e(TAG, "Error dispatching relative volume update", e);
5172 public int getRemoteStreamMaxVolume() {
5173 synchronized (mMainRemote) {
5174 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
5177 return mMainRemote.mVolumeMax;
5181 public int getRemoteStreamVolume() {
5182 synchronized (mMainRemote) {
5183 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
5186 return mMainRemote.mVolume;
5190 public void setRemoteStreamVolume(int vol) {
5191 if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
5192 int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
5193 synchronized (mMainRemote) {
5194 if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
5197 rccId = mMainRemote.mRccId;
5199 IRemoteVolumeObserver rvo = null;
5200 synchronized (mRCStack) {
5201 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5202 while(stackIterator.hasNext()) {
5203 RemoteControlStackEntry rcse = stackIterator.next();
5204 if (rcse.mRccId == rccId) {
5205 //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
5206 rvo = rcse.mRemoteVolumeObs;
5213 rvo.dispatchRemoteVolumeUpdate(0, vol);
5214 } catch (RemoteException e) {
5215 Log.e(TAG, "Error dispatching absolute volume update", e);
5221 * Call to make AudioService reevaluate whether it's in a mode where remote players should
5222 * have their volume controlled. In this implementation this is only to reset whether
5223 * VolumePanel should display remote volumes
5225 private void postReevaluateRemote() {
5226 sendMsg(mAudioHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
5229 private void onReevaluateRemote() {
5230 if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
5231 // is there a registered RemoteControlClient that is handling remote playback
5232 boolean hasRemotePlayback = false;
5233 synchronized (mRCStack) {
5234 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5235 while(stackIterator.hasNext()) {
5236 RemoteControlStackEntry rcse = stackIterator.next();
5237 if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
5238 hasRemotePlayback = true;
5243 synchronized (mMainRemote) {
5244 if (mHasRemotePlayback != hasRemotePlayback) {
5245 mHasRemotePlayback = hasRemotePlayback;
5246 mVolumePanel.postRemoteSliderVisibility(hasRemotePlayback);
5251 //==========================================================================================
5252 // Device orientation
5253 //==========================================================================================
5255 * Handles device configuration changes that may map to a change in the orientation.
5256 * This feature is optional, and is defined by the definition and value of the
5257 * "ro.audio.monitorOrientation" system property.
5259 private void handleConfigurationChanged(Context context) {
5261 // reading new orientation "safely" (i.e. under try catch) in case anything
5262 // goes wrong when obtaining resources and configuration
5263 int newOrientation = context.getResources().getConfiguration().orientation;
5264 if (newOrientation != mDeviceOrientation) {
5265 mDeviceOrientation = newOrientation;
5266 setOrientationForAudioSystem();
5268 } catch (Exception e) {
5269 Log.e(TAG, "Error retrieving device orientation: " + e);
5273 private void setOrientationForAudioSystem() {
5274 switch (mDeviceOrientation) {
5275 case Configuration.ORIENTATION_LANDSCAPE:
5276 //Log.i(TAG, "orientation is landscape");
5277 AudioSystem.setParameters("orientation=landscape");
5279 case Configuration.ORIENTATION_PORTRAIT:
5280 //Log.i(TAG, "orientation is portrait");
5281 AudioSystem.setParameters("orientation=portrait");
5283 case Configuration.ORIENTATION_SQUARE:
5284 //Log.i(TAG, "orientation is square");
5285 AudioSystem.setParameters("orientation=square");
5287 case Configuration.ORIENTATION_UNDEFINED:
5288 //Log.i(TAG, "orientation is undefined");
5289 AudioSystem.setParameters("orientation=undefined");
5292 Log.e(TAG, "Unknown orientation");
5297 // Handles request to override default use of A2DP for media.
5298 public void setBluetoothA2dpOnInt(boolean on) {
5299 synchronized (mBluetoothA2dpEnabledLock) {
5300 mBluetoothA2dpEnabled = on;
5301 mAudioHandler.removeMessages(MSG_SET_FORCE_BT_A2DP_USE);
5302 AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
5303 mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
5308 public void setRingtonePlayer(IRingtonePlayer player) {
5309 mContext.enforceCallingOrSelfPermission(REMOTE_AUDIO_PLAYBACK, null);
5310 mRingtonePlayer = player;
5314 public IRingtonePlayer getRingtonePlayer() {
5315 return mRingtonePlayer;
5319 public AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
5320 synchronized (mCurAudioRoutes) {
5321 AudioRoutesInfo routes = new AudioRoutesInfo(mCurAudioRoutes);
5322 mRoutesObservers.register(observer);
5328 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
5329 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
5334 dumpStreamStates(pw);
5335 pw.println("\nAudio routes:");
5336 pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
5337 pw.print(" mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);