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