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