FileDocCategorySizeDatePackage
AudioService.javaAPI DocAndroid 1.5 API49483Wed May 06 22:42:00 BST 2009android.media

AudioService

public class AudioService extends IAudioService.Stub
The implementation of the volume manager service.

This implementation focuses on delivering a responsive UI. Most methods are asynchronous to external calls. For example, the task of setting a volume will update our internal state, but in a separate thread will set the system volume and later persist to the database. Similarly, setting the ringer mode will update the state and broadcast a change and in a separate thread later persist the ringer mode.

hide

Fields Summary
private static final String
TAG
private static final int
PERSIST_DELAY
How long to delay before persisting a change in volume/ringer mode.
private android.content.Context
mContext
private android.content.ContentResolver
mContentResolver
private android.view.VolumePanel
mVolumePanel
The UI
private static final int
SHARED_MSG
Used when a message should be shared across all stream types.
private static final int
SENDMSG_REPLACE
If the msg is already queued, replace it with this one.
private static final int
SENDMSG_NOOP
If the msg is already queued, ignore this one and leave the old.
private static final int
SENDMSG_QUEUE
If the msg is already queued, queue this one and leave the old.
private static final int
MSG_SET_SYSTEM_VOLUME
private static final int
MSG_PERSIST_VOLUME
private static final int
MSG_PERSIST_RINGER_MODE
private static final int
MSG_PERSIST_VIBRATE_SETTING
private static final int
MSG_MEDIA_SERVER_DIED
private static final int
MSG_MEDIA_SERVER_STARTED
private static final int
MSG_PLAY_SOUND_EFFECT
private AudioSystemThread
mAudioSystemThread
private AudioHandler
mAudioHandler
private VolumeStreamState[]
mStreamStates
private SettingsObserver
mSettingsObserver
private boolean
mMicMute
private int
mMode
private int[]
mRoutes
private Object
mSettingsLock
private boolean
mMediaServerOk
private SoundPool
mSoundPool
private Object
mSoundEffectsLock
private static final int
NUM_SOUNDPOOL_CHANNELS
private static final int
SOUND_EFFECT_VOLUME
private static final String
SOUND_EFFECTS_PATH
private static final String[]
SOUND_EFFECT_FILES
private int[]
SOUND_EFFECT_FILES_MAP
private AudioSystem.ErrorCallback
mAudioSystemCallback
private int
mRingerMode
Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL}, {@link AudioManager#RINGER_MODE_SILENT}, or {@link AudioManager#RINGER_MODE_VIBRATE}.
private int
mMuteAffectedStreams
private int
mVibrateSetting
Has multiple bits per vibrate type to indicate the type's vibrate setting. See {@link #setVibrateSetting(int, int)}.

NOTE: This is not the final decision of whether vibrate is on/off for the type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.

Constructors Summary
public AudioService(android.content.Context context)

hide


    ///////////////////////////////////////////////////////////////////////////
    // Construction
    ///////////////////////////////////////////////////////////////////////////

      
       
        mContext = context;
        mContentResolver = context.getContentResolver();
        mVolumePanel = new VolumePanel(context, this);
        mSettingsObserver = new SettingsObserver();
        
        createAudioSystemThread();
        createStreamStates();
        readPersistedSettings();
        readAudioSettings();
        mMediaServerOk = true;
        AudioSystem.setErrorCallback(mAudioSystemCallback);
        loadSoundEffects();
    
Methods Summary
public voidadjustStreamVolume(int streamType, int direction, int flags)

see
AudioManager#adjustStreamVolume(int, int, int)

        ensureValidDirection(direction);
        ensureValidStreamType(streamType);

        boolean notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
                Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1;
        if (notificationsUseRingVolume && streamType == AudioManager.STREAM_NOTIFICATION) {
            // Redirect the volume change to the ring stream
            streamType = AudioManager.STREAM_RING;
        }

        VolumeStreamState streamState = mStreamStates[streamType];
        final int oldIndex = streamState.mIndex;
        boolean adjustVolume = true;

        // If either the client forces allowing ringer modes for this adjustment,
        // or the stream type is one that is affected by ringer modes
        if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0
                || streamType == AudioManager.STREAM_RING) {
            // Check if the ringer mode changes with this volume adjustment. If
            // it does, it will handle adjusting the volume, so we won't below
            adjustVolume = checkForRingerModeChange(oldIndex, direction);
        }

        if (adjustVolume && streamState.adjustIndex(direction)) {

            boolean alsoUpdateNotificationVolume =  notificationsUseRingVolume &&
                    streamType == AudioManager.STREAM_RING;
            if (alsoUpdateNotificationVolume) {
                mStreamStates[AudioManager.STREAM_NOTIFICATION].adjustIndex(direction);
            }

            // Post message to set system volume (it in turn will post a message
            // to persist). Do not change volume if stream is muted.
            if (streamState.muteCount() == 0) {
                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
                        streamState, 0);

                if (alsoUpdateNotificationVolume) {
                    sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, AudioManager.STREAM_NOTIFICATION,
                            SENDMSG_NOOP, 0, 0, mStreamStates[AudioManager.STREAM_NOTIFICATION], 0);
                }
            }
        }

        // UI
        mVolumePanel.postVolumeChanged(streamType, flags);
        // Broadcast Intent
        sendVolumeUpdate(streamType);
    
public voidadjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags)

see
AudioManager#adjustVolume(int, int, int)


        int streamType = getActiveStreamType(suggestedStreamType);

        // Don't play sound on other streams
        if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
            flags &= ~AudioManager.FLAG_PLAY_SOUND;
        }

        adjustStreamVolume(streamType, direction, flags);
    
public voidadjustVolume(int direction, int flags)

see
AudioManager#adjustVolume(int, int)

        adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
    
private voidapplyAudioSettings()

        synchronized (mSettingsLock) {
            AudioSystem.muteMicrophone(mMicMute);
            AudioSystem.setMode(mMode);
            for (int mode = 0; mode < AudioSystem.NUM_MODES; mode++) {
                AudioSystem.setRouting(mode, mRoutes[mode], AudioSystem.ROUTE_ALL);
            }
        }
   
private voidbroadcastRingerMode()

        // Send sticky broadcast
        if (ActivityManagerNative.isSystemReady()) {
            Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
            broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
            long origCallerIdentityToken = Binder.clearCallingIdentity();
            mContext.sendStickyBroadcast(broadcast);
            Binder.restoreCallingIdentity(origCallerIdentityToken);
        }
    
private voidbroadcastVibrateSetting(int vibrateType)

        // Send broadcast
        if (ActivityManagerNative.isSystemReady()) {
            Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
            mContext.sendBroadcast(broadcast);
        }
    
booleancheckAudioSettingsPermission(java.lang.String method)

        if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
                == PackageManager.PERMISSION_GRANTED) {
            return true;
        }
        String msg = "Audio Settings Permission Denial: " + method + " from pid="
                + Binder.getCallingPid()
                + ", uid=" + Binder.getCallingUid();
        Log.w(TAG, msg);
        return false;
    
private booleancheckForRingerModeChange(int oldIndex, int direction)
Checks if the adjustment should change ringer mode instead of just adjusting volume. If so, this will set the proper ringer mode and volume indices on the stream states.

        boolean adjustVolumeIndex = true;
        int newRingerMode = mRingerMode;

        if (mRingerMode == AudioManager.RINGER_MODE_NORMAL && oldIndex == 1
                && direction == AudioManager.ADJUST_LOWER) {
            newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
        } else if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
            if (direction == AudioManager.ADJUST_RAISE) {
                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
            } else if (direction == AudioManager.ADJUST_LOWER) {
                newRingerMode = AudioManager.RINGER_MODE_SILENT;
            }
        } else if (direction == AudioManager.ADJUST_RAISE
                && mRingerMode == AudioManager.RINGER_MODE_SILENT) {
            newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
        }

        if (newRingerMode != mRingerMode) {
            setRingerMode(newRingerMode);

            /*
             * If we are changing ringer modes, do not increment/decrement the
             * volume index. Instead, the handler for the message above will
             * take care of changing the index.
             */
            adjustVolumeIndex = false;
        }

        return adjustVolumeIndex;
    
private voidcreateAudioSystemThread()

        mAudioSystemThread = new AudioSystemThread();
        mAudioSystemThread.start();
        waitForAudioHandlerCreation();
    
private voidcreateStreamStates()

        final int[] volumeLevelsPhone =
            createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_VOICE_CALL]);
        final int[] volumeLevelsCoarse =
            createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_SYSTEM]);
        final int[] volumeLevelsFine =
            createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
        final int[] volumeLevelsBtPhone =
            createVolumeLevels(0,
                    AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_BLUETOOTH_SCO]);

        int numStreamTypes = AudioSystem.getNumStreamTypes();
        VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];

        for (int i = 0; i < numStreamTypes; i++) {
            final int[] levels;

            switch (i) {

                case AudioSystem.STREAM_MUSIC:
                    levels = volumeLevelsFine;
                    break;

                case AudioSystem.STREAM_VOICE_CALL:
                    levels = volumeLevelsPhone;
                    break;

                case AudioSystem.STREAM_BLUETOOTH_SCO:
                    levels = volumeLevelsBtPhone;
                    break;

                default:
                    levels = volumeLevelsCoarse;
                    break;
            }

            if (i == AudioSystem.STREAM_BLUETOOTH_SCO) {
                streams[i] = new VolumeStreamState(AudioManager.DEFAULT_STREAM_VOLUME[i], i,levels);
            } else {
                streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[i], i, levels);
            }
        }
    
private static int[]createVolumeLevels(int offset, int numlevels)

        double curve = 1.0f; // 1.4f
        int [] volumes = new int[numlevels + offset];
        for (int i = 0; i < offset; i++) {
            volumes[i] = 0;
        }

        double val = 0;
        double max = Math.pow(numlevels - 1, curve);
        for (int i = 0; i < numlevels; i++) {
            val = Math.pow(i, curve) / max;
            volumes[offset + i] = (int) (val * 100.0f);
        }
        return volumes;
    
private voidensureValidDirection(int direction)

        if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
            throw new IllegalArgumentException("Bad direction " + direction);
        }
    
private voidensureValidStreamType(int streamType)

        if (streamType < 0 || streamType >= mStreamStates.length) {
            throw new IllegalArgumentException("Bad stream type " + streamType);
        }
    
private intgetActiveStreamType(int suggestedStreamType)

        boolean isOffhook = false;
        try {
            ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
            if (phone != null) isOffhook = phone.isOffhook();
        } catch (RemoteException e) {
            Log.w(TAG, "Couldn't connect to phone service", e);
        }

        if ((getRouting(AudioSystem.MODE_IN_CALL) & AudioSystem.ROUTE_BLUETOOTH_SCO) != 0) {
            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
            return AudioSystem.STREAM_BLUETOOTH_SCO;
        } else if (isOffhook) {
            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
            return AudioSystem.STREAM_VOICE_CALL;
        } else if (AudioSystem.isMusicActive()) {
            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
            return AudioSystem.STREAM_MUSIC;
        } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING...");
            return AudioSystem.STREAM_RING;
        } else {
            // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
            return suggestedStreamType;
        }
    
public intgetMode()

see
AudioManager#getMode()

        return mMode;
    
private static intgetMsg(int baseMsg, int streamType)

        return (baseMsg & 0xffff) | streamType << 16;
    
private static intgetMsgBase(int msg)

        return msg & 0xffff;
    
public intgetRingerMode()

see
AudioManager#getRingerMode()

        return mRingerMode;
    
public intgetRouting(int mode)

see
AudioManager#getRouting(int)

        return mRoutes[mode];
    
public intgetStreamMaxVolume(int streamType)

see
AudioManager#getStreamMaxVolume(int)

        ensureValidStreamType(streamType);
        return mStreamStates[streamType].getMaxIndex();
    
public intgetStreamVolume(int streamType)

see
AudioManager#getStreamVolume(int)

        ensureValidStreamType(streamType);
        return mStreamStates[streamType].mIndex;
    
public static intgetValueForVibrateSetting(int existingValue, int vibrateType, int vibrateSetting)

see
#setVibrateSetting(int, int)


        // First clear the existing setting. Each vibrate type has two bits in
        // the value. Note '3' is '11' in binary.
        existingValue &= ~(3 << (vibrateType * 2));

        // Set into the old value
        existingValue |= (vibrateSetting & 3) << (vibrateType * 2);

        return existingValue;
    
public intgetVibrateSetting(int vibrateType)

see
AudioManager#getVibrateSetting(int)

        return (mVibrateSetting >> (vibrateType * 2)) & 3;
    
public booleanisMicrophoneMute()

see
AudioManager#isMicrophoneMute()

        return mMicMute;
    
public booleanisMusicActive()

see
AudioManager#isMusicActive()

        return AudioSystem.isMusicActive();
    
public booleanisStreamAffectedByMute(int streamType)

        return (mMuteAffectedStreams & (1 << streamType)) != 0;
    
public booleanisStreamAffectedByRingerMode(int streamType)

        int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
                Settings.System.MODE_RINGER_STREAMS_AFFECTED, 0);
        return (ringerModeAffectedStreams & (1 << streamType)) != 0;
    
public booleanloadSoundEffects()
Loads samples into the soundpool. This method must be called at when sound effects are enabled

        synchronized (mSoundEffectsLock) {
            mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
            if (mSoundPool == null) {
                return false;
            }
            /*
             * poolId table: The value -1 in this table indicates that corresponding
             * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
             * Once loaded, the value in poolId is the sample ID and the same
             * sample can be reused for another effect using the same file.
             */
            int[] poolId = new int[SOUND_EFFECT_FILES.length];
            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
                poolId[fileIdx] = -1;
            }
            /*
             * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
             * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
             * this indicates we have a valid sample loaded for this effect.
             */
            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
                // Do not load sample if this effect uses the MediaPlayer
                if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
                    continue;
                }
                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
                    String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
                    int sampleId = mSoundPool.load(filePath, 0);
                    SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
                    if (sampleId <= 0) {
                        Log.w(TAG, "Soundpool could not load file: "+filePath);
                    }
                } else {
                    SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
                }
            }
        }

        return true;
    
public voidplaySoundEffect(int effectType)

see
AudioManager#playSoundEffect(int)

        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
                effectType, SOUND_EFFECT_VOLUME, null, 0);
    
public voidplaySoundEffectVolume(int effectType, float volume)

see
AudioManager#playSoundEffect(int, float)

        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
                effectType, (int) (volume * 1000), null, 0);
    
private voidreadAudioSettings()

        synchronized (mSettingsLock) {
            mMicMute = AudioSystem.isMicrophoneMuted();
            mMode = AudioSystem.getMode();
            for (int mode = 0; mode < AudioSystem.NUM_MODES; mode++) {
                mRoutes[mode] = AudioSystem.getRouting(mode);
            }
        }
    
private voidreadPersistedSettings()

        final ContentResolver cr = mContentResolver;

        mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);

        mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);

        mMuteAffectedStreams = System.getInt(cr,
                System.MUTE_STREAMS_AFFECTED,
                ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));

        // Each stream will read its own persisted settings

        // Broadcast the sticky intent
        broadcastRingerMode();

        // Broadcast vibrate settings
        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
    
private static voidsendMsg(android.os.Handler handler, int baseMsg, int streamType, int existingMsgPolicy, int arg1, int arg2, java.lang.Object obj, int delay)

        int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);

        if (existingMsgPolicy == SENDMSG_REPLACE) {
            handler.removeMessages(msg);
        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
            return;
        }

        handler
                .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
    
private voidsendVolumeUpdate(int streamType)

        Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, getStreamVolume(streamType));

        // Currently, sending the intent only when the stream is BLUETOOTH_SCO
        if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
            mContext.sendBroadcast(intent);
        }
    
public voidsetMicrophoneMute(boolean on)

see
AudioManager#setMicrophoneMute(boolean)

        if (!checkAudioSettingsPermission("setMicrophoneMute()")) {
            return;
        }
        synchronized (mSettingsLock) {
            if (on != mMicMute) {
                AudioSystem.muteMicrophone(on);
                mMicMute = on;
            }
        }
    
public voidsetMode(int mode)

see
AudioManager#setMode(int)

        if (!checkAudioSettingsPermission("setMode()")) {
            return;
        }
        synchronized (mSettingsLock) {
            if (mode != mMode) {
                AudioSystem.setMode(mode);
                mMode = mode;
            }
            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
            int index = mStreamStates[streamType].mIndex;
            syncRingerAndNotificationStreamVolume(streamType, index, true);
            setStreamVolumeInt(streamType, index, true);
        }
    
public voidsetParameter(java.lang.String key, java.lang.String value)

see
AudioManager#setParameter(String, String)

        AudioSystem.setParameter(key, value);
    
public voidsetRingerMode(int ringerMode)

see
AudioManager#setRingerMode(int)

        if (ringerMode != mRingerMode) {
            setRingerModeInt(ringerMode);

            // Send sticky broadcast
            broadcastRingerMode();
        }
    
private voidsetRingerModeInt(int ringerMode)

        mRingerMode = ringerMode;

        // Adjust volumes via posting message
        int numStreamTypes = AudioSystem.getNumStreamTypes();
        if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
                if (!isStreamAffectedByRingerMode(streamType)) continue;
                // Bring back last audible volume
                setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
                                   false);
            }
        } else {
            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
                if (!isStreamAffectedByRingerMode(streamType)) continue;
                // Either silent or vibrate, either way volume is 0
                setStreamVolumeInt(streamType, 0, false);
            }
        }
        
        // Post a persist ringer mode msg
        sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
                SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
    
public voidsetRouting(int mode, int routes, int mask)

see
AudioManager#setRouting(int, int, int)

        if (!checkAudioSettingsPermission("setRouting()")) {
            return;
        }
        synchronized (mSettingsLock) {
            if ((mRoutes[mode] & mask) != (routes & mask)) {
                AudioSystem.setRouting(mode, routes, mask);
                mRoutes[mode] = (mRoutes[mode] & ~mask) | (routes & mask);
            }
            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
            int index = mStreamStates[streamType].mIndex;
            syncRingerAndNotificationStreamVolume(streamType, index, true);
            setStreamVolumeInt(streamType, index, true);
        }
    
public voidsetStreamMute(int streamType, boolean state, android.os.IBinder cb)

see
AudioManager#setStreamMute(int, boolean)

        if (isStreamAffectedByMute(streamType)) {
            mStreamStates[streamType].mute(cb, state);
        }
    
public voidsetStreamSolo(int streamType, boolean state, android.os.IBinder cb)

see
AudioManager#setStreamSolo(int, boolean)

        for (int stream = 0; stream < mStreamStates.length; stream++) {
            if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
            // Bring back last audible volume
            mStreamStates[stream].mute(cb, state);
         }
    
public voidsetStreamVolume(int streamType, int index, int flags)

see
AudioManager#setStreamVolume(int, int, int)

        ensureValidStreamType(streamType);
        syncRingerAndNotificationStreamVolume(streamType, index, false);

        setStreamVolumeInt(streamType, index, false);

        // UI, etc.
        mVolumePanel.postVolumeChanged(streamType, flags);
        // Broadcast Intent
        sendVolumeUpdate(streamType);
    
private voidsetStreamVolumeInt(int streamType, int index, boolean force)
Sets the stream state's index, and posts a message to set system volume. This will not call out to the UI. Assumes a valid stream type.

param
streamType Type of the stream
param
index Desired volume index of the stream
param
force If true, set the volume even if the desired volume is same as the current volume.

        VolumeStreamState streamState = mStreamStates[streamType];
        if (streamState.setIndex(index) || force) {
            // Post message to set system volume (it in turn will post a message
            // to persist). Do not change volume if stream is muted.
            if (streamState.muteCount() == 0) {
                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
                        streamState, 0);
            }
        }
    
public voidsetVibrateSetting(int vibrateType, int vibrateSetting)

see
AudioManager#setVibrateSetting(int, int)


        mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);

        // Broadcast change
        broadcastVibrateSetting(vibrateType);

        // Post message to set ringer mode (it in turn will post a message
        // to persist)
        sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
                null, 0);
    
public booleanshouldVibrate(int vibrateType)

see
AudioManager#shouldVibrate(int)


        switch (getVibrateSetting(vibrateType)) {

            case AudioManager.VIBRATE_SETTING_ON:
                return mRingerMode != AudioManager.RINGER_MODE_SILENT;

            case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
                return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;

            case AudioManager.VIBRATE_SETTING_OFF:
                // Phone ringer should always vibrate in vibrate mode
                if (vibrateType == AudioManager.VIBRATE_TYPE_RINGER) {
                    return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
                }

            default:
                return false;
        }
    
private voidsyncRingerAndNotificationStreamVolume(int streamType, int index, boolean force)
Sync the STREAM_RING and STREAM_NOTIFICATION volumes if mandated by the value in Settings.

param
streamType Type of the stream
param
index Volume index for the stream
param
force If true, set the volume even if the current and desired volume as same

        boolean notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
                Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1;
        if (notificationsUseRingVolume) {
            if (streamType == AudioManager.STREAM_NOTIFICATION) {
                // Redirect the volume change to the ring stream
                streamType = AudioManager.STREAM_RING;
            }
            if (streamType == AudioManager.STREAM_RING) {
                // One-off to sync notification volume to ringer volume
                setStreamVolumeInt(AudioManager.STREAM_NOTIFICATION, index, force);
            }
        }
    
public voidunloadSoundEffects()
Unloads samples from the sound pool. This method can be called to free some memory when sound effects are disabled.

        synchronized (mSoundEffectsLock) {
            if (mSoundPool == null) {
                return;
            }
            int[] poolId = new int[SOUND_EFFECT_FILES.length];
            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
                poolId[fileIdx] = 0;
            }

            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
                if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
                    continue;
                }
                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
                    mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
                    SOUND_EFFECT_FILES_MAP[effect][1] = -1;
                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
                }
            }
            mSoundPool = null;
        }
    
private voidwaitForAudioHandlerCreation()
Waits for the volume handler to be created by the other thread.

        synchronized(this) {
            while (mAudioHandler == null) {
                try {
                    // Wait for mAudioHandler to be set by the other thread
                    wait();
                } catch (InterruptedException e) {
                    Log.e(TAG, "Interrupted while waiting on volume handler.");
                }
            }
        }