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