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