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