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