AudioService.java revision e5e1e870fdb35f5291790e6d178bde7126f6fe35
1/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.media;
18
19import android.app.ActivityManagerNative;
20import android.content.BroadcastReceiver;
21import android.content.ComponentName;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.content.IntentFilter;
26import android.bluetooth.BluetoothA2dp;
27import android.bluetooth.BluetoothClass;
28import android.bluetooth.BluetoothDevice;
29import android.bluetooth.BluetoothHeadset;
30import android.content.pm.PackageManager;
31import android.database.ContentObserver;
32import android.media.MediaPlayer.OnCompletionListener;
33import android.media.MediaPlayer.OnErrorListener;
34import android.os.Binder;
35import android.os.Environment;
36import android.os.Handler;
37import android.os.IBinder;
38import android.os.Looper;
39import android.os.Message;
40import android.os.RemoteException;
41import android.os.ServiceManager;
42import android.provider.Settings;
43import android.provider.Settings.System;
44import android.util.Log;
45import android.view.KeyEvent;
46import android.view.VolumePanel;
47import android.os.SystemProperties;
48
49import com.android.internal.telephony.ITelephony;
50
51import java.io.FileDescriptor;
52import java.io.IOException;
53import java.io.PrintWriter;
54import java.util.ArrayList;
55import java.util.HashMap;
56import java.util.Iterator;
57import java.util.Map;
58import java.util.Set;
59import java.util.Stack;
60
61/**
62 * The implementation of the volume manager service.
63 * <p>
64 * This implementation focuses on delivering a responsive UI. Most methods are
65 * asynchronous to external calls. For example, the task of setting a volume
66 * will update our internal state, but in a separate thread will set the system
67 * volume and later persist to the database. Similarly, setting the ringer mode
68 * will update the state and broadcast a change and in a separate thread later
69 * persist the ringer mode.
70 *
71 * @hide
72 */
73public class AudioService extends IAudioService.Stub {
74
75    private static final String TAG = "AudioService";
76
77    /** How long to delay before persisting a change in volume/ringer mode. */
78    private static final int PERSIST_DELAY = 3000;
79
80    private Context mContext;
81    private ContentResolver mContentResolver;
82
83
84    /** The UI */
85    private VolumePanel mVolumePanel;
86
87    // sendMsg() flags
88    /** Used when a message should be shared across all stream types. */
89    private static final int SHARED_MSG = -1;
90    /** If the msg is already queued, replace it with this one. */
91    private static final int SENDMSG_REPLACE = 0;
92    /** If the msg is already queued, ignore this one and leave the old. */
93    private static final int SENDMSG_NOOP = 1;
94    /** If the msg is already queued, queue this one and leave the old. */
95    private static final int SENDMSG_QUEUE = 2;
96
97    // AudioHandler message.whats
98    private static final int MSG_SET_SYSTEM_VOLUME = 0;
99    private static final int MSG_PERSIST_VOLUME = 1;
100    private static final int MSG_PERSIST_RINGER_MODE = 3;
101    private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
102    private static final int MSG_MEDIA_SERVER_DIED = 5;
103    private static final int MSG_MEDIA_SERVER_STARTED = 6;
104    private static final int MSG_PLAY_SOUND_EFFECT = 7;
105
106    /** @see AudioSystemThread */
107    private AudioSystemThread mAudioSystemThread;
108    /** @see AudioHandler */
109    private AudioHandler mAudioHandler;
110    /** @see VolumeStreamState */
111    private VolumeStreamState[] mStreamStates;
112    private SettingsObserver mSettingsObserver;
113
114    private int mMode;
115    private Object mSettingsLock = new Object();
116    private boolean mMediaServerOk;
117
118    private SoundPool mSoundPool;
119    private Object mSoundEffectsLock = new Object();
120    private static final int NUM_SOUNDPOOL_CHANNELS = 4;
121    private static final int SOUND_EFFECT_VOLUME = 1000;
122
123    /* Sound effect file names  */
124    private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
125    private static final String[] SOUND_EFFECT_FILES = new String[] {
126        "Effect_Tick.ogg",
127        "KeypressStandard.ogg",
128        "KeypressSpacebar.ogg",
129        "KeypressDelete.ogg",
130        "KeypressReturn.ogg"
131    };
132
133    /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
134     * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
135     * uses soundpool (second column) */
136    private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
137        {0, -1},  // FX_KEY_CLICK
138        {0, -1},  // FX_FOCUS_NAVIGATION_UP
139        {0, -1},  // FX_FOCUS_NAVIGATION_DOWN
140        {0, -1},  // FX_FOCUS_NAVIGATION_LEFT
141        {0, -1},  // FX_FOCUS_NAVIGATION_RIGHT
142        {1, -1},  // FX_KEYPRESS_STANDARD
143        {2, -1},  // FX_KEYPRESS_SPACEBAR
144        {3, -1},  // FX_FOCUS_DELETE
145        {4, -1}   // FX_FOCUS_RETURN
146    };
147
148   /** @hide Maximum volume index values for audio streams */
149    private int[] MAX_STREAM_VOLUME = new int[] {
150        5,  // STREAM_VOICE_CALL
151        7,  // STREAM_SYSTEM
152        7,  // STREAM_RING
153        15, // STREAM_MUSIC
154        7,  // STREAM_ALARM
155        7,  // STREAM_NOTIFICATION
156        15, // STREAM_BLUETOOTH_SCO
157        7,  // STREAM_SYSTEM_ENFORCED
158        15, // STREAM_DTMF
159        15  // STREAM_TTS
160    };
161    /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
162     * of another stream: This avoids multiplying the volume settings for hidden
163     * stream types that follow other stream behavior for volume settings
164     * NOTE: do not create loops in aliases! */
165    private int[] STREAM_VOLUME_ALIAS = new int[] {
166        AudioSystem.STREAM_VOICE_CALL,  // STREAM_VOICE_CALL
167        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM
168        AudioSystem.STREAM_RING,  // STREAM_RING
169        AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
170        AudioSystem.STREAM_ALARM,  // STREAM_ALARM
171        AudioSystem.STREAM_NOTIFICATION,  // STREAM_NOTIFICATION
172        AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
173        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM_ENFORCED
174        AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
175        AudioSystem.STREAM_MUSIC  // STREAM_TTS
176    };
177
178    private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
179        public void onError(int error) {
180            switch (error) {
181            case AudioSystem.AUDIO_STATUS_SERVER_DIED:
182                if (mMediaServerOk) {
183                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
184                            null, 1500);
185                    mMediaServerOk = false;
186                }
187                break;
188            case AudioSystem.AUDIO_STATUS_OK:
189                if (!mMediaServerOk) {
190                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
191                            null, 0);
192                    mMediaServerOk = true;
193                }
194                break;
195            default:
196                break;
197            }
198       }
199    };
200
201    /**
202     * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
203     * {@link AudioManager#RINGER_MODE_SILENT}, or
204     * {@link AudioManager#RINGER_MODE_VIBRATE}.
205     */
206    private int mRingerMode;
207
208    /** @see System#MODE_RINGER_STREAMS_AFFECTED */
209    private int mRingerModeAffectedStreams;
210
211    /** @see System#MUTE_STREAMS_AFFECTED */
212    private int mMuteAffectedStreams;
213
214    /**
215     * Has multiple bits per vibrate type to indicate the type's vibrate
216     * setting. See {@link #setVibrateSetting(int, int)}.
217     * <p>
218     * NOTE: This is not the final decision of whether vibrate is on/off for the
219     * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
220     */
221    private int mVibrateSetting;
222
223    /** @see System#NOTIFICATIONS_USE_RING_VOLUME */
224    private int mNotificationsUseRingVolume;
225
226    // Broadcast receiver for device connections intent broadcasts
227    private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
228
229    //  Broadcast receiver for media button broadcasts (separate from mReceiver to
230    //  independently change its priority)
231    private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
232
233    // Devices currently connected
234    private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
235
236    // Forced device usage for communications
237    private int mForcedUseForComm;
238
239    // List of binder death handlers for setMode() client processes.
240    // The last process to have called setMode() is at the top of the list.
241    private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
242
243    // List of clients having issued a SCO start request
244    private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
245
246    // BluetoothHeadset API to control SCO connection
247    private BluetoothHeadset mBluetoothHeadset;
248
249    // Bluetooth headset connection state
250    private boolean mBluetoothHeadsetConnected;
251
252    ///////////////////////////////////////////////////////////////////////////
253    // Construction
254    ///////////////////////////////////////////////////////////////////////////
255
256    /** @hide */
257    public AudioService(Context context) {
258        mContext = context;
259        mContentResolver = context.getContentResolver();
260
261       // Intialized volume
262        MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
263            "ro.config.vc_call_vol_steps",
264           MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
265
266        mVolumePanel = new VolumePanel(context, this);
267        mSettingsObserver = new SettingsObserver();
268        mForcedUseForComm = AudioSystem.FORCE_NONE;
269        createAudioSystemThread();
270        readPersistedSettings();
271        createStreamStates();
272        // Call setMode() to initialize mSetModeDeathHandlers
273        mMode = AudioSystem.MODE_INVALID;
274        setMode(AudioSystem.MODE_NORMAL, null);
275        mMediaServerOk = true;
276        AudioSystem.setErrorCallback(mAudioSystemCallback);
277        loadSoundEffects();
278
279        mBluetoothHeadsetConnected = false;
280        mBluetoothHeadset = new BluetoothHeadset(context,
281                                                 mBluetoothHeadsetServiceListener);
282
283        // Register for device connection intent broadcasts.
284        IntentFilter intentFilter =
285                new IntentFilter(Intent.ACTION_HEADSET_PLUG);
286        intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
287        intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
288        intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
289        intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
290        context.registerReceiver(mReceiver, intentFilter);
291
292        // Register for media button intent broadcasts.
293        intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
294        intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
295        context.registerReceiver(mMediaButtonReceiver, intentFilter);
296    }
297
298    private void createAudioSystemThread() {
299        mAudioSystemThread = new AudioSystemThread();
300        mAudioSystemThread.start();
301        waitForAudioHandlerCreation();
302    }
303
304    /** Waits for the volume handler to be created by the other thread. */
305    private void waitForAudioHandlerCreation() {
306        synchronized(this) {
307            while (mAudioHandler == null) {
308                try {
309                    // Wait for mAudioHandler to be set by the other thread
310                    wait();
311                } catch (InterruptedException e) {
312                    Log.e(TAG, "Interrupted while waiting on volume handler.");
313                }
314            }
315        }
316    }
317
318    private void createStreamStates() {
319        int numStreamTypes = AudioSystem.getNumStreamTypes();
320        VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
321
322        for (int i = 0; i < numStreamTypes; i++) {
323            streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
324        }
325
326        // Correct stream index values for streams with aliases
327        for (int i = 0; i < numStreamTypes; i++) {
328            if (STREAM_VOLUME_ALIAS[i] != i) {
329                int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
330                streams[i].mIndex = streams[i].getValidIndex(index);
331                setStreamVolumeIndex(i, index);
332                index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
333                streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
334            }
335        }
336    }
337
338    private void readPersistedSettings() {
339        final ContentResolver cr = mContentResolver;
340
341        mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
342
343        mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
344
345        mRingerModeAffectedStreams = Settings.System.getInt(cr,
346                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
347                ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
348                 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
349
350        mMuteAffectedStreams = System.getInt(cr,
351                System.MUTE_STREAMS_AFFECTED,
352                ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
353
354        mNotificationsUseRingVolume = System.getInt(cr,
355                Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1);
356
357        if (mNotificationsUseRingVolume == 1) {
358            STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
359        }
360        // Each stream will read its own persisted settings
361
362        // Broadcast the sticky intent
363        broadcastRingerMode();
364
365        // Broadcast vibrate settings
366        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
367        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
368    }
369
370    private void setStreamVolumeIndex(int stream, int index) {
371        AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
372    }
373
374    private int rescaleIndex(int index, int srcStream, int dstStream) {
375        return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
376    }
377
378    ///////////////////////////////////////////////////////////////////////////
379    // IPC methods
380    ///////////////////////////////////////////////////////////////////////////
381
382    /** @see AudioManager#adjustVolume(int, int) */
383    public void adjustVolume(int direction, int flags) {
384        adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
385    }
386
387    /** @see AudioManager#adjustVolume(int, int, int) */
388    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
389
390        int streamType = getActiveStreamType(suggestedStreamType);
391
392        // Don't play sound on other streams
393        if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
394            flags &= ~AudioManager.FLAG_PLAY_SOUND;
395        }
396
397        adjustStreamVolume(streamType, direction, flags);
398    }
399
400    /** @see AudioManager#adjustStreamVolume(int, int, int) */
401    public void adjustStreamVolume(int streamType, int direction, int flags) {
402        ensureValidDirection(direction);
403        ensureValidStreamType(streamType);
404
405
406        VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
407        final int oldIndex = streamState.mIndex;
408        boolean adjustVolume = true;
409
410        // If either the client forces allowing ringer modes for this adjustment,
411        // or the stream type is one that is affected by ringer modes
412        if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0
413                || streamType == AudioSystem.STREAM_RING) {
414            // Check if the ringer mode changes with this volume adjustment. If
415            // it does, it will handle adjusting the volume, so we won't below
416            adjustVolume = checkForRingerModeChange(oldIndex, direction);
417        }
418
419        if (adjustVolume && streamState.adjustIndex(direction)) {
420            // Post message to set system volume (it in turn will post a message
421            // to persist). Do not change volume if stream is muted.
422            if (streamState.muteCount() == 0) {
423                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,
424                        streamState, 0);
425            }
426        }
427
428        // UI
429        mVolumePanel.postVolumeChanged(streamType, flags);
430        // Broadcast Intent
431        sendVolumeUpdate(streamType, oldIndex, streamState.mIndex);
432    }
433
434    /** @see AudioManager#setStreamVolume(int, int, int) */
435    public void setStreamVolume(int streamType, int index, int flags) {
436        ensureValidStreamType(streamType);
437
438        final int oldIndex = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
439
440        index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
441        setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
442
443        // UI, etc.
444        mVolumePanel.postVolumeChanged(streamType, flags);
445        // Broadcast Intent
446        sendVolumeUpdate(streamType, oldIndex, index);
447    }
448
449    private void sendVolumeUpdate(int streamType, int oldIndex, int index) {
450        oldIndex = (oldIndex + 5) / 10;
451        index = (index + 5) / 10;
452
453        Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
454        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
455        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
456        intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
457
458        mContext.sendBroadcast(intent);
459    }
460
461    /**
462     * Sets the stream state's index, and posts a message to set system volume.
463     * This will not call out to the UI. Assumes a valid stream type.
464     *
465     * @param streamType Type of the stream
466     * @param index Desired volume index of the stream
467     * @param force If true, set the volume even if the desired volume is same
468     * as the current volume.
469     * @param lastAudible If true, stores new index as last audible one
470     */
471    private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
472        VolumeStreamState streamState = mStreamStates[streamType];
473        if (streamState.setIndex(index, lastAudible) || force) {
474            // Post message to set system volume (it in turn will post a message
475            // to persist).
476            // If stream is muted or we are in silent mode and stream is affected by ringer mode
477            // and the new volume is not 0, just persist the new volume but do not change
478            // current value
479            if (streamState.muteCount() == 0 &&
480                (mRingerMode == AudioManager.RINGER_MODE_NORMAL ||
481                !isStreamAffectedByRingerMode(streamType) ||
482                index == 0)) {
483                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
484                        streamState, 0);
485            } else {
486                // Post a persist volume msg
487                sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
488                        SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
489            }
490        }
491    }
492
493    /** @see AudioManager#setStreamSolo(int, boolean) */
494    public void setStreamSolo(int streamType, boolean state, IBinder cb) {
495        for (int stream = 0; stream < mStreamStates.length; stream++) {
496            if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
497            // Bring back last audible volume
498            mStreamStates[stream].mute(cb, state);
499         }
500    }
501
502    /** @see AudioManager#setStreamMute(int, boolean) */
503    public void setStreamMute(int streamType, boolean state, IBinder cb) {
504        if (isStreamAffectedByMute(streamType)) {
505            mStreamStates[streamType].mute(cb, state);
506        }
507    }
508
509    /** @see AudioManager#getStreamVolume(int) */
510    public int getStreamVolume(int streamType) {
511        ensureValidStreamType(streamType);
512        return (mStreamStates[streamType].mIndex + 5) / 10;
513    }
514
515    /** @see AudioManager#getStreamMaxVolume(int) */
516    public int getStreamMaxVolume(int streamType) {
517        ensureValidStreamType(streamType);
518        return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
519    }
520
521    /** @see AudioManager#getRingerMode() */
522    public int getRingerMode() {
523        return mRingerMode;
524    }
525
526    /** @see AudioManager#setRingerMode(int) */
527    public void setRingerMode(int ringerMode) {
528        synchronized (mSettingsLock) {
529            if (ringerMode != mRingerMode) {
530                setRingerModeInt(ringerMode, true);
531                // Send sticky broadcast
532                broadcastRingerMode();
533            }
534        }
535    }
536
537    private void setRingerModeInt(int ringerMode, boolean persist) {
538        mRingerMode = ringerMode;
539
540        // Adjust volumes via posting message
541        int numStreamTypes = AudioSystem.getNumStreamTypes();
542        if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
543            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
544                if (!isStreamAffectedByRingerMode(streamType)) continue;
545                // Bring back last audible volume
546                setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
547                                   true, false);
548            }
549        } else {
550            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
551                if (isStreamAffectedByRingerMode(streamType)) {
552                    // Either silent or vibrate, either way volume is 0
553                    setStreamVolumeInt(streamType, 0, false, false);
554                } else {
555                    // restore stream volume in the case the stream changed from affected
556                    // to non affected by ringer mode. Does not arm to do it for streams that
557                    // are not affected as well.
558                    setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
559                            true, false);
560                }
561            }
562        }
563
564        // Post a persist ringer mode msg
565        if (persist) {
566            sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
567                    SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
568        }
569    }
570
571    /** @see AudioManager#shouldVibrate(int) */
572    public boolean shouldVibrate(int vibrateType) {
573
574        switch (getVibrateSetting(vibrateType)) {
575
576            case AudioManager.VIBRATE_SETTING_ON:
577                return mRingerMode != AudioManager.RINGER_MODE_SILENT;
578
579            case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
580                return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
581
582            case AudioManager.VIBRATE_SETTING_OFF:
583                // Phone ringer should always vibrate in vibrate mode
584                if (vibrateType == AudioManager.VIBRATE_TYPE_RINGER) {
585                    return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
586                }
587
588            default:
589                return false;
590        }
591    }
592
593    /** @see AudioManager#getVibrateSetting(int) */
594    public int getVibrateSetting(int vibrateType) {
595        return (mVibrateSetting >> (vibrateType * 2)) & 3;
596    }
597
598    /** @see AudioManager#setVibrateSetting(int, int) */
599    public void setVibrateSetting(int vibrateType, int vibrateSetting) {
600
601        mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
602
603        // Broadcast change
604        broadcastVibrateSetting(vibrateType);
605
606        // Post message to set ringer mode (it in turn will post a message
607        // to persist)
608        sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
609                null, 0);
610    }
611
612    /**
613     * @see #setVibrateSetting(int, int)
614     */
615    public static int getValueForVibrateSetting(int existingValue, int vibrateType,
616            int vibrateSetting) {
617
618        // First clear the existing setting. Each vibrate type has two bits in
619        // the value. Note '3' is '11' in binary.
620        existingValue &= ~(3 << (vibrateType * 2));
621
622        // Set into the old value
623        existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
624
625        return existingValue;
626    }
627
628    private class SetModeDeathHandler implements IBinder.DeathRecipient {
629        private IBinder mCb; // To be notified of client's death
630        private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
631
632        SetModeDeathHandler(IBinder cb) {
633            mCb = cb;
634        }
635
636        public void binderDied() {
637            synchronized(mSetModeDeathHandlers) {
638                Log.w(TAG, "setMode() client died");
639                int index = mSetModeDeathHandlers.indexOf(this);
640                if (index < 0) {
641                    Log.w(TAG, "unregistered setMode() client died");
642                } else {
643                    mSetModeDeathHandlers.remove(this);
644                    // If dead client was a the top of client list,
645                    // apply next mode in the stack
646                    if (index == 0) {
647                        // mSetModeDeathHandlers is never empty as the initial entry
648                        // created when AudioService starts is never removed
649                        SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0);
650                        int mode = hdlr.getMode();
651                        if (AudioService.this.mMode != mode) {
652                            if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
653                                AudioService.this.mMode = mode;
654                            }
655                        }
656                    }
657                }
658            }
659        }
660
661        public void setMode(int mode) {
662            mMode = mode;
663        }
664
665        public int getMode() {
666            return mMode;
667        }
668
669        public IBinder getBinder() {
670            return mCb;
671        }
672    }
673
674    /** @see AudioManager#setMode(int) */
675    public void setMode(int mode, IBinder cb) {
676        if (!checkAudioSettingsPermission("setMode()")) {
677            return;
678        }
679
680        if (mode < AudioSystem.MODE_CURRENT || mode > AudioSystem.MODE_IN_CALL) {
681            return;
682        }
683
684        synchronized (mSettingsLock) {
685            if (mode == AudioSystem.MODE_CURRENT) {
686                mode = mMode;
687            }
688            if (mode != mMode) {
689                if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
690                    checkForUndispatchedAudioFocusChange(mMode, mode);
691                    mMode = mode;
692
693                    synchronized(mSetModeDeathHandlers) {
694                        SetModeDeathHandler hdlr = null;
695                        Iterator iter = mSetModeDeathHandlers.iterator();
696                        while (iter.hasNext()) {
697                            SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
698                            if (h.getBinder() == cb) {
699                                hdlr = h;
700                                // Remove from client list so that it is re-inserted at top of list
701                                iter.remove();
702                                break;
703                            }
704                        }
705                        if (hdlr == null) {
706                            hdlr = new SetModeDeathHandler(cb);
707                            // cb is null when setMode() is called by AudioService constructor
708                            if (cb != null) {
709                                // Register for client death notification
710                                try {
711                                    cb.linkToDeath(hdlr, 0);
712                                } catch (RemoteException e) {
713                                    // Client has died!
714                                    Log.w(TAG, "setMode() could not link to "+cb+" binder death");
715                                }
716                            }
717                        }
718                        // Last client to call setMode() is always at top of client list
719                        // as required by SetModeDeathHandler.binderDied()
720                        mSetModeDeathHandlers.add(0, hdlr);
721                        hdlr.setMode(mode);
722                    }
723
724                    if (mode != AudioSystem.MODE_NORMAL) {
725                        clearAllScoClients();
726                    }
727                }
728            }
729            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
730            int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
731            setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, true);
732        }
733    }
734
735    /** @see AudioManager#getMode() */
736    public int getMode() {
737        return mMode;
738    }
739
740    /** @see AudioManager#playSoundEffect(int) */
741    public void playSoundEffect(int effectType) {
742        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
743                effectType, -1, null, 0);
744    }
745
746    /** @see AudioManager#playSoundEffect(int, float) */
747    public void playSoundEffectVolume(int effectType, float volume) {
748        loadSoundEffects();
749        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
750                effectType, (int) (volume * 1000), null, 0);
751    }
752
753    /**
754     * Loads samples into the soundpool.
755     * This method must be called at when sound effects are enabled
756     */
757    public boolean loadSoundEffects() {
758        synchronized (mSoundEffectsLock) {
759            if (mSoundPool != null) {
760                return true;
761            }
762            mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
763            if (mSoundPool == null) {
764                return false;
765            }
766            /*
767             * poolId table: The value -1 in this table indicates that corresponding
768             * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
769             * Once loaded, the value in poolId is the sample ID and the same
770             * sample can be reused for another effect using the same file.
771             */
772            int[] poolId = new int[SOUND_EFFECT_FILES.length];
773            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
774                poolId[fileIdx] = -1;
775            }
776            /*
777             * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
778             * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
779             * this indicates we have a valid sample loaded for this effect.
780             */
781            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
782                // Do not load sample if this effect uses the MediaPlayer
783                if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
784                    continue;
785                }
786                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
787                    String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
788                    int sampleId = mSoundPool.load(filePath, 0);
789                    SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
790                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
791                    if (sampleId <= 0) {
792                        Log.w(TAG, "Soundpool could not load file: "+filePath);
793                    }
794                } else {
795                    SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
796                }
797            }
798        }
799
800        return true;
801    }
802
803    /**
804     *  Unloads samples from the sound pool.
805     *  This method can be called to free some memory when
806     *  sound effects are disabled.
807     */
808    public void unloadSoundEffects() {
809        synchronized (mSoundEffectsLock) {
810            if (mSoundPool == null) {
811                return;
812            }
813            int[] poolId = new int[SOUND_EFFECT_FILES.length];
814            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
815                poolId[fileIdx] = 0;
816            }
817
818            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
819                if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
820                    continue;
821                }
822                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
823                    mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
824                    SOUND_EFFECT_FILES_MAP[effect][1] = -1;
825                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
826                }
827            }
828            mSoundPool = null;
829        }
830    }
831
832    /** @see AudioManager#reloadAudioSettings() */
833    public void reloadAudioSettings() {
834        // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
835        readPersistedSettings();
836
837        // restore volume settings
838        int numStreamTypes = AudioSystem.getNumStreamTypes();
839        for (int streamType = 0; streamType < numStreamTypes; streamType++) {
840            VolumeStreamState streamState = mStreamStates[streamType];
841
842            String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
843            String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
844            int index = Settings.System.getInt(mContentResolver,
845                                           settingName,
846                                           AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
847            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
848                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
849            } else {
850                index *= 10;
851            }
852            streamState.mIndex = streamState.getValidIndex(index);
853
854            index = (index + 5) / 10;
855            index = Settings.System.getInt(mContentResolver,
856                                            lastAudibleSettingName,
857                                            (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
858            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
859                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
860            } else {
861                index *= 10;
862            }
863            streamState.mLastAudibleIndex = streamState.getValidIndex(index);
864
865            // unmute stream that whas muted but is not affect by mute anymore
866            if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
867                int size = streamState.mDeathHandlers.size();
868                for (int i = 0; i < size; i++) {
869                    streamState.mDeathHandlers.get(i).mMuteCount = 1;
870                    streamState.mDeathHandlers.get(i).mute(false);
871                }
872            }
873            // apply stream volume
874            if (streamState.muteCount() == 0) {
875                setStreamVolumeIndex(streamType, streamState.mIndex);
876            }
877        }
878
879        // apply new ringer mode
880        setRingerModeInt(getRingerMode(), false);
881    }
882
883    /** @see AudioManager#setSpeakerphoneOn() */
884    public void setSpeakerphoneOn(boolean on){
885        if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
886            return;
887        }
888        if (on) {
889            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER);
890            mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
891        } else {
892            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
893            mForcedUseForComm = AudioSystem.FORCE_NONE;
894        }
895    }
896
897    /** @see AudioManager#isSpeakerphoneOn() */
898    public boolean isSpeakerphoneOn() {
899        if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
900            return true;
901        } else {
902            return false;
903        }
904    }
905
906    /** @see AudioManager#setBluetoothScoOn() */
907    public void setBluetoothScoOn(boolean on){
908        if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
909            return;
910        }
911        if (on) {
912            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO);
913            AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO);
914            mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
915        } else {
916            AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
917            AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE);
918            mForcedUseForComm = AudioSystem.FORCE_NONE;
919        }
920    }
921
922    /** @see AudioManager#isBluetoothScoOn() */
923    public boolean isBluetoothScoOn() {
924        if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
925            return true;
926        } else {
927            return false;
928        }
929    }
930
931    /** @see AudioManager#startBluetoothSco() */
932    public void startBluetoothSco(IBinder cb){
933        if (!checkAudioSettingsPermission("startBluetoothSco()")) {
934            return;
935        }
936        ScoClient client = getScoClient(cb);
937        client.incCount();
938    }
939
940    /** @see AudioManager#stopBluetoothSco() */
941    public void stopBluetoothSco(IBinder cb){
942        if (!checkAudioSettingsPermission("stopBluetoothSco()")) {
943            return;
944        }
945        ScoClient client = getScoClient(cb);
946        client.decCount();
947    }
948
949    private class ScoClient implements IBinder.DeathRecipient {
950        private IBinder mCb; // To be notified of client's death
951        private int mStartcount; // number of SCO connections started by this client
952
953        ScoClient(IBinder cb) {
954            mCb = cb;
955            mStartcount = 0;
956        }
957
958        public void binderDied() {
959            synchronized(mScoClients) {
960                Log.w(TAG, "SCO client died");
961                int index = mScoClients.indexOf(this);
962                if (index < 0) {
963                    Log.w(TAG, "unregistered SCO client died");
964                } else {
965                    clearCount(true);
966                    mScoClients.remove(this);
967                }
968            }
969        }
970
971        public void incCount() {
972            synchronized(mScoClients) {
973                requestScoState(BluetoothHeadset.AUDIO_STATE_CONNECTED);
974                if (mStartcount == 0) {
975                    try {
976                        mCb.linkToDeath(this, 0);
977                    } catch (RemoteException e) {
978                        // client has already died!
979                        Log.w(TAG, "ScoClient  incCount() could not link to "+mCb+" binder death");
980                    }
981                }
982                mStartcount++;
983            }
984        }
985
986        public void decCount() {
987            synchronized(mScoClients) {
988                if (mStartcount == 0) {
989                    Log.w(TAG, "ScoClient.decCount() already 0");
990                } else {
991                    mStartcount--;
992                    if (mStartcount == 0) {
993                        mCb.unlinkToDeath(this, 0);
994                    }
995                    requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
996                }
997            }
998        }
999
1000        public void clearCount(boolean stopSco) {
1001            synchronized(mScoClients) {
1002                mStartcount = 0;
1003                mCb.unlinkToDeath(this, 0);
1004                if (stopSco) {
1005                    requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
1006                }
1007            }
1008        }
1009
1010        public int getCount() {
1011            return mStartcount;
1012        }
1013
1014        public IBinder getBinder() {
1015            return mCb;
1016        }
1017
1018        public int totalCount() {
1019            synchronized(mScoClients) {
1020                int count = 0;
1021                int size = mScoClients.size();
1022                for (int i = 0; i < size; i++) {
1023                    count += mScoClients.get(i).getCount();
1024                }
1025                return count;
1026            }
1027        }
1028
1029        private void requestScoState(int state) {
1030            if (totalCount() == 0 &&
1031                mBluetoothHeadsetConnected &&
1032                AudioService.this.mMode == AudioSystem.MODE_NORMAL) {
1033                if (state == BluetoothHeadset.AUDIO_STATE_CONNECTED) {
1034                    mBluetoothHeadset.startVoiceRecognition();
1035                } else {
1036                    mBluetoothHeadset.stopVoiceRecognition();
1037                }
1038            }
1039        }
1040    }
1041
1042    public ScoClient getScoClient(IBinder cb) {
1043        synchronized(mScoClients) {
1044            ScoClient client;
1045            int size = mScoClients.size();
1046            for (int i = 0; i < size; i++) {
1047                client = mScoClients.get(i);
1048                if (client.getBinder() == cb)
1049                    return client;
1050            }
1051            client = new ScoClient(cb);
1052            mScoClients.add(client);
1053            return client;
1054        }
1055    }
1056
1057    public void clearAllScoClients() {
1058        synchronized(mScoClients) {
1059            int size = mScoClients.size();
1060            for (int i = 0; i < size; i++) {
1061                mScoClients.get(i).clearCount(false);
1062            }
1063        }
1064    }
1065
1066    private BluetoothHeadset.ServiceListener mBluetoothHeadsetServiceListener =
1067        new BluetoothHeadset.ServiceListener() {
1068        public void onServiceConnected() {
1069            if (mBluetoothHeadset != null &&
1070                mBluetoothHeadset.getState() == BluetoothHeadset.STATE_CONNECTED) {
1071                mBluetoothHeadsetConnected = true;
1072            }
1073        }
1074        public void onServiceDisconnected() {
1075            if (mBluetoothHeadset != null &&
1076                mBluetoothHeadset.getState() == BluetoothHeadset.STATE_DISCONNECTED) {
1077                mBluetoothHeadsetConnected = false;
1078                clearAllScoClients();
1079            }
1080        }
1081    };
1082
1083    ///////////////////////////////////////////////////////////////////////////
1084    // Internal methods
1085    ///////////////////////////////////////////////////////////////////////////
1086
1087    /**
1088     * Checks if the adjustment should change ringer mode instead of just
1089     * adjusting volume. If so, this will set the proper ringer mode and volume
1090     * indices on the stream states.
1091     */
1092    private boolean checkForRingerModeChange(int oldIndex, int direction) {
1093        boolean adjustVolumeIndex = true;
1094        int newRingerMode = mRingerMode;
1095
1096        if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
1097            // audible mode, at the bottom of the scale
1098            if (direction == AudioManager.ADJUST_LOWER
1099                    && (oldIndex + 5) / 10 == 1) {
1100                // "silent mode", but which one?
1101                newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
1102                    ? AudioManager.RINGER_MODE_VIBRATE
1103                    : AudioManager.RINGER_MODE_SILENT;
1104            }
1105        } else {
1106            if (direction == AudioManager.ADJUST_RAISE) {
1107                // exiting silent mode
1108                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
1109            }
1110        }
1111
1112        if (newRingerMode != mRingerMode) {
1113            setRingerMode(newRingerMode);
1114
1115            /*
1116             * If we are changing ringer modes, do not increment/decrement the
1117             * volume index. Instead, the handler for the message above will
1118             * take care of changing the index.
1119             */
1120            adjustVolumeIndex = false;
1121        }
1122
1123        return adjustVolumeIndex;
1124    }
1125
1126    public boolean isStreamAffectedByRingerMode(int streamType) {
1127        return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
1128    }
1129
1130    public boolean isStreamAffectedByMute(int streamType) {
1131        return (mMuteAffectedStreams & (1 << streamType)) != 0;
1132    }
1133
1134    private void ensureValidDirection(int direction) {
1135        if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
1136            throw new IllegalArgumentException("Bad direction " + direction);
1137        }
1138    }
1139
1140    private void ensureValidStreamType(int streamType) {
1141        if (streamType < 0 || streamType >= mStreamStates.length) {
1142            throw new IllegalArgumentException("Bad stream type " + streamType);
1143        }
1144    }
1145
1146    private int getActiveStreamType(int suggestedStreamType) {
1147        boolean isOffhook = false;
1148        try {
1149            ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1150            if (phone != null) isOffhook = phone.isOffhook();
1151        } catch (RemoteException e) {
1152            Log.w(TAG, "Couldn't connect to phone service", e);
1153        }
1154
1155        if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) {
1156            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1157            return AudioSystem.STREAM_BLUETOOTH_SCO;
1158        } else if (isOffhook || AudioSystem.isStreamActive(AudioSystem.STREAM_VOICE_CALL)) {
1159            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1160            return AudioSystem.STREAM_VOICE_CALL;
1161        } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC)) {
1162            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
1163            return AudioSystem.STREAM_MUSIC;
1164        } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
1165            // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING...");
1166            return AudioSystem.STREAM_RING;
1167        } else {
1168            // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1169            return suggestedStreamType;
1170        }
1171    }
1172
1173    private void broadcastRingerMode() {
1174        // Send sticky broadcast
1175        Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
1176        broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
1177        broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1178                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1179        long origCallerIdentityToken = Binder.clearCallingIdentity();
1180        mContext.sendStickyBroadcast(broadcast);
1181        Binder.restoreCallingIdentity(origCallerIdentityToken);
1182    }
1183
1184    private void broadcastVibrateSetting(int vibrateType) {
1185        // Send broadcast
1186        if (ActivityManagerNative.isSystemReady()) {
1187            Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
1188            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
1189            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
1190            mContext.sendBroadcast(broadcast);
1191        }
1192    }
1193
1194    // Message helper methods
1195    private static int getMsg(int baseMsg, int streamType) {
1196        return (baseMsg & 0xffff) | streamType << 16;
1197    }
1198
1199    private static int getMsgBase(int msg) {
1200        return msg & 0xffff;
1201    }
1202
1203    private static void sendMsg(Handler handler, int baseMsg, int streamType,
1204            int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
1205        int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
1206
1207        if (existingMsgPolicy == SENDMSG_REPLACE) {
1208            handler.removeMessages(msg);
1209        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
1210            return;
1211        }
1212
1213        handler
1214                .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
1215    }
1216
1217    boolean checkAudioSettingsPermission(String method) {
1218        if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
1219                == PackageManager.PERMISSION_GRANTED) {
1220            return true;
1221        }
1222        String msg = "Audio Settings Permission Denial: " + method + " from pid="
1223                + Binder.getCallingPid()
1224                + ", uid=" + Binder.getCallingUid();
1225        Log.w(TAG, msg);
1226        return false;
1227    }
1228
1229
1230    ///////////////////////////////////////////////////////////////////////////
1231    // Inner classes
1232    ///////////////////////////////////////////////////////////////////////////
1233
1234    public class VolumeStreamState {
1235        private final int mStreamType;
1236
1237        private String mVolumeIndexSettingName;
1238        private String mLastAudibleVolumeIndexSettingName;
1239        private int mIndexMax;
1240        private int mIndex;
1241        private int mLastAudibleIndex;
1242        private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
1243
1244        private VolumeStreamState(String settingName, int streamType) {
1245
1246            setVolumeIndexSettingName(settingName);
1247
1248            mStreamType = streamType;
1249
1250            final ContentResolver cr = mContentResolver;
1251            mIndexMax = MAX_STREAM_VOLUME[streamType];
1252            mIndex = Settings.System.getInt(cr,
1253                                            mVolumeIndexSettingName,
1254                                            AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1255            mLastAudibleIndex = Settings.System.getInt(cr,
1256                                                       mLastAudibleVolumeIndexSettingName,
1257                                                       (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1258            AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
1259            mIndexMax *= 10;
1260            mIndex = getValidIndex(10 * mIndex);
1261            mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
1262            setStreamVolumeIndex(streamType, mIndex);
1263            mDeathHandlers = new ArrayList<VolumeDeathHandler>();
1264        }
1265
1266        public void setVolumeIndexSettingName(String settingName) {
1267            mVolumeIndexSettingName = settingName;
1268            mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1269        }
1270
1271        public boolean adjustIndex(int deltaIndex) {
1272            return setIndex(mIndex + deltaIndex * 10, true);
1273        }
1274
1275        public boolean setIndex(int index, boolean lastAudible) {
1276            int oldIndex = mIndex;
1277            mIndex = getValidIndex(index);
1278
1279            if (oldIndex != mIndex) {
1280                if (lastAudible) {
1281                    mLastAudibleIndex = mIndex;
1282                }
1283                // Apply change to all streams using this one as alias
1284                int numStreamTypes = AudioSystem.getNumStreamTypes();
1285                for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1286                    if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
1287                        mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
1288                    }
1289                }
1290                return true;
1291            } else {
1292                return false;
1293            }
1294        }
1295
1296        public int getMaxIndex() {
1297            return mIndexMax;
1298        }
1299
1300        public void mute(IBinder cb, boolean state) {
1301            VolumeDeathHandler handler = getDeathHandler(cb, state);
1302            if (handler == null) {
1303                Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
1304                return;
1305            }
1306            handler.mute(state);
1307        }
1308
1309        private int getValidIndex(int index) {
1310            if (index < 0) {
1311                return 0;
1312            } else if (index > mIndexMax) {
1313                return mIndexMax;
1314            }
1315
1316            return index;
1317        }
1318
1319        private class VolumeDeathHandler implements IBinder.DeathRecipient {
1320            private IBinder mICallback; // To be notified of client's death
1321            private int mMuteCount; // Number of active mutes for this client
1322
1323            VolumeDeathHandler(IBinder cb) {
1324                mICallback = cb;
1325            }
1326
1327            public void mute(boolean state) {
1328                synchronized(mDeathHandlers) {
1329                    if (state) {
1330                        if (mMuteCount == 0) {
1331                            // Register for client death notification
1332                            try {
1333                                mICallback.linkToDeath(this, 0);
1334                                mDeathHandlers.add(this);
1335                                // If the stream is not yet muted by any client, set lvel to 0
1336                                if (muteCount() == 0) {
1337                                    setIndex(0, false);
1338                                    sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1339                                            VolumeStreamState.this, 0);
1340                                }
1341                            } catch (RemoteException e) {
1342                                // Client has died!
1343                                binderDied();
1344                                mDeathHandlers.notify();
1345                                return;
1346                            }
1347                        } else {
1348                            Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
1349                        }
1350                        mMuteCount++;
1351                    } else {
1352                        if (mMuteCount == 0) {
1353                            Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
1354                        } else {
1355                            mMuteCount--;
1356                            if (mMuteCount == 0) {
1357                                // Unregistr from client death notification
1358                                mDeathHandlers.remove(this);
1359                                mICallback.unlinkToDeath(this, 0);
1360                                if (muteCount() == 0) {
1361                                    // If the stream is not mut any more, restore it's volume if
1362                                    // ringer mode allows it
1363                                    if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
1364                                        setIndex(mLastAudibleIndex, false);
1365                                        sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1366                                                VolumeStreamState.this, 0);
1367                                    }
1368                                }
1369                            }
1370                        }
1371                    }
1372                    mDeathHandlers.notify();
1373                }
1374            }
1375
1376            public void binderDied() {
1377                Log.w(TAG, "Volume service client died for stream: "+mStreamType);
1378                if (mMuteCount != 0) {
1379                    // Reset all active mute requests from this client.
1380                    mMuteCount = 1;
1381                    mute(false);
1382                }
1383            }
1384        }
1385
1386        private int muteCount() {
1387            int count = 0;
1388            int size = mDeathHandlers.size();
1389            for (int i = 0; i < size; i++) {
1390                count += mDeathHandlers.get(i).mMuteCount;
1391            }
1392            return count;
1393        }
1394
1395        private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
1396            synchronized(mDeathHandlers) {
1397                VolumeDeathHandler handler;
1398                int size = mDeathHandlers.size();
1399                for (int i = 0; i < size; i++) {
1400                    handler = mDeathHandlers.get(i);
1401                    if (cb.equals(handler.mICallback)) {
1402                        return handler;
1403                    }
1404                }
1405                // If this is the first mute request for this client, create a new
1406                // client death handler. Otherwise, it is an out of sequence unmute request.
1407                if (state) {
1408                    handler = new VolumeDeathHandler(cb);
1409                } else {
1410                    Log.w(TAG, "stream was not muted by this client");
1411                    handler = null;
1412                }
1413                return handler;
1414            }
1415        }
1416    }
1417
1418    /** Thread that handles native AudioSystem control. */
1419    private class AudioSystemThread extends Thread {
1420        AudioSystemThread() {
1421            super("AudioService");
1422        }
1423
1424        @Override
1425        public void run() {
1426            // Set this thread up so the handler will work on it
1427            Looper.prepare();
1428
1429            synchronized(AudioService.this) {
1430                mAudioHandler = new AudioHandler();
1431
1432                // Notify that the handler has been created
1433                AudioService.this.notify();
1434            }
1435
1436            // Listen for volume change requests that are set by VolumePanel
1437            Looper.loop();
1438        }
1439    }
1440
1441    /** Handles internal volume messages in separate volume thread. */
1442    private class AudioHandler extends Handler {
1443
1444        private void setSystemVolume(VolumeStreamState streamState) {
1445
1446            // Adjust volume
1447            setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
1448
1449            // Apply change to all streams using this one as alias
1450            int numStreamTypes = AudioSystem.getNumStreamTypes();
1451            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1452                if (streamType != streamState.mStreamType &&
1453                    STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
1454                    setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
1455                }
1456            }
1457
1458            // Post a persist volume msg
1459            sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
1460                    SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY);
1461        }
1462
1463        private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) {
1464            if (current) {
1465                System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
1466                              (streamState.mIndex + 5)/ 10);
1467            }
1468            if (lastAudible) {
1469                System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
1470                    (streamState.mLastAudibleIndex + 5) / 10);
1471            }
1472        }
1473
1474        private void persistRingerMode() {
1475            System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
1476        }
1477
1478        private void persistVibrateSetting() {
1479            System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
1480        }
1481
1482        private void playSoundEffect(int effectType, int volume) {
1483            synchronized (mSoundEffectsLock) {
1484                if (mSoundPool == null) {
1485                    return;
1486                }
1487                float volFloat;
1488                // use STREAM_MUSIC volume attenuated by 3 dB if volume is not specified by caller
1489                if (volume < 0) {
1490                    // Same linear to log conversion as in native AudioSystem::linearToLog() (AudioSystem.cpp)
1491                    float dBPerStep = (float)((0.5 * 100) / MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
1492                    int musicVolIndex = (mStreamStates[AudioSystem.STREAM_MUSIC].mIndex + 5) / 10;
1493                    float musicVoldB = dBPerStep * (musicVolIndex - MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
1494                    volFloat = (float)Math.pow(10, (musicVoldB - 3)/20);
1495                } else {
1496                    volFloat = (float) volume / 1000.0f;
1497                }
1498
1499                if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
1500                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
1501                } else {
1502                    MediaPlayer mediaPlayer = new MediaPlayer();
1503                    if (mediaPlayer != null) {
1504                        try {
1505                            String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
1506                            mediaPlayer.setDataSource(filePath);
1507                            mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
1508                            mediaPlayer.prepare();
1509                            mediaPlayer.setVolume(volFloat, volFloat);
1510                            mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
1511                                public void onCompletion(MediaPlayer mp) {
1512                                    cleanupPlayer(mp);
1513                                }
1514                            });
1515                            mediaPlayer.setOnErrorListener(new OnErrorListener() {
1516                                public boolean onError(MediaPlayer mp, int what, int extra) {
1517                                    cleanupPlayer(mp);
1518                                    return true;
1519                                }
1520                            });
1521                            mediaPlayer.start();
1522                        } catch (IOException ex) {
1523                            Log.w(TAG, "MediaPlayer IOException: "+ex);
1524                        } catch (IllegalArgumentException ex) {
1525                            Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
1526                        } catch (IllegalStateException ex) {
1527                            Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1528                        }
1529                    }
1530                }
1531            }
1532        }
1533
1534        private void cleanupPlayer(MediaPlayer mp) {
1535            if (mp != null) {
1536                try {
1537                    mp.stop();
1538                    mp.release();
1539                } catch (IllegalStateException ex) {
1540                    Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1541                }
1542            }
1543        }
1544
1545        @Override
1546        public void handleMessage(Message msg) {
1547            int baseMsgWhat = getMsgBase(msg.what);
1548
1549            switch (baseMsgWhat) {
1550
1551                case MSG_SET_SYSTEM_VOLUME:
1552                    setSystemVolume((VolumeStreamState) msg.obj);
1553                    break;
1554
1555                case MSG_PERSIST_VOLUME:
1556                    persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0));
1557                    break;
1558
1559                case MSG_PERSIST_RINGER_MODE:
1560                    persistRingerMode();
1561                    break;
1562
1563                case MSG_PERSIST_VIBRATE_SETTING:
1564                    persistVibrateSetting();
1565                    break;
1566
1567                case MSG_MEDIA_SERVER_DIED:
1568                    // Force creation of new IAudioflinger interface
1569                    if (!mMediaServerOk) {
1570                        Log.e(TAG, "Media server died.");
1571                        AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC);
1572                        sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
1573                                null, 500);
1574                    }
1575                    break;
1576
1577                case MSG_MEDIA_SERVER_STARTED:
1578                    Log.e(TAG, "Media server started.");
1579                    // Restore device connection states
1580                    Set set = mConnectedDevices.entrySet();
1581                    Iterator i = set.iterator();
1582                    while(i.hasNext()){
1583                        Map.Entry device = (Map.Entry)i.next();
1584                        AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
1585                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
1586                                                             (String)device.getValue());
1587                    }
1588
1589                    // Restore call state
1590                    AudioSystem.setPhoneState(mMode);
1591
1592                    // Restore forced usage for communcations and record
1593                    AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
1594                    AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
1595
1596                    // Restore stream volumes
1597                    int numStreamTypes = AudioSystem.getNumStreamTypes();
1598                    for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1599                        int index;
1600                        VolumeStreamState streamState = mStreamStates[streamType];
1601                        AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
1602                        if (streamState.muteCount() == 0) {
1603                            index = streamState.mIndex;
1604                        } else {
1605                            index = 0;
1606                        }
1607                        setStreamVolumeIndex(streamType, index);
1608                    }
1609
1610                    // Restore ringer mode
1611                    setRingerModeInt(getRingerMode(), false);
1612                    break;
1613
1614                case MSG_PLAY_SOUND_EFFECT:
1615                    playSoundEffect(msg.arg1, msg.arg2);
1616                    break;
1617            }
1618        }
1619    }
1620
1621    private class SettingsObserver extends ContentObserver {
1622
1623        SettingsObserver() {
1624            super(new Handler());
1625            mContentResolver.registerContentObserver(Settings.System.getUriFor(
1626                Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
1627            mContentResolver.registerContentObserver(Settings.System.getUriFor(
1628                    Settings.System.NOTIFICATIONS_USE_RING_VOLUME), false, this);
1629        }
1630
1631        @Override
1632        public void onChange(boolean selfChange) {
1633            super.onChange(selfChange);
1634            synchronized (mSettingsLock) {
1635                int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
1636                        Settings.System.MODE_RINGER_STREAMS_AFFECTED,
1637                        0);
1638                if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
1639                    /*
1640                     * Ensure all stream types that should be affected by ringer mode
1641                     * are in the proper state.
1642                     */
1643                    mRingerModeAffectedStreams = ringerModeAffectedStreams;
1644                    setRingerModeInt(getRingerMode(), false);
1645                }
1646
1647                int notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
1648                        Settings.System.NOTIFICATIONS_USE_RING_VOLUME,
1649                        1);
1650                if (notificationsUseRingVolume != mNotificationsUseRingVolume) {
1651                    mNotificationsUseRingVolume = notificationsUseRingVolume;
1652                    if (mNotificationsUseRingVolume == 1) {
1653                        STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
1654                        mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
1655                                System.VOLUME_SETTINGS[AudioSystem.STREAM_RING]);
1656                    } else {
1657                        STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
1658                        mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
1659                                System.VOLUME_SETTINGS[AudioSystem.STREAM_NOTIFICATION]);
1660                        // Persist notification volume volume as it was not persisted while aliased to ring volume
1661                        //  and persist with no delay as there might be registered observers of the persisted
1662                        //  notification volume.
1663                        sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
1664                                SENDMSG_REPLACE, 1, 1, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0);
1665                    }
1666                }
1667            }
1668        }
1669    }
1670
1671    /**
1672     * Receiver for misc intent broadcasts the Phone app cares about.
1673     */
1674    private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
1675        @Override
1676        public void onReceive(Context context, Intent intent) {
1677            String action = intent.getAction();
1678
1679            if (action.equals(Intent.ACTION_DOCK_EVENT)) {
1680                int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1681                        Intent.EXTRA_DOCK_STATE_UNDOCKED);
1682                int config;
1683                switch (dockState) {
1684                    case Intent.EXTRA_DOCK_STATE_DESK:
1685                        config = AudioSystem.FORCE_BT_DESK_DOCK;
1686                        break;
1687                    case Intent.EXTRA_DOCK_STATE_CAR:
1688                        config = AudioSystem.FORCE_BT_CAR_DOCK;
1689                        break;
1690                    case Intent.EXTRA_DOCK_STATE_UNDOCKED:
1691                    default:
1692                        config = AudioSystem.FORCE_NONE;
1693                }
1694                AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
1695            } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
1696                int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE,
1697                                               BluetoothA2dp.STATE_DISCONNECTED);
1698                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1699                String address = btDevice.getAddress();
1700                boolean isConnected = (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
1701                                       ((String)mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)).equals(address));
1702
1703                if (isConnected &&
1704                    state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) {
1705                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1706                            AudioSystem.DEVICE_STATE_UNAVAILABLE,
1707                            address);
1708                    mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
1709                } else if (!isConnected &&
1710                             (state == BluetoothA2dp.STATE_CONNECTED ||
1711                              state == BluetoothA2dp.STATE_PLAYING)) {
1712                    AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1713                                                         AudioSystem.DEVICE_STATE_AVAILABLE,
1714                                                         address);
1715                    // Reset A2DP suspend state each time a new sink is connected
1716                    AudioSystem.setParameters("A2dpSuspended=false");
1717                    mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
1718                            address);
1719                }
1720            } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
1721                int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
1722                                               BluetoothHeadset.STATE_ERROR);
1723                int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
1724                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1725                String address = null;
1726                if (btDevice != null) {
1727                    address = btDevice.getAddress();
1728                    BluetoothClass btClass = btDevice.getBluetoothClass();
1729                    if (btClass != null) {
1730                        switch (btClass.getDeviceClass()) {
1731                        case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
1732                        case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
1733                            device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
1734                            break;
1735                        case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
1736                            device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
1737                            break;
1738                        }
1739                    }
1740                }
1741
1742                boolean isConnected = (mConnectedDevices.containsKey(device) &&
1743                                       ((String)mConnectedDevices.get(device)).equals(address));
1744
1745                if (isConnected && state != BluetoothHeadset.STATE_CONNECTED) {
1746                    AudioSystem.setDeviceConnectionState(device,
1747                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE,
1748                                                         address);
1749                    mConnectedDevices.remove(device);
1750                    mBluetoothHeadsetConnected = false;
1751                    clearAllScoClients();
1752                } else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) {
1753                    AudioSystem.setDeviceConnectionState(device,
1754                                                         AudioSystem.DEVICE_STATE_AVAILABLE,
1755                                                         address);
1756                    mConnectedDevices.put(new Integer(device), address);
1757                    mBluetoothHeadsetConnected = true;
1758                }
1759            } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
1760                int state = intent.getIntExtra("state", 0);
1761                int microphone = intent.getIntExtra("microphone", 0);
1762
1763                if (microphone != 0) {
1764                    boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
1765                    if (state == 0 && isConnected) {
1766                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1767                                AudioSystem.DEVICE_STATE_UNAVAILABLE,
1768                                "");
1769                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
1770                    } else if (state == 1 && !isConnected)  {
1771                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1772                                AudioSystem.DEVICE_STATE_AVAILABLE,
1773                                "");
1774                        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
1775                    }
1776                } else {
1777                    boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
1778                    if (state == 0 && isConnected) {
1779                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1780                                AudioSystem.DEVICE_STATE_UNAVAILABLE,
1781                                "");
1782                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
1783                    } else if (state == 1 && !isConnected)  {
1784                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1785                                AudioSystem.DEVICE_STATE_AVAILABLE,
1786                                "");
1787                        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
1788                    }
1789                }
1790            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
1791                int state = intent.getIntExtra(BluetoothHeadset.EXTRA_AUDIO_STATE,
1792                                               BluetoothHeadset.STATE_ERROR);
1793                synchronized (mScoClients) {
1794                    if (!mScoClients.isEmpty()) {
1795                        switch (state) {
1796                        case BluetoothHeadset.AUDIO_STATE_CONNECTED:
1797                            state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
1798                            break;
1799                        case BluetoothHeadset.AUDIO_STATE_DISCONNECTED:
1800                            state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
1801                            break;
1802                        default:
1803                            state = AudioManager.SCO_AUDIO_STATE_ERROR;
1804                            break;
1805                        }
1806                        if (state != AudioManager.SCO_AUDIO_STATE_ERROR) {
1807                            Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
1808                            newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
1809                            mContext.sendStickyBroadcast(newIntent);
1810                        }
1811                    }
1812                }
1813            }
1814        }
1815    }
1816
1817    //==========================================================================================
1818    // AudioFocus
1819    //==========================================================================================
1820    /**
1821     * Flag to indicate that the top of the audio focus stack needs to recover focus
1822     * but hasn't been signaled yet.
1823     */
1824    private boolean mHasUndispatchedAudioFocus = false;
1825
1826    private void checkForUndispatchedAudioFocusChange(int prevMode, int newMode) {
1827        // when exiting a call
1828        if ((prevMode == AudioSystem.MODE_IN_CALL) && (newMode != AudioSystem.MODE_IN_CALL)) {
1829            // check for undispatched remote control focus gain
1830            if (mHasUndispatchedAudioFocus) {
1831                notifyTopOfAudioFocusStack();
1832            }
1833        }
1834    }
1835
1836    private void notifyTopOfAudioFocusStack() {
1837        // notify the top of the stack it gained focus
1838        if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
1839            if (canReassignAudioFocus()) {
1840                try {
1841                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
1842                            AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
1843                    mHasUndispatchedAudioFocus = false;
1844                } catch (RemoteException e) {
1845                    Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
1846                    e.printStackTrace();
1847                }
1848            } else {
1849                mHasUndispatchedAudioFocus = true;
1850            }
1851        }
1852    }
1853
1854    private static class FocusStackEntry {
1855        public int mStreamType = -1;// no stream type
1856        public boolean mIsTransportControlReceiver = false;
1857        public IAudioFocusDispatcher mFocusDispatcher = null;
1858        public IBinder mSourceRef = null;
1859        public String mClientId;
1860        public int mFocusChangeType;
1861
1862        public FocusStackEntry() {
1863        }
1864
1865        public FocusStackEntry(int streamType, int duration, boolean isTransportControlReceiver,
1866                IAudioFocusDispatcher afl, IBinder source, String id) {
1867            mStreamType = streamType;
1868            mIsTransportControlReceiver = isTransportControlReceiver;
1869            mFocusDispatcher = afl;
1870            mSourceRef = source;
1871            mClientId = id;
1872            mFocusChangeType = duration;
1873        }
1874    }
1875
1876    private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
1877
1878    /**
1879     * Helper function:
1880     * Display in the log the current entries in the audio focus stack
1881     */
1882    private void dumpFocusStack(PrintWriter pw) {
1883        pw.println("Audio Focus stack entries:");
1884        synchronized(mFocusStack) {
1885            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
1886            while(stackIterator.hasNext()) {
1887                FocusStackEntry fse = stackIterator.next();
1888                pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
1889                        + " -- duration: " +fse.mFocusChangeType);
1890            }
1891        }
1892    }
1893
1894    /**
1895     * Helper function:
1896     * Remove a focus listener from the focus stack.
1897     * @param focusListenerToRemove the focus listener
1898     * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
1899     *   focus, notify the next item in the stack it gained focus.
1900     */
1901    private void removeFocusStackEntry(String clientToRemove, boolean signal) {
1902        // is the current top of the focus stack abandoning focus? (because of death or request)
1903        if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
1904        {
1905            //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
1906            mFocusStack.pop();
1907            if (signal) {
1908                // notify the new top of the stack it gained focus
1909                notifyTopOfAudioFocusStack();
1910            }
1911        } else {
1912            // focus is abandoned by a client that's not at the top of the stack,
1913            // no need to update focus.
1914            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
1915            while(stackIterator.hasNext()) {
1916                FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
1917                if(fse.mClientId.equals(clientToRemove)) {
1918                    Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
1919                            + fse.mClientId);
1920                    mFocusStack.remove(fse);
1921                }
1922            }
1923        }
1924    }
1925
1926    /**
1927     * Helper function:
1928     * Remove focus listeners from the focus stack for a particular client.
1929     */
1930    private void removeFocusStackEntryForClient(IBinder cb) {
1931        // is the owner of the audio focus part of the client to remove?
1932        boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
1933                mFocusStack.peek().mSourceRef.equals(cb);
1934        Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
1935        while(stackIterator.hasNext()) {
1936            FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
1937            if(fse.mSourceRef.equals(cb)) {
1938                Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
1939                        + fse.mClientId);
1940                mFocusStack.remove(fse);
1941            }
1942        }
1943        if (isTopOfStackForClientToRemove) {
1944            // we removed an entry at the top of the stack:
1945            //  notify the new top of the stack it gained focus.
1946            notifyTopOfAudioFocusStack();
1947        }
1948    }
1949
1950    /**
1951     * Helper function:
1952     * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
1953     */
1954    private boolean canReassignAudioFocus() {
1955        // focus requests are rejected during a phone call
1956        if (getMode() == AudioSystem.MODE_IN_CALL) {
1957            Log.i(TAG, " AudioFocus  can't be reassigned during a call, exiting");
1958            return false;
1959        }
1960        return true;
1961    }
1962
1963    /**
1964     * Inner class to monitor audio focus client deaths, and remove them from the audio focus
1965     * stack if necessary.
1966     */
1967    private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
1968        private IBinder mCb; // To be notified of client's death
1969
1970        AudioFocusDeathHandler(IBinder cb) {
1971            mCb = cb;
1972        }
1973
1974        public void binderDied() {
1975            synchronized(mFocusStack) {
1976                Log.w(TAG, "  AudioFocus   audio focus client died");
1977                removeFocusStackEntryForClient(mCb);
1978            }
1979        }
1980
1981        public IBinder getBinder() {
1982            return mCb;
1983        }
1984    }
1985
1986
1987    /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
1988    public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
1989            IAudioFocusDispatcher fd, String clientId) {
1990        Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
1991        // the main stream type for the audio focus request is currently not used. It may
1992        // potentially be used to handle multiple stream type-dependent audio focuses.
1993
1994        if ((cb == null) || !cb.pingBinder()) {
1995            Log.i(TAG, " AudioFocus  DOA client for requestAudioFocus(), exiting");
1996            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
1997        }
1998
1999        if (!canReassignAudioFocus()) {
2000            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
2001        }
2002
2003        synchronized(mFocusStack) {
2004            if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
2005                // if focus is already owned by this client and the reason for acquiring the focus
2006                // hasn't changed, don't do anything
2007                if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
2008                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2009                }
2010                // the reason for the audio focus request has changed: remove the current top of
2011                // stack and respond as if we had a new focus owner
2012                mFocusStack.pop();
2013            }
2014
2015            // notify current top of stack it is losing focus
2016            if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
2017                try {
2018                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
2019                            -1 * focusChangeHint, // loss and gain codes are inverse of each other
2020                            mFocusStack.peek().mClientId);
2021                } catch (RemoteException e) {
2022                    Log.e(TAG, " Failure to signal loss of focus due to "+ e);
2023                    e.printStackTrace();
2024                }
2025            }
2026
2027            // push focus requester at the top of the audio focus stack
2028            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb,
2029                    clientId));
2030        }//synchronized(mFocusStack)
2031
2032        // handle the potential premature death of the new holder of the focus
2033        // (premature death == death before abandoning focus)
2034        // Register for client death notification
2035        AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
2036        try {
2037            cb.linkToDeath(afdh, 0);
2038        } catch (RemoteException e) {
2039            // client has already died!
2040            Log.w(TAG, " AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
2041        }
2042
2043        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2044    }
2045
2046    /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
2047    public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
2048        Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
2049
2050        // this will take care of notifying the new focus owner if needed
2051        removeFocusStackEntry(clientId, true);
2052
2053        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2054    }
2055
2056
2057    public void unregisterAudioFocusClient(String clientId) {
2058        removeFocusStackEntry(clientId, false);
2059    }
2060
2061
2062    //==========================================================================================
2063    // RemoteControl
2064    //==========================================================================================
2065    /**
2066     * Receiver for media button intents. Handles the dispatching of the media button event
2067     * to one of the registered listeners, or if there was none, resumes the intent broadcast
2068     * to the rest of the system.
2069     */
2070    private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
2071        @Override
2072        public void onReceive(Context context, Intent intent) {
2073            String action = intent.getAction();
2074            if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
2075                return;
2076            }
2077            KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
2078            if (event != null) {
2079                // if in a call or ringing, do not break the current phone app behavior
2080                // TODO modify this to let the phone app specifically get the RC focus
2081                //      add modify the phone app to take advantage of the new API
2082                if ((getMode() == AudioSystem.MODE_IN_CALL) ||
2083                        (getMode() == AudioSystem.MODE_RINGTONE)) {
2084                    return;
2085                }
2086                synchronized(mRCStack) {
2087                    if (!mRCStack.empty()) {
2088                        // create a new intent specifically aimed at the current registered listener
2089                        Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
2090                        targetedIntent.putExtras(intent.getExtras());
2091                        targetedIntent.setComponent(mRCStack.peek().mReceiverComponent);
2092                        // trap the current broadcast
2093                        abortBroadcast();
2094                        //Log.v(TAG, " Sending intent" + targetedIntent);
2095                        context.sendBroadcast(targetedIntent, null);
2096                    }
2097                }
2098            }
2099        }
2100    }
2101
2102    private static class RemoteControlStackEntry {
2103        public ComponentName mReceiverComponent;// always non null
2104        // TODO implement registration expiration?
2105        //public int mRegistrationTime;
2106
2107        public RemoteControlStackEntry() {
2108        }
2109
2110        public RemoteControlStackEntry(ComponentName r) {
2111            mReceiverComponent = r;
2112        }
2113    }
2114
2115    private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
2116
2117    /**
2118     * Helper function:
2119     * Display in the log the current entries in the remote control focus stack
2120     */
2121    private void dumpRCStack(PrintWriter pw) {
2122        pw.println("Remote Control stack entries:");
2123        synchronized(mRCStack) {
2124            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2125            while(stackIterator.hasNext()) {
2126                RemoteControlStackEntry fse = stackIterator.next();
2127                pw.println("     receiver:" + fse.mReceiverComponent);
2128            }
2129        }
2130    }
2131
2132    /**
2133     * Helper function:
2134     * Set the new remote control receiver at the top of the RC focus stack
2135     */
2136    private void pushMediaButtonReceiver(ComponentName newReceiver) {
2137        // already at top of stack?
2138        if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) {
2139            return;
2140        }
2141        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2142        while(stackIterator.hasNext()) {
2143            RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
2144            if(rcse.mReceiverComponent.equals(newReceiver)) {
2145                mRCStack.remove(rcse);
2146                break;
2147            }
2148        }
2149        mRCStack.push(new RemoteControlStackEntry(newReceiver));
2150    }
2151
2152    /**
2153     * Helper function:
2154     * Remove the remote control receiver from the RC focus stack
2155     */
2156    private void removeMediaButtonReceiver(ComponentName newReceiver) {
2157        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2158        while(stackIterator.hasNext()) {
2159            RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
2160            if(rcse.mReceiverComponent.equals(newReceiver)) {
2161                mRCStack.remove(rcse);
2162                break;
2163            }
2164        }
2165    }
2166
2167
2168    /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
2169    public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
2170        Log.i(TAG, "  Remote Control   registerMediaButtonEventReceiver() for " + eventReceiver);
2171
2172        synchronized(mRCStack) {
2173            pushMediaButtonReceiver(eventReceiver);
2174        }
2175    }
2176
2177    /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */
2178    public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
2179        Log.i(TAG, "  Remote Control   registerMediaButtonEventReceiver() for " + eventReceiver);
2180
2181        synchronized(mRCStack) {
2182            removeMediaButtonReceiver(eventReceiver);
2183        }
2184    }
2185
2186
2187    @Override
2188    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2189        // TODO probably a lot more to do here than just the audio focus and remote control stacks
2190        dumpFocusStack(pw);
2191        dumpRCStack(pw);
2192    }
2193
2194
2195}
2196