AudioService.java revision 9760647dd0ee67e7c20f3e9d661d2006b1df0b54
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_REPLACE,
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_NOOP,
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_REPLACE,
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_NOOP,
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            int index = Settings.System.getInt(mContentResolver,
2039                            mVolumeIndexSettingName,
2040                            AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
2041
2042            mIndex.clear();
2043            mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
2044
2045            index = Settings.System.getInt(mContentResolver,
2046                            mLastAudibleVolumeIndexSettingName,
2047                            (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
2048            mLastAudibleIndex.clear();
2049            mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
2050
2051            int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
2052            for (int i = 0; remainingDevices != 0; i++) {
2053                int device = (1 << i);
2054                if ((device & remainingDevices) == 0) {
2055                    continue;
2056                }
2057                remainingDevices &= ~device;
2058
2059                // retrieve current volume for device
2060                String name = getSettingNameForDevice(false, device);
2061                index = Settings.System.getInt(mContentResolver, name, -1);
2062                if (index == -1) {
2063                    continue;
2064                }
2065                mIndex.put(device, getValidIndex(10 * index));
2066
2067                // retrieve last audible volume for device
2068                name = getSettingNameForDevice(true, device);
2069                index = Settings.System.getInt(mContentResolver, name, -1);
2070                mLastAudibleIndex.put(device, getValidIndex(10 * index));
2071            }
2072        }
2073
2074        public void applyDeviceVolume(int device) {
2075            AudioSystem.setStreamVolumeIndex(mStreamType,
2076                                             (getIndex(device, false  /* lastAudible */) + 5)/10,
2077                                             device);
2078        }
2079
2080        public void applyAllVolumes() {
2081            // apply default volume first: by convention this will reset all
2082            // devices volumes in audio policy manager to the supplied value
2083            AudioSystem.setStreamVolumeIndex(mStreamType,
2084                    (getIndex(AudioSystem.DEVICE_OUT_DEFAULT, false /* lastAudible */) + 5)/10,
2085                    AudioSystem.DEVICE_OUT_DEFAULT);
2086            // then apply device specific volumes
2087            Set set = mIndex.entrySet();
2088            Iterator i = set.iterator();
2089            while (i.hasNext()) {
2090                Map.Entry entry = (Map.Entry)i.next();
2091                int device = ((Integer)entry.getKey()).intValue();
2092                if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
2093                    AudioSystem.setStreamVolumeIndex(mStreamType,
2094                                                     ((Integer)entry.getValue() + 5)/10,
2095                                                     device);
2096                }
2097            }
2098        }
2099
2100        public boolean adjustIndex(int deltaIndex, int device) {
2101            return setIndex(getIndex(device,
2102                                     false  /* lastAudible */) + deltaIndex * 10,
2103                            device,
2104                            true  /* lastAudible */);
2105        }
2106
2107        public boolean setIndex(int index, int device, boolean lastAudible) {
2108            int oldIndex = getIndex(device, false  /* lastAudible */);
2109            index = getValidIndex(index);
2110            mIndex.put(device, getValidIndex(index));
2111
2112            if (oldIndex != index) {
2113                if (lastAudible) {
2114                    mLastAudibleIndex.put(device, index);
2115                }
2116                // Apply change to all streams using this one as alias
2117                int numStreamTypes = AudioSystem.getNumStreamTypes();
2118                for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2119                    if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
2120                        mStreamStates[streamType].setIndex(rescaleIndex(index,
2121                                                                        mStreamType,
2122                                                                        streamType),
2123                                                           device,
2124                                                           lastAudible);
2125                    }
2126                }
2127                return true;
2128            } else {
2129                return false;
2130            }
2131        }
2132
2133        public int getIndex(int device, boolean lastAudible) {
2134            HashMap <Integer, Integer> indexes;
2135            if (lastAudible) {
2136                indexes = mLastAudibleIndex;
2137            } else {
2138                indexes = mIndex;
2139            }
2140            Integer index = indexes.get(device);
2141            if (index == null) {
2142                // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
2143                index = indexes.get(AudioSystem.DEVICE_OUT_DEFAULT);
2144            }
2145            return index.intValue();
2146        }
2147
2148        public void setLastAudibleIndex(int index, int device) {
2149            mLastAudibleIndex.put(device, getValidIndex(index));
2150        }
2151
2152        public void adjustLastAudibleIndex(int deltaIndex, int device) {
2153            setLastAudibleIndex(getIndex(device,
2154                                         true  /* lastAudible */) + deltaIndex * 10,
2155                                device);
2156        }
2157
2158        public int getMaxIndex() {
2159            return mIndexMax;
2160        }
2161
2162        public void mute(IBinder cb, boolean state) {
2163            VolumeDeathHandler handler = getDeathHandler(cb, state);
2164            if (handler == null) {
2165                Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
2166                return;
2167            }
2168            handler.mute(state);
2169        }
2170
2171        private int getValidIndex(int index) {
2172            if (index < 0) {
2173                return 0;
2174            } else if (index > mIndexMax) {
2175                return mIndexMax;
2176            }
2177
2178            return index;
2179        }
2180
2181        private class VolumeDeathHandler implements IBinder.DeathRecipient {
2182            private IBinder mICallback; // To be notified of client's death
2183            private int mMuteCount; // Number of active mutes for this client
2184
2185            VolumeDeathHandler(IBinder cb) {
2186                mICallback = cb;
2187            }
2188
2189            public void mute(boolean state) {
2190                synchronized(mDeathHandlers) {
2191                    if (state) {
2192                        if (mMuteCount == 0) {
2193                            // Register for client death notification
2194                            try {
2195                                // mICallback can be 0 if muted by AudioService
2196                                if (mICallback != null) {
2197                                    mICallback.linkToDeath(this, 0);
2198                                }
2199                                mDeathHandlers.add(this);
2200                                // If the stream is not yet muted by any client, set level to 0
2201                                if (muteCount() == 0) {
2202                                    Set set = mIndex.entrySet();
2203                                    Iterator i = set.iterator();
2204                                    while (i.hasNext()) {
2205                                        Map.Entry entry = (Map.Entry)i.next();
2206                                        int device = ((Integer)entry.getKey()).intValue();
2207                                        setIndex(0, device, false /* lastAudible */);
2208                                    }
2209                                    sendMsg(mAudioHandler,
2210                                            MSG_SET_ALL_VOLUMES,
2211                                            SENDMSG_NOOP,
2212                                            0,
2213                                            0,
2214                                            VolumeStreamState.this, 0);
2215                                }
2216                            } catch (RemoteException e) {
2217                                // Client has died!
2218                                binderDied();
2219                                mDeathHandlers.notify();
2220                                return;
2221                            }
2222                        } else {
2223                            Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
2224                        }
2225                        mMuteCount++;
2226                    } else {
2227                        if (mMuteCount == 0) {
2228                            Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
2229                        } else {
2230                            mMuteCount--;
2231                            if (mMuteCount == 0) {
2232                                // Unregistr from client death notification
2233                                mDeathHandlers.remove(this);
2234                                // mICallback can be 0 if muted by AudioService
2235                                if (mICallback != null) {
2236                                    mICallback.unlinkToDeath(this, 0);
2237                                }
2238                                if (muteCount() == 0) {
2239                                    // If the stream is not muted any more, restore it's volume if
2240                                    // ringer mode allows it
2241                                    if (!isStreamAffectedByRingerMode(mStreamType) ||
2242                                            mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
2243                                        Set set = mIndex.entrySet();
2244                                        Iterator i = set.iterator();
2245                                        while (i.hasNext()) {
2246                                            Map.Entry entry = (Map.Entry)i.next();
2247                                            int device = ((Integer)entry.getKey()).intValue();
2248                                            setIndex(getIndex(device,
2249                                                              true  /* lastAudible */),
2250                                                     device,
2251                                                     false  /* lastAudible */);
2252                                        }
2253                                        sendMsg(mAudioHandler,
2254                                                MSG_SET_ALL_VOLUMES,
2255                                                SENDMSG_NOOP,
2256                                                0,
2257                                                0,
2258                                                VolumeStreamState.this, 0);
2259                                    }
2260                                }
2261                            }
2262                        }
2263                    }
2264                    mDeathHandlers.notify();
2265                }
2266            }
2267
2268            public void binderDied() {
2269                Log.w(TAG, "Volume service client died for stream: "+mStreamType);
2270                if (mMuteCount != 0) {
2271                    // Reset all active mute requests from this client.
2272                    mMuteCount = 1;
2273                    mute(false);
2274                }
2275            }
2276        }
2277
2278        private int muteCount() {
2279            int count = 0;
2280            int size = mDeathHandlers.size();
2281            for (int i = 0; i < size; i++) {
2282                count += mDeathHandlers.get(i).mMuteCount;
2283            }
2284            return count;
2285        }
2286
2287        private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
2288            synchronized(mDeathHandlers) {
2289                VolumeDeathHandler handler;
2290                int size = mDeathHandlers.size();
2291                for (int i = 0; i < size; i++) {
2292                    handler = mDeathHandlers.get(i);
2293                    if (cb == handler.mICallback) {
2294                        return handler;
2295                    }
2296                }
2297                // If this is the first mute request for this client, create a new
2298                // client death handler. Otherwise, it is an out of sequence unmute request.
2299                if (state) {
2300                    handler = new VolumeDeathHandler(cb);
2301                } else {
2302                    Log.w(TAG, "stream was not muted by this client");
2303                    handler = null;
2304                }
2305                return handler;
2306            }
2307        }
2308    }
2309
2310    /** Thread that handles native AudioSystem control. */
2311    private class AudioSystemThread extends Thread {
2312        AudioSystemThread() {
2313            super("AudioService");
2314        }
2315
2316        @Override
2317        public void run() {
2318            // Set this thread up so the handler will work on it
2319            Looper.prepare();
2320
2321            synchronized(AudioService.this) {
2322                mAudioHandler = new AudioHandler();
2323
2324                // Notify that the handler has been created
2325                AudioService.this.notify();
2326            }
2327
2328            // Listen for volume change requests that are set by VolumePanel
2329            Looper.loop();
2330        }
2331    }
2332
2333    /** Handles internal volume messages in separate volume thread. */
2334    private class AudioHandler extends Handler {
2335
2336        private void setDeviceVolume(VolumeStreamState streamState, int device) {
2337
2338            // Apply volume
2339            streamState.applyDeviceVolume(device);
2340
2341            // Apply change to all streams using this one as alias
2342            int numStreamTypes = AudioSystem.getNumStreamTypes();
2343            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2344                if (streamType != streamState.mStreamType &&
2345                        STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
2346                    mStreamStates[streamType].applyDeviceVolume(device);
2347                }
2348            }
2349
2350            // Post a persist volume msg
2351            sendMsg(mAudioHandler,
2352                    MSG_PERSIST_VOLUME,
2353                    SENDMSG_REPLACE,
2354                    PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
2355                    device,
2356                    streamState,
2357                    PERSIST_DELAY);
2358
2359        }
2360
2361        private void setAllVolumes(VolumeStreamState streamState) {
2362
2363            // Apply volume
2364            streamState.applyAllVolumes();
2365
2366            // Apply change to all streams using this one as alias
2367            int numStreamTypes = AudioSystem.getNumStreamTypes();
2368            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2369                if (streamType != streamState.mStreamType &&
2370                        STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
2371                    mStreamStates[streamType].applyAllVolumes();
2372                }
2373            }
2374        }
2375
2376        private void persistVolume(VolumeStreamState streamState,
2377                                   int persistType,
2378                                   int device) {
2379            if ((persistType & PERSIST_CURRENT) != 0) {
2380                System.putInt(mContentResolver,
2381                          streamState.getSettingNameForDevice(false /* lastAudible */, device),
2382                          (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10);
2383            }
2384            if ((persistType & PERSIST_LAST_AUDIBLE) != 0) {
2385                System.putInt(mContentResolver,
2386                        streamState.getSettingNameForDevice(true /* lastAudible */, device),
2387                        (streamState.getIndex(device, true  /* lastAudible */) + 5) / 10);
2388            }
2389        }
2390
2391        private void persistRingerMode(int ringerMode) {
2392            System.putInt(mContentResolver, System.MODE_RINGER, ringerMode);
2393        }
2394
2395        private void persistVibrateSetting() {
2396            System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
2397        }
2398
2399        private void playSoundEffect(int effectType, int volume) {
2400            synchronized (mSoundEffectsLock) {
2401                if (mSoundPool == null) {
2402                    return;
2403                }
2404                float volFloat;
2405                // use default if volume is not specified by caller
2406                if (volume < 0) {
2407                    volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
2408                } else {
2409                    volFloat = (float) volume / 1000.0f;
2410                }
2411
2412                if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
2413                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
2414                } else {
2415                    MediaPlayer mediaPlayer = new MediaPlayer();
2416                    try {
2417                        String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
2418                        mediaPlayer.setDataSource(filePath);
2419                        mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
2420                        mediaPlayer.prepare();
2421                        mediaPlayer.setVolume(volFloat, volFloat);
2422                        mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
2423                            public void onCompletion(MediaPlayer mp) {
2424                                cleanupPlayer(mp);
2425                            }
2426                        });
2427                        mediaPlayer.setOnErrorListener(new OnErrorListener() {
2428                            public boolean onError(MediaPlayer mp, int what, int extra) {
2429                                cleanupPlayer(mp);
2430                                return true;
2431                            }
2432                        });
2433                        mediaPlayer.start();
2434                    } catch (IOException ex) {
2435                        Log.w(TAG, "MediaPlayer IOException: "+ex);
2436                    } catch (IllegalArgumentException ex) {
2437                        Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
2438                    } catch (IllegalStateException ex) {
2439                        Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2440                    }
2441                }
2442            }
2443        }
2444
2445        private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
2446            Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
2447                    receiver == null ? "" : receiver.flattenToString());
2448        }
2449
2450        private void cleanupPlayer(MediaPlayer mp) {
2451            if (mp != null) {
2452                try {
2453                    mp.stop();
2454                    mp.release();
2455                } catch (IllegalStateException ex) {
2456                    Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2457                }
2458            }
2459        }
2460
2461        private void setForceUse(int usage, int config) {
2462            AudioSystem.setForceUse(usage, config);
2463        }
2464
2465        @Override
2466        public void handleMessage(Message msg) {
2467
2468            switch (msg.what) {
2469
2470                case MSG_SET_DEVICE_VOLUME:
2471                    setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
2472                    break;
2473
2474                case MSG_SET_ALL_VOLUMES:
2475                    setAllVolumes((VolumeStreamState) msg.obj);
2476                    break;
2477
2478                case MSG_PERSIST_VOLUME:
2479                    persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2);
2480                    break;
2481
2482                case MSG_PERSIST_MASTER_VOLUME:
2483                    Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER,
2484                            (float)msg.arg1 / (float)1000.0);
2485                    break;
2486
2487                case MSG_PERSIST_RINGER_MODE:
2488                    // note that the value persisted is the current ringer mode, not the
2489                    // value of ringer mode as of the time the request was made to persist
2490                    persistRingerMode(getRingerMode());
2491                    break;
2492
2493                case MSG_PERSIST_VIBRATE_SETTING:
2494                    persistVibrateSetting();
2495                    break;
2496
2497                case MSG_MEDIA_SERVER_DIED:
2498                    if (!mMediaServerOk) {
2499                        Log.e(TAG, "Media server died.");
2500                        // Force creation of new IAudioFlinger interface so that we are notified
2501                        // when new media_server process is back to life.
2502                        AudioSystem.setErrorCallback(mAudioSystemCallback);
2503                        sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
2504                                null, 500);
2505                    }
2506                    break;
2507
2508                case MSG_MEDIA_SERVER_STARTED:
2509                    Log.e(TAG, "Media server started.");
2510                    // indicate to audio HAL that we start the reconfiguration phase after a media
2511                    // server crash
2512                    // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
2513                    // process restarts after a crash, not the first time it is started.
2514                    AudioSystem.setParameters("restarting=true");
2515
2516                    // Restore device connection states
2517                    synchronized (mConnectedDevices) {
2518                        Set set = mConnectedDevices.entrySet();
2519                        Iterator i = set.iterator();
2520                        while (i.hasNext()) {
2521                            Map.Entry device = (Map.Entry)i.next();
2522                            AudioSystem.setDeviceConnectionState(
2523                                                            ((Integer)device.getKey()).intValue(),
2524                                                            AudioSystem.DEVICE_STATE_AVAILABLE,
2525                                                            (String)device.getValue());
2526                        }
2527                    }
2528                    // Restore call state
2529                    AudioSystem.setPhoneState(mMode);
2530
2531                    // Restore forced usage for communcations and record
2532                    AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
2533                    AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
2534
2535                    // Restore stream volumes
2536                    int numStreamTypes = AudioSystem.getNumStreamTypes();
2537                    for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2538                        VolumeStreamState streamState = mStreamStates[streamType];
2539                        AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
2540
2541                        streamState.applyAllVolumes();
2542                    }
2543
2544                    // Restore ringer mode
2545                    setRingerModeInt(getRingerMode(), false);
2546
2547                    // Restore master volume
2548                    restoreMasterVolume();
2549
2550                    // indicate the end of reconfiguration phase to audio HAL
2551                    AudioSystem.setParameters("restarting=false");
2552                    break;
2553
2554                case MSG_LOAD_SOUND_EFFECTS:
2555                    loadSoundEffects();
2556                    break;
2557
2558                case MSG_PLAY_SOUND_EFFECT:
2559                    playSoundEffect(msg.arg1, msg.arg2);
2560                    break;
2561
2562                case MSG_BTA2DP_DOCK_TIMEOUT:
2563                    // msg.obj  == address of BTA2DP device
2564                    synchronized (mConnectedDevices) {
2565                        makeA2dpDeviceUnavailableNow( (String) msg.obj );
2566                    }
2567                    break;
2568
2569                case MSG_SET_FORCE_USE:
2570                    setForceUse(msg.arg1, msg.arg2);
2571                    break;
2572
2573                case MSG_PERSIST_MEDIABUTTONRECEIVER:
2574                    onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
2575                    break;
2576
2577                case MSG_RCDISPLAY_CLEAR:
2578                    onRcDisplayClear();
2579                    break;
2580
2581                case MSG_RCDISPLAY_UPDATE:
2582                    // msg.obj is guaranteed to be non null
2583                    onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
2584                    break;
2585
2586                case MSG_BT_HEADSET_CNCT_FAILED:
2587                    resetBluetoothSco();
2588                    break;
2589            }
2590        }
2591    }
2592
2593    private class SettingsObserver extends ContentObserver {
2594
2595        SettingsObserver() {
2596            super(new Handler());
2597            mContentResolver.registerContentObserver(Settings.System.getUriFor(
2598                Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
2599        }
2600
2601        @Override
2602        public void onChange(boolean selfChange) {
2603            super.onChange(selfChange);
2604            // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode.
2605            //       However there appear to be some missing locks around mRingerModeMutedStreams
2606            //       and mRingerModeAffectedStreams, so will leave this synchronized for now.
2607            //       mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once).
2608            synchronized (mSettingsLock) {
2609                int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
2610                       Settings.System.MODE_RINGER_STREAMS_AFFECTED,
2611                       ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
2612                       (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
2613                if (mVoiceCapable) {
2614                    ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
2615                } else {
2616                    ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
2617                }
2618                if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
2619                    /*
2620                     * Ensure all stream types that should be affected by ringer mode
2621                     * are in the proper state.
2622                     */
2623                    mRingerModeAffectedStreams = ringerModeAffectedStreams;
2624                    setRingerModeInt(getRingerMode(), false);
2625                }
2626            }
2627        }
2628    }
2629
2630    // must be called synchronized on mConnectedDevices
2631    private void makeA2dpDeviceAvailable(String address) {
2632        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2633                AudioSystem.DEVICE_STATE_AVAILABLE,
2634                address);
2635        // Reset A2DP suspend state each time a new sink is connected
2636        AudioSystem.setParameters("A2dpSuspended=false");
2637        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
2638                address);
2639    }
2640
2641    // must be called synchronized on mConnectedDevices
2642    private void makeA2dpDeviceUnavailableNow(String address) {
2643        Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
2644        mContext.sendBroadcast(noisyIntent);
2645        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2646                AudioSystem.DEVICE_STATE_UNAVAILABLE,
2647                address);
2648        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2649    }
2650
2651    // must be called synchronized on mConnectedDevices
2652    private void makeA2dpDeviceUnavailableLater(String address) {
2653        // prevent any activity on the A2DP audio output to avoid unwanted
2654        // reconnection of the sink.
2655        AudioSystem.setParameters("A2dpSuspended=true");
2656        // the device will be made unavailable later, so consider it disconnected right away
2657        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
2658        // send the delayed message to make the device unavailable later
2659        Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
2660        mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
2661
2662    }
2663
2664    // must be called synchronized on mConnectedDevices
2665    private void cancelA2dpDeviceTimeout() {
2666        mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2667    }
2668
2669    // must be called synchronized on mConnectedDevices
2670    private boolean hasScheduledA2dpDockTimeout() {
2671        return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
2672    }
2673
2674    private void handleA2dpConnectionStateChange(BluetoothDevice btDevice, int state)
2675    {
2676        if (btDevice == null) {
2677            return;
2678        }
2679        String address = btDevice.getAddress();
2680        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2681            address = "";
2682        }
2683        synchronized (mConnectedDevices) {
2684            boolean isConnected =
2685                (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
2686                 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
2687
2688            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
2689                if (btDevice.isBluetoothDock()) {
2690                    if (state == BluetoothProfile.STATE_DISCONNECTED) {
2691                        // introduction of a delay for transient disconnections of docks when
2692                        // power is rapidly turned off/on, this message will be canceled if
2693                        // we reconnect the dock under a preset delay
2694                        makeA2dpDeviceUnavailableLater(address);
2695                        // the next time isConnected is evaluated, it will be false for the dock
2696                    }
2697                } else {
2698                    makeA2dpDeviceUnavailableNow(address);
2699                }
2700            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
2701                if (btDevice.isBluetoothDock()) {
2702                    // this could be a reconnection after a transient disconnection
2703                    cancelA2dpDeviceTimeout();
2704                    mDockAddress = address;
2705                } else {
2706                    // this could be a connection of another A2DP device before the timeout of
2707                    // a dock: cancel the dock timeout, and make the dock unavailable now
2708                    if(hasScheduledA2dpDockTimeout()) {
2709                        cancelA2dpDeviceTimeout();
2710                        makeA2dpDeviceUnavailableNow(mDockAddress);
2711                    }
2712                }
2713                makeA2dpDeviceAvailable(address);
2714            }
2715        }
2716    }
2717
2718    /* cache of the address of the last dock the device was connected to */
2719    private String mDockAddress;
2720
2721    /**
2722     * Receiver for misc intent broadcasts the Phone app cares about.
2723     */
2724    private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
2725        @Override
2726        public void onReceive(Context context, Intent intent) {
2727            String action = intent.getAction();
2728
2729            if (action.equals(Intent.ACTION_DOCK_EVENT)) {
2730                int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
2731                        Intent.EXTRA_DOCK_STATE_UNDOCKED);
2732                int config;
2733                switch (dockState) {
2734                    case Intent.EXTRA_DOCK_STATE_DESK:
2735                        config = AudioSystem.FORCE_BT_DESK_DOCK;
2736                        break;
2737                    case Intent.EXTRA_DOCK_STATE_CAR:
2738                        config = AudioSystem.FORCE_BT_CAR_DOCK;
2739                        break;
2740                    case Intent.EXTRA_DOCK_STATE_LE_DESK:
2741                        config = AudioSystem.FORCE_ANALOG_DOCK;
2742                        break;
2743                    case Intent.EXTRA_DOCK_STATE_HE_DESK:
2744                        config = AudioSystem.FORCE_DIGITAL_DOCK;
2745                        break;
2746                    case Intent.EXTRA_DOCK_STATE_UNDOCKED:
2747                    default:
2748                        config = AudioSystem.FORCE_NONE;
2749                }
2750                AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
2751            } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
2752                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
2753                                               BluetoothProfile.STATE_DISCONNECTED);
2754                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
2755
2756                handleA2dpConnectionStateChange(btDevice, state);
2757            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
2758                int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
2759                                               BluetoothProfile.STATE_DISCONNECTED);
2760                int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
2761                String address = null;
2762
2763                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
2764                if (btDevice == null) {
2765                    return;
2766                }
2767
2768                address = btDevice.getAddress();
2769                BluetoothClass btClass = btDevice.getBluetoothClass();
2770                if (btClass != null) {
2771                    switch (btClass.getDeviceClass()) {
2772                    case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
2773                    case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
2774                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
2775                        break;
2776                    case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
2777                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
2778                        break;
2779                    }
2780                }
2781
2782                if (!BluetoothAdapter.checkBluetoothAddress(address)) {
2783                    address = "";
2784                }
2785
2786                synchronized (mConnectedDevices) {
2787                    boolean isConnected = (mConnectedDevices.containsKey(device) &&
2788                                           mConnectedDevices.get(device).equals(address));
2789
2790                    synchronized (mScoClients) {
2791                        if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
2792                            AudioSystem.setDeviceConnectionState(device,
2793                                                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
2794                                                             address);
2795                            mConnectedDevices.remove(device);
2796                            mBluetoothHeadsetDevice = null;
2797                            resetBluetoothSco();
2798                        } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
2799                            AudioSystem.setDeviceConnectionState(device,
2800                                                                 AudioSystem.DEVICE_STATE_AVAILABLE,
2801                                                                 address);
2802                            mConnectedDevices.put(new Integer(device), address);
2803                            mBluetoothHeadsetDevice = btDevice;
2804                        }
2805                    }
2806                }
2807            } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
2808                int state = intent.getIntExtra("state", 0);
2809                int microphone = intent.getIntExtra("microphone", 0);
2810
2811                synchronized (mConnectedDevices) {
2812                    if (microphone != 0) {
2813                        boolean isConnected =
2814                            mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
2815                        if (state == 0 && isConnected) {
2816                            AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
2817                                    AudioSystem.DEVICE_STATE_UNAVAILABLE,
2818                                    "");
2819                            mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
2820                        } else if (state == 1 && !isConnected)  {
2821                            AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
2822                                    AudioSystem.DEVICE_STATE_AVAILABLE,
2823                                    "");
2824                            mConnectedDevices.put(
2825                                    new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
2826                        }
2827                    } else {
2828                        boolean isConnected =
2829                            mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
2830                        if (state == 0 && isConnected) {
2831                            AudioSystem.setDeviceConnectionState(
2832                                    AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
2833                                    AudioSystem.DEVICE_STATE_UNAVAILABLE,
2834                                    "");
2835                            mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
2836                        } else if (state == 1 && !isConnected)  {
2837                            AudioSystem.setDeviceConnectionState(
2838                                    AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
2839                                    AudioSystem.DEVICE_STATE_AVAILABLE,
2840                                    "");
2841                            mConnectedDevices.put(
2842                                    new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
2843                        }
2844                    }
2845                }
2846            } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) {
2847                int state = intent.getIntExtra("state", 0);
2848                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state);
2849                synchronized (mConnectedDevices) {
2850                    boolean isConnected =
2851                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
2852                    if (state == 0 && isConnected) {
2853                        AudioSystem.setDeviceConnectionState(
2854                                                        AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
2855                                                        AudioSystem.DEVICE_STATE_UNAVAILABLE,
2856                                                        "");
2857                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET);
2858                    } else if (state == 1 && !isConnected)  {
2859                        AudioSystem.setDeviceConnectionState(
2860                                                        AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET,
2861                                                        AudioSystem.DEVICE_STATE_AVAILABLE,
2862                                                        "");
2863                        mConnectedDevices.put(
2864                                new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), "");
2865                    }
2866                }
2867            } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) {
2868                int state = intent.getIntExtra("state", 0);
2869                Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state);
2870                synchronized (mConnectedDevices) {
2871                    boolean isConnected =
2872                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
2873                    if (state == 0 && isConnected) {
2874                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
2875                                                             AudioSystem.DEVICE_STATE_UNAVAILABLE,
2876                                                             "");
2877                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL);
2878                    } else if (state == 1 && !isConnected)  {
2879                        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL,
2880                                                             AudioSystem.DEVICE_STATE_AVAILABLE,
2881                                                             "");
2882                        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), "");
2883                    }
2884                }
2885            } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) {
2886                int state = intent.getIntExtra("state", 0);
2887                Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state);
2888                synchronized (mConnectedDevices) {
2889                    boolean isConnected =
2890                        mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
2891                    if (state == 0 && isConnected) {
2892                        AudioSystem.setDeviceConnectionState(
2893                                                         AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
2894                                                         AudioSystem.DEVICE_STATE_UNAVAILABLE,
2895                                                         "");
2896                        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET);
2897                    } else if (state == 1 && !isConnected)  {
2898                        AudioSystem.setDeviceConnectionState(
2899                                                         AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET,
2900                                                         AudioSystem.DEVICE_STATE_AVAILABLE,
2901                                                         "");
2902                        mConnectedDevices.put(
2903                                new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), "");
2904                    }
2905                }
2906            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
2907                boolean broadcast = false;
2908                int state = AudioManager.SCO_AUDIO_STATE_ERROR;
2909                synchronized (mScoClients) {
2910                    int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
2911                    // broadcast intent if the connection was initated by AudioService
2912                    if (!mScoClients.isEmpty() &&
2913                            (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
2914                             mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
2915                             mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
2916                        broadcast = true;
2917                    }
2918                    switch (btState) {
2919                    case BluetoothHeadset.STATE_AUDIO_CONNECTED:
2920                        state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
2921                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
2922                            mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
2923                            mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
2924                            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
2925                        }
2926                        break;
2927                    case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
2928                        state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
2929                        mScoAudioState = SCO_STATE_INACTIVE;
2930                        clearAllScoClients(0, false);
2931                        break;
2932                    case BluetoothHeadset.STATE_AUDIO_CONNECTING:
2933                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
2934                            mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
2935                            mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
2936                            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
2937                        }
2938                    default:
2939                        // do not broadcast CONNECTING or invalid state
2940                        broadcast = false;
2941                        break;
2942                    }
2943                }
2944                if (broadcast) {
2945                    broadcastScoConnectionState(state);
2946                    //FIXME: this is to maintain compatibility with deprecated intent
2947                    // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
2948                    Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
2949                    newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
2950                    mContext.sendStickyBroadcast(newIntent);
2951                }
2952            } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
2953                mBootCompleted = true;
2954                sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP,
2955                        0, 0, null, 0);
2956
2957                mKeyguardManager =
2958                        (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
2959                mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
2960                resetBluetoothSco();
2961                getBluetoothHeadset();
2962                //FIXME: this is to maintain compatibility with deprecated intent
2963                // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
2964                Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
2965                newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
2966                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
2967                mContext.sendStickyBroadcast(newIntent);
2968
2969                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
2970                if (adapter != null) {
2971                    adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
2972                                            BluetoothProfile.A2DP);
2973                }
2974
2975                if (mUseMasterVolume) {
2976                    // Send sticky broadcast for initial master mute state
2977                    sendMasterMuteUpdate(false, 0);
2978                }
2979            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
2980                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
2981                    // a package is being removed, not replaced
2982                    String packageName = intent.getData().getSchemeSpecificPart();
2983                    if (packageName != null) {
2984                        removeMediaButtonReceiverForPackage(packageName);
2985                    }
2986                }
2987            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
2988                AudioSystem.setParameters("screen_state=on");
2989            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
2990                AudioSystem.setParameters("screen_state=off");
2991            }
2992        }
2993    }
2994
2995    //==========================================================================================
2996    // AudioFocus
2997    //==========================================================================================
2998
2999    /* constant to identify focus stack entry that is used to hold the focus while the phone
3000     * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
3001     * entering and exiting calls.
3002     */
3003    public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
3004
3005    private final static Object mAudioFocusLock = new Object();
3006
3007    private final static Object mRingingLock = new Object();
3008
3009    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
3010        @Override
3011        public void onCallStateChanged(int state, String incomingNumber) {
3012            if (state == TelephonyManager.CALL_STATE_RINGING) {
3013                //Log.v(TAG, " CALL_STATE_RINGING");
3014                synchronized(mRingingLock) {
3015                    mIsRinging = true;
3016                }
3017            } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
3018                    || (state == TelephonyManager.CALL_STATE_IDLE)) {
3019                synchronized(mRingingLock) {
3020                    mIsRinging = false;
3021                }
3022            }
3023        }
3024    };
3025
3026    private void notifyTopOfAudioFocusStack() {
3027        // notify the top of the stack it gained focus
3028        if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
3029            if (canReassignAudioFocus()) {
3030                try {
3031                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
3032                            AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
3033                } catch (RemoteException e) {
3034                    Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
3035                    e.printStackTrace();
3036                }
3037            }
3038        }
3039    }
3040
3041    private static class FocusStackEntry {
3042        public int mStreamType = -1;// no stream type
3043        public IAudioFocusDispatcher mFocusDispatcher = null;
3044        public IBinder mSourceRef = null;
3045        public String mClientId;
3046        public int mFocusChangeType;
3047        public AudioFocusDeathHandler mHandler;
3048        public String mPackageName;
3049        public int mCallingUid;
3050
3051        public FocusStackEntry() {
3052        }
3053
3054        public FocusStackEntry(int streamType, int duration,
3055                IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
3056                String pn, int uid) {
3057            mStreamType = streamType;
3058            mFocusDispatcher = afl;
3059            mSourceRef = source;
3060            mClientId = id;
3061            mFocusChangeType = duration;
3062            mHandler = hdlr;
3063            mPackageName = pn;
3064            mCallingUid = uid;
3065        }
3066
3067        public void unlinkToDeath() {
3068            try {
3069                if (mSourceRef != null && mHandler != null) {
3070                    mSourceRef.unlinkToDeath(mHandler, 0);
3071                    mHandler = null;
3072                }
3073            } catch (java.util.NoSuchElementException e) {
3074                Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
3075            }
3076        }
3077
3078        @Override
3079        protected void finalize() throws Throwable {
3080            unlinkToDeath(); // unlink exception handled inside method
3081            super.finalize();
3082        }
3083    }
3084
3085    private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
3086
3087    /**
3088     * Helper function:
3089     * Display in the log the current entries in the audio focus stack
3090     */
3091    private void dumpFocusStack(PrintWriter pw) {
3092        pw.println("\nAudio Focus stack entries:");
3093        synchronized(mAudioFocusLock) {
3094            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3095            while(stackIterator.hasNext()) {
3096                FocusStackEntry fse = stackIterator.next();
3097                pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
3098                        + " -- duration: " + fse.mFocusChangeType
3099                        + " -- uid: " + fse.mCallingUid);
3100            }
3101        }
3102    }
3103
3104    /**
3105     * Helper function:
3106     * Called synchronized on mAudioFocusLock
3107     * Remove a focus listener from the focus stack.
3108     * @param focusListenerToRemove the focus listener
3109     * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
3110     *   focus, notify the next item in the stack it gained focus.
3111     */
3112    private void removeFocusStackEntry(String clientToRemove, boolean signal) {
3113        // is the current top of the focus stack abandoning focus? (because of request, not death)
3114        if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
3115        {
3116            //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
3117            FocusStackEntry fse = mFocusStack.pop();
3118            fse.unlinkToDeath();
3119            if (signal) {
3120                // notify the new top of the stack it gained focus
3121                notifyTopOfAudioFocusStack();
3122                // there's a new top of the stack, let the remote control know
3123                synchronized(mRCStack) {
3124                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3125                }
3126            }
3127        } else {
3128            // focus is abandoned by a client that's not at the top of the stack,
3129            // no need to update focus.
3130            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3131            while(stackIterator.hasNext()) {
3132                FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
3133                if(fse.mClientId.equals(clientToRemove)) {
3134                    Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
3135                            + fse.mClientId);
3136                    stackIterator.remove();
3137                    fse.unlinkToDeath();
3138                }
3139            }
3140        }
3141    }
3142
3143    /**
3144     * Helper function:
3145     * Called synchronized on mAudioFocusLock
3146     * Remove focus listeners from the focus stack for a particular client when it has died.
3147     */
3148    private void removeFocusStackEntryForClient(IBinder cb) {
3149        // is the owner of the audio focus part of the client to remove?
3150        boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
3151                mFocusStack.peek().mSourceRef.equals(cb);
3152        Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3153        while(stackIterator.hasNext()) {
3154            FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
3155            if(fse.mSourceRef.equals(cb)) {
3156                Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
3157                        + fse.mClientId);
3158                stackIterator.remove();
3159                // the client just died, no need to unlink to its death
3160            }
3161        }
3162        if (isTopOfStackForClientToRemove) {
3163            // we removed an entry at the top of the stack:
3164            //  notify the new top of the stack it gained focus.
3165            notifyTopOfAudioFocusStack();
3166            // there's a new top of the stack, let the remote control know
3167            synchronized(mRCStack) {
3168                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3169            }
3170        }
3171    }
3172
3173    /**
3174     * Helper function:
3175     * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
3176     */
3177    private boolean canReassignAudioFocus() {
3178        // focus requests are rejected during a phone call or when the phone is ringing
3179        // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
3180        if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
3181            return false;
3182        }
3183        return true;
3184    }
3185
3186    /**
3187     * Inner class to monitor audio focus client deaths, and remove them from the audio focus
3188     * stack if necessary.
3189     */
3190    private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
3191        private IBinder mCb; // To be notified of client's death
3192
3193        AudioFocusDeathHandler(IBinder cb) {
3194            mCb = cb;
3195        }
3196
3197        public void binderDied() {
3198            synchronized(mAudioFocusLock) {
3199                Log.w(TAG, "  AudioFocus   audio focus client died");
3200                removeFocusStackEntryForClient(mCb);
3201            }
3202        }
3203
3204        public IBinder getBinder() {
3205            return mCb;
3206        }
3207    }
3208
3209
3210    /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
3211    public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
3212            IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
3213        Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
3214        // the main stream type for the audio focus request is currently not used. It may
3215        // potentially be used to handle multiple stream type-dependent audio focuses.
3216
3217        // we need a valid binder callback for clients
3218        if (!cb.pingBinder()) {
3219            Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
3220            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3221        }
3222
3223        synchronized(mAudioFocusLock) {
3224            if (!canReassignAudioFocus()) {
3225                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3226            }
3227
3228            // handle the potential premature death of the new holder of the focus
3229            // (premature death == death before abandoning focus)
3230            // Register for client death notification
3231            AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
3232            try {
3233                cb.linkToDeath(afdh, 0);
3234            } catch (RemoteException e) {
3235                // client has already died!
3236                Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
3237                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3238            }
3239
3240            if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
3241                // if focus is already owned by this client and the reason for acquiring the focus
3242                // hasn't changed, don't do anything
3243                if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
3244                    // unlink death handler so it can be gc'ed.
3245                    // linkToDeath() creates a JNI global reference preventing collection.
3246                    cb.unlinkToDeath(afdh, 0);
3247                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3248                }
3249                // the reason for the audio focus request has changed: remove the current top of
3250                // stack and respond as if we had a new focus owner
3251                FocusStackEntry fse = mFocusStack.pop();
3252                fse.unlinkToDeath();
3253            }
3254
3255            // notify current top of stack it is losing focus
3256            if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
3257                try {
3258                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
3259                            -1 * focusChangeHint, // loss and gain codes are inverse of each other
3260                            mFocusStack.peek().mClientId);
3261                } catch (RemoteException e) {
3262                    Log.e(TAG, " Failure to signal loss of focus due to "+ e);
3263                    e.printStackTrace();
3264                }
3265            }
3266
3267            // focus requester might already be somewhere below in the stack, remove it
3268            removeFocusStackEntry(clientId, false /* signal */);
3269
3270            // push focus requester at the top of the audio focus stack
3271            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
3272                    clientId, afdh, callingPackageName, Binder.getCallingUid()));
3273
3274            // there's a new top of the stack, let the remote control know
3275            synchronized(mRCStack) {
3276                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3277            }
3278        }//synchronized(mAudioFocusLock)
3279
3280        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3281    }
3282
3283    /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
3284    public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
3285        Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
3286        try {
3287            // this will take care of notifying the new focus owner if needed
3288            synchronized(mAudioFocusLock) {
3289                removeFocusStackEntry(clientId, true);
3290            }
3291        } catch (java.util.ConcurrentModificationException cme) {
3292            // Catching this exception here is temporary. It is here just to prevent
3293            // a crash seen when the "Silent" notification is played. This is believed to be fixed
3294            // but this try catch block is left just to be safe.
3295            Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
3296            cme.printStackTrace();
3297        }
3298
3299        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3300    }
3301
3302
3303    public void unregisterAudioFocusClient(String clientId) {
3304        synchronized(mAudioFocusLock) {
3305            removeFocusStackEntry(clientId, false);
3306        }
3307    }
3308
3309
3310    //==========================================================================================
3311    // RemoteControl
3312    //==========================================================================================
3313    /**
3314     * Receiver for media button intents. Handles the dispatching of the media button event
3315     * to one of the registered listeners, or if there was none, resumes the intent broadcast
3316     * to the rest of the system.
3317     */
3318    private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
3319        @Override
3320        public void onReceive(Context context, Intent intent) {
3321            String action = intent.getAction();
3322            if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
3323                return;
3324            }
3325            KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
3326            if (event != null) {
3327                // if in a call or ringing, do not break the current phone app behavior
3328                // TODO modify this to let the phone app specifically get the RC focus
3329                //      add modify the phone app to take advantage of the new API
3330                synchronized(mRingingLock) {
3331                    if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) ||
3332                            (getMode() == AudioSystem.MODE_IN_COMMUNICATION) ||
3333                            (getMode() == AudioSystem.MODE_RINGTONE) ) {
3334                        return;
3335                    }
3336                }
3337                synchronized(mRCStack) {
3338                    if (!mRCStack.empty()) {
3339                        // create a new intent to fill in the extras of the registered PendingIntent
3340                        Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
3341                        Bundle extras = intent.getExtras();
3342                        if (extras != null) {
3343                            targetedIntent.putExtras(extras);
3344                            // trap the current broadcast
3345                            abortBroadcast();
3346                            //Log.v(TAG, " Sending intent" + targetedIntent);
3347                            // send the intent that was registered by the client
3348                            try {
3349                                mRCStack.peek().mMediaIntent.send(context, 0, targetedIntent);
3350                            } catch (CanceledException e) {
3351                                Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
3352                                e.printStackTrace();
3353                            }
3354                        }
3355                    }
3356                }
3357            }
3358        }
3359    }
3360
3361    private final Object mCurrentRcLock = new Object();
3362    /**
3363     * The one remote control client which will receive a request for display information.
3364     * This object may be null.
3365     * Access protected by mCurrentRcLock.
3366     */
3367    private IRemoteControlClient mCurrentRcClient = null;
3368
3369    private final static int RC_INFO_NONE = 0;
3370    private final static int RC_INFO_ALL =
3371        RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
3372        RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
3373        RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
3374        RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
3375
3376    /**
3377     * A monotonically increasing generation counter for mCurrentRcClient.
3378     * Only accessed with a lock on mCurrentRcLock.
3379     * No value wrap-around issues as we only act on equal values.
3380     */
3381    private int mCurrentRcClientGen = 0;
3382
3383    /**
3384     * Inner class to monitor remote control client deaths, and remove the client for the
3385     * remote control stack if necessary.
3386     */
3387    private class RcClientDeathHandler implements IBinder.DeathRecipient {
3388        private IBinder mCb; // To be notified of client's death
3389        private PendingIntent mMediaIntent;
3390
3391        RcClientDeathHandler(IBinder cb, PendingIntent pi) {
3392            mCb = cb;
3393            mMediaIntent = pi;
3394        }
3395
3396        public void binderDied() {
3397            Log.w(TAG, "  RemoteControlClient died");
3398            // remote control client died, make sure the displays don't use it anymore
3399            //  by setting its remote control client to null
3400            registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
3401        }
3402
3403        public IBinder getBinder() {
3404            return mCb;
3405        }
3406    }
3407
3408    private static class RemoteControlStackEntry {
3409        /**
3410         * The target for the ACTION_MEDIA_BUTTON events.
3411         * Always non null.
3412         */
3413        public PendingIntent mMediaIntent;
3414        /**
3415         * The registered media button event receiver.
3416         * Always non null.
3417         */
3418        public ComponentName mReceiverComponent;
3419        public String mCallingPackageName;
3420        public int mCallingUid;
3421        /**
3422         * Provides access to the information to display on the remote control.
3423         * May be null (when a media button event receiver is registered,
3424         *     but no remote control client has been registered) */
3425        public IRemoteControlClient mRcClient;
3426        public RcClientDeathHandler mRcClientDeathHandler;
3427
3428        /** precondition: mediaIntent != null, eventReceiver != null */
3429        public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
3430            mMediaIntent = mediaIntent;
3431            mReceiverComponent = eventReceiver;
3432            mCallingUid = -1;
3433            mRcClient = null;
3434        }
3435
3436        public void unlinkToRcClientDeath() {
3437            if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
3438                try {
3439                    mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
3440                    mRcClientDeathHandler = null;
3441                } catch (java.util.NoSuchElementException e) {
3442                    // not much we can do here
3443                    Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
3444                    e.printStackTrace();
3445                }
3446            }
3447        }
3448
3449        @Override
3450        protected void finalize() throws Throwable {
3451            unlinkToRcClientDeath();// unlink exception handled inside method
3452            super.finalize();
3453        }
3454    }
3455
3456    /**
3457     *  The stack of remote control event receivers.
3458     *  Code sections and methods that modify the remote control event receiver stack are
3459     *  synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
3460     *  stack, audio focus or RC, can lead to a change in the remote control display
3461     */
3462    private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
3463
3464    /**
3465     * Helper function:
3466     * Display in the log the current entries in the remote control focus stack
3467     */
3468    private void dumpRCStack(PrintWriter pw) {
3469        pw.println("\nRemote Control stack entries:");
3470        synchronized(mRCStack) {
3471            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3472            while(stackIterator.hasNext()) {
3473                RemoteControlStackEntry rcse = stackIterator.next();
3474                pw.println("  pi: " + rcse.mMediaIntent +
3475                        "  -- ercvr: " + rcse.mReceiverComponent +
3476                        "  -- client: " + rcse.mRcClient +
3477                        "  -- uid: " + rcse.mCallingUid);
3478            }
3479        }
3480    }
3481
3482    /**
3483     * Helper function:
3484     * Remove any entry in the remote control stack that has the same package name as packageName
3485     * Pre-condition: packageName != null
3486     */
3487    private void removeMediaButtonReceiverForPackage(String packageName) {
3488        synchronized(mRCStack) {
3489            if (mRCStack.empty()) {
3490                return;
3491            } else {
3492                RemoteControlStackEntry oldTop = mRCStack.peek();
3493                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3494                // iterate over the stack entries
3495                while(stackIterator.hasNext()) {
3496                    RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
3497                    if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
3498                        // a stack entry is from the package being removed, remove it from the stack
3499                        stackIterator.remove();
3500                        rcse.unlinkToRcClientDeath();
3501                    }
3502                }
3503                if (mRCStack.empty()) {
3504                    // no saved media button receiver
3505                    mAudioHandler.sendMessage(
3506                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
3507                                    null));
3508                } else if (oldTop != mRCStack.peek()) {
3509                    // the top of the stack has changed, save it in the system settings
3510                    // by posting a message to persist it
3511                    mAudioHandler.sendMessage(
3512                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
3513                                    mRCStack.peek().mReceiverComponent));
3514                }
3515            }
3516        }
3517    }
3518
3519    /**
3520     * Helper function:
3521     * Restore remote control receiver from the system settings.
3522     */
3523    private void restoreMediaButtonReceiver() {
3524        String receiverName = Settings.System.getString(mContentResolver,
3525                Settings.System.MEDIA_BUTTON_RECEIVER);
3526        if ((null != receiverName) && !receiverName.isEmpty()) {
3527            ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
3528            // construct a PendingIntent targeted to the restored component name
3529            // for the media button and register it
3530            Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
3531            //     the associated intent will be handled by the component being registered
3532            mediaButtonIntent.setComponent(eventReceiver);
3533            PendingIntent pi = PendingIntent.getBroadcast(mContext,
3534                    0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
3535            registerMediaButtonIntent(pi, eventReceiver);
3536        }
3537    }
3538
3539    /**
3540     * Helper function:
3541     * Set the new remote control receiver at the top of the RC focus stack.
3542     * precondition: mediaIntent != null, target != null
3543     */
3544    private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
3545        // already at top of stack?
3546        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
3547            return;
3548        }
3549        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3550        RemoteControlStackEntry rcse = null;
3551        boolean wasInsideStack = false;
3552        while(stackIterator.hasNext()) {
3553            rcse = (RemoteControlStackEntry)stackIterator.next();
3554            if(rcse.mMediaIntent.equals(mediaIntent)) {
3555                wasInsideStack = true;
3556                stackIterator.remove();
3557                break;
3558            }
3559        }
3560        if (!wasInsideStack) {
3561            rcse = new RemoteControlStackEntry(mediaIntent, target);
3562        }
3563        mRCStack.push(rcse);
3564
3565        // post message to persist the default media button receiver
3566        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
3567                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
3568    }
3569
3570    /**
3571     * Helper function:
3572     * Remove the remote control receiver from the RC focus stack.
3573     * precondition: pi != null
3574     */
3575    private void removeMediaButtonReceiver(PendingIntent pi) {
3576        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3577        while(stackIterator.hasNext()) {
3578            RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
3579            if(rcse.mMediaIntent.equals(pi)) {
3580                stackIterator.remove();
3581                rcse.unlinkToRcClientDeath();
3582                break;
3583            }
3584        }
3585    }
3586
3587    /**
3588     * Helper function:
3589     * Called synchronized on mRCStack
3590     */
3591    private boolean isCurrentRcController(PendingIntent pi) {
3592        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
3593            return true;
3594        }
3595        return false;
3596    }
3597
3598    //==========================================================================================
3599    // Remote control display / client
3600    //==========================================================================================
3601    /**
3602     * Update the remote control displays with the new "focused" client generation
3603     */
3604    private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
3605            PendingIntent newMediaIntent, boolean clearing) {
3606        // NOTE: Only one IRemoteControlDisplay supported in this implementation
3607        if (mRcDisplay != null) {
3608            try {
3609                mRcDisplay.setCurrentClientId(
3610                        newClientGeneration, newMediaIntent, clearing);
3611            } catch (RemoteException e) {
3612                Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
3613                // if we had a display before, stop monitoring its death
3614                rcDisplay_stopDeathMonitor_syncRcStack();
3615                mRcDisplay = null;
3616            }
3617        }
3618    }
3619
3620    /**
3621     * Update the remote control clients with the new "focused" client generation
3622     */
3623    private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
3624        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3625        while(stackIterator.hasNext()) {
3626            RemoteControlStackEntry se = stackIterator.next();
3627            if ((se != null) && (se.mRcClient != null)) {
3628                try {
3629                    se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
3630                } catch (RemoteException e) {
3631                    Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e);
3632                    stackIterator.remove();
3633                    se.unlinkToRcClientDeath();
3634                }
3635            }
3636        }
3637    }
3638
3639    /**
3640     * Update the displays and clients with the new "focused" client generation and name
3641     * @param newClientGeneration the new generation value matching a client update
3642     * @param newClientEventReceiver the media button event receiver associated with the client.
3643     *    May be null, which implies there is no registered media button event receiver.
3644     * @param clearing true if the new client generation value maps to a remote control update
3645     *    where the display should be cleared.
3646     */
3647    private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
3648            PendingIntent newMediaIntent, boolean clearing) {
3649        // send the new valid client generation ID to all displays
3650        setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
3651        // send the new valid client generation ID to all clients
3652        setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
3653    }
3654
3655    /**
3656     * Called when processing MSG_RCDISPLAY_CLEAR event
3657     */
3658    private void onRcDisplayClear() {
3659        if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
3660
3661        synchronized(mRCStack) {
3662            synchronized(mCurrentRcLock) {
3663                mCurrentRcClientGen++;
3664                // synchronously update the displays and clients with the new client generation
3665                setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
3666                        null /*newMediaIntent*/, true /*clearing*/);
3667            }
3668        }
3669    }
3670
3671    /**
3672     * Called when processing MSG_RCDISPLAY_UPDATE event
3673     */
3674    private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
3675        synchronized(mRCStack) {
3676            synchronized(mCurrentRcLock) {
3677                if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
3678                    if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
3679
3680                    mCurrentRcClientGen++;
3681                    // synchronously update the displays and clients with
3682                    //      the new client generation
3683                    setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
3684                            rcse.mMediaIntent /*newMediaIntent*/,
3685                            false /*clearing*/);
3686
3687                    // tell the current client that it needs to send info
3688                    try {
3689                        mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
3690                                flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
3691                    } catch (RemoteException e) {
3692                        Log.e(TAG, "Current valid remote client is dead: "+e);
3693                        mCurrentRcClient = null;
3694                    }
3695                } else {
3696                    // the remote control display owner has changed between the
3697                    // the message to update the display was sent, and the time it
3698                    // gets to be processed (now)
3699                }
3700            }
3701        }
3702    }
3703
3704
3705    /**
3706     * Helper function:
3707     * Called synchronized on mRCStack
3708     */
3709    private void clearRemoteControlDisplay_syncAfRcs() {
3710        synchronized(mCurrentRcLock) {
3711            mCurrentRcClient = null;
3712        }
3713        // will cause onRcDisplayClear() to be called in AudioService's handler thread
3714        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
3715    }
3716
3717    /**
3718     * Helper function for code readability: only to be called from
3719     *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
3720     *    this method.
3721     * Preconditions:
3722     *    - called synchronized mAudioFocusLock then on mRCStack
3723     *    - mRCStack.isEmpty() is false
3724     */
3725    private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
3726        RemoteControlStackEntry rcse = mRCStack.peek();
3727        int infoFlagsAboutToBeUsed = infoChangedFlags;
3728        // this is where we enforce opt-in for information display on the remote controls
3729        //   with the new AudioManager.registerRemoteControlClient() API
3730        if (rcse.mRcClient == null) {
3731            //Log.w(TAG, "Can't update remote control display with null remote control client");
3732            clearRemoteControlDisplay_syncAfRcs();
3733            return;
3734        }
3735        synchronized(mCurrentRcLock) {
3736            if (!rcse.mRcClient.equals(mCurrentRcClient)) {
3737                // new RC client, assume every type of information shall be queried
3738                infoFlagsAboutToBeUsed = RC_INFO_ALL;
3739            }
3740            mCurrentRcClient = rcse.mRcClient;
3741        }
3742        // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
3743        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
3744                infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
3745    }
3746
3747    /**
3748     * Helper function:
3749     * Called synchronized on mAudioFocusLock, then mRCStack
3750     * Check whether the remote control display should be updated, triggers the update if required
3751     * @param infoChangedFlags the flags corresponding to the remote control client information
3752     *     that has changed, if applicable (checking for the update conditions might trigger a
3753     *     clear, rather than an update event).
3754     */
3755    private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
3756        // determine whether the remote control display should be refreshed
3757        // if either stack is empty, there is a mismatch, so clear the RC display
3758        if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
3759            clearRemoteControlDisplay_syncAfRcs();
3760            return;
3761        }
3762        // if the top of the two stacks belong to different packages, there is a mismatch, clear
3763        if ((mRCStack.peek().mCallingPackageName != null)
3764                && (mFocusStack.peek().mPackageName != null)
3765                && !(mRCStack.peek().mCallingPackageName.compareTo(
3766                        mFocusStack.peek().mPackageName) == 0)) {
3767            clearRemoteControlDisplay_syncAfRcs();
3768            return;
3769        }
3770        // if the audio focus didn't originate from the same Uid as the one in which the remote
3771        //   control information will be retrieved, clear
3772        if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
3773            clearRemoteControlDisplay_syncAfRcs();
3774            return;
3775        }
3776        // refresh conditions were verified: update the remote controls
3777        // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
3778        updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
3779    }
3780
3781    /**
3782     * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
3783     * precondition: mediaIntent != null, target != null
3784     */
3785    public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
3786        Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
3787
3788        synchronized(mAudioFocusLock) {
3789            synchronized(mRCStack) {
3790                pushMediaButtonReceiver(mediaIntent, eventReceiver);
3791                // new RC client, assume every type of information shall be queried
3792                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3793            }
3794        }
3795    }
3796
3797    /**
3798     * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
3799     * precondition: mediaIntent != null, eventReceiver != null
3800     */
3801    public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
3802    {
3803        Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
3804
3805        synchronized(mAudioFocusLock) {
3806            synchronized(mRCStack) {
3807                boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
3808                removeMediaButtonReceiver(mediaIntent);
3809                if (topOfStackWillChange) {
3810                    // current RC client will change, assume every type of info needs to be queried
3811                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3812                }
3813            }
3814        }
3815    }
3816
3817    /**
3818     * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
3819     * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
3820     *     without modifying the RC stack, but while still causing the display to refresh (will
3821     *     become blank as a result of this)
3822     */
3823    public void registerRemoteControlClient(PendingIntent mediaIntent,
3824            IRemoteControlClient rcClient, String callingPackageName) {
3825        if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
3826        synchronized(mAudioFocusLock) {
3827            synchronized(mRCStack) {
3828                // store the new display information
3829                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3830                while(stackIterator.hasNext()) {
3831                    RemoteControlStackEntry rcse = stackIterator.next();
3832                    if(rcse.mMediaIntent.equals(mediaIntent)) {
3833                        // already had a remote control client?
3834                        if (rcse.mRcClientDeathHandler != null) {
3835                            // stop monitoring the old client's death
3836                            rcse.unlinkToRcClientDeath();
3837                        }
3838                        // save the new remote control client
3839                        rcse.mRcClient = rcClient;
3840                        rcse.mCallingPackageName = callingPackageName;
3841                        rcse.mCallingUid = Binder.getCallingUid();
3842                        if (rcClient == null) {
3843                            // here rcse.mRcClientDeathHandler is null;
3844                            break;
3845                        }
3846
3847                        // there is a new (non-null) client:
3848                        // 1/ give the new client the current display (if any)
3849                        if (mRcDisplay != null) {
3850                            try {
3851                                rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
3852                            } catch (RemoteException e) {
3853                                Log.e(TAG, "Error connecting remote control display to client: "+e);
3854                                e.printStackTrace();
3855                            }
3856                        }
3857                        // 2/ monitor the new client's death
3858                        IBinder b = rcse.mRcClient.asBinder();
3859                        RcClientDeathHandler rcdh =
3860                                new RcClientDeathHandler(b, rcse.mMediaIntent);
3861                        try {
3862                            b.linkToDeath(rcdh, 0);
3863                        } catch (RemoteException e) {
3864                            // remote control client is DOA, disqualify it
3865                            Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
3866                            rcse.mRcClient = null;
3867                        }
3868                        rcse.mRcClientDeathHandler = rcdh;
3869                        break;
3870                    }
3871                }
3872                // if the eventReceiver is at the top of the stack
3873                // then check for potential refresh of the remote controls
3874                if (isCurrentRcController(mediaIntent)) {
3875                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3876                }
3877            }
3878        }
3879    }
3880
3881    /**
3882     * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
3883     * rcClient is guaranteed non-null
3884     */
3885    public void unregisterRemoteControlClient(PendingIntent mediaIntent,
3886            IRemoteControlClient rcClient) {
3887        synchronized(mAudioFocusLock) {
3888            synchronized(mRCStack) {
3889                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3890                while(stackIterator.hasNext()) {
3891                    RemoteControlStackEntry rcse = stackIterator.next();
3892                    if ((rcse.mMediaIntent.equals(mediaIntent))
3893                            && rcClient.equals(rcse.mRcClient)) {
3894                        // we found the IRemoteControlClient to unregister
3895                        // stop monitoring its death
3896                        rcse.unlinkToRcClientDeath();
3897                        // reset the client-related fields
3898                        rcse.mRcClient = null;
3899                        rcse.mCallingPackageName = null;
3900                    }
3901                }
3902            }
3903        }
3904    }
3905
3906    /**
3907     * The remote control displays.
3908     * Access synchronized on mRCStack
3909     * NOTE: Only one IRemoteControlDisplay supported in this implementation
3910     */
3911    private IRemoteControlDisplay mRcDisplay;
3912    private RcDisplayDeathHandler mRcDisplayDeathHandler;
3913    private int mArtworkExpectedWidth = -1;
3914    private int mArtworkExpectedHeight = -1;
3915    /**
3916     * Inner class to monitor remote control display deaths, and unregister them from the list
3917     * of displays if necessary.
3918     */
3919    private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
3920        private IBinder mCb; // To be notified of client's death
3921
3922        public RcDisplayDeathHandler(IBinder b) {
3923            if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b);
3924            mCb = b;
3925        }
3926
3927        public void binderDied() {
3928            synchronized(mRCStack) {
3929                Log.w(TAG, "RemoteControl: display died");
3930                mRcDisplay = null;
3931            }
3932        }
3933
3934        public void unlinkToRcDisplayDeath() {
3935            if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb);
3936            try {
3937                mCb.unlinkToDeath(this, 0);
3938            } catch (java.util.NoSuchElementException e) {
3939                // not much we can do here, the display was being unregistered anyway
3940                Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()");
3941                e.printStackTrace();
3942            }
3943        }
3944
3945    }
3946
3947    private void rcDisplay_stopDeathMonitor_syncRcStack() {
3948        if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null)
3949            // we had a display before, stop monitoring its death
3950            mRcDisplayDeathHandler.unlinkToRcDisplayDeath();
3951        }
3952    }
3953
3954    private void rcDisplay_startDeathMonitor_syncRcStack() {
3955        if (mRcDisplay != null) {
3956            // new non-null display, monitor its death
3957            IBinder b = mRcDisplay.asBinder();
3958            mRcDisplayDeathHandler = new RcDisplayDeathHandler(b);
3959            try {
3960                b.linkToDeath(mRcDisplayDeathHandler, 0);
3961            } catch (RemoteException e) {
3962                // remote control display is DOA, disqualify it
3963                Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
3964                mRcDisplay = null;
3965            }
3966        }
3967    }
3968
3969    /**
3970     * Register an IRemoteControlDisplay.
3971     * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
3972     * at the top of the stack to update the new display with its information.
3973     * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
3974     * @param rcd the IRemoteControlDisplay to register. No effect if null.
3975     */
3976    public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
3977        if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
3978        synchronized(mAudioFocusLock) {
3979            synchronized(mRCStack) {
3980                if ((mRcDisplay == rcd) || (rcd == null)) {
3981                    return;
3982                }
3983                // if we had a display before, stop monitoring its death
3984                rcDisplay_stopDeathMonitor_syncRcStack();
3985                mRcDisplay = rcd;
3986                // new display, start monitoring its death
3987                rcDisplay_startDeathMonitor_syncRcStack();
3988
3989                // let all the remote control clients there is a new display
3990                // no need to unplug the previous because we only support one display
3991                // and the clients don't track the death of the display
3992                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
3993                while(stackIterator.hasNext()) {
3994                    RemoteControlStackEntry rcse = stackIterator.next();
3995                    if(rcse.mRcClient != null) {
3996                        try {
3997                            rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
3998                        } catch (RemoteException e) {
3999                            Log.e(TAG, "Error connecting remote control display to client: " + e);
4000                            e.printStackTrace();
4001                        }
4002                    }
4003                }
4004
4005                // we have a new display, of which all the clients are now aware: have it be updated
4006                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
4007            }
4008        }
4009    }
4010
4011    /**
4012     * Unregister an IRemoteControlDisplay.
4013     * Since only one IRemoteControlDisplay is supported, this has no effect if the one to
4014     *    unregister is not the current one.
4015     * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
4016     */
4017    public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
4018        if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
4019        synchronized(mRCStack) {
4020            // only one display here, so you can only unregister the current display
4021            if ((rcd == null) || (rcd != mRcDisplay)) {
4022                if (DEBUG_RC) Log.w(TAG, "    trying to unregister unregistered RCD");
4023                return;
4024            }
4025            // if we had a display before, stop monitoring its death
4026            rcDisplay_stopDeathMonitor_syncRcStack();
4027            mRcDisplay = null;
4028
4029            // disconnect this remote control display from all the clients
4030            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4031            while(stackIterator.hasNext()) {
4032                RemoteControlStackEntry rcse = stackIterator.next();
4033                if(rcse.mRcClient != null) {
4034                    try {
4035                        rcse.mRcClient.unplugRemoteControlDisplay(rcd);
4036                    } catch (RemoteException e) {
4037                        Log.e(TAG, "Error disconnecting remote control display to client: " + e);
4038                        e.printStackTrace();
4039                    }
4040                }
4041            }
4042        }
4043    }
4044
4045    public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
4046        synchronized(mRCStack) {
4047            // NOTE: Only one IRemoteControlDisplay supported in this implementation
4048            mArtworkExpectedWidth = w;
4049            mArtworkExpectedHeight = h;
4050        }
4051    }
4052
4053    @Override
4054    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
4055        // TODO probably a lot more to do here than just the audio focus and remote control stacks
4056        dumpFocusStack(pw);
4057        dumpRCStack(pw);
4058    }
4059
4060
4061}
4062