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