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