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