AudioService.java revision d5603c126acdd94d9f7400bb1d04188020a425b4
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.media;
18
19import android.app.ActivityManagerNative;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.Intent;
23import android.content.IntentFilter;
24import android.bluetooth.BluetoothIntent;
25import android.content.BroadcastReceiver;
26import android.bluetooth.BluetoothHeadset;
27import android.bluetooth.BluetoothA2dp;
28import android.bluetooth.BluetoothDevice;
29import android.bluetooth.BluetoothClass;
30
31import android.content.pm.PackageManager;
32import android.database.ContentObserver;
33import android.media.MediaPlayer.OnCompletionListener;
34import android.media.MediaPlayer.OnErrorListener;
35import android.os.Binder;
36import android.os.Environment;
37import android.os.Handler;
38import android.os.IBinder;
39import android.os.Looper;
40import android.os.Message;
41import android.os.RemoteException;
42import android.os.ServiceManager;
43import android.provider.Settings;
44import android.provider.Settings.System;
45import android.util.Log;
46import android.view.VolumePanel;
47
48import com.android.internal.telephony.ITelephony;
49
50import java.io.IOException;
51import java.util.ArrayList;
52import java.util.HashMap;
53import java.util.Iterator;
54import java.util.Map;
55import java.util.Set;
56
57/**
58 * The implementation of the volume manager service.
59 * <p>
60 * This implementation focuses on delivering a responsive UI. Most methods are
61 * asynchronous to external calls. For example, the task of setting a volume
62 * will update our internal state, but in a separate thread will set the system
63 * volume and later persist to the database. Similarly, setting the ringer mode
64 * will update the state and broadcast a change and in a separate thread later
65 * persist the ringer mode.
66 *
67 * @hide
68 */
69public class AudioService extends IAudioService.Stub {
70
71    private static final String TAG = "AudioService";
72
73    /** How long to delay before persisting a change in volume/ringer mode. */
74    private static final int PERSIST_DELAY = 3000;
75
76    private Context mContext;
77    private ContentResolver mContentResolver;
78
79    /** The UI */
80    private VolumePanel mVolumePanel;
81
82    // sendMsg() flags
83    /** Used when a message should be shared across all stream types. */
84    private static final int SHARED_MSG = -1;
85    /** If the msg is already queued, replace it with this one. */
86    private static final int SENDMSG_REPLACE = 0;
87    /** If the msg is already queued, ignore this one and leave the old. */
88    private static final int SENDMSG_NOOP = 1;
89    /** If the msg is already queued, queue this one and leave the old. */
90    private static final int SENDMSG_QUEUE = 2;
91
92    // AudioHandler message.whats
93    private static final int MSG_SET_SYSTEM_VOLUME = 0;
94    private static final int MSG_PERSIST_VOLUME = 1;
95    private static final int MSG_PERSIST_RINGER_MODE = 3;
96    private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
97    private static final int MSG_MEDIA_SERVER_DIED = 5;
98    private static final int MSG_MEDIA_SERVER_STARTED = 6;
99    private static final int MSG_PLAY_SOUND_EFFECT = 7;
100
101    /** @see AudioSystemThread */
102    private AudioSystemThread mAudioSystemThread;
103    /** @see AudioHandler */
104    private AudioHandler mAudioHandler;
105    /** @see VolumeStreamState */
106    private VolumeStreamState[] mStreamStates;
107    private SettingsObserver mSettingsObserver;
108
109    private int mMode;
110    private Object mSettingsLock = new Object();
111    private boolean mMediaServerOk;
112
113    private SoundPool mSoundPool;
114    private Object mSoundEffectsLock = new Object();
115    private static final int NUM_SOUNDPOOL_CHANNELS = 4;
116    private static final int SOUND_EFFECT_VOLUME = 1000;
117
118    /* Sound effect file names  */
119    private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
120    private static final String[] SOUND_EFFECT_FILES = new String[] {
121        "Effect_Tick.ogg",
122        "KeypressStandard.ogg",
123        "KeypressSpacebar.ogg",
124        "KeypressDelete.ogg",
125        "KeypressReturn.ogg"
126    };
127
128    /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
129     * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
130     * uses soundpool (second column) */
131    private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
132        {0, -1},  // FX_KEY_CLICK
133        {0, -1},  // FX_FOCUS_NAVIGATION_UP
134        {0, -1},  // FX_FOCUS_NAVIGATION_DOWN
135        {0, -1},  // FX_FOCUS_NAVIGATION_LEFT
136        {0, -1},  // FX_FOCUS_NAVIGATION_RIGHT
137        {1, -1},  // FX_KEYPRESS_STANDARD
138        {2, -1},  // FX_KEYPRESS_SPACEBAR
139        {3, -1},  // FX_FOCUS_DELETE
140        {4, -1}   // FX_FOCUS_RETURN
141    };
142
143    /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
144     * of another stream: This avoids multiplying the volume settings for hidden
145     * stream types that follow other stream behavior for volume settings
146     * NOTE: do not create loops in aliases! */
147    private int[] STREAM_VOLUME_ALIAS = new int[] {
148        AudioSystem.STREAM_VOICE_CALL,  // STREAM_VOICE_CALL
149        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM
150        AudioSystem.STREAM_RING,  // STREAM_RING
151        AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
152        AudioSystem.STREAM_ALARM,  // STREAM_ALARM
153        AudioSystem.STREAM_NOTIFICATION,  // STREAM_NOTIFICATION
154        AudioSystem.STREAM_VOICE_CALL, // STREAM_BLUETOOTH_SCO
155        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM_ENFORCED
156        AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
157        AudioSystem.STREAM_MUSIC  // STREAM_TTS
158    };
159
160    private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
161        public void onError(int error) {
162            switch (error) {
163            case AudioSystem.AUDIO_STATUS_SERVER_DIED:
164                if (mMediaServerOk) {
165                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
166                            null, 1500);
167                }
168                break;
169            case AudioSystem.AUDIO_STATUS_OK:
170                if (!mMediaServerOk) {
171                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
172                            null, 0);
173                }
174                break;
175            default:
176                break;
177            }
178       }
179    };
180
181    /**
182     * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
183     * {@link AudioManager#RINGER_MODE_SILENT}, or
184     * {@link AudioManager#RINGER_MODE_VIBRATE}.
185     */
186    private int mRingerMode;
187
188    /** @see System#MODE_RINGER_STREAMS_AFFECTED */
189    private int mRingerModeAffectedStreams;
190
191    /** @see System#MUTE_STREAMS_AFFECTED */
192    private int mMuteAffectedStreams;
193
194    /**
195     * Has multiple bits per vibrate type to indicate the type's vibrate
196     * setting. See {@link #setVibrateSetting(int, int)}.
197     * <p>
198     * NOTE: This is not the final decision of whether vibrate is on/off for the
199     * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
200     */
201    private int mVibrateSetting;
202
203    /** @see System#NOTIFICATIONS_USE_RING_VOLUME */
204    private int mNotificationsUseRingVolume;
205
206    // Broadcast receiver for device connections intent broadcasts
207    private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
208
209    //TODO: use common definitions with HeadsetObserver
210    private static final int BIT_HEADSET = (1 << 0);
211    private static final int BIT_HEADSET_NO_MIC = (1 << 1);
212    private static final int BIT_TTY = (1 << 2);
213    private static final int BIT_FM_HEADSET = (1 << 3);
214    private static final int BIT_FM_SPEAKER = (1 << 4);
215
216    private int mHeadsetState;
217
218    // Devices currently connected
219    private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
220
221    // Forced device usage for communications
222    private int mForcedUseForComm;
223
224    private BluetoothDevice mBluetoothDevice = null;
225
226    ///////////////////////////////////////////////////////////////////////////
227    // Construction
228    ///////////////////////////////////////////////////////////////////////////
229
230    /** @hide */
231    public AudioService(Context context) {
232        mContext = context;
233        mContentResolver = context.getContentResolver();
234        mVolumePanel = new VolumePanel(context, this);
235        mSettingsObserver = new SettingsObserver();
236        mMode = AudioSystem.MODE_NORMAL;
237        mHeadsetState = 0;
238        mForcedUseForComm = AudioSystem.FORCE_NONE;
239        createAudioSystemThread();
240        readPersistedSettings();
241        createStreamStates();
242        mMediaServerOk = true;
243        AudioSystem.setErrorCallback(mAudioSystemCallback);
244        loadSoundEffects();
245
246        // Register for device connection intent broadcasts.
247        IntentFilter intentFilter =
248                new IntentFilter(Intent.ACTION_HEADSET_PLUG);
249        intentFilter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
250        intentFilter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION);
251        context.registerReceiver(mReceiver, intentFilter);
252    }
253
254    private void createAudioSystemThread() {
255        mAudioSystemThread = new AudioSystemThread();
256        mAudioSystemThread.start();
257        waitForAudioHandlerCreation();
258    }
259
260    /** Waits for the volume handler to be created by the other thread. */
261    private void waitForAudioHandlerCreation() {
262        synchronized(this) {
263            while (mAudioHandler == null) {
264                try {
265                    // Wait for mAudioHandler to be set by the other thread
266                    wait();
267                } catch (InterruptedException e) {
268                    Log.e(TAG, "Interrupted while waiting on volume handler.");
269                }
270            }
271        }
272    }
273
274    private void createStreamStates() {
275        int numStreamTypes = AudioSystem.getNumStreamTypes();
276        VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
277
278        for (int i = 0; i < numStreamTypes; i++) {
279            streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
280        }
281
282        // Correct stream index values for streams with aliases
283        for (int i = 0; i < numStreamTypes; i++) {
284            if (STREAM_VOLUME_ALIAS[i] != i) {
285                int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
286                streams[i].mIndex = streams[i].getValidIndex(index);
287                setStreamVolumeIndex(i, index);
288                index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
289                streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
290            }
291        }
292    }
293
294    private void readPersistedSettings() {
295        final ContentResolver cr = mContentResolver;
296
297        mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
298
299        mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
300
301        mRingerModeAffectedStreams = Settings.System.getInt(cr,
302                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
303                ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
304                 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
305
306        mMuteAffectedStreams = System.getInt(cr,
307                System.MUTE_STREAMS_AFFECTED,
308                ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
309
310        mNotificationsUseRingVolume = System.getInt(cr,
311                Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1);
312
313        if (mNotificationsUseRingVolume == 1) {
314            STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
315        }
316        // Each stream will read its own persisted settings
317
318        // Broadcast the sticky intent
319        broadcastRingerMode();
320
321        // Broadcast vibrate settings
322        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
323        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
324    }
325
326    private void setStreamVolumeIndex(int stream, int index) {
327        AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
328    }
329
330    private int rescaleIndex(int index, int srcStream, int dstStream) {
331        return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
332    }
333
334    ///////////////////////////////////////////////////////////////////////////
335    // IPC methods
336    ///////////////////////////////////////////////////////////////////////////
337
338    /** @see AudioManager#adjustVolume(int, int) */
339    public void adjustVolume(int direction, int flags) {
340        adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
341    }
342
343    /** @see AudioManager#adjustVolume(int, int, int) */
344    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
345
346        int streamType = getActiveStreamType(suggestedStreamType);
347
348        // Don't play sound on other streams
349        if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
350            flags &= ~AudioManager.FLAG_PLAY_SOUND;
351        }
352
353        adjustStreamVolume(streamType, direction, flags);
354    }
355
356    /** @see AudioManager#adjustStreamVolume(int, int, int) */
357    public void adjustStreamVolume(int streamType, int direction, int flags) {
358        ensureValidDirection(direction);
359        ensureValidStreamType(streamType);
360
361
362        VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
363        final int oldIndex = streamState.mIndex;
364        boolean adjustVolume = true;
365
366        // If either the client forces allowing ringer modes for this adjustment,
367        // or the stream type is one that is affected by ringer modes
368        if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0
369                || streamType == AudioSystem.STREAM_RING) {
370            // Check if the ringer mode changes with this volume adjustment. If
371            // it does, it will handle adjusting the volume, so we won't below
372            adjustVolume = checkForRingerModeChange(oldIndex, direction);
373        }
374
375        if (adjustVolume && streamState.adjustIndex(direction)) {
376            // Post message to set system volume (it in turn will post a message
377            // to persist). Do not change volume if stream is muted.
378            if (streamState.muteCount() == 0) {
379                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,
380                        streamState, 0);
381            }
382        }
383
384        // UI
385        mVolumePanel.postVolumeChanged(streamType, flags);
386        // Broadcast Intent
387        sendVolumeUpdate(streamType);
388    }
389
390    /** @see AudioManager#setStreamVolume(int, int, int) */
391    public void setStreamVolume(int streamType, int index, int flags) {
392        ensureValidStreamType(streamType);
393        index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
394        setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
395
396        // UI, etc.
397        mVolumePanel.postVolumeChanged(streamType, flags);
398        // Broadcast Intent
399        sendVolumeUpdate(streamType);
400    }
401
402    private void sendVolumeUpdate(int streamType) {
403        Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
404        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
405        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, getStreamVolume(streamType));
406
407        // Currently, sending the intent only when the stream is BLUETOOTH_SCO
408        if (streamType == AudioSystem.STREAM_BLUETOOTH_SCO) {
409            mContext.sendBroadcast(intent);
410        }
411    }
412
413    /**
414     * Sets the stream state's index, and posts a message to set system volume.
415     * This will not call out to the UI. Assumes a valid stream type.
416     *
417     * @param streamType Type of the stream
418     * @param index Desired volume index of the stream
419     * @param force If true, set the volume even if the desired volume is same
420     * as the current volume.
421     * @param lastAudible If true, stores new index as last audible one
422     */
423    private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
424        VolumeStreamState streamState = mStreamStates[streamType];
425        if (streamState.setIndex(index, lastAudible) || force) {
426            // Post message to set system volume (it in turn will post a message
427            // to persist). Do not change volume if stream is muted.
428            if (streamState.muteCount() == 0) {
429                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
430                        streamState, 0);
431            }
432        }
433    }
434
435    /** @see AudioManager#setStreamSolo(int, boolean) */
436    public void setStreamSolo(int streamType, boolean state, IBinder cb) {
437        for (int stream = 0; stream < mStreamStates.length; stream++) {
438            if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
439            // Bring back last audible volume
440            mStreamStates[stream].mute(cb, state);
441         }
442    }
443
444    /** @see AudioManager#setStreamMute(int, boolean) */
445    public void setStreamMute(int streamType, boolean state, IBinder cb) {
446        if (isStreamAffectedByMute(streamType)) {
447            mStreamStates[streamType].mute(cb, state);
448        }
449    }
450
451    /** @see AudioManager#getStreamVolume(int) */
452    public int getStreamVolume(int streamType) {
453        ensureValidStreamType(streamType);
454        return (mStreamStates[streamType].mIndex + 5) / 10;
455    }
456
457    /** @see AudioManager#getStreamMaxVolume(int) */
458    public int getStreamMaxVolume(int streamType) {
459        ensureValidStreamType(streamType);
460        return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
461    }
462
463    /** @see AudioManager#getRingerMode() */
464    public int getRingerMode() {
465        return mRingerMode;
466    }
467
468    /** @see AudioManager#setRingerMode(int) */
469    public void setRingerMode(int ringerMode) {
470        synchronized (mSettingsLock) {
471            if (ringerMode != mRingerMode) {
472                setRingerModeInt(ringerMode, true);
473                // Send sticky broadcast
474                broadcastRingerMode();
475            }
476        }
477    }
478
479    private void setRingerModeInt(int ringerMode, boolean persist) {
480        mRingerMode = ringerMode;
481
482        // Adjust volumes via posting message
483        int numStreamTypes = AudioSystem.getNumStreamTypes();
484        if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
485            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
486                if (!isStreamAffectedByRingerMode(streamType)) continue;
487                // Bring back last audible volume
488                setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
489                                   false, false);
490            }
491        } else {
492            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
493                if (isStreamAffectedByRingerMode(streamType)) {
494                    // Either silent or vibrate, either way volume is 0
495                    setStreamVolumeInt(streamType, 0, false, false);
496                } else {
497                    // restore stream volume in the case the stream changed from affected
498                    // to non affected by ringer mode. Does not arm to do it for streams that
499                    // are not affected as well.
500                    setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
501                            false, false);
502                }
503            }
504        }
505
506        // Post a persist ringer mode msg
507        if (persist) {
508            sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
509                    SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
510        }
511    }
512
513    /** @see AudioManager#shouldVibrate(int) */
514    public boolean shouldVibrate(int vibrateType) {
515
516        switch (getVibrateSetting(vibrateType)) {
517
518            case AudioManager.VIBRATE_SETTING_ON:
519                return mRingerMode != AudioManager.RINGER_MODE_SILENT;
520
521            case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
522                return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
523
524            case AudioManager.VIBRATE_SETTING_OFF:
525                // Phone ringer should always vibrate in vibrate mode
526                if (vibrateType == AudioManager.VIBRATE_TYPE_RINGER) {
527                    return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
528                }
529
530            default:
531                return false;
532        }
533    }
534
535    /** @see AudioManager#getVibrateSetting(int) */
536    public int getVibrateSetting(int vibrateType) {
537        return (mVibrateSetting >> (vibrateType * 2)) & 3;
538    }
539
540    /** @see AudioManager#setVibrateSetting(int, int) */
541    public void setVibrateSetting(int vibrateType, int vibrateSetting) {
542
543        mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
544
545        // Broadcast change
546        broadcastVibrateSetting(vibrateType);
547
548        // Post message to set ringer mode (it in turn will post a message
549        // to persist)
550        sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
551                null, 0);
552    }
553
554    /**
555     * @see #setVibrateSetting(int, int)
556     */
557    public static int getValueForVibrateSetting(int existingValue, int vibrateType,
558            int vibrateSetting) {
559
560        // First clear the existing setting. Each vibrate type has two bits in
561        // the value. Note '3' is '11' in binary.
562        existingValue &= ~(3 << (vibrateType * 2));
563
564        // Set into the old value
565        existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
566
567        return existingValue;
568    }
569
570    /** @see AudioManager#setMode(int) */
571    public void setMode(int mode) {
572        if (!checkAudioSettingsPermission("setMode()")) {
573            return;
574        }
575
576        if (mode < AudioSystem.MODE_CURRENT || mode > AudioSystem.MODE_IN_CALL) {
577            return;
578        }
579
580        synchronized (mSettingsLock) {
581            if (mode == AudioSystem.MODE_CURRENT) {
582                mode = mMode;
583            }
584            if (mode != mMode) {
585                if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
586                    mMode = mode;
587                }
588            }
589            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
590            int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
591            setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, true);
592        }
593    }
594
595    /** @see AudioManager#getMode() */
596    public int getMode() {
597        return mMode;
598    }
599
600    /** @see AudioManager#playSoundEffect(int) */
601    public void playSoundEffect(int effectType) {
602        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
603                effectType, SOUND_EFFECT_VOLUME, null, 0);
604    }
605
606    /** @see AudioManager#playSoundEffect(int, float) */
607    public void playSoundEffectVolume(int effectType, float volume) {
608        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
609                effectType, (int) (volume * 1000), null, 0);
610    }
611
612    /**
613     * Loads samples into the soundpool.
614     * This method must be called at when sound effects are enabled
615     */
616    public boolean loadSoundEffects() {
617        synchronized (mSoundEffectsLock) {
618            mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
619            if (mSoundPool == null) {
620                return false;
621            }
622            /*
623             * poolId table: The value -1 in this table indicates that corresponding
624             * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
625             * Once loaded, the value in poolId is the sample ID and the same
626             * sample can be reused for another effect using the same file.
627             */
628            int[] poolId = new int[SOUND_EFFECT_FILES.length];
629            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
630                poolId[fileIdx] = -1;
631            }
632            /*
633             * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
634             * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
635             * this indicates we have a valid sample loaded for this effect.
636             */
637            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
638                // Do not load sample if this effect uses the MediaPlayer
639                if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
640                    continue;
641                }
642                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
643                    String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
644                    int sampleId = mSoundPool.load(filePath, 0);
645                    SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
646                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
647                    if (sampleId <= 0) {
648                        Log.w(TAG, "Soundpool could not load file: "+filePath);
649                    }
650                } else {
651                    SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
652                }
653            }
654        }
655
656        return true;
657    }
658
659    /**
660     *  Unloads samples from the sound pool.
661     *  This method can be called to free some memory when
662     *  sound effects are disabled.
663     */
664    public void unloadSoundEffects() {
665        synchronized (mSoundEffectsLock) {
666            if (mSoundPool == null) {
667                return;
668            }
669            int[] poolId = new int[SOUND_EFFECT_FILES.length];
670            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
671                poolId[fileIdx] = 0;
672            }
673
674            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
675                if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
676                    continue;
677                }
678                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
679                    mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
680                    SOUND_EFFECT_FILES_MAP[effect][1] = -1;
681                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
682                }
683            }
684            mSoundPool = null;
685        }
686    }
687
688    /** @see AudioManager#reloadAudioSettings() */
689    public void reloadAudioSettings() {
690        // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
691        readPersistedSettings();
692
693        // restore volume settings
694        int numStreamTypes = AudioSystem.getNumStreamTypes();
695        for (int streamType = 0; streamType < numStreamTypes; streamType++) {
696            VolumeStreamState streamState = mStreamStates[streamType];
697
698            String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
699            String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
700            int index = Settings.System.getInt(mContentResolver,
701                                           settingName,
702                                           AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
703            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
704                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
705            } else {
706                index *= 10;
707            }
708            streamState.mIndex = streamState.getValidIndex(index);
709
710            index = (index + 5) / 10;
711            index = Settings.System.getInt(mContentResolver,
712                                            lastAudibleSettingName,
713                                            (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
714            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
715                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
716            } else {
717                index *= 10;
718            }
719            streamState.mLastAudibleIndex = streamState.getValidIndex(index);
720
721            // unmute stream that whas muted but is not affect by mute anymore
722            if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
723                int size = streamState.mDeathHandlers.size();
724                for (int i = 0; i < size; i++) {
725                    streamState.mDeathHandlers.get(i).mMuteCount = 1;
726                    streamState.mDeathHandlers.get(i).mute(false);
727                }
728            }
729            // apply stream volume
730            if (streamState.muteCount() == 0) {
731                setStreamVolumeIndex(streamType, streamState.mIndex);
732            }
733        }
734
735        // apply new ringer mode
736        setRingerModeInt(getRingerMode(), false);
737    }
738
739    /** @see AudioManager#setSpeakerphoneOn() */
740    public void setSpeakerphoneOn(boolean on){
741        if (on) {
742            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER);
743            mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
744        } else {
745            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
746            mForcedUseForComm = AudioSystem.FORCE_NONE;
747        }
748    }
749
750    /** @see AudioManager#isSpeakerphoneOn() */
751    public boolean isSpeakerphoneOn() {
752        if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
753            return true;
754        } else {
755            return false;
756        }
757    }
758
759    /** @see AudioManager#setBluetoothScoOn() */
760    public void setBluetoothScoOn(boolean on){
761        if (on) {
762            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO);
763            AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO);
764            mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
765        } else {
766            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
767            AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE);
768            mForcedUseForComm = AudioSystem.FORCE_NONE;
769        }
770    }
771
772    /** @see AudioManager#isBluetoothScoOn() */
773    public boolean isBluetoothScoOn() {
774        if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
775            return true;
776        } else {
777            return false;
778        }
779    }
780
781    ///////////////////////////////////////////////////////////////////////////
782    // Internal methods
783    ///////////////////////////////////////////////////////////////////////////
784
785    /**
786     * Checks if the adjustment should change ringer mode instead of just
787     * adjusting volume. If so, this will set the proper ringer mode and volume
788     * indices on the stream states.
789     */
790    private boolean checkForRingerModeChange(int oldIndex, int direction) {
791        boolean adjustVolumeIndex = true;
792        int newRingerMode = mRingerMode;
793
794        if (mRingerMode == AudioManager.RINGER_MODE_NORMAL && (oldIndex + 5) / 10 == 1
795                && direction == AudioManager.ADJUST_LOWER) {
796            newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
797        } else if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
798            if (direction == AudioManager.ADJUST_RAISE) {
799                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
800            } else if (direction == AudioManager.ADJUST_LOWER) {
801                newRingerMode = AudioManager.RINGER_MODE_SILENT;
802            }
803        } else if (direction == AudioManager.ADJUST_RAISE
804                && mRingerMode == AudioManager.RINGER_MODE_SILENT) {
805            newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
806        }
807
808        if (newRingerMode != mRingerMode) {
809            setRingerMode(newRingerMode);
810
811            /*
812             * If we are changing ringer modes, do not increment/decrement the
813             * volume index. Instead, the handler for the message above will
814             * take care of changing the index.
815             */
816            adjustVolumeIndex = false;
817        }
818
819        return adjustVolumeIndex;
820    }
821
822    public boolean isStreamAffectedByRingerMode(int streamType) {
823        return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
824    }
825
826    public boolean isStreamAffectedByMute(int streamType) {
827        return (mMuteAffectedStreams & (1 << streamType)) != 0;
828    }
829
830    private void ensureValidDirection(int direction) {
831        if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
832            throw new IllegalArgumentException("Bad direction " + direction);
833        }
834    }
835
836    private void ensureValidStreamType(int streamType) {
837        if (streamType < 0 || streamType >= mStreamStates.length) {
838            throw new IllegalArgumentException("Bad stream type " + streamType);
839        }
840    }
841
842    private int getActiveStreamType(int suggestedStreamType) {
843        boolean isOffhook = false;
844        try {
845            ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
846            if (phone != null) isOffhook = phone.isOffhook();
847        } catch (RemoteException e) {
848            Log.w(TAG, "Couldn't connect to phone service", e);
849        }
850
851        if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) {
852            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
853            return AudioSystem.STREAM_BLUETOOTH_SCO;
854        } else if (isOffhook) {
855            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
856            return AudioSystem.STREAM_VOICE_CALL;
857        } else if (AudioSystem.isMusicActive()) {
858            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
859            return AudioSystem.STREAM_MUSIC;
860        } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
861            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING...");
862            return AudioSystem.STREAM_RING;
863        } else {
864            // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
865            return suggestedStreamType;
866        }
867    }
868
869    private void broadcastRingerMode() {
870        // Send sticky broadcast
871        if (ActivityManagerNative.isSystemReady()) {
872            Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
873            broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
874            long origCallerIdentityToken = Binder.clearCallingIdentity();
875            mContext.sendStickyBroadcast(broadcast);
876            Binder.restoreCallingIdentity(origCallerIdentityToken);
877        }
878    }
879
880    private void broadcastVibrateSetting(int vibrateType) {
881        // Send broadcast
882        if (ActivityManagerNative.isSystemReady()) {
883            Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
884            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
885            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
886            mContext.sendBroadcast(broadcast);
887        }
888    }
889
890    // Message helper methods
891    private static int getMsg(int baseMsg, int streamType) {
892        return (baseMsg & 0xffff) | streamType << 16;
893    }
894
895    private static int getMsgBase(int msg) {
896        return msg & 0xffff;
897    }
898
899    private static void sendMsg(Handler handler, int baseMsg, int streamType,
900            int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
901        int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
902
903        if (existingMsgPolicy == SENDMSG_REPLACE) {
904            handler.removeMessages(msg);
905        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
906            return;
907        }
908
909        handler
910                .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
911    }
912
913    boolean checkAudioSettingsPermission(String method) {
914        if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
915                == PackageManager.PERMISSION_GRANTED) {
916            return true;
917        }
918        String msg = "Audio Settings Permission Denial: " + method + " from pid="
919                + Binder.getCallingPid()
920                + ", uid=" + Binder.getCallingUid();
921        Log.w(TAG, msg);
922        return false;
923    }
924
925
926    ///////////////////////////////////////////////////////////////////////////
927    // Inner classes
928    ///////////////////////////////////////////////////////////////////////////
929
930    public class VolumeStreamState {
931        private final String mVolumeIndexSettingName;
932        private final String mLastAudibleVolumeIndexSettingName;
933        private final int mStreamType;
934
935        private int mIndexMax;
936        private int mIndex;
937        private int mLastAudibleIndex;
938        private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
939
940        private VolumeStreamState(String settingName, int streamType) {
941
942            mVolumeIndexSettingName = settingName;
943            mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
944
945            mStreamType = streamType;
946
947            final ContentResolver cr = mContentResolver;
948            mIndexMax = AudioManager.MAX_STREAM_VOLUME[streamType];
949            mIndex = Settings.System.getInt(cr,
950                                            mVolumeIndexSettingName,
951                                            AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
952            mLastAudibleIndex = Settings.System.getInt(cr,
953                                                       mLastAudibleVolumeIndexSettingName,
954                                                       (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
955            AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
956            mIndexMax *= 10;
957            mIndex = getValidIndex(10 * mIndex);
958            mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
959            setStreamVolumeIndex(streamType, mIndex);
960            mDeathHandlers = new ArrayList<VolumeDeathHandler>();
961        }
962
963        public boolean adjustIndex(int deltaIndex) {
964            return setIndex(mIndex + deltaIndex * 10, true);
965        }
966
967        public boolean setIndex(int index, boolean lastAudible) {
968            int oldIndex = mIndex;
969            mIndex = getValidIndex(index);
970
971            if (oldIndex != mIndex) {
972                if (lastAudible) {
973                    mLastAudibleIndex = mIndex;
974                }
975                // Apply change to all streams using this one as alias
976                int numStreamTypes = AudioSystem.getNumStreamTypes();
977                for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
978                    if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
979                        mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
980                    }
981                }
982                return true;
983            } else {
984                return false;
985            }
986        }
987
988        public int getMaxIndex() {
989            return mIndexMax;
990        }
991
992        public void mute(IBinder cb, boolean state) {
993            VolumeDeathHandler handler = getDeathHandler(cb, state);
994            if (handler == null) {
995                Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
996                return;
997            }
998            handler.mute(state);
999        }
1000
1001        private int getValidIndex(int index) {
1002            if (index < 0) {
1003                return 0;
1004            } else if (index > mIndexMax) {
1005                return mIndexMax;
1006            }
1007
1008            return index;
1009        }
1010
1011        private class VolumeDeathHandler implements IBinder.DeathRecipient {
1012            private IBinder mICallback; // To be notified of client's death
1013            private int mMuteCount; // Number of active mutes for this client
1014
1015            VolumeDeathHandler(IBinder cb) {
1016                mICallback = cb;
1017            }
1018
1019            public void mute(boolean state) {
1020                synchronized(mDeathHandlers) {
1021                    if (state) {
1022                        if (mMuteCount == 0) {
1023                            // Register for client death notification
1024                            try {
1025                                mICallback.linkToDeath(this, 0);
1026                                mDeathHandlers.add(this);
1027                                // If the stream is not yet muted by any client, set lvel to 0
1028                                if (muteCount() == 0) {
1029                                    setIndex(0, false);
1030                                    sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1031                                            VolumeStreamState.this, 0);
1032                                }
1033                            } catch (RemoteException e) {
1034                                // Client has died!
1035                                binderDied();
1036                                mDeathHandlers.notify();
1037                                return;
1038                            }
1039                        } else {
1040                            Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
1041                        }
1042                        mMuteCount++;
1043                    } else {
1044                        if (mMuteCount == 0) {
1045                            Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
1046                        } else {
1047                            mMuteCount--;
1048                            if (mMuteCount == 0) {
1049                                // Unregistr from client death notification
1050                                mDeathHandlers.remove(this);
1051                                mICallback.unlinkToDeath(this, 0);
1052                                if (muteCount() == 0) {
1053                                    // If the stream is not mut any more, restore it's volume if
1054                                    // ringer mode allows it
1055                                    if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
1056                                        setIndex(mLastAudibleIndex, false);
1057                                        sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1058                                                VolumeStreamState.this, 0);
1059                                    }
1060                                }
1061                            }
1062                        }
1063                    }
1064                    mDeathHandlers.notify();
1065                }
1066            }
1067
1068            public void binderDied() {
1069                Log.w(TAG, "Volume service client died for stream: "+mStreamType);
1070                if (mMuteCount != 0) {
1071                    // Reset all active mute requests from this client.
1072                    mMuteCount = 1;
1073                    mute(false);
1074                }
1075            }
1076        }
1077
1078        private int muteCount() {
1079            int count = 0;
1080            int size = mDeathHandlers.size();
1081            for (int i = 0; i < size; i++) {
1082                count += mDeathHandlers.get(i).mMuteCount;
1083            }
1084            return count;
1085        }
1086
1087        private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
1088            synchronized(mDeathHandlers) {
1089                VolumeDeathHandler handler;
1090                int size = mDeathHandlers.size();
1091                for (int i = 0; i < size; i++) {
1092                    handler = mDeathHandlers.get(i);
1093                    if (cb.equals(handler.mICallback)) {
1094                        return handler;
1095                    }
1096                }
1097                // If this is the first mute request for this client, create a new
1098                // client death handler. Otherwise, it is an out of sequence unmute request.
1099                if (state) {
1100                    handler = new VolumeDeathHandler(cb);
1101                } else {
1102                    Log.w(TAG, "stream was not muted by this client");
1103                    handler = null;
1104                }
1105                return handler;
1106            }
1107        }
1108    }
1109
1110    /** Thread that handles native AudioSystem control. */
1111    private class AudioSystemThread extends Thread {
1112        AudioSystemThread() {
1113            super("AudioService");
1114        }
1115
1116        @Override
1117        public void run() {
1118            // Set this thread up so the handler will work on it
1119            Looper.prepare();
1120
1121            synchronized(AudioService.this) {
1122                mAudioHandler = new AudioHandler();
1123
1124                // Notify that the handler has been created
1125                AudioService.this.notify();
1126            }
1127
1128            // Listen for volume change requests that are set by VolumePanel
1129            Looper.loop();
1130        }
1131    }
1132
1133    /** Handles internal volume messages in separate volume thread. */
1134    private class AudioHandler extends Handler {
1135
1136        private void setSystemVolume(VolumeStreamState streamState) {
1137
1138            // Adjust volume
1139            setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
1140
1141            // Apply change to all streams using this one as alias
1142            int numStreamTypes = AudioSystem.getNumStreamTypes();
1143            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1144                if (streamType != streamState.mStreamType &&
1145                    STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
1146                    setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
1147                }
1148            }
1149
1150            // Post a persist volume msg
1151            sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
1152                    SENDMSG_REPLACE, 0, 0, streamState, PERSIST_DELAY);
1153        }
1154
1155        private void persistVolume(VolumeStreamState streamState) {
1156            System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
1157                    (streamState.mIndex + 5)/ 10);
1158            System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
1159                    (streamState.mLastAudibleIndex + 5) / 10);
1160        }
1161
1162        private void persistRingerMode() {
1163            System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
1164        }
1165
1166        private void persistVibrateSetting() {
1167            System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
1168        }
1169
1170        private void playSoundEffect(int effectType, int volume) {
1171            synchronized (mSoundEffectsLock) {
1172                if (mSoundPool == null) {
1173                    return;
1174                }
1175
1176                if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
1177                    float v = (float) volume / 1000.0f;
1178                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], v, v, 0, 0, 1.0f);
1179                } else {
1180                    MediaPlayer mediaPlayer = new MediaPlayer();
1181                    if (mediaPlayer != null) {
1182                        try {
1183                            String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
1184                            mediaPlayer.setDataSource(filePath);
1185                            mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
1186                            mediaPlayer.prepare();
1187                            mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
1188                                public void onCompletion(MediaPlayer mp) {
1189                                    cleanupPlayer(mp);
1190                                }
1191                            });
1192                            mediaPlayer.setOnErrorListener(new OnErrorListener() {
1193                                public boolean onError(MediaPlayer mp, int what, int extra) {
1194                                    cleanupPlayer(mp);
1195                                    return true;
1196                                }
1197                            });
1198                            mediaPlayer.start();
1199                        } catch (IOException ex) {
1200                            Log.w(TAG, "MediaPlayer IOException: "+ex);
1201                        } catch (IllegalArgumentException ex) {
1202                            Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
1203                        } catch (IllegalStateException ex) {
1204                            Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1205                        }
1206                    }
1207                }
1208            }
1209        }
1210
1211        private void cleanupPlayer(MediaPlayer mp) {
1212            if (mp != null) {
1213                try {
1214                    mp.stop();
1215                    mp.release();
1216                } catch (IllegalStateException ex) {
1217                    Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1218                }
1219            }
1220        }
1221
1222        @Override
1223        public void handleMessage(Message msg) {
1224            int baseMsgWhat = getMsgBase(msg.what);
1225
1226            switch (baseMsgWhat) {
1227
1228                case MSG_SET_SYSTEM_VOLUME:
1229                    setSystemVolume((VolumeStreamState) msg.obj);
1230                    break;
1231
1232                case MSG_PERSIST_VOLUME:
1233                    persistVolume((VolumeStreamState) msg.obj);
1234                    break;
1235
1236                case MSG_PERSIST_RINGER_MODE:
1237                    persistRingerMode();
1238                    break;
1239
1240                case MSG_PERSIST_VIBRATE_SETTING:
1241                    persistVibrateSetting();
1242                    break;
1243
1244                case MSG_MEDIA_SERVER_DIED:
1245                    Log.e(TAG, "Media server died.");
1246                    // Force creation of new IAudioflinger interface
1247                    mMediaServerOk = false;
1248                    AudioSystem.isMusicActive();
1249                    break;
1250
1251                case MSG_MEDIA_SERVER_STARTED:
1252                    Log.e(TAG, "Media server started.");
1253                    // Restore device connection states
1254                    Set set = mConnectedDevices.entrySet();
1255                    Iterator i = set.iterator();
1256                    while(i.hasNext()){
1257                        Map.Entry device = (Map.Entry)i.next();
1258                        AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
1259                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
1260                                                             (String)device.getValue());
1261                    }
1262
1263                    // Restore call state
1264                    AudioSystem.setPhoneState(mMode);
1265
1266                    // Restore forced usage for communcations and record
1267                    AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
1268                    AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
1269
1270                    // Restore stream volumes
1271                    int numStreamTypes = AudioSystem.getNumStreamTypes();
1272                    for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1273                        int index;
1274                        VolumeStreamState streamState = mStreamStates[streamType];
1275                        AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
1276                        if (streamState.muteCount() == 0) {
1277                            index = streamState.mIndex;
1278                        } else {
1279                            index = 0;
1280                        }
1281                        setStreamVolumeIndex(streamType, index);
1282                    }
1283
1284                    // Restore ringer mode
1285                    setRingerModeInt(getRingerMode(), false);
1286
1287                    mMediaServerOk = true;
1288                    break;
1289
1290                case MSG_PLAY_SOUND_EFFECT:
1291                    playSoundEffect(msg.arg1, msg.arg2);
1292                    break;
1293            }
1294        }
1295    }
1296
1297    private class SettingsObserver extends ContentObserver {
1298
1299        SettingsObserver() {
1300            super(new Handler());
1301            mContentResolver.registerContentObserver(Settings.System.getUriFor(
1302                Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
1303            mContentResolver.registerContentObserver(Settings.System.getUriFor(
1304                    Settings.System.NOTIFICATIONS_USE_RING_VOLUME), false, this);
1305        }
1306
1307        @Override
1308        public void onChange(boolean selfChange) {
1309            super.onChange(selfChange);
1310            synchronized (mSettingsLock) {
1311                int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
1312                        Settings.System.MODE_RINGER_STREAMS_AFFECTED,
1313                        0);
1314                if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
1315                    /*
1316                     * Ensure all stream types that should be affected by ringer mode
1317                     * are in the proper state.
1318                     */
1319                    mRingerModeAffectedStreams = ringerModeAffectedStreams;
1320                    setRingerModeInt(getRingerMode(), false);
1321                }
1322
1323                int notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
1324                        Settings.System.NOTIFICATIONS_USE_RING_VOLUME,
1325                        1);
1326                if (notificationsUseRingVolume != mNotificationsUseRingVolume) {
1327                    mNotificationsUseRingVolume = notificationsUseRingVolume;
1328                    if (mNotificationsUseRingVolume == 1) {
1329                        STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
1330                    } else {
1331                        STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
1332                        // Persist notification volume volume as it was not persisted while aliased to ring volume
1333                        sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
1334                                SENDMSG_REPLACE, 0, 0, mStreamStates[AudioSystem.STREAM_NOTIFICATION], PERSIST_DELAY);
1335                    }
1336                }
1337            }
1338        }
1339    }
1340
1341    /**
1342     * Receiver for misc intent broadcasts the Phone app cares about.
1343     */
1344    private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
1345        @Override
1346        public void onReceive(Context context, Intent intent) {
1347            String action = intent.getAction();
1348
1349            if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
1350                int state = intent.getIntExtra(BluetoothA2dp.SINK_STATE,
1351                                               BluetoothA2dp.STATE_DISCONNECTED);
1352                String address = intent.getStringExtra(BluetoothIntent.ADDRESS);
1353
1354                boolean isConnected = (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
1355                                       ((String)mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)).equals(address));
1356
1357                if (isConnected &&
1358                    state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) {
1359                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1360                            AudioSystem.DEVICE_STATE_UNAVAILABLE,
1361                            address);
1362                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
1363                } else if (!isConnected &&
1364                           (state == BluetoothA2dp.STATE_CONNECTED || state != BluetoothA2dp.STATE_PLAYING)){
1365                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1366                                                         AudioSystem.DEVICE_STATE_AVAILABLE,
1367                                                         address);
1368                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), address);
1369                }
1370            } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) {
1371                int state = intent.getIntExtra(BluetoothIntent.HEADSET_STATE,
1372                                               BluetoothHeadset.STATE_ERROR);
1373                String address = intent.getStringExtra(BluetoothIntent.ADDRESS);
1374                int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
1375                if (mBluetoothDevice == null) {
1376                    mBluetoothDevice = (BluetoothDevice)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
1377                }
1378                if (mBluetoothDevice != null) {
1379                    int btClass = mBluetoothDevice.getRemoteClass(address);
1380                    if (BluetoothClass.Device.Major.getDeviceMajor(btClass) == BluetoothClass.Device.Major.AUDIO_VIDEO) {
1381                        switch (BluetoothClass.Device.getDevice(btClass)) {
1382                        case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
1383                        case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
1384                            device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
1385                            break;
1386                        case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
1387                            device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
1388                            break;
1389                        default:
1390                            break;
1391                        }
1392                    }
1393                }
1394
1395                boolean isConnected = (mConnectedDevices.containsKey(device) &&
1396                                       ((String)mConnectedDevices.get(device)).equals(address));
1397
1398                if (isConnected && state != BluetoothHeadset.STATE_CONNECTED) {
1399                    AudioSystem.setDeviceConnectionState(device,
1400                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE,
1401                                                         address);
1402                    mConnectedDevices.remove(device);
1403                } else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) {
1404                    AudioSystem.setDeviceConnectionState(device,
1405                                                         AudioSystem.DEVICE_STATE_AVAILABLE,
1406                                                         address);
1407                    mConnectedDevices.put( new Integer(device), address);
1408                }
1409            } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
1410                int state = intent.getIntExtra("state", 0);
1411                if ((state & BIT_HEADSET) == 0 &&
1412                    (mHeadsetState & BIT_HEADSET) != 0) {
1413                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1414                            AudioSystem.DEVICE_STATE_UNAVAILABLE,
1415                            "");
1416                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
1417                } else if ((state & BIT_HEADSET) != 0 &&
1418                    (mHeadsetState & BIT_HEADSET) == 0)  {
1419                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1420                            AudioSystem.DEVICE_STATE_AVAILABLE,
1421                            "");
1422                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
1423                }
1424                if ((state & BIT_HEADSET_NO_MIC) == 0 &&
1425                    (mHeadsetState & BIT_HEADSET_NO_MIC) != 0) {
1426                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1427                            AudioSystem.DEVICE_STATE_UNAVAILABLE,
1428                            "");
1429                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
1430                } else if ((state & BIT_HEADSET_NO_MIC) != 0 &&
1431                    (mHeadsetState & BIT_HEADSET_NO_MIC) == 0)  {
1432                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1433                            AudioSystem.DEVICE_STATE_AVAILABLE,
1434                            "");
1435                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
1436                }
1437                if ((state & BIT_TTY) == 0 &&
1438                    (mHeadsetState & BIT_TTY) != 0) {
1439                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_TTY,
1440                            AudioSystem.DEVICE_STATE_UNAVAILABLE,
1441                            "");
1442                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_TTY);
1443                } else if ((state & BIT_TTY) != 0 &&
1444                    (mHeadsetState & BIT_TTY) == 0)  {
1445                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_TTY,
1446                            AudioSystem.DEVICE_STATE_AVAILABLE,
1447                            "");
1448                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_TTY), "");
1449                }
1450                if ((state & BIT_FM_HEADSET) == 0 &&
1451                    (mHeadsetState & BIT_FM_HEADSET) != 0) {
1452                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_HEADPHONE,
1453                            AudioSystem.DEVICE_STATE_UNAVAILABLE,
1454                            "");
1455                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_FM_HEADPHONE);
1456                } else if ((state & BIT_FM_HEADSET) != 0 &&
1457                    (mHeadsetState & BIT_FM_HEADSET) == 0)  {
1458                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_HEADPHONE,
1459                            AudioSystem.DEVICE_STATE_AVAILABLE,
1460                            "");
1461                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_FM_HEADPHONE), "");
1462                }
1463                if ((state & BIT_FM_SPEAKER) == 0 &&
1464                    (mHeadsetState & BIT_FM_SPEAKER) != 0) {
1465                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_SPEAKER,
1466                            AudioSystem.DEVICE_STATE_UNAVAILABLE,
1467                            "");
1468                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_FM_SPEAKER);
1469                } else if ((state & BIT_FM_SPEAKER) != 0 &&
1470                    (mHeadsetState & BIT_FM_SPEAKER) == 0)  {
1471                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_SPEAKER,
1472                            AudioSystem.DEVICE_STATE_AVAILABLE,
1473                            "");
1474                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_FM_SPEAKER), "");
1475                }
1476                mHeadsetState = state;
1477            }
1478        }
1479    }
1480}
1481