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