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