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