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