AudioService.java revision 72843377d50a8f95cceaf33d316ab68d9b47f2eb
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
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
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17package android.media;
18
19import android.app.ActivityManagerNative;
20import android.bluetooth.BluetoothA2dp;
21import android.bluetooth.BluetoothAdapter;
22import android.bluetooth.BluetoothClass;
23import android.bluetooth.BluetoothDevice;
24import android.bluetooth.BluetoothHeadset;
25import android.bluetooth.BluetoothProfile;
26import android.content.BroadcastReceiver;
27import android.content.ComponentName;
28import android.content.ContentResolver;
29import android.content.Context;
30import android.content.Intent;
31import android.content.IntentFilter;
32import android.content.pm.PackageManager;
33import android.database.ContentObserver;
34import android.media.MediaPlayer.OnCompletionListener;
35import android.media.MediaPlayer.OnErrorListener;
36import android.os.Binder;
37import android.os.Environment;
38import android.os.Handler;
39import android.os.IBinder;
40import android.os.Looper;
41import android.os.Message;
42import android.os.RemoteException;
43import android.os.ServiceManager;
44import android.os.SystemProperties;
45import android.provider.Settings;
46import android.provider.Settings.System;
47import android.telephony.PhoneStateListener;
48import android.telephony.TelephonyManager;
49import android.util.Log;
50import android.view.KeyEvent;
51import android.view.VolumePanel;
52
53import com.android.internal.telephony.ITelephony;
54
55import java.io.FileDescriptor;
56import java.io.IOException;
57import java.io.PrintWriter;
58import java.util.ArrayList;
59import java.util.HashMap;
60import java.util.Iterator;
61import java.util.List;
62import java.util.Map;
63import java.util.NoSuchElementException;
64import java.util.Set;
65import java.util.Stack;
66
67/**
68 * The implementation of the volume manager service.
69 * <p>
70 * This implementation focuses on delivering a responsive UI. Most methods are
71 * asynchronous to external calls. For example, the task of setting a volume
72 * will update our internal state, but in a separate thread will set the system
73 * volume and later persist to the database. Similarly, setting the ringer mode
74 * will update the state and broadcast a change and in a separate thread later
75 * persist the ringer mode.
76 *
77 * @hide
78 */
79public class AudioService extends IAudioService.Stub {
80
81    private static final String TAG = "AudioService";
82
83    /** How long to delay before persisting a change in volume/ringer mode. */
84    private static final int PERSIST_DELAY = 3000;
85
86    private Context mContext;
87    private ContentResolver mContentResolver;
88    private boolean mVoiceCapable;
89
90    /** The UI */
91    private VolumePanel mVolumePanel;
92
93    // sendMsg() flags
94    /** Used when a message should be shared across all stream types. */
95    private static final int SHARED_MSG = -1;
96    /** If the msg is already queued, replace it with this one. */
97    private static final int SENDMSG_REPLACE = 0;
98    /** If the msg is already queued, ignore this one and leave the old. */
99    private static final int SENDMSG_NOOP = 1;
100    /** If the msg is already queued, queue this one and leave the old. */
101    private static final int SENDMSG_QUEUE = 2;
102
103    // AudioHandler message.whats
104    private static final int MSG_SET_SYSTEM_VOLUME = 0;
105    private static final int MSG_PERSIST_VOLUME = 1;
106    private static final int MSG_PERSIST_RINGER_MODE = 3;
107    private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
108    private static final int MSG_MEDIA_SERVER_DIED = 5;
109    private static final int MSG_MEDIA_SERVER_STARTED = 6;
110    private static final int MSG_PLAY_SOUND_EFFECT = 7;
111    private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8;
112    private static final int MSG_LOAD_SOUND_EFFECTS = 9;
113    private static final int MSG_SET_FORCE_USE = 10;
114    private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11;
115
116
117    private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
118
119    /** @see AudioSystemThread */
120    private AudioSystemThread mAudioSystemThread;
121    /** @see AudioHandler */
122    private AudioHandler mAudioHandler;
123    /** @see VolumeStreamState */
124    private VolumeStreamState[] mStreamStates;
125    private SettingsObserver mSettingsObserver;
126
127    private int mMode;
128    private Object mSettingsLock = new Object();
129    private boolean mMediaServerOk;
130
131    private SoundPool mSoundPool;
132    private Object mSoundEffectsLock = new Object();
133    private static final int NUM_SOUNDPOOL_CHANNELS = 4;
134    private static final int SOUND_EFFECT_VOLUME = 1000;
135
136    /* Sound effect file names  */
137    private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
138    private static final String[] SOUND_EFFECT_FILES = new String[] {
139        "Effect_Tick.ogg",
140        "KeypressStandard.ogg",
141        "KeypressSpacebar.ogg",
142        "KeypressDelete.ogg",
143        "KeypressReturn.ogg"
144    };
145
146    /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
147     * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
148     * uses soundpool (second column) */
149    private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
150        {0, -1},  // FX_KEY_CLICK
151        {0, -1},  // FX_FOCUS_NAVIGATION_UP
152        {0, -1},  // FX_FOCUS_NAVIGATION_DOWN
153        {0, -1},  // FX_FOCUS_NAVIGATION_LEFT
154        {0, -1},  // FX_FOCUS_NAVIGATION_RIGHT
155        {1, -1},  // FX_KEYPRESS_STANDARD
156        {2, -1},  // FX_KEYPRESS_SPACEBAR
157        {3, -1},  // FX_FOCUS_DELETE
158        {4, -1}   // FX_FOCUS_RETURN
159    };
160
161   /** @hide Maximum volume index values for audio streams */
162    private int[] MAX_STREAM_VOLUME = new int[] {
163        5,  // STREAM_VOICE_CALL
164        7,  // STREAM_SYSTEM
165        7,  // STREAM_RING
166        15, // STREAM_MUSIC
167        7,  // STREAM_ALARM
168        7,  // STREAM_NOTIFICATION
169        15, // STREAM_BLUETOOTH_SCO
170        7,  // STREAM_SYSTEM_ENFORCED
171        15, // STREAM_DTMF
172        15  // STREAM_TTS
173    };
174    /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
175     * of another stream: This avoids multiplying the volume settings for hidden
176     * stream types that follow other stream behavior for volume settings
177     * NOTE: do not create loops in aliases! */
178    private int[] STREAM_VOLUME_ALIAS = new int[] {
179        AudioSystem.STREAM_VOICE_CALL,  // STREAM_VOICE_CALL
180        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM
181        AudioSystem.STREAM_RING,  // STREAM_RING
182        AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
183        AudioSystem.STREAM_ALARM,  // STREAM_ALARM
184        AudioSystem.STREAM_NOTIFICATION,  // STREAM_NOTIFICATION
185        AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
186        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM_ENFORCED
187        AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
188        AudioSystem.STREAM_MUSIC  // STREAM_TTS
189    };
190
191    private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
192        public void onError(int error) {
193            switch (error) {
194            case AudioSystem.AUDIO_STATUS_SERVER_DIED:
195                if (mMediaServerOk) {
196                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
197                            null, 1500);
198                    mMediaServerOk = false;
199                }
200                break;
201            case AudioSystem.AUDIO_STATUS_OK:
202                if (!mMediaServerOk) {
203                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
204                            null, 0);
205                    mMediaServerOk = true;
206                }
207                break;
208            default:
209                break;
210            }
211       }
212    };
213
214    /**
215     * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
216     * {@link AudioManager#RINGER_MODE_SILENT}, or
217     * {@link AudioManager#RINGER_MODE_VIBRATE}.
218     */
219    private int mRingerMode;
220
221    /** @see System#MODE_RINGER_STREAMS_AFFECTED */
222    private int mRingerModeAffectedStreams;
223
224    // Streams currently muted by ringer mode
225    private int mRingerModeMutedStreams;
226
227    /** @see System#MUTE_STREAMS_AFFECTED */
228    private int mMuteAffectedStreams;
229
230    /**
231     * Has multiple bits per vibrate type to indicate the type's vibrate
232     * setting. See {@link #setVibrateSetting(int, int)}.
233     * <p>
234     * NOTE: This is not the final decision of whether vibrate is on/off for the
235     * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
236     */
237    private int mVibrateSetting;
238
239    /** @see System#NOTIFICATIONS_USE_RING_VOLUME */
240    private int mNotificationsUseRingVolume;
241
242    // Broadcast receiver for device connections intent broadcasts
243    private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
244
245    //  Broadcast receiver for media button broadcasts (separate from mReceiver to
246    //  independently change its priority)
247    private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
248
249    // Used to alter media button redirection when the phone is ringing.
250    private boolean mIsRinging = false;
251
252    // Devices currently connected
253    private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
254
255    // Forced device usage for communications
256    private int mForcedUseForComm;
257
258    // List of binder death handlers for setMode() client processes.
259    // The last process to have called setMode() is at the top of the list.
260    private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
261
262    // List of clients having issued a SCO start request
263    private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
264
265    // BluetoothHeadset API to control SCO connection
266    private BluetoothHeadset mBluetoothHeadset;
267
268    // Bluetooth headset device
269    private BluetoothDevice mBluetoothHeadsetDevice;
270
271    // Indicate if SCO audio connection is currently active and if the initiator is
272    // audio service (internal) or bluetooth headset (external)
273    private int mScoAudioState;
274    // SCO audio state is not active
275    private static final int SCO_STATE_INACTIVE = 0;
276    // SCO audio state is active or starting due to a local request to start a virtual call
277    private static final int SCO_STATE_ACTIVE_INTERNAL = 1;
278    // SCO audio state is active due to an action in BT handsfree (either voice recognition or
279    // in call audio)
280    private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
281
282    // true if boot sequence has been completed
283    private boolean mBootCompleted;
284    // listener for SoundPool sample load completion indication
285    private SoundPoolCallback mSoundPoolCallBack;
286    // thread for SoundPool listener
287    private SoundPoolListenerThread mSoundPoolListenerThread;
288    // message looper for SoundPool listener
289    private Looper mSoundPoolLooper = null;
290    // default volume applied to sound played with playSoundEffect()
291    private static final int SOUND_EFFECT_DEFAULT_VOLUME_DB = -20;
292    // volume applied to sound played with playSoundEffect() read from ro.config.sound_fx_volume
293    private int SOUND_EFFECT_VOLUME_DB;
294    // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification
295    // stopped
296    private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
297    // previous volume adjustment direction received by checkForRingerModeChange()
298    private int mPrevVolDirection = AudioManager.ADJUST_SAME;
299
300    ///////////////////////////////////////////////////////////////////////////
301    // Construction
302    ///////////////////////////////////////////////////////////////////////////
303
304    /** @hide */
305    public AudioService(Context context) {
306        mContext = context;
307        mContentResolver = context.getContentResolver();
308        mVoiceCapable = mContext.getResources().getBoolean(
309                com.android.internal.R.bool.config_voice_capable);
310
311       // Intialized volume
312        MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
313            "ro.config.vc_call_vol_steps",
314           MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
315
316        SOUND_EFFECT_VOLUME_DB = SystemProperties.getInt(
317                "ro.config.sound_fx_volume",
318                SOUND_EFFECT_DEFAULT_VOLUME_DB);
319
320        mVolumePanel = new VolumePanel(context, this);
321        mSettingsObserver = new SettingsObserver();
322        mForcedUseForComm = AudioSystem.FORCE_NONE;
323        createAudioSystemThread();
324        readPersistedSettings();
325        createStreamStates();
326        // Call setMode() to initialize mSetModeDeathHandlers
327        mMode = AudioSystem.MODE_INVALID;
328        setMode(AudioSystem.MODE_NORMAL, null);
329        mMediaServerOk = true;
330
331        // Call setRingerModeInt() to apply correct mute
332        // state on streams affected by ringer mode.
333        mRingerModeMutedStreams = 0;
334        setRingerModeInt(getRingerMode(), false);
335
336        AudioSystem.setErrorCallback(mAudioSystemCallback);
337
338        mBluetoothHeadsetDevice = null;
339        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
340        if (adapter != null) {
341            adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
342                                    BluetoothProfile.HEADSET);
343        }
344
345        // Register for device connection intent broadcasts.
346        IntentFilter intentFilter =
347                new IntentFilter(Intent.ACTION_HEADSET_PLUG);
348
349        intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
350        intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
351        intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
352        intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
353        intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
354        intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
355        intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
356        intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
357        context.registerReceiver(mReceiver, intentFilter);
358
359        // Register for package removal intent broadcasts for media button receiver persistence
360        IntentFilter pkgFilter = new IntentFilter();
361        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
362        pkgFilter.addDataScheme("package");
363        context.registerReceiver(mReceiver, pkgFilter);
364
365        // Register for media button intent broadcasts.
366        intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
367        intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
368        context.registerReceiver(mMediaButtonReceiver, intentFilter);
369
370        // Register for phone state monitoring
371        TelephonyManager tmgr = (TelephonyManager)
372                context.getSystemService(Context.TELEPHONY_SERVICE);
373        tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
374    }
375
376    private void createAudioSystemThread() {
377        mAudioSystemThread = new AudioSystemThread();
378        mAudioSystemThread.start();
379        waitForAudioHandlerCreation();
380    }
381
382    /** Waits for the volume handler to be created by the other thread. */
383    private void waitForAudioHandlerCreation() {
384        synchronized(this) {
385            while (mAudioHandler == null) {
386                try {
387                    // Wait for mAudioHandler to be set by the other thread
388                    wait();
389                } catch (InterruptedException e) {
390                    Log.e(TAG, "Interrupted while waiting on volume handler.");
391                }
392            }
393        }
394    }
395
396    private void createStreamStates() {
397        int numStreamTypes = AudioSystem.getNumStreamTypes();
398        VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
399
400        for (int i = 0; i < numStreamTypes; i++) {
401            streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
402        }
403
404        // Correct stream index values for streams with aliases
405        for (int i = 0; i < numStreamTypes; i++) {
406            if (STREAM_VOLUME_ALIAS[i] != i) {
407                int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
408                streams[i].mIndex = streams[i].getValidIndex(index);
409                setStreamVolumeIndex(i, index);
410                index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
411                streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
412            }
413        }
414    }
415
416    private void readPersistedSettings() {
417        final ContentResolver cr = mContentResolver;
418
419        mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
420
421        mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
422
423        mRingerModeAffectedStreams = Settings.System.getInt(cr,
424                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
425                ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
426                 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)|
427                 (1 << AudioSystem.STREAM_MUSIC)));
428
429        if (!mVoiceCapable) {
430            mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
431        }
432        mMuteAffectedStreams = System.getInt(cr,
433                System.MUTE_STREAMS_AFFECTED,
434                ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
435
436        if (mVoiceCapable) {
437            mNotificationsUseRingVolume = System.getInt(cr,
438                    Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1);
439        } else {
440            mNotificationsUseRingVolume = 1;
441        }
442
443        if (mNotificationsUseRingVolume == 1) {
444            STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
445        }
446        // Each stream will read its own persisted settings
447
448        // Broadcast the sticky intent
449        broadcastRingerMode();
450
451        // Broadcast vibrate settings
452        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
453        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
454
455        // Restore the default media button receiver from the system settings
456        restoreMediaButtonReceiver();
457    }
458
459    private void setStreamVolumeIndex(int stream, int index) {
460        AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
461    }
462
463    private int rescaleIndex(int index, int srcStream, int dstStream) {
464        return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
465    }
466
467    ///////////////////////////////////////////////////////////////////////////
468    // IPC methods
469    ///////////////////////////////////////////////////////////////////////////
470
471    /** @see AudioManager#adjustVolume(int, int) */
472    public void adjustVolume(int direction, int flags) {
473        adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
474    }
475
476    /** @see AudioManager#adjustVolume(int, int, int) */
477    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
478
479        int streamType;
480        if ((flags & AudioManager.FLAG_FORCE_STREAM) != 0) {
481            streamType = suggestedStreamType;
482        } else {
483            streamType = getActiveStreamType(suggestedStreamType);
484        }
485
486        // Don't play sound on other streams
487        if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
488            flags &= ~AudioManager.FLAG_PLAY_SOUND;
489        }
490
491        adjustStreamVolume(streamType, direction, flags);
492    }
493
494    /** @see AudioManager#adjustStreamVolume(int, int, int) */
495    public void adjustStreamVolume(int streamType, int direction, int flags) {
496        ensureValidDirection(direction);
497        ensureValidStreamType(streamType);
498
499
500        VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
501        final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
502        boolean adjustVolume = true;
503
504        // If either the client forces allowing ringer modes for this adjustment,
505        // or the stream type is one that is affected by ringer modes
506        if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
507             (!mVoiceCapable && streamType != AudioSystem.STREAM_VOICE_CALL &&
508               streamType != AudioSystem.STREAM_BLUETOOTH_SCO) ||
509                (mVoiceCapable && streamType == AudioSystem.STREAM_RING)) {
510            // Check if the ringer mode changes with this volume adjustment. If
511            // it does, it will handle adjusting the volume, so we won't below
512            adjustVolume = checkForRingerModeChange(oldIndex, direction);
513        }
514
515        // If stream is muted, adjust last audible index only
516        int index;
517        if (streamState.muteCount() != 0) {
518            if (adjustVolume) {
519                streamState.adjustLastAudibleIndex(direction);
520                // Post a persist volume msg
521                sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
522                        SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
523            }
524            index = streamState.mLastAudibleIndex;
525        } else {
526            if (adjustVolume && streamState.adjustIndex(direction)) {
527                // Post message to set system volume (it in turn will post a message
528                // to persist). Do not change volume if stream is muted.
529                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,
530                        streamState, 0);
531            }
532            index = streamState.mIndex;
533        }
534
535        sendVolumeUpdate(streamType, oldIndex, index, flags);
536    }
537
538    /** @see AudioManager#setStreamVolume(int, int, int) */
539    public void setStreamVolume(int streamType, int index, int flags) {
540        ensureValidStreamType(streamType);
541        VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
542
543        final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
544
545        index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
546        setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
547
548        index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
549
550        sendVolumeUpdate(streamType, oldIndex, index, flags);
551    }
552
553    // UI update and Broadcast Intent
554    private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
555        if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
556            streamType = AudioSystem.STREAM_NOTIFICATION;
557        }
558
559        mVolumePanel.postVolumeChanged(streamType, flags);
560
561        oldIndex = (oldIndex + 5) / 10;
562        index = (index + 5) / 10;
563        Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
564        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
565        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
566        intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
567        mContext.sendBroadcast(intent);
568    }
569
570    /**
571     * Sets the stream state's index, and posts a message to set system volume.
572     * This will not call out to the UI. Assumes a valid stream type.
573     *
574     * @param streamType Type of the stream
575     * @param index Desired volume index of the stream
576     * @param force If true, set the volume even if the desired volume is same
577     * as the current volume.
578     * @param lastAudible If true, stores new index as last audible one
579     */
580    private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
581        VolumeStreamState streamState = mStreamStates[streamType];
582
583        // If stream is muted, set last audible index only
584        if (streamState.muteCount() != 0) {
585            // Do not allow last audible index to be 0
586            if (index != 0) {
587                streamState.setLastAudibleIndex(index);
588                // Post a persist volume msg
589                sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
590                        SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
591            }
592        } else {
593            if (streamState.setIndex(index, lastAudible) || force) {
594                // Post message to set system volume (it in turn will post a message
595                // to persist).
596                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
597                        streamState, 0);
598            }
599        }
600    }
601
602    /** @see AudioManager#setStreamSolo(int, boolean) */
603    public void setStreamSolo(int streamType, boolean state, IBinder cb) {
604        for (int stream = 0; stream < mStreamStates.length; stream++) {
605            if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
606            // Bring back last audible volume
607            mStreamStates[stream].mute(cb, state);
608         }
609    }
610
611    /** @see AudioManager#setStreamMute(int, boolean) */
612    public void setStreamMute(int streamType, boolean state, IBinder cb) {
613        if (isStreamAffectedByMute(streamType)) {
614            mStreamStates[streamType].mute(cb, state);
615        }
616    }
617
618    /** get stream mute state. */
619    public boolean isStreamMute(int streamType) {
620        return (mStreamStates[streamType].muteCount() != 0);
621    }
622
623    /** @see AudioManager#getStreamVolume(int) */
624    public int getStreamVolume(int streamType) {
625        ensureValidStreamType(streamType);
626        return (mStreamStates[streamType].mIndex + 5) / 10;
627    }
628
629    /** @see AudioManager#getStreamMaxVolume(int) */
630    public int getStreamMaxVolume(int streamType) {
631        ensureValidStreamType(streamType);
632        return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
633    }
634
635
636    /** Get last audible volume before stream was muted. */
637    public int getLastAudibleStreamVolume(int streamType) {
638        ensureValidStreamType(streamType);
639        return (mStreamStates[streamType].mLastAudibleIndex + 5) / 10;
640    }
641
642    /** @see AudioManager#getRingerMode() */
643    public int getRingerMode() {
644        return mRingerMode;
645    }
646
647    /** @see AudioManager#setRingerMode(int) */
648    public void setRingerMode(int ringerMode) {
649        synchronized (mSettingsLock) {
650            if (ringerMode != mRingerMode) {
651                setRingerModeInt(ringerMode, true);
652                // Send sticky broadcast
653                broadcastRingerMode();
654            }
655        }
656    }
657
658    private void setRingerModeInt(int ringerMode, boolean persist) {
659        mRingerMode = ringerMode;
660
661        // Mute stream if not previously muted by ringer mode and ringer mode
662        // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
663        // Unmute stream if previously muted by ringer mode and ringer mode
664        // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
665        int numStreamTypes = AudioSystem.getNumStreamTypes();
666        for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
667            if (isStreamMutedByRingerMode(streamType)) {
668                if (!isStreamAffectedByRingerMode(streamType) ||
669                    mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
670                    mStreamStates[streamType].mute(null, false);
671                    mRingerModeMutedStreams &= ~(1 << streamType);
672                }
673            } else {
674                if (isStreamAffectedByRingerMode(streamType) &&
675                    mRingerMode != AudioManager.RINGER_MODE_NORMAL) {
676                   mStreamStates[streamType].mute(null, true);
677                   mRingerModeMutedStreams |= (1 << streamType);
678               }
679            }
680        }
681
682        // Post a persist ringer mode msg
683        if (persist) {
684            sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
685                    SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
686        }
687    }
688
689    /** @see AudioManager#shouldVibrate(int) */
690    public boolean shouldVibrate(int vibrateType) {
691
692        switch (getVibrateSetting(vibrateType)) {
693
694            case AudioManager.VIBRATE_SETTING_ON:
695                return mRingerMode != AudioManager.RINGER_MODE_SILENT;
696
697            case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
698                return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
699
700            case AudioManager.VIBRATE_SETTING_OFF:
701                // return false, even for incoming calls
702                return false;
703
704            default:
705                return false;
706        }
707    }
708
709    /** @see AudioManager#getVibrateSetting(int) */
710    public int getVibrateSetting(int vibrateType) {
711        return (mVibrateSetting >> (vibrateType * 2)) & 3;
712    }
713
714    /** @see AudioManager#setVibrateSetting(int, int) */
715    public void setVibrateSetting(int vibrateType, int vibrateSetting) {
716
717        mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
718
719        // Broadcast change
720        broadcastVibrateSetting(vibrateType);
721
722        // Post message to set ringer mode (it in turn will post a message
723        // to persist)
724        sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
725                null, 0);
726    }
727
728    /**
729     * @see #setVibrateSetting(int, int)
730     */
731    public static int getValueForVibrateSetting(int existingValue, int vibrateType,
732            int vibrateSetting) {
733
734        // First clear the existing setting. Each vibrate type has two bits in
735        // the value. Note '3' is '11' in binary.
736        existingValue &= ~(3 << (vibrateType * 2));
737
738        // Set into the old value
739        existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
740
741        return existingValue;
742    }
743
744    private class SetModeDeathHandler implements IBinder.DeathRecipient {
745        private IBinder mCb; // To be notified of client's death
746        private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
747
748        SetModeDeathHandler(IBinder cb) {
749            mCb = cb;
750        }
751
752        public void binderDied() {
753            synchronized(mSetModeDeathHandlers) {
754                Log.w(TAG, "setMode() client died");
755                int index = mSetModeDeathHandlers.indexOf(this);
756                if (index < 0) {
757                    Log.w(TAG, "unregistered setMode() client died");
758                } else {
759                    mSetModeDeathHandlers.remove(this);
760                    // If dead client was a the top of client list,
761                    // apply next mode in the stack
762                    if (index == 0) {
763                        // mSetModeDeathHandlers is never empty as the initial entry
764                        // created when AudioService starts is never removed
765                        SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0);
766                        int mode = hdlr.getMode();
767                        if (AudioService.this.mMode != mode) {
768                            if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
769                                AudioService.this.mMode = mode;
770                                if (mode != AudioSystem.MODE_NORMAL) {
771                                    synchronized(mScoClients) {
772                                        checkScoAudioState();
773                                        if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
774                                            mBluetoothHeadset.stopVoiceRecognition(
775                                                    mBluetoothHeadsetDevice);
776                                            mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
777                                                    mBluetoothHeadsetDevice);
778                                        } else {
779                                            clearAllScoClients(mCb, true);
780                                        }
781                                    }
782                                }
783                            }
784                        }
785                    }
786                }
787            }
788        }
789
790        public void setMode(int mode) {
791            mMode = mode;
792        }
793
794        public int getMode() {
795            return mMode;
796        }
797
798        public IBinder getBinder() {
799            return mCb;
800        }
801    }
802
803    /** @see AudioManager#setMode(int) */
804    public void setMode(int mode, IBinder cb) {
805        if (!checkAudioSettingsPermission("setMode()")) {
806            return;
807        }
808
809        if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
810            return;
811        }
812
813        synchronized (mSettingsLock) {
814            if (mode == AudioSystem.MODE_CURRENT) {
815                mode = mMode;
816            }
817            if (mode != mMode) {
818
819                // automatically handle audio focus for mode changes
820                handleFocusForCalls(mMode, mode, cb);
821
822                if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
823                    mMode = mode;
824
825                    synchronized(mSetModeDeathHandlers) {
826                        SetModeDeathHandler hdlr = null;
827                        Iterator iter = mSetModeDeathHandlers.iterator();
828                        while (iter.hasNext()) {
829                            SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
830                            if (h.getBinder() == cb) {
831                                hdlr = h;
832                                // Remove from client list so that it is re-inserted at top of list
833                                iter.remove();
834                                break;
835                            }
836                        }
837                        if (hdlr == null) {
838                            hdlr = new SetModeDeathHandler(cb);
839                            // cb is null when setMode() is called by AudioService constructor
840                            if (cb != null) {
841                                // Register for client death notification
842                                try {
843                                    cb.linkToDeath(hdlr, 0);
844                                } catch (RemoteException e) {
845                                    // Client has died!
846                                    Log.w(TAG, "setMode() could not link to "+cb+" binder death");
847                                }
848                            }
849                        }
850                        // Last client to call setMode() is always at top of client list
851                        // as required by SetModeDeathHandler.binderDied()
852                        mSetModeDeathHandlers.add(0, hdlr);
853                        hdlr.setMode(mode);
854                    }
855
856                    // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
857                    // SCO connections not started by the application changing the mode
858                    if (mode != AudioSystem.MODE_NORMAL) {
859                        synchronized(mScoClients) {
860                            checkScoAudioState();
861                            if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) {
862                                mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice);
863                                mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
864                                    mBluetoothHeadsetDevice);
865                            } else {
866                                clearAllScoClients(cb, true);
867                            }
868                        }
869                    }
870                }
871            }
872            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
873            int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
874            setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false);
875        }
876    }
877
878    /** pre-condition: oldMode != newMode */
879    private void handleFocusForCalls(int oldMode, int newMode, IBinder cb) {
880        // if ringing
881        if (newMode == AudioSystem.MODE_RINGTONE) {
882            // if not ringing silently
883            int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
884            if (ringVolume > 0) {
885                // request audio focus for the communication focus entry
886                requestAudioFocus(AudioManager.STREAM_RING,
887                        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
888                        null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
889                        IN_VOICE_COMM_FOCUS_ID /*clientId*/);
890
891            }
892        }
893        // if entering call
894        else if ((newMode == AudioSystem.MODE_IN_CALL)
895                || (newMode == AudioSystem.MODE_IN_COMMUNICATION)) {
896            // request audio focus for the communication focus entry
897            // (it's ok if focus was already requested during ringing)
898            requestAudioFocus(AudioManager.STREAM_RING,
899                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
900                    null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
901                    IN_VOICE_COMM_FOCUS_ID /*clientId*/);
902        }
903        // if exiting call
904        else if (newMode == AudioSystem.MODE_NORMAL) {
905            // abandon audio focus for communication focus entry
906            abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
907        }
908    }
909
910    /** @see AudioManager#getMode() */
911    public int getMode() {
912        return mMode;
913    }
914
915    /** @see AudioManager#playSoundEffect(int) */
916    public void playSoundEffect(int effectType) {
917        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
918                effectType, -1, null, 0);
919    }
920
921    /** @see AudioManager#playSoundEffect(int, float) */
922    public void playSoundEffectVolume(int effectType, float volume) {
923        loadSoundEffects();
924        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
925                effectType, (int) (volume * 1000), null, 0);
926    }
927
928    /**
929     * Loads samples into the soundpool.
930     * This method must be called at when sound effects are enabled
931     */
932    public boolean loadSoundEffects() {
933        int status;
934
935        synchronized (mSoundEffectsLock) {
936            if (!mBootCompleted) {
937                Log.w(TAG, "loadSoundEffects() called before boot complete");
938                return false;
939            }
940
941            if (mSoundPool != null) {
942                return true;
943            }
944            mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
945            if (mSoundPool == null) {
946                Log.w(TAG, "loadSoundEffects() could not allocate sound pool");
947                return false;
948            }
949
950            try {
951                mSoundPoolCallBack = null;
952                mSoundPoolListenerThread = new SoundPoolListenerThread();
953                mSoundPoolListenerThread.start();
954                // Wait for mSoundPoolCallBack to be set by the other thread
955                mSoundEffectsLock.wait();
956            } catch (InterruptedException e) {
957                Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
958            }
959
960            if (mSoundPoolCallBack == null) {
961                Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
962                if (mSoundPoolLooper != null) {
963                    mSoundPoolLooper.quit();
964                    mSoundPoolLooper = null;
965                }
966                mSoundPoolListenerThread = null;
967                mSoundPool.release();
968                mSoundPool = null;
969                return false;
970            }
971            /*
972             * poolId table: The value -1 in this table indicates that corresponding
973             * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
974             * Once loaded, the value in poolId is the sample ID and the same
975             * sample can be reused for another effect using the same file.
976             */
977            int[] poolId = new int[SOUND_EFFECT_FILES.length];
978            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
979                poolId[fileIdx] = -1;
980            }
981            /*
982             * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
983             * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
984             * this indicates we have a valid sample loaded for this effect.
985             */
986
987            int lastSample = 0;
988            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
989                // Do not load sample if this effect uses the MediaPlayer
990                if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
991                    continue;
992                }
993                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
994                    String filePath = Environment.getRootDirectory()
995                            + SOUND_EFFECTS_PATH
996                            + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
997                    int sampleId = mSoundPool.load(filePath, 0);
998                    if (sampleId <= 0) {
999                        Log.w(TAG, "Soundpool could not load file: "+filePath);
1000                    } else {
1001                        SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
1002                        poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
1003                        lastSample = sampleId;
1004                    }
1005                } else {
1006                    SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
1007                }
1008            }
1009            // wait for all samples to be loaded
1010            if (lastSample != 0) {
1011                mSoundPoolCallBack.setLastSample(lastSample);
1012
1013                try {
1014                    mSoundEffectsLock.wait();
1015                    status = mSoundPoolCallBack.status();
1016                } catch (java.lang.InterruptedException e) {
1017                    Log.w(TAG, "Interrupted while waiting sound pool callback.");
1018                    status = -1;
1019                }
1020            } else {
1021                status = -1;
1022            }
1023
1024            if (mSoundPoolLooper != null) {
1025                mSoundPoolLooper.quit();
1026                mSoundPoolLooper = null;
1027            }
1028            mSoundPoolListenerThread = null;
1029            if (status != 0) {
1030                Log.w(TAG,
1031                        "loadSoundEffects(), Error "
1032                                + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
1033                                + " while loading samples");
1034                for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1035                    if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
1036                        SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1037                    }
1038                }
1039
1040                mSoundPool.release();
1041                mSoundPool = null;
1042            }
1043        }
1044        return (status == 0);
1045    }
1046
1047    /**
1048     *  Unloads samples from the sound pool.
1049     *  This method can be called to free some memory when
1050     *  sound effects are disabled.
1051     */
1052    public void unloadSoundEffects() {
1053        synchronized (mSoundEffectsLock) {
1054            if (mSoundPool == null) {
1055                return;
1056            }
1057
1058            mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
1059            mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
1060
1061            int[] poolId = new int[SOUND_EFFECT_FILES.length];
1062            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1063                poolId[fileIdx] = 0;
1064            }
1065
1066            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1067                if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
1068                    continue;
1069                }
1070                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
1071                    mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
1072                    SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1073                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
1074                }
1075            }
1076            mSoundPool.release();
1077            mSoundPool = null;
1078        }
1079    }
1080
1081    class SoundPoolListenerThread extends Thread {
1082        public SoundPoolListenerThread() {
1083            super("SoundPoolListenerThread");
1084        }
1085
1086        @Override
1087        public void run() {
1088
1089            Looper.prepare();
1090            mSoundPoolLooper = Looper.myLooper();
1091
1092            synchronized (mSoundEffectsLock) {
1093                if (mSoundPool != null) {
1094                    mSoundPoolCallBack = new SoundPoolCallback();
1095                    mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
1096                }
1097                mSoundEffectsLock.notify();
1098            }
1099            Looper.loop();
1100        }
1101    }
1102
1103    private final class SoundPoolCallback implements
1104            android.media.SoundPool.OnLoadCompleteListener {
1105
1106        int mStatus;
1107        int mLastSample;
1108
1109        public int status() {
1110            return mStatus;
1111        }
1112
1113        public void setLastSample(int sample) {
1114            mLastSample = sample;
1115        }
1116
1117        public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
1118            synchronized (mSoundEffectsLock) {
1119                if (status != 0) {
1120                    mStatus = status;
1121                }
1122                if (sampleId == mLastSample) {
1123                    mSoundEffectsLock.notify();
1124                }
1125            }
1126        }
1127    }
1128
1129    /** @see AudioManager#reloadAudioSettings() */
1130    public void reloadAudioSettings() {
1131        // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
1132        readPersistedSettings();
1133
1134        // restore volume settings
1135        int numStreamTypes = AudioSystem.getNumStreamTypes();
1136        for (int streamType = 0; streamType < numStreamTypes; streamType++) {
1137            VolumeStreamState streamState = mStreamStates[streamType];
1138
1139            String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
1140            String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1141            int index = Settings.System.getInt(mContentResolver,
1142                                           settingName,
1143                                           AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1144            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
1145                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
1146            } else {
1147                index *= 10;
1148            }
1149            streamState.mIndex = streamState.getValidIndex(index);
1150
1151            index = (index + 5) / 10;
1152            index = Settings.System.getInt(mContentResolver,
1153                                            lastAudibleSettingName,
1154                                            (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1155            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
1156                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
1157            } else {
1158                index *= 10;
1159            }
1160            streamState.mLastAudibleIndex = streamState.getValidIndex(index);
1161
1162            // unmute stream that was muted but is not affect by mute anymore
1163            if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
1164                int size = streamState.mDeathHandlers.size();
1165                for (int i = 0; i < size; i++) {
1166                    streamState.mDeathHandlers.get(i).mMuteCount = 1;
1167                    streamState.mDeathHandlers.get(i).mute(false);
1168                }
1169            }
1170            // apply stream volume
1171            if (streamState.muteCount() == 0) {
1172                setStreamVolumeIndex(streamType, streamState.mIndex);
1173            }
1174        }
1175
1176        // apply new ringer mode
1177        setRingerModeInt(getRingerMode(), false);
1178    }
1179
1180    /** @see AudioManager#setSpeakerphoneOn() */
1181    public void setSpeakerphoneOn(boolean on){
1182        if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
1183            return;
1184        }
1185        mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
1186
1187        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1188                AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
1189    }
1190
1191    /** @see AudioManager#isSpeakerphoneOn() */
1192    public boolean isSpeakerphoneOn() {
1193        return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
1194    }
1195
1196    /** @see AudioManager#setBluetoothScoOn() */
1197    public void setBluetoothScoOn(boolean on){
1198        if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
1199            return;
1200        }
1201        mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
1202
1203        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1204                AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
1205        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1206                AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
1207    }
1208
1209    /** @see AudioManager#isBluetoothScoOn() */
1210    public boolean isBluetoothScoOn() {
1211        return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
1212    }
1213
1214    /** @see AudioManager#startBluetoothSco() */
1215    public void startBluetoothSco(IBinder cb){
1216        if (!checkAudioSettingsPermission("startBluetoothSco()")) {
1217            return;
1218        }
1219        ScoClient client = getScoClient(cb, true);
1220        client.incCount();
1221    }
1222
1223    /** @see AudioManager#stopBluetoothSco() */
1224    public void stopBluetoothSco(IBinder cb){
1225        if (!checkAudioSettingsPermission("stopBluetoothSco()")) {
1226            return;
1227        }
1228        ScoClient client = getScoClient(cb, false);
1229        if (client != null) {
1230            client.decCount();
1231        }
1232    }
1233
1234    private class ScoClient implements IBinder.DeathRecipient {
1235        private IBinder mCb; // To be notified of client's death
1236        private int mStartcount; // number of SCO connections started by this client
1237
1238        ScoClient(IBinder cb) {
1239            mCb = cb;
1240            mStartcount = 0;
1241        }
1242
1243        public void binderDied() {
1244            synchronized(mScoClients) {
1245                Log.w(TAG, "SCO client died");
1246                int index = mScoClients.indexOf(this);
1247                if (index < 0) {
1248                    Log.w(TAG, "unregistered SCO client died");
1249                } else {
1250                    clearCount(true);
1251                    mScoClients.remove(this);
1252                }
1253            }
1254        }
1255
1256        public void incCount() {
1257            synchronized(mScoClients) {
1258                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
1259                if (mStartcount == 0) {
1260                    try {
1261                        mCb.linkToDeath(this, 0);
1262                    } catch (RemoteException e) {
1263                        // client has already died!
1264                        Log.w(TAG, "ScoClient  incCount() could not link to "+mCb+" binder death");
1265                    }
1266                }
1267                mStartcount++;
1268            }
1269        }
1270
1271        public void decCount() {
1272            synchronized(mScoClients) {
1273                if (mStartcount == 0) {
1274                    Log.w(TAG, "ScoClient.decCount() already 0");
1275                } else {
1276                    mStartcount--;
1277                    if (mStartcount == 0) {
1278                        try {
1279                            mCb.unlinkToDeath(this, 0);
1280                        } catch (NoSuchElementException e) {
1281                            Log.w(TAG, "decCount() going to 0 but not registered to binder");
1282                        }
1283                    }
1284                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1285                }
1286            }
1287        }
1288
1289        public void clearCount(boolean stopSco) {
1290            synchronized(mScoClients) {
1291                if (mStartcount != 0) {
1292                    try {
1293                        mCb.unlinkToDeath(this, 0);
1294                    } catch (NoSuchElementException e) {
1295                        Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
1296                    }
1297                }
1298                mStartcount = 0;
1299                if (stopSco) {
1300                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1301                }
1302            }
1303        }
1304
1305        public int getCount() {
1306            return mStartcount;
1307        }
1308
1309        public IBinder getBinder() {
1310            return mCb;
1311        }
1312
1313        public int totalCount() {
1314            synchronized(mScoClients) {
1315                int count = 0;
1316                int size = mScoClients.size();
1317                for (int i = 0; i < size; i++) {
1318                    count += mScoClients.get(i).getCount();
1319                }
1320                return count;
1321            }
1322        }
1323
1324        private void requestScoState(int state) {
1325            if (mBluetoothHeadset == null) {
1326                return;
1327            }
1328
1329            checkScoAudioState();
1330
1331            if (totalCount() == 0 &&
1332                mBluetoothHeadsetDevice != null) {
1333                // Accept SCO audio activation only in NORMAL audio mode or if the mode is
1334                // currently controlled by the same client.
1335                if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL ||
1336                        mSetModeDeathHandlers.get(0).getBinder() == mCb) &&
1337                        state == BluetoothHeadset.STATE_AUDIO_CONNECTED &&
1338                        mScoAudioState == SCO_STATE_INACTIVE) {
1339                    mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1340                    mBluetoothHeadset.startScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice);
1341                } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
1342                        mScoAudioState == SCO_STATE_ACTIVE_INTERNAL){
1343                    mBluetoothHeadset.stopScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice);
1344                }
1345            }
1346        }
1347    }
1348
1349    private void checkScoAudioState() {
1350        if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
1351                mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
1352                mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
1353                != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1354            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
1355        }
1356    }
1357
1358    private ScoClient getScoClient(IBinder cb, boolean create) {
1359        synchronized(mScoClients) {
1360            ScoClient client = null;
1361            int size = mScoClients.size();
1362            for (int i = 0; i < size; i++) {
1363                client = mScoClients.get(i);
1364                if (client.getBinder() == cb)
1365                    return client;
1366            }
1367            if (create) {
1368                client = new ScoClient(cb);
1369                mScoClients.add(client);
1370            }
1371            return client;
1372        }
1373    }
1374
1375    public void clearAllScoClients(IBinder exceptBinder, boolean stopSco) {
1376        synchronized(mScoClients) {
1377            ScoClient savedClient = null;
1378            int size = mScoClients.size();
1379            for (int i = 0; i < size; i++) {
1380                ScoClient cl = mScoClients.get(i);
1381                if (cl.getBinder() != exceptBinder) {
1382                    cl.clearCount(stopSco);
1383                } else {
1384                    savedClient = cl;
1385                }
1386            }
1387            mScoClients.clear();
1388            if (savedClient != null) {
1389                mScoClients.add(savedClient);
1390            }
1391        }
1392    }
1393
1394    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
1395        new BluetoothProfile.ServiceListener() {
1396        public void onServiceConnected(int profile, BluetoothProfile proxy) {
1397            synchronized (mScoClients) {
1398                mBluetoothHeadset = (BluetoothHeadset) proxy;
1399                List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices();
1400                if (deviceList.size() > 0) {
1401                    mBluetoothHeadsetDevice = deviceList.get(0);
1402                } else {
1403                    mBluetoothHeadsetDevice = null;
1404                }
1405            }
1406        }
1407        public void onServiceDisconnected(int profile) {
1408            synchronized (mScoClients) {
1409                if (mBluetoothHeadset != null) {
1410                    List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices();
1411                    if (devices.size() == 0) {
1412                        mBluetoothHeadsetDevice = null;
1413                        clearAllScoClients(null, false);
1414                        mScoAudioState = SCO_STATE_INACTIVE;
1415                    }
1416                    mBluetoothHeadset = null;
1417                }
1418            }
1419        }
1420    };
1421
1422    ///////////////////////////////////////////////////////////////////////////
1423    // Internal methods
1424    ///////////////////////////////////////////////////////////////////////////
1425
1426    /**
1427     * Checks if the adjustment should change ringer mode instead of just
1428     * adjusting volume. If so, this will set the proper ringer mode and volume
1429     * indices on the stream states.
1430     */
1431    private boolean checkForRingerModeChange(int oldIndex, int direction) {
1432        boolean adjustVolumeIndex = true;
1433        int newRingerMode = mRingerMode;
1434
1435        if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
1436            // audible mode, at the bottom of the scale
1437            if ((direction == AudioManager.ADJUST_LOWER &&
1438                 mPrevVolDirection != AudioManager.ADJUST_LOWER) &&
1439                ((oldIndex + 5) / 10 == 0)) {
1440                // "silent mode", but which one?
1441                newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
1442                    ? AudioManager.RINGER_MODE_VIBRATE
1443                    : AudioManager.RINGER_MODE_SILENT;
1444            }
1445        } else {
1446            if (direction == AudioManager.ADJUST_RAISE) {
1447                // exiting silent mode
1448                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
1449            } else {
1450                // prevent last audible index to reach 0
1451                adjustVolumeIndex = false;
1452            }
1453        }
1454
1455        if (newRingerMode != mRingerMode) {
1456            setRingerMode(newRingerMode);
1457
1458            /*
1459             * If we are changing ringer modes, do not increment/decrement the
1460             * volume index. Instead, the handler for the message above will
1461             * take care of changing the index.
1462             */
1463            adjustVolumeIndex = false;
1464        }
1465
1466        mPrevVolDirection = direction;
1467
1468        return adjustVolumeIndex;
1469    }
1470
1471    public boolean isStreamAffectedByRingerMode(int streamType) {
1472        return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
1473    }
1474
1475    private boolean isStreamMutedByRingerMode(int streamType) {
1476        return (mRingerModeMutedStreams & (1 << streamType)) != 0;
1477    }
1478
1479    public boolean isStreamAffectedByMute(int streamType) {
1480        return (mMuteAffectedStreams & (1 << streamType)) != 0;
1481    }
1482
1483    private void ensureValidDirection(int direction) {
1484        if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
1485            throw new IllegalArgumentException("Bad direction " + direction);
1486        }
1487    }
1488
1489    private void ensureValidStreamType(int streamType) {
1490        if (streamType < 0 || streamType >= mStreamStates.length) {
1491            throw new IllegalArgumentException("Bad stream type " + streamType);
1492        }
1493    }
1494
1495    private int getActiveStreamType(int suggestedStreamType) {
1496
1497        if (mVoiceCapable) {
1498            boolean isOffhook = false;
1499            try {
1500                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1501                if (phone != null) isOffhook = phone.isOffhook();
1502            } catch (RemoteException e) {
1503                Log.w(TAG, "Couldn't connect to phone service", e);
1504            }
1505
1506            if (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION) {
1507                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
1508                        == AudioSystem.FORCE_BT_SCO) {
1509                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1510                    return AudioSystem.STREAM_BLUETOOTH_SCO;
1511                } else {
1512                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1513                    return AudioSystem.STREAM_VOICE_CALL;
1514                }
1515            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
1516                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
1517                return AudioSystem.STREAM_MUSIC;
1518            } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
1519                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."
1520                //        + " b/c USE_DEFAULT_STREAM_TYPE...");
1521                return AudioSystem.STREAM_RING;
1522            } else {
1523                // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1524                return suggestedStreamType;
1525            }
1526        } else {
1527            if (getMode() == AudioManager.MODE_IN_COMMUNICATION) {
1528                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
1529                        == AudioSystem.FORCE_BT_SCO) {
1530                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1531                    return AudioSystem.STREAM_BLUETOOTH_SCO;
1532                } else {
1533                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1534                    return AudioSystem.STREAM_VOICE_CALL;
1535                }
1536            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
1537                            NOTIFICATION_VOLUME_DELAY_MS) ||
1538                       AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
1539                            NOTIFICATION_VOLUME_DELAY_MS)) {
1540                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION...");
1541                return AudioSystem.STREAM_NOTIFICATION;
1542            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) ||
1543                       (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) {
1544                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC "
1545                //        + " b/c USE_DEFAULT_STREAM_TYPE...");
1546                return AudioSystem.STREAM_MUSIC;
1547            } else {
1548                // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1549                return suggestedStreamType;
1550            }
1551        }
1552    }
1553
1554    private void broadcastRingerMode() {
1555        // Send sticky broadcast
1556        Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
1557        broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
1558        broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1559                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1560        long origCallerIdentityToken = Binder.clearCallingIdentity();
1561        mContext.sendStickyBroadcast(broadcast);
1562        Binder.restoreCallingIdentity(origCallerIdentityToken);
1563    }
1564
1565    private void broadcastVibrateSetting(int vibrateType) {
1566        // Send broadcast
1567        if (ActivityManagerNative.isSystemReady()) {
1568            Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
1569            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
1570            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
1571            mContext.sendBroadcast(broadcast);
1572        }
1573    }
1574
1575    // Message helper methods
1576    private static int getMsg(int baseMsg, int streamType) {
1577        return (baseMsg & 0xffff) | streamType << 16;
1578    }
1579
1580    private static int getMsgBase(int msg) {
1581        return msg & 0xffff;
1582    }
1583
1584    private static void sendMsg(Handler handler, int baseMsg, int streamType,
1585            int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
1586        int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
1587
1588        if (existingMsgPolicy == SENDMSG_REPLACE) {
1589            handler.removeMessages(msg);
1590        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
1591            return;
1592        }
1593
1594        handler
1595                .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
1596    }
1597
1598    boolean checkAudioSettingsPermission(String method) {
1599        if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
1600                == PackageManager.PERMISSION_GRANTED) {
1601            return true;
1602        }
1603        String msg = "Audio Settings Permission Denial: " + method + " from pid="
1604                + Binder.getCallingPid()
1605                + ", uid=" + Binder.getCallingUid();
1606        Log.w(TAG, msg);
1607        return false;
1608    }
1609
1610
1611    ///////////////////////////////////////////////////////////////////////////
1612    // Inner classes
1613    ///////////////////////////////////////////////////////////////////////////
1614
1615    public class VolumeStreamState {
1616        private final int mStreamType;
1617
1618        private String mVolumeIndexSettingName;
1619        private String mLastAudibleVolumeIndexSettingName;
1620        private int mIndexMax;
1621        private int mIndex;
1622        private int mLastAudibleIndex;
1623        private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
1624
1625        private VolumeStreamState(String settingName, int streamType) {
1626
1627            setVolumeIndexSettingName(settingName);
1628
1629            mStreamType = streamType;
1630
1631            final ContentResolver cr = mContentResolver;
1632            mIndexMax = MAX_STREAM_VOLUME[streamType];
1633            mIndex = Settings.System.getInt(cr,
1634                                            mVolumeIndexSettingName,
1635                                            AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1636            mLastAudibleIndex = Settings.System.getInt(cr,
1637                                                       mLastAudibleVolumeIndexSettingName,
1638                                                       (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1639            AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
1640            mIndexMax *= 10;
1641            mIndex = getValidIndex(10 * mIndex);
1642            mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
1643            setStreamVolumeIndex(streamType, mIndex);
1644            mDeathHandlers = new ArrayList<VolumeDeathHandler>();
1645        }
1646
1647        public void setVolumeIndexSettingName(String settingName) {
1648            mVolumeIndexSettingName = settingName;
1649            mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1650        }
1651
1652        public boolean adjustIndex(int deltaIndex) {
1653            return setIndex(mIndex + deltaIndex * 10, true);
1654        }
1655
1656        public boolean setIndex(int index, boolean lastAudible) {
1657            int oldIndex = mIndex;
1658            mIndex = getValidIndex(index);
1659
1660            if (oldIndex != mIndex) {
1661                if (lastAudible) {
1662                    mLastAudibleIndex = mIndex;
1663                }
1664                // Apply change to all streams using this one as alias
1665                int numStreamTypes = AudioSystem.getNumStreamTypes();
1666                for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1667                    if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
1668                        mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
1669                    }
1670                }
1671                return true;
1672            } else {
1673                return false;
1674            }
1675        }
1676
1677        public void setLastAudibleIndex(int index) {
1678            mLastAudibleIndex = getValidIndex(index);
1679        }
1680
1681        public void adjustLastAudibleIndex(int deltaIndex) {
1682            setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10);
1683        }
1684
1685        public int getMaxIndex() {
1686            return mIndexMax;
1687        }
1688
1689        public void mute(IBinder cb, boolean state) {
1690            VolumeDeathHandler handler = getDeathHandler(cb, state);
1691            if (handler == null) {
1692                Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
1693                return;
1694            }
1695            handler.mute(state);
1696        }
1697
1698        private int getValidIndex(int index) {
1699            if (index < 0) {
1700                return 0;
1701            } else if (index > mIndexMax) {
1702                return mIndexMax;
1703            }
1704
1705            return index;
1706        }
1707
1708        private class VolumeDeathHandler implements IBinder.DeathRecipient {
1709            private IBinder mICallback; // To be notified of client's death
1710            private int mMuteCount; // Number of active mutes for this client
1711
1712            VolumeDeathHandler(IBinder cb) {
1713                mICallback = cb;
1714            }
1715
1716            public void mute(boolean state) {
1717                synchronized(mDeathHandlers) {
1718                    if (state) {
1719                        if (mMuteCount == 0) {
1720                            // Register for client death notification
1721                            try {
1722                                // mICallback can be 0 if muted by AudioService
1723                                if (mICallback != null) {
1724                                    mICallback.linkToDeath(this, 0);
1725                                }
1726                                mDeathHandlers.add(this);
1727                                // If the stream is not yet muted by any client, set lvel to 0
1728                                if (muteCount() == 0) {
1729                                    setIndex(0, false);
1730                                    sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1731                                            VolumeStreamState.this, 0);
1732                                }
1733                            } catch (RemoteException e) {
1734                                // Client has died!
1735                                binderDied();
1736                                mDeathHandlers.notify();
1737                                return;
1738                            }
1739                        } else {
1740                            Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
1741                        }
1742                        mMuteCount++;
1743                    } else {
1744                        if (mMuteCount == 0) {
1745                            Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
1746                        } else {
1747                            mMuteCount--;
1748                            if (mMuteCount == 0) {
1749                                // Unregistr from client death notification
1750                                mDeathHandlers.remove(this);
1751                                // mICallback can be 0 if muted by AudioService
1752                                if (mICallback != null) {
1753                                    mICallback.unlinkToDeath(this, 0);
1754                                }
1755                                if (muteCount() == 0) {
1756                                    // If the stream is not muted any more, restore it's volume if
1757                                    // ringer mode allows it
1758                                    if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
1759                                        setIndex(mLastAudibleIndex, false);
1760                                        sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1761                                                VolumeStreamState.this, 0);
1762                                    }
1763                                }
1764                            }
1765                        }
1766                    }
1767                    mDeathHandlers.notify();
1768                }
1769            }
1770
1771            public void binderDied() {
1772                Log.w(TAG, "Volume service client died for stream: "+mStreamType);
1773                if (mMuteCount != 0) {
1774                    // Reset all active mute requests from this client.
1775                    mMuteCount = 1;
1776                    mute(false);
1777                }
1778            }
1779        }
1780
1781        private int muteCount() {
1782            int count = 0;
1783            int size = mDeathHandlers.size();
1784            for (int i = 0; i < size; i++) {
1785                count += mDeathHandlers.get(i).mMuteCount;
1786            }
1787            return count;
1788        }
1789
1790        private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
1791            synchronized(mDeathHandlers) {
1792                VolumeDeathHandler handler;
1793                int size = mDeathHandlers.size();
1794                for (int i = 0; i < size; i++) {
1795                    handler = mDeathHandlers.get(i);
1796                    if (cb == handler.mICallback) {
1797                        return handler;
1798                    }
1799                }
1800                // If this is the first mute request for this client, create a new
1801                // client death handler. Otherwise, it is an out of sequence unmute request.
1802                if (state) {
1803                    handler = new VolumeDeathHandler(cb);
1804                } else {
1805                    Log.w(TAG, "stream was not muted by this client");
1806                    handler = null;
1807                }
1808                return handler;
1809            }
1810        }
1811    }
1812
1813    /** Thread that handles native AudioSystem control. */
1814    private class AudioSystemThread extends Thread {
1815        AudioSystemThread() {
1816            super("AudioService");
1817        }
1818
1819        @Override
1820        public void run() {
1821            // Set this thread up so the handler will work on it
1822            Looper.prepare();
1823
1824            synchronized(AudioService.this) {
1825                mAudioHandler = new AudioHandler();
1826
1827                // Notify that the handler has been created
1828                AudioService.this.notify();
1829            }
1830
1831            // Listen for volume change requests that are set by VolumePanel
1832            Looper.loop();
1833        }
1834    }
1835
1836    /** Handles internal volume messages in separate volume thread. */
1837    private class AudioHandler extends Handler {
1838
1839        private void setSystemVolume(VolumeStreamState streamState) {
1840
1841            // Adjust volume
1842            setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
1843
1844            // Apply change to all streams using this one as alias
1845            int numStreamTypes = AudioSystem.getNumStreamTypes();
1846            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1847                if (streamType != streamState.mStreamType &&
1848                    STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
1849                    setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
1850                }
1851            }
1852
1853            // Post a persist volume msg
1854            sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
1855                    SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY);
1856        }
1857
1858        private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) {
1859            if (current) {
1860                System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
1861                              (streamState.mIndex + 5)/ 10);
1862            }
1863            if (lastAudible) {
1864                System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
1865                    (streamState.mLastAudibleIndex + 5) / 10);
1866            }
1867        }
1868
1869        private void persistRingerMode() {
1870            System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
1871        }
1872
1873        private void persistVibrateSetting() {
1874            System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
1875        }
1876
1877        private void playSoundEffect(int effectType, int volume) {
1878            synchronized (mSoundEffectsLock) {
1879                if (mSoundPool == null) {
1880                    return;
1881                }
1882                float volFloat;
1883                // use default if volume is not specified by caller
1884                if (volume < 0) {
1885                    volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
1886                } else {
1887                    volFloat = (float) volume / 1000.0f;
1888                }
1889
1890                if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
1891                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
1892                } else {
1893                    MediaPlayer mediaPlayer = new MediaPlayer();
1894                    if (mediaPlayer != null) {
1895                        try {
1896                            String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
1897                            mediaPlayer.setDataSource(filePath);
1898                            mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
1899                            mediaPlayer.prepare();
1900                            mediaPlayer.setVolume(volFloat, volFloat);
1901                            mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
1902                                public void onCompletion(MediaPlayer mp) {
1903                                    cleanupPlayer(mp);
1904                                }
1905                            });
1906                            mediaPlayer.setOnErrorListener(new OnErrorListener() {
1907                                public boolean onError(MediaPlayer mp, int what, int extra) {
1908                                    cleanupPlayer(mp);
1909                                    return true;
1910                                }
1911                            });
1912                            mediaPlayer.start();
1913                        } catch (IOException ex) {
1914                            Log.w(TAG, "MediaPlayer IOException: "+ex);
1915                        } catch (IllegalArgumentException ex) {
1916                            Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
1917                        } catch (IllegalStateException ex) {
1918                            Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1919                        }
1920                    }
1921                }
1922            }
1923        }
1924
1925        private void persistMediaButtonReceiver(ComponentName receiver) {
1926            Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
1927                    receiver == null ? "" : receiver.flattenToString());
1928        }
1929
1930        private void cleanupPlayer(MediaPlayer mp) {
1931            if (mp != null) {
1932                try {
1933                    mp.stop();
1934                    mp.release();
1935                } catch (IllegalStateException ex) {
1936                    Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1937                }
1938            }
1939        }
1940
1941        private void setForceUse(int usage, int config) {
1942            AudioSystem.setForceUse(usage, config);
1943        }
1944
1945        @Override
1946        public void handleMessage(Message msg) {
1947            int baseMsgWhat = getMsgBase(msg.what);
1948
1949            switch (baseMsgWhat) {
1950
1951                case MSG_SET_SYSTEM_VOLUME:
1952                    setSystemVolume((VolumeStreamState) msg.obj);
1953                    break;
1954
1955                case MSG_PERSIST_VOLUME:
1956                    persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0));
1957                    break;
1958
1959                case MSG_PERSIST_RINGER_MODE:
1960                    persistRingerMode();
1961                    break;
1962
1963                case MSG_PERSIST_VIBRATE_SETTING:
1964                    persistVibrateSetting();
1965                    break;
1966
1967                case MSG_MEDIA_SERVER_DIED:
1968                    if (!mMediaServerOk) {
1969                        Log.e(TAG, "Media server died.");
1970                        // Force creation of new IAudioFlinger interface so that we are notified
1971                        // when new media_server process is back to life.
1972                        AudioSystem.setErrorCallback(mAudioSystemCallback);
1973                        sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
1974                                null, 500);
1975                    }
1976                    break;
1977
1978                case MSG_MEDIA_SERVER_STARTED:
1979                    Log.e(TAG, "Media server started.");
1980                    // indicate to audio HAL that we start the reconfiguration phase after a media
1981                    // server crash
1982                    // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
1983                    // process restarts after a crash, not the first time it is started.
1984                    AudioSystem.setParameters("restarting=true");
1985
1986                    // Restore device connection states
1987                    Set set = mConnectedDevices.entrySet();
1988                    Iterator i = set.iterator();
1989                    while(i.hasNext()){
1990                        Map.Entry device = (Map.Entry)i.next();
1991                        AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
1992                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
1993                                                             (String)device.getValue());
1994                    }
1995
1996                    // Restore call state
1997                    AudioSystem.setPhoneState(mMode);
1998
1999                    // Restore forced usage for communcations and record
2000                    AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
2001                    AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
2002
2003                    // Restore stream volumes
2004                    int numStreamTypes = AudioSystem.getNumStreamTypes();
2005                    for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2006                        int index;
2007                        VolumeStreamState streamState = mStreamStates[streamType];
2008                        AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
2009                        if (streamState.muteCount() == 0) {
2010                            index = streamState.mIndex;
2011                        } else {
2012                            index = 0;
2013                        }
2014                        setStreamVolumeIndex(streamType, index);
2015                    }
2016
2017                    // Restore ringer mode
2018                    setRingerModeInt(getRingerMode(), false);
2019
2020                    // indicate the end of reconfiguration phase to audio HAL
2021                    AudioSystem.setParameters("restarting=false");
2022                    break;
2023
2024                case MSG_LOAD_SOUND_EFFECTS:
2025                    loadSoundEffects();
2026                    break;
2027
2028                case MSG_PLAY_SOUND_EFFECT:
2029                    playSoundEffect(msg.arg1, msg.arg2);
2030                    break;
2031
2032                case MSG_BTA2DP_DOCK_TIMEOUT:
2033                    // msg.obj  == address of BTA2DP device
2034                    makeA2dpDeviceUnavailableNow( (String) msg.obj );
2035                    break;
2036
2037                case MSG_SET_FORCE_USE:
2038                    setForceUse(msg.arg1, msg.arg2);
2039                    break;
2040
2041                case MSG_PERSIST_MEDIABUTTONRECEIVER:
2042                    persistMediaButtonReceiver( (ComponentName) msg.obj );
2043                    break;
2044            }
2045        }
2046    }
2047
2048    private class SettingsObserver extends ContentObserver {
2049
2050        SettingsObserver() {
2051            super(new Handler());
2052            mContentResolver.registerContentObserver(Settings.System.getUriFor(
2053                Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
2054            mContentResolver.registerContentObserver(Settings.System.getUriFor(
2055                    Settings.System.NOTIFICATIONS_USE_RING_VOLUME), false, this);
2056        }
2057
2058        @Override
2059        public void onChange(boolean selfChange) {
2060            super.onChange(selfChange);
2061            synchronized (mSettingsLock) {
2062                int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
2063                        Settings.System.MODE_RINGER_STREAMS_AFFECTED,
2064                        0);
2065                if (!mVoiceCapable) {
2066                    ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
2067                }
2068
2069                if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
2070                    /*
2071                     * Ensure all stream types that should be affected by ringer mode
2072                     * are in the proper state.
2073                     */
2074                    mRingerModeAffectedStreams = ringerModeAffectedStreams;
2075                    setRingerModeInt(getRingerMode(), false);
2076                }
2077
2078                int notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
2079                        Settings.System.NOTIFICATIONS_USE_RING_VOLUME,
2080                        1);
2081                if (mVoiceCapable) {
2082                    if (notificationsUseRingVolume != mNotificationsUseRingVolume) {
2083                        mNotificationsUseRingVolume = notificationsUseRingVolume;
2084                        if (mNotificationsUseRingVolume == 1) {
2085                            STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
2086                            mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
2087                                    System.VOLUME_SETTINGS[AudioSystem.STREAM_RING]);
2088                        } else {
2089                            STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
2090                            mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
2091                                    System.VOLUME_SETTINGS[AudioSystem.STREAM_NOTIFICATION]);
2092                            // Persist notification volume volume as it was not persisted while aliased to ring volume
2093                            //  and persist with no delay as there might be registered observers of the persisted
2094                            //  notification volume.
2095                            sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
2096                                    SENDMSG_REPLACE, 1, 1, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0);
2097                        }
2098                    }
2099                }
2100            }
2101        }
2102    }
2103
2104    private void makeA2dpDeviceAvailable(String address) {
2105        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2106                AudioSystem.DEVICE_STATE_AVAILABLE,
2107                address);
2108        // Reset A2DP suspend state each time a new sink is connected
2109        AudioSystem.setParameters("A2dpSuspended=false");
2110        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
2111                address);
2112    }
2113
2114    private void makeA2dpDeviceUnavailableNow(String address) {
2115        Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
2116        mContext.sendBroadcast(noisyIntent);
2117        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2118                AudioSystem.DEVICE_STATE_UNAVAILABLE,
2119                address);
2120        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2121    }
2122
2123    private void makeA2dpDeviceUnavailableLater(String address) {
2124        // prevent any activity on the A2DP audio output to avoid unwanted
2125        // reconnection of the sink.
2126        AudioSystem.setParameters("A2dpSuspended=true");
2127        // the device will be made unavailable later, so consider it disconnected right away
2128        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2129        // send the delayed message to make the device unavailable later
2130        Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
2131        mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
2132
2133    }
2134
2135    private void cancelA2dpDeviceTimeout() {
2136        mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2137    }
2138
2139    private boolean hasScheduledA2dpDockTimeout() {
2140        return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2141    }
2142
2143    /* cache of the address of the last dock the device was connected to */
2144    private String mDockAddress;
2145
2146    /**
2147     * Receiver for misc intent broadcasts the Phone app cares about.
2148     */
2149    private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
2150        @Override
2151        public void onReceive(Context context, Intent intent) {
2152            String action = intent.getAction();
2153
2154            if (action.equals(Intent.ACTION_DOCK_EVENT)) {
2155                int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
2156                        Intent.EXTRA_DOCK_STATE_UNDOCKED);
2157                int config;
2158                switch (dockState) {
2159                    case Intent.EXTRA_DOCK_STATE_DESK:
2160                        config = AudioSystem.FORCE_BT_DESK_DOCK;
2161                        break;
2162                    case Intent.EXTRA_DOCK_STATE_CAR:
2163                        config = AudioSystem.FORCE_BT_CAR_DOCK;
2164                        break;
2165                    case Intent.EXTRA_DOCK_STATE_LE_DESK:
2166                        config = AudioSystem.FORCE_ANALOG_DOCK;
2167                        break;
2168                    case Intent.EXTRA_DOCK_STATE_HE_DESK:
2169                        config = AudioSystem.FORCE_DIGITAL_DOCK;
2170                        break;
2171                    case Intent.EXTRA_DOCK_STATE_UNDOCKED:
2172                    default:
2173                        config = AudioSystem.FORCE_NONE;
2174                }
2175                AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
2176            } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
2177                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
2178                                               BluetoothProfile.STATE_DISCONNECTED);
2179                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
2180                String address = btDevice.getAddress();
2181                boolean isConnected =
2182                    (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
2183                     mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
2184
2185                if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
2186                    if (btDevice.isBluetoothDock()) {
2187                        if (state == BluetoothProfile.STATE_DISCONNECTED) {
2188                            // introduction of a delay for transient disconnections of docks when
2189                            // power is rapidly turned off/on, this message will be canceled if
2190                            // we reconnect the dock under a preset delay
2191                            makeA2dpDeviceUnavailableLater(address);
2192                            // the next time isConnected is evaluated, it will be false for the dock
2193                        }
2194                    } else {
2195                        makeA2dpDeviceUnavailableNow(address);
2196                    }
2197                } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
2198                    if (btDevice.isBluetoothDock()) {
2199                        // this could be a reconnection after a transient disconnection
2200                        cancelA2dpDeviceTimeout();
2201                        mDockAddress = address;
2202                    } else {
2203                        // this could be a connection of another A2DP device before the timeout of
2204                        // a dock: cancel the dock timeout, and make the dock unavailable now
2205                        if(hasScheduledA2dpDockTimeout()) {
2206                            cancelA2dpDeviceTimeout();
2207                            makeA2dpDeviceUnavailableNow(mDockAddress);
2208                        }
2209                    }
2210                    makeA2dpDeviceAvailable(address);
2211                }
2212            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
2213                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
2214                                               BluetoothProfile.STATE_DISCONNECTED);
2215                int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
2216                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
2217                String address = null;
2218                if (btDevice != null) {
2219                    address = btDevice.getAddress();
2220                    BluetoothClass btClass = btDevice.getBluetoothClass();
2221                    if (btClass != null) {
2222                        switch (btClass.getDeviceClass()) {
2223                        case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
2224                        case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
2225                            device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
2226                            break;
2227                        case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
2228                            device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
2229                            break;
2230                        }
2231                    }
2232                }
2233
2234                boolean isConnected = (mConnectedDevices.containsKey(device) &&
2235                                       mConnectedDevices.get(device).equals(address));
2236
2237                synchronized (mScoClients) {
2238                    if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
2239                        AudioSystem.setDeviceConnectionState(device,
2240                                                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
2241                                                             address);
2242                        mConnectedDevices.remove(device);
2243                        mBluetoothHeadsetDevice = null;
2244                        clearAllScoClients(null, false);
2245                        mScoAudioState = SCO_STATE_INACTIVE;
2246                    } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
2247                        AudioSystem.setDeviceConnectionState(device,
2248                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
2249                                                             address);
2250                        mConnectedDevices.put(new Integer(device), address);
2251                        mBluetoothHeadsetDevice = btDevice;
2252                    }
2253                }
2254            } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
2255                int state = intent.getIntExtra("state", 0);
2256                int microphone = intent.getIntExtra("microphone", 0);
2257
2258                if (microphone != 0) {
2259                    boolean isConnected =
2260                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
2261                    if (state == 0 && isConnected) {
2262                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
2263                                AudioSystem.DEVICE_STATE_UNAVAILABLE,
2264                                "");
2265                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
2266                    } else if (state == 1 && !isConnected)  {
2267                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
2268                                AudioSystem.DEVICE_STATE_AVAILABLE,
2269                                "");
2270                        mConnectedDevices.put(
2271                                new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
2272                    }
2273                } else {
2274                    boolean isConnected =
2275                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
2276                    if (state == 0 && isConnected) {
2277                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
2278                                AudioSystem.DEVICE_STATE_UNAVAILABLE,
2279                                "");
2280                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
2281                    } else if (state == 1 && !isConnected)  {
2282                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
2283                                AudioSystem.DEVICE_STATE_AVAILABLE,
2284                                "");
2285                        mConnectedDevices.put(
2286                                new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
2287                    }
2288                }
2289            } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
2290                int state = intent.getIntExtra("state", 0);
2291                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
2292                boolean isConnected =
2293                    mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
2294                if (state == 0 && isConnected) {
2295                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
2296                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
2297                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
2298                } else if (state == 1 && !isConnected)  {
2299                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
2300                                                         AudioSystem.DEVICE_STATE_AVAILABLE, "");
2301                    mConnectedDevices.put(
2302                            new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
2303                }
2304            } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
2305                int state = intent.getIntExtra("state", 0);
2306                Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
2307                boolean isConnected =
2308                    mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
2309                if (state == 0 && isConnected) {
2310                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
2311                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
2312                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
2313                } else if (state == 1 && !isConnected)  {
2314                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
2315                                                         AudioSystem.DEVICE_STATE_AVAILABLE, "");
2316                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
2317                }
2318            } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) {
2319                int state = intent.getIntExtra("state", 0);
2320                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
2321                boolean isConnected =
2322                    mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
2323                if (state == 0 && isConnected) {
2324                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
2325                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE, "");
2326                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
2327                } else if (state == 1 && !isConnected)  {
2328                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
2329                                                         AudioSystem.DEVICE_STATE_AVAILABLE, "");
2330                    mConnectedDevices.put(
2331                            new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
2332                }
2333            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
2334                boolean broadcast = false;
2335                int audioState = AudioManager.SCO_AUDIO_STATE_ERROR;
2336                synchronized (mScoClients) {
2337                    int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
2338                    if (!mScoClients.isEmpty() && mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
2339                        broadcast = true;
2340                    }
2341                    switch (btState) {
2342                    case BluetoothHeadset.STATE_AUDIO_CONNECTED:
2343                        audioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
2344                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) {
2345                            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
2346                        }
2347                        break;
2348                    case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
2349                        audioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
2350                        mScoAudioState = SCO_STATE_INACTIVE;
2351                        clearAllScoClients(null, false);
2352                        break;
2353                    case BluetoothHeadset.STATE_AUDIO_CONNECTING:
2354                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) {
2355                            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
2356                        }
2357                    default:
2358                        // do not broadcast CONNECTING or invalid state
2359                        broadcast = false;
2360                        break;
2361                    }
2362                }
2363                if (broadcast) {
2364                    Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
2365                    newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, audioState);
2366                    mContext.sendStickyBroadcast(newIntent);
2367                }
2368            } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
2369                mBootCompleted = true;
2370                sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP,
2371                        0, 0, null, 0);
2372                Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
2373                newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
2374                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
2375                mContext.sendStickyBroadcast(newIntent);
2376            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
2377                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
2378                    // a package is being removed, not replaced
2379                    String packageName = intent.getData().getSchemeSpecificPart();
2380                    if (packageName != null) {
2381                        removeMediaButtonReceiverForPackage(packageName);
2382                    }
2383                }
2384            }
2385        }
2386    }
2387
2388    //==========================================================================================
2389    // AudioFocus
2390    //==========================================================================================
2391
2392    /* constant to identify focus stack entry that is used to hold the focus while the phone
2393     * is ringing or during a call
2394     */
2395    private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
2396
2397    private final static Object mAudioFocusLock = new Object();
2398
2399    private final static Object mRingingLock = new Object();
2400
2401    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
2402        @Override
2403        public void onCallStateChanged(int state, String incomingNumber) {
2404            if (state == TelephonyManager.CALL_STATE_RINGING) {
2405                //Log.v(TAG, " CALL_STATE_RINGING");
2406                synchronized(mRingingLock) {
2407                    mIsRinging = true;
2408                }
2409            } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
2410                    || (state == TelephonyManager.CALL_STATE_IDLE)) {
2411                synchronized(mRingingLock) {
2412                    mIsRinging = false;
2413                }
2414            }
2415        }
2416    };
2417
2418    private void notifyTopOfAudioFocusStack() {
2419        // notify the top of the stack it gained focus
2420        if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
2421            if (canReassignAudioFocus()) {
2422                try {
2423                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
2424                            AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
2425                } catch (RemoteException e) {
2426                    Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
2427                    e.printStackTrace();
2428                }
2429            }
2430        }
2431    }
2432
2433    private static class FocusStackEntry {
2434        public int mStreamType = -1;// no stream type
2435        public boolean mIsTransportControlReceiver = false;
2436        public IAudioFocusDispatcher mFocusDispatcher = null;
2437        public IBinder mSourceRef = null;
2438        public String mClientId;
2439        public int mFocusChangeType;
2440
2441        public FocusStackEntry() {
2442        }
2443
2444        public FocusStackEntry(int streamType, int duration, boolean isTransportControlReceiver,
2445                IAudioFocusDispatcher afl, IBinder source, String id) {
2446            mStreamType = streamType;
2447            mIsTransportControlReceiver = isTransportControlReceiver;
2448            mFocusDispatcher = afl;
2449            mSourceRef = source;
2450            mClientId = id;
2451            mFocusChangeType = duration;
2452        }
2453    }
2454
2455    private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
2456
2457    /**
2458     * Helper function:
2459     * Display in the log the current entries in the audio focus stack
2460     */
2461    private void dumpFocusStack(PrintWriter pw) {
2462        pw.println("\nAudio Focus stack entries:");
2463        synchronized(mAudioFocusLock) {
2464            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2465            while(stackIterator.hasNext()) {
2466                FocusStackEntry fse = stackIterator.next();
2467                pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
2468                        + " -- duration: " +fse.mFocusChangeType);
2469            }
2470        }
2471    }
2472
2473    /**
2474     * Helper function:
2475     * Remove a focus listener from the focus stack.
2476     * @param focusListenerToRemove the focus listener
2477     * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
2478     *   focus, notify the next item in the stack it gained focus.
2479     */
2480    private void removeFocusStackEntry(String clientToRemove, boolean signal) {
2481        // is the current top of the focus stack abandoning focus? (because of death or request)
2482        if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
2483        {
2484            //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
2485            mFocusStack.pop();
2486            if (signal) {
2487                // notify the new top of the stack it gained focus
2488                notifyTopOfAudioFocusStack();
2489            }
2490        } else {
2491            // focus is abandoned by a client that's not at the top of the stack,
2492            // no need to update focus.
2493            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2494            while(stackIterator.hasNext()) {
2495                FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
2496                if(fse.mClientId.equals(clientToRemove)) {
2497                    Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
2498                            + fse.mClientId);
2499                    stackIterator.remove();
2500                }
2501            }
2502        }
2503    }
2504
2505    /**
2506     * Helper function:
2507     * Remove focus listeners from the focus stack for a particular client.
2508     */
2509    private void removeFocusStackEntryForClient(IBinder cb) {
2510        // is the owner of the audio focus part of the client to remove?
2511        boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
2512                mFocusStack.peek().mSourceRef.equals(cb);
2513        Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2514        while(stackIterator.hasNext()) {
2515            FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
2516            if(fse.mSourceRef.equals(cb)) {
2517                Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
2518                        + fse.mClientId);
2519                stackIterator.remove();
2520            }
2521        }
2522        if (isTopOfStackForClientToRemove) {
2523            // we removed an entry at the top of the stack:
2524            //  notify the new top of the stack it gained focus.
2525            notifyTopOfAudioFocusStack();
2526        }
2527    }
2528
2529    /**
2530     * Helper function:
2531     * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
2532     */
2533    private boolean canReassignAudioFocus() {
2534        // focus requests are rejected during a phone call or when the phone is ringing
2535        // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
2536        if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
2537            return false;
2538        }
2539        return true;
2540    }
2541
2542    /**
2543     * Inner class to monitor audio focus client deaths, and remove them from the audio focus
2544     * stack if necessary.
2545     */
2546    private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
2547        private IBinder mCb; // To be notified of client's death
2548
2549        AudioFocusDeathHandler(IBinder cb) {
2550            mCb = cb;
2551        }
2552
2553        public void binderDied() {
2554            synchronized(mAudioFocusLock) {
2555                Log.w(TAG, "  AudioFocus   audio focus client died");
2556                removeFocusStackEntryForClient(mCb);
2557            }
2558        }
2559
2560        public IBinder getBinder() {
2561            return mCb;
2562        }
2563    }
2564
2565
2566    /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
2567    public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
2568            IAudioFocusDispatcher fd, String clientId) {
2569        Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
2570        // the main stream type for the audio focus request is currently not used. It may
2571        // potentially be used to handle multiple stream type-dependent audio focuses.
2572
2573        // we need a valid binder callback for clients
2574        if (!cb.pingBinder()) {
2575            Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
2576            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
2577        }
2578
2579        synchronized(mAudioFocusLock) {
2580            if (!canReassignAudioFocus()) {
2581                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
2582            }
2583
2584            if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
2585                // if focus is already owned by this client and the reason for acquiring the focus
2586                // hasn't changed, don't do anything
2587                if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
2588                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2589                }
2590                // the reason for the audio focus request has changed: remove the current top of
2591                // stack and respond as if we had a new focus owner
2592                mFocusStack.pop();
2593            }
2594
2595            // notify current top of stack it is losing focus
2596            if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
2597                try {
2598                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
2599                            -1 * focusChangeHint, // loss and gain codes are inverse of each other
2600                            mFocusStack.peek().mClientId);
2601                } catch (RemoteException e) {
2602                    Log.e(TAG, " Failure to signal loss of focus due to "+ e);
2603                    e.printStackTrace();
2604                }
2605            }
2606
2607            // focus requester might already be somewhere below in the stack, remove it
2608            removeFocusStackEntry(clientId, false);
2609
2610            // push focus requester at the top of the audio focus stack
2611            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb,
2612                    clientId));
2613        }//synchronized(mAudioFocusLock)
2614
2615        // handle the potential premature death of the new holder of the focus
2616        // (premature death == death before abandoning focus)
2617        // Register for client death notification
2618        AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
2619        try {
2620            cb.linkToDeath(afdh, 0);
2621        } catch (RemoteException e) {
2622            // client has already died!
2623            Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
2624        }
2625
2626        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2627    }
2628
2629    /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
2630    public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
2631        Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
2632        try {
2633            // this will take care of notifying the new focus owner if needed
2634            synchronized(mAudioFocusLock) {
2635                removeFocusStackEntry(clientId, true);
2636            }
2637        } catch (java.util.ConcurrentModificationException cme) {
2638            // Catching this exception here is temporary. It is here just to prevent
2639            // a crash seen when the "Silent" notification is played. This is believed to be fixed
2640            // but this try catch block is left just to be safe.
2641            Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
2642            cme.printStackTrace();
2643        }
2644
2645        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2646    }
2647
2648
2649    public void unregisterAudioFocusClient(String clientId) {
2650        synchronized(mAudioFocusLock) {
2651            removeFocusStackEntry(clientId, false);
2652        }
2653    }
2654
2655
2656    //==========================================================================================
2657    // RemoteControl
2658    //==========================================================================================
2659    /**
2660     * Receiver for media button intents. Handles the dispatching of the media button event
2661     * to one of the registered listeners, or if there was none, resumes the intent broadcast
2662     * to the rest of the system.
2663     */
2664    private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
2665        @Override
2666        public void onReceive(Context context, Intent intent) {
2667            String action = intent.getAction();
2668            if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
2669                return;
2670            }
2671            KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
2672            if (event != null) {
2673                // if in a call or ringing, do not break the current phone app behavior
2674                // TODO modify this to let the phone app specifically get the RC focus
2675                //      add modify the phone app to take advantage of the new API
2676                synchronized(mRingingLock) {
2677                    if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) ||
2678                            (getMode() == AudioSystem.MODE_IN_COMMUNICATION) ||
2679                            (getMode() == AudioSystem.MODE_RINGTONE) ) {
2680                        return;
2681                    }
2682                }
2683                synchronized(mRCStack) {
2684                    if (!mRCStack.empty()) {
2685                        // create a new intent specifically aimed at the current registered listener
2686                        Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
2687                        targetedIntent.putExtras(intent.getExtras());
2688                        targetedIntent.setComponent(mRCStack.peek().mReceiverComponent);
2689                        // trap the current broadcast
2690                        abortBroadcast();
2691                        //Log.v(TAG, " Sending intent" + targetedIntent);
2692                        context.sendBroadcast(targetedIntent, null);
2693                    }
2694                }
2695            }
2696        }
2697    }
2698
2699    private static class RemoteControlStackEntry {
2700        public ComponentName mReceiverComponent;// always non null
2701        // TODO implement registration expiration?
2702        //public int mRegistrationTime;
2703
2704        public RemoteControlStackEntry() {
2705        }
2706
2707        public RemoteControlStackEntry(ComponentName r) {
2708            mReceiverComponent = r;
2709        }
2710    }
2711
2712    private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
2713
2714    /**
2715     * Helper function:
2716     * Display in the log the current entries in the remote control focus stack
2717     */
2718    private void dumpRCStack(PrintWriter pw) {
2719        pw.println("\nRemote Control stack entries:");
2720        synchronized(mRCStack) {
2721            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2722            while(stackIterator.hasNext()) {
2723                RemoteControlStackEntry fse = stackIterator.next();
2724                pw.println("     receiver:" + fse.mReceiverComponent);
2725            }
2726        }
2727    }
2728
2729    /**
2730     * Helper function:
2731     * Remove any entry in the remote control stack that has the same package name as packageName
2732     * Pre-condition: packageName != null
2733     */
2734    private void removeMediaButtonReceiverForPackage(String packageName) {
2735        synchronized(mRCStack) {
2736            if (mRCStack.empty()) {
2737                return;
2738            } else {
2739                RemoteControlStackEntry oldTop = mRCStack.peek();
2740                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2741                // iterate over the stack entries
2742                while(stackIterator.hasNext()) {
2743                    RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
2744                    if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
2745                        // a stack entry is from the package being removed, remove it from the stack
2746                        stackIterator.remove();
2747                    }
2748                }
2749                if (mRCStack.empty()) {
2750                    // no saved media button receiver
2751                    mAudioHandler.sendMessage(
2752                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
2753                                    null));
2754                    return;
2755                } else if (oldTop != mRCStack.peek()) {
2756                    // the top of the stack has changed, save it in the system settings
2757                    // by posting a message to persist it
2758                    mAudioHandler.sendMessage(
2759                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
2760                                    mRCStack.peek().mReceiverComponent));
2761                }
2762            }
2763        }
2764    }
2765
2766    /**
2767     * Helper function:
2768     * Restore remote control receiver from the system settings
2769     */
2770    private void restoreMediaButtonReceiver() {
2771        String receiverName = Settings.System.getString(mContentResolver,
2772                Settings.System.MEDIA_BUTTON_RECEIVER);
2773        if ((null != receiverName) && !receiverName.isEmpty()) {
2774            ComponentName receiverComponentName = ComponentName.unflattenFromString(receiverName);
2775            registerMediaButtonEventReceiver(receiverComponentName);
2776        }
2777    }
2778
2779    /**
2780     * Helper function:
2781     * Set the new remote control receiver at the top of the RC focus stack
2782     */
2783    private void pushMediaButtonReceiver(ComponentName newReceiver) {
2784        // already at top of stack?
2785        if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) {
2786            return;
2787        }
2788        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2789        while(stackIterator.hasNext()) {
2790            RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
2791            if(rcse.mReceiverComponent.equals(newReceiver)) {
2792                stackIterator.remove();
2793                break;
2794            }
2795        }
2796        mRCStack.push(new RemoteControlStackEntry(newReceiver));
2797
2798        // post message to persist the default media button receiver
2799        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
2800                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, newReceiver/*obj*/) );
2801    }
2802
2803    /**
2804     * Helper function:
2805     * Remove the remote control receiver from the RC focus stack
2806     */
2807    private void removeMediaButtonReceiver(ComponentName newReceiver) {
2808        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2809        while(stackIterator.hasNext()) {
2810            RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
2811            if(rcse.mReceiverComponent.equals(newReceiver)) {
2812                stackIterator.remove();
2813                break;
2814            }
2815        }
2816    }
2817
2818
2819    /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
2820    public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
2821        Log.i(TAG, "  Remote Control   registerMediaButtonEventReceiver() for " + eventReceiver);
2822
2823        synchronized(mRCStack) {
2824            pushMediaButtonReceiver(eventReceiver);
2825        }
2826    }
2827
2828    /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */
2829    public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
2830        Log.i(TAG, "  Remote Control   unregisterMediaButtonEventReceiver() for " + eventReceiver);
2831
2832        synchronized(mRCStack) {
2833            removeMediaButtonReceiver(eventReceiver);
2834        }
2835    }
2836
2837
2838    @Override
2839    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2840        // TODO probably a lot more to do here than just the audio focus and remote control stacks
2841        dumpFocusStack(pw);
2842        dumpRCStack(pw);
2843    }
2844
2845
2846}
2847