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