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