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