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