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