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