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