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