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