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