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 android.app.ActivityManagerNative;
20 import android.content.BroadcastReceiver;
21 import android.content.ComponentName;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.bluetooth.BluetoothA2dp;
27 import android.bluetooth.BluetoothClass;
28 import android.bluetooth.BluetoothDevice;
29 import android.bluetooth.BluetoothHeadset;
30 import android.content.pm.PackageManager;
31 import android.database.ContentObserver;
32 import android.media.MediaPlayer.OnCompletionListener;
33 import android.media.MediaPlayer.OnErrorListener;
34 import android.os.Binder;
35 import android.os.Environment;
36 import android.os.Handler;
37 import android.os.IBinder;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.os.RemoteException;
41 import android.os.ServiceManager;
42 import android.provider.Settings;
43 import android.provider.Settings.System;
44 import android.telephony.PhoneStateListener;
45 import android.telephony.TelephonyManager;
46 import android.util.Log;
47 import android.view.KeyEvent;
48 import android.view.VolumePanel;
49 import android.os.SystemProperties;
51 import com.android.internal.telephony.ITelephony;
53 import java.io.FileDescriptor;
54 import java.io.IOException;
55 import java.io.PrintWriter;
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.Iterator;
61 import java.util.Stack;
64 * The implementation of the volume manager service.
66 * This implementation focuses on delivering a responsive UI. Most methods are
67 * asynchronous to external calls. For example, the task of setting a volume
68 * will update our internal state, but in a separate thread will set the system
69 * volume and later persist to the database. Similarly, setting the ringer mode
70 * will update the state and broadcast a change and in a separate thread later
71 * persist the ringer mode.
75 public class AudioService extends IAudioService.Stub {
77 private static final String TAG = "AudioService";
79 /** How long to delay before persisting a change in volume/ringer mode. */
80 private static final int PERSIST_DELAY = 3000;
82 private Context mContext;
83 private ContentResolver mContentResolver;
87 private VolumePanel mVolumePanel;
90 /** Used when a message should be shared across all stream types. */
91 private static final int SHARED_MSG = -1;
92 /** If the msg is already queued, replace it with this one. */
93 private static final int SENDMSG_REPLACE = 0;
94 /** If the msg is already queued, ignore this one and leave the old. */
95 private static final int SENDMSG_NOOP = 1;
96 /** If the msg is already queued, queue this one and leave the old. */
97 private static final int SENDMSG_QUEUE = 2;
99 // AudioHandler message.whats
100 private static final int MSG_SET_SYSTEM_VOLUME = 0;
101 private static final int MSG_PERSIST_VOLUME = 1;
102 private static final int MSG_PERSIST_RINGER_MODE = 3;
103 private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
104 private static final int MSG_MEDIA_SERVER_DIED = 5;
105 private static final int MSG_MEDIA_SERVER_STARTED = 6;
106 private static final int MSG_PLAY_SOUND_EFFECT = 7;
107 private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8;
109 private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
111 /** @see AudioSystemThread */
112 private AudioSystemThread mAudioSystemThread;
113 /** @see AudioHandler */
114 private AudioHandler mAudioHandler;
115 /** @see VolumeStreamState */
116 private VolumeStreamState[] mStreamStates;
117 private SettingsObserver mSettingsObserver;
120 private Object mSettingsLock = new Object();
121 private boolean mMediaServerOk;
123 private SoundPool mSoundPool;
124 private Object mSoundEffectsLock = new Object();
125 private static final int NUM_SOUNDPOOL_CHANNELS = 4;
126 private static final int SOUND_EFFECT_VOLUME = 1000;
128 /* Sound effect file names */
129 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
130 private static final String[] SOUND_EFFECT_FILES = new String[] {
132 "KeypressStandard.ogg",
133 "KeypressSpacebar.ogg",
134 "KeypressDelete.ogg",
138 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
139 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
140 * uses soundpool (second column) */
141 private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
142 {0, -1}, // FX_KEY_CLICK
143 {0, -1}, // FX_FOCUS_NAVIGATION_UP
144 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN
145 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT
146 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT
147 {1, -1}, // FX_KEYPRESS_STANDARD
148 {2, -1}, // FX_KEYPRESS_SPACEBAR
149 {3, -1}, // FX_FOCUS_DELETE
150 {4, -1} // FX_FOCUS_RETURN
153 /** @hide Maximum volume index values for audio streams */
154 private int[] MAX_STREAM_VOLUME = new int[] {
155 5, // STREAM_VOICE_CALL
160 7, // STREAM_NOTIFICATION
161 15, // STREAM_BLUETOOTH_SCO
162 7, // STREAM_SYSTEM_ENFORCED
166 /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
167 * of another stream: This avoids multiplying the volume settings for hidden
168 * stream types that follow other stream behavior for volume settings
169 * NOTE: do not create loops in aliases! */
170 private int[] STREAM_VOLUME_ALIAS = new int[] {
171 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
172 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM
173 AudioSystem.STREAM_RING, // STREAM_RING
174 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
175 AudioSystem.STREAM_ALARM, // STREAM_ALARM
176 AudioSystem.STREAM_NOTIFICATION, // STREAM_NOTIFICATION
177 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
178 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM_ENFORCED
179 AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
180 AudioSystem.STREAM_MUSIC // STREAM_TTS
183 private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
184 public void onError(int error) {
186 case AudioSystem.AUDIO_STATUS_SERVER_DIED:
187 if (mMediaServerOk) {
188 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
190 mMediaServerOk = false;
193 case AudioSystem.AUDIO_STATUS_OK:
194 if (!mMediaServerOk) {
195 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
197 mMediaServerOk = true;
207 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
208 * {@link AudioManager#RINGER_MODE_SILENT}, or
209 * {@link AudioManager#RINGER_MODE_VIBRATE}.
211 private int mRingerMode;
213 /** @see System#MODE_RINGER_STREAMS_AFFECTED */
214 private int mRingerModeAffectedStreams;
216 // Streams currently muted by ringer mode
217 private int mRingerModeMutedStreams;
219 /** @see System#MUTE_STREAMS_AFFECTED */
220 private int mMuteAffectedStreams;
223 * Has multiple bits per vibrate type to indicate the type's vibrate
224 * setting. See {@link #setVibrateSetting(int, int)}.
226 * NOTE: This is not the final decision of whether vibrate is on/off for the
227 * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
229 private int mVibrateSetting;
231 /** @see System#NOTIFICATIONS_USE_RING_VOLUME */
232 private int mNotificationsUseRingVolume;
234 // Broadcast receiver for device connections intent broadcasts
235 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
237 // Broadcast receiver for media button broadcasts (separate from mReceiver to
238 // independently change its priority)
239 private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
241 // Devices currently connected
242 private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
244 // Forced device usage for communications
245 private int mForcedUseForComm;
247 // List of binder death handlers for setMode() client processes.
248 // The last process to have called setMode() is at the top of the list.
249 private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
251 // List of clients having issued a SCO start request
252 private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
254 // BluetoothHeadset API to control SCO connection
255 private BluetoothHeadset mBluetoothHeadset;
257 // Bluetooth headset connection state
258 private boolean mBluetoothHeadsetConnected;
260 ///////////////////////////////////////////////////////////////////////////
262 ///////////////////////////////////////////////////////////////////////////
265 public AudioService(Context context) {
267 mContentResolver = context.getContentResolver();
270 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
271 "ro.config.vc_call_vol_steps",
272 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
274 mVolumePanel = new VolumePanel(context, this);
275 mSettingsObserver = new SettingsObserver();
276 mForcedUseForComm = AudioSystem.FORCE_NONE;
277 createAudioSystemThread();
278 readPersistedSettings();
279 createStreamStates();
280 // Call setMode() to initialize mSetModeDeathHandlers
281 mMode = AudioSystem.MODE_INVALID;
282 setMode(AudioSystem.MODE_NORMAL, null);
283 mMediaServerOk = true;
285 // Call setRingerModeInt() to apply correct mute
286 // state on streams affected by ringer mode.
287 mRingerModeMutedStreams = 0;
288 setRingerModeInt(getRingerMode(), false);
290 AudioSystem.setErrorCallback(mAudioSystemCallback);
293 mBluetoothHeadsetConnected = false;
294 mBluetoothHeadset = new BluetoothHeadset(context,
295 mBluetoothHeadsetServiceListener);
297 // Register for device connection intent broadcasts.
298 IntentFilter intentFilter =
299 new IntentFilter(Intent.ACTION_HEADSET_PLUG);
300 intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
301 intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
302 intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
303 intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
304 context.registerReceiver(mReceiver, intentFilter);
306 // Register for media button intent broadcasts.
307 intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
308 intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
309 context.registerReceiver(mMediaButtonReceiver, intentFilter);
311 // Register for phone state monitoring
312 TelephonyManager tmgr = (TelephonyManager)
313 context.getSystemService(Context.TELEPHONY_SERVICE);
314 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
317 private void createAudioSystemThread() {
318 mAudioSystemThread = new AudioSystemThread();
319 mAudioSystemThread.start();
320 waitForAudioHandlerCreation();
323 /** Waits for the volume handler to be created by the other thread. */
324 private void waitForAudioHandlerCreation() {
326 while (mAudioHandler == null) {
328 // Wait for mAudioHandler to be set by the other thread
330 } catch (InterruptedException e) {
331 Log.e(TAG, "Interrupted while waiting on volume handler.");
337 private void createStreamStates() {
338 int numStreamTypes = AudioSystem.getNumStreamTypes();
339 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
341 for (int i = 0; i < numStreamTypes; i++) {
342 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
345 // Correct stream index values for streams with aliases
346 for (int i = 0; i < numStreamTypes; i++) {
347 if (STREAM_VOLUME_ALIAS[i] != i) {
348 int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
349 streams[i].mIndex = streams[i].getValidIndex(index);
350 setStreamVolumeIndex(i, index);
351 index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
352 streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
357 private void readPersistedSettings() {
358 final ContentResolver cr = mContentResolver;
360 mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
362 mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
364 mRingerModeAffectedStreams = Settings.System.getInt(cr,
365 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
366 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
367 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
369 mMuteAffectedStreams = System.getInt(cr,
370 System.MUTE_STREAMS_AFFECTED,
371 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
373 mNotificationsUseRingVolume = System.getInt(cr,
374 Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1);
376 if (mNotificationsUseRingVolume == 1) {
377 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
379 // Each stream will read its own persisted settings
381 // Broadcast the sticky intent
382 broadcastRingerMode();
384 // Broadcast vibrate settings
385 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
386 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
389 private void setStreamVolumeIndex(int stream, int index) {
390 AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
393 private int rescaleIndex(int index, int srcStream, int dstStream) {
394 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
397 ///////////////////////////////////////////////////////////////////////////
399 ///////////////////////////////////////////////////////////////////////////
401 /** @see AudioManager#adjustVolume(int, int) */
402 public void adjustVolume(int direction, int flags) {
403 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
406 /** @see AudioManager#adjustVolume(int, int, int) */
407 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
409 int streamType = getActiveStreamType(suggestedStreamType);
411 // Don't play sound on other streams
412 if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
413 flags &= ~AudioManager.FLAG_PLAY_SOUND;
416 adjustStreamVolume(streamType, direction, flags);
419 /** @see AudioManager#adjustStreamVolume(int, int, int) */
420 public void adjustStreamVolume(int streamType, int direction, int flags) {
421 ensureValidDirection(direction);
422 ensureValidStreamType(streamType);
425 VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
426 final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
427 boolean adjustVolume = true;
429 // If either the client forces allowing ringer modes for this adjustment,
430 // or the stream type is one that is affected by ringer modes
431 if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0
432 || streamType == AudioSystem.STREAM_RING) {
433 // Check if the ringer mode changes with this volume adjustment. If
434 // it does, it will handle adjusting the volume, so we won't below
435 adjustVolume = checkForRingerModeChange(oldIndex, direction);
438 // If stream is muted, adjust last audible index only
440 if (streamState.muteCount() != 0) {
442 streamState.adjustLastAudibleIndex(direction);
443 // Post a persist volume msg
444 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
445 SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
447 index = streamState.mLastAudibleIndex;
449 if (adjustVolume && streamState.adjustIndex(direction)) {
450 // Post message to set system volume (it in turn will post a message
451 // to persist). Do not change volume if stream is muted.
452 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,
455 index = streamState.mIndex;
458 mVolumePanel.postVolumeChanged(streamType, flags);
460 sendVolumeUpdate(streamType, oldIndex, index);
463 /** @see AudioManager#setStreamVolume(int, int, int) */
464 public void setStreamVolume(int streamType, int index, int flags) {
465 ensureValidStreamType(streamType);
466 VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
468 final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
470 index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
471 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
473 index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
476 mVolumePanel.postVolumeChanged(streamType, flags);
478 sendVolumeUpdate(streamType, oldIndex, index);
481 private void sendVolumeUpdate(int streamType, int oldIndex, int index) {
482 oldIndex = (oldIndex + 5) / 10;
483 index = (index + 5) / 10;
485 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
486 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
487 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
488 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
490 mContext.sendBroadcast(intent);
494 * Sets the stream state's index, and posts a message to set system volume.
495 * This will not call out to the UI. Assumes a valid stream type.
497 * @param streamType Type of the stream
498 * @param index Desired volume index of the stream
499 * @param force If true, set the volume even if the desired volume is same
500 * as the current volume.
501 * @param lastAudible If true, stores new index as last audible one
503 private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
504 VolumeStreamState streamState = mStreamStates[streamType];
506 // If stream is muted, set last audible index only
507 if (streamState.muteCount() != 0) {
508 // Do not allow last audible index to be 0
510 streamState.setLastAudibleIndex(index);
511 // Post a persist volume msg
512 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
513 SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
516 if (streamState.setIndex(index, lastAudible) || force) {
517 // Post message to set system volume (it in turn will post a message
519 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
525 /** @see AudioManager#setStreamSolo(int, boolean) */
526 public void setStreamSolo(int streamType, boolean state, IBinder cb) {
527 for (int stream = 0; stream < mStreamStates.length; stream++) {
528 if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
529 // Bring back last audible volume
530 mStreamStates[stream].mute(cb, state);
534 /** @see AudioManager#setStreamMute(int, boolean) */
535 public void setStreamMute(int streamType, boolean state, IBinder cb) {
536 if (isStreamAffectedByMute(streamType)) {
537 mStreamStates[streamType].mute(cb, state);
541 /** @see AudioManager#getStreamVolume(int) */
542 public int getStreamVolume(int streamType) {
543 ensureValidStreamType(streamType);
544 return (mStreamStates[streamType].mIndex + 5) / 10;
547 /** @see AudioManager#getStreamMaxVolume(int) */
548 public int getStreamMaxVolume(int streamType) {
549 ensureValidStreamType(streamType);
550 return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
553 /** @see AudioManager#getRingerMode() */
554 public int getRingerMode() {
558 /** @see AudioManager#setRingerMode(int) */
559 public void setRingerMode(int ringerMode) {
560 synchronized (mSettingsLock) {
561 if (ringerMode != mRingerMode) {
562 setRingerModeInt(ringerMode, true);
563 // Send sticky broadcast
564 broadcastRingerMode();
569 private void setRingerModeInt(int ringerMode, boolean persist) {
570 mRingerMode = ringerMode;
572 // Mute stream if not previously muted by ringer mode and ringer mode
573 // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
574 // Unmute stream if previously muted by ringer mode and ringer mode
575 // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
576 int numStreamTypes = AudioSystem.getNumStreamTypes();
577 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
578 if (isStreamMutedByRingerMode(streamType)) {
579 if (!isStreamAffectedByRingerMode(streamType) ||
580 mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
581 mStreamStates[streamType].mute(null, false);
582 mRingerModeMutedStreams &= ~(1 << streamType);
585 if (isStreamAffectedByRingerMode(streamType) &&
586 mRingerMode != AudioManager.RINGER_MODE_NORMAL) {
587 mStreamStates[streamType].mute(null, true);
588 mRingerModeMutedStreams |= (1 << streamType);
593 // Post a persist ringer mode msg
595 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
596 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
600 /** @see AudioManager#shouldVibrate(int) */
601 public boolean shouldVibrate(int vibrateType) {
603 switch (getVibrateSetting(vibrateType)) {
605 case AudioManager.VIBRATE_SETTING_ON:
606 return mRingerMode != AudioManager.RINGER_MODE_SILENT;
608 case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
609 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
611 case AudioManager.VIBRATE_SETTING_OFF:
612 // return false, even for incoming calls
620 /** @see AudioManager#getVibrateSetting(int) */
621 public int getVibrateSetting(int vibrateType) {
622 return (mVibrateSetting >> (vibrateType * 2)) & 3;
625 /** @see AudioManager#setVibrateSetting(int, int) */
626 public void setVibrateSetting(int vibrateType, int vibrateSetting) {
628 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
631 broadcastVibrateSetting(vibrateType);
633 // Post message to set ringer mode (it in turn will post a message
635 sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
640 * @see #setVibrateSetting(int, int)
642 public static int getValueForVibrateSetting(int existingValue, int vibrateType,
643 int vibrateSetting) {
645 // First clear the existing setting. Each vibrate type has two bits in
646 // the value. Note '3' is '11' in binary.
647 existingValue &= ~(3 << (vibrateType * 2));
649 // Set into the old value
650 existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
652 return existingValue;
655 private class SetModeDeathHandler implements IBinder.DeathRecipient {
656 private IBinder mCb; // To be notified of client's death
657 private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
659 SetModeDeathHandler(IBinder cb) {
663 public void binderDied() {
664 synchronized(mSetModeDeathHandlers) {
665 Log.w(TAG, "setMode() client died");
666 int index = mSetModeDeathHandlers.indexOf(this);
668 Log.w(TAG, "unregistered setMode() client died");
670 mSetModeDeathHandlers.remove(this);
671 // If dead client was a the top of client list,
672 // apply next mode in the stack
674 // mSetModeDeathHandlers is never empty as the initial entry
675 // created when AudioService starts is never removed
676 SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0);
677 int mode = hdlr.getMode();
678 if (AudioService.this.mMode != mode) {
679 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
680 AudioService.this.mMode = mode;
688 public void setMode(int mode) {
692 public int getMode() {
696 public IBinder getBinder() {
701 /** @see AudioManager#setMode(int) */
702 public void setMode(int mode, IBinder cb) {
703 if (!checkAudioSettingsPermission("setMode()")) {
707 if (mode < AudioSystem.MODE_CURRENT || mode > AudioSystem.MODE_IN_CALL) {
711 synchronized (mSettingsLock) {
712 if (mode == AudioSystem.MODE_CURRENT) {
716 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
719 synchronized(mSetModeDeathHandlers) {
720 SetModeDeathHandler hdlr = null;
721 Iterator iter = mSetModeDeathHandlers.iterator();
722 while (iter.hasNext()) {
723 SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
724 if (h.getBinder() == cb) {
726 // Remove from client list so that it is re-inserted at top of list
732 hdlr = new SetModeDeathHandler(cb);
733 // cb is null when setMode() is called by AudioService constructor
735 // Register for client death notification
737 cb.linkToDeath(hdlr, 0);
738 } catch (RemoteException e) {
740 Log.w(TAG, "setMode() could not link to "+cb+" binder death");
744 // Last client to call setMode() is always at top of client list
745 // as required by SetModeDeathHandler.binderDied()
746 mSetModeDeathHandlers.add(0, hdlr);
750 if (mode != AudioSystem.MODE_NORMAL) {
751 clearAllScoClients();
755 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
756 int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
757 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false);
761 /** @see AudioManager#getMode() */
762 public int getMode() {
766 /** @see AudioManager#playSoundEffect(int) */
767 public void playSoundEffect(int effectType) {
768 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
769 effectType, -1, null, 0);
772 /** @see AudioManager#playSoundEffect(int, float) */
773 public void playSoundEffectVolume(int effectType, float volume) {
775 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
776 effectType, (int) (volume * 1000), null, 0);
780 * Loads samples into the soundpool.
781 * This method must be called at when sound effects are enabled
783 public boolean loadSoundEffects() {
784 synchronized (mSoundEffectsLock) {
785 if (mSoundPool != null) {
788 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
789 if (mSoundPool == null) {
793 * poolId table: The value -1 in this table indicates that corresponding
794 * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
795 * Once loaded, the value in poolId is the sample ID and the same
796 * sample can be reused for another effect using the same file.
798 int[] poolId = new int[SOUND_EFFECT_FILES.length];
799 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
800 poolId[fileIdx] = -1;
803 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
804 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
805 * this indicates we have a valid sample loaded for this effect.
807 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
808 // Do not load sample if this effect uses the MediaPlayer
809 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
812 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
813 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
814 int sampleId = mSoundPool.load(filePath, 0);
815 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
816 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
818 Log.w(TAG, "Soundpool could not load file: "+filePath);
821 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
830 * Unloads samples from the sound pool.
831 * This method can be called to free some memory when
832 * sound effects are disabled.
834 public void unloadSoundEffects() {
835 synchronized (mSoundEffectsLock) {
836 if (mSoundPool == null) {
839 int[] poolId = new int[SOUND_EFFECT_FILES.length];
840 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
844 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
845 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
848 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
849 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
850 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
851 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
858 /** @see AudioManager#reloadAudioSettings() */
859 public void reloadAudioSettings() {
860 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
861 readPersistedSettings();
863 // restore volume settings
864 int numStreamTypes = AudioSystem.getNumStreamTypes();
865 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
866 VolumeStreamState streamState = mStreamStates[streamType];
868 String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
869 String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
870 int index = Settings.System.getInt(mContentResolver,
872 AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
873 if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
874 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
878 streamState.mIndex = streamState.getValidIndex(index);
880 index = (index + 5) / 10;
881 index = Settings.System.getInt(mContentResolver,
882 lastAudibleSettingName,
883 (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
884 if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
885 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
889 streamState.mLastAudibleIndex = streamState.getValidIndex(index);
891 // unmute stream that was muted but is not affect by mute anymore
892 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
893 int size = streamState.mDeathHandlers.size();
894 for (int i = 0; i < size; i++) {
895 streamState.mDeathHandlers.get(i).mMuteCount = 1;
896 streamState.mDeathHandlers.get(i).mute(false);
899 // apply stream volume
900 if (streamState.muteCount() == 0) {
901 setStreamVolumeIndex(streamType, streamState.mIndex);
905 // apply new ringer mode
906 setRingerModeInt(getRingerMode(), false);
909 /** @see AudioManager#setSpeakerphoneOn() */
910 public void setSpeakerphoneOn(boolean on){
911 if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
915 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER);
916 mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
918 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
919 mForcedUseForComm = AudioSystem.FORCE_NONE;
923 /** @see AudioManager#isSpeakerphoneOn() */
924 public boolean isSpeakerphoneOn() {
925 if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
932 /** @see AudioManager#setBluetoothScoOn() */
933 public void setBluetoothScoOn(boolean on){
934 if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
938 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO);
939 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO);
940 mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
942 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
943 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE);
944 mForcedUseForComm = AudioSystem.FORCE_NONE;
948 /** @see AudioManager#isBluetoothScoOn() */
949 public boolean isBluetoothScoOn() {
950 if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
957 /** @see AudioManager#startBluetoothSco() */
958 public void startBluetoothSco(IBinder cb){
959 if (!checkAudioSettingsPermission("startBluetoothSco()")) {
962 ScoClient client = getScoClient(cb);
966 /** @see AudioManager#stopBluetoothSco() */
967 public void stopBluetoothSco(IBinder cb){
968 if (!checkAudioSettingsPermission("stopBluetoothSco()")) {
971 ScoClient client = getScoClient(cb);
975 private class ScoClient implements IBinder.DeathRecipient {
976 private IBinder mCb; // To be notified of client's death
977 private int mStartcount; // number of SCO connections started by this client
979 ScoClient(IBinder cb) {
984 public void binderDied() {
985 synchronized(mScoClients) {
986 Log.w(TAG, "SCO client died");
987 int index = mScoClients.indexOf(this);
989 Log.w(TAG, "unregistered SCO client died");
992 mScoClients.remove(this);
997 public void incCount() {
998 synchronized(mScoClients) {
999 requestScoState(BluetoothHeadset.AUDIO_STATE_CONNECTED);
1000 if (mStartcount == 0) {
1002 mCb.linkToDeath(this, 0);
1003 } catch (RemoteException e) {
1004 // client has already died!
1005 Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death");
1012 public void decCount() {
1013 synchronized(mScoClients) {
1014 if (mStartcount == 0) {
1015 Log.w(TAG, "ScoClient.decCount() already 0");
1018 if (mStartcount == 0) {
1019 mCb.unlinkToDeath(this, 0);
1021 requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
1026 public void clearCount(boolean stopSco) {
1027 synchronized(mScoClients) {
1029 mCb.unlinkToDeath(this, 0);
1031 requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
1036 public int getCount() {
1040 public IBinder getBinder() {
1044 public int totalCount() {
1045 synchronized(mScoClients) {
1047 int size = mScoClients.size();
1048 for (int i = 0; i < size; i++) {
1049 count += mScoClients.get(i).getCount();
1055 private void requestScoState(int state) {
1056 if (totalCount() == 0 &&
1057 mBluetoothHeadsetConnected &&
1058 AudioService.this.mMode == AudioSystem.MODE_NORMAL) {
1059 if (state == BluetoothHeadset.AUDIO_STATE_CONNECTED) {
1060 mBluetoothHeadset.startVoiceRecognition();
1062 mBluetoothHeadset.stopVoiceRecognition();
1068 public ScoClient getScoClient(IBinder cb) {
1069 synchronized(mScoClients) {
1071 int size = mScoClients.size();
1072 for (int i = 0; i < size; i++) {
1073 client = mScoClients.get(i);
1074 if (client.getBinder() == cb)
1077 client = new ScoClient(cb);
1078 mScoClients.add(client);
1083 public void clearAllScoClients() {
1084 synchronized(mScoClients) {
1085 int size = mScoClients.size();
1086 for (int i = 0; i < size; i++) {
1087 mScoClients.get(i).clearCount(false);
1092 private BluetoothHeadset.ServiceListener mBluetoothHeadsetServiceListener =
1093 new BluetoothHeadset.ServiceListener() {
1094 public void onServiceConnected() {
1095 if (mBluetoothHeadset != null &&
1096 mBluetoothHeadset.getState() == BluetoothHeadset.STATE_CONNECTED) {
1097 mBluetoothHeadsetConnected = true;
1100 public void onServiceDisconnected() {
1101 if (mBluetoothHeadset != null &&
1102 mBluetoothHeadset.getState() == BluetoothHeadset.STATE_DISCONNECTED) {
1103 mBluetoothHeadsetConnected = false;
1104 clearAllScoClients();
1109 ///////////////////////////////////////////////////////////////////////////
1111 ///////////////////////////////////////////////////////////////////////////
1114 * Checks if the adjustment should change ringer mode instead of just
1115 * adjusting volume. If so, this will set the proper ringer mode and volume
1116 * indices on the stream states.
1118 private boolean checkForRingerModeChange(int oldIndex, int direction) {
1119 boolean adjustVolumeIndex = true;
1120 int newRingerMode = mRingerMode;
1122 if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
1123 // audible mode, at the bottom of the scale
1124 if (direction == AudioManager.ADJUST_LOWER
1125 && (oldIndex + 5) / 10 == 1) {
1126 // "silent mode", but which one?
1127 newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
1128 ? AudioManager.RINGER_MODE_VIBRATE
1129 : AudioManager.RINGER_MODE_SILENT;
1132 if (direction == AudioManager.ADJUST_RAISE) {
1133 // exiting silent mode
1134 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
1136 // prevent last audible index to reach 0
1137 adjustVolumeIndex = false;
1141 if (newRingerMode != mRingerMode) {
1142 setRingerMode(newRingerMode);
1145 * If we are changing ringer modes, do not increment/decrement the
1146 * volume index. Instead, the handler for the message above will
1147 * take care of changing the index.
1149 adjustVolumeIndex = false;
1152 return adjustVolumeIndex;
1155 public boolean isStreamAffectedByRingerMode(int streamType) {
1156 return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
1159 private boolean isStreamMutedByRingerMode(int streamType) {
1160 return (mRingerModeMutedStreams & (1 << streamType)) != 0;
1163 public boolean isStreamAffectedByMute(int streamType) {
1164 return (mMuteAffectedStreams & (1 << streamType)) != 0;
1167 private void ensureValidDirection(int direction) {
1168 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
1169 throw new IllegalArgumentException("Bad direction " + direction);
1173 private void ensureValidStreamType(int streamType) {
1174 if (streamType < 0 || streamType >= mStreamStates.length) {
1175 throw new IllegalArgumentException("Bad stream type " + streamType);
1179 private int getActiveStreamType(int suggestedStreamType) {
1180 boolean isOffhook = false;
1182 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1183 if (phone != null) isOffhook = phone.isOffhook();
1184 } catch (RemoteException e) {
1185 Log.w(TAG, "Couldn't connect to phone service", e);
1188 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) {
1189 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1190 return AudioSystem.STREAM_BLUETOOTH_SCO;
1191 } else if (isOffhook || AudioSystem.isStreamActive(AudioSystem.STREAM_VOICE_CALL)) {
1192 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1193 return AudioSystem.STREAM_VOICE_CALL;
1194 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC)) {
1195 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
1196 return AudioSystem.STREAM_MUSIC;
1197 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
1198 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING...");
1199 return AudioSystem.STREAM_RING;
1201 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1202 return suggestedStreamType;
1206 private void broadcastRingerMode() {
1207 // Send sticky broadcast
1208 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
1209 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
1210 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1211 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1212 long origCallerIdentityToken = Binder.clearCallingIdentity();
1213 mContext.sendStickyBroadcast(broadcast);
1214 Binder.restoreCallingIdentity(origCallerIdentityToken);
1217 private void broadcastVibrateSetting(int vibrateType) {
1219 if (ActivityManagerNative.isSystemReady()) {
1220 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
1221 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
1222 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
1223 mContext.sendBroadcast(broadcast);
1227 // Message helper methods
1228 private static int getMsg(int baseMsg, int streamType) {
1229 return (baseMsg & 0xffff) | streamType << 16;
1232 private static int getMsgBase(int msg) {
1233 return msg & 0xffff;
1236 private static void sendMsg(Handler handler, int baseMsg, int streamType,
1237 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
1238 int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
1240 if (existingMsgPolicy == SENDMSG_REPLACE) {
1241 handler.removeMessages(msg);
1242 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
1247 .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
1250 boolean checkAudioSettingsPermission(String method) {
1251 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
1252 == PackageManager.PERMISSION_GRANTED) {
1255 String msg = "Audio Settings Permission Denial: " + method + " from pid="
1256 + Binder.getCallingPid()
1257 + ", uid=" + Binder.getCallingUid();
1263 ///////////////////////////////////////////////////////////////////////////
1265 ///////////////////////////////////////////////////////////////////////////
1267 public class VolumeStreamState {
1268 private final int mStreamType;
1270 private String mVolumeIndexSettingName;
1271 private String mLastAudibleVolumeIndexSettingName;
1272 private int mIndexMax;
1274 private int mLastAudibleIndex;
1275 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
1277 private VolumeStreamState(String settingName, int streamType) {
1279 setVolumeIndexSettingName(settingName);
1281 mStreamType = streamType;
1283 final ContentResolver cr = mContentResolver;
1284 mIndexMax = MAX_STREAM_VOLUME[streamType];
1285 mIndex = Settings.System.getInt(cr,
1286 mVolumeIndexSettingName,
1287 AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1288 mLastAudibleIndex = Settings.System.getInt(cr,
1289 mLastAudibleVolumeIndexSettingName,
1290 (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1291 AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
1293 mIndex = getValidIndex(10 * mIndex);
1294 mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
1295 setStreamVolumeIndex(streamType, mIndex);
1296 mDeathHandlers = new ArrayList<VolumeDeathHandler>();
1299 public void setVolumeIndexSettingName(String settingName) {
1300 mVolumeIndexSettingName = settingName;
1301 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1304 public boolean adjustIndex(int deltaIndex) {
1305 return setIndex(mIndex + deltaIndex * 10, true);
1308 public boolean setIndex(int index, boolean lastAudible) {
1309 int oldIndex = mIndex;
1310 mIndex = getValidIndex(index);
1312 if (oldIndex != mIndex) {
1314 mLastAudibleIndex = mIndex;
1316 // Apply change to all streams using this one as alias
1317 int numStreamTypes = AudioSystem.getNumStreamTypes();
1318 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1319 if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
1320 mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
1329 public void setLastAudibleIndex(int index) {
1330 mLastAudibleIndex = getValidIndex(index);
1333 public void adjustLastAudibleIndex(int deltaIndex) {
1334 setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10);
1337 public int getMaxIndex() {
1341 public void mute(IBinder cb, boolean state) {
1342 VolumeDeathHandler handler = getDeathHandler(cb, state);
1343 if (handler == null) {
1344 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
1347 handler.mute(state);
1350 private int getValidIndex(int index) {
1353 } else if (index > mIndexMax) {
1360 private class VolumeDeathHandler implements IBinder.DeathRecipient {
1361 private IBinder mICallback; // To be notified of client's death
1362 private int mMuteCount; // Number of active mutes for this client
1364 VolumeDeathHandler(IBinder cb) {
1368 public void mute(boolean state) {
1369 synchronized(mDeathHandlers) {
1371 if (mMuteCount == 0) {
1372 // Register for client death notification
1374 // mICallback can be 0 if muted by AudioService
1375 if (mICallback != null) {
1376 mICallback.linkToDeath(this, 0);
1378 mDeathHandlers.add(this);
1379 // If the stream is not yet muted by any client, set lvel to 0
1380 if (muteCount() == 0) {
1382 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1383 VolumeStreamState.this, 0);
1385 } catch (RemoteException e) {
1388 mDeathHandlers.notify();
1392 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
1396 if (mMuteCount == 0) {
1397 Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
1400 if (mMuteCount == 0) {
1401 // Unregistr from client death notification
1402 mDeathHandlers.remove(this);
1403 // mICallback can be 0 if muted by AudioService
1404 if (mICallback != null) {
1405 mICallback.unlinkToDeath(this, 0);
1407 if (muteCount() == 0) {
1408 // If the stream is not muted any more, restore it's volume if
1409 // ringer mode allows it
1410 if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
1411 setIndex(mLastAudibleIndex, false);
1412 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1413 VolumeStreamState.this, 0);
1419 mDeathHandlers.notify();
1423 public void binderDied() {
1424 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
1425 if (mMuteCount != 0) {
1426 // Reset all active mute requests from this client.
1433 private int muteCount() {
1435 int size = mDeathHandlers.size();
1436 for (int i = 0; i < size; i++) {
1437 count += mDeathHandlers.get(i).mMuteCount;
1442 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
1443 synchronized(mDeathHandlers) {
1444 VolumeDeathHandler handler;
1445 int size = mDeathHandlers.size();
1446 for (int i = 0; i < size; i++) {
1447 handler = mDeathHandlers.get(i);
1448 if (cb == handler.mICallback) {
1452 // If this is the first mute request for this client, create a new
1453 // client death handler. Otherwise, it is an out of sequence unmute request.
1455 handler = new VolumeDeathHandler(cb);
1457 Log.w(TAG, "stream was not muted by this client");
1465 /** Thread that handles native AudioSystem control. */
1466 private class AudioSystemThread extends Thread {
1467 AudioSystemThread() {
1468 super("AudioService");
1473 // Set this thread up so the handler will work on it
1476 synchronized(AudioService.this) {
1477 mAudioHandler = new AudioHandler();
1479 // Notify that the handler has been created
1480 AudioService.this.notify();
1483 // Listen for volume change requests that are set by VolumePanel
1488 /** Handles internal volume messages in separate volume thread. */
1489 private class AudioHandler extends Handler {
1491 private void setSystemVolume(VolumeStreamState streamState) {
1494 setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
1496 // Apply change to all streams using this one as alias
1497 int numStreamTypes = AudioSystem.getNumStreamTypes();
1498 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1499 if (streamType != streamState.mStreamType &&
1500 STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
1501 setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
1505 // Post a persist volume msg
1506 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
1507 SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY);
1510 private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) {
1512 System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
1513 (streamState.mIndex + 5)/ 10);
1516 System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
1517 (streamState.mLastAudibleIndex + 5) / 10);
1521 private void persistRingerMode() {
1522 System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
1525 private void persistVibrateSetting() {
1526 System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
1529 private void playSoundEffect(int effectType, int volume) {
1530 synchronized (mSoundEffectsLock) {
1531 if (mSoundPool == null) {
1535 // use STREAM_MUSIC volume attenuated by 3 dB if volume is not specified by caller
1537 // Same linear to log conversion as in native AudioSystem::linearToLog() (AudioSystem.cpp)
1538 float dBPerStep = (float)((0.5 * 100) / MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
1539 int musicVolIndex = (mStreamStates[AudioSystem.STREAM_MUSIC].mIndex + 5) / 10;
1540 float musicVoldB = dBPerStep * (musicVolIndex - MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
1541 volFloat = (float)Math.pow(10, (musicVoldB - 3)/20);
1543 volFloat = (float) volume / 1000.0f;
1546 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
1547 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
1549 MediaPlayer mediaPlayer = new MediaPlayer();
1550 if (mediaPlayer != null) {
1552 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
1553 mediaPlayer.setDataSource(filePath);
1554 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
1555 mediaPlayer.prepare();
1556 mediaPlayer.setVolume(volFloat, volFloat);
1557 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
1558 public void onCompletion(MediaPlayer mp) {
1562 mediaPlayer.setOnErrorListener(new OnErrorListener() {
1563 public boolean onError(MediaPlayer mp, int what, int extra) {
1568 mediaPlayer.start();
1569 } catch (IOException ex) {
1570 Log.w(TAG, "MediaPlayer IOException: "+ex);
1571 } catch (IllegalArgumentException ex) {
1572 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
1573 } catch (IllegalStateException ex) {
1574 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1581 private void cleanupPlayer(MediaPlayer mp) {
1586 } catch (IllegalStateException ex) {
1587 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1593 public void handleMessage(Message msg) {
1594 int baseMsgWhat = getMsgBase(msg.what);
1596 switch (baseMsgWhat) {
1598 case MSG_SET_SYSTEM_VOLUME:
1599 setSystemVolume((VolumeStreamState) msg.obj);
1602 case MSG_PERSIST_VOLUME:
1603 persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0));
1606 case MSG_PERSIST_RINGER_MODE:
1607 persistRingerMode();
1610 case MSG_PERSIST_VIBRATE_SETTING:
1611 persistVibrateSetting();
1614 case MSG_MEDIA_SERVER_DIED:
1615 // Force creation of new IAudioflinger interface
1616 if (!mMediaServerOk) {
1617 Log.e(TAG, "Media server died.");
1618 AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC);
1619 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
1624 case MSG_MEDIA_SERVER_STARTED:
1625 Log.e(TAG, "Media server started.");
1626 // Restore device connection states
1627 Set set = mConnectedDevices.entrySet();
1628 Iterator i = set.iterator();
1630 Map.Entry device = (Map.Entry)i.next();
1631 AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
1632 AudioSystem.DEVICE_STATE_AVAILABLE,
1633 (String)device.getValue());
1636 // Restore call state
1637 AudioSystem.setPhoneState(mMode);
1639 // Restore forced usage for communcations and record
1640 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
1641 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
1643 // Restore stream volumes
1644 int numStreamTypes = AudioSystem.getNumStreamTypes();
1645 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1647 VolumeStreamState streamState = mStreamStates[streamType];
1648 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
1649 if (streamState.muteCount() == 0) {
1650 index = streamState.mIndex;
1654 setStreamVolumeIndex(streamType, index);
1657 // Restore ringer mode
1658 setRingerModeInt(getRingerMode(), false);
1661 case MSG_PLAY_SOUND_EFFECT:
1662 playSoundEffect(msg.arg1, msg.arg2);
1665 case MSG_BTA2DP_DOCK_TIMEOUT:
1666 // msg.obj == address of BTA2DP device
1667 makeA2dpDeviceUnavailableNow( (String) msg.obj );
1673 private class SettingsObserver extends ContentObserver {
1675 SettingsObserver() {
1676 super(new Handler());
1677 mContentResolver.registerContentObserver(Settings.System.getUriFor(
1678 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
1679 mContentResolver.registerContentObserver(Settings.System.getUriFor(
1680 Settings.System.NOTIFICATIONS_USE_RING_VOLUME), false, this);
1684 public void onChange(boolean selfChange) {
1685 super.onChange(selfChange);
1686 synchronized (mSettingsLock) {
1687 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
1688 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
1690 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
1692 * Ensure all stream types that should be affected by ringer mode
1693 * are in the proper state.
1695 mRingerModeAffectedStreams = ringerModeAffectedStreams;
1696 setRingerModeInt(getRingerMode(), false);
1699 int notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
1700 Settings.System.NOTIFICATIONS_USE_RING_VOLUME,
1702 if (notificationsUseRingVolume != mNotificationsUseRingVolume) {
1703 mNotificationsUseRingVolume = notificationsUseRingVolume;
1704 if (mNotificationsUseRingVolume == 1) {
1705 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
1706 mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
1707 System.VOLUME_SETTINGS[AudioSystem.STREAM_RING]);
1709 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
1710 mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
1711 System.VOLUME_SETTINGS[AudioSystem.STREAM_NOTIFICATION]);
1712 // Persist notification volume volume as it was not persisted while aliased to ring volume
1713 // and persist with no delay as there might be registered observers of the persisted
1714 // notification volume.
1715 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
1716 SENDMSG_REPLACE, 1, 1, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0);
1723 private void makeA2dpDeviceAvailable(String address) {
1724 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1725 AudioSystem.DEVICE_STATE_AVAILABLE,
1727 // Reset A2DP suspend state each time a new sink is connected
1728 AudioSystem.setParameters("A2dpSuspended=false");
1729 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
1733 private void makeA2dpDeviceUnavailableNow(String address) {
1734 Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
1735 mContext.sendBroadcast(noisyIntent);
1736 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1737 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1739 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
1742 private void makeA2dpDeviceUnavailableLater(String address) {
1743 // prevent any activity on the A2DP audio output to avoid unwanted
1744 // reconnection of the sink.
1745 AudioSystem.setParameters("A2dpSuspended=true");
1746 // the device will be made unavailable later, so consider it disconnected right away
1747 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
1748 // send the delayed message to make the device unavailable later
1749 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
1750 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
1754 private void cancelA2dpDeviceTimeout() {
1755 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
1758 private boolean hasScheduledA2dpDockTimeout() {
1759 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
1762 /* cache of the address of the last dock the device was connected to */
1763 private String mDockAddress;
1766 * Receiver for misc intent broadcasts the Phone app cares about.
1768 private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
1770 public void onReceive(Context context, Intent intent) {
1771 String action = intent.getAction();
1773 if (action.equals(Intent.ACTION_DOCK_EVENT)) {
1774 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1775 Intent.EXTRA_DOCK_STATE_UNDOCKED);
1777 switch (dockState) {
1778 case Intent.EXTRA_DOCK_STATE_DESK:
1779 config = AudioSystem.FORCE_BT_DESK_DOCK;
1781 case Intent.EXTRA_DOCK_STATE_CAR:
1782 config = AudioSystem.FORCE_BT_CAR_DOCK;
1784 case Intent.EXTRA_DOCK_STATE_UNDOCKED:
1786 config = AudioSystem.FORCE_NONE;
1788 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
1789 } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
1790 int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE,
1791 BluetoothA2dp.STATE_DISCONNECTED);
1792 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1793 String address = btDevice.getAddress();
1794 boolean isConnected = (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
1795 ((String)mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)).equals(address));
1798 state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) {
1799 if (btDevice.isBluetoothDock()) {
1800 if (state == BluetoothA2dp.STATE_DISCONNECTED) {
1801 // introduction of a delay for transient disconnections of docks when
1802 // power is rapidly turned off/on, this message will be canceled if
1803 // we reconnect the dock under a preset delay
1804 makeA2dpDeviceUnavailableLater(address);
1805 // the next time isConnected is evaluated, it will be false for the dock
1808 makeA2dpDeviceUnavailableNow(address);
1810 } else if (!isConnected &&
1811 (state == BluetoothA2dp.STATE_CONNECTED ||
1812 state == BluetoothA2dp.STATE_PLAYING)) {
1813 if (btDevice.isBluetoothDock()) {
1814 // this could be a reconnection after a transient disconnection
1815 cancelA2dpDeviceTimeout();
1816 mDockAddress = address;
1818 // this could be a connection of another A2DP device before the timeout of
1819 // a dock: cancel the dock timeout, and make the dock unavailable now
1820 if(hasScheduledA2dpDockTimeout()) {
1821 cancelA2dpDeviceTimeout();
1822 makeA2dpDeviceUnavailableNow(mDockAddress);
1825 makeA2dpDeviceAvailable(address);
1827 } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
1828 int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
1829 BluetoothHeadset.STATE_ERROR);
1830 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
1831 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1832 String address = null;
1833 if (btDevice != null) {
1834 address = btDevice.getAddress();
1835 BluetoothClass btClass = btDevice.getBluetoothClass();
1836 if (btClass != null) {
1837 switch (btClass.getDeviceClass()) {
1838 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
1839 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
1840 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
1842 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
1843 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
1849 boolean isConnected = (mConnectedDevices.containsKey(device) &&
1850 ((String)mConnectedDevices.get(device)).equals(address));
1852 if (isConnected && state != BluetoothHeadset.STATE_CONNECTED) {
1853 AudioSystem.setDeviceConnectionState(device,
1854 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1856 mConnectedDevices.remove(device);
1857 mBluetoothHeadsetConnected = false;
1858 clearAllScoClients();
1859 } else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) {
1860 AudioSystem.setDeviceConnectionState(device,
1861 AudioSystem.DEVICE_STATE_AVAILABLE,
1863 mConnectedDevices.put(new Integer(device), address);
1864 mBluetoothHeadsetConnected = true;
1866 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
1867 int state = intent.getIntExtra("state", 0);
1868 int microphone = intent.getIntExtra("microphone", 0);
1870 if (microphone != 0) {
1871 boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
1872 if (state == 0 && isConnected) {
1873 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1874 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1876 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
1877 } else if (state == 1 && !isConnected) {
1878 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1879 AudioSystem.DEVICE_STATE_AVAILABLE,
1881 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
1884 boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
1885 if (state == 0 && isConnected) {
1886 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1887 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1889 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
1890 } else if (state == 1 && !isConnected) {
1891 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1892 AudioSystem.DEVICE_STATE_AVAILABLE,
1894 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
1897 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
1898 int state = intent.getIntExtra(BluetoothHeadset.EXTRA_AUDIO_STATE,
1899 BluetoothHeadset.STATE_ERROR);
1900 synchronized (mScoClients) {
1901 if (!mScoClients.isEmpty()) {
1903 case BluetoothHeadset.AUDIO_STATE_CONNECTED:
1904 state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
1906 case BluetoothHeadset.AUDIO_STATE_DISCONNECTED:
1907 state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
1910 state = AudioManager.SCO_AUDIO_STATE_ERROR;
1913 if (state != AudioManager.SCO_AUDIO_STATE_ERROR) {
1914 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
1915 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
1916 mContext.sendStickyBroadcast(newIntent);
1924 //==========================================================================================
1926 //==========================================================================================
1928 /* constant to identify focus stack entry that is used to hold the focus while the phone
1929 * is ringing or during a call
1931 private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
1933 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1935 public void onCallStateChanged(int state, String incomingNumber) {
1936 if (state == TelephonyManager.CALL_STATE_RINGING) {
1937 //Log.v(TAG, " CALL_STATE_RINGING");
1938 int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
1939 if (ringVolume > 0) {
1940 requestAudioFocus(AudioManager.STREAM_RING,
1941 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
1942 null, null /* both allowed to be null only for this clientId */,
1943 IN_VOICE_COMM_FOCUS_ID /*clientId*/);
1945 } else if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
1946 //Log.v(TAG, " CALL_STATE_OFFHOOK");
1947 requestAudioFocus(AudioManager.STREAM_RING,
1948 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
1949 null, null /* both allowed to be null only for this clientId */,
1950 IN_VOICE_COMM_FOCUS_ID /*clientId*/);
1951 } else if (state == TelephonyManager.CALL_STATE_IDLE) {
1952 //Log.v(TAG, " CALL_STATE_IDLE");
1953 abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
1958 private void notifyTopOfAudioFocusStack() {
1959 // notify the top of the stack it gained focus
1960 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
1961 if (canReassignAudioFocus()) {
1963 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
1964 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
1965 } catch (RemoteException e) {
1966 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
1967 e.printStackTrace();
1973 private static class FocusStackEntry {
1974 public int mStreamType = -1;// no stream type
1975 public boolean mIsTransportControlReceiver = false;
1976 public IAudioFocusDispatcher mFocusDispatcher = null;
1977 public IBinder mSourceRef = null;
1978 public String mClientId;
1979 public int mFocusChangeType;
1981 public FocusStackEntry() {
1984 public FocusStackEntry(int streamType, int duration, boolean isTransportControlReceiver,
1985 IAudioFocusDispatcher afl, IBinder source, String id) {
1986 mStreamType = streamType;
1987 mIsTransportControlReceiver = isTransportControlReceiver;
1988 mFocusDispatcher = afl;
1989 mSourceRef = source;
1991 mFocusChangeType = duration;
1995 private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
1999 * Display in the log the current entries in the audio focus stack
2001 private void dumpFocusStack(PrintWriter pw) {
2002 pw.println("\nAudio Focus stack entries:");
2003 synchronized(mFocusStack) {
2004 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2005 while(stackIterator.hasNext()) {
2006 FocusStackEntry fse = stackIterator.next();
2007 pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
2008 + " -- duration: " +fse.mFocusChangeType);
2015 * Remove a focus listener from the focus stack.
2016 * @param focusListenerToRemove the focus listener
2017 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
2018 * focus, notify the next item in the stack it gained focus.
2020 private void removeFocusStackEntry(String clientToRemove, boolean signal) {
2021 // is the current top of the focus stack abandoning focus? (because of death or request)
2022 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
2024 //Log.i(TAG, " removeFocusStackEntry() removing top of stack");
2027 // notify the new top of the stack it gained focus
2028 notifyTopOfAudioFocusStack();
2031 // focus is abandoned by a client that's not at the top of the stack,
2032 // no need to update focus.
2033 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2034 while(stackIterator.hasNext()) {
2035 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
2036 if(fse.mClientId.equals(clientToRemove)) {
2037 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
2039 mFocusStack.remove(fse);
2047 * Remove focus listeners from the focus stack for a particular client.
2049 private void removeFocusStackEntryForClient(IBinder cb) {
2050 // is the owner of the audio focus part of the client to remove?
2051 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
2052 mFocusStack.peek().mSourceRef.equals(cb);
2053 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2054 while(stackIterator.hasNext()) {
2055 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
2056 if(fse.mSourceRef.equals(cb)) {
2057 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for "
2059 mFocusStack.remove(fse);
2062 if (isTopOfStackForClientToRemove) {
2063 // we removed an entry at the top of the stack:
2064 // notify the new top of the stack it gained focus.
2065 notifyTopOfAudioFocusStack();
2071 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
2073 private boolean canReassignAudioFocus() {
2074 // focus requests are rejected during a phone call or when the phone is ringing
2075 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
2076 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
2083 * Inner class to monitor audio focus client deaths, and remove them from the audio focus
2084 * stack if necessary.
2086 private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
2087 private IBinder mCb; // To be notified of client's death
2089 AudioFocusDeathHandler(IBinder cb) {
2093 public void binderDied() {
2094 synchronized(mFocusStack) {
2095 Log.w(TAG, " AudioFocus audio focus client died");
2096 removeFocusStackEntryForClient(mCb);
2100 public IBinder getBinder() {
2106 /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
2107 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
2108 IAudioFocusDispatcher fd, String clientId) {
2109 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId);
2110 // the main stream type for the audio focus request is currently not used. It may
2111 // potentially be used to handle multiple stream type-dependent audio focuses.
2113 // we need a valid binder callback for clients other than the AudioService's phone
2115 if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId) && ((cb == null) || !cb.pingBinder())) {
2116 Log.i(TAG, " AudioFocus DOA client for requestAudioFocus(), exiting");
2117 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
2120 if (!canReassignAudioFocus()) {
2121 return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
2124 synchronized(mFocusStack) {
2125 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
2126 // if focus is already owned by this client and the reason for acquiring the focus
2127 // hasn't changed, don't do anything
2128 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
2129 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2131 // the reason for the audio focus request has changed: remove the current top of
2132 // stack and respond as if we had a new focus owner
2136 // notify current top of stack it is losing focus
2137 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
2139 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
2140 -1 * focusChangeHint, // loss and gain codes are inverse of each other
2141 mFocusStack.peek().mClientId);
2142 } catch (RemoteException e) {
2143 Log.e(TAG, " Failure to signal loss of focus due to "+ e);
2144 e.printStackTrace();
2148 // focus requester might already be somewhere below in the stack, remove it
2149 removeFocusStackEntry(clientId, false);
2151 // push focus requester at the top of the audio focus stack
2152 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb,
2154 }//synchronized(mFocusStack)
2156 // handle the potential premature death of the new holder of the focus
2157 // (premature death == death before abandoning focus) for a client which is not the
2158 // AudioService's phone state listener
2159 if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {
2160 // Register for client death notification
2161 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
2163 cb.linkToDeath(afdh, 0);
2164 } catch (RemoteException e) {
2165 // client has already died!
2166 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death");
2170 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2173 /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
2174 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
2175 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId);
2177 // this will take care of notifying the new focus owner if needed
2178 synchronized(mFocusStack) {
2179 removeFocusStackEntry(clientId, true);
2182 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2186 public void unregisterAudioFocusClient(String clientId) {
2187 synchronized(mFocusStack) {
2188 removeFocusStackEntry(clientId, false);
2193 //==========================================================================================
2195 //==========================================================================================
2197 * Receiver for media button intents. Handles the dispatching of the media button event
2198 * to one of the registered listeners, or if there was none, resumes the intent broadcast
2199 * to the rest of the system.
2201 private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
2203 public void onReceive(Context context, Intent intent) {
2204 String action = intent.getAction();
2205 if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
2208 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
2209 if (event != null) {
2210 // if in a call or ringing, do not break the current phone app behavior
2211 // TODO modify this to let the phone app specifically get the RC focus
2212 // add modify the phone app to take advantage of the new API
2213 if ((getMode() == AudioSystem.MODE_IN_CALL) ||
2214 (getMode() == AudioSystem.MODE_RINGTONE)) {
2217 synchronized(mRCStack) {
2218 if (!mRCStack.empty()) {
2219 // create a new intent specifically aimed at the current registered listener
2220 Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
2221 targetedIntent.putExtras(intent.getExtras());
2222 targetedIntent.setComponent(mRCStack.peek().mReceiverComponent);
2223 // trap the current broadcast
2225 //Log.v(TAG, " Sending intent" + targetedIntent);
2226 context.sendBroadcast(targetedIntent, null);
2233 private static class RemoteControlStackEntry {
2234 public ComponentName mReceiverComponent;// always non null
2235 // TODO implement registration expiration?
2236 //public int mRegistrationTime;
2238 public RemoteControlStackEntry() {
2241 public RemoteControlStackEntry(ComponentName r) {
2242 mReceiverComponent = r;
2246 private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
2250 * Display in the log the current entries in the remote control focus stack
2252 private void dumpRCStack(PrintWriter pw) {
2253 pw.println("\nRemote Control stack entries:");
2254 synchronized(mRCStack) {
2255 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2256 while(stackIterator.hasNext()) {
2257 RemoteControlStackEntry fse = stackIterator.next();
2258 pw.println(" receiver:" + fse.mReceiverComponent);
2265 * Set the new remote control receiver at the top of the RC focus stack
2267 private void pushMediaButtonReceiver(ComponentName newReceiver) {
2268 // already at top of stack?
2269 if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) {
2272 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2273 while(stackIterator.hasNext()) {
2274 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
2275 if(rcse.mReceiverComponent.equals(newReceiver)) {
2276 mRCStack.remove(rcse);
2280 mRCStack.push(new RemoteControlStackEntry(newReceiver));
2285 * Remove the remote control receiver from the RC focus stack
2287 private void removeMediaButtonReceiver(ComponentName newReceiver) {
2288 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2289 while(stackIterator.hasNext()) {
2290 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
2291 if(rcse.mReceiverComponent.equals(newReceiver)) {
2292 mRCStack.remove(rcse);
2299 /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
2300 public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
2301 Log.i(TAG, " Remote Control registerMediaButtonEventReceiver() for " + eventReceiver);
2303 synchronized(mRCStack) {
2304 pushMediaButtonReceiver(eventReceiver);
2308 /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */
2309 public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
2310 Log.i(TAG, " Remote Control unregisterMediaButtonEventReceiver() for " + eventReceiver);
2312 synchronized(mRCStack) {
2313 removeMediaButtonReceiver(eventReceiver);
2319 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2320 // TODO probably a lot more to do here than just the audio focus and remote control stacks