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