AudioService.java revision f9113d311b33a14b324a02e3310185af582a7882
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 static android.media.AudioManager.RINGER_MODE_NORMAL;
20import static android.media.AudioManager.RINGER_MODE_SILENT;
21import static android.media.AudioManager.RINGER_MODE_VIBRATE;
22
23import android.app.ActivityManagerNative;
24import android.app.KeyguardManager;
25import android.app.PendingIntent;
26import android.app.PendingIntent.CanceledException;
27import android.bluetooth.BluetoothA2dp;
28import android.bluetooth.BluetoothAdapter;
29import android.bluetooth.BluetoothClass;
30import android.bluetooth.BluetoothDevice;
31import android.bluetooth.BluetoothHeadset;
32import android.bluetooth.BluetoothProfile;
33import android.content.BroadcastReceiver;
34import android.content.ComponentName;
35import android.content.ContentResolver;
36import android.content.Context;
37import android.content.Intent;
38import android.content.IntentFilter;
39import android.content.pm.PackageManager;
40import android.database.ContentObserver;
41import android.media.MediaPlayer.OnCompletionListener;
42import android.media.MediaPlayer.OnErrorListener;
43import android.os.Binder;
44import android.os.Bundle;
45import android.os.Environment;
46import android.os.Handler;
47import android.os.IBinder;
48import android.os.Looper;
49import android.os.Message;
50import android.os.RemoteException;
51import android.os.ServiceManager;
52import android.os.SystemProperties;
53import android.provider.Settings;
54import android.provider.Settings.System;
55import android.telephony.PhoneStateListener;
56import android.telephony.TelephonyManager;
57import android.util.Log;
58import android.view.KeyEvent;
59import android.view.VolumePanel;
60
61import com.android.internal.telephony.ITelephony;
62
63import java.io.FileDescriptor;
64import java.io.IOException;
65import java.io.PrintWriter;
66import java.util.ArrayList;
67import java.util.HashMap;
68import java.util.Iterator;
69import java.util.List;
70import java.util.Map;
71import java.util.NoSuchElementException;
72import java.util.Set;
73import java.util.Stack;
74
75/**
76 * The implementation of the volume manager service.
77 * <p>
78 * This implementation focuses on delivering a responsive UI. Most methods are
79 * asynchronous to external calls. For example, the task of setting a volume
80 * will update our internal state, but in a separate thread will set the system
81 * volume and later persist to the database. Similarly, setting the ringer mode
82 * will update the state and broadcast a change and in a separate thread later
83 * persist the ringer mode.
84 *
85 * @hide
86 */
87public class AudioService extends IAudioService.Stub {
88
89    private static final String TAG = "AudioService";
90
91    /** Debug remote control client/display feature */
92    protected static final boolean DEBUG_RC = false;
93
94    /** How long to delay before persisting a change in volume/ringer mode. */
95    private static final int PERSIST_DELAY = 3000;
96
97    private Context mContext;
98    private ContentResolver mContentResolver;
99    private boolean mVoiceCapable;
100
101    /** The UI */
102    private VolumePanel mVolumePanel;
103
104    // sendMsg() flags
105    /** Used when a message should be shared across all stream types. */
106    private static final int SHARED_MSG = -1;
107    /** If the msg is already queued, replace it with this one. */
108    private static final int SENDMSG_REPLACE = 0;
109    /** If the msg is already queued, ignore this one and leave the old. */
110    private static final int SENDMSG_NOOP = 1;
111    /** If the msg is already queued, queue this one and leave the old. */
112    private static final int SENDMSG_QUEUE = 2;
113
114    // AudioHandler message.whats
115    private static final int MSG_SET_SYSTEM_VOLUME = 0;
116    private static final int MSG_PERSIST_VOLUME = 1;
117    private static final int MSG_PERSIST_MASTER_VOLUME = 2;
118    private static final int MSG_PERSIST_RINGER_MODE = 3;
119    private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
120    private static final int MSG_MEDIA_SERVER_DIED = 5;
121    private static final int MSG_MEDIA_SERVER_STARTED = 6;
122    private static final int MSG_PLAY_SOUND_EFFECT = 7;
123    private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8;
124    private static final int MSG_LOAD_SOUND_EFFECTS = 9;
125    private static final int MSG_SET_FORCE_USE = 10;
126    private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11;
127    private static final int MSG_BT_HEADSET_CNCT_FAILED = 12;
128    private static final int MSG_RCDISPLAY_CLEAR = 13;
129    private static final int MSG_RCDISPLAY_UPDATE = 14;
130
131    private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
132    // Timeout for connection to bluetooth headset service
133    private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000;
134
135    // Amount to raise/lower master volume
136    // FIXME - this should probably be in a resource
137    private static final float MASTER_VOLUME_INCREMENT = 0.05f;
138
139    /** @see AudioSystemThread */
140    private AudioSystemThread mAudioSystemThread;
141    /** @see AudioHandler */
142    private AudioHandler mAudioHandler;
143    /** @see VolumeStreamState */
144    private VolumeStreamState[] mStreamStates;
145    private SettingsObserver mSettingsObserver;
146
147    private int mMode;
148    private Object mSettingsLock = new Object();
149    private boolean mMediaServerOk;
150
151    private SoundPool mSoundPool;
152    private Object mSoundEffectsLock = new Object();
153    private static final int NUM_SOUNDPOOL_CHANNELS = 4;
154    private static final int SOUND_EFFECT_VOLUME = 1000;
155
156    // Internally master volume is a float in the 0.0 - 1.0 range,
157    // but to support integer based AudioManager API we translate it to 0 - 100
158    private static final int MAX_MASTER_VOLUME = 100;
159
160    /* Sound effect file names  */
161    private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
162    private static final String[] SOUND_EFFECT_FILES = new String[] {
163        "Effect_Tick.ogg",
164        "KeypressStandard.ogg",
165        "KeypressSpacebar.ogg",
166        "KeypressDelete.ogg",
167        "KeypressReturn.ogg"
168    };
169
170    /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
171     * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
172     * uses soundpool (second column) */
173    private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
174        {0, -1},  // FX_KEY_CLICK
175        {0, -1},  // FX_FOCUS_NAVIGATION_UP
176        {0, -1},  // FX_FOCUS_NAVIGATION_DOWN
177        {0, -1},  // FX_FOCUS_NAVIGATION_LEFT
178        {0, -1},  // FX_FOCUS_NAVIGATION_RIGHT
179        {1, -1},  // FX_KEYPRESS_STANDARD
180        {2, -1},  // FX_KEYPRESS_SPACEBAR
181        {3, -1},  // FX_FOCUS_DELETE
182        {4, -1}   // FX_FOCUS_RETURN
183    };
184
185   /** @hide Maximum volume index values for audio streams */
186    private int[] MAX_STREAM_VOLUME = new int[] {
187        5,  // STREAM_VOICE_CALL
188        7,  // STREAM_SYSTEM
189        7,  // STREAM_RING
190        15, // STREAM_MUSIC
191        7,  // STREAM_ALARM
192        7,  // STREAM_NOTIFICATION
193        15, // STREAM_BLUETOOTH_SCO
194        7,  // STREAM_SYSTEM_ENFORCED
195        15, // STREAM_DTMF
196        15  // STREAM_TTS
197    };
198    /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
199     * of another stream: This avoids multiplying the volume settings for hidden
200     * stream types that follow other stream behavior for volume settings
201     * NOTE: do not create loops in aliases! */
202    private int[] STREAM_VOLUME_ALIAS = new int[] {
203        AudioSystem.STREAM_VOICE_CALL,  // STREAM_VOICE_CALL
204        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM
205        AudioSystem.STREAM_RING,  // STREAM_RING
206        AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
207        AudioSystem.STREAM_ALARM,  // STREAM_ALARM
208        AudioSystem.STREAM_RING,   // STREAM_NOTIFICATION
209        AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
210        AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM_ENFORCED
211        AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
212        AudioSystem.STREAM_MUSIC  // STREAM_TTS
213    };
214
215    private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
216        public void onError(int error) {
217            switch (error) {
218            case AudioSystem.AUDIO_STATUS_SERVER_DIED:
219                if (mMediaServerOk) {
220                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
221                            null, 1500);
222                    mMediaServerOk = false;
223                }
224                break;
225            case AudioSystem.AUDIO_STATUS_OK:
226                if (!mMediaServerOk) {
227                    sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
228                            null, 0);
229                    mMediaServerOk = true;
230                }
231                break;
232            default:
233                break;
234            }
235       }
236    };
237
238    /**
239     * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
240     * {@link AudioManager#RINGER_MODE_SILENT}, or
241     * {@link AudioManager#RINGER_MODE_VIBRATE}.
242     */
243    private int mRingerMode;
244
245    /** @see System#MODE_RINGER_STREAMS_AFFECTED */
246    private int mRingerModeAffectedStreams;
247
248    // Streams currently muted by ringer mode
249    private int mRingerModeMutedStreams;
250
251    /** @see System#MUTE_STREAMS_AFFECTED */
252    private int mMuteAffectedStreams;
253
254    /**
255     * Has multiple bits per vibrate type to indicate the type's vibrate
256     * setting. See {@link #setVibrateSetting(int, int)}.
257     * <p>
258     * NOTE: This is not the final decision of whether vibrate is on/off for the
259     * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
260     */
261    private int mVibrateSetting;
262
263    // Broadcast receiver for device connections intent broadcasts
264    private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
265
266    //  Broadcast receiver for media button broadcasts (separate from mReceiver to
267    //  independently change its priority)
268    private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
269
270    // Used to alter media button redirection when the phone is ringing.
271    private boolean mIsRinging = false;
272
273    // Devices currently connected
274    private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
275
276    // Forced device usage for communications
277    private int mForcedUseForComm;
278
279    // True if we have master volume support
280    private final boolean mUseMasterVolume;
281
282    // List of binder death handlers for setMode() client processes.
283    // The last process to have called setMode() is at the top of the list.
284    private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
285
286    // List of clients having issued a SCO start request
287    private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
288
289    // BluetoothHeadset API to control SCO connection
290    private BluetoothHeadset mBluetoothHeadset;
291
292    // Bluetooth headset device
293    private BluetoothDevice mBluetoothHeadsetDevice;
294
295    // Indicate if SCO audio connection is currently active and if the initiator is
296    // audio service (internal) or bluetooth headset (external)
297    private int mScoAudioState;
298    // SCO audio state is not active
299    private static final int SCO_STATE_INACTIVE = 0;
300    // SCO audio activation request waiting for headset service to connect
301    private static final int SCO_STATE_ACTIVATE_REQ = 1;
302    // SCO audio state is active or starting due to a local request to start a virtual call
303    private static final int SCO_STATE_ACTIVE_INTERNAL = 3;
304    // SCO audio deactivation request waiting for headset service to connect
305    private static final int SCO_STATE_DEACTIVATE_REQ = 5;
306
307    // SCO audio state is active due to an action in BT handsfree (either voice recognition or
308    // in call audio)
309    private static final int SCO_STATE_ACTIVE_EXTERNAL = 2;
310    // Deactivation request for all SCO connections (initiated by audio mode change)
311    // waiting for headset service to connect
312    private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4;
313
314    // Current connection state indicated by bluetooth headset
315    private int mScoConnectionState;
316
317    // true if boot sequence has been completed
318    private boolean mBootCompleted;
319    // listener for SoundPool sample load completion indication
320    private SoundPoolCallback mSoundPoolCallBack;
321    // thread for SoundPool listener
322    private SoundPoolListenerThread mSoundPoolListenerThread;
323    // message looper for SoundPool listener
324    private Looper mSoundPoolLooper = null;
325    // default volume applied to sound played with playSoundEffect()
326    private static final int SOUND_EFFECT_DEFAULT_VOLUME_DB = -20;
327    // volume applied to sound played with playSoundEffect() read from ro.config.sound_fx_volume
328    private int SOUND_EFFECT_VOLUME_DB;
329    // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification
330    // stopped
331    private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
332    // previous volume adjustment direction received by checkForRingerModeChange()
333    private int mPrevVolDirection = AudioManager.ADJUST_SAME;
334    // Keyguard manager proxy
335    private KeyguardManager mKeyguardManager;
336
337    ///////////////////////////////////////////////////////////////////////////
338    // Construction
339    ///////////////////////////////////////////////////////////////////////////
340
341    /** @hide */
342    public AudioService(Context context) {
343        mContext = context;
344        mContentResolver = context.getContentResolver();
345        mVoiceCapable = mContext.getResources().getBoolean(
346                com.android.internal.R.bool.config_voice_capable);
347
348       // Intialized volume
349        MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
350            "ro.config.vc_call_vol_steps",
351           MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
352
353        SOUND_EFFECT_VOLUME_DB = SystemProperties.getInt(
354                "ro.config.sound_fx_volume",
355                SOUND_EFFECT_DEFAULT_VOLUME_DB);
356
357        mVolumePanel = new VolumePanel(context, this);
358        mForcedUseForComm = AudioSystem.FORCE_NONE;
359        createAudioSystemThread();
360        readPersistedSettings();
361        mSettingsObserver = new SettingsObserver();
362        createStreamStates();
363
364        mMode = AudioSystem.MODE_NORMAL;
365        mMediaServerOk = true;
366
367        // Call setRingerModeInt() to apply correct mute
368        // state on streams affected by ringer mode.
369        mRingerModeMutedStreams = 0;
370        setRingerModeInt(getRingerMode(), false);
371
372        AudioSystem.setErrorCallback(mAudioSystemCallback);
373
374        // Register for device connection intent broadcasts.
375        IntentFilter intentFilter =
376                new IntentFilter(Intent.ACTION_HEADSET_PLUG);
377
378        intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED);
379        intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
380        intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED);
381        intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
382        intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG);
383        intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG);
384        intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG);
385        intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
386        intentFilter.addAction(Intent.ACTION_SCREEN_ON);
387        intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
388        context.registerReceiver(mReceiver, intentFilter);
389
390        // Register for package removal intent broadcasts for media button receiver persistence
391        IntentFilter pkgFilter = new IntentFilter();
392        pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
393        pkgFilter.addDataScheme("package");
394        context.registerReceiver(mReceiver, pkgFilter);
395
396        // Register for media button intent broadcasts.
397        intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
398        // Workaround for bug on priority setting
399        //intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
400        intentFilter.setPriority(Integer.MAX_VALUE);
401        context.registerReceiver(mMediaButtonReceiver, intentFilter);
402
403        // Register for phone state monitoring
404        TelephonyManager tmgr = (TelephonyManager)
405                context.getSystemService(Context.TELEPHONY_SERVICE);
406        tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
407
408        mUseMasterVolume = context.getResources().getBoolean(
409                com.android.internal.R.bool.config_useMasterVolume);
410        if (mUseMasterVolume) {
411            float volume = Settings.System.getFloat(mContentResolver,
412                    Settings.System.VOLUME_MASTER, -1.0f);
413            if (volume >= 0.0f) {
414                AudioSystem.setMasterVolume(volume);
415            }
416        }
417    }
418
419    private void createAudioSystemThread() {
420        mAudioSystemThread = new AudioSystemThread();
421        mAudioSystemThread.start();
422        waitForAudioHandlerCreation();
423    }
424
425    /** Waits for the volume handler to be created by the other thread. */
426    private void waitForAudioHandlerCreation() {
427        synchronized(this) {
428            while (mAudioHandler == null) {
429                try {
430                    // Wait for mAudioHandler to be set by the other thread
431                    wait();
432                } catch (InterruptedException e) {
433                    Log.e(TAG, "Interrupted while waiting on volume handler.");
434                }
435            }
436        }
437    }
438
439    private void createStreamStates() {
440        int numStreamTypes = AudioSystem.getNumStreamTypes();
441        VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
442
443        for (int i = 0; i < numStreamTypes; i++) {
444            streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
445        }
446
447        // Correct stream index values for streams with aliases
448        for (int i = 0; i < numStreamTypes; i++) {
449            if (STREAM_VOLUME_ALIAS[i] != i) {
450                int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
451                streams[i].mIndex = streams[i].getValidIndex(index);
452                setStreamVolumeIndex(i, index);
453                index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
454                streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
455            }
456        }
457    }
458
459    private void readPersistedSettings() {
460        final ContentResolver cr = mContentResolver;
461
462        mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
463        // sanity check in case the settings are restored from a device with incompatible
464        // ringer modes
465        if (!AudioManager.isValidRingerMode(mRingerMode)) {
466            mRingerMode = AudioManager.RINGER_MODE_NORMAL;
467            System.putInt(cr, System.MODE_RINGER, mRingerMode);
468        }
469
470        mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
471
472        // make sure settings for ringer mode are consistent with device type: non voice capable
473        // devices (tablets) include media stream in silent mode whereas phones don't.
474        mRingerModeAffectedStreams = Settings.System.getInt(cr,
475                Settings.System.MODE_RINGER_STREAMS_AFFECTED,
476                ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
477                 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
478        if (mVoiceCapable) {
479            mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
480        } else {
481            mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
482        }
483        Settings.System.putInt(cr,
484                Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams);
485
486        mMuteAffectedStreams = System.getInt(cr,
487                System.MUTE_STREAMS_AFFECTED,
488                ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
489
490        // Each stream will read its own persisted settings
491
492        // Broadcast the sticky intent
493        broadcastRingerMode();
494
495        // Broadcast vibrate settings
496        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
497        broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
498
499        // Restore the default media button receiver from the system settings
500        restoreMediaButtonReceiver();
501    }
502
503    private void setStreamVolumeIndex(int stream, int index) {
504        AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
505    }
506
507    private int rescaleIndex(int index, int srcStream, int dstStream) {
508        return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
509    }
510
511    ///////////////////////////////////////////////////////////////////////////
512    // IPC methods
513    ///////////////////////////////////////////////////////////////////////////
514
515    /** @see AudioManager#adjustVolume(int, int) */
516    public void adjustVolume(int direction, int flags) {
517        adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
518    }
519
520    /** @see AudioManager#adjustVolume(int, int, int) */
521    public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
522
523        int streamType;
524        if ((flags & AudioManager.FLAG_FORCE_STREAM) != 0) {
525            streamType = suggestedStreamType;
526        } else {
527            streamType = getActiveStreamType(suggestedStreamType);
528        }
529
530        // Play sounds on STREAM_RING only and if lock screen is not on.
531        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 &&
532                ((STREAM_VOLUME_ALIAS[streamType] != AudioSystem.STREAM_RING)
533                 || (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) {
534            flags &= ~AudioManager.FLAG_PLAY_SOUND;
535        }
536
537        adjustStreamVolume(streamType, direction, flags);
538    }
539
540    /** @see AudioManager#adjustStreamVolume(int, int, int) */
541    public void adjustStreamVolume(int streamType, int direction, int flags) {
542        ensureValidDirection(direction);
543        ensureValidStreamType(streamType);
544
545        // use stream type alias here so that streams with same alias have the same behavior,
546        // including with regard to silent mode control (e.g the use of STREAM_RING below and in
547        // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION)
548        int streamTypeAlias = STREAM_VOLUME_ALIAS[streamType];
549        VolumeStreamState streamState = mStreamStates[streamTypeAlias];
550        final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
551        boolean adjustVolume = true;
552
553        // If either the client forces allowing ringer modes for this adjustment,
554        // or the stream type is one that is affected by ringer modes
555        if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
556                streamTypeAlias == AudioSystem.STREAM_RING ||
557                (!mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_MUSIC)) {
558            // do not vibrate if already in vibrate mode
559            if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
560                flags &= ~AudioManager.FLAG_VIBRATE;
561            }
562            // Check if the ringer mode changes with this volume adjustment. If
563            // it does, it will handle adjusting the volume, so we won't below
564            adjustVolume = checkForRingerModeChange(oldIndex, direction, streamTypeAlias);
565        }
566
567        // If stream is muted, adjust last audible index only
568        int index;
569        if (streamState.muteCount() != 0) {
570            if (adjustVolume) {
571                // adjust volume on all stream types sharing the same alias otherwise a query
572                // on last audible index for an alias would not give the correct value
573                int numStreamTypes = AudioSystem.getNumStreamTypes();
574                for (int i = numStreamTypes - 1; i >= 0; i--) {
575                    if (STREAM_VOLUME_ALIAS[i] == streamTypeAlias) {
576                        VolumeStreamState s = mStreamStates[i];
577
578                        s.adjustLastAudibleIndex(direction);
579                        // Post a persist volume msg
580                        sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, i,
581                                SENDMSG_REPLACE, 0, 1, s, PERSIST_DELAY);
582                    }
583                }
584            }
585            index = streamState.mLastAudibleIndex;
586        } else {
587            if (adjustVolume && streamState.adjustIndex(direction)) {
588                // Post message to set system volume (it in turn will post a message
589                // to persist). Do not change volume if stream is muted.
590                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamTypeAlias, SENDMSG_NOOP, 0, 0,
591                        streamState, 0);
592            }
593            index = streamState.mIndex;
594        }
595
596        sendVolumeUpdate(streamType, oldIndex, index, flags);
597    }
598
599    /** @see AudioManager#adjustMasterVolume(int) */
600    public void adjustMasterVolume(int direction, int flags) {
601        ensureValidDirection(direction);
602
603        float volume = AudioSystem.getMasterVolume();
604        Log.d(TAG, "adjustMasterVolume old=" + volume);
605        if (volume >= 0.0) {
606            // get current master volume adjusted to 0 to 100
607            int oldVolume = getMasterVolume();
608            if (direction == AudioManager.ADJUST_RAISE) {
609                volume += MASTER_VOLUME_INCREMENT;
610                if (volume > 1.0f) volume = 1.0f;
611            } else if (direction == AudioManager.ADJUST_LOWER) {
612                volume -= MASTER_VOLUME_INCREMENT;
613                if (volume < 0.0f) volume = 0.0f;
614            }
615            AudioSystem.setMasterVolume(volume);
616            // Post a persist master volume msg
617            sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, 0, SENDMSG_REPLACE,
618                    Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY);
619            sendMasterVolumeUpdate(flags, oldVolume, getMasterVolume());
620        }
621        Log.d(TAG, "adjustMasterVolume new=" + volume);
622    }
623
624    /** @see AudioManager#setStreamVolume(int, int, int) */
625    public void setStreamVolume(int streamType, int index, int flags) {
626        ensureValidStreamType(streamType);
627        VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
628
629        final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
630
631        // setting ring or notifications volume to 0 on voice capable devices enters silent mode
632        if (mVoiceCapable && (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
633                (STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING))) {
634            int newRingerMode = mRingerMode;
635            if (index == 0) {
636                newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
637                    ? AudioManager.RINGER_MODE_VIBRATE
638                    : AudioManager.RINGER_MODE_SILENT;
639                setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
640            } else {
641                newRingerMode = AudioManager.RINGER_MODE_NORMAL;
642            }
643            if (newRingerMode != mRingerMode) {
644                setRingerMode(newRingerMode);
645            }
646        }
647
648        index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
649        setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
650
651        index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
652
653        sendVolumeUpdate(streamType, oldIndex, index, flags);
654    }
655
656    // UI update and Broadcast Intent
657    private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
658        if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
659            streamType = AudioSystem.STREAM_NOTIFICATION;
660        }
661
662        mVolumePanel.postVolumeChanged(streamType, flags);
663
664        oldIndex = (oldIndex + 5) / 10;
665        index = (index + 5) / 10;
666        Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
667        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
668        intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
669        intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
670        mContext.sendBroadcast(intent);
671    }
672
673    // UI update and Broadcast Intent
674    private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) {
675        mVolumePanel.postMasterVolumeChanged(flags);
676
677        Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
678        intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
679        intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
680        mContext.sendBroadcast(intent);
681    }
682
683    // UI update and Broadcast Intent
684    private void sendMasterMuteUpdate(boolean muted, int flags) {
685        mVolumePanel.postMasterMuteChanged(flags);
686
687        Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION);
688        intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
689        long origCallerIdentityToken = Binder.clearCallingIdentity();
690        mContext.sendStickyBroadcast(intent);
691        Binder.restoreCallingIdentity(origCallerIdentityToken);
692    }
693
694    /**
695     * Sets the stream state's index, and posts a message to set system volume.
696     * This will not call out to the UI. Assumes a valid stream type.
697     *
698     * @param streamType Type of the stream
699     * @param index Desired volume index of the stream
700     * @param force If true, set the volume even if the desired volume is same
701     * as the current volume.
702     * @param lastAudible If true, stores new index as last audible one
703     */
704    private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
705        VolumeStreamState streamState = mStreamStates[streamType];
706
707        // If stream is muted, set last audible index only
708        if (streamState.muteCount() != 0) {
709            // Do not allow last audible index to be 0
710            if (index != 0) {
711                streamState.setLastAudibleIndex(index);
712                // Post a persist volume msg
713                sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
714                        SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
715            }
716        } else {
717            if (streamState.setIndex(index, lastAudible) || force) {
718                // Post message to set system volume (it in turn will post a message
719                // to persist).
720                sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
721                        streamState, 0);
722            }
723        }
724    }
725
726    /** @see AudioManager#setStreamSolo(int, boolean) */
727    public void setStreamSolo(int streamType, boolean state, IBinder cb) {
728        for (int stream = 0; stream < mStreamStates.length; stream++) {
729            if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
730            // Bring back last audible volume
731            mStreamStates[stream].mute(cb, state);
732         }
733    }
734
735    /** @see AudioManager#setStreamMute(int, boolean) */
736    public void setStreamMute(int streamType, boolean state, IBinder cb) {
737        if (isStreamAffectedByMute(streamType)) {
738            mStreamStates[streamType].mute(cb, state);
739        }
740    }
741
742    /** get stream mute state. */
743    public boolean isStreamMute(int streamType) {
744        return (mStreamStates[streamType].muteCount() != 0);
745    }
746
747    /** @see AudioManager#setMasterMute(boolean, IBinder) */
748    public void setMasterMute(boolean state, IBinder cb) {
749        AudioSystem.setMasterMute(state);
750        sendMasterMuteUpdate(state, AudioManager.FLAG_SHOW_UI);
751    }
752
753    /** get master mute state. */
754    public boolean isMasterMute() {
755        return AudioSystem.getMasterMute();
756    }
757
758    /** @see AudioManager#getStreamVolume(int) */
759    public int getStreamVolume(int streamType) {
760        ensureValidStreamType(streamType);
761        return (mStreamStates[streamType].mIndex + 5) / 10;
762    }
763
764    public int getMasterVolume() {
765        if (isMasterMute()) return 0;
766        return getLastAudibleMasterVolume();
767    }
768
769    public void setMasterVolume(int volume, int flags) {
770        AudioSystem.setMasterVolume((float)volume / MAX_MASTER_VOLUME);
771    }
772
773    /** @see AudioManager#getStreamMaxVolume(int) */
774    public int getStreamMaxVolume(int streamType) {
775        ensureValidStreamType(streamType);
776        return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
777    }
778
779    public int getMasterMaxVolume() {
780        return MAX_MASTER_VOLUME;
781    }
782
783    /** Get last audible volume before stream was muted. */
784    public int getLastAudibleStreamVolume(int streamType) {
785        ensureValidStreamType(streamType);
786        return (mStreamStates[streamType].mLastAudibleIndex + 5) / 10;
787    }
788
789    /** Get last audible master volume before it was muted. */
790    public int getLastAudibleMasterVolume() {
791        return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME);
792    }
793
794    /** @see AudioManager#getRingerMode() */
795    public int getRingerMode() {
796        return mRingerMode;
797    }
798
799    /** @see AudioManager#setRingerMode(int) */
800    public void setRingerMode(int ringerMode) {
801        synchronized (mSettingsLock) {
802            if (ringerMode != mRingerMode) {
803                setRingerModeInt(ringerMode, true);
804                // Send sticky broadcast
805                broadcastRingerMode();
806            }
807        }
808    }
809
810    private void setRingerModeInt(int ringerMode, boolean persist) {
811        mRingerMode = ringerMode;
812
813        // Mute stream if not previously muted by ringer mode and ringer mode
814        // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
815        // Unmute stream if previously muted by ringer mode and ringer mode
816        // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
817        int numStreamTypes = AudioSystem.getNumStreamTypes();
818        for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
819            if (isStreamMutedByRingerMode(streamType)) {
820                if (!isStreamAffectedByRingerMode(streamType) ||
821                    mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
822                    // ring and notifications volume should never be 0 when not silenced
823                    // on voice capable devices
824                    if (mVoiceCapable &&
825                            STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING &&
826                            mStreamStates[streamType].mLastAudibleIndex == 0) {
827                        mStreamStates[streamType].mLastAudibleIndex = 10;
828                    }
829                    mStreamStates[streamType].mute(null, false);
830                    mRingerModeMutedStreams &= ~(1 << streamType);
831                }
832            } else {
833                if (isStreamAffectedByRingerMode(streamType) &&
834                    mRingerMode != AudioManager.RINGER_MODE_NORMAL) {
835                   mStreamStates[streamType].mute(null, true);
836                   mRingerModeMutedStreams |= (1 << streamType);
837               }
838            }
839        }
840
841        // Post a persist ringer mode msg
842        if (persist) {
843            sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
844                    SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
845        }
846    }
847
848    /** @see AudioManager#shouldVibrate(int) */
849    public boolean shouldVibrate(int vibrateType) {
850
851        switch (getVibrateSetting(vibrateType)) {
852
853            case AudioManager.VIBRATE_SETTING_ON:
854                return mRingerMode != AudioManager.RINGER_MODE_SILENT;
855
856            case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
857                return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
858
859            case AudioManager.VIBRATE_SETTING_OFF:
860                // return false, even for incoming calls
861                return false;
862
863            default:
864                return false;
865        }
866    }
867
868    /** @see AudioManager#getVibrateSetting(int) */
869    public int getVibrateSetting(int vibrateType) {
870        return (mVibrateSetting >> (vibrateType * 2)) & 3;
871    }
872
873    /** @see AudioManager#setVibrateSetting(int, int) */
874    public void setVibrateSetting(int vibrateType, int vibrateSetting) {
875
876        mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
877
878        // Broadcast change
879        broadcastVibrateSetting(vibrateType);
880
881        // Post message to set ringer mode (it in turn will post a message
882        // to persist)
883        sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
884                null, 0);
885    }
886
887    /**
888     * @see #setVibrateSetting(int, int)
889     */
890    public static int getValueForVibrateSetting(int existingValue, int vibrateType,
891            int vibrateSetting) {
892
893        // First clear the existing setting. Each vibrate type has two bits in
894        // the value. Note '3' is '11' in binary.
895        existingValue &= ~(3 << (vibrateType * 2));
896
897        // Set into the old value
898        existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
899
900        return existingValue;
901    }
902
903    private class SetModeDeathHandler implements IBinder.DeathRecipient {
904        private IBinder mCb; // To be notified of client's death
905        private int mPid;
906        private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
907
908        SetModeDeathHandler(IBinder cb, int pid) {
909            mCb = cb;
910            mPid = pid;
911        }
912
913        public void binderDied() {
914            int newModeOwnerPid = 0;
915            synchronized(mSetModeDeathHandlers) {
916                Log.w(TAG, "setMode() client died");
917                int index = mSetModeDeathHandlers.indexOf(this);
918                if (index < 0) {
919                    Log.w(TAG, "unregistered setMode() client died");
920                } else {
921                    newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid);
922                }
923            }
924            // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
925            // SCO connections not started by the application changing the mode
926            if (newModeOwnerPid != 0) {
927                 disconnectBluetoothSco(newModeOwnerPid);
928            }
929        }
930
931        public int getPid() {
932            return mPid;
933        }
934
935        public void setMode(int mode) {
936            mMode = mode;
937        }
938
939        public int getMode() {
940            return mMode;
941        }
942
943        public IBinder getBinder() {
944            return mCb;
945        }
946    }
947
948    /** @see AudioManager#setMode(int) */
949    public void setMode(int mode, IBinder cb) {
950        if (!checkAudioSettingsPermission("setMode()")) {
951            return;
952        }
953
954        if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) {
955            return;
956        }
957
958        int newModeOwnerPid = 0;
959        synchronized(mSetModeDeathHandlers) {
960            if (mode == AudioSystem.MODE_CURRENT) {
961                mode = mMode;
962            }
963            newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid());
964        }
965        // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all
966        // SCO connections not started by the application changing the mode
967        if (newModeOwnerPid != 0) {
968             disconnectBluetoothSco(newModeOwnerPid);
969        }
970    }
971
972    // must be called synchronized on mSetModeDeathHandlers
973    // setModeInt() returns a valid PID if the audio mode was successfully set to
974    // any mode other than NORMAL.
975    int setModeInt(int mode, IBinder cb, int pid) {
976        int newModeOwnerPid = 0;
977        if (cb == null) {
978            Log.e(TAG, "setModeInt() called with null binder");
979            return newModeOwnerPid;
980        }
981
982        SetModeDeathHandler hdlr = null;
983        Iterator iter = mSetModeDeathHandlers.iterator();
984        while (iter.hasNext()) {
985            SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
986            if (h.getPid() == pid) {
987                hdlr = h;
988                // Remove from client list so that it is re-inserted at top of list
989                iter.remove();
990                hdlr.getBinder().unlinkToDeath(hdlr, 0);
991                break;
992            }
993        }
994        int status = AudioSystem.AUDIO_STATUS_OK;
995        do {
996            if (mode == AudioSystem.MODE_NORMAL) {
997                // get new mode from client at top the list if any
998                if (!mSetModeDeathHandlers.isEmpty()) {
999                    hdlr = mSetModeDeathHandlers.get(0);
1000                    cb = hdlr.getBinder();
1001                    mode = hdlr.getMode();
1002                }
1003            } else {
1004                if (hdlr == null) {
1005                    hdlr = new SetModeDeathHandler(cb, pid);
1006                }
1007                // Register for client death notification
1008                try {
1009                    cb.linkToDeath(hdlr, 0);
1010                } catch (RemoteException e) {
1011                    // Client has died!
1012                    Log.w(TAG, "setMode() could not link to "+cb+" binder death");
1013                }
1014
1015                // Last client to call setMode() is always at top of client list
1016                // as required by SetModeDeathHandler.binderDied()
1017                mSetModeDeathHandlers.add(0, hdlr);
1018                hdlr.setMode(mode);
1019            }
1020
1021            if (mode != mMode) {
1022                status = AudioSystem.setPhoneState(mode);
1023                if (status == AudioSystem.AUDIO_STATUS_OK) {
1024                    // automatically handle audio focus for mode changes
1025                    handleFocusForCalls(mMode, mode, cb);
1026                    mMode = mode;
1027                } else {
1028                    if (hdlr != null) {
1029                        mSetModeDeathHandlers.remove(hdlr);
1030                        cb.unlinkToDeath(hdlr, 0);
1031                    }
1032                    // force reading new top of mSetModeDeathHandlers stack
1033                    mode = AudioSystem.MODE_NORMAL;
1034                }
1035            } else {
1036                status = AudioSystem.AUDIO_STATUS_OK;
1037            }
1038        } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty());
1039
1040        if (status == AudioSystem.AUDIO_STATUS_OK) {
1041            if (mode != AudioSystem.MODE_NORMAL) {
1042                if (mSetModeDeathHandlers.isEmpty()) {
1043                    Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack");
1044                } else {
1045                    newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid();
1046                }
1047            }
1048            int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
1049            int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
1050            setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false);
1051        }
1052        return newModeOwnerPid;
1053    }
1054
1055    /** pre-condition: oldMode != newMode */
1056    private void handleFocusForCalls(int oldMode, int newMode, IBinder cb) {
1057        // if ringing
1058        if (newMode == AudioSystem.MODE_RINGTONE) {
1059            // if not ringing silently
1060            int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
1061            if (ringVolume > 0) {
1062                // request audio focus for the communication focus entry
1063                requestAudioFocus(AudioManager.STREAM_RING,
1064                        AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
1065                        null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
1066                        IN_VOICE_COMM_FOCUS_ID /*clientId*/,
1067                        "system");
1068
1069            }
1070        }
1071        // if entering call
1072        else if ((newMode == AudioSystem.MODE_IN_CALL)
1073                || (newMode == AudioSystem.MODE_IN_COMMUNICATION)) {
1074            // request audio focus for the communication focus entry
1075            // (it's ok if focus was already requested during ringing)
1076            requestAudioFocus(AudioManager.STREAM_RING,
1077                    AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb,
1078                    null /* IAudioFocusDispatcher allowed to be null only for this clientId */,
1079                    IN_VOICE_COMM_FOCUS_ID /*clientId*/,
1080                    "system");
1081        }
1082        // if exiting call
1083        else if (newMode == AudioSystem.MODE_NORMAL) {
1084            // abandon audio focus for communication focus entry
1085            abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
1086        }
1087    }
1088
1089    /** @see AudioManager#getMode() */
1090    public int getMode() {
1091        return mMode;
1092    }
1093
1094    /** @see AudioManager#playSoundEffect(int) */
1095    public void playSoundEffect(int effectType) {
1096        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
1097                effectType, -1, null, 0);
1098    }
1099
1100    /** @see AudioManager#playSoundEffect(int, float) */
1101    public void playSoundEffectVolume(int effectType, float volume) {
1102        loadSoundEffects();
1103        sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
1104                effectType, (int) (volume * 1000), null, 0);
1105    }
1106
1107    /**
1108     * Loads samples into the soundpool.
1109     * This method must be called at when sound effects are enabled
1110     */
1111    public boolean loadSoundEffects() {
1112        int status;
1113
1114        synchronized (mSoundEffectsLock) {
1115            if (!mBootCompleted) {
1116                Log.w(TAG, "loadSoundEffects() called before boot complete");
1117                return false;
1118            }
1119
1120            if (mSoundPool != null) {
1121                return true;
1122            }
1123            mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
1124            if (mSoundPool == null) {
1125                Log.w(TAG, "loadSoundEffects() could not allocate sound pool");
1126                return false;
1127            }
1128
1129            try {
1130                mSoundPoolCallBack = null;
1131                mSoundPoolListenerThread = new SoundPoolListenerThread();
1132                mSoundPoolListenerThread.start();
1133                // Wait for mSoundPoolCallBack to be set by the other thread
1134                mSoundEffectsLock.wait();
1135            } catch (InterruptedException e) {
1136                Log.w(TAG, "Interrupted while waiting sound pool listener thread.");
1137            }
1138
1139            if (mSoundPoolCallBack == null) {
1140                Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread");
1141                if (mSoundPoolLooper != null) {
1142                    mSoundPoolLooper.quit();
1143                    mSoundPoolLooper = null;
1144                }
1145                mSoundPoolListenerThread = null;
1146                mSoundPool.release();
1147                mSoundPool = null;
1148                return false;
1149            }
1150            /*
1151             * poolId table: The value -1 in this table indicates that corresponding
1152             * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
1153             * Once loaded, the value in poolId is the sample ID and the same
1154             * sample can be reused for another effect using the same file.
1155             */
1156            int[] poolId = new int[SOUND_EFFECT_FILES.length];
1157            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1158                poolId[fileIdx] = -1;
1159            }
1160            /*
1161             * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
1162             * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
1163             * this indicates we have a valid sample loaded for this effect.
1164             */
1165
1166            int lastSample = 0;
1167            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1168                // Do not load sample if this effect uses the MediaPlayer
1169                if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
1170                    continue;
1171                }
1172                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
1173                    String filePath = Environment.getRootDirectory()
1174                            + SOUND_EFFECTS_PATH
1175                            + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
1176                    int sampleId = mSoundPool.load(filePath, 0);
1177                    if (sampleId <= 0) {
1178                        Log.w(TAG, "Soundpool could not load file: "+filePath);
1179                    } else {
1180                        SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
1181                        poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
1182                        lastSample = sampleId;
1183                    }
1184                } else {
1185                    SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
1186                }
1187            }
1188            // wait for all samples to be loaded
1189            if (lastSample != 0) {
1190                mSoundPoolCallBack.setLastSample(lastSample);
1191
1192                try {
1193                    mSoundEffectsLock.wait();
1194                    status = mSoundPoolCallBack.status();
1195                } catch (java.lang.InterruptedException e) {
1196                    Log.w(TAG, "Interrupted while waiting sound pool callback.");
1197                    status = -1;
1198                }
1199            } else {
1200                status = -1;
1201            }
1202
1203            if (mSoundPoolLooper != null) {
1204                mSoundPoolLooper.quit();
1205                mSoundPoolLooper = null;
1206            }
1207            mSoundPoolListenerThread = null;
1208            if (status != 0) {
1209                Log.w(TAG,
1210                        "loadSoundEffects(), Error "
1211                                + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1)
1212                                + " while loading samples");
1213                for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1214                    if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) {
1215                        SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1216                    }
1217                }
1218
1219                mSoundPool.release();
1220                mSoundPool = null;
1221            }
1222        }
1223        return (status == 0);
1224    }
1225
1226    /**
1227     *  Unloads samples from the sound pool.
1228     *  This method can be called to free some memory when
1229     *  sound effects are disabled.
1230     */
1231    public void unloadSoundEffects() {
1232        synchronized (mSoundEffectsLock) {
1233            if (mSoundPool == null) {
1234                return;
1235            }
1236
1237            mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS);
1238            mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT);
1239
1240            int[] poolId = new int[SOUND_EFFECT_FILES.length];
1241            for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
1242                poolId[fileIdx] = 0;
1243            }
1244
1245            for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
1246                if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
1247                    continue;
1248                }
1249                if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
1250                    mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
1251                    SOUND_EFFECT_FILES_MAP[effect][1] = -1;
1252                    poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
1253                }
1254            }
1255            mSoundPool.release();
1256            mSoundPool = null;
1257        }
1258    }
1259
1260    class SoundPoolListenerThread extends Thread {
1261        public SoundPoolListenerThread() {
1262            super("SoundPoolListenerThread");
1263        }
1264
1265        @Override
1266        public void run() {
1267
1268            Looper.prepare();
1269            mSoundPoolLooper = Looper.myLooper();
1270
1271            synchronized (mSoundEffectsLock) {
1272                if (mSoundPool != null) {
1273                    mSoundPoolCallBack = new SoundPoolCallback();
1274                    mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack);
1275                }
1276                mSoundEffectsLock.notify();
1277            }
1278            Looper.loop();
1279        }
1280    }
1281
1282    private final class SoundPoolCallback implements
1283            android.media.SoundPool.OnLoadCompleteListener {
1284
1285        int mStatus;
1286        int mLastSample;
1287
1288        public int status() {
1289            return mStatus;
1290        }
1291
1292        public void setLastSample(int sample) {
1293            mLastSample = sample;
1294        }
1295
1296        public void onLoadComplete(SoundPool soundPool, int sampleId, int status) {
1297            synchronized (mSoundEffectsLock) {
1298                if (status != 0) {
1299                    mStatus = status;
1300                }
1301                if (sampleId == mLastSample) {
1302                    mSoundEffectsLock.notify();
1303                }
1304            }
1305        }
1306    }
1307
1308    /** @see AudioManager#reloadAudioSettings() */
1309    public void reloadAudioSettings() {
1310        // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
1311        readPersistedSettings();
1312
1313        // restore volume settings
1314        int numStreamTypes = AudioSystem.getNumStreamTypes();
1315        for (int streamType = 0; streamType < numStreamTypes; streamType++) {
1316            VolumeStreamState streamState = mStreamStates[streamType];
1317
1318            String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
1319            String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1320            int index = Settings.System.getInt(mContentResolver,
1321                                           settingName,
1322                                           AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1323            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
1324                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
1325            } else {
1326                index *= 10;
1327            }
1328            streamState.mIndex = streamState.getValidIndex(index);
1329
1330            index = (index + 5) / 10;
1331            index = Settings.System.getInt(mContentResolver,
1332                                            lastAudibleSettingName,
1333                                            (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1334            if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
1335                index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
1336            } else {
1337                index *= 10;
1338            }
1339            streamState.mLastAudibleIndex = streamState.getValidIndex(index);
1340
1341            // unmute stream that was muted but is not affect by mute anymore
1342            if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
1343                int size = streamState.mDeathHandlers.size();
1344                for (int i = 0; i < size; i++) {
1345                    streamState.mDeathHandlers.get(i).mMuteCount = 1;
1346                    streamState.mDeathHandlers.get(i).mute(false);
1347                }
1348            }
1349            // apply stream volume
1350            if (streamState.muteCount() == 0) {
1351                setStreamVolumeIndex(streamType, streamState.mIndex);
1352            }
1353        }
1354
1355        // apply new ringer mode
1356        setRingerModeInt(getRingerMode(), false);
1357    }
1358
1359    /** @see AudioManager#setSpeakerphoneOn() */
1360    public void setSpeakerphoneOn(boolean on){
1361        if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
1362            return;
1363        }
1364        mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE;
1365
1366        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1367                AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
1368    }
1369
1370    /** @see AudioManager#isSpeakerphoneOn() */
1371    public boolean isSpeakerphoneOn() {
1372        return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER);
1373    }
1374
1375    /** @see AudioManager#setBluetoothScoOn() */
1376    public void setBluetoothScoOn(boolean on){
1377        if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
1378            return;
1379        }
1380        mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE;
1381
1382        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1383                AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0);
1384        sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE,
1385                AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0);
1386    }
1387
1388    /** @see AudioManager#isBluetoothScoOn() */
1389    public boolean isBluetoothScoOn() {
1390        return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO);
1391    }
1392
1393    /** @see AudioManager#startBluetoothSco() */
1394    public void startBluetoothSco(IBinder cb){
1395        if (!checkAudioSettingsPermission("startBluetoothSco()") ||
1396                !mBootCompleted) {
1397            return;
1398        }
1399        ScoClient client = getScoClient(cb, true);
1400        client.incCount();
1401    }
1402
1403    /** @see AudioManager#stopBluetoothSco() */
1404    public void stopBluetoothSco(IBinder cb){
1405        if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
1406                !mBootCompleted) {
1407            return;
1408        }
1409        ScoClient client = getScoClient(cb, false);
1410        if (client != null) {
1411            client.decCount();
1412        }
1413    }
1414
1415    private class ScoClient implements IBinder.DeathRecipient {
1416        private IBinder mCb; // To be notified of client's death
1417        private int mCreatorPid;
1418        private int mStartcount; // number of SCO connections started by this client
1419
1420        ScoClient(IBinder cb) {
1421            mCb = cb;
1422            mCreatorPid = Binder.getCallingPid();
1423            mStartcount = 0;
1424        }
1425
1426        public void binderDied() {
1427            synchronized(mScoClients) {
1428                Log.w(TAG, "SCO client died");
1429                int index = mScoClients.indexOf(this);
1430                if (index < 0) {
1431                    Log.w(TAG, "unregistered SCO client died");
1432                } else {
1433                    clearCount(true);
1434                    mScoClients.remove(this);
1435                }
1436            }
1437        }
1438
1439        public void incCount() {
1440            synchronized(mScoClients) {
1441                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
1442                if (mStartcount == 0) {
1443                    try {
1444                        mCb.linkToDeath(this, 0);
1445                    } catch (RemoteException e) {
1446                        // client has already died!
1447                        Log.w(TAG, "ScoClient  incCount() could not link to "+mCb+" binder death");
1448                    }
1449                }
1450                mStartcount++;
1451            }
1452        }
1453
1454        public void decCount() {
1455            synchronized(mScoClients) {
1456                if (mStartcount == 0) {
1457                    Log.w(TAG, "ScoClient.decCount() already 0");
1458                } else {
1459                    mStartcount--;
1460                    if (mStartcount == 0) {
1461                        try {
1462                            mCb.unlinkToDeath(this, 0);
1463                        } catch (NoSuchElementException e) {
1464                            Log.w(TAG, "decCount() going to 0 but not registered to binder");
1465                        }
1466                    }
1467                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1468                }
1469            }
1470        }
1471
1472        public void clearCount(boolean stopSco) {
1473            synchronized(mScoClients) {
1474                if (mStartcount != 0) {
1475                    try {
1476                        mCb.unlinkToDeath(this, 0);
1477                    } catch (NoSuchElementException e) {
1478                        Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
1479                    }
1480                }
1481                mStartcount = 0;
1482                if (stopSco) {
1483                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1484                }
1485            }
1486        }
1487
1488        public int getCount() {
1489            return mStartcount;
1490        }
1491
1492        public IBinder getBinder() {
1493            return mCb;
1494        }
1495
1496        public int getPid() {
1497            return mCreatorPid;
1498        }
1499
1500        public int totalCount() {
1501            synchronized(mScoClients) {
1502                int count = 0;
1503                int size = mScoClients.size();
1504                for (int i = 0; i < size; i++) {
1505                    count += mScoClients.get(i).getCount();
1506                }
1507                return count;
1508            }
1509        }
1510
1511        private void requestScoState(int state) {
1512            checkScoAudioState();
1513            if (totalCount() == 0) {
1514                if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
1515                    // Make sure that the state transitions to CONNECTING even if we cannot initiate
1516                    // the connection.
1517                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
1518                    // Accept SCO audio activation only in NORMAL audio mode or if the mode is
1519                    // currently controlled by the same client process.
1520                    synchronized(mSetModeDeathHandlers) {
1521                        if ((mSetModeDeathHandlers.isEmpty() ||
1522                                mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
1523                                (mScoAudioState == SCO_STATE_INACTIVE ||
1524                                 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
1525                            if (mScoAudioState == SCO_STATE_INACTIVE) {
1526                                if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1527                                    if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1528                                            mBluetoothHeadsetDevice)) {
1529                                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1530                                    } else {
1531                                        broadcastScoConnectionState(
1532                                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1533                                    }
1534                                } else if (getBluetoothHeadset()) {
1535                                    mScoAudioState = SCO_STATE_ACTIVATE_REQ;
1536                                }
1537                            } else {
1538                                mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1539                                broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
1540                            }
1541                        } else {
1542                            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1543                        }
1544                    }
1545                } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
1546                              (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
1547                               mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
1548                    if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
1549                        if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1550                            if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
1551                                    mBluetoothHeadsetDevice)) {
1552                                mScoAudioState = SCO_STATE_INACTIVE;
1553                                broadcastScoConnectionState(
1554                                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1555                            }
1556                        } else if (getBluetoothHeadset()) {
1557                            mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
1558                        }
1559                    } else {
1560                        mScoAudioState = SCO_STATE_INACTIVE;
1561                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1562                    }
1563                }
1564            }
1565        }
1566    }
1567
1568    private void checkScoAudioState() {
1569        if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
1570                mScoAudioState == SCO_STATE_INACTIVE &&
1571                mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
1572                != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1573            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
1574        }
1575    }
1576
1577    private ScoClient getScoClient(IBinder cb, boolean create) {
1578        synchronized(mScoClients) {
1579            ScoClient client = null;
1580            int size = mScoClients.size();
1581            for (int i = 0; i < size; i++) {
1582                client = mScoClients.get(i);
1583                if (client.getBinder() == cb)
1584                    return client;
1585            }
1586            if (create) {
1587                client = new ScoClient(cb);
1588                mScoClients.add(client);
1589            }
1590            return client;
1591        }
1592    }
1593
1594    public void clearAllScoClients(int exceptPid, boolean stopSco) {
1595        synchronized(mScoClients) {
1596            ScoClient savedClient = null;
1597            int size = mScoClients.size();
1598            for (int i = 0; i < size; i++) {
1599                ScoClient cl = mScoClients.get(i);
1600                if (cl.getPid() != exceptPid) {
1601                    cl.clearCount(stopSco);
1602                } else {
1603                    savedClient = cl;
1604                }
1605            }
1606            mScoClients.clear();
1607            if (savedClient != null) {
1608                mScoClients.add(savedClient);
1609            }
1610        }
1611    }
1612
1613    private boolean getBluetoothHeadset() {
1614        boolean result = false;
1615        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1616        if (adapter != null) {
1617            result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
1618                                    BluetoothProfile.HEADSET);
1619        }
1620        // If we could not get a bluetooth headset proxy, send a failure message
1621        // without delay to reset the SCO audio state and clear SCO clients.
1622        // If we could get a proxy, send a delayed failure message that will reset our state
1623        // in case we don't receive onServiceConnected().
1624        sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
1625                SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
1626        return result;
1627    }
1628
1629    private void disconnectBluetoothSco(int exceptPid) {
1630        synchronized(mScoClients) {
1631            checkScoAudioState();
1632            if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
1633                    mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
1634                if (mBluetoothHeadsetDevice != null) {
1635                    if (mBluetoothHeadset != null) {
1636                        if (!mBluetoothHeadset.stopVoiceRecognition(
1637                                mBluetoothHeadsetDevice)) {
1638                            sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
1639                                    SENDMSG_REPLACE, 0, 0, null, 0);
1640                        }
1641                    } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
1642                            getBluetoothHeadset()) {
1643                        mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
1644                    }
1645                }
1646            } else {
1647                clearAllScoClients(exceptPid, true);
1648            }
1649        }
1650    }
1651
1652    private void resetBluetoothSco() {
1653        synchronized(mScoClients) {
1654            clearAllScoClients(0, false);
1655            mScoAudioState = SCO_STATE_INACTIVE;
1656            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1657        }
1658    }
1659
1660    private void broadcastScoConnectionState(int state) {
1661        if (state != mScoConnectionState) {
1662            Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
1663            newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
1664            newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
1665                    mScoConnectionState);
1666            mContext.sendStickyBroadcast(newIntent);
1667            mScoConnectionState = state;
1668        }
1669    }
1670
1671    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
1672        new BluetoothProfile.ServiceListener() {
1673        public void onServiceConnected(int profile, BluetoothProfile proxy) {
1674            BluetoothDevice btDevice;
1675            List<BluetoothDevice> deviceList;
1676            switch(profile) {
1677            case BluetoothProfile.A2DP:
1678                BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
1679                deviceList = a2dp.getConnectedDevices();
1680                if (deviceList.size() > 0) {
1681                    btDevice = deviceList.get(0);
1682                    handleA2dpConnectionStateChange(btDevice, a2dp.getConnectionState(btDevice));
1683                }
1684                break;
1685
1686            case BluetoothProfile.HEADSET:
1687                synchronized (mScoClients) {
1688                    // Discard timeout message
1689                    mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
1690                    mBluetoothHeadset = (BluetoothHeadset) proxy;
1691                    deviceList = mBluetoothHeadset.getConnectedDevices();
1692                    if (deviceList.size() > 0) {
1693                        mBluetoothHeadsetDevice = deviceList.get(0);
1694                    } else {
1695                        mBluetoothHeadsetDevice = null;
1696                    }
1697                    // Refresh SCO audio state
1698                    checkScoAudioState();
1699                    // Continue pending action if any
1700                    if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
1701                            mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
1702                            mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
1703                        boolean status = false;
1704                        if (mBluetoothHeadsetDevice != null) {
1705                            switch (mScoAudioState) {
1706                            case SCO_STATE_ACTIVATE_REQ:
1707                                mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1708                                status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1709                                        mBluetoothHeadsetDevice);
1710                                break;
1711                            case SCO_STATE_DEACTIVATE_REQ:
1712                                status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
1713                                        mBluetoothHeadsetDevice);
1714                                break;
1715                            case SCO_STATE_DEACTIVATE_EXT_REQ:
1716                                status = mBluetoothHeadset.stopVoiceRecognition(
1717                                        mBluetoothHeadsetDevice);
1718                            }
1719                        }
1720                        if (!status) {
1721                            sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0,
1722                                    SENDMSG_REPLACE, 0, 0, null, 0);
1723                        }
1724                    }
1725                }
1726                break;
1727
1728            default:
1729                break;
1730            }
1731        }
1732        public void onServiceDisconnected(int profile) {
1733            switch(profile) {
1734            case BluetoothProfile.A2DP:
1735                synchronized (mConnectedDevices) {
1736                    if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
1737                        makeA2dpDeviceUnavailableNow(
1738                                mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
1739                    }
1740                }
1741                break;
1742
1743            case BluetoothProfile.HEADSET:
1744                synchronized (mScoClients) {
1745                    mBluetoothHeadset = null;
1746                }
1747                break;
1748
1749            default:
1750                break;
1751            }
1752        }
1753    };
1754
1755    ///////////////////////////////////////////////////////////////////////////
1756    // Internal methods
1757    ///////////////////////////////////////////////////////////////////////////
1758
1759    /**
1760     * Checks if the adjustment should change ringer mode instead of just
1761     * adjusting volume. If so, this will set the proper ringer mode and volume
1762     * indices on the stream states.
1763     */
1764    private boolean checkForRingerModeChange(int oldIndex, int direction, int streamType) {
1765        boolean adjustVolumeIndex = true;
1766        int newRingerMode = mRingerMode;
1767        int uiIndex = (oldIndex + 5) / 10;
1768        boolean vibeInSilent = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1;
1769
1770        if (mRingerMode == RINGER_MODE_NORMAL) {
1771            if ((direction == AudioManager.ADJUST_LOWER) && (uiIndex <= 1)) {
1772                // enter silent mode if current index is the last audible one and not repeating a
1773                // volume key down
1774                if (vibeInSilent || mPrevVolDirection != AudioManager.ADJUST_LOWER) {
1775                    // "silent mode", but which one?
1776                    newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_SILENT;
1777                }
1778                if (uiIndex == 0 ||
1779                        (!vibeInSilent &&
1780                         mPrevVolDirection == AudioManager.ADJUST_LOWER &&
1781                         mVoiceCapable && streamType == AudioSystem.STREAM_RING)) {
1782                    adjustVolumeIndex = false;
1783                }
1784            }
1785        } else if (mRingerMode == RINGER_MODE_VIBRATE) {
1786            if ((direction == AudioManager.ADJUST_LOWER)) {
1787                // Set it to silent, if it wasn't a long-press
1788                if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
1789                    newRingerMode = RINGER_MODE_SILENT;
1790                }
1791            } else if (direction == AudioManager.ADJUST_RAISE) {
1792                newRingerMode = RINGER_MODE_NORMAL;
1793            }
1794            adjustVolumeIndex = false;
1795        } else {
1796            if (direction == AudioManager.ADJUST_RAISE) {
1797                // exiting silent mode
1798                // If VIBRATE_IN_SILENT, then go into vibrate mode
1799                newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_NORMAL;
1800            }
1801            adjustVolumeIndex = false;
1802        }
1803
1804        if (newRingerMode != mRingerMode) {
1805            setRingerMode(newRingerMode);
1806        }
1807
1808        mPrevVolDirection = direction;
1809
1810        return adjustVolumeIndex;
1811    }
1812
1813    public boolean isStreamAffectedByRingerMode(int streamType) {
1814        return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
1815    }
1816
1817    private boolean isStreamMutedByRingerMode(int streamType) {
1818        return (mRingerModeMutedStreams & (1 << streamType)) != 0;
1819    }
1820
1821    public boolean isStreamAffectedByMute(int streamType) {
1822        return (mMuteAffectedStreams & (1 << streamType)) != 0;
1823    }
1824
1825    private void ensureValidDirection(int direction) {
1826        if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
1827            throw new IllegalArgumentException("Bad direction " + direction);
1828        }
1829    }
1830
1831    private void ensureValidStreamType(int streamType) {
1832        if (streamType < 0 || streamType >= mStreamStates.length) {
1833            throw new IllegalArgumentException("Bad stream type " + streamType);
1834        }
1835    }
1836
1837    private int getActiveStreamType(int suggestedStreamType) {
1838
1839        if (mVoiceCapable) {
1840            boolean isOffhook = false;
1841            try {
1842                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1843                if (phone != null) isOffhook = phone.isOffhook();
1844            } catch (RemoteException e) {
1845                Log.w(TAG, "Couldn't connect to phone service", e);
1846            }
1847
1848            if (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION) {
1849                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
1850                        == AudioSystem.FORCE_BT_SCO) {
1851                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1852                    return AudioSystem.STREAM_BLUETOOTH_SCO;
1853                } else {
1854                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1855                    return AudioSystem.STREAM_VOICE_CALL;
1856                }
1857            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
1858                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
1859                return AudioSystem.STREAM_MUSIC;
1860            } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
1861                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."
1862                //        + " b/c USE_DEFAULT_STREAM_TYPE...");
1863                return AudioSystem.STREAM_RING;
1864            } else {
1865                // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1866                return suggestedStreamType;
1867            }
1868        } else {
1869            if (getMode() == AudioManager.MODE_IN_COMMUNICATION) {
1870                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
1871                        == AudioSystem.FORCE_BT_SCO) {
1872                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1873                    return AudioSystem.STREAM_BLUETOOTH_SCO;
1874                } else {
1875                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1876                    return AudioSystem.STREAM_VOICE_CALL;
1877                }
1878            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
1879                            NOTIFICATION_VOLUME_DELAY_MS) ||
1880                       AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
1881                            NOTIFICATION_VOLUME_DELAY_MS)) {
1882                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION...");
1883                return AudioSystem.STREAM_NOTIFICATION;
1884            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) ||
1885                       (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) {
1886                // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC "
1887                //        + " b/c USE_DEFAULT_STREAM_TYPE...");
1888                return AudioSystem.STREAM_MUSIC;
1889            } else {
1890                // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1891                return suggestedStreamType;
1892            }
1893        }
1894    }
1895
1896    private void broadcastRingerMode() {
1897        // Send sticky broadcast
1898        Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
1899        broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
1900        broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1901                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1902        long origCallerIdentityToken = Binder.clearCallingIdentity();
1903        mContext.sendStickyBroadcast(broadcast);
1904        Binder.restoreCallingIdentity(origCallerIdentityToken);
1905    }
1906
1907    private void broadcastVibrateSetting(int vibrateType) {
1908        // Send broadcast
1909        if (ActivityManagerNative.isSystemReady()) {
1910            Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
1911            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
1912            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
1913            mContext.sendBroadcast(broadcast);
1914        }
1915    }
1916
1917    // Message helper methods
1918    private static int getMsg(int baseMsg, int streamType) {
1919        return (baseMsg & 0xffff) | streamType << 16;
1920    }
1921
1922    private static int getMsgBase(int msg) {
1923        return msg & 0xffff;
1924    }
1925
1926    private static void sendMsg(Handler handler, int baseMsg, int streamType,
1927            int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
1928        int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
1929
1930        if (existingMsgPolicy == SENDMSG_REPLACE) {
1931            handler.removeMessages(msg);
1932        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
1933            return;
1934        }
1935
1936        handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
1937    }
1938
1939    boolean checkAudioSettingsPermission(String method) {
1940        if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
1941                == PackageManager.PERMISSION_GRANTED) {
1942            return true;
1943        }
1944        String msg = "Audio Settings Permission Denial: " + method + " from pid="
1945                + Binder.getCallingPid()
1946                + ", uid=" + Binder.getCallingUid();
1947        Log.w(TAG, msg);
1948        return false;
1949    }
1950
1951
1952    ///////////////////////////////////////////////////////////////////////////
1953    // Inner classes
1954    ///////////////////////////////////////////////////////////////////////////
1955
1956    public class VolumeStreamState {
1957        private final int mStreamType;
1958
1959        private String mVolumeIndexSettingName;
1960        private String mLastAudibleVolumeIndexSettingName;
1961        private int mIndexMax;
1962        private int mIndex;
1963        private int mLastAudibleIndex;
1964        private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
1965
1966        private VolumeStreamState(String settingName, int streamType) {
1967
1968            setVolumeIndexSettingName(settingName);
1969
1970            mStreamType = streamType;
1971
1972            final ContentResolver cr = mContentResolver;
1973            mIndexMax = MAX_STREAM_VOLUME[streamType];
1974            mIndex = Settings.System.getInt(cr,
1975                                            mVolumeIndexSettingName,
1976                                            AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1977            mLastAudibleIndex = Settings.System.getInt(cr,
1978                                                       mLastAudibleVolumeIndexSettingName,
1979                                                       (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1980            AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
1981            mIndexMax *= 10;
1982            mIndex = getValidIndex(10 * mIndex);
1983            mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
1984            setStreamVolumeIndex(streamType, mIndex);
1985            mDeathHandlers = new ArrayList<VolumeDeathHandler>();
1986        }
1987
1988        public void setVolumeIndexSettingName(String settingName) {
1989            mVolumeIndexSettingName = settingName;
1990            mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1991        }
1992
1993        public boolean adjustIndex(int deltaIndex) {
1994            return setIndex(mIndex + deltaIndex * 10, true);
1995        }
1996
1997        public boolean setIndex(int index, boolean lastAudible) {
1998            int oldIndex = mIndex;
1999            mIndex = getValidIndex(index);
2000
2001            if (oldIndex != mIndex) {
2002                if (lastAudible) {
2003                    mLastAudibleIndex = mIndex;
2004                }
2005                // Apply change to all streams using this one as alias
2006                int numStreamTypes = AudioSystem.getNumStreamTypes();
2007                for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2008                    if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
2009                        mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
2010                    }
2011                }
2012                return true;
2013            } else {
2014                return false;
2015            }
2016        }
2017
2018        public void setLastAudibleIndex(int index) {
2019            mLastAudibleIndex = getValidIndex(index);
2020        }
2021
2022        public void adjustLastAudibleIndex(int deltaIndex) {
2023            setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10);
2024        }
2025
2026        public int getMaxIndex() {
2027            return mIndexMax;
2028        }
2029
2030        public void mute(IBinder cb, boolean state) {
2031            VolumeDeathHandler handler = getDeathHandler(cb, state);
2032            if (handler == null) {
2033                Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
2034                return;
2035            }
2036            handler.mute(state);
2037        }
2038
2039        private int getValidIndex(int index) {
2040            if (index < 0) {
2041                return 0;
2042            } else if (index > mIndexMax) {
2043                return mIndexMax;
2044            }
2045
2046            return index;
2047        }
2048
2049        private class VolumeDeathHandler implements IBinder.DeathRecipient {
2050            private IBinder mICallback; // To be notified of client's death
2051            private int mMuteCount; // Number of active mutes for this client
2052
2053            VolumeDeathHandler(IBinder cb) {
2054                mICallback = cb;
2055            }
2056
2057            public void mute(boolean state) {
2058                synchronized(mDeathHandlers) {
2059                    if (state) {
2060                        if (mMuteCount == 0) {
2061                            // Register for client death notification
2062                            try {
2063                                // mICallback can be 0 if muted by AudioService
2064                                if (mICallback != null) {
2065                                    mICallback.linkToDeath(this, 0);
2066                                }
2067                                mDeathHandlers.add(this);
2068                                // If the stream is not yet muted by any client, set lvel to 0
2069                                if (muteCount() == 0) {
2070                                    setIndex(0, false);
2071                                    sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
2072                                            VolumeStreamState.this, 0);
2073                                }
2074                            } catch (RemoteException e) {
2075                                // Client has died!
2076                                binderDied();
2077                                mDeathHandlers.notify();
2078                                return;
2079                            }
2080                        } else {
2081                            Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
2082                        }
2083                        mMuteCount++;
2084                    } else {
2085                        if (mMuteCount == 0) {
2086                            Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
2087                        } else {
2088                            mMuteCount--;
2089                            if (mMuteCount == 0) {
2090                                // Unregistr from client death notification
2091                                mDeathHandlers.remove(this);
2092                                // mICallback can be 0 if muted by AudioService
2093                                if (mICallback != null) {
2094                                    mICallback.unlinkToDeath(this, 0);
2095                                }
2096                                if (muteCount() == 0) {
2097                                    // If the stream is not muted any more, restore it's volume if
2098                                    // ringer mode allows it
2099                                    if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
2100                                        setIndex(mLastAudibleIndex, false);
2101                                        sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
2102                                                VolumeStreamState.this, 0);
2103                                    }
2104                                }
2105                            }
2106                        }
2107                    }
2108                    mDeathHandlers.notify();
2109                }
2110            }
2111
2112            public void binderDied() {
2113                Log.w(TAG, "Volume service client died for stream: "+mStreamType);
2114                if (mMuteCount != 0) {
2115                    // Reset all active mute requests from this client.
2116                    mMuteCount = 1;
2117                    mute(false);
2118                }
2119            }
2120        }
2121
2122        private int muteCount() {
2123            int count = 0;
2124            int size = mDeathHandlers.size();
2125            for (int i = 0; i < size; i++) {
2126                count += mDeathHandlers.get(i).mMuteCount;
2127            }
2128            return count;
2129        }
2130
2131        private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
2132            synchronized(mDeathHandlers) {
2133                VolumeDeathHandler handler;
2134                int size = mDeathHandlers.size();
2135                for (int i = 0; i < size; i++) {
2136                    handler = mDeathHandlers.get(i);
2137                    if (cb == handler.mICallback) {
2138                        return handler;
2139                    }
2140                }
2141                // If this is the first mute request for this client, create a new
2142                // client death handler. Otherwise, it is an out of sequence unmute request.
2143                if (state) {
2144                    handler = new VolumeDeathHandler(cb);
2145                } else {
2146                    Log.w(TAG, "stream was not muted by this client");
2147                    handler = null;
2148                }
2149                return handler;
2150            }
2151        }
2152    }
2153
2154    /** Thread that handles native AudioSystem control. */
2155    private class AudioSystemThread extends Thread {
2156        AudioSystemThread() {
2157            super("AudioService");
2158        }
2159
2160        @Override
2161        public void run() {
2162            // Set this thread up so the handler will work on it
2163            Looper.prepare();
2164
2165            synchronized(AudioService.this) {
2166                mAudioHandler = new AudioHandler();
2167
2168                // Notify that the handler has been created
2169                AudioService.this.notify();
2170            }
2171
2172            // Listen for volume change requests that are set by VolumePanel
2173            Looper.loop();
2174        }
2175    }
2176
2177    /** Handles internal volume messages in separate volume thread. */
2178    private class AudioHandler extends Handler {
2179
2180        private void setSystemVolume(VolumeStreamState streamState) {
2181
2182            // Adjust volume
2183            setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
2184
2185            // Apply change to all streams using this one as alias
2186            int numStreamTypes = AudioSystem.getNumStreamTypes();
2187            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2188                if (streamType != streamState.mStreamType &&
2189                    STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
2190                    setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
2191                }
2192            }
2193
2194            // Post a persist volume msg
2195            sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
2196                    SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY);
2197        }
2198
2199        private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) {
2200            if (current) {
2201                System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
2202                              (streamState.mIndex + 5)/ 10);
2203            }
2204            if (lastAudible) {
2205                System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
2206                    (streamState.mLastAudibleIndex + 5) / 10);
2207            }
2208        }
2209
2210        private void persistRingerMode() {
2211            System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
2212        }
2213
2214        private void persistVibrateSetting() {
2215            System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
2216        }
2217
2218        private void playSoundEffect(int effectType, int volume) {
2219            synchronized (mSoundEffectsLock) {
2220                if (mSoundPool == null) {
2221                    return;
2222                }
2223                float volFloat;
2224                // use default if volume is not specified by caller
2225                if (volume < 0) {
2226                    volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
2227                } else {
2228                    volFloat = (float) volume / 1000.0f;
2229                }
2230
2231                if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
2232                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
2233                } else {
2234                    MediaPlayer mediaPlayer = new MediaPlayer();
2235                    if (mediaPlayer != null) {
2236                        try {
2237                            String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
2238                            mediaPlayer.setDataSource(filePath);
2239                            mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
2240                            mediaPlayer.prepare();
2241                            mediaPlayer.setVolume(volFloat, volFloat);
2242                            mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
2243                                public void onCompletion(MediaPlayer mp) {
2244                                    cleanupPlayer(mp);
2245                                }
2246                            });
2247                            mediaPlayer.setOnErrorListener(new OnErrorListener() {
2248                                public boolean onError(MediaPlayer mp, int what, int extra) {
2249                                    cleanupPlayer(mp);
2250                                    return true;
2251                                }
2252                            });
2253                            mediaPlayer.start();
2254                        } catch (IOException ex) {
2255                            Log.w(TAG, "MediaPlayer IOException: "+ex);
2256                        } catch (IllegalArgumentException ex) {
2257                            Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
2258                        } catch (IllegalStateException ex) {
2259                            Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2260                        }
2261                    }
2262                }
2263            }
2264        }
2265
2266        private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
2267            Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
2268                    receiver == null ? "" : receiver.flattenToString());
2269        }
2270
2271        private void cleanupPlayer(MediaPlayer mp) {
2272            if (mp != null) {
2273                try {
2274                    mp.stop();
2275                    mp.release();
2276                } catch (IllegalStateException ex) {
2277                    Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2278                }
2279            }
2280        }
2281
2282        private void setForceUse(int usage, int config) {
2283            AudioSystem.setForceUse(usage, config);
2284        }
2285
2286        @Override
2287        public void handleMessage(Message msg) {
2288            int baseMsgWhat = getMsgBase(msg.what);
2289
2290            switch (baseMsgWhat) {
2291
2292                case MSG_SET_SYSTEM_VOLUME:
2293                    setSystemVolume((VolumeStreamState) msg.obj);
2294                    break;
2295
2296                case MSG_PERSIST_VOLUME:
2297                    persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0));
2298                    break;
2299
2300                case MSG_PERSIST_MASTER_VOLUME:
2301                    Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER,
2302                            (float)msg.arg1 / (float)1000.0);
2303                    break;
2304
2305                case MSG_PERSIST_RINGER_MODE:
2306                    persistRingerMode();
2307                    break;
2308
2309                case MSG_PERSIST_VIBRATE_SETTING:
2310                    persistVibrateSetting();
2311                    break;
2312
2313                case MSG_MEDIA_SERVER_DIED:
2314                    if (!mMediaServerOk) {
2315                        Log.e(TAG, "Media server died.");
2316                        // Force creation of new IAudioFlinger interface so that we are notified
2317                        // when new media_server process is back to life.
2318                        AudioSystem.setErrorCallback(mAudioSystemCallback);
2319                        sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
2320                                null, 500);
2321                    }
2322                    break;
2323
2324                case MSG_MEDIA_SERVER_STARTED:
2325                    Log.e(TAG, "Media server started.");
2326                    // indicate to audio HAL that we start the reconfiguration phase after a media
2327                    // server crash
2328                    // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
2329                    // process restarts after a crash, not the first time it is started.
2330                    AudioSystem.setParameters("restarting=true");
2331
2332                    // Restore device connection states
2333                    synchronized (mConnectedDevices) {
2334                        Set set = mConnectedDevices.entrySet();
2335                        Iterator i = set.iterator();
2336                        while(i.hasNext()){
2337                            Map.Entry device = (Map.Entry)i.next();
2338                            AudioSystem.setDeviceConnectionState(
2339                                                            ((Integer)device.getKey()).intValue(),
2340                                                            AudioSystem.DEVICE_STATE_AVAILABLE,
2341                                                            (String)device.getValue());
2342                        }
2343                    }
2344                    // Restore call state
2345                    AudioSystem.setPhoneState(mMode);
2346
2347                    // Restore forced usage for communcations and record
2348                    AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
2349                    AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
2350
2351                    // Restore stream volumes
2352                    int numStreamTypes = AudioSystem.getNumStreamTypes();
2353                    for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2354                        int index;
2355                        VolumeStreamState streamState = mStreamStates[streamType];
2356                        AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
2357                        if (streamState.muteCount() == 0) {
2358                            index = streamState.mIndex;
2359                        } else {
2360                            index = 0;
2361                        }
2362                        setStreamVolumeIndex(streamType, index);
2363                    }
2364
2365                    // Restore ringer mode
2366                    setRingerModeInt(getRingerMode(), false);
2367
2368                    // indicate the end of reconfiguration phase to audio HAL
2369                    AudioSystem.setParameters("restarting=false");
2370                    break;
2371
2372                case MSG_LOAD_SOUND_EFFECTS:
2373                    loadSoundEffects();
2374                    break;
2375
2376                case MSG_PLAY_SOUND_EFFECT:
2377                    playSoundEffect(msg.arg1, msg.arg2);
2378                    break;
2379
2380                case MSG_BTA2DP_DOCK_TIMEOUT:
2381                    // msg.obj  == address of BTA2DP device
2382                    synchronized (mConnectedDevices) {
2383                        makeA2dpDeviceUnavailableNow( (String) msg.obj );
2384                    }
2385                    break;
2386
2387                case MSG_SET_FORCE_USE:
2388                    setForceUse(msg.arg1, msg.arg2);
2389                    break;
2390
2391                case MSG_PERSIST_MEDIABUTTONRECEIVER:
2392                    onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
2393                    break;
2394
2395                case MSG_RCDISPLAY_CLEAR:
2396                    onRcDisplayClear();
2397                    break;
2398
2399                case MSG_RCDISPLAY_UPDATE:
2400                    // msg.obj is guaranteed to be non null
2401                    onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
2402                    break;
2403
2404                case MSG_BT_HEADSET_CNCT_FAILED:
2405                    resetBluetoothSco();
2406                    break;
2407            }
2408        }
2409    }
2410
2411    private class SettingsObserver extends ContentObserver {
2412
2413        SettingsObserver() {
2414            super(new Handler());
2415            mContentResolver.registerContentObserver(Settings.System.getUriFor(
2416                Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
2417        }
2418
2419        @Override
2420        public void onChange(boolean selfChange) {
2421            super.onChange(selfChange);
2422            synchronized (mSettingsLock) {
2423                int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
2424                       Settings.System.MODE_RINGER_STREAMS_AFFECTED,
2425                       ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
2426                       (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
2427                if (mVoiceCapable) {
2428                    ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
2429                } else {
2430                    ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
2431                }
2432                if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
2433                    /*
2434                     * Ensure all stream types that should be affected by ringer mode
2435                     * are in the proper state.
2436                     */
2437                    mRingerModeAffectedStreams = ringerModeAffectedStreams;
2438                    setRingerModeInt(getRingerMode(), false);
2439                }
2440            }
2441        }
2442    }
2443
2444    // must be called synchronized on mConnectedDevices
2445    private void makeA2dpDeviceAvailable(String address) {
2446        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2447                AudioSystem.DEVICE_STATE_AVAILABLE,
2448                address);
2449        // Reset A2DP suspend state each time a new sink is connected
2450        AudioSystem.setParameters("A2dpSuspended=false");
2451        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
2452                address);
2453    }
2454
2455    // must be called synchronized on mConnectedDevices
2456    private void makeA2dpDeviceUnavailableNow(String address) {
2457        Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
2458        mContext.sendBroadcast(noisyIntent);
2459        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2460                AudioSystem.DEVICE_STATE_UNAVAILABLE,
2461                address);
2462        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2463    }
2464
2465    // must be called synchronized on mConnectedDevices
2466    private void makeA2dpDeviceUnavailableLater(String address) {
2467        // prevent any activity on the A2DP audio output to avoid unwanted
2468        // reconnection of the sink.
2469        AudioSystem.setParameters("A2dpSuspended=true");
2470        // the device will be made unavailable later, so consider it disconnected right away
2471        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2472        // send the delayed message to make the device unavailable later
2473        Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
2474        mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
2475
2476    }
2477
2478    // must be called synchronized on mConnectedDevices
2479    private void cancelA2dpDeviceTimeout() {
2480        mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2481    }
2482
2483    // must be called synchronized on mConnectedDevices
2484    private boolean hasScheduledA2dpDockTimeout() {
2485        return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2486    }
2487
2488    private void handleA2dpConnectionStateChange(BluetoothDevice btDevice, int state)
2489    {
2490        if (btDevice == null) {
2491            return;
2492        }
2493        String address = btDevice.getAddress();
2494        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2495            address = "";
2496        }
2497        synchronized (mConnectedDevices) {
2498            boolean isConnected =
2499                (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
2500                 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
2501
2502            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
2503                if (btDevice.isBluetoothDock()) {
2504                    if (state == BluetoothProfile.STATE_DISCONNECTED) {
2505                        // introduction of a delay for transient disconnections of docks when
2506                        // power is rapidly turned off/on, this message will be canceled if
2507                        // we reconnect the dock under a preset delay
2508                        makeA2dpDeviceUnavailableLater(address);
2509                        // the next time isConnected is evaluated, it will be false for the dock
2510                    }
2511                } else {
2512                    makeA2dpDeviceUnavailableNow(address);
2513                }
2514            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
2515                if (btDevice.isBluetoothDock()) {
2516                    // this could be a reconnection after a transient disconnection
2517                    cancelA2dpDeviceTimeout();
2518                    mDockAddress = address;
2519                } else {
2520                    // this could be a connection of another A2DP device before the timeout of
2521                    // a dock: cancel the dock timeout, and make the dock unavailable now
2522                    if(hasScheduledA2dpDockTimeout()) {
2523                        cancelA2dpDeviceTimeout();
2524                        makeA2dpDeviceUnavailableNow(mDockAddress);
2525                    }
2526                }
2527                makeA2dpDeviceAvailable(address);
2528            }
2529        }
2530    }
2531
2532    /* cache of the address of the last dock the device was connected to */
2533    private String mDockAddress;
2534
2535    /**
2536     * Receiver for misc intent broadcasts the Phone app cares about.
2537     */
2538    private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
2539        @Override
2540        public void onReceive(Context context, Intent intent) {
2541            String action = intent.getAction();
2542
2543            if (action.equals(Intent.ACTION_DOCK_EVENT)) {
2544                int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
2545                        Intent.EXTRA_DOCK_STATE_UNDOCKED);
2546                int config;
2547                switch (dockState) {
2548                    case Intent.EXTRA_DOCK_STATE_DESK:
2549                        config = AudioSystem.FORCE_BT_DESK_DOCK;
2550                        break;
2551                    case Intent.EXTRA_DOCK_STATE_CAR:
2552                        config = AudioSystem.FORCE_BT_CAR_DOCK;
2553                        break;
2554                    case Intent.EXTRA_DOCK_STATE_LE_DESK:
2555                        config = AudioSystem.FORCE_ANALOG_DOCK;
2556                        break;
2557                    case Intent.EXTRA_DOCK_STATE_HE_DESK:
2558                        config = AudioSystem.FORCE_DIGITAL_DOCK;
2559                        break;
2560                    case Intent.EXTRA_DOCK_STATE_UNDOCKED:
2561                    default:
2562                        config = AudioSystem.FORCE_NONE;
2563                }
2564                AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
2565            } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
2566                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
2567                                               BluetoothProfile.STATE_DISCONNECTED);
2568                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
2569
2570                handleA2dpConnectionStateChange(btDevice, state);
2571            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
2572                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
2573                                               BluetoothProfile.STATE_DISCONNECTED);
2574                int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
2575                String address = null;
2576
2577                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
2578                if (btDevice == null) {
2579                    return;
2580                }
2581
2582                address = btDevice.getAddress();
2583                BluetoothClass btClass = btDevice.getBluetoothClass();
2584                if (btClass != null) {
2585                    switch (btClass.getDeviceClass()) {
2586                    case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
2587                    case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
2588                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
2589                        break;
2590                    case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
2591                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
2592                        break;
2593                    }
2594                }
2595
2596                if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2597                    address = "";
2598                }
2599
2600                synchronized (mConnectedDevices) {
2601                    boolean isConnected = (mConnectedDevices.containsKey(device) &&
2602                                           mConnectedDevices.get(device).equals(address));
2603
2604                    synchronized (mScoClients) {
2605                        if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
2606                            AudioSystem.setDeviceConnectionState(device,
2607                                                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
2608                                                             address);
2609                            mConnectedDevices.remove(device);
2610                            mBluetoothHeadsetDevice = null;
2611                            resetBluetoothSco();
2612                        } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
2613                            AudioSystem.setDeviceConnectionState(device,
2614                                                                 AudioSystem.DEVICE_STATE_AVAILABLE,
2615                                                                 address);
2616                            mConnectedDevices.put(new Integer(device), address);
2617                            mBluetoothHeadsetDevice = btDevice;
2618                        }
2619                    }
2620                }
2621            } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
2622                int state = intent.getIntExtra("state", 0);
2623                int microphone = intent.getIntExtra("microphone", 0);
2624
2625                synchronized (mConnectedDevices) {
2626                    if (microphone != 0) {
2627                        boolean isConnected =
2628                            mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
2629                        if (state == 0 && isConnected) {
2630                            AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
2631                                    AudioSystem.DEVICE_STATE_UNAVAILABLE,
2632                                    "");
2633                            mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
2634                        } else if (state == 1 && !isConnected)  {
2635                            AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
2636                                    AudioSystem.DEVICE_STATE_AVAILABLE,
2637                                    "");
2638                            mConnectedDevices.put(
2639                                    new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
2640                        }
2641                    } else {
2642                        boolean isConnected =
2643                            mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
2644                        if (state == 0 && isConnected) {
2645                            AudioSystem.setDeviceConnectionState(
2646                                    AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
2647                                    AudioSystem.DEVICE_STATE_UNAVAILABLE,
2648                                    "");
2649                            mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
2650                        } else if (state == 1 && !isConnected)  {
2651                            AudioSystem.setDeviceConnectionState(
2652                                    AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
2653                                    AudioSystem.DEVICE_STATE_AVAILABLE,
2654                                    "");
2655                            mConnectedDevices.put(
2656                                    new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
2657                        }
2658                    }
2659                }
2660            } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
2661                int state = intent.getIntExtra("state", 0);
2662                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
2663                synchronized (mConnectedDevices) {
2664                    boolean isConnected =
2665                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
2666                    if (state == 0 && isConnected) {
2667                        AudioSystem.setDeviceConnectionState(
2668                                                        AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
2669                                                        AudioSystem.DEVICE_STATE_UNAVAILABLE,
2670                                                        "");
2671                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
2672                    } else if (state == 1 && !isConnected)  {
2673                        AudioSystem.setDeviceConnectionState(
2674                                                        AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
2675                                                        AudioSystem.DEVICE_STATE_AVAILABLE,
2676                                                        "");
2677                        mConnectedDevices.put(
2678                                new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
2679                    }
2680                }
2681            } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
2682                int state = intent.getIntExtra("state", 0);
2683                Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
2684                synchronized (mConnectedDevices) {
2685                    boolean isConnected =
2686                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
2687                    if (state == 0 && isConnected) {
2688                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
2689                                                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
2690                                                             "");
2691                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
2692                    } else if (state == 1 && !isConnected)  {
2693                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
2694                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
2695                                                             "");
2696                        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
2697                    }
2698                }
2699            } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) {
2700                int state = intent.getIntExtra("state", 0);
2701                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
2702                synchronized (mConnectedDevices) {
2703                    boolean isConnected =
2704                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
2705                    if (state == 0 && isConnected) {
2706                        AudioSystem.setDeviceConnectionState(
2707                                                         AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
2708                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE,
2709                                                         "");
2710                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
2711                    } else if (state == 1 && !isConnected)  {
2712                        AudioSystem.setDeviceConnectionState(
2713                                                         AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
2714                                                         AudioSystem.DEVICE_STATE_AVAILABLE,
2715                                                         "");
2716                        mConnectedDevices.put(
2717                                new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
2718                    }
2719                }
2720            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
2721                boolean broadcast = false;
2722                int state = AudioManager.SCO_AUDIO_STATE_ERROR;
2723                synchronized (mScoClients) {
2724                    int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
2725                    // broadcast intent if the connection was initated by AudioService
2726                    if (!mScoClients.isEmpty() &&
2727                            (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
2728                             mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
2729                             mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
2730                        broadcast = true;
2731                    }
2732                    switch (btState) {
2733                    case BluetoothHeadset.STATE_AUDIO_CONNECTED:
2734                        state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
2735                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
2736                            mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
2737                            mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
2738                            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
2739                        }
2740                        break;
2741                    case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
2742                        state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
2743                        mScoAudioState = SCO_STATE_INACTIVE;
2744                        clearAllScoClients(0, false);
2745                        break;
2746                    case BluetoothHeadset.STATE_AUDIO_CONNECTING:
2747                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
2748                            mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
2749                            mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
2750                            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
2751                        }
2752                    default:
2753                        // do not broadcast CONNECTING or invalid state
2754                        broadcast = false;
2755                        break;
2756                    }
2757                }
2758                if (broadcast) {
2759                    broadcastScoConnectionState(state);
2760                    //FIXME: this is to maintain compatibility with deprecated intent
2761                    // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
2762                    Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
2763                    newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
2764                    mContext.sendStickyBroadcast(newIntent);
2765                }
2766            } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
2767                mBootCompleted = true;
2768                sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP,
2769                        0, 0, null, 0);
2770
2771                mKeyguardManager =
2772                        (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
2773                mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
2774                resetBluetoothSco();
2775                getBluetoothHeadset();
2776                //FIXME: this is to maintain compatibility with deprecated intent
2777                // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
2778                Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
2779                newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
2780                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
2781                mContext.sendStickyBroadcast(newIntent);
2782
2783                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
2784                if (adapter != null) {
2785                    adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
2786                                            BluetoothProfile.A2DP);
2787                }
2788
2789                if (mUseMasterVolume) {
2790                    // Send sticky broadcast for initial master mute state
2791                    sendMasterMuteUpdate(false, 0);
2792                }
2793            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
2794                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
2795                    // a package is being removed, not replaced
2796                    String packageName = intent.getData().getSchemeSpecificPart();
2797                    if (packageName != null) {
2798                        removeMediaButtonReceiverForPackage(packageName);
2799                    }
2800                }
2801            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
2802                AudioSystem.setParameters("screen_state=on");
2803            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
2804                AudioSystem.setParameters("screen_state=off");
2805            }
2806        }
2807    }
2808
2809    //==========================================================================================
2810    // AudioFocus
2811    //==========================================================================================
2812
2813    /* constant to identify focus stack entry that is used to hold the focus while the phone
2814     * is ringing or during a call
2815     */
2816    private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
2817
2818    private final static Object mAudioFocusLock = new Object();
2819
2820    private final static Object mRingingLock = new Object();
2821
2822    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
2823        @Override
2824        public void onCallStateChanged(int state, String incomingNumber) {
2825            if (state == TelephonyManager.CALL_STATE_RINGING) {
2826                //Log.v(TAG, " CALL_STATE_RINGING");
2827                synchronized(mRingingLock) {
2828                    mIsRinging = true;
2829                }
2830            } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
2831                    || (state == TelephonyManager.CALL_STATE_IDLE)) {
2832                synchronized(mRingingLock) {
2833                    mIsRinging = false;
2834                }
2835            }
2836        }
2837    };
2838
2839    private void notifyTopOfAudioFocusStack() {
2840        // notify the top of the stack it gained focus
2841        if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
2842            if (canReassignAudioFocus()) {
2843                try {
2844                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
2845                            AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
2846                } catch (RemoteException e) {
2847                    Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
2848                    e.printStackTrace();
2849                }
2850            }
2851        }
2852    }
2853
2854    private static class FocusStackEntry {
2855        public int mStreamType = -1;// no stream type
2856        public IAudioFocusDispatcher mFocusDispatcher = null;
2857        public IBinder mSourceRef = null;
2858        public String mClientId;
2859        public int mFocusChangeType;
2860        public AudioFocusDeathHandler mHandler;
2861        public String mPackageName;
2862        public int mCallingUid;
2863
2864        public FocusStackEntry() {
2865        }
2866
2867        public FocusStackEntry(int streamType, int duration,
2868                IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
2869                String pn, int uid) {
2870            mStreamType = streamType;
2871            mFocusDispatcher = afl;
2872            mSourceRef = source;
2873            mClientId = id;
2874            mFocusChangeType = duration;
2875            mHandler = hdlr;
2876            mPackageName = pn;
2877            mCallingUid = uid;
2878        }
2879
2880        public void unlinkToDeath() {
2881            try {
2882                if (mSourceRef != null && mHandler != null) {
2883                    mSourceRef.unlinkToDeath(mHandler, 0);
2884                    mHandler = null;
2885                }
2886            } catch (java.util.NoSuchElementException e) {
2887                Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
2888            }
2889        }
2890
2891        @Override
2892        protected void finalize() throws Throwable {
2893            unlinkToDeath(); // unlink exception handled inside method
2894            super.finalize();
2895        }
2896    }
2897
2898    private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
2899
2900    /**
2901     * Helper function:
2902     * Display in the log the current entries in the audio focus stack
2903     */
2904    private void dumpFocusStack(PrintWriter pw) {
2905        pw.println("\nAudio Focus stack entries:");
2906        synchronized(mAudioFocusLock) {
2907            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2908            while(stackIterator.hasNext()) {
2909                FocusStackEntry fse = stackIterator.next();
2910                pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
2911                        + " -- duration: " + fse.mFocusChangeType
2912                        + " -- uid: " + fse.mCallingUid);
2913            }
2914        }
2915    }
2916
2917    /**
2918     * Helper function:
2919     * Called synchronized on mAudioFocusLock
2920     * Remove a focus listener from the focus stack.
2921     * @param focusListenerToRemove the focus listener
2922     * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
2923     *   focus, notify the next item in the stack it gained focus.
2924     */
2925    private void removeFocusStackEntry(String clientToRemove, boolean signal) {
2926        // is the current top of the focus stack abandoning focus? (because of request, not death)
2927        if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
2928        {
2929            //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
2930            FocusStackEntry fse = mFocusStack.pop();
2931            fse.unlinkToDeath();
2932            if (signal) {
2933                // notify the new top of the stack it gained focus
2934                notifyTopOfAudioFocusStack();
2935                // there's a new top of the stack, let the remote control know
2936                synchronized(mRCStack) {
2937                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
2938                }
2939            }
2940        } else {
2941            // focus is abandoned by a client that's not at the top of the stack,
2942            // no need to update focus.
2943            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2944            while(stackIterator.hasNext()) {
2945                FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
2946                if(fse.mClientId.equals(clientToRemove)) {
2947                    Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
2948                            + fse.mClientId);
2949                    stackIterator.remove();
2950                    fse.unlinkToDeath();
2951                }
2952            }
2953        }
2954    }
2955
2956    /**
2957     * Helper function:
2958     * Called synchronized on mAudioFocusLock
2959     * Remove focus listeners from the focus stack for a particular client when it has died.
2960     */
2961    private void removeFocusStackEntryForClient(IBinder cb) {
2962        // is the owner of the audio focus part of the client to remove?
2963        boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
2964                mFocusStack.peek().mSourceRef.equals(cb);
2965        Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2966        while(stackIterator.hasNext()) {
2967            FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
2968            if(fse.mSourceRef.equals(cb)) {
2969                Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
2970                        + fse.mClientId);
2971                stackIterator.remove();
2972                // the client just died, no need to unlink to its death
2973            }
2974        }
2975        if (isTopOfStackForClientToRemove) {
2976            // we removed an entry at the top of the stack:
2977            //  notify the new top of the stack it gained focus.
2978            notifyTopOfAudioFocusStack();
2979            // there's a new top of the stack, let the remote control know
2980            synchronized(mRCStack) {
2981                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
2982            }
2983        }
2984    }
2985
2986    /**
2987     * Helper function:
2988     * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
2989     */
2990    private boolean canReassignAudioFocus() {
2991        // focus requests are rejected during a phone call or when the phone is ringing
2992        // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
2993        if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
2994            return false;
2995        }
2996        return true;
2997    }
2998
2999    /**
3000     * Inner class to monitor audio focus client deaths, and remove them from the audio focus
3001     * stack if necessary.
3002     */
3003    private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
3004        private IBinder mCb; // To be notified of client's death
3005
3006        AudioFocusDeathHandler(IBinder cb) {
3007            mCb = cb;
3008        }
3009
3010        public void binderDied() {
3011            synchronized(mAudioFocusLock) {
3012                Log.w(TAG, "  AudioFocus   audio focus client died");
3013                removeFocusStackEntryForClient(mCb);
3014            }
3015        }
3016
3017        public IBinder getBinder() {
3018            return mCb;
3019        }
3020    }
3021
3022
3023    /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
3024    public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
3025            IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
3026        Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
3027        // the main stream type for the audio focus request is currently not used. It may
3028        // potentially be used to handle multiple stream type-dependent audio focuses.
3029
3030        // we need a valid binder callback for clients
3031        if (!cb.pingBinder()) {
3032            Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
3033            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3034        }
3035
3036        synchronized(mAudioFocusLock) {
3037            if (!canReassignAudioFocus()) {
3038                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3039            }
3040
3041            // handle the potential premature death of the new holder of the focus
3042            // (premature death == death before abandoning focus)
3043            // Register for client death notification
3044            AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
3045            try {
3046                cb.linkToDeath(afdh, 0);
3047            } catch (RemoteException e) {
3048                // client has already died!
3049                Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
3050                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3051            }
3052
3053            if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
3054                // if focus is already owned by this client and the reason for acquiring the focus
3055                // hasn't changed, don't do anything
3056                if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
3057                    // unlink death handler so it can be gc'ed.
3058                    // linkToDeath() creates a JNI global reference preventing collection.
3059                    cb.unlinkToDeath(afdh, 0);
3060                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3061                }
3062                // the reason for the audio focus request has changed: remove the current top of
3063                // stack and respond as if we had a new focus owner
3064                FocusStackEntry fse = mFocusStack.pop();
3065                fse.unlinkToDeath();
3066            }
3067
3068            // notify current top of stack it is losing focus
3069            if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
3070                try {
3071                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
3072                            -1 * focusChangeHint, // loss and gain codes are inverse of each other
3073                            mFocusStack.peek().mClientId);
3074                } catch (RemoteException e) {
3075                    Log.e(TAG, " Failure to signal loss of focus due to "+ e);
3076                    e.printStackTrace();
3077                }
3078            }
3079
3080            // focus requester might already be somewhere below in the stack, remove it
3081            removeFocusStackEntry(clientId, false /* signal */);
3082
3083            // push focus requester at the top of the audio focus stack
3084            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
3085                    clientId, afdh, callingPackageName, Binder.getCallingUid()));
3086
3087            // there's a new top of the stack, let the remote control know
3088            synchronized(mRCStack) {
3089                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3090            }
3091        }//synchronized(mAudioFocusLock)
3092
3093        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3094    }
3095
3096    /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
3097    public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
3098        Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
3099        try {
3100            // this will take care of notifying the new focus owner if needed
3101            synchronized(mAudioFocusLock) {
3102                removeFocusStackEntry(clientId, true);
3103            }
3104        } catch (java.util.ConcurrentModificationException cme) {
3105            // Catching this exception here is temporary. It is here just to prevent
3106            // a crash seen when the "Silent" notification is played. This is believed to be fixed
3107            // but this try catch block is left just to be safe.
3108            Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
3109            cme.printStackTrace();
3110        }
3111
3112        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3113    }
3114
3115
3116    public void unregisterAudioFocusClient(String clientId) {
3117        synchronized(mAudioFocusLock) {
3118            removeFocusStackEntry(clientId, false);
3119        }
3120    }
3121
3122
3123    //==========================================================================================
3124    // RemoteControl
3125    //==========================================================================================
3126    /**
3127     * Receiver for media button intents. Handles the dispatching of the media button event
3128     * to one of the registered listeners, or if there was none, resumes the intent broadcast
3129     * to the rest of the system.
3130     */
3131    private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
3132        @Override
3133        public void onReceive(Context context, Intent intent) {
3134            String action = intent.getAction();
3135            if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
3136                return;
3137            }
3138            KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
3139            if (event != null) {
3140                // if in a call or ringing, do not break the current phone app behavior
3141                // TODO modify this to let the phone app specifically get the RC focus
3142                //      add modify the phone app to take advantage of the new API
3143                synchronized(mRingingLock) {
3144                    if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) ||
3145                            (getMode() == AudioSystem.MODE_IN_COMMUNICATION) ||
3146                            (getMode() == AudioSystem.MODE_RINGTONE) ) {
3147                        return;
3148                    }
3149                }
3150                synchronized(mRCStack) {
3151                    if (!mRCStack.empty()) {
3152                        // create a new intent to fill in the extras of the registered PendingIntent
3153                        Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
3154                        Bundle extras = intent.getExtras();
3155                        if (extras != null) {
3156                            targetedIntent.putExtras(extras);
3157                            // trap the current broadcast
3158                            abortBroadcast();
3159                            //Log.v(TAG, " Sending intent" + targetedIntent);
3160                            // send the intent that was registered by the client
3161                            try {
3162                                mRCStack.peek().mMediaIntent.send(context, 0, targetedIntent);
3163                            } catch (CanceledException e) {
3164                                Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
3165                                e.printStackTrace();
3166                            }
3167                        }
3168                    }
3169                }
3170            }
3171        }
3172    }
3173
3174    private final Object mCurrentRcLock = new Object();
3175    /**
3176     * The one remote control client which will receive a request for display information.
3177     * This object may be null.
3178     * Access protected by mCurrentRcLock.
3179     */
3180    private IRemoteControlClient mCurrentRcClient = null;
3181
3182    private final static int RC_INFO_NONE = 0;
3183    private final static int RC_INFO_ALL =
3184        RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
3185        RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
3186        RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
3187        RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
3188
3189    /**
3190     * A monotonically increasing generation counter for mCurrentRcClient.
3191     * Only accessed with a lock on mCurrentRcLock.
3192     * No value wrap-around issues as we only act on equal values.
3193     */
3194    private int mCurrentRcClientGen = 0;
3195
3196    /**
3197     * Inner class to monitor remote control client deaths, and remove the client for the
3198     * remote control stack if necessary.
3199     */
3200    private class RcClientDeathHandler implements IBinder.DeathRecipient {
3201        private IBinder mCb; // To be notified of client's death
3202        private PendingIntent mMediaIntent;
3203
3204        RcClientDeathHandler(IBinder cb, PendingIntent pi) {
3205            mCb = cb;
3206            mMediaIntent = pi;
3207        }
3208
3209        public void binderDied() {
3210            Log.w(TAG, "  RemoteControlClient died");
3211            // remote control client died, make sure the displays don't use it anymore
3212            //  by setting its remote control client to null
3213            registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
3214        }
3215
3216        public IBinder getBinder() {
3217            return mCb;
3218        }
3219    }
3220
3221    private static class RemoteControlStackEntry {
3222        /**
3223         * The target for the ACTION_MEDIA_BUTTON events.
3224         * Always non null.
3225         */
3226        public PendingIntent mMediaIntent;
3227        /**
3228         * The registered media button event receiver.
3229         * Always non null.
3230         */
3231        public ComponentName mReceiverComponent;
3232        public String mCallingPackageName;
3233        public int mCallingUid;
3234        /**
3235         * Provides access to the information to display on the remote control.
3236         * May be null (when a media button event receiver is registered,
3237         *     but no remote control client has been registered) */
3238        public IRemoteControlClient mRcClient;
3239        public RcClientDeathHandler mRcClientDeathHandler;
3240
3241        /** precondition: mediaIntent != null, eventReceiver != null */
3242        public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
3243            mMediaIntent = mediaIntent;
3244            mReceiverComponent = eventReceiver;
3245            mCallingUid = -1;
3246            mRcClient = null;
3247        }
3248
3249        public void unlinkToRcClientDeath() {
3250            if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
3251                try {
3252                    mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
3253                    mRcClientDeathHandler = null;
3254                } catch (java.util.NoSuchElementException e) {
3255                    // not much we can do here
3256                    Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
3257                    e.printStackTrace();
3258                }
3259            }
3260        }
3261
3262        @Override
3263        protected void finalize() throws Throwable {
3264            unlinkToRcClientDeath();// unlink exception handled inside method
3265            super.finalize();
3266        }
3267    }
3268
3269    /**
3270     *  The stack of remote control event receivers.
3271     *  Code sections and methods that modify the remote control event receiver stack are
3272     *  synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
3273     *  stack, audio focus or RC, can lead to a change in the remote control display
3274     */
3275    private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
3276
3277    /**
3278     * Helper function:
3279     * Display in the log the current entries in the remote control focus stack
3280     */
3281    private void dumpRCStack(PrintWriter pw) {
3282        pw.println("\nRemote Control stack entries:");
3283        synchronized(mRCStack) {
3284            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3285            while(stackIterator.hasNext()) {
3286                RemoteControlStackEntry rcse = stackIterator.next();
3287                pw.println("  pi: " + rcse.mMediaIntent +
3288                        "  -- ercvr: " + rcse.mReceiverComponent +
3289                        "  -- client: " + rcse.mRcClient +
3290                        "  -- uid: " + rcse.mCallingUid);
3291            }
3292        }
3293    }
3294
3295    /**
3296     * Helper function:
3297     * Remove any entry in the remote control stack that has the same package name as packageName
3298     * Pre-condition: packageName != null
3299     */
3300    private void removeMediaButtonReceiverForPackage(String packageName) {
3301        synchronized(mRCStack) {
3302            if (mRCStack.empty()) {
3303                return;
3304            } else {
3305                RemoteControlStackEntry oldTop = mRCStack.peek();
3306                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3307                // iterate over the stack entries
3308                while(stackIterator.hasNext()) {
3309                    RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
3310                    if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
3311                        // a stack entry is from the package being removed, remove it from the stack
3312                        stackIterator.remove();
3313                        rcse.unlinkToRcClientDeath();
3314                    }
3315                }
3316                if (mRCStack.empty()) {
3317                    // no saved media button receiver
3318                    mAudioHandler.sendMessage(
3319                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
3320                                    null));
3321                } else if (oldTop != mRCStack.peek()) {
3322                    // the top of the stack has changed, save it in the system settings
3323                    // by posting a message to persist it
3324                    mAudioHandler.sendMessage(
3325                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
3326                                    mRCStack.peek().mReceiverComponent));
3327                }
3328            }
3329        }
3330    }
3331
3332    /**
3333     * Helper function:
3334     * Restore remote control receiver from the system settings.
3335     */
3336    private void restoreMediaButtonReceiver() {
3337        String receiverName = Settings.System.getString(mContentResolver,
3338                Settings.System.MEDIA_BUTTON_RECEIVER);
3339        if ((null != receiverName) && !receiverName.isEmpty()) {
3340            ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
3341            // construct a PendingIntent targeted to the restored component name
3342            // for the media button and register it
3343            Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
3344            //     the associated intent will be handled by the component being registered
3345            mediaButtonIntent.setComponent(eventReceiver);
3346            PendingIntent pi = PendingIntent.getBroadcast(mContext,
3347                    0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
3348            registerMediaButtonIntent(pi, eventReceiver);
3349        }
3350    }
3351
3352    /**
3353     * Helper function:
3354     * Set the new remote control receiver at the top of the RC focus stack.
3355     * precondition: mediaIntent != null, target != null
3356     */
3357    private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
3358        // already at top of stack?
3359        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
3360            return;
3361        }
3362        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3363        RemoteControlStackEntry rcse = null;
3364        boolean wasInsideStack = false;
3365        while(stackIterator.hasNext()) {
3366            rcse = (RemoteControlStackEntry)stackIterator.next();
3367            if(rcse.mMediaIntent.equals(mediaIntent)) {
3368                wasInsideStack = true;
3369                stackIterator.remove();
3370                break;
3371            }
3372        }
3373        if (!wasInsideStack) {
3374            rcse = new RemoteControlStackEntry(mediaIntent, target);
3375        }
3376        mRCStack.push(rcse);
3377
3378        // post message to persist the default media button receiver
3379        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
3380                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
3381    }
3382
3383    /**
3384     * Helper function:
3385     * Remove the remote control receiver from the RC focus stack.
3386     * precondition: pi != null
3387     */
3388    private void removeMediaButtonReceiver(PendingIntent pi) {
3389        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3390        while(stackIterator.hasNext()) {
3391            RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
3392            if(rcse.mMediaIntent.equals(pi)) {
3393                stackIterator.remove();
3394                rcse.unlinkToRcClientDeath();
3395                break;
3396            }
3397        }
3398    }
3399
3400    /**
3401     * Helper function:
3402     * Called synchronized on mRCStack
3403     */
3404    private boolean isCurrentRcController(PendingIntent pi) {
3405        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
3406            return true;
3407        }
3408        return false;
3409    }
3410
3411    //==========================================================================================
3412    // Remote control display / client
3413    //==========================================================================================
3414    /**
3415     * Update the remote control displays with the new "focused" client generation
3416     */
3417    private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
3418            PendingIntent newMediaIntent, boolean clearing) {
3419        // NOTE: Only one IRemoteControlDisplay supported in this implementation
3420        if (mRcDisplay != null) {
3421            try {
3422                mRcDisplay.setCurrentClientId(
3423                        newClientGeneration, newMediaIntent, clearing);
3424            } catch (RemoteException e) {
3425                Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
3426                // if we had a display before, stop monitoring its death
3427                rcDisplay_stopDeathMonitor_syncRcStack();
3428                mRcDisplay = null;
3429            }
3430        }
3431    }
3432
3433    /**
3434     * Update the remote control clients with the new "focused" client generation
3435     */
3436    private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
3437        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3438        while(stackIterator.hasNext()) {
3439            RemoteControlStackEntry se = stackIterator.next();
3440            if ((se != null) && (se.mRcClient != null)) {
3441                try {
3442                    se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
3443                } catch (RemoteException e) {
3444                    Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e);
3445                    stackIterator.remove();
3446                    se.unlinkToRcClientDeath();
3447                }
3448            }
3449        }
3450    }
3451
3452    /**
3453     * Update the displays and clients with the new "focused" client generation and name
3454     * @param newClientGeneration the new generation value matching a client update
3455     * @param newClientEventReceiver the media button event receiver associated with the client.
3456     *    May be null, which implies there is no registered media button event receiver.
3457     * @param clearing true if the new client generation value maps to a remote control update
3458     *    where the display should be cleared.
3459     */
3460    private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
3461            PendingIntent newMediaIntent, boolean clearing) {
3462        // send the new valid client generation ID to all displays
3463        setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
3464        // send the new valid client generation ID to all clients
3465        setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
3466    }
3467
3468    /**
3469     * Called when processing MSG_RCDISPLAY_CLEAR event
3470     */
3471    private void onRcDisplayClear() {
3472        if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
3473
3474        synchronized(mRCStack) {
3475            synchronized(mCurrentRcLock) {
3476                mCurrentRcClientGen++;
3477                // synchronously update the displays and clients with the new client generation
3478                setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
3479                        null /*newMediaIntent*/, true /*clearing*/);
3480            }
3481        }
3482    }
3483
3484    /**
3485     * Called when processing MSG_RCDISPLAY_UPDATE event
3486     */
3487    private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
3488        synchronized(mRCStack) {
3489            synchronized(mCurrentRcLock) {
3490                if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
3491                    if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
3492
3493                    mCurrentRcClientGen++;
3494                    // synchronously update the displays and clients with
3495                    //      the new client generation
3496                    setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
3497                            rcse.mMediaIntent /*newMediaIntent*/,
3498                            false /*clearing*/);
3499
3500                    // tell the current client that it needs to send info
3501                    try {
3502                        mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
3503                                flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
3504                    } catch (RemoteException e) {
3505                        Log.e(TAG, "Current valid remote client is dead: "+e);
3506                        mCurrentRcClient = null;
3507                    }
3508                } else {
3509                    // the remote control display owner has changed between the
3510                    // the message to update the display was sent, and the time it
3511                    // gets to be processed (now)
3512                }
3513            }
3514        }
3515    }
3516
3517
3518    /**
3519     * Helper function:
3520     * Called synchronized on mRCStack
3521     */
3522    private void clearRemoteControlDisplay_syncAfRcs() {
3523        synchronized(mCurrentRcLock) {
3524            mCurrentRcClient = null;
3525        }
3526        // will cause onRcDisplayClear() to be called in AudioService's handler thread
3527        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
3528    }
3529
3530    /**
3531     * Helper function for code readability: only to be called from
3532     *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
3533     *    this method.
3534     * Preconditions:
3535     *    - called synchronized mAudioFocusLock then on mRCStack
3536     *    - mRCStack.isEmpty() is false
3537     */
3538    private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
3539        RemoteControlStackEntry rcse = mRCStack.peek();
3540        int infoFlagsAboutToBeUsed = infoChangedFlags;
3541        // this is where we enforce opt-in for information display on the remote controls
3542        //   with the new AudioManager.registerRemoteControlClient() API
3543        if (rcse.mRcClient == null) {
3544            //Log.w(TAG, "Can't update remote control display with null remote control client");
3545            clearRemoteControlDisplay_syncAfRcs();
3546            return;
3547        }
3548        synchronized(mCurrentRcLock) {
3549            if (!rcse.mRcClient.equals(mCurrentRcClient)) {
3550                // new RC client, assume every type of information shall be queried
3551                infoFlagsAboutToBeUsed = RC_INFO_ALL;
3552            }
3553            mCurrentRcClient = rcse.mRcClient;
3554        }
3555        // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
3556        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
3557                infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
3558    }
3559
3560    /**
3561     * Helper function:
3562     * Called synchronized on mAudioFocusLock, then mRCStack
3563     * Check whether the remote control display should be updated, triggers the update if required
3564     * @param infoChangedFlags the flags corresponding to the remote control client information
3565     *     that has changed, if applicable (checking for the update conditions might trigger a
3566     *     clear, rather than an update event).
3567     */
3568    private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
3569        // determine whether the remote control display should be refreshed
3570        // if either stack is empty, there is a mismatch, so clear the RC display
3571        if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
3572            clearRemoteControlDisplay_syncAfRcs();
3573            return;
3574        }
3575        // if the top of the two stacks belong to different packages, there is a mismatch, clear
3576        if ((mRCStack.peek().mCallingPackageName != null)
3577                && (mFocusStack.peek().mPackageName != null)
3578                && !(mRCStack.peek().mCallingPackageName.compareTo(
3579                        mFocusStack.peek().mPackageName) == 0)) {
3580            clearRemoteControlDisplay_syncAfRcs();
3581            return;
3582        }
3583        // if the audio focus didn't originate from the same Uid as the one in which the remote
3584        //   control information will be retrieved, clear
3585        if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
3586            clearRemoteControlDisplay_syncAfRcs();
3587            return;
3588        }
3589        // refresh conditions were verified: update the remote controls
3590        // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
3591        updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
3592    }
3593
3594    /**
3595     * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
3596     * precondition: mediaIntent != null, target != null
3597     */
3598    public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
3599        Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
3600
3601        synchronized(mAudioFocusLock) {
3602            synchronized(mRCStack) {
3603                pushMediaButtonReceiver(mediaIntent, eventReceiver);
3604                // new RC client, assume every type of information shall be queried
3605                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3606            }
3607        }
3608    }
3609
3610    /**
3611     * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
3612     * precondition: mediaIntent != null, eventReceiver != null
3613     */
3614    public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
3615    {
3616        Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
3617
3618        synchronized(mAudioFocusLock) {
3619            synchronized(mRCStack) {
3620                boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
3621                removeMediaButtonReceiver(mediaIntent);
3622                if (topOfStackWillChange) {
3623                    // current RC client will change, assume every type of info needs to be queried
3624                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3625                }
3626            }
3627        }
3628    }
3629
3630    /**
3631     * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
3632     * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
3633     *     without modifying the RC stack, but while still causing the display to refresh (will
3634     *     become blank as a result of this)
3635     */
3636    public void registerRemoteControlClient(PendingIntent mediaIntent,
3637            IRemoteControlClient rcClient, String callingPackageName) {
3638        if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
3639        synchronized(mAudioFocusLock) {
3640            synchronized(mRCStack) {
3641                // store the new display information
3642                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3643                while(stackIterator.hasNext()) {
3644                    RemoteControlStackEntry rcse = stackIterator.next();
3645                    if(rcse.mMediaIntent.equals(mediaIntent)) {
3646                        // already had a remote control client?
3647                        if (rcse.mRcClientDeathHandler != null) {
3648                            // stop monitoring the old client's death
3649                            rcse.unlinkToRcClientDeath();
3650                        }
3651                        // save the new remote control client
3652                        rcse.mRcClient = rcClient;
3653                        rcse.mCallingPackageName = callingPackageName;
3654                        rcse.mCallingUid = Binder.getCallingUid();
3655                        if (rcClient == null) {
3656                            // here rcse.mRcClientDeathHandler is null;
3657                            break;
3658                        }
3659
3660                        // there is a new (non-null) client:
3661                        // 1/ give the new client the current display (if any)
3662                        if (mRcDisplay != null) {
3663                            try {
3664                                rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
3665                            } catch (RemoteException e) {
3666                                Log.e(TAG, "Error connecting remote control display to client: "+e);
3667                                e.printStackTrace();
3668                            }
3669                        }
3670                        // 2/ monitor the new client's death
3671                        IBinder b = rcse.mRcClient.asBinder();
3672                        RcClientDeathHandler rcdh =
3673                                new RcClientDeathHandler(b, rcse.mMediaIntent);
3674                        try {
3675                            b.linkToDeath(rcdh, 0);
3676                        } catch (RemoteException e) {
3677                            // remote control client is DOA, disqualify it
3678                            Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
3679                            rcse.mRcClient = null;
3680                        }
3681                        rcse.mRcClientDeathHandler = rcdh;
3682                        break;
3683                    }
3684                }
3685                // if the eventReceiver is at the top of the stack
3686                // then check for potential refresh of the remote controls
3687                if (isCurrentRcController(mediaIntent)) {
3688                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3689                }
3690            }
3691        }
3692    }
3693
3694    /**
3695     * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
3696     * rcClient is guaranteed non-null
3697     */
3698    public void unregisterRemoteControlClient(PendingIntent mediaIntent,
3699            IRemoteControlClient rcClient) {
3700        synchronized(mAudioFocusLock) {
3701            synchronized(mRCStack) {
3702                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3703                while(stackIterator.hasNext()) {
3704                    RemoteControlStackEntry rcse = stackIterator.next();
3705                    if ((rcse.mMediaIntent.equals(mediaIntent))
3706                            && rcClient.equals(rcse.mRcClient)) {
3707                        // we found the IRemoteControlClient to unregister
3708                        // stop monitoring its death
3709                        rcse.unlinkToRcClientDeath();
3710                        // reset the client-related fields
3711                        rcse.mRcClient = null;
3712                        rcse.mCallingPackageName = null;
3713                    }
3714                }
3715            }
3716        }
3717    }
3718
3719    /**
3720     * The remote control displays.
3721     * Access synchronized on mRCStack
3722     * NOTE: Only one IRemoteControlDisplay supported in this implementation
3723     */
3724    private IRemoteControlDisplay mRcDisplay;
3725    private RcDisplayDeathHandler mRcDisplayDeathHandler;
3726    private int mArtworkExpectedWidth = -1;
3727    private int mArtworkExpectedHeight = -1;
3728    /**
3729     * Inner class to monitor remote control display deaths, and unregister them from the list
3730     * of displays if necessary.
3731     */
3732    private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
3733        private IBinder mCb; // To be notified of client's death
3734
3735        public RcDisplayDeathHandler(IBinder b) {
3736            if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b);
3737            mCb = b;
3738        }
3739
3740        public void binderDied() {
3741            synchronized(mRCStack) {
3742                Log.w(TAG, "RemoteControl: display died");
3743                mRcDisplay = null;
3744            }
3745        }
3746
3747        public void unlinkToRcDisplayDeath() {
3748            if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb);
3749            try {
3750                mCb.unlinkToDeath(this, 0);
3751            } catch (java.util.NoSuchElementException e) {
3752                // not much we can do here, the display was being unregistered anyway
3753                Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()");
3754                e.printStackTrace();
3755            }
3756        }
3757
3758    }
3759
3760    private void rcDisplay_stopDeathMonitor_syncRcStack() {
3761        if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null)
3762            // we had a display before, stop monitoring its death
3763            mRcDisplayDeathHandler.unlinkToRcDisplayDeath();
3764        }
3765    }
3766
3767    private void rcDisplay_startDeathMonitor_syncRcStack() {
3768        if (mRcDisplay != null) {
3769            // new non-null display, monitor its death
3770            IBinder b = mRcDisplay.asBinder();
3771            mRcDisplayDeathHandler = new RcDisplayDeathHandler(b);
3772            try {
3773                b.linkToDeath(mRcDisplayDeathHandler, 0);
3774            } catch (RemoteException e) {
3775                // remote control display is DOA, disqualify it
3776                Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
3777                mRcDisplay = null;
3778            }
3779        }
3780    }
3781
3782    /**
3783     * Register an IRemoteControlDisplay.
3784     * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
3785     * at the top of the stack to update the new display with its information.
3786     * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
3787     * @param rcd the IRemoteControlDisplay to register. No effect if null.
3788     */
3789    public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
3790        if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
3791        synchronized(mAudioFocusLock) {
3792            synchronized(mRCStack) {
3793                if ((mRcDisplay == rcd) || (rcd == null)) {
3794                    return;
3795                }
3796                // if we had a display before, stop monitoring its death
3797                rcDisplay_stopDeathMonitor_syncRcStack();
3798                mRcDisplay = rcd;
3799                // new display, start monitoring its death
3800                rcDisplay_startDeathMonitor_syncRcStack();
3801
3802                // let all the remote control clients there is a new display
3803                // no need to unplug the previous because we only support one display
3804                // and the clients don't track the death of the display
3805                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3806                while(stackIterator.hasNext()) {
3807                    RemoteControlStackEntry rcse = stackIterator.next();
3808                    if(rcse.mRcClient != null) {
3809                        try {
3810                            rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
3811                        } catch (RemoteException e) {
3812                            Log.e(TAG, "Error connecting remote control display to client: " + e);
3813                            e.printStackTrace();
3814                        }
3815                    }
3816                }
3817
3818                // we have a new display, of which all the clients are now aware: have it be updated
3819                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3820            }
3821        }
3822    }
3823
3824    /**
3825     * Unregister an IRemoteControlDisplay.
3826     * Since only one IRemoteControlDisplay is supported, this has no effect if the one to
3827     *    unregister is not the current one.
3828     * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
3829     */
3830    public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
3831        if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
3832        synchronized(mRCStack) {
3833            // only one display here, so you can only unregister the current display
3834            if ((rcd == null) || (rcd != mRcDisplay)) {
3835                if (DEBUG_RC) Log.w(TAG, "    trying to unregister unregistered RCD");
3836                return;
3837            }
3838            // if we had a display before, stop monitoring its death
3839            rcDisplay_stopDeathMonitor_syncRcStack();
3840            mRcDisplay = null;
3841
3842            // disconnect this remote control display from all the clients
3843            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3844            while(stackIterator.hasNext()) {
3845                RemoteControlStackEntry rcse = stackIterator.next();
3846                if(rcse.mRcClient != null) {
3847                    try {
3848                        rcse.mRcClient.unplugRemoteControlDisplay(rcd);
3849                    } catch (RemoteException e) {
3850                        Log.e(TAG, "Error disconnecting remote control display to client: " + e);
3851                        e.printStackTrace();
3852                    }
3853                }
3854            }
3855        }
3856    }
3857
3858    public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
3859        synchronized(mRCStack) {
3860            // NOTE: Only one IRemoteControlDisplay supported in this implementation
3861            mArtworkExpectedWidth = w;
3862            mArtworkExpectedHeight = h;
3863        }
3864    }
3865
3866    @Override
3867    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
3868        // TODO probably a lot more to do here than just the audio focus and remote control stacks
3869        dumpFocusStack(pw);
3870        dumpRCStack(pw);
3871    }
3872
3873
3874}
3875