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