AudioService.java revision 0e8392e1dc1c023cb4d14a10778ae9cca44aac86
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 static android.media.AudioManager.RINGER_MODE_NORMAL;
20import static android.media.AudioManager.RINGER_MODE_SILENT;
21import static android.media.AudioManager.RINGER_MODE_VIBRATE;
22
23import android.app.ActivityManagerNative;
24import android.app.KeyguardManager;
25import android.app.PendingIntent;
26import android.app.PendingIntent.CanceledException;
27import android.bluetooth.BluetoothA2dp;
28import android.bluetooth.BluetoothAdapter;
29import android.bluetooth.BluetoothClass;
30import android.bluetooth.BluetoothDevice;
31import android.bluetooth.BluetoothHeadset;
32import android.bluetooth.BluetoothProfile;
33import android.content.BroadcastReceiver;
34import android.content.ComponentName;
35import android.content.ContentResolver;
36import android.content.Context;
37import android.content.Intent;
38import android.content.IntentFilter;
39import android.content.pm.PackageManager;
40import android.database.ContentObserver;
41import android.media.MediaPlayer.OnCompletionListener;
42import android.media.MediaPlayer.OnErrorListener;
43import android.os.Binder;
44import android.os.Bundle;
45import android.os.Environment;
46import android.os.Handler;
47import android.os.IBinder;
48import android.os.Looper;
49import android.os.Message;
50import android.os.RemoteException;
51import android.os.ServiceManager;
52import android.os.SystemProperties;
53import android.provider.Settings;
54import android.provider.Settings.System;
55import android.telephony.PhoneStateListener;
56import android.telephony.TelephonyManager;
57import android.util.Log;
58import android.view.KeyEvent;
59import android.view.VolumePanel;
60
61import com.android.internal.telephony.ITelephony;
62
63import java.io.FileDescriptor;
64import java.io.IOException;
65import java.io.PrintWriter;
66import java.util.ArrayList;
67import java.util.HashMap;
68import java.util.Iterator;
69import java.util.List;
70import java.util.Map;
71import java.util.NoSuchElementException;
72import java.util.Set;
73import java.util.Stack;
74
75/**
76 * The implementation of the volume manager service.
77 * <p>
78 * This implementation focuses on delivering a responsive UI. Most methods are
79 * asynchronous to external calls. For example, the task of setting a volume
80 * will update our internal state, but in a separate thread will set the system
81 * volume and later persist to the database. Similarly, setting the ringer mode
82 * will update the state and broadcast a change and in a separate thread later
83 * persist the ringer mode.
84 *
85 * @hide
86 */
87public class AudioService extends IAudioService.Stub {
88
89    private static final String TAG = "AudioService";
90
91    /** Debug remote control client/display feature */
92    protected static final boolean DEBUG_RC = false;
93
94    /** How long to delay before persisting a change in volume/ringer mode. */
95    private static final int PERSIST_DELAY = 3000;
96
97    private Context mContext;
98    private ContentResolver mContentResolver;
99    private boolean mVoiceCapable;
100
101    /** The UI */
102    private VolumePanel mVolumePanel;
103
104    // sendMsg() flags
105    /** Used when a message should be shared across all stream types. */
106    private static final int SHARED_MSG = -1;
107    /** If the msg is already queued, replace it with this one. */
108    private static final int SENDMSG_REPLACE = 0;
109    /** If the msg is already queued, ignore this one and leave the old. */
110    private static final int SENDMSG_NOOP = 1;
111    /** If the msg is already queued, queue this one and leave the old. */
112    private static final int SENDMSG_QUEUE = 2;
113
114    // AudioHandler message.whats
115    private static final int MSG_SET_SYSTEM_VOLUME = 0;
116    private static final int MSG_PERSIST_VOLUME = 1;
117    private static final int MSG_PERSIST_MASTER_VOLUME = 2;
118    private static final int MSG_PERSIST_RINGER_MODE = 3;
119    private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
120    private static final int MSG_MEDIA_SERVER_DIED = 5;
121    private static final int MSG_MEDIA_SERVER_STARTED = 6;
122    private static final int MSG_PLAY_SOUND_EFFECT = 7;
123    private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8;
124    private static final int MSG_LOAD_SOUND_EFFECTS = 9;
125    private static final int MSG_SET_FORCE_USE = 10;
126    private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11;
127    private static final int MSG_BT_HEADSET_CNCT_FAILED = 12;
128    private static final int MSG_RCDISPLAY_CLEAR = 13;
129    private static final int MSG_RCDISPLAY_UPDATE = 14;
130
131    private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
132    // Timeout for connection to bluetooth headset service
133    private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
134
135    /** @see AudioSystemThread */
136    private AudioSystemThread mAudioSystemThread;
137    /** @see AudioHandler */
138    private AudioHandler mAudioHandler;
139    /** @see VolumeStreamState */
140    private VolumeStreamState[] mStreamStates;
141    private SettingsObserver mSettingsObserver;
142
143    private int mMode;
144    private Object mSettingsLock = new Object();
145    private boolean mMediaServerOk;
146
147    private SoundPool mSoundPool;
148    private Object mSoundEffectsLock = new Object();
149    private static final int NUM_SOUNDPOOL_CHANNELS = 4;
150    private static final int SOUND_EFFECT_VOLUME = 1000;
151
152    // Internally master volume is a float in the 0.0 - 1.0 range,
153    // but to support integer based AudioManager API we translate it to 0 - 100
154    private static final int MAX_MASTER_VOLUME = 100;
155
156    /* Sound effect file names  */
157    private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
158    private static final String[] SOUND_EFFECT_FILES = new String[] {
159        "Effect_Tick.ogg",
160        "KeypressStandard.ogg",
161        "KeypressSpacebar.ogg",
162        "KeypressDelete.ogg",
163        "KeypressReturn.ogg"
164    };
165
166    /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
167     * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
168     * uses soundpool (second column) */
169    private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
170        {0, -1},  // FX_KEY_CLICK
171        {0, -1},  // FX_FOCUS_NAVIGATION_UP
172        {0, -1},  // FX_FOCUS_NAVIGATION_DOWN
173        {0, -1},  // FX_FOCUS_NAVIGATION_LEFT
174        {0, -1},  // FX_FOCUS_NAVIGATION_RIGHT
175        {1, -1},  // FX_KEYPRESS_STANDARD
176        {2, -1},  // FX_KEYPRESS_SPACEBAR
177        {3, -1},  // FX_FOCUS_DELETE
178        {4, -1}   // FX_FOCUS_RETURN
179    };
180
181   /** @hide Maximum volume index values for audio streams */
182    private int[] MAX_STREAM_VOLUME = new int[] {
183        5,  // STREAM_VOICE_CALL
184        7,  // STREAM_SYSTEM
185        7,  // STREAM_RING
186        15, // STREAM_MUSIC
187        7,  // STREAM_ALARM
188        7,  // STREAM_NOTIFICATION
189        15, // STREAM_BLUETOOTH_SCO
190        7,  // STREAM_SYSTEM_ENFORCED
191        15, // STREAM_DTMF
192        15  // STREAM_TTS
193    };
194    /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
195     * of another stream: This avoids multiplying the volume settings for hidden
196     * stream types that follow other stream behavior for volume settings
197     * NOTE: do not create loops in aliases! */
198    private int[] STREAM_VOLUME_ALIAS = new int[] {
199        AudioSystem.STREAM_VOICE_CALL,  // STREAM_VOICE_CALL
200        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM
201        AudioSystem.STREAM_RING,  // STREAM_RING
202        AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
203        AudioSystem.STREAM_ALARM,  // STREAM_ALARM
204        AudioSystem.STREAM_RING,   // STREAM_NOTIFICATION
205        AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
206        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM_ENFORCED
207        AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
208        AudioSystem.STREAM_MUSIC  // STREAM_TTS
209    };
210
211    private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
212        public void onError(int error) {
213            switch (error) {
214            case AudioSystem.AUDIO_STATUS_SERVER_DIED:
215                if (mMediaServerOk) {
216                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
217                            null, 1500);
218                    mMediaServerOk = false;
219                }
220                break;
221            case AudioSystem.AUDIO_STATUS_OK:
222                if (!mMediaServerOk) {
223                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
224                            null, 0);
225                    mMediaServerOk = true;
226                }
227                break;
228            default:
229                break;
230            }
231       }
232    };
233
234    /**
235     * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
236     * {@link AudioManager#RINGER_MODE_SILENT}, or
237     * {@link AudioManager#RINGER_MODE_VIBRATE}.
238     */
239    private int mRingerMode;
240
241    /** @see System#MODE_RINGER_STREAMS_AFFECTED */
242    private int mRingerModeAffectedStreams;
243
244    // Streams currently muted by ringer mode
245    private int mRingerModeMutedStreams;
246
247    /** @see System#MUTE_STREAMS_AFFECTED */
248    private int mMuteAffectedStreams;
249
250    /**
251     * Has multiple bits per vibrate type to indicate the type's vibrate
252     * setting. See {@link #setVibrateSetting(int, int)}.
253     * <p>
254     * NOTE: This is not the final decision of whether vibrate is on/off for the
255     * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
256     */
257    private int mVibrateSetting;
258
259    // Broadcast receiver for device connections intent broadcasts
260    private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
261
262    //  Broadcast receiver for media button broadcasts (separate from mReceiver to
263    //  independently change its priority)
264    private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
265
266    // Used to alter media button redirection when the phone is ringing.
267    private boolean mIsRinging = false;
268
269    // Devices currently connected
270    private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
271
272    // Forced device usage for communications
273    private int mForcedUseForComm;
274
275    // True if we have master volume support
276    private final boolean mUseMasterVolume;
277
278    private final int[] mMasterVolumeRamp;
279
280    // List of binder death handlers for setMode() client processes.
281    // The last process to have called setMode() is at the top of the list.
282    private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
283
284    // List of clients having issued a SCO start request
285    private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
286
287    // BluetoothHeadset API to control SCO connection
288    private BluetoothHeadset mBluetoothHeadset;
289
290    // Bluetooth headset device
291    private BluetoothDevice mBluetoothHeadsetDevice;
292
293    // Indicate if SCO audio connection is currently active and if the initiator is
294    // audio service (internal) or bluetooth headset (external)
295    private int mScoAudioState;
296    // SCO audio state is not active
297    private static final int SCO_STATE_INACTIVE = 0;
298    // SCO audio activation request waiting for headset service to connect
299    private static final int SCO_STATE_ACTIVATE_REQ = 1;
300    // SCO audio state is active or starting due to a local request to start a virtual call
301    private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
302    // SCO audio deactivation request waiting for headset service to connect
303    private static final int SCO_STATE_DEACTIVATE_REQ = 5;
304
305    // SCO audio state is active due to an action in BT handsfree (either voice recognition or
306    // in call audio)
307    private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
308    // Deactivation request for all SCO connections (initiated by audio mode change)
309    // waiting for headset service to connect
310    private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
311
312    // Current connection state indicated by bluetooth headset
313    private int mScoConnectionState;
314
315    // true if boot sequence has been completed
316    private boolean mBootCompleted;
317    // listener for SoundPool sample load completion indication
318    private SoundPoolCallback mSoundPoolCallBack;
319    // thread for SoundPool listener
320    private SoundPoolListenerThread mSoundPoolListenerThread;
321    // message looper for SoundPool listener
322    private Looper mSoundPoolLooper = null;
323    // default volume applied to sound played with playSoundEffect()
324    private static final int SOUND_EFFECT_DEFAULT_VOLUME_DB = -20;
325    // volume applied to sound played with playSoundEffect() read from ro.config.sound_fx_volume
326    private int SOUND_EFFECT_VOLUME_DB;
327    // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification
328    // stopped
329    private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
330    // previous volume adjustment direction received by checkForRingerModeChange()
331    private int mPrevVolDirection = AudioManager.ADJUST_SAME;
332    // Keyguard manager proxy
333    private KeyguardManager mKeyguardManager;
334
335    ///////////////////////////////////////////////////////////////////////////
336    // Construction
337    ///////////////////////////////////////////////////////////////////////////
338
339    /** @hide */
340    public AudioService(Context context) {
341        mContext = context;
342        mContentResolver = context.getContentResolver();
343        mVoiceCapable = mContext.getResources().getBoolean(
344                com.android.internal.R.bool.config_voice_capable);
345
346       // Intialized volume
347        MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
348            "ro.config.vc_call_vol_steps",
349           MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
350
351        SOUND_EFFECT_VOLUME_DB = SystemProperties.getInt(
352                "ro.config.sound_fx_volume",
353                SOUND_EFFECT_DEFAULT_VOLUME_DB);
354
355        mVolumePanel = new VolumePanel(context, this);
356        mForcedUseForComm = AudioSystem.FORCE_NONE;
357        createAudioSystemThread();
358        readPersistedSettings();
359        mSettingsObserver = new SettingsObserver();
360        createStreamStates();
361
362        mMode = AudioSystem.MODE_NORMAL;
363        mMediaServerOk = true;
364
365        // Call setRingerModeInt() to apply correct mute
366        // state on streams affected by ringer mode.
367        mRingerModeMutedStreams = 0;
368        setRingerModeInt(getRingerMode(), false);
369
370        AudioSystem.setErrorCallback(mAudioSystemCallback);
371
372        // Register for device connection intent broadcasts.
373        IntentFilter intentFilter =
374                new IntentFilter(Intent.ACTION_HEADSET_PLUG);
375
376        intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
377        intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
378        intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
379        intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
380        intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
381        intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
382        intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
383        intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
384        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
385        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
386        context.registerReceiver(mReceiver, intentFilter);
387
388        // Register for package removal intent broadcasts for media button receiver persistence
389        IntentFilter pkgFilter = new IntentFilter();
390        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
391        pkgFilter.addDataScheme("package");
392        context.registerReceiver(mReceiver, pkgFilter);
393
394        // Register for media button intent broadcasts.
395        intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
396        // Workaround for bug on priority setting
397        //intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
398        intentFilter.setPriority(Integer.MAX_VALUE);
399        context.registerReceiver(mMediaButtonReceiver, intentFilter);
400
401        // Register for phone state monitoring
402        TelephonyManager tmgr = (TelephonyManager)
403                context.getSystemService(Context.TELEPHONY_SERVICE);
404        tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
405
406        mUseMasterVolume = context.getResources().getBoolean(
407                com.android.internal.R.bool.config_useMasterVolume);
408        restoreMasterVolume();
409
410        mMasterVolumeRamp = context.getResources().getIntArray(
411                com.android.internal.R.array.config_masterVolumeRamp);
412    }
413
414    private void createAudioSystemThread() {
415        mAudioSystemThread = new AudioSystemThread();
416        mAudioSystemThread.start();
417        waitForAudioHandlerCreation();
418    }
419
420    /** Waits for the volume handler to be created by the other thread. */
421    private void waitForAudioHandlerCreation() {
422        synchronized(this) {
423            while (mAudioHandler == null) {
424                try {
425                    // Wait for mAudioHandler to be set by the other thread
426                    wait();
427                } catch (InterruptedException e) {
428                    Log.e(TAG, "Interrupted while waiting on volume handler.");
429                }
430            }
431        }
432    }
433
434    private void createStreamStates() {
435        int numStreamTypes = AudioSystem.getNumStreamTypes();
436        VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
437
438        for (int i = 0; i < numStreamTypes; i++) {
439            streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
440        }
441
442        // Correct stream index values for streams with aliases
443        for (int i = 0; i < numStreamTypes; i++) {
444            if (STREAM_VOLUME_ALIAS[i] != i) {
445                int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
446                streams[i].mIndex = streams[i].getValidIndex(index);
447                setStreamVolumeIndex(i, index);
448                index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
449                streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
450            }
451        }
452    }
453
454    private void readPersistedSettings() {
455        final ContentResolver cr = mContentResolver;
456
457        mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
458        // sanity check in case the settings are restored from a device with incompatible
459        // ringer modes
460        if (!AudioManager.isValidRingerMode(mRingerMode)) {
461            mRingerMode = AudioManager.RINGER_MODE_NORMAL;
462            System.putInt(cr, System.MODE_RINGER, mRingerMode);
463        }
464
465        mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
466
467        // make sure settings for ringer mode are consistent with device type: non voice capable
468        // devices (tablets) include media stream in silent mode whereas phones don't.
469        mRingerModeAffectedStreams = Settings.System.getInt(cr,
470                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
471                ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
472                 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
473        if (mVoiceCapable) {
474            mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
475        } else {
476            mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
477        }
478        Settings.System.putInt(cr,
479                Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams);
480
481        mMuteAffectedStreams = System.getInt(cr,
482                System.MUTE_STREAMS_AFFECTED,
483                ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
484
485        // Each stream will read its own persisted settings
486
487        // Broadcast the sticky intent
488        broadcastRingerMode();
489
490        // Broadcast vibrate settings
491        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
492        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
493
494        // Restore the default media button receiver from the system settings
495        restoreMediaButtonReceiver();
496    }
497
498    private void setStreamVolumeIndex(int stream, int index) {
499        AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
500    }
501
502    private int rescaleIndex(int index, int srcStream, int dstStream) {
503        return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
504    }
505
506    ///////////////////////////////////////////////////////////////////////////
507    // IPC methods
508    ///////////////////////////////////////////////////////////////////////////
509
510    /** @see AudioManager#adjustVolume(int, int) */
511    public void adjustVolume(int direction, int flags) {
512        adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
513    }
514
515    /** @see AudioManager#adjustVolume(int, int, int) */
516    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
517
518        int streamType;
519        if ((flags & AudioManager.FLAG_FORCE_STREAM) != 0) {
520            streamType = suggestedStreamType;
521        } else {
522            streamType = getActiveStreamType(suggestedStreamType);
523        }
524
525        // Play sounds on STREAM_RING only and if lock screen is not on.
526        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
527                ((STREAM_VOLUME_ALIAS[streamType] != AudioSystem.STREAM_RING)
528                 || (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
529            flags &= ~AudioManager.FLAG_PLAY_SOUND;
530        }
531
532        adjustStreamVolume(streamType, direction, flags);
533    }
534
535    /** @see AudioManager#adjustStreamVolume(int, int, int) */
536    public void adjustStreamVolume(int streamType, int direction, int flags) {
537        ensureValidDirection(direction);
538        ensureValidStreamType(streamType);
539
540        // use stream type alias here so that streams with same alias have the same behavior,
541        // including with regard to silent mode control (e.g the use of STREAM_RING below and in
542        // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
543        int streamTypeAlias = STREAM_VOLUME_ALIAS[streamType];
544        VolumeStreamState streamState = mStreamStates[streamTypeAlias];
545        final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
546        boolean adjustVolume = true;
547
548        // If either the client forces allowing ringer modes for this adjustment,
549        // or the stream type is one that is affected by ringer modes
550        if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
551                streamTypeAlias == AudioSystem.STREAM_RING ||
552                (!mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_MUSIC)) {
553            // do not vibrate if already in vibrate mode
554            if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
555                flags &= ~AudioManager.FLAG_VIBRATE;
556            }
557            // Check if the ringer mode changes with this volume adjustment. If
558            // it does, it will handle adjusting the volume, so we won't below
559            adjustVolume = checkForRingerModeChange(oldIndex, direction, streamTypeAlias);
560        }
561
562        // If stream is muted, adjust last audible index only
563        int index;
564        if (streamState.muteCount() != 0) {
565            if (adjustVolume) {
566                // adjust volume on all stream types sharing the same alias otherwise a query
567                // on last audible index for an alias would not give the correct value
568                int numStreamTypes = AudioSystem.getNumStreamTypes();
569                for (int i = numStreamTypes - 1; i >= 0; i--) {
570                    if (STREAM_VOLUME_ALIAS[i] == streamTypeAlias) {
571                        VolumeStreamState s = mStreamStates[i];
572
573                        s.adjustLastAudibleIndex(direction);
574                        // Post a persist volume msg
575                        sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, i,
576                                SENDMSG_REPLACE, 0, 1, s, PERSIST_DELAY);
577                    }
578                }
579            }
580            index = streamState.mLastAudibleIndex;
581        } else {
582            if (adjustVolume && streamState.adjustIndex(direction)) {
583                // Post message to set system volume (it in turn will post a message
584                // to persist). Do not change volume if stream is muted.
585                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamTypeAlias, SENDMSG_NOOP, 0, 0,
586                        streamState, 0);
587            }
588            index = streamState.mIndex;
589        }
590
591        sendVolumeUpdate(streamType, oldIndex, index, flags);
592    }
593
594    /** @see AudioManager#adjustMasterVolume(int) */
595    public void adjustMasterVolume(int direction, int flags) {
596        ensureValidDirection(direction);
597        int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
598        int delta = 0;
599        for (int i = 0; i < mMasterVolumeRamp.length; i += 2) {
600            int testVolume = mMasterVolumeRamp[i];
601            int testDelta =  mMasterVolumeRamp[i + 1];
602            if (direction == AudioManager.ADJUST_RAISE) {
603                if (volume >= testVolume) {
604                    delta = testDelta;
605                } else {
606                    break;
607                }
608            } else if (direction == AudioManager.ADJUST_LOWER) {
609                if (volume - testDelta >= testVolume) {
610                    delta = -testDelta;
611                } else {
612                    break;
613                }
614            }
615        }
616//        Log.d(TAG, "adjustMasterVolume volume: " + volume + " delta: " + delta + " direction: " + direction);
617        setMasterVolume(volume + delta, flags);
618    }
619
620    /** @see AudioManager#setStreamVolume(int, int, int) */
621    public void setStreamVolume(int streamType, int index, int flags) {
622        ensureValidStreamType(streamType);
623        VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
624
625        final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
626
627        // setting ring or notifications volume to 0 on voice capable devices enters silent mode
628        if (mVoiceCapable && (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
629                (STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING))) {
630            int newRingerMode = mRingerMode;
631            if (index == 0) {
632                newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
633                    ? AudioManager.RINGER_MODE_VIBRATE
634                    : AudioManager.RINGER_MODE_SILENT;
635                setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
636            } else {
637                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
638            }
639            if (newRingerMode != mRingerMode) {
640                setRingerMode(newRingerMode);
641            }
642        }
643
644        index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
645        setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
646
647        index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
648
649        sendVolumeUpdate(streamType, oldIndex, index, flags);
650    }
651
652    // UI update and Broadcast Intent
653    private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
654        if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
655            streamType = AudioSystem.STREAM_NOTIFICATION;
656        }
657
658        mVolumePanel.postVolumeChanged(streamType, flags);
659
660        oldIndex = (oldIndex + 5) / 10;
661        index = (index + 5) / 10;
662        Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
663        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
664        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
665        intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
666        mContext.sendBroadcast(intent);
667    }
668
669    // UI update and Broadcast Intent
670    private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) {
671        mVolumePanel.postMasterVolumeChanged(flags);
672
673        Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
674        intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
675        intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
676        mContext.sendBroadcast(intent);
677    }
678
679    // UI update and Broadcast Intent
680    private void sendMasterMuteUpdate(boolean muted, int flags) {
681        mVolumePanel.postMasterMuteChanged(flags);
682
683        Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
684        intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
685        long origCallerIdentityToken = Binder.clearCallingIdentity();
686        mContext.sendStickyBroadcast(intent);
687        Binder.restoreCallingIdentity(origCallerIdentityToken);
688    }
689
690    /**
691     * Sets the stream state's index, and posts a message to set system volume.
692     * This will not call out to the UI. Assumes a valid stream type.
693     *
694     * @param streamType Type of the stream
695     * @param index Desired volume index of the stream
696     * @param force If true, set the volume even if the desired volume is same
697     * as the current volume.
698     * @param lastAudible If true, stores new index as last audible one
699     */
700    private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
701        VolumeStreamState streamState = mStreamStates[streamType];
702
703        // If stream is muted, set last audible index only
704        if (streamState.muteCount() != 0) {
705            // Do not allow last audible index to be 0
706            if (index != 0) {
707                streamState.setLastAudibleIndex(index);
708                // Post a persist volume msg
709                sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
710                        SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
711            }
712        } else {
713            if (streamState.setIndex(index, lastAudible) || force) {
714                // Post message to set system volume (it in turn will post a message
715                // to persist).
716                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
717                        streamState, 0);
718            }
719        }
720    }
721
722    /** @see AudioManager#setStreamSolo(int, boolean) */
723    public void setStreamSolo(int streamType, boolean state, IBinder cb) {
724        for (int stream = 0; stream < mStreamStates.length; stream++) {
725            if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
726            // Bring back last audible volume
727            mStreamStates[stream].mute(cb, state);
728         }
729    }
730
731    /** @see AudioManager#setStreamMute(int, boolean) */
732    public void setStreamMute(int streamType, boolean state, IBinder cb) {
733        if (isStreamAffectedByMute(streamType)) {
734            mStreamStates[streamType].mute(cb, state);
735        }
736    }
737
738    /** get stream mute state. */
739    public boolean isStreamMute(int streamType) {
740        return (mStreamStates[streamType].muteCount() != 0);
741    }
742
743    /** @see AudioManager#setMasterMute(boolean, IBinder) */
744    public void setMasterMute(boolean state, IBinder cb) {
745        if (state != AudioSystem.getMasterMute()) {
746            AudioSystem.setMasterMute(state);
747            sendMasterMuteUpdate(state, AudioManager.FLAG_SHOW_UI);
748        }
749    }
750
751    /** get master mute state. */
752    public boolean isMasterMute() {
753        return AudioSystem.getMasterMute();
754    }
755
756    /** @see AudioManager#getStreamVolume(int) */
757    public int getStreamVolume(int streamType) {
758        ensureValidStreamType(streamType);
759        return (mStreamStates[streamType].mIndex + 5) / 10;
760    }
761
762    public int getMasterVolume() {
763        if (isMasterMute()) return 0;
764        return getLastAudibleMasterVolume();
765    }
766
767    public void setMasterVolume(int volume, int flags) {
768        if (volume < 0) {
769            volume = 0;
770        } else if (volume > MAX_MASTER_VOLUME) {
771            volume = MAX_MASTER_VOLUME;
772        }
773        doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags);
774    }
775
776    private void doSetMasterVolume(float volume, int flags) {
777        // don't allow changing master volume when muted
778        if (!AudioSystem.getMasterMute()) {
779            int oldVolume = getMasterVolume();
780            AudioSystem.setMasterVolume(volume);
781
782            int newVolume = getMasterVolume();
783            if (newVolume != oldVolume) {
784                // Post a persist master volume msg
785                sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, 0, SENDMSG_REPLACE,
786                        Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
787                sendMasterVolumeUpdate(flags, oldVolume, newVolume);
788            }
789        }
790    }
791
792    /** @see AudioManager#getStreamMaxVolume(int) */
793    public int getStreamMaxVolume(int streamType) {
794        ensureValidStreamType(streamType);
795        return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
796    }
797
798    public int getMasterMaxVolume() {
799        return MAX_MASTER_VOLUME;
800    }
801
802    /** Get last audible volume before stream was muted. */
803    public int getLastAudibleStreamVolume(int streamType) {
804        ensureValidStreamType(streamType);
805        return (mStreamStates[streamType].mLastAudibleIndex + 5) / 10;
806    }
807
808    /** Get last audible master volume before it was muted. */
809    public int getLastAudibleMasterVolume() {
810        return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
811    }
812
813    /** @see AudioManager#getRingerMode() */
814    public int getRingerMode() {
815        return mRingerMode;
816    }
817
818    /** @see AudioManager#setRingerMode(int) */
819    public void setRingerMode(int ringerMode) {
820        synchronized (mSettingsLock) {
821            if (ringerMode != mRingerMode) {
822                setRingerModeInt(ringerMode, true);
823                // Send sticky broadcast
824                broadcastRingerMode();
825            }
826        }
827    }
828
829    private void setRingerModeInt(int ringerMode, boolean persist) {
830        mRingerMode = ringerMode;
831
832        // Mute stream if not previously muted by ringer mode and ringer mode
833        // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
834        // Unmute stream if previously muted by ringer mode and ringer mode
835        // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
836        int numStreamTypes = AudioSystem.getNumStreamTypes();
837        for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
838            if (isStreamMutedByRingerMode(streamType)) {
839                if (!isStreamAffectedByRingerMode(streamType) ||
840                    mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
841                    // ring and notifications volume should never be 0 when not silenced
842                    // on voice capable devices
843                    if (mVoiceCapable &&
844                            STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING &&
845                            mStreamStates[streamType].mLastAudibleIndex == 0) {
846                        mStreamStates[streamType].mLastAudibleIndex = 10;
847                    }
848                    mStreamStates[streamType].mute(null, false);
849                    mRingerModeMutedStreams &= ~(1 << streamType);
850                }
851            } else {
852                if (isStreamAffectedByRingerMode(streamType) &&
853                    mRingerMode != AudioManager.RINGER_MODE_NORMAL) {
854                   mStreamStates[streamType].mute(null, true);
855                   mRingerModeMutedStreams |= (1 << streamType);
856               }
857            }
858        }
859
860        // Post a persist ringer mode msg
861        if (persist) {
862            sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
863                    SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
864        }
865    }
866
867    private void restoreMasterVolume() {
868        if (mUseMasterVolume) {
869            float volume = Settings.System.getFloat(mContentResolver,
870                    Settings.System.VOLUME_MASTER, -1.0f);
871            if (volume >= 0.0f) {
872                AudioSystem.setMasterVolume(volume);
873            }
874        }
875    }
876
877    /** @see AudioManager#shouldVibrate(int) */
878    public boolean shouldVibrate(int vibrateType) {
879
880        switch (getVibrateSetting(vibrateType)) {
881
882            case AudioManager.VIBRATE_SETTING_ON:
883                return mRingerMode != AudioManager.RINGER_MODE_SILENT;
884
885            case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
886                return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
887
888            case AudioManager.VIBRATE_SETTING_OFF:
889                // return false, even for incoming calls
890                return false;
891
892            default:
893                return false;
894        }
895    }
896
897    /** @see AudioManager#getVibrateSetting(int) */
898    public int getVibrateSetting(int vibrateType) {
899        return (mVibrateSetting >> (vibrateType * 2)) & 3;
900    }
901
902    /** @see AudioManager#setVibrateSetting(int, int) */
903    public void setVibrateSetting(int vibrateType, int vibrateSetting) {
904
905        mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
906
907        // Broadcast change
908        broadcastVibrateSetting(vibrateType);
909
910        // Post message to set ringer mode (it in turn will post a message
911        // to persist)
912        sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
913                null, 0);
914    }
915
916    /**
917     * @see #setVibrateSetting(int, int)
918     */
919    public static int getValueForVibrateSetting(int existingValue, int vibrateType,
920            int vibrateSetting) {
921
922        // First clear the existing setting. Each vibrate type has two bits in
923        // the value. Note '3' is '11' in binary.
924        existingValue &= ~(3 << (vibrateType * 2));
925
926        // Set into the old value
927        existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
928
929        return existingValue;
930    }
931
932    private class SetModeDeathHandler implements IBinder.DeathRecipient {
933        private IBinder mCb; // To be notified of client's death
934        private int mPid;
935        private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
936
937        SetModeDeathHandler(IBinder cb, int pid) {
938            mCb = cb;
939            mPid = pid;
940        }
941
942        public void binderDied() {
943            int newModeOwnerPid = 0;
944            synchronized(mSetModeDeathHandlers) {
945                Log.w(TAG, "setMode() client died");
946                int index = mSetModeDeathHandlers.indexOf(this);
947                if (index < 0) {
948                    Log.w(TAG, "unregistered setMode() client died");
949                } else {
950                    newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid);
951                }
952            }
953            // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
954            // SCO connections not started by the application changing the mode
955            if (newModeOwnerPid != 0) {
956                 disconnectBluetoothSco(newModeOwnerPid);
957            }
958        }
959
960        public int getPid() {
961            return mPid;
962        }
963
964        public void setMode(int mode) {
965            mMode = mode;
966        }
967
968        public int getMode() {
969            return mMode;
970        }
971
972        public IBinder getBinder() {
973            return mCb;
974        }
975    }
976
977    /** @see AudioManager#setMode(int) */
978    public void setMode(int mode, IBinder cb) {
979        if (!checkAudioSettingsPermission("setMode()")) {
980            return;
981        }
982
983        if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
984            return;
985        }
986
987        int newModeOwnerPid = 0;
988        synchronized(mSetModeDeathHandlers) {
989            if (mode == AudioSystem.MODE_CURRENT) {
990                mode = mMode;
991            }
992            newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid());
993        }
994        // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
995        // SCO connections not started by the application changing the mode
996        if (newModeOwnerPid != 0) {
997             disconnectBluetoothSco(newModeOwnerPid);
998        }
999    }
1000
1001    // must be called synchronized on mSetModeDeathHandlers
1002    // setModeInt() returns a valid PID if the audio mode was successfully set to
1003    // any mode other than NORMAL.
1004    int setModeInt(int mode, IBinder cb, int pid) {
1005        int newModeOwnerPid = 0;
1006        if (cb == null) {
1007            Log.e(TAG, "setModeInt() called with null binder");
1008            return newModeOwnerPid;
1009        }
1010
1011        SetModeDeathHandler hdlr = null;
1012        Iterator iter = mSetModeDeathHandlers.iterator();
1013        while (iter.hasNext()) {
1014            SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
1015            if (h.getPid() == pid) {
1016                hdlr = h;
1017                // Remove from client list so that it is re-inserted at top of list
1018                iter.remove();
1019                hdlr.getBinder().unlinkToDeath(hdlr, 0);
1020                break;
1021            }
1022        }
1023        int status = AudioSystem.AUDIO_STATUS_OK;
1024        do {
1025            if (mode == AudioSystem.MODE_NORMAL) {
1026                // get new mode from client at top the list if any
1027                if (!mSetModeDeathHandlers.isEmpty()) {
1028                    hdlr = mSetModeDeathHandlers.get(0);
1029                    cb = hdlr.getBinder();
1030                    mode = hdlr.getMode();
1031                }
1032            } else {
1033                if (hdlr == null) {
1034                    hdlr = new SetModeDeathHandler(cb, pid);
1035                }
1036                // Register for client death notification
1037                try {
1038                    cb.linkToDeath(hdlr, 0);
1039                } catch (RemoteException e) {
1040                    // Client has died!
1041                    Log.w(TAG, "setMode() could not link to "+cb+" binder death");
1042                }
1043
1044                // Last client to call setMode() is always at top of client list
1045                // as required by SetModeDeathHandler.binderDied()
1046                mSetModeDeathHandlers.add(0, hdlr);
1047                hdlr.setMode(mode);
1048            }
1049
1050            if (mode != mMode) {
1051                status = AudioSystem.setPhoneState(mode);
1052                if (status == AudioSystem.AUDIO_STATUS_OK) {
1053                    // automatically handle audio focus for mode changes
1054                    handleFocusForCalls(mMode, mode, cb);
1055                    mMode = mode;
1056                } else {
1057                    if (hdlr != null) {
1058                        mSetModeDeathHandlers.remove(hdlr);
1059                        cb.unlinkToDeath(hdlr, 0);
1060                    }
1061                    // force reading new top of mSetModeDeathHandlers stack
1062                    mode = AudioSystem.MODE_NORMAL;
1063                }
1064            } else {
1065                status = AudioSystem.AUDIO_STATUS_OK;
1066            }
1067        } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
1068
1069        if (status == AudioSystem.AUDIO_STATUS_OK) {
1070            if (mode != AudioSystem.MODE_NORMAL) {
1071                if (mSetModeDeathHandlers.isEmpty()) {
1072                    Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
1073                } else {
1074                    newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
1075                }
1076            }
1077            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
1078            int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
1079            setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false);
1080        }
1081        return newModeOwnerPid;
1082    }
1083
1084    /** pre-condition: oldMode != newMode */
1085    private void handleFocusForCalls(int oldMode, int newMode, IBinder cb) {
1086        // if ringing
1087        if (newMode == AudioSystem.MODE_RINGTONE) {
1088            // if not ringing silently
1089            int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
1090            if (ringVolume > 0) {
1091                // request audio focus for the communication focus entry
1092                requestAudioFocus(AudioManager.STREAM_RING,
1093                        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
1094                        null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
1095                        IN_VOICE_COMM_FOCUS_ID /*clientId*/,
1096                        "system");
1097
1098            }
1099        }
1100        // if entering call
1101        else if ((newMode == AudioSystem.MODE_IN_CALL)
1102                || (newMode == AudioSystem.MODE_IN_COMMUNICATION)) {
1103            // request audio focus for the communication focus entry
1104            // (it's ok if focus was already requested during ringing)
1105            requestAudioFocus(AudioManager.STREAM_RING,
1106                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
1107                    null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
1108                    IN_VOICE_COMM_FOCUS_ID /*clientId*/,
1109                    "system");
1110        }
1111        // if exiting call
1112        else if (newMode == AudioSystem.MODE_NORMAL) {
1113            // abandon audio focus for communication focus entry
1114            abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
1115        }
1116    }
1117
1118    /** @see AudioManager#getMode() */
1119    public int getMode() {
1120        return mMode;
1121    }
1122
1123    /** @see AudioManager#playSoundEffect(int) */
1124    public void playSoundEffect(int effectType) {
1125        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
1126                effectType, -1, null, 0);
1127    }
1128
1129    /** @see AudioManager#playSoundEffect(int, float) */
1130    public void playSoundEffectVolume(int effectType, float volume) {
1131        loadSoundEffects();
1132        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
1133                effectType, (int) (volume * 1000), null, 0);
1134    }
1135
1136    /**
1137     * Loads samples into the soundpool.
1138     * This method must be called at when sound effects are enabled
1139     */
1140    public boolean loadSoundEffects() {
1141        int status;
1142
1143        synchronized (mSoundEffectsLock) {
1144            if (!mBootCompleted) {
1145                Log.w(TAG, "loadSoundEffects() called before boot complete");
1146                return false;
1147            }
1148
1149            if (mSoundPool != null) {
1150                return true;
1151            }
1152            mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
1153            if (mSoundPool == null) {
1154                Log.w(TAG, "loadSoundEffects() could not allocate sound pool");
1155                return false;
1156            }
1157
1158            try {
1159                mSoundPoolCallBack = null;
1160                mSoundPoolListenerThread = new SoundPoolListenerThread();
1161                mSoundPoolListenerThread.start();
1162                // Wait for mSoundPoolCallBack to be set by the other thread
1163                mSoundEffectsLock.wait();
1164            } catch (InterruptedException e) {
1165                Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
1166            }
1167
1168            if (mSoundPoolCallBack == null) {
1169                Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
1170                if (mSoundPoolLooper != null) {
1171                    mSoundPoolLooper.quit();
1172                    mSoundPoolLooper = null;
1173                }
1174                mSoundPoolListenerThread = null;
1175                mSoundPool.release();
1176                mSoundPool = null;
1177                return false;
1178            }
1179            /*
1180             * poolId table: The value -1 in this table indicates that corresponding
1181             * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
1182             * Once loaded, the value in poolId is the sample ID and the same
1183             * sample can be reused for another effect using the same file.
1184             */
1185            int[] poolId = new int[SOUND_EFFECT_FILES.length];
1186            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1187                poolId[fileIdx] = -1;
1188            }
1189            /*
1190             * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
1191             * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
1192             * this indicates we have a valid sample loaded for this effect.
1193             */
1194
1195            int lastSample = 0;
1196            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1197                // Do not load sample if this effect uses the MediaPlayer
1198                if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
1199                    continue;
1200                }
1201                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
1202                    String filePath = Environment.getRootDirectory()
1203                            + SOUND_EFFECTS_PATH
1204                            + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
1205                    int sampleId = mSoundPool.load(filePath, 0);
1206                    if (sampleId <= 0) {
1207                        Log.w(TAG, "Soundpool could not load file: "+filePath);
1208                    } else {
1209                        SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
1210                        poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
1211                        lastSample = sampleId;
1212                    }
1213                } else {
1214                    SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
1215                }
1216            }
1217            // wait for all samples to be loaded
1218            if (lastSample != 0) {
1219                mSoundPoolCallBack.setLastSample(lastSample);
1220
1221                try {
1222                    mSoundEffectsLock.wait();
1223                    status = mSoundPoolCallBack.status();
1224                } catch (java.lang.InterruptedException e) {
1225                    Log.w(TAG, "Interrupted while waiting sound pool callback.");
1226                    status = -1;
1227                }
1228            } else {
1229                status = -1;
1230            }
1231
1232            if (mSoundPoolLooper != null) {
1233                mSoundPoolLooper.quit();
1234                mSoundPoolLooper = null;
1235            }
1236            mSoundPoolListenerThread = null;
1237            if (status != 0) {
1238                Log.w(TAG,
1239                        "loadSoundEffects(), Error "
1240                                + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
1241                                + " while loading samples");
1242                for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1243                    if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
1244                        SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1245                    }
1246                }
1247
1248                mSoundPool.release();
1249                mSoundPool = null;
1250            }
1251        }
1252        return (status == 0);
1253    }
1254
1255    /**
1256     *  Unloads samples from the sound pool.
1257     *  This method can be called to free some memory when
1258     *  sound effects are disabled.
1259     */
1260    public void unloadSoundEffects() {
1261        synchronized (mSoundEffectsLock) {
1262            if (mSoundPool == null) {
1263                return;
1264            }
1265
1266            mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
1267            mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
1268
1269            int[] poolId = new int[SOUND_EFFECT_FILES.length];
1270            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1271                poolId[fileIdx] = 0;
1272            }
1273
1274            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1275                if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
1276                    continue;
1277                }
1278                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
1279                    mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
1280                    SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1281                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
1282                }
1283            }
1284            mSoundPool.release();
1285            mSoundPool = null;
1286        }
1287    }
1288
1289    class SoundPoolListenerThread extends Thread {
1290        public SoundPoolListenerThread() {
1291            super("SoundPoolListenerThread");
1292        }
1293
1294        @Override
1295        public void run() {
1296
1297            Looper.prepare();
1298            mSoundPoolLooper = Looper.myLooper();
1299
1300            synchronized (mSoundEffectsLock) {
1301                if (mSoundPool != null) {
1302                    mSoundPoolCallBack = new SoundPoolCallback();
1303                    mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
1304                }
1305                mSoundEffectsLock.notify();
1306            }
1307            Looper.loop();
1308        }
1309    }
1310
1311    private final class SoundPoolCallback implements
1312            android.media.SoundPool.OnLoadCompleteListener {
1313
1314        int mStatus;
1315        int mLastSample;
1316
1317        public int status() {
1318            return mStatus;
1319        }
1320
1321        public void setLastSample(int sample) {
1322            mLastSample = sample;
1323        }
1324
1325        public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
1326            synchronized (mSoundEffectsLock) {
1327                if (status != 0) {
1328                    mStatus = status;
1329                }
1330                if (sampleId == mLastSample) {
1331                    mSoundEffectsLock.notify();
1332                }
1333            }
1334        }
1335    }
1336
1337    /** @see AudioManager#reloadAudioSettings() */
1338    public void reloadAudioSettings() {
1339        // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
1340        readPersistedSettings();
1341
1342        // restore volume settings
1343        int numStreamTypes = AudioSystem.getNumStreamTypes();
1344        for (int streamType = 0; streamType < numStreamTypes; streamType++) {
1345            VolumeStreamState streamState = mStreamStates[streamType];
1346
1347            String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
1348            String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1349            int index = Settings.System.getInt(mContentResolver,
1350                                           settingName,
1351                                           AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1352            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
1353                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
1354            } else {
1355                index *= 10;
1356            }
1357            streamState.mIndex = streamState.getValidIndex(index);
1358
1359            index = (index + 5) / 10;
1360            index = Settings.System.getInt(mContentResolver,
1361                                            lastAudibleSettingName,
1362                                            (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1363            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
1364                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
1365            } else {
1366                index *= 10;
1367            }
1368            streamState.mLastAudibleIndex = streamState.getValidIndex(index);
1369
1370            // unmute stream that was muted but is not affect by mute anymore
1371            if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
1372                int size = streamState.mDeathHandlers.size();
1373                for (int i = 0; i < size; i++) {
1374                    streamState.mDeathHandlers.get(i).mMuteCount = 1;
1375                    streamState.mDeathHandlers.get(i).mute(false);
1376                }
1377            }
1378            // apply stream volume
1379            if (streamState.muteCount() == 0) {
1380                setStreamVolumeIndex(streamType, streamState.mIndex);
1381            }
1382        }
1383
1384        // apply new ringer mode
1385        setRingerModeInt(getRingerMode(), false);
1386    }
1387
1388    /** @see AudioManager#setSpeakerphoneOn() */
1389    public void setSpeakerphoneOn(boolean on){
1390        if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
1391            return;
1392        }
1393        mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
1394
1395        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1396                AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
1397    }
1398
1399    /** @see AudioManager#isSpeakerphoneOn() */
1400    public boolean isSpeakerphoneOn() {
1401        return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
1402    }
1403
1404    /** @see AudioManager#setBluetoothScoOn() */
1405    public void setBluetoothScoOn(boolean on){
1406        if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
1407            return;
1408        }
1409        mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
1410
1411        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1412                AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
1413        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1414                AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
1415    }
1416
1417    /** @see AudioManager#isBluetoothScoOn() */
1418    public boolean isBluetoothScoOn() {
1419        return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
1420    }
1421
1422    /** @see AudioManager#startBluetoothSco() */
1423    public void startBluetoothSco(IBinder cb){
1424        if (!checkAudioSettingsPermission("startBluetoothSco()") ||
1425                !mBootCompleted) {
1426            return;
1427        }
1428        ScoClient client = getScoClient(cb, true);
1429        client.incCount();
1430    }
1431
1432    /** @see AudioManager#stopBluetoothSco() */
1433    public void stopBluetoothSco(IBinder cb){
1434        if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
1435                !mBootCompleted) {
1436            return;
1437        }
1438        ScoClient client = getScoClient(cb, false);
1439        if (client != null) {
1440            client.decCount();
1441        }
1442    }
1443
1444    private class ScoClient implements IBinder.DeathRecipient {
1445        private IBinder mCb; // To be notified of client's death
1446        private int mCreatorPid;
1447        private int mStartcount; // number of SCO connections started by this client
1448
1449        ScoClient(IBinder cb) {
1450            mCb = cb;
1451            mCreatorPid = Binder.getCallingPid();
1452            mStartcount = 0;
1453        }
1454
1455        public void binderDied() {
1456            synchronized(mScoClients) {
1457                Log.w(TAG, "SCO client died");
1458                int index = mScoClients.indexOf(this);
1459                if (index < 0) {
1460                    Log.w(TAG, "unregistered SCO client died");
1461                } else {
1462                    clearCount(true);
1463                    mScoClients.remove(this);
1464                }
1465            }
1466        }
1467
1468        public void incCount() {
1469            synchronized(mScoClients) {
1470                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
1471                if (mStartcount == 0) {
1472                    try {
1473                        mCb.linkToDeath(this, 0);
1474                    } catch (RemoteException e) {
1475                        // client has already died!
1476                        Log.w(TAG, "ScoClient  incCount() could not link to "+mCb+" binder death");
1477                    }
1478                }
1479                mStartcount++;
1480            }
1481        }
1482
1483        public void decCount() {
1484            synchronized(mScoClients) {
1485                if (mStartcount == 0) {
1486                    Log.w(TAG, "ScoClient.decCount() already 0");
1487                } else {
1488                    mStartcount--;
1489                    if (mStartcount == 0) {
1490                        try {
1491                            mCb.unlinkToDeath(this, 0);
1492                        } catch (NoSuchElementException e) {
1493                            Log.w(TAG, "decCount() going to 0 but not registered to binder");
1494                        }
1495                    }
1496                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1497                }
1498            }
1499        }
1500
1501        public void clearCount(boolean stopSco) {
1502            synchronized(mScoClients) {
1503                if (mStartcount != 0) {
1504                    try {
1505                        mCb.unlinkToDeath(this, 0);
1506                    } catch (NoSuchElementException e) {
1507                        Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
1508                    }
1509                }
1510                mStartcount = 0;
1511                if (stopSco) {
1512                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1513                }
1514            }
1515        }
1516
1517        public int getCount() {
1518            return mStartcount;
1519        }
1520
1521        public IBinder getBinder() {
1522            return mCb;
1523        }
1524
1525        public int getPid() {
1526            return mCreatorPid;
1527        }
1528
1529        public int totalCount() {
1530            synchronized(mScoClients) {
1531                int count = 0;
1532                int size = mScoClients.size();
1533                for (int i = 0; i < size; i++) {
1534                    count += mScoClients.get(i).getCount();
1535                }
1536                return count;
1537            }
1538        }
1539
1540        private void requestScoState(int state) {
1541            checkScoAudioState();
1542            if (totalCount() == 0) {
1543                if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
1544                    // Make sure that the state transitions to CONNECTING even if we cannot initiate
1545                    // the connection.
1546                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
1547                    // Accept SCO audio activation only in NORMAL audio mode or if the mode is
1548                    // currently controlled by the same client process.
1549                    synchronized(mSetModeDeathHandlers) {
1550                        if ((mSetModeDeathHandlers.isEmpty() ||
1551                                mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
1552                                (mScoAudioState == SCO_STATE_INACTIVE ||
1553                                 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
1554                            if (mScoAudioState == SCO_STATE_INACTIVE) {
1555                                if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1556                                    if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1557                                            mBluetoothHeadsetDevice)) {
1558                                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1559                                    } else {
1560                                        broadcastScoConnectionState(
1561                                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1562                                    }
1563                                } else if (getBluetoothHeadset()) {
1564                                    mScoAudioState = SCO_STATE_ACTIVATE_REQ;
1565                                }
1566                            } else {
1567                                mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1568                                broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
1569                            }
1570                        } else {
1571                            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1572                        }
1573                    }
1574                } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
1575                              (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
1576                               mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
1577                    if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
1578                        if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1579                            if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
1580                                    mBluetoothHeadsetDevice)) {
1581                                mScoAudioState = SCO_STATE_INACTIVE;
1582                                broadcastScoConnectionState(
1583                                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1584                            }
1585                        } else if (getBluetoothHeadset()) {
1586                            mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
1587                        }
1588                    } else {
1589                        mScoAudioState = SCO_STATE_INACTIVE;
1590                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1591                    }
1592                }
1593            }
1594        }
1595    }
1596
1597    private void checkScoAudioState() {
1598        if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
1599                mScoAudioState == SCO_STATE_INACTIVE &&
1600                mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
1601                != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1602            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
1603        }
1604    }
1605
1606    private ScoClient getScoClient(IBinder cb, boolean create) {
1607        synchronized(mScoClients) {
1608            ScoClient client = null;
1609            int size = mScoClients.size();
1610            for (int i = 0; i < size; i++) {
1611                client = mScoClients.get(i);
1612                if (client.getBinder() == cb)
1613                    return client;
1614            }
1615            if (create) {
1616                client = new ScoClient(cb);
1617                mScoClients.add(client);
1618            }
1619            return client;
1620        }
1621    }
1622
1623    public void clearAllScoClients(int exceptPid, boolean stopSco) {
1624        synchronized(mScoClients) {
1625            ScoClient savedClient = null;
1626            int size = mScoClients.size();
1627            for (int i = 0; i < size; i++) {
1628                ScoClient cl = mScoClients.get(i);
1629                if (cl.getPid() != exceptPid) {
1630                    cl.clearCount(stopSco);
1631                } else {
1632                    savedClient = cl;
1633                }
1634            }
1635            mScoClients.clear();
1636            if (savedClient != null) {
1637                mScoClients.add(savedClient);
1638            }
1639        }
1640    }
1641
1642    private boolean getBluetoothHeadset() {
1643        boolean result = false;
1644        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1645        if (adapter != null) {
1646            result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
1647                                    BluetoothProfile.HEADSET);
1648        }
1649        // If we could not get a bluetooth headset proxy, send a failure message
1650        // without delay to reset the SCO audio state and clear SCO clients.
1651        // If we could get a proxy, send a delayed failure message that will reset our state
1652        // in case we don't receive onServiceConnected().
1653        sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
1654                SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
1655        return result;
1656    }
1657
1658    private void disconnectBluetoothSco(int exceptPid) {
1659        synchronized(mScoClients) {
1660            checkScoAudioState();
1661            if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
1662                    mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
1663                if (mBluetoothHeadsetDevice != null) {
1664                    if (mBluetoothHeadset != null) {
1665                        if (!mBluetoothHeadset.stopVoiceRecognition(
1666                                mBluetoothHeadsetDevice)) {
1667                            sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
1668                                    SENDMSG_REPLACE, 0, 0, null, 0);
1669                        }
1670                    } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
1671                            getBluetoothHeadset()) {
1672                        mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
1673                    }
1674                }
1675            } else {
1676                clearAllScoClients(exceptPid, true);
1677            }
1678        }
1679    }
1680
1681    private void resetBluetoothSco() {
1682        synchronized(mScoClients) {
1683            clearAllScoClients(0, false);
1684            mScoAudioState = SCO_STATE_INACTIVE;
1685            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1686        }
1687    }
1688
1689    private void broadcastScoConnectionState(int state) {
1690        if (state != mScoConnectionState) {
1691            Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
1692            newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
1693            newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
1694                    mScoConnectionState);
1695            mContext.sendStickyBroadcast(newIntent);
1696            mScoConnectionState = state;
1697        }
1698    }
1699
1700    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
1701        new BluetoothProfile.ServiceListener() {
1702        public void onServiceConnected(int profile, BluetoothProfile proxy) {
1703            BluetoothDevice btDevice;
1704            List<BluetoothDevice> deviceList;
1705            switch(profile) {
1706            case BluetoothProfile.A2DP:
1707                BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
1708                deviceList = a2dp.getConnectedDevices();
1709                if (deviceList.size() > 0) {
1710                    btDevice = deviceList.get(0);
1711                    handleA2dpConnectionStateChange(btDevice, a2dp.getConnectionState(btDevice));
1712                }
1713                break;
1714
1715            case BluetoothProfile.HEADSET:
1716                synchronized (mScoClients) {
1717                    // Discard timeout message
1718                    mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
1719                    mBluetoothHeadset = (BluetoothHeadset) proxy;
1720                    deviceList = mBluetoothHeadset.getConnectedDevices();
1721                    if (deviceList.size() > 0) {
1722                        mBluetoothHeadsetDevice = deviceList.get(0);
1723                    } else {
1724                        mBluetoothHeadsetDevice = null;
1725                    }
1726                    // Refresh SCO audio state
1727                    checkScoAudioState();
1728                    // Continue pending action if any
1729                    if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
1730                            mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
1731                            mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
1732                        boolean status = false;
1733                        if (mBluetoothHeadsetDevice != null) {
1734                            switch (mScoAudioState) {
1735                            case SCO_STATE_ACTIVATE_REQ:
1736                                mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1737                                status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1738                                        mBluetoothHeadsetDevice);
1739                                break;
1740                            case SCO_STATE_DEACTIVATE_REQ:
1741                                status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
1742                                        mBluetoothHeadsetDevice);
1743                                break;
1744                            case SCO_STATE_DEACTIVATE_EXT_REQ:
1745                                status = mBluetoothHeadset.stopVoiceRecognition(
1746                                        mBluetoothHeadsetDevice);
1747                            }
1748                        }
1749                        if (!status) {
1750                            sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
1751                                    SENDMSG_REPLACE, 0, 0, null, 0);
1752                        }
1753                    }
1754                }
1755                break;
1756
1757            default:
1758                break;
1759            }
1760        }
1761        public void onServiceDisconnected(int profile) {
1762            switch(profile) {
1763            case BluetoothProfile.A2DP:
1764                synchronized (mConnectedDevices) {
1765                    if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
1766                        makeA2dpDeviceUnavailableNow(
1767                                mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
1768                    }
1769                }
1770                break;
1771
1772            case BluetoothProfile.HEADSET:
1773                synchronized (mScoClients) {
1774                    mBluetoothHeadset = null;
1775                }
1776                break;
1777
1778            default:
1779                break;
1780            }
1781        }
1782    };
1783
1784    ///////////////////////////////////////////////////////////////////////////
1785    // Internal methods
1786    ///////////////////////////////////////////////////////////////////////////
1787
1788    /**
1789     * Checks if the adjustment should change ringer mode instead of just
1790     * adjusting volume. If so, this will set the proper ringer mode and volume
1791     * indices on the stream states.
1792     */
1793    private boolean checkForRingerModeChange(int oldIndex, int direction, int streamType) {
1794        boolean adjustVolumeIndex = true;
1795        int newRingerMode = mRingerMode;
1796        int uiIndex = (oldIndex + 5) / 10;
1797        boolean vibeInSilent = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1;
1798
1799        if (mRingerMode == RINGER_MODE_NORMAL) {
1800            if ((direction == AudioManager.ADJUST_LOWER) && (uiIndex <= 1)) {
1801                // enter silent mode if current index is the last audible one and not repeating a
1802                // volume key down
1803                if (vibeInSilent || mPrevVolDirection != AudioManager.ADJUST_LOWER) {
1804                    // "silent mode", but which one?
1805                    newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_SILENT;
1806                }
1807                if (uiIndex == 0 ||
1808                        (!vibeInSilent &&
1809                         mPrevVolDirection == AudioManager.ADJUST_LOWER &&
1810                         mVoiceCapable && streamType == AudioSystem.STREAM_RING)) {
1811                    adjustVolumeIndex = false;
1812                }
1813            }
1814        } else if (mRingerMode == RINGER_MODE_VIBRATE) {
1815            if ((direction == AudioManager.ADJUST_LOWER)) {
1816                // Set it to silent, if it wasn't a long-press
1817                if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
1818                    newRingerMode = RINGER_MODE_SILENT;
1819                }
1820            } else if (direction == AudioManager.ADJUST_RAISE) {
1821                newRingerMode = RINGER_MODE_NORMAL;
1822            }
1823            adjustVolumeIndex = false;
1824        } else {
1825            if (direction == AudioManager.ADJUST_RAISE) {
1826                // exiting silent mode
1827                // If VIBRATE_IN_SILENT, then go into vibrate mode
1828                newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_NORMAL;
1829            }
1830            adjustVolumeIndex = false;
1831        }
1832
1833        if (newRingerMode != mRingerMode) {
1834            setRingerMode(newRingerMode);
1835        }
1836
1837        mPrevVolDirection = direction;
1838
1839        return adjustVolumeIndex;
1840    }
1841
1842    public boolean isStreamAffectedByRingerMode(int streamType) {
1843        return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
1844    }
1845
1846    private boolean isStreamMutedByRingerMode(int streamType) {
1847        return (mRingerModeMutedStreams & (1 << streamType)) != 0;
1848    }
1849
1850    public boolean isStreamAffectedByMute(int streamType) {
1851        return (mMuteAffectedStreams & (1 << streamType)) != 0;
1852    }
1853
1854    private void ensureValidDirection(int direction) {
1855        if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
1856            throw new IllegalArgumentException("Bad direction " + direction);
1857        }
1858    }
1859
1860    private void ensureValidStreamType(int streamType) {
1861        if (streamType < 0 || streamType >= mStreamStates.length) {
1862            throw new IllegalArgumentException("Bad stream type " + streamType);
1863        }
1864    }
1865
1866    private int getActiveStreamType(int suggestedStreamType) {
1867
1868        if (mVoiceCapable) {
1869            boolean isOffhook = false;
1870            try {
1871                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1872                if (phone != null) isOffhook = phone.isOffhook();
1873            } catch (RemoteException e) {
1874                Log.w(TAG, "Couldn't connect to phone service", e);
1875            }
1876
1877            if (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION) {
1878                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
1879                        == AudioSystem.FORCE_BT_SCO) {
1880                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1881                    return AudioSystem.STREAM_BLUETOOTH_SCO;
1882                } else {
1883                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1884                    return AudioSystem.STREAM_VOICE_CALL;
1885                }
1886            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
1887                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
1888                return AudioSystem.STREAM_MUSIC;
1889            } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
1890                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."
1891                //        + " b/c USE_DEFAULT_STREAM_TYPE...");
1892                return AudioSystem.STREAM_RING;
1893            } else {
1894                // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1895                return suggestedStreamType;
1896            }
1897        } else {
1898            if (getMode() == AudioManager.MODE_IN_COMMUNICATION) {
1899                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
1900                        == AudioSystem.FORCE_BT_SCO) {
1901                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1902                    return AudioSystem.STREAM_BLUETOOTH_SCO;
1903                } else {
1904                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1905                    return AudioSystem.STREAM_VOICE_CALL;
1906                }
1907            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
1908                            NOTIFICATION_VOLUME_DELAY_MS) ||
1909                       AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
1910                            NOTIFICATION_VOLUME_DELAY_MS)) {
1911                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION...");
1912                return AudioSystem.STREAM_NOTIFICATION;
1913            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) ||
1914                       (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) {
1915                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC "
1916                //        + " b/c USE_DEFAULT_STREAM_TYPE...");
1917                return AudioSystem.STREAM_MUSIC;
1918            } else {
1919                // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1920                return suggestedStreamType;
1921            }
1922        }
1923    }
1924
1925    private void broadcastRingerMode() {
1926        // Send sticky broadcast
1927        Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
1928        broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
1929        broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1930                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1931        long origCallerIdentityToken = Binder.clearCallingIdentity();
1932        mContext.sendStickyBroadcast(broadcast);
1933        Binder.restoreCallingIdentity(origCallerIdentityToken);
1934    }
1935
1936    private void broadcastVibrateSetting(int vibrateType) {
1937        // Send broadcast
1938        if (ActivityManagerNative.isSystemReady()) {
1939            Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
1940            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
1941            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
1942            mContext.sendBroadcast(broadcast);
1943        }
1944    }
1945
1946    // Message helper methods
1947    private static int getMsg(int baseMsg, int streamType) {
1948        return (baseMsg & 0xffff) | streamType << 16;
1949    }
1950
1951    private static int getMsgBase(int msg) {
1952        return msg & 0xffff;
1953    }
1954
1955    private static void sendMsg(Handler handler, int baseMsg, int streamType,
1956            int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
1957        int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
1958
1959        if (existingMsgPolicy == SENDMSG_REPLACE) {
1960            handler.removeMessages(msg);
1961        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
1962            return;
1963        }
1964
1965        handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
1966    }
1967
1968    boolean checkAudioSettingsPermission(String method) {
1969        if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
1970                == PackageManager.PERMISSION_GRANTED) {
1971            return true;
1972        }
1973        String msg = "Audio Settings Permission Denial: " + method + " from pid="
1974                + Binder.getCallingPid()
1975                + ", uid=" + Binder.getCallingUid();
1976        Log.w(TAG, msg);
1977        return false;
1978    }
1979
1980
1981    ///////////////////////////////////////////////////////////////////////////
1982    // Inner classes
1983    ///////////////////////////////////////////////////////////////////////////
1984
1985    public class VolumeStreamState {
1986        private final int mStreamType;
1987
1988        private String mVolumeIndexSettingName;
1989        private String mLastAudibleVolumeIndexSettingName;
1990        private int mIndexMax;
1991        private int mIndex;
1992        private int mLastAudibleIndex;
1993        private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
1994
1995        private VolumeStreamState(String settingName, int streamType) {
1996
1997            setVolumeIndexSettingName(settingName);
1998
1999            mStreamType = streamType;
2000
2001            final ContentResolver cr = mContentResolver;
2002            mIndexMax = MAX_STREAM_VOLUME[streamType];
2003            mIndex = Settings.System.getInt(cr,
2004                                            mVolumeIndexSettingName,
2005                                            AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
2006            mLastAudibleIndex = Settings.System.getInt(cr,
2007                                                       mLastAudibleVolumeIndexSettingName,
2008                                                       (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
2009            AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
2010            mIndexMax *= 10;
2011            mIndex = getValidIndex(10 * mIndex);
2012            mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
2013            setStreamVolumeIndex(streamType, mIndex);
2014            mDeathHandlers = new ArrayList<VolumeDeathHandler>();
2015        }
2016
2017        public void setVolumeIndexSettingName(String settingName) {
2018            mVolumeIndexSettingName = settingName;
2019            mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
2020        }
2021
2022        public boolean adjustIndex(int deltaIndex) {
2023            return setIndex(mIndex + deltaIndex * 10, true);
2024        }
2025
2026        public boolean setIndex(int index, boolean lastAudible) {
2027            int oldIndex = mIndex;
2028            mIndex = getValidIndex(index);
2029
2030            if (oldIndex != mIndex) {
2031                if (lastAudible) {
2032                    mLastAudibleIndex = mIndex;
2033                }
2034                // Apply change to all streams using this one as alias
2035                int numStreamTypes = AudioSystem.getNumStreamTypes();
2036                for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2037                    if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
2038                        mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
2039                    }
2040                }
2041                return true;
2042            } else {
2043                return false;
2044            }
2045        }
2046
2047        public void setLastAudibleIndex(int index) {
2048            mLastAudibleIndex = getValidIndex(index);
2049        }
2050
2051        public void adjustLastAudibleIndex(int deltaIndex) {
2052            setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10);
2053        }
2054
2055        public int getMaxIndex() {
2056            return mIndexMax;
2057        }
2058
2059        public void mute(IBinder cb, boolean state) {
2060            VolumeDeathHandler handler = getDeathHandler(cb, state);
2061            if (handler == null) {
2062                Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
2063                return;
2064            }
2065            handler.mute(state);
2066        }
2067
2068        private int getValidIndex(int index) {
2069            if (index < 0) {
2070                return 0;
2071            } else if (index > mIndexMax) {
2072                return mIndexMax;
2073            }
2074
2075            return index;
2076        }
2077
2078        private class VolumeDeathHandler implements IBinder.DeathRecipient {
2079            private IBinder mICallback; // To be notified of client's death
2080            private int mMuteCount; // Number of active mutes for this client
2081
2082            VolumeDeathHandler(IBinder cb) {
2083                mICallback = cb;
2084            }
2085
2086            public void mute(boolean state) {
2087                synchronized(mDeathHandlers) {
2088                    if (state) {
2089                        if (mMuteCount == 0) {
2090                            // Register for client death notification
2091                            try {
2092                                // mICallback can be 0 if muted by AudioService
2093                                if (mICallback != null) {
2094                                    mICallback.linkToDeath(this, 0);
2095                                }
2096                                mDeathHandlers.add(this);
2097                                // If the stream is not yet muted by any client, set lvel to 0
2098                                if (muteCount() == 0) {
2099                                    setIndex(0, false);
2100                                    sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
2101                                            VolumeStreamState.this, 0);
2102                                }
2103                            } catch (RemoteException e) {
2104                                // Client has died!
2105                                binderDied();
2106                                mDeathHandlers.notify();
2107                                return;
2108                            }
2109                        } else {
2110                            Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
2111                        }
2112                        mMuteCount++;
2113                    } else {
2114                        if (mMuteCount == 0) {
2115                            Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
2116                        } else {
2117                            mMuteCount--;
2118                            if (mMuteCount == 0) {
2119                                // Unregistr from client death notification
2120                                mDeathHandlers.remove(this);
2121                                // mICallback can be 0 if muted by AudioService
2122                                if (mICallback != null) {
2123                                    mICallback.unlinkToDeath(this, 0);
2124                                }
2125                                if (muteCount() == 0) {
2126                                    // If the stream is not muted any more, restore it's volume if
2127                                    // ringer mode allows it
2128                                    if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
2129                                        setIndex(mLastAudibleIndex, false);
2130                                        sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
2131                                                VolumeStreamState.this, 0);
2132                                    }
2133                                }
2134                            }
2135                        }
2136                    }
2137                    mDeathHandlers.notify();
2138                }
2139            }
2140
2141            public void binderDied() {
2142                Log.w(TAG, "Volume service client died for stream: "+mStreamType);
2143                if (mMuteCount != 0) {
2144                    // Reset all active mute requests from this client.
2145                    mMuteCount = 1;
2146                    mute(false);
2147                }
2148            }
2149        }
2150
2151        private int muteCount() {
2152            int count = 0;
2153            int size = mDeathHandlers.size();
2154            for (int i = 0; i < size; i++) {
2155                count += mDeathHandlers.get(i).mMuteCount;
2156            }
2157            return count;
2158        }
2159
2160        private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
2161            synchronized(mDeathHandlers) {
2162                VolumeDeathHandler handler;
2163                int size = mDeathHandlers.size();
2164                for (int i = 0; i < size; i++) {
2165                    handler = mDeathHandlers.get(i);
2166                    if (cb == handler.mICallback) {
2167                        return handler;
2168                    }
2169                }
2170                // If this is the first mute request for this client, create a new
2171                // client death handler. Otherwise, it is an out of sequence unmute request.
2172                if (state) {
2173                    handler = new VolumeDeathHandler(cb);
2174                } else {
2175                    Log.w(TAG, "stream was not muted by this client");
2176                    handler = null;
2177                }
2178                return handler;
2179            }
2180        }
2181    }
2182
2183    /** Thread that handles native AudioSystem control. */
2184    private class AudioSystemThread extends Thread {
2185        AudioSystemThread() {
2186            super("AudioService");
2187        }
2188
2189        @Override
2190        public void run() {
2191            // Set this thread up so the handler will work on it
2192            Looper.prepare();
2193
2194            synchronized(AudioService.this) {
2195                mAudioHandler = new AudioHandler();
2196
2197                // Notify that the handler has been created
2198                AudioService.this.notify();
2199            }
2200
2201            // Listen for volume change requests that are set by VolumePanel
2202            Looper.loop();
2203        }
2204    }
2205
2206    /** Handles internal volume messages in separate volume thread. */
2207    private class AudioHandler extends Handler {
2208
2209        private void setSystemVolume(VolumeStreamState streamState) {
2210
2211            // Adjust volume
2212            setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
2213
2214            // Apply change to all streams using this one as alias
2215            int numStreamTypes = AudioSystem.getNumStreamTypes();
2216            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2217                if (streamType != streamState.mStreamType &&
2218                    STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
2219                    setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
2220                }
2221            }
2222
2223            // Post a persist volume msg
2224            sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
2225                    SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY);
2226        }
2227
2228        private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) {
2229            if (current) {
2230                System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
2231                              (streamState.mIndex + 5)/ 10);
2232            }
2233            if (lastAudible) {
2234                System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
2235                    (streamState.mLastAudibleIndex + 5) / 10);
2236            }
2237        }
2238
2239        private void persistRingerMode() {
2240            System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
2241        }
2242
2243        private void persistVibrateSetting() {
2244            System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
2245        }
2246
2247        private void playSoundEffect(int effectType, int volume) {
2248            synchronized (mSoundEffectsLock) {
2249                if (mSoundPool == null) {
2250                    return;
2251                }
2252                float volFloat;
2253                // use default if volume is not specified by caller
2254                if (volume < 0) {
2255                    volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
2256                } else {
2257                    volFloat = (float) volume / 1000.0f;
2258                }
2259
2260                if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
2261                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
2262                } else {
2263                    MediaPlayer mediaPlayer = new MediaPlayer();
2264                    if (mediaPlayer != null) {
2265                        try {
2266                            String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
2267                            mediaPlayer.setDataSource(filePath);
2268                            mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
2269                            mediaPlayer.prepare();
2270                            mediaPlayer.setVolume(volFloat, volFloat);
2271                            mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
2272                                public void onCompletion(MediaPlayer mp) {
2273                                    cleanupPlayer(mp);
2274                                }
2275                            });
2276                            mediaPlayer.setOnErrorListener(new OnErrorListener() {
2277                                public boolean onError(MediaPlayer mp, int what, int extra) {
2278                                    cleanupPlayer(mp);
2279                                    return true;
2280                                }
2281                            });
2282                            mediaPlayer.start();
2283                        } catch (IOException ex) {
2284                            Log.w(TAG, "MediaPlayer IOException: "+ex);
2285                        } catch (IllegalArgumentException ex) {
2286                            Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
2287                        } catch (IllegalStateException ex) {
2288                            Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2289                        }
2290                    }
2291                }
2292            }
2293        }
2294
2295        private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
2296            Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
2297                    receiver == null ? "" : receiver.flattenToString());
2298        }
2299
2300        private void cleanupPlayer(MediaPlayer mp) {
2301            if (mp != null) {
2302                try {
2303                    mp.stop();
2304                    mp.release();
2305                } catch (IllegalStateException ex) {
2306                    Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2307                }
2308            }
2309        }
2310
2311        private void setForceUse(int usage, int config) {
2312            AudioSystem.setForceUse(usage, config);
2313        }
2314
2315        @Override
2316        public void handleMessage(Message msg) {
2317            int baseMsgWhat = getMsgBase(msg.what);
2318
2319            switch (baseMsgWhat) {
2320
2321                case MSG_SET_SYSTEM_VOLUME:
2322                    setSystemVolume((VolumeStreamState) msg.obj);
2323                    break;
2324
2325                case MSG_PERSIST_VOLUME:
2326                    persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0));
2327                    break;
2328
2329                case MSG_PERSIST_MASTER_VOLUME:
2330                    Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER,
2331                            (float)msg.arg1 / (float)1000.0);
2332                    break;
2333
2334                case MSG_PERSIST_RINGER_MODE:
2335                    persistRingerMode();
2336                    break;
2337
2338                case MSG_PERSIST_VIBRATE_SETTING:
2339                    persistVibrateSetting();
2340                    break;
2341
2342                case MSG_MEDIA_SERVER_DIED:
2343                    if (!mMediaServerOk) {
2344                        Log.e(TAG, "Media server died.");
2345                        // Force creation of new IAudioFlinger interface so that we are notified
2346                        // when new media_server process is back to life.
2347                        AudioSystem.setErrorCallback(mAudioSystemCallback);
2348                        sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
2349                                null, 500);
2350                    }
2351                    break;
2352
2353                case MSG_MEDIA_SERVER_STARTED:
2354                    Log.e(TAG, "Media server started.");
2355                    // indicate to audio HAL that we start the reconfiguration phase after a media
2356                    // server crash
2357                    // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
2358                    // process restarts after a crash, not the first time it is started.
2359                    AudioSystem.setParameters("restarting=true");
2360
2361                    // Restore device connection states
2362                    synchronized (mConnectedDevices) {
2363                        Set set = mConnectedDevices.entrySet();
2364                        Iterator i = set.iterator();
2365                        while(i.hasNext()){
2366                            Map.Entry device = (Map.Entry)i.next();
2367                            AudioSystem.setDeviceConnectionState(
2368                                                            ((Integer)device.getKey()).intValue(),
2369                                                            AudioSystem.DEVICE_STATE_AVAILABLE,
2370                                                            (String)device.getValue());
2371                        }
2372                    }
2373                    // Restore call state
2374                    AudioSystem.setPhoneState(mMode);
2375
2376                    // Restore forced usage for communcations and record
2377                    AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
2378                    AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
2379
2380                    // Restore stream volumes
2381                    int numStreamTypes = AudioSystem.getNumStreamTypes();
2382                    for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2383                        int index;
2384                        VolumeStreamState streamState = mStreamStates[streamType];
2385                        AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
2386                        if (streamState.muteCount() == 0) {
2387                            index = streamState.mIndex;
2388                        } else {
2389                            index = 0;
2390                        }
2391                        setStreamVolumeIndex(streamType, index);
2392                    }
2393
2394                    // Restore ringer mode
2395                    setRingerModeInt(getRingerMode(), false);
2396
2397                    // Restore master volume
2398                    restoreMasterVolume();
2399
2400                    // indicate the end of reconfiguration phase to audio HAL
2401                    AudioSystem.setParameters("restarting=false");
2402                    break;
2403
2404                case MSG_LOAD_SOUND_EFFECTS:
2405                    loadSoundEffects();
2406                    break;
2407
2408                case MSG_PLAY_SOUND_EFFECT:
2409                    playSoundEffect(msg.arg1, msg.arg2);
2410                    break;
2411
2412                case MSG_BTA2DP_DOCK_TIMEOUT:
2413                    // msg.obj  == address of BTA2DP device
2414                    synchronized (mConnectedDevices) {
2415                        makeA2dpDeviceUnavailableNow( (String) msg.obj );
2416                    }
2417                    break;
2418
2419                case MSG_SET_FORCE_USE:
2420                    setForceUse(msg.arg1, msg.arg2);
2421                    break;
2422
2423                case MSG_PERSIST_MEDIABUTTONRECEIVER:
2424                    onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
2425                    break;
2426
2427                case MSG_RCDISPLAY_CLEAR:
2428                    onRcDisplayClear();
2429                    break;
2430
2431                case MSG_RCDISPLAY_UPDATE:
2432                    // msg.obj is guaranteed to be non null
2433                    onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
2434                    break;
2435
2436                case MSG_BT_HEADSET_CNCT_FAILED:
2437                    resetBluetoothSco();
2438                    break;
2439            }
2440        }
2441    }
2442
2443    private class SettingsObserver extends ContentObserver {
2444
2445        SettingsObserver() {
2446            super(new Handler());
2447            mContentResolver.registerContentObserver(Settings.System.getUriFor(
2448                Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
2449        }
2450
2451        @Override
2452        public void onChange(boolean selfChange) {
2453            super.onChange(selfChange);
2454            synchronized (mSettingsLock) {
2455                int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
2456                       Settings.System.MODE_RINGER_STREAMS_AFFECTED,
2457                       ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
2458                       (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
2459                if (mVoiceCapable) {
2460                    ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
2461                } else {
2462                    ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
2463                }
2464                if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
2465                    /*
2466                     * Ensure all stream types that should be affected by ringer mode
2467                     * are in the proper state.
2468                     */
2469                    mRingerModeAffectedStreams = ringerModeAffectedStreams;
2470                    setRingerModeInt(getRingerMode(), false);
2471                }
2472            }
2473        }
2474    }
2475
2476    // must be called synchronized on mConnectedDevices
2477    private void makeA2dpDeviceAvailable(String address) {
2478        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2479                AudioSystem.DEVICE_STATE_AVAILABLE,
2480                address);
2481        // Reset A2DP suspend state each time a new sink is connected
2482        AudioSystem.setParameters("A2dpSuspended=false");
2483        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
2484                address);
2485    }
2486
2487    // must be called synchronized on mConnectedDevices
2488    private void makeA2dpDeviceUnavailableNow(String address) {
2489        Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
2490        mContext.sendBroadcast(noisyIntent);
2491        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2492                AudioSystem.DEVICE_STATE_UNAVAILABLE,
2493                address);
2494        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2495    }
2496
2497    // must be called synchronized on mConnectedDevices
2498    private void makeA2dpDeviceUnavailableLater(String address) {
2499        // prevent any activity on the A2DP audio output to avoid unwanted
2500        // reconnection of the sink.
2501        AudioSystem.setParameters("A2dpSuspended=true");
2502        // the device will be made unavailable later, so consider it disconnected right away
2503        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2504        // send the delayed message to make the device unavailable later
2505        Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
2506        mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
2507
2508    }
2509
2510    // must be called synchronized on mConnectedDevices
2511    private void cancelA2dpDeviceTimeout() {
2512        mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2513    }
2514
2515    // must be called synchronized on mConnectedDevices
2516    private boolean hasScheduledA2dpDockTimeout() {
2517        return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2518    }
2519
2520    private void handleA2dpConnectionStateChange(BluetoothDevice btDevice, int state)
2521    {
2522        if (btDevice == null) {
2523            return;
2524        }
2525        String address = btDevice.getAddress();
2526        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2527            address = "";
2528        }
2529        synchronized (mConnectedDevices) {
2530            boolean isConnected =
2531                (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
2532                 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
2533
2534            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
2535                if (btDevice.isBluetoothDock()) {
2536                    if (state == BluetoothProfile.STATE_DISCONNECTED) {
2537                        // introduction of a delay for transient disconnections of docks when
2538                        // power is rapidly turned off/on, this message will be canceled if
2539                        // we reconnect the dock under a preset delay
2540                        makeA2dpDeviceUnavailableLater(address);
2541                        // the next time isConnected is evaluated, it will be false for the dock
2542                    }
2543                } else {
2544                    makeA2dpDeviceUnavailableNow(address);
2545                }
2546            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
2547                if (btDevice.isBluetoothDock()) {
2548                    // this could be a reconnection after a transient disconnection
2549                    cancelA2dpDeviceTimeout();
2550                    mDockAddress = address;
2551                } else {
2552                    // this could be a connection of another A2DP device before the timeout of
2553                    // a dock: cancel the dock timeout, and make the dock unavailable now
2554                    if(hasScheduledA2dpDockTimeout()) {
2555                        cancelA2dpDeviceTimeout();
2556                        makeA2dpDeviceUnavailableNow(mDockAddress);
2557                    }
2558                }
2559                makeA2dpDeviceAvailable(address);
2560            }
2561        }
2562    }
2563
2564    /* cache of the address of the last dock the device was connected to */
2565    private String mDockAddress;
2566
2567    /**
2568     * Receiver for misc intent broadcasts the Phone app cares about.
2569     */
2570    private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
2571        @Override
2572        public void onReceive(Context context, Intent intent) {
2573            String action = intent.getAction();
2574
2575            if (action.equals(Intent.ACTION_DOCK_EVENT)) {
2576                int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
2577                        Intent.EXTRA_DOCK_STATE_UNDOCKED);
2578                int config;
2579                switch (dockState) {
2580                    case Intent.EXTRA_DOCK_STATE_DESK:
2581                        config = AudioSystem.FORCE_BT_DESK_DOCK;
2582                        break;
2583                    case Intent.EXTRA_DOCK_STATE_CAR:
2584                        config = AudioSystem.FORCE_BT_CAR_DOCK;
2585                        break;
2586                    case Intent.EXTRA_DOCK_STATE_LE_DESK:
2587                        config = AudioSystem.FORCE_ANALOG_DOCK;
2588                        break;
2589                    case Intent.EXTRA_DOCK_STATE_HE_DESK:
2590                        config = AudioSystem.FORCE_DIGITAL_DOCK;
2591                        break;
2592                    case Intent.EXTRA_DOCK_STATE_UNDOCKED:
2593                    default:
2594                        config = AudioSystem.FORCE_NONE;
2595                }
2596                AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
2597            } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
2598                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
2599                                               BluetoothProfile.STATE_DISCONNECTED);
2600                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
2601
2602                handleA2dpConnectionStateChange(btDevice, state);
2603            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
2604                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
2605                                               BluetoothProfile.STATE_DISCONNECTED);
2606                int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
2607                String address = null;
2608
2609                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
2610                if (btDevice == null) {
2611                    return;
2612                }
2613
2614                address = btDevice.getAddress();
2615                BluetoothClass btClass = btDevice.getBluetoothClass();
2616                if (btClass != null) {
2617                    switch (btClass.getDeviceClass()) {
2618                    case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
2619                    case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
2620                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
2621                        break;
2622                    case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
2623                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
2624                        break;
2625                    }
2626                }
2627
2628                if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2629                    address = "";
2630                }
2631
2632                synchronized (mConnectedDevices) {
2633                    boolean isConnected = (mConnectedDevices.containsKey(device) &&
2634                                           mConnectedDevices.get(device).equals(address));
2635
2636                    synchronized (mScoClients) {
2637                        if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
2638                            AudioSystem.setDeviceConnectionState(device,
2639                                                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
2640                                                             address);
2641                            mConnectedDevices.remove(device);
2642                            mBluetoothHeadsetDevice = null;
2643                            resetBluetoothSco();
2644                        } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
2645                            AudioSystem.setDeviceConnectionState(device,
2646                                                                 AudioSystem.DEVICE_STATE_AVAILABLE,
2647                                                                 address);
2648                            mConnectedDevices.put(new Integer(device), address);
2649                            mBluetoothHeadsetDevice = btDevice;
2650                        }
2651                    }
2652                }
2653            } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
2654                int state = intent.getIntExtra("state", 0);
2655                int microphone = intent.getIntExtra("microphone", 0);
2656
2657                synchronized (mConnectedDevices) {
2658                    if (microphone != 0) {
2659                        boolean isConnected =
2660                            mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
2661                        if (state == 0 && isConnected) {
2662                            AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
2663                                    AudioSystem.DEVICE_STATE_UNAVAILABLE,
2664                                    "");
2665                            mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
2666                        } else if (state == 1 && !isConnected)  {
2667                            AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
2668                                    AudioSystem.DEVICE_STATE_AVAILABLE,
2669                                    "");
2670                            mConnectedDevices.put(
2671                                    new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
2672                        }
2673                    } else {
2674                        boolean isConnected =
2675                            mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
2676                        if (state == 0 && isConnected) {
2677                            AudioSystem.setDeviceConnectionState(
2678                                    AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
2679                                    AudioSystem.DEVICE_STATE_UNAVAILABLE,
2680                                    "");
2681                            mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
2682                        } else if (state == 1 && !isConnected)  {
2683                            AudioSystem.setDeviceConnectionState(
2684                                    AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
2685                                    AudioSystem.DEVICE_STATE_AVAILABLE,
2686                                    "");
2687                            mConnectedDevices.put(
2688                                    new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
2689                        }
2690                    }
2691                }
2692            } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
2693                int state = intent.getIntExtra("state", 0);
2694                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
2695                synchronized (mConnectedDevices) {
2696                    boolean isConnected =
2697                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
2698                    if (state == 0 && isConnected) {
2699                        AudioSystem.setDeviceConnectionState(
2700                                                        AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
2701                                                        AudioSystem.DEVICE_STATE_UNAVAILABLE,
2702                                                        "");
2703                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
2704                    } else if (state == 1 && !isConnected)  {
2705                        AudioSystem.setDeviceConnectionState(
2706                                                        AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
2707                                                        AudioSystem.DEVICE_STATE_AVAILABLE,
2708                                                        "");
2709                        mConnectedDevices.put(
2710                                new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
2711                    }
2712                }
2713            } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
2714                int state = intent.getIntExtra("state", 0);
2715                Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
2716                synchronized (mConnectedDevices) {
2717                    boolean isConnected =
2718                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
2719                    if (state == 0 && isConnected) {
2720                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
2721                                                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
2722                                                             "");
2723                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
2724                    } else if (state == 1 && !isConnected)  {
2725                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
2726                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
2727                                                             "");
2728                        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
2729                    }
2730                }
2731            } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) {
2732                int state = intent.getIntExtra("state", 0);
2733                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
2734                synchronized (mConnectedDevices) {
2735                    boolean isConnected =
2736                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
2737                    if (state == 0 && isConnected) {
2738                        AudioSystem.setDeviceConnectionState(
2739                                                         AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
2740                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE,
2741                                                         "");
2742                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
2743                    } else if (state == 1 && !isConnected)  {
2744                        AudioSystem.setDeviceConnectionState(
2745                                                         AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
2746                                                         AudioSystem.DEVICE_STATE_AVAILABLE,
2747                                                         "");
2748                        mConnectedDevices.put(
2749                                new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
2750                    }
2751                }
2752            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
2753                boolean broadcast = false;
2754                int state = AudioManager.SCO_AUDIO_STATE_ERROR;
2755                synchronized (mScoClients) {
2756                    int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
2757                    // broadcast intent if the connection was initated by AudioService
2758                    if (!mScoClients.isEmpty() &&
2759                            (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
2760                             mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
2761                             mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
2762                        broadcast = true;
2763                    }
2764                    switch (btState) {
2765                    case BluetoothHeadset.STATE_AUDIO_CONNECTED:
2766                        state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
2767                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
2768                            mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
2769                            mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
2770                            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
2771                        }
2772                        break;
2773                    case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
2774                        state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
2775                        mScoAudioState = SCO_STATE_INACTIVE;
2776                        clearAllScoClients(0, false);
2777                        break;
2778                    case BluetoothHeadset.STATE_AUDIO_CONNECTING:
2779                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
2780                            mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
2781                            mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
2782                            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
2783                        }
2784                    default:
2785                        // do not broadcast CONNECTING or invalid state
2786                        broadcast = false;
2787                        break;
2788                    }
2789                }
2790                if (broadcast) {
2791                    broadcastScoConnectionState(state);
2792                    //FIXME: this is to maintain compatibility with deprecated intent
2793                    // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
2794                    Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
2795                    newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
2796                    mContext.sendStickyBroadcast(newIntent);
2797                }
2798            } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
2799                mBootCompleted = true;
2800                sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP,
2801                        0, 0, null, 0);
2802
2803                mKeyguardManager =
2804                        (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
2805                mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
2806                resetBluetoothSco();
2807                getBluetoothHeadset();
2808                //FIXME: this is to maintain compatibility with deprecated intent
2809                // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
2810                Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
2811                newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
2812                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
2813                mContext.sendStickyBroadcast(newIntent);
2814
2815                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
2816                if (adapter != null) {
2817                    adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
2818                                            BluetoothProfile.A2DP);
2819                }
2820
2821                if (mUseMasterVolume) {
2822                    // Send sticky broadcast for initial master mute state
2823                    sendMasterMuteUpdate(false, 0);
2824                }
2825            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
2826                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
2827                    // a package is being removed, not replaced
2828                    String packageName = intent.getData().getSchemeSpecificPart();
2829                    if (packageName != null) {
2830                        removeMediaButtonReceiverForPackage(packageName);
2831                    }
2832                }
2833            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
2834                AudioSystem.setParameters("screen_state=on");
2835            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
2836                AudioSystem.setParameters("screen_state=off");
2837            }
2838        }
2839    }
2840
2841    //==========================================================================================
2842    // AudioFocus
2843    //==========================================================================================
2844
2845    /* constant to identify focus stack entry that is used to hold the focus while the phone
2846     * is ringing or during a call
2847     */
2848    private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
2849
2850    private final static Object mAudioFocusLock = new Object();
2851
2852    private final static Object mRingingLock = new Object();
2853
2854    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
2855        @Override
2856        public void onCallStateChanged(int state, String incomingNumber) {
2857            if (state == TelephonyManager.CALL_STATE_RINGING) {
2858                //Log.v(TAG, " CALL_STATE_RINGING");
2859                synchronized(mRingingLock) {
2860                    mIsRinging = true;
2861                }
2862            } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
2863                    || (state == TelephonyManager.CALL_STATE_IDLE)) {
2864                synchronized(mRingingLock) {
2865                    mIsRinging = false;
2866                }
2867            }
2868        }
2869    };
2870
2871    private void notifyTopOfAudioFocusStack() {
2872        // notify the top of the stack it gained focus
2873        if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
2874            if (canReassignAudioFocus()) {
2875                try {
2876                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
2877                            AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
2878                } catch (RemoteException e) {
2879                    Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
2880                    e.printStackTrace();
2881                }
2882            }
2883        }
2884    }
2885
2886    private static class FocusStackEntry {
2887        public int mStreamType = -1;// no stream type
2888        public IAudioFocusDispatcher mFocusDispatcher = null;
2889        public IBinder mSourceRef = null;
2890        public String mClientId;
2891        public int mFocusChangeType;
2892        public AudioFocusDeathHandler mHandler;
2893        public String mPackageName;
2894        public int mCallingUid;
2895
2896        public FocusStackEntry() {
2897        }
2898
2899        public FocusStackEntry(int streamType, int duration,
2900                IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
2901                String pn, int uid) {
2902            mStreamType = streamType;
2903            mFocusDispatcher = afl;
2904            mSourceRef = source;
2905            mClientId = id;
2906            mFocusChangeType = duration;
2907            mHandler = hdlr;
2908            mPackageName = pn;
2909            mCallingUid = uid;
2910        }
2911
2912        public void unlinkToDeath() {
2913            try {
2914                if (mSourceRef != null && mHandler != null) {
2915                    mSourceRef.unlinkToDeath(mHandler, 0);
2916                    mHandler = null;
2917                }
2918            } catch (java.util.NoSuchElementException e) {
2919                Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
2920            }
2921        }
2922
2923        @Override
2924        protected void finalize() throws Throwable {
2925            unlinkToDeath(); // unlink exception handled inside method
2926            super.finalize();
2927        }
2928    }
2929
2930    private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
2931
2932    /**
2933     * Helper function:
2934     * Display in the log the current entries in the audio focus stack
2935     */
2936    private void dumpFocusStack(PrintWriter pw) {
2937        pw.println("\nAudio Focus stack entries:");
2938        synchronized(mAudioFocusLock) {
2939            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2940            while(stackIterator.hasNext()) {
2941                FocusStackEntry fse = stackIterator.next();
2942                pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
2943                        + " -- duration: " + fse.mFocusChangeType
2944                        + " -- uid: " + fse.mCallingUid);
2945            }
2946        }
2947    }
2948
2949    /**
2950     * Helper function:
2951     * Called synchronized on mAudioFocusLock
2952     * Remove a focus listener from the focus stack.
2953     * @param focusListenerToRemove the focus listener
2954     * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
2955     *   focus, notify the next item in the stack it gained focus.
2956     */
2957    private void removeFocusStackEntry(String clientToRemove, boolean signal) {
2958        // is the current top of the focus stack abandoning focus? (because of request, not death)
2959        if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
2960        {
2961            //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
2962            FocusStackEntry fse = mFocusStack.pop();
2963            fse.unlinkToDeath();
2964            if (signal) {
2965                // notify the new top of the stack it gained focus
2966                notifyTopOfAudioFocusStack();
2967                // there's a new top of the stack, let the remote control know
2968                synchronized(mRCStack) {
2969                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
2970                }
2971            }
2972        } else {
2973            // focus is abandoned by a client that's not at the top of the stack,
2974            // no need to update focus.
2975            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2976            while(stackIterator.hasNext()) {
2977                FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
2978                if(fse.mClientId.equals(clientToRemove)) {
2979                    Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
2980                            + fse.mClientId);
2981                    stackIterator.remove();
2982                    fse.unlinkToDeath();
2983                }
2984            }
2985        }
2986    }
2987
2988    /**
2989     * Helper function:
2990     * Called synchronized on mAudioFocusLock
2991     * Remove focus listeners from the focus stack for a particular client when it has died.
2992     */
2993    private void removeFocusStackEntryForClient(IBinder cb) {
2994        // is the owner of the audio focus part of the client to remove?
2995        boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
2996                mFocusStack.peek().mSourceRef.equals(cb);
2997        Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2998        while(stackIterator.hasNext()) {
2999            FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
3000            if(fse.mSourceRef.equals(cb)) {
3001                Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
3002                        + fse.mClientId);
3003                stackIterator.remove();
3004                // the client just died, no need to unlink to its death
3005            }
3006        }
3007        if (isTopOfStackForClientToRemove) {
3008            // we removed an entry at the top of the stack:
3009            //  notify the new top of the stack it gained focus.
3010            notifyTopOfAudioFocusStack();
3011            // there's a new top of the stack, let the remote control know
3012            synchronized(mRCStack) {
3013                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3014            }
3015        }
3016    }
3017
3018    /**
3019     * Helper function:
3020     * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
3021     */
3022    private boolean canReassignAudioFocus() {
3023        // focus requests are rejected during a phone call or when the phone is ringing
3024        // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
3025        if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
3026            return false;
3027        }
3028        return true;
3029    }
3030
3031    /**
3032     * Inner class to monitor audio focus client deaths, and remove them from the audio focus
3033     * stack if necessary.
3034     */
3035    private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
3036        private IBinder mCb; // To be notified of client's death
3037
3038        AudioFocusDeathHandler(IBinder cb) {
3039            mCb = cb;
3040        }
3041
3042        public void binderDied() {
3043            synchronized(mAudioFocusLock) {
3044                Log.w(TAG, "  AudioFocus   audio focus client died");
3045                removeFocusStackEntryForClient(mCb);
3046            }
3047        }
3048
3049        public IBinder getBinder() {
3050            return mCb;
3051        }
3052    }
3053
3054
3055    /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
3056    public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
3057            IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
3058        Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
3059        // the main stream type for the audio focus request is currently not used. It may
3060        // potentially be used to handle multiple stream type-dependent audio focuses.
3061
3062        // we need a valid binder callback for clients
3063        if (!cb.pingBinder()) {
3064            Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
3065            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3066        }
3067
3068        synchronized(mAudioFocusLock) {
3069            if (!canReassignAudioFocus()) {
3070                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3071            }
3072
3073            // handle the potential premature death of the new holder of the focus
3074            // (premature death == death before abandoning focus)
3075            // Register for client death notification
3076            AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
3077            try {
3078                cb.linkToDeath(afdh, 0);
3079            } catch (RemoteException e) {
3080                // client has already died!
3081                Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
3082                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3083            }
3084
3085            if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
3086                // if focus is already owned by this client and the reason for acquiring the focus
3087                // hasn't changed, don't do anything
3088                if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
3089                    // unlink death handler so it can be gc'ed.
3090                    // linkToDeath() creates a JNI global reference preventing collection.
3091                    cb.unlinkToDeath(afdh, 0);
3092                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3093                }
3094                // the reason for the audio focus request has changed: remove the current top of
3095                // stack and respond as if we had a new focus owner
3096                FocusStackEntry fse = mFocusStack.pop();
3097                fse.unlinkToDeath();
3098            }
3099
3100            // notify current top of stack it is losing focus
3101            if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
3102                try {
3103                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
3104                            -1 * focusChangeHint, // loss and gain codes are inverse of each other
3105                            mFocusStack.peek().mClientId);
3106                } catch (RemoteException e) {
3107                    Log.e(TAG, " Failure to signal loss of focus due to "+ e);
3108                    e.printStackTrace();
3109                }
3110            }
3111
3112            // focus requester might already be somewhere below in the stack, remove it
3113            removeFocusStackEntry(clientId, false /* signal */);
3114
3115            // push focus requester at the top of the audio focus stack
3116            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
3117                    clientId, afdh, callingPackageName, Binder.getCallingUid()));
3118
3119            // there's a new top of the stack, let the remote control know
3120            synchronized(mRCStack) {
3121                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3122            }
3123        }//synchronized(mAudioFocusLock)
3124
3125        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3126    }
3127
3128    /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
3129    public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
3130        Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
3131        try {
3132            // this will take care of notifying the new focus owner if needed
3133            synchronized(mAudioFocusLock) {
3134                removeFocusStackEntry(clientId, true);
3135            }
3136        } catch (java.util.ConcurrentModificationException cme) {
3137            // Catching this exception here is temporary. It is here just to prevent
3138            // a crash seen when the "Silent" notification is played. This is believed to be fixed
3139            // but this try catch block is left just to be safe.
3140            Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
3141            cme.printStackTrace();
3142        }
3143
3144        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3145    }
3146
3147
3148    public void unregisterAudioFocusClient(String clientId) {
3149        synchronized(mAudioFocusLock) {
3150            removeFocusStackEntry(clientId, false);
3151        }
3152    }
3153
3154
3155    //==========================================================================================
3156    // RemoteControl
3157    //==========================================================================================
3158    /**
3159     * Receiver for media button intents. Handles the dispatching of the media button event
3160     * to one of the registered listeners, or if there was none, resumes the intent broadcast
3161     * to the rest of the system.
3162     */
3163    private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
3164        @Override
3165        public void onReceive(Context context, Intent intent) {
3166            String action = intent.getAction();
3167            if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
3168                return;
3169            }
3170            KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
3171            if (event != null) {
3172                // if in a call or ringing, do not break the current phone app behavior
3173                // TODO modify this to let the phone app specifically get the RC focus
3174                //      add modify the phone app to take advantage of the new API
3175                synchronized(mRingingLock) {
3176                    if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) ||
3177                            (getMode() == AudioSystem.MODE_IN_COMMUNICATION) ||
3178                            (getMode() == AudioSystem.MODE_RINGTONE) ) {
3179                        return;
3180                    }
3181                }
3182                synchronized(mRCStack) {
3183                    if (!mRCStack.empty()) {
3184                        // create a new intent to fill in the extras of the registered PendingIntent
3185                        Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
3186                        Bundle extras = intent.getExtras();
3187                        if (extras != null) {
3188                            targetedIntent.putExtras(extras);
3189                            // trap the current broadcast
3190                            abortBroadcast();
3191                            //Log.v(TAG, " Sending intent" + targetedIntent);
3192                            // send the intent that was registered by the client
3193                            try {
3194                                mRCStack.peek().mMediaIntent.send(context, 0, targetedIntent);
3195                            } catch (CanceledException e) {
3196                                Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
3197                                e.printStackTrace();
3198                            }
3199                        }
3200                    }
3201                }
3202            }
3203        }
3204    }
3205
3206    private final Object mCurrentRcLock = new Object();
3207    /**
3208     * The one remote control client which will receive a request for display information.
3209     * This object may be null.
3210     * Access protected by mCurrentRcLock.
3211     */
3212    private IRemoteControlClient mCurrentRcClient = null;
3213
3214    private final static int RC_INFO_NONE = 0;
3215    private final static int RC_INFO_ALL =
3216        RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
3217        RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
3218        RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
3219        RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
3220
3221    /**
3222     * A monotonically increasing generation counter for mCurrentRcClient.
3223     * Only accessed with a lock on mCurrentRcLock.
3224     * No value wrap-around issues as we only act on equal values.
3225     */
3226    private int mCurrentRcClientGen = 0;
3227
3228    /**
3229     * Inner class to monitor remote control client deaths, and remove the client for the
3230     * remote control stack if necessary.
3231     */
3232    private class RcClientDeathHandler implements IBinder.DeathRecipient {
3233        private IBinder mCb; // To be notified of client's death
3234        private PendingIntent mMediaIntent;
3235
3236        RcClientDeathHandler(IBinder cb, PendingIntent pi) {
3237            mCb = cb;
3238            mMediaIntent = pi;
3239        }
3240
3241        public void binderDied() {
3242            Log.w(TAG, "  RemoteControlClient died");
3243            // remote control client died, make sure the displays don't use it anymore
3244            //  by setting its remote control client to null
3245            registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
3246        }
3247
3248        public IBinder getBinder() {
3249            return mCb;
3250        }
3251    }
3252
3253    private static class RemoteControlStackEntry {
3254        /**
3255         * The target for the ACTION_MEDIA_BUTTON events.
3256         * Always non null.
3257         */
3258        public PendingIntent mMediaIntent;
3259        /**
3260         * The registered media button event receiver.
3261         * Always non null.
3262         */
3263        public ComponentName mReceiverComponent;
3264        public String mCallingPackageName;
3265        public int mCallingUid;
3266        /**
3267         * Provides access to the information to display on the remote control.
3268         * May be null (when a media button event receiver is registered,
3269         *     but no remote control client has been registered) */
3270        public IRemoteControlClient mRcClient;
3271        public RcClientDeathHandler mRcClientDeathHandler;
3272
3273        /** precondition: mediaIntent != null, eventReceiver != null */
3274        public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
3275            mMediaIntent = mediaIntent;
3276            mReceiverComponent = eventReceiver;
3277            mCallingUid = -1;
3278            mRcClient = null;
3279        }
3280
3281        public void unlinkToRcClientDeath() {
3282            if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
3283                try {
3284                    mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
3285                    mRcClientDeathHandler = null;
3286                } catch (java.util.NoSuchElementException e) {
3287                    // not much we can do here
3288                    Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
3289                    e.printStackTrace();
3290                }
3291            }
3292        }
3293
3294        @Override
3295        protected void finalize() throws Throwable {
3296            unlinkToRcClientDeath();// unlink exception handled inside method
3297            super.finalize();
3298        }
3299    }
3300
3301    /**
3302     *  The stack of remote control event receivers.
3303     *  Code sections and methods that modify the remote control event receiver stack are
3304     *  synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
3305     *  stack, audio focus or RC, can lead to a change in the remote control display
3306     */
3307    private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
3308
3309    /**
3310     * Helper function:
3311     * Display in the log the current entries in the remote control focus stack
3312     */
3313    private void dumpRCStack(PrintWriter pw) {
3314        pw.println("\nRemote Control stack entries:");
3315        synchronized(mRCStack) {
3316            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3317            while(stackIterator.hasNext()) {
3318                RemoteControlStackEntry rcse = stackIterator.next();
3319                pw.println("  pi: " + rcse.mMediaIntent +
3320                        "  -- ercvr: " + rcse.mReceiverComponent +
3321                        "  -- client: " + rcse.mRcClient +
3322                        "  -- uid: " + rcse.mCallingUid);
3323            }
3324        }
3325    }
3326
3327    /**
3328     * Helper function:
3329     * Remove any entry in the remote control stack that has the same package name as packageName
3330     * Pre-condition: packageName != null
3331     */
3332    private void removeMediaButtonReceiverForPackage(String packageName) {
3333        synchronized(mRCStack) {
3334            if (mRCStack.empty()) {
3335                return;
3336            } else {
3337                RemoteControlStackEntry oldTop = mRCStack.peek();
3338                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3339                // iterate over the stack entries
3340                while(stackIterator.hasNext()) {
3341                    RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
3342                    if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
3343                        // a stack entry is from the package being removed, remove it from the stack
3344                        stackIterator.remove();
3345                        rcse.unlinkToRcClientDeath();
3346                    }
3347                }
3348                if (mRCStack.empty()) {
3349                    // no saved media button receiver
3350                    mAudioHandler.sendMessage(
3351                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
3352                                    null));
3353                } else if (oldTop != mRCStack.peek()) {
3354                    // the top of the stack has changed, save it in the system settings
3355                    // by posting a message to persist it
3356                    mAudioHandler.sendMessage(
3357                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
3358                                    mRCStack.peek().mReceiverComponent));
3359                }
3360            }
3361        }
3362    }
3363
3364    /**
3365     * Helper function:
3366     * Restore remote control receiver from the system settings.
3367     */
3368    private void restoreMediaButtonReceiver() {
3369        String receiverName = Settings.System.getString(mContentResolver,
3370                Settings.System.MEDIA_BUTTON_RECEIVER);
3371        if ((null != receiverName) && !receiverName.isEmpty()) {
3372            ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
3373            // construct a PendingIntent targeted to the restored component name
3374            // for the media button and register it
3375            Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
3376            //     the associated intent will be handled by the component being registered
3377            mediaButtonIntent.setComponent(eventReceiver);
3378            PendingIntent pi = PendingIntent.getBroadcast(mContext,
3379                    0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
3380            registerMediaButtonIntent(pi, eventReceiver);
3381        }
3382    }
3383
3384    /**
3385     * Helper function:
3386     * Set the new remote control receiver at the top of the RC focus stack.
3387     * precondition: mediaIntent != null, target != null
3388     */
3389    private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
3390        // already at top of stack?
3391        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
3392            return;
3393        }
3394        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3395        RemoteControlStackEntry rcse = null;
3396        boolean wasInsideStack = false;
3397        while(stackIterator.hasNext()) {
3398            rcse = (RemoteControlStackEntry)stackIterator.next();
3399            if(rcse.mMediaIntent.equals(mediaIntent)) {
3400                wasInsideStack = true;
3401                stackIterator.remove();
3402                break;
3403            }
3404        }
3405        if (!wasInsideStack) {
3406            rcse = new RemoteControlStackEntry(mediaIntent, target);
3407        }
3408        mRCStack.push(rcse);
3409
3410        // post message to persist the default media button receiver
3411        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
3412                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
3413    }
3414
3415    /**
3416     * Helper function:
3417     * Remove the remote control receiver from the RC focus stack.
3418     * precondition: pi != null
3419     */
3420    private void removeMediaButtonReceiver(PendingIntent pi) {
3421        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3422        while(stackIterator.hasNext()) {
3423            RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
3424            if(rcse.mMediaIntent.equals(pi)) {
3425                stackIterator.remove();
3426                rcse.unlinkToRcClientDeath();
3427                break;
3428            }
3429        }
3430    }
3431
3432    /**
3433     * Helper function:
3434     * Called synchronized on mRCStack
3435     */
3436    private boolean isCurrentRcController(PendingIntent pi) {
3437        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
3438            return true;
3439        }
3440        return false;
3441    }
3442
3443    //==========================================================================================
3444    // Remote control display / client
3445    //==========================================================================================
3446    /**
3447     * Update the remote control displays with the new "focused" client generation
3448     */
3449    private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
3450            PendingIntent newMediaIntent, boolean clearing) {
3451        // NOTE: Only one IRemoteControlDisplay supported in this implementation
3452        if (mRcDisplay != null) {
3453            try {
3454                mRcDisplay.setCurrentClientId(
3455                        newClientGeneration, newMediaIntent, clearing);
3456            } catch (RemoteException e) {
3457                Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
3458                // if we had a display before, stop monitoring its death
3459                rcDisplay_stopDeathMonitor_syncRcStack();
3460                mRcDisplay = null;
3461            }
3462        }
3463    }
3464
3465    /**
3466     * Update the remote control clients with the new "focused" client generation
3467     */
3468    private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
3469        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3470        while(stackIterator.hasNext()) {
3471            RemoteControlStackEntry se = stackIterator.next();
3472            if ((se != null) && (se.mRcClient != null)) {
3473                try {
3474                    se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
3475                } catch (RemoteException e) {
3476                    Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e);
3477                    stackIterator.remove();
3478                    se.unlinkToRcClientDeath();
3479                }
3480            }
3481        }
3482    }
3483
3484    /**
3485     * Update the displays and clients with the new "focused" client generation and name
3486     * @param newClientGeneration the new generation value matching a client update
3487     * @param newClientEventReceiver the media button event receiver associated with the client.
3488     *    May be null, which implies there is no registered media button event receiver.
3489     * @param clearing true if the new client generation value maps to a remote control update
3490     *    where the display should be cleared.
3491     */
3492    private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
3493            PendingIntent newMediaIntent, boolean clearing) {
3494        // send the new valid client generation ID to all displays
3495        setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
3496        // send the new valid client generation ID to all clients
3497        setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
3498    }
3499
3500    /**
3501     * Called when processing MSG_RCDISPLAY_CLEAR event
3502     */
3503    private void onRcDisplayClear() {
3504        if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
3505
3506        synchronized(mRCStack) {
3507            synchronized(mCurrentRcLock) {
3508                mCurrentRcClientGen++;
3509                // synchronously update the displays and clients with the new client generation
3510                setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
3511                        null /*newMediaIntent*/, true /*clearing*/);
3512            }
3513        }
3514    }
3515
3516    /**
3517     * Called when processing MSG_RCDISPLAY_UPDATE event
3518     */
3519    private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
3520        synchronized(mRCStack) {
3521            synchronized(mCurrentRcLock) {
3522                if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
3523                    if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
3524
3525                    mCurrentRcClientGen++;
3526                    // synchronously update the displays and clients with
3527                    //      the new client generation
3528                    setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
3529                            rcse.mMediaIntent /*newMediaIntent*/,
3530                            false /*clearing*/);
3531
3532                    // tell the current client that it needs to send info
3533                    try {
3534                        mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
3535                                flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
3536                    } catch (RemoteException e) {
3537                        Log.e(TAG, "Current valid remote client is dead: "+e);
3538                        mCurrentRcClient = null;
3539                    }
3540                } else {
3541                    // the remote control display owner has changed between the
3542                    // the message to update the display was sent, and the time it
3543                    // gets to be processed (now)
3544                }
3545            }
3546        }
3547    }
3548
3549
3550    /**
3551     * Helper function:
3552     * Called synchronized on mRCStack
3553     */
3554    private void clearRemoteControlDisplay_syncAfRcs() {
3555        synchronized(mCurrentRcLock) {
3556            mCurrentRcClient = null;
3557        }
3558        // will cause onRcDisplayClear() to be called in AudioService's handler thread
3559        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
3560    }
3561
3562    /**
3563     * Helper function for code readability: only to be called from
3564     *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
3565     *    this method.
3566     * Preconditions:
3567     *    - called synchronized mAudioFocusLock then on mRCStack
3568     *    - mRCStack.isEmpty() is false
3569     */
3570    private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
3571        RemoteControlStackEntry rcse = mRCStack.peek();
3572        int infoFlagsAboutToBeUsed = infoChangedFlags;
3573        // this is where we enforce opt-in for information display on the remote controls
3574        //   with the new AudioManager.registerRemoteControlClient() API
3575        if (rcse.mRcClient == null) {
3576            //Log.w(TAG, "Can't update remote control display with null remote control client");
3577            clearRemoteControlDisplay_syncAfRcs();
3578            return;
3579        }
3580        synchronized(mCurrentRcLock) {
3581            if (!rcse.mRcClient.equals(mCurrentRcClient)) {
3582                // new RC client, assume every type of information shall be queried
3583                infoFlagsAboutToBeUsed = RC_INFO_ALL;
3584            }
3585            mCurrentRcClient = rcse.mRcClient;
3586        }
3587        // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
3588        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
3589                infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
3590    }
3591
3592    /**
3593     * Helper function:
3594     * Called synchronized on mAudioFocusLock, then mRCStack
3595     * Check whether the remote control display should be updated, triggers the update if required
3596     * @param infoChangedFlags the flags corresponding to the remote control client information
3597     *     that has changed, if applicable (checking for the update conditions might trigger a
3598     *     clear, rather than an update event).
3599     */
3600    private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
3601        // determine whether the remote control display should be refreshed
3602        // if either stack is empty, there is a mismatch, so clear the RC display
3603        if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
3604            clearRemoteControlDisplay_syncAfRcs();
3605            return;
3606        }
3607        // if the top of the two stacks belong to different packages, there is a mismatch, clear
3608        if ((mRCStack.peek().mCallingPackageName != null)
3609                && (mFocusStack.peek().mPackageName != null)
3610                && !(mRCStack.peek().mCallingPackageName.compareTo(
3611                        mFocusStack.peek().mPackageName) == 0)) {
3612            clearRemoteControlDisplay_syncAfRcs();
3613            return;
3614        }
3615        // if the audio focus didn't originate from the same Uid as the one in which the remote
3616        //   control information will be retrieved, clear
3617        if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
3618            clearRemoteControlDisplay_syncAfRcs();
3619            return;
3620        }
3621        // refresh conditions were verified: update the remote controls
3622        // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
3623        updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
3624    }
3625
3626    /**
3627     * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
3628     * precondition: mediaIntent != null, target != null
3629     */
3630    public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
3631        Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
3632
3633        synchronized(mAudioFocusLock) {
3634            synchronized(mRCStack) {
3635                pushMediaButtonReceiver(mediaIntent, eventReceiver);
3636                // new RC client, assume every type of information shall be queried
3637                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3638            }
3639        }
3640    }
3641
3642    /**
3643     * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
3644     * precondition: mediaIntent != null, eventReceiver != null
3645     */
3646    public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
3647    {
3648        Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
3649
3650        synchronized(mAudioFocusLock) {
3651            synchronized(mRCStack) {
3652                boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
3653                removeMediaButtonReceiver(mediaIntent);
3654                if (topOfStackWillChange) {
3655                    // current RC client will change, assume every type of info needs to be queried
3656                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3657                }
3658            }
3659        }
3660    }
3661
3662    /**
3663     * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
3664     * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
3665     *     without modifying the RC stack, but while still causing the display to refresh (will
3666     *     become blank as a result of this)
3667     */
3668    public void registerRemoteControlClient(PendingIntent mediaIntent,
3669            IRemoteControlClient rcClient, String callingPackageName) {
3670        if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
3671        synchronized(mAudioFocusLock) {
3672            synchronized(mRCStack) {
3673                // store the new display information
3674                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3675                while(stackIterator.hasNext()) {
3676                    RemoteControlStackEntry rcse = stackIterator.next();
3677                    if(rcse.mMediaIntent.equals(mediaIntent)) {
3678                        // already had a remote control client?
3679                        if (rcse.mRcClientDeathHandler != null) {
3680                            // stop monitoring the old client's death
3681                            rcse.unlinkToRcClientDeath();
3682                        }
3683                        // save the new remote control client
3684                        rcse.mRcClient = rcClient;
3685                        rcse.mCallingPackageName = callingPackageName;
3686                        rcse.mCallingUid = Binder.getCallingUid();
3687                        if (rcClient == null) {
3688                            // here rcse.mRcClientDeathHandler is null;
3689                            break;
3690                        }
3691
3692                        // there is a new (non-null) client:
3693                        // 1/ give the new client the current display (if any)
3694                        if (mRcDisplay != null) {
3695                            try {
3696                                rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
3697                            } catch (RemoteException e) {
3698                                Log.e(TAG, "Error connecting remote control display to client: "+e);
3699                                e.printStackTrace();
3700                            }
3701                        }
3702                        // 2/ monitor the new client's death
3703                        IBinder b = rcse.mRcClient.asBinder();
3704                        RcClientDeathHandler rcdh =
3705                                new RcClientDeathHandler(b, rcse.mMediaIntent);
3706                        try {
3707                            b.linkToDeath(rcdh, 0);
3708                        } catch (RemoteException e) {
3709                            // remote control client is DOA, disqualify it
3710                            Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
3711                            rcse.mRcClient = null;
3712                        }
3713                        rcse.mRcClientDeathHandler = rcdh;
3714                        break;
3715                    }
3716                }
3717                // if the eventReceiver is at the top of the stack
3718                // then check for potential refresh of the remote controls
3719                if (isCurrentRcController(mediaIntent)) {
3720                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3721                }
3722            }
3723        }
3724    }
3725
3726    /**
3727     * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
3728     * rcClient is guaranteed non-null
3729     */
3730    public void unregisterRemoteControlClient(PendingIntent mediaIntent,
3731            IRemoteControlClient rcClient) {
3732        synchronized(mAudioFocusLock) {
3733            synchronized(mRCStack) {
3734                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3735                while(stackIterator.hasNext()) {
3736                    RemoteControlStackEntry rcse = stackIterator.next();
3737                    if ((rcse.mMediaIntent.equals(mediaIntent))
3738                            && rcClient.equals(rcse.mRcClient)) {
3739                        // we found the IRemoteControlClient to unregister
3740                        // stop monitoring its death
3741                        rcse.unlinkToRcClientDeath();
3742                        // reset the client-related fields
3743                        rcse.mRcClient = null;
3744                        rcse.mCallingPackageName = null;
3745                    }
3746                }
3747            }
3748        }
3749    }
3750
3751    /**
3752     * The remote control displays.
3753     * Access synchronized on mRCStack
3754     * NOTE: Only one IRemoteControlDisplay supported in this implementation
3755     */
3756    private IRemoteControlDisplay mRcDisplay;
3757    private RcDisplayDeathHandler mRcDisplayDeathHandler;
3758    private int mArtworkExpectedWidth = -1;
3759    private int mArtworkExpectedHeight = -1;
3760    /**
3761     * Inner class to monitor remote control display deaths, and unregister them from the list
3762     * of displays if necessary.
3763     */
3764    private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
3765        private IBinder mCb; // To be notified of client's death
3766
3767        public RcDisplayDeathHandler(IBinder b) {
3768            if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b);
3769            mCb = b;
3770        }
3771
3772        public void binderDied() {
3773            synchronized(mRCStack) {
3774                Log.w(TAG, "RemoteControl: display died");
3775                mRcDisplay = null;
3776            }
3777        }
3778
3779        public void unlinkToRcDisplayDeath() {
3780            if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb);
3781            try {
3782                mCb.unlinkToDeath(this, 0);
3783            } catch (java.util.NoSuchElementException e) {
3784                // not much we can do here, the display was being unregistered anyway
3785                Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()");
3786                e.printStackTrace();
3787            }
3788        }
3789
3790    }
3791
3792    private void rcDisplay_stopDeathMonitor_syncRcStack() {
3793        if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null)
3794            // we had a display before, stop monitoring its death
3795            mRcDisplayDeathHandler.unlinkToRcDisplayDeath();
3796        }
3797    }
3798
3799    private void rcDisplay_startDeathMonitor_syncRcStack() {
3800        if (mRcDisplay != null) {
3801            // new non-null display, monitor its death
3802            IBinder b = mRcDisplay.asBinder();
3803            mRcDisplayDeathHandler = new RcDisplayDeathHandler(b);
3804            try {
3805                b.linkToDeath(mRcDisplayDeathHandler, 0);
3806            } catch (RemoteException e) {
3807                // remote control display is DOA, disqualify it
3808                Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
3809                mRcDisplay = null;
3810            }
3811        }
3812    }
3813
3814    /**
3815     * Register an IRemoteControlDisplay.
3816     * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
3817     * at the top of the stack to update the new display with its information.
3818     * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
3819     * @param rcd the IRemoteControlDisplay to register. No effect if null.
3820     */
3821    public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
3822        if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
3823        synchronized(mAudioFocusLock) {
3824            synchronized(mRCStack) {
3825                if ((mRcDisplay == rcd) || (rcd == null)) {
3826                    return;
3827                }
3828                // if we had a display before, stop monitoring its death
3829                rcDisplay_stopDeathMonitor_syncRcStack();
3830                mRcDisplay = rcd;
3831                // new display, start monitoring its death
3832                rcDisplay_startDeathMonitor_syncRcStack();
3833
3834                // let all the remote control clients there is a new display
3835                // no need to unplug the previous because we only support one display
3836                // and the clients don't track the death of the display
3837                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3838                while(stackIterator.hasNext()) {
3839                    RemoteControlStackEntry rcse = stackIterator.next();
3840                    if(rcse.mRcClient != null) {
3841                        try {
3842                            rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
3843                        } catch (RemoteException e) {
3844                            Log.e(TAG, "Error connecting remote control display to client: " + e);
3845                            e.printStackTrace();
3846                        }
3847                    }
3848                }
3849
3850                // we have a new display, of which all the clients are now aware: have it be updated
3851                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3852            }
3853        }
3854    }
3855
3856    /**
3857     * Unregister an IRemoteControlDisplay.
3858     * Since only one IRemoteControlDisplay is supported, this has no effect if the one to
3859     *    unregister is not the current one.
3860     * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
3861     */
3862    public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
3863        if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
3864        synchronized(mRCStack) {
3865            // only one display here, so you can only unregister the current display
3866            if ((rcd == null) || (rcd != mRcDisplay)) {
3867                if (DEBUG_RC) Log.w(TAG, "    trying to unregister unregistered RCD");
3868                return;
3869            }
3870            // if we had a display before, stop monitoring its death
3871            rcDisplay_stopDeathMonitor_syncRcStack();
3872            mRcDisplay = null;
3873
3874            // disconnect this remote control display from all the clients
3875            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3876            while(stackIterator.hasNext()) {
3877                RemoteControlStackEntry rcse = stackIterator.next();
3878                if(rcse.mRcClient != null) {
3879                    try {
3880                        rcse.mRcClient.unplugRemoteControlDisplay(rcd);
3881                    } catch (RemoteException e) {
3882                        Log.e(TAG, "Error disconnecting remote control display to client: " + e);
3883                        e.printStackTrace();
3884                    }
3885                }
3886            }
3887        }
3888    }
3889
3890    public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
3891        synchronized(mRCStack) {
3892            // NOTE: Only one IRemoteControlDisplay supported in this implementation
3893            mArtworkExpectedWidth = w;
3894            mArtworkExpectedHeight = h;
3895        }
3896    }
3897
3898    @Override
3899    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3900        // TODO probably a lot more to do here than just the audio focus and remote control stacks
3901        dumpFocusStack(pw);
3902        dumpRCStack(pw);
3903    }
3904
3905
3906}
3907