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