AudioService.java revision 3114ce3861f20f9a5c2c59dd2629197a1f4874a8
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        if (!checkAudioSettingsPermission("setBluetoothA2dpOn()")) {
1703            return;
1704        }
1705        setBluetoothA2dpOnInt(on);
1706    }
1707
1708    /** @see AudioManager#isBluetoothA2dpOn() */
1709    public boolean isBluetoothA2dpOn() {
1710        synchronized (mBluetoothA2dpEnabledLock) {
1711            return mBluetoothA2dpEnabled;
1712        }
1713    }
1714
1715    /** @see AudioManager#startBluetoothSco() */
1716    public void startBluetoothSco(IBinder cb){
1717        if (!checkAudioSettingsPermission("startBluetoothSco()") ||
1718                !mBootCompleted) {
1719            return;
1720        }
1721        ScoClient client = getScoClient(cb, true);
1722        client.incCount();
1723    }
1724
1725    /** @see AudioManager#stopBluetoothSco() */
1726    public void stopBluetoothSco(IBinder cb){
1727        if (!checkAudioSettingsPermission("stopBluetoothSco()") ||
1728                !mBootCompleted) {
1729            return;
1730        }
1731        ScoClient client = getScoClient(cb, false);
1732        if (client != null) {
1733            client.decCount();
1734        }
1735    }
1736
1737
1738    private class ScoClient implements IBinder.DeathRecipient {
1739        private IBinder mCb; // To be notified of client's death
1740        private int mCreatorPid;
1741        private int mStartcount; // number of SCO connections started by this client
1742
1743        ScoClient(IBinder cb) {
1744            mCb = cb;
1745            mCreatorPid = Binder.getCallingPid();
1746            mStartcount = 0;
1747        }
1748
1749        public void binderDied() {
1750            synchronized(mScoClients) {
1751                Log.w(TAG, "SCO client died");
1752                int index = mScoClients.indexOf(this);
1753                if (index < 0) {
1754                    Log.w(TAG, "unregistered SCO client died");
1755                } else {
1756                    clearCount(true);
1757                    mScoClients.remove(this);
1758                }
1759            }
1760        }
1761
1762        public void incCount() {
1763            synchronized(mScoClients) {
1764                requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED);
1765                if (mStartcount == 0) {
1766                    try {
1767                        mCb.linkToDeath(this, 0);
1768                    } catch (RemoteException e) {
1769                        // client has already died!
1770                        Log.w(TAG, "ScoClient  incCount() could not link to "+mCb+" binder death");
1771                    }
1772                }
1773                mStartcount++;
1774            }
1775        }
1776
1777        public void decCount() {
1778            synchronized(mScoClients) {
1779                if (mStartcount == 0) {
1780                    Log.w(TAG, "ScoClient.decCount() already 0");
1781                } else {
1782                    mStartcount--;
1783                    if (mStartcount == 0) {
1784                        try {
1785                            mCb.unlinkToDeath(this, 0);
1786                        } catch (NoSuchElementException e) {
1787                            Log.w(TAG, "decCount() going to 0 but not registered to binder");
1788                        }
1789                    }
1790                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1791                }
1792            }
1793        }
1794
1795        public void clearCount(boolean stopSco) {
1796            synchronized(mScoClients) {
1797                if (mStartcount != 0) {
1798                    try {
1799                        mCb.unlinkToDeath(this, 0);
1800                    } catch (NoSuchElementException e) {
1801                        Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder");
1802                    }
1803                }
1804                mStartcount = 0;
1805                if (stopSco) {
1806                    requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED);
1807                }
1808            }
1809        }
1810
1811        public int getCount() {
1812            return mStartcount;
1813        }
1814
1815        public IBinder getBinder() {
1816            return mCb;
1817        }
1818
1819        public int getPid() {
1820            return mCreatorPid;
1821        }
1822
1823        public int totalCount() {
1824            synchronized(mScoClients) {
1825                int count = 0;
1826                int size = mScoClients.size();
1827                for (int i = 0; i < size; i++) {
1828                    count += mScoClients.get(i).getCount();
1829                }
1830                return count;
1831            }
1832        }
1833
1834        private void requestScoState(int state) {
1835            checkScoAudioState();
1836            if (totalCount() == 0) {
1837                if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) {
1838                    // Make sure that the state transitions to CONNECTING even if we cannot initiate
1839                    // the connection.
1840                    broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING);
1841                    // Accept SCO audio activation only in NORMAL audio mode or if the mode is
1842                    // currently controlled by the same client process.
1843                    synchronized(mSetModeDeathHandlers) {
1844                        if ((mSetModeDeathHandlers.isEmpty() ||
1845                                mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) &&
1846                                (mScoAudioState == SCO_STATE_INACTIVE ||
1847                                 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
1848                            if (mScoAudioState == SCO_STATE_INACTIVE) {
1849                                if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1850                                    if (mBluetoothHeadset.startScoUsingVirtualVoiceCall(
1851                                            mBluetoothHeadsetDevice)) {
1852                                        mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1853                                    } else {
1854                                        broadcastScoConnectionState(
1855                                                AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1856                                    }
1857                                } else if (getBluetoothHeadset()) {
1858                                    mScoAudioState = SCO_STATE_ACTIVATE_REQ;
1859                                }
1860                            } else {
1861                                mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
1862                                broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED);
1863                            }
1864                        } else {
1865                            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1866                        }
1867                    }
1868                } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED &&
1869                              (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
1870                               mScoAudioState == SCO_STATE_ACTIVATE_REQ)) {
1871                    if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) {
1872                        if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) {
1873                            if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
1874                                    mBluetoothHeadsetDevice)) {
1875                                mScoAudioState = SCO_STATE_INACTIVE;
1876                                broadcastScoConnectionState(
1877                                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1878                            }
1879                        } else if (getBluetoothHeadset()) {
1880                            mScoAudioState = SCO_STATE_DEACTIVATE_REQ;
1881                        }
1882                    } else {
1883                        mScoAudioState = SCO_STATE_INACTIVE;
1884                        broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1885                    }
1886                }
1887            }
1888        }
1889    }
1890
1891    private void checkScoAudioState() {
1892        if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null &&
1893                mScoAudioState == SCO_STATE_INACTIVE &&
1894                mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice)
1895                != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) {
1896            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
1897        }
1898    }
1899
1900    private ScoClient getScoClient(IBinder cb, boolean create) {
1901        synchronized(mScoClients) {
1902            ScoClient client = null;
1903            int size = mScoClients.size();
1904            for (int i = 0; i < size; i++) {
1905                client = mScoClients.get(i);
1906                if (client.getBinder() == cb)
1907                    return client;
1908            }
1909            if (create) {
1910                client = new ScoClient(cb);
1911                mScoClients.add(client);
1912            }
1913            return client;
1914        }
1915    }
1916
1917    public void clearAllScoClients(int exceptPid, boolean stopSco) {
1918        synchronized(mScoClients) {
1919            ScoClient savedClient = null;
1920            int size = mScoClients.size();
1921            for (int i = 0; i < size; i++) {
1922                ScoClient cl = mScoClients.get(i);
1923                if (cl.getPid() != exceptPid) {
1924                    cl.clearCount(stopSco);
1925                } else {
1926                    savedClient = cl;
1927                }
1928            }
1929            mScoClients.clear();
1930            if (savedClient != null) {
1931                mScoClients.add(savedClient);
1932            }
1933        }
1934    }
1935
1936    private boolean getBluetoothHeadset() {
1937        boolean result = false;
1938        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
1939        if (adapter != null) {
1940            result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
1941                                    BluetoothProfile.HEADSET);
1942        }
1943        // If we could not get a bluetooth headset proxy, send a failure message
1944        // without delay to reset the SCO audio state and clear SCO clients.
1945        // If we could get a proxy, send a delayed failure message that will reset our state
1946        // in case we don't receive onServiceConnected().
1947        sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
1948                SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0);
1949        return result;
1950    }
1951
1952    private void disconnectBluetoothSco(int exceptPid) {
1953        synchronized(mScoClients) {
1954            checkScoAudioState();
1955            if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL ||
1956                    mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
1957                if (mBluetoothHeadsetDevice != null) {
1958                    if (mBluetoothHeadset != null) {
1959                        if (!mBluetoothHeadset.stopVoiceRecognition(
1960                                mBluetoothHeadsetDevice)) {
1961                            sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
1962                                    SENDMSG_REPLACE, 0, 0, null, 0);
1963                        }
1964                    } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL &&
1965                            getBluetoothHeadset()) {
1966                        mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ;
1967                    }
1968                }
1969            } else {
1970                clearAllScoClients(exceptPid, true);
1971            }
1972        }
1973    }
1974
1975    private void resetBluetoothSco() {
1976        synchronized(mScoClients) {
1977            clearAllScoClients(0, false);
1978            mScoAudioState = SCO_STATE_INACTIVE;
1979            broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
1980        }
1981    }
1982
1983    private void broadcastScoConnectionState(int state) {
1984        if (state != mScoConnectionState) {
1985            Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED);
1986            newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
1987            newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
1988                    mScoConnectionState);
1989            mContext.sendStickyBroadcast(newIntent);
1990            mScoConnectionState = state;
1991        }
1992    }
1993
1994    private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener =
1995        new BluetoothProfile.ServiceListener() {
1996        public void onServiceConnected(int profile, BluetoothProfile proxy) {
1997            BluetoothDevice btDevice;
1998            List<BluetoothDevice> deviceList;
1999            switch(profile) {
2000            case BluetoothProfile.A2DP:
2001                BluetoothA2dp a2dp = (BluetoothA2dp) proxy;
2002                deviceList = a2dp.getConnectedDevices();
2003                if (deviceList.size() > 0) {
2004                    btDevice = deviceList.get(0);
2005                    synchronized (mConnectedDevices) {
2006                        int state = a2dp.getConnectionState(btDevice);
2007                        int delay = checkSendBecomingNoisyIntent(
2008                                                AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2009                                                (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
2010                        queueMsgUnderWakeLock(mAudioHandler,
2011                                MSG_SET_A2DP_CONNECTION_STATE,
2012                                state,
2013                                0,
2014                                btDevice,
2015                                delay);
2016                    }
2017                }
2018                break;
2019
2020            case BluetoothProfile.HEADSET:
2021                synchronized (mScoClients) {
2022                    // Discard timeout message
2023                    mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED);
2024                    mBluetoothHeadset = (BluetoothHeadset) proxy;
2025                    deviceList = mBluetoothHeadset.getConnectedDevices();
2026                    if (deviceList.size() > 0) {
2027                        mBluetoothHeadsetDevice = deviceList.get(0);
2028                    } else {
2029                        mBluetoothHeadsetDevice = null;
2030                    }
2031                    // Refresh SCO audio state
2032                    checkScoAudioState();
2033                    // Continue pending action if any
2034                    if (mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
2035                            mScoAudioState == SCO_STATE_DEACTIVATE_REQ ||
2036                            mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) {
2037                        boolean status = false;
2038                        if (mBluetoothHeadsetDevice != null) {
2039                            switch (mScoAudioState) {
2040                            case SCO_STATE_ACTIVATE_REQ:
2041                                mScoAudioState = SCO_STATE_ACTIVE_INTERNAL;
2042                                status = mBluetoothHeadset.startScoUsingVirtualVoiceCall(
2043                                        mBluetoothHeadsetDevice);
2044                                break;
2045                            case SCO_STATE_DEACTIVATE_REQ:
2046                                status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall(
2047                                        mBluetoothHeadsetDevice);
2048                                break;
2049                            case SCO_STATE_DEACTIVATE_EXT_REQ:
2050                                status = mBluetoothHeadset.stopVoiceRecognition(
2051                                        mBluetoothHeadsetDevice);
2052                            }
2053                        }
2054                        if (!status) {
2055                            sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED,
2056                                    SENDMSG_REPLACE, 0, 0, null, 0);
2057                        }
2058                    }
2059                }
2060                break;
2061
2062            default:
2063                break;
2064            }
2065        }
2066        public void onServiceDisconnected(int profile) {
2067            switch(profile) {
2068            case BluetoothProfile.A2DP:
2069                synchronized (mConnectedDevices) {
2070                    if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) {
2071                        makeA2dpDeviceUnavailableNow(
2072                                mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP));
2073                    }
2074                }
2075                break;
2076
2077            case BluetoothProfile.HEADSET:
2078                synchronized (mScoClients) {
2079                    mBluetoothHeadset = null;
2080                }
2081                break;
2082
2083            default:
2084                break;
2085            }
2086        }
2087    };
2088
2089    ///////////////////////////////////////////////////////////////////////////
2090    // Internal methods
2091    ///////////////////////////////////////////////////////////////////////////
2092
2093    /**
2094     * Checks if the adjustment should change ringer mode instead of just
2095     * adjusting volume. If so, this will set the proper ringer mode and volume
2096     * indices on the stream states.
2097     */
2098    private boolean checkForRingerModeChange(int oldIndex, int direction,  int step) {
2099        boolean adjustVolumeIndex = true;
2100        int ringerMode = getRingerMode();
2101
2102        switch (ringerMode) {
2103        case RINGER_MODE_NORMAL:
2104            if (direction == AudioManager.ADJUST_LOWER) {
2105                if (mHasVibrator) {
2106                    // "step" is the delta in internal index units corresponding to a
2107                    // change of 1 in UI index units.
2108                    // Because of rounding when rescaling from one stream index range to its alias
2109                    // index range, we cannot simply test oldIndex == step:
2110                    //   (step <= oldIndex < 2 * step) is equivalent to: (old UI index == 1)
2111                    if (step <= oldIndex && oldIndex < 2 * step) {
2112                        ringerMode = RINGER_MODE_VIBRATE;
2113                    }
2114                } else {
2115                    // (oldIndex < step) is equivalent to (old UI index == 0)
2116                    if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) {
2117                        ringerMode = RINGER_MODE_SILENT;
2118                    }
2119                }
2120            }
2121            break;
2122        case RINGER_MODE_VIBRATE:
2123            if (!mHasVibrator) {
2124                Log.e(TAG, "checkForRingerModeChange() current ringer mode is vibrate" +
2125                        "but no vibrator is present");
2126                break;
2127            }
2128            if ((direction == AudioManager.ADJUST_LOWER)) {
2129                if (mPrevVolDirection != AudioManager.ADJUST_LOWER) {
2130                    ringerMode = RINGER_MODE_SILENT;
2131                }
2132            } else if (direction == AudioManager.ADJUST_RAISE) {
2133                ringerMode = RINGER_MODE_NORMAL;
2134            }
2135            adjustVolumeIndex = false;
2136            break;
2137        case RINGER_MODE_SILENT:
2138            if (direction == AudioManager.ADJUST_RAISE) {
2139                if (mHasVibrator) {
2140                    ringerMode = RINGER_MODE_VIBRATE;
2141                } else {
2142                    ringerMode = RINGER_MODE_NORMAL;
2143                }
2144            }
2145            adjustVolumeIndex = false;
2146            break;
2147        default:
2148            Log.e(TAG, "checkForRingerModeChange() wrong ringer mode: "+ringerMode);
2149            break;
2150        }
2151
2152        setRingerMode(ringerMode);
2153
2154        mPrevVolDirection = direction;
2155
2156        return adjustVolumeIndex;
2157    }
2158
2159    public boolean isStreamAffectedByRingerMode(int streamType) {
2160        return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
2161    }
2162
2163    private boolean isStreamMutedByRingerMode(int streamType) {
2164        return (mRingerModeMutedStreams & (1 << streamType)) != 0;
2165    }
2166
2167    public boolean isStreamAffectedByMute(int streamType) {
2168        return (mMuteAffectedStreams & (1 << streamType)) != 0;
2169    }
2170
2171    private void ensureValidDirection(int direction) {
2172        if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
2173            throw new IllegalArgumentException("Bad direction " + direction);
2174        }
2175    }
2176
2177    private void ensureValidSteps(int steps) {
2178        if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) {
2179            throw new IllegalArgumentException("Bad volume adjust steps " + steps);
2180        }
2181    }
2182
2183    private void ensureValidStreamType(int streamType) {
2184        if (streamType < 0 || streamType >= mStreamStates.length) {
2185            throw new IllegalArgumentException("Bad stream type " + streamType);
2186        }
2187    }
2188
2189    private boolean isInCommunication() {
2190        boolean isOffhook = false;
2191
2192        if (mVoiceCapable) {
2193            try {
2194                ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
2195                if (phone != null) isOffhook = phone.isOffhook();
2196            } catch (RemoteException e) {
2197                Log.w(TAG, "Couldn't connect to phone service", e);
2198            }
2199        }
2200        return (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION);
2201    }
2202
2203    private int getActiveStreamType(int suggestedStreamType) {
2204        if (mVoiceCapable) {
2205            if (isInCommunication()) {
2206                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
2207                        == AudioSystem.FORCE_BT_SCO) {
2208                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
2209                    return AudioSystem.STREAM_BLUETOOTH_SCO;
2210                } else {
2211                    // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
2212                    return AudioSystem.STREAM_VOICE_CALL;
2213                }
2214            } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
2215                // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
2216                // volume can have priority over STREAM_MUSIC
2217                if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
2218                    if (DEBUG_VOL)
2219                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
2220                    return STREAM_REMOTE_MUSIC;
2221                } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
2222                    if (DEBUG_VOL)
2223                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
2224                    return AudioSystem.STREAM_MUSIC;
2225                } else {
2226                    if (DEBUG_VOL)
2227                        Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING b/c default");
2228                    return AudioSystem.STREAM_RING;
2229                }
2230            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
2231                if (DEBUG_VOL)
2232                    Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
2233                return AudioSystem.STREAM_MUSIC;
2234            } else {
2235                if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
2236                        + suggestedStreamType);
2237                return suggestedStreamType;
2238            }
2239        } else {
2240            if (isInCommunication()) {
2241                if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION)
2242                        == AudioSystem.FORCE_BT_SCO) {
2243                    if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO");
2244                    return AudioSystem.STREAM_BLUETOOTH_SCO;
2245                } else {
2246                    if (DEBUG_VOL)  Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL");
2247                    return AudioSystem.STREAM_VOICE_CALL;
2248                }
2249            } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
2250                    NOTIFICATION_VOLUME_DELAY_MS) ||
2251                    AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
2252                            NOTIFICATION_VOLUME_DELAY_MS)) {
2253                if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
2254                return AudioSystem.STREAM_NOTIFICATION;
2255            } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
2256                if (checkUpdateRemoteStateIfActive(AudioSystem.STREAM_MUSIC)) {
2257                    // Having the suggested stream be USE_DEFAULT_STREAM_TYPE is how remote control
2258                    // volume can have priority over STREAM_MUSIC
2259                    if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
2260                    return STREAM_REMOTE_MUSIC;
2261                } else {
2262                    if (DEBUG_VOL)
2263                        Log.v(TAG, "getActiveStreamType: using STREAM_MUSIC as default");
2264                    return AudioSystem.STREAM_MUSIC;
2265                }
2266            } else {
2267                if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Returning suggested type "
2268                        + suggestedStreamType);
2269                return suggestedStreamType;
2270            }
2271        }
2272    }
2273
2274    private void broadcastRingerMode(int ringerMode) {
2275        // Send sticky broadcast
2276        Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
2277        broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
2278        broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
2279                | Intent.FLAG_RECEIVER_REPLACE_PENDING);
2280        long origCallerIdentityToken = Binder.clearCallingIdentity();
2281        mContext.sendStickyBroadcast(broadcast);
2282        Binder.restoreCallingIdentity(origCallerIdentityToken);
2283    }
2284
2285    private void broadcastVibrateSetting(int vibrateType) {
2286        // Send broadcast
2287        if (ActivityManagerNative.isSystemReady()) {
2288            Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
2289            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
2290            broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
2291            mContext.sendBroadcast(broadcast);
2292        }
2293    }
2294
2295    // Message helper methods
2296    /**
2297     * Queue a message on the given handler's message queue, after acquiring the service wake lock.
2298     * Note that the wake lock needs to be released after the message has been handled.
2299     */
2300    private void queueMsgUnderWakeLock(Handler handler, int msg,
2301            int arg1, int arg2, Object obj, int delay) {
2302        mMediaEventWakeLock.acquire();
2303        sendMsg(handler, msg, SENDMSG_QUEUE, arg1, arg2, obj, delay);
2304    }
2305
2306    private static void sendMsg(Handler handler, int msg,
2307            int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
2308
2309        if (existingMsgPolicy == SENDMSG_REPLACE) {
2310            handler.removeMessages(msg);
2311        } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
2312            return;
2313        }
2314
2315        handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
2316    }
2317
2318    boolean checkAudioSettingsPermission(String method) {
2319        if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
2320                == PackageManager.PERMISSION_GRANTED) {
2321            return true;
2322        }
2323        String msg = "Audio Settings Permission Denial: " + method + " from pid="
2324                + Binder.getCallingPid()
2325                + ", uid=" + Binder.getCallingUid();
2326        Log.w(TAG, msg);
2327        return false;
2328    }
2329
2330    private int getDeviceForStream(int stream) {
2331        int device = AudioSystem.getDevicesForStream(stream);
2332        if ((device & (device - 1)) != 0) {
2333            // Multiple device selection is either:
2334            //  - speaker + one other device: give priority to speaker in this case.
2335            //  - one A2DP device + another device: happens with duplicated output. In this case
2336            // retain the device on the A2DP output as the other must not correspond to an active
2337            // selection if not the speaker.
2338            if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) {
2339                device = AudioSystem.DEVICE_OUT_SPEAKER;
2340            } else {
2341                device &= AudioSystem.DEVICE_OUT_ALL_A2DP;
2342            }
2343        }
2344        return device;
2345    }
2346
2347    public void setWiredDeviceConnectionState(int device, int state, String name) {
2348        synchronized (mConnectedDevices) {
2349            int delay = checkSendBecomingNoisyIntent(device, state);
2350            queueMsgUnderWakeLock(mAudioHandler,
2351                    MSG_SET_WIRED_DEVICE_CONNECTION_STATE,
2352                    device,
2353                    state,
2354                    name,
2355                    delay);
2356        }
2357    }
2358
2359    public int setBluetoothA2dpDeviceConnectionState(BluetoothDevice device, int state)
2360    {
2361        int delay;
2362        synchronized (mConnectedDevices) {
2363            delay = checkSendBecomingNoisyIntent(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
2364                                            (state == BluetoothA2dp.STATE_CONNECTED) ? 1 : 0);
2365            queueMsgUnderWakeLock(mAudioHandler,
2366                    MSG_SET_A2DP_CONNECTION_STATE,
2367                    state,
2368                    0,
2369                    device,
2370                    delay);
2371        }
2372        return delay;
2373    }
2374
2375    ///////////////////////////////////////////////////////////////////////////
2376    // Inner classes
2377    ///////////////////////////////////////////////////////////////////////////
2378
2379    public class VolumeStreamState {
2380        private final int mStreamType;
2381
2382        private String mVolumeIndexSettingName;
2383        private String mLastAudibleVolumeIndexSettingName;
2384        private int mIndexMax;
2385        private final ConcurrentHashMap<Integer, Integer> mIndex =
2386                                            new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
2387        private final ConcurrentHashMap<Integer, Integer> mLastAudibleIndex =
2388                                            new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4);
2389        private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death
2390
2391        private VolumeStreamState(String settingName, int streamType) {
2392
2393            mVolumeIndexSettingName = settingName;
2394            mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
2395
2396            mStreamType = streamType;
2397            mIndexMax = MAX_STREAM_VOLUME[streamType];
2398            AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
2399            mIndexMax *= 10;
2400
2401            readSettings();
2402
2403            mDeathHandlers = new ArrayList<VolumeDeathHandler>();
2404        }
2405
2406        public String getSettingNameForDevice(boolean lastAudible, int device) {
2407            String name = lastAudible ?
2408                            mLastAudibleVolumeIndexSettingName :
2409                            mVolumeIndexSettingName;
2410            String suffix = AudioSystem.getDeviceName(device);
2411            if (suffix.isEmpty()) {
2412                return name;
2413            }
2414            return name + "_" + suffix;
2415        }
2416
2417        public synchronized void readSettings() {
2418            int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
2419
2420            for (int i = 0; remainingDevices != 0; i++) {
2421                int device = (1 << i);
2422                if ((device & remainingDevices) == 0) {
2423                    continue;
2424                }
2425                remainingDevices &= ~device;
2426
2427                // retrieve current volume for device
2428                String name = getSettingNameForDevice(false /* lastAudible */, device);
2429                // if no volume stored for current stream and device, use default volume if default
2430                // device, continue otherwise
2431                int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
2432                                        AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
2433                int index = Settings.System.getInt(mContentResolver, name, defaultIndex);
2434                if (index == -1) {
2435                    continue;
2436                }
2437
2438                // retrieve last audible volume for device
2439                name = getSettingNameForDevice(true  /* lastAudible */, device);
2440                // use stored last audible index if present, otherwise use current index if not 0
2441                // or default index
2442                defaultIndex = (index > 0) ?
2443                                    index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
2444                int lastAudibleIndex = Settings.System.getInt(mContentResolver, name, defaultIndex);
2445
2446                // a last audible index of 0 should never be stored for ring and notification
2447                // streams on phones (voice capable devices).
2448                // same for system stream on phones and tablets
2449                if ((lastAudibleIndex == 0) &&
2450                        ((mVoiceCapable &&
2451                                (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
2452                         (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
2453                    lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
2454                    // Correct the data base
2455                    sendMsg(mAudioHandler,
2456                            MSG_PERSIST_VOLUME,
2457                            SENDMSG_QUEUE,
2458                            PERSIST_LAST_AUDIBLE,
2459                            device,
2460                            this,
2461                            PERSIST_DELAY);
2462                }
2463                mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex));
2464                // the initial index should never be 0 for ring and notification streams on phones
2465                // (voice capable devices) if not in silent or vibrate mode.
2466                // same for system stream on phones and tablets
2467                if ((index == 0) && (mRingerMode == AudioManager.RINGER_MODE_NORMAL) &&
2468                        ((mVoiceCapable &&
2469                                (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
2470                         (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
2471                    index = lastAudibleIndex;
2472                    // Correct the data base
2473                    sendMsg(mAudioHandler,
2474                            MSG_PERSIST_VOLUME,
2475                            SENDMSG_QUEUE,
2476                            PERSIST_CURRENT,
2477                            device,
2478                            this,
2479                            PERSIST_DELAY);
2480                }
2481                mIndex.put(device, getValidIndex(10 * index));
2482            }
2483        }
2484
2485        public void applyDeviceVolume(int device) {
2486            AudioSystem.setStreamVolumeIndex(mStreamType,
2487                                             (getIndex(device, false  /* lastAudible */) + 5)/10,
2488                                             device);
2489        }
2490
2491        public synchronized void applyAllVolumes() {
2492            // apply default volume first: by convention this will reset all
2493            // devices volumes in audio policy manager to the supplied value
2494            AudioSystem.setStreamVolumeIndex(mStreamType,
2495                    (getIndex(AudioSystem.DEVICE_OUT_DEFAULT, false /* lastAudible */) + 5)/10,
2496                    AudioSystem.DEVICE_OUT_DEFAULT);
2497            // then apply device specific volumes
2498            Set set = mIndex.entrySet();
2499            Iterator i = set.iterator();
2500            while (i.hasNext()) {
2501                Map.Entry entry = (Map.Entry)i.next();
2502                int device = ((Integer)entry.getKey()).intValue();
2503                if (device != AudioSystem.DEVICE_OUT_DEFAULT) {
2504                    AudioSystem.setStreamVolumeIndex(mStreamType,
2505                                                     ((Integer)entry.getValue() + 5)/10,
2506                                                     device);
2507                }
2508            }
2509        }
2510
2511        public boolean adjustIndex(int deltaIndex, int device) {
2512            return setIndex(getIndex(device,
2513                                     false  /* lastAudible */) + deltaIndex,
2514                            device,
2515                            true  /* lastAudible */);
2516        }
2517
2518        public synchronized boolean setIndex(int index, int device, boolean lastAudible) {
2519            int oldIndex = getIndex(device, false  /* lastAudible */);
2520            index = getValidIndex(index);
2521            mIndex.put(device, getValidIndex(index));
2522
2523            if (oldIndex != index) {
2524                if (lastAudible) {
2525                    mLastAudibleIndex.put(device, index);
2526                }
2527                // Apply change to all streams using this one as alias
2528                // if changing volume of current device, also change volume of current
2529                // device on aliased stream
2530                boolean currentDevice = (device == getDeviceForStream(mStreamType));
2531                int numStreamTypes = AudioSystem.getNumStreamTypes();
2532                for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2533                    if (streamType != mStreamType &&
2534                            mStreamVolumeAlias[streamType] == mStreamType) {
2535                        int scaledIndex = rescaleIndex(index, mStreamType, streamType);
2536                        mStreamStates[streamType].setIndex(scaledIndex,
2537                                                           device,
2538                                                           lastAudible);
2539                        if (currentDevice) {
2540                            mStreamStates[streamType].setIndex(scaledIndex,
2541                                                               getDeviceForStream(streamType),
2542                                                               lastAudible);
2543                        }
2544                    }
2545                }
2546                return true;
2547            } else {
2548                return false;
2549            }
2550        }
2551
2552        public synchronized int getIndex(int device, boolean lastAudible) {
2553            ConcurrentHashMap <Integer, Integer> indexes;
2554            if (lastAudible) {
2555                indexes = mLastAudibleIndex;
2556            } else {
2557                indexes = mIndex;
2558            }
2559            Integer index = indexes.get(device);
2560            if (index == null) {
2561                // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT
2562                index = indexes.get(AudioSystem.DEVICE_OUT_DEFAULT);
2563            }
2564            return index.intValue();
2565        }
2566
2567        public synchronized void setLastAudibleIndex(int index, int device) {
2568            // Apply change to all streams using this one as alias
2569            // if changing volume of current device, also change volume of current
2570            // device on aliased stream
2571            boolean currentDevice = (device == getDeviceForStream(mStreamType));
2572            int numStreamTypes = AudioSystem.getNumStreamTypes();
2573            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2574                if (streamType != mStreamType &&
2575                        mStreamVolumeAlias[streamType] == mStreamType) {
2576                    int scaledIndex = rescaleIndex(index, mStreamType, streamType);
2577                    mStreamStates[streamType].setLastAudibleIndex(scaledIndex, device);
2578                    if (currentDevice) {
2579                        mStreamStates[streamType].setLastAudibleIndex(scaledIndex,
2580                                                                   getDeviceForStream(streamType));
2581                    }
2582                }
2583            }
2584            mLastAudibleIndex.put(device, getValidIndex(index));
2585        }
2586
2587        public synchronized void adjustLastAudibleIndex(int deltaIndex, int device) {
2588            setLastAudibleIndex(getIndex(device,
2589                                         true  /* lastAudible */) + deltaIndex,
2590                                device);
2591        }
2592
2593        public int getMaxIndex() {
2594            return mIndexMax;
2595        }
2596
2597        // only called by setAllIndexes() which is already synchronized
2598        public ConcurrentHashMap <Integer, Integer> getAllIndexes(boolean lastAudible) {
2599            if (lastAudible) {
2600                return mLastAudibleIndex;
2601            } else {
2602                return mIndex;
2603            }
2604        }
2605
2606        public synchronized void setAllIndexes(VolumeStreamState srcStream, boolean lastAudible) {
2607            ConcurrentHashMap <Integer, Integer> indexes = srcStream.getAllIndexes(lastAudible);
2608            Set set = indexes.entrySet();
2609            Iterator i = set.iterator();
2610            while (i.hasNext()) {
2611                Map.Entry entry = (Map.Entry)i.next();
2612                int device = ((Integer)entry.getKey()).intValue();
2613                int index = ((Integer)entry.getValue()).intValue();
2614                index = rescaleIndex(index, srcStream.getStreamType(), mStreamType);
2615                setIndex(index, device, lastAudible);
2616            }
2617        }
2618
2619        public synchronized void mute(IBinder cb, boolean state) {
2620            VolumeDeathHandler handler = getDeathHandler(cb, state);
2621            if (handler == null) {
2622                Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
2623                return;
2624            }
2625            handler.mute(state);
2626        }
2627
2628        public int getStreamType() {
2629            return mStreamType;
2630        }
2631
2632        private int getValidIndex(int index) {
2633            if (index < 0) {
2634                return 0;
2635            } else if (index > mIndexMax) {
2636                return mIndexMax;
2637            }
2638
2639            return index;
2640        }
2641
2642        private class VolumeDeathHandler implements IBinder.DeathRecipient {
2643            private IBinder mICallback; // To be notified of client's death
2644            private int mMuteCount; // Number of active mutes for this client
2645
2646            VolumeDeathHandler(IBinder cb) {
2647                mICallback = cb;
2648            }
2649
2650            // must be called while synchronized on parent VolumeStreamState
2651            public void mute(boolean state) {
2652                if (state) {
2653                    if (mMuteCount == 0) {
2654                        // Register for client death notification
2655                        try {
2656                            // mICallback can be 0 if muted by AudioService
2657                            if (mICallback != null) {
2658                                mICallback.linkToDeath(this, 0);
2659                            }
2660                            mDeathHandlers.add(this);
2661                            // If the stream is not yet muted by any client, set level to 0
2662                            if (muteCount() == 0) {
2663                                Set set = mIndex.entrySet();
2664                                Iterator i = set.iterator();
2665                                while (i.hasNext()) {
2666                                    Map.Entry entry = (Map.Entry)i.next();
2667                                    int device = ((Integer)entry.getKey()).intValue();
2668                                    setIndex(0, device, false /* lastAudible */);
2669                                }
2670                                sendMsg(mAudioHandler,
2671                                        MSG_SET_ALL_VOLUMES,
2672                                        SENDMSG_QUEUE,
2673                                        0,
2674                                        0,
2675                                        VolumeStreamState.this, 0);
2676                            }
2677                        } catch (RemoteException e) {
2678                            // Client has died!
2679                            binderDied();
2680                            return;
2681                        }
2682                    } else {
2683                        Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
2684                    }
2685                    mMuteCount++;
2686                } else {
2687                    if (mMuteCount == 0) {
2688                        Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
2689                    } else {
2690                        mMuteCount--;
2691                        if (mMuteCount == 0) {
2692                            // Unregister from client death notification
2693                            mDeathHandlers.remove(this);
2694                            // mICallback can be 0 if muted by AudioService
2695                            if (mICallback != null) {
2696                                mICallback.unlinkToDeath(this, 0);
2697                            }
2698                            if (muteCount() == 0) {
2699                                // If the stream is not muted any more, restore its volume if
2700                                // ringer mode allows it
2701                                if (!isStreamAffectedByRingerMode(mStreamType) ||
2702                                        mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
2703                                    Set set = mIndex.entrySet();
2704                                    Iterator i = set.iterator();
2705                                    while (i.hasNext()) {
2706                                        Map.Entry entry = (Map.Entry)i.next();
2707                                        int device = ((Integer)entry.getKey()).intValue();
2708                                        setIndex(getIndex(device,
2709                                                          true  /* lastAudible */),
2710                                                 device,
2711                                                 false  /* lastAudible */);
2712                                    }
2713                                    sendMsg(mAudioHandler,
2714                                            MSG_SET_ALL_VOLUMES,
2715                                            SENDMSG_QUEUE,
2716                                            0,
2717                                            0,
2718                                            VolumeStreamState.this, 0);
2719                                }
2720                            }
2721                        }
2722                    }
2723                }
2724            }
2725
2726            public void binderDied() {
2727                Log.w(TAG, "Volume service client died for stream: "+mStreamType);
2728                if (mMuteCount != 0) {
2729                    // Reset all active mute requests from this client.
2730                    mMuteCount = 1;
2731                    mute(false);
2732                }
2733            }
2734        }
2735
2736        private synchronized int muteCount() {
2737            int count = 0;
2738            int size = mDeathHandlers.size();
2739            for (int i = 0; i < size; i++) {
2740                count += mDeathHandlers.get(i).mMuteCount;
2741            }
2742            return count;
2743        }
2744
2745        // only called by mute() which is already synchronized
2746        private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
2747            VolumeDeathHandler handler;
2748            int size = mDeathHandlers.size();
2749            for (int i = 0; i < size; i++) {
2750                handler = mDeathHandlers.get(i);
2751                if (cb == handler.mICallback) {
2752                    return handler;
2753                }
2754            }
2755            // If this is the first mute request for this client, create a new
2756            // client death handler. Otherwise, it is an out of sequence unmute request.
2757            if (state) {
2758                handler = new VolumeDeathHandler(cb);
2759            } else {
2760                Log.w(TAG, "stream was not muted by this client");
2761                handler = null;
2762            }
2763            return handler;
2764        }
2765
2766        private void dump(PrintWriter pw) {
2767            pw.print("   Current: ");
2768            Set set = mIndex.entrySet();
2769            Iterator i = set.iterator();
2770            while (i.hasNext()) {
2771                Map.Entry entry = (Map.Entry)i.next();
2772                pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
2773                             + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
2774            }
2775            pw.print("\n   Last audible: ");
2776            set = mLastAudibleIndex.entrySet();
2777            i = set.iterator();
2778            while (i.hasNext()) {
2779                Map.Entry entry = (Map.Entry)i.next();
2780                pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue())
2781                             + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", ");
2782            }
2783        }
2784    }
2785
2786    /** Thread that handles native AudioSystem control. */
2787    private class AudioSystemThread extends Thread {
2788        AudioSystemThread() {
2789            super("AudioService");
2790        }
2791
2792        @Override
2793        public void run() {
2794            // Set this thread up so the handler will work on it
2795            Looper.prepare();
2796
2797            synchronized(AudioService.this) {
2798                mAudioHandler = new AudioHandler();
2799
2800                // Notify that the handler has been created
2801                AudioService.this.notify();
2802            }
2803
2804            // Listen for volume change requests that are set by VolumePanel
2805            Looper.loop();
2806        }
2807    }
2808
2809    /** Handles internal volume messages in separate volume thread. */
2810    private class AudioHandler extends Handler {
2811
2812        private void setDeviceVolume(VolumeStreamState streamState, int device) {
2813
2814            // Apply volume
2815            streamState.applyDeviceVolume(device);
2816
2817            // Apply change to all streams using this one as alias
2818            int numStreamTypes = AudioSystem.getNumStreamTypes();
2819            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2820                if (streamType != streamState.mStreamType &&
2821                        mStreamVolumeAlias[streamType] == streamState.mStreamType) {
2822                    mStreamStates[streamType].applyDeviceVolume(getDeviceForStream(streamType));
2823                }
2824            }
2825
2826            // Post a persist volume msg
2827            sendMsg(mAudioHandler,
2828                    MSG_PERSIST_VOLUME,
2829                    SENDMSG_QUEUE,
2830                    PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
2831                    device,
2832                    streamState,
2833                    PERSIST_DELAY);
2834
2835        }
2836
2837        private void setAllVolumes(VolumeStreamState streamState) {
2838
2839            // Apply volume
2840            streamState.applyAllVolumes();
2841
2842            // Apply change to all streams using this one as alias
2843            int numStreamTypes = AudioSystem.getNumStreamTypes();
2844            for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
2845                if (streamType != streamState.mStreamType &&
2846                        mStreamVolumeAlias[streamType] == streamState.mStreamType) {
2847                    mStreamStates[streamType].applyAllVolumes();
2848                }
2849            }
2850        }
2851
2852        private void persistVolume(VolumeStreamState streamState,
2853                                   int persistType,
2854                                   int device) {
2855            if ((persistType & PERSIST_CURRENT) != 0) {
2856                System.putInt(mContentResolver,
2857                          streamState.getSettingNameForDevice(false /* lastAudible */, device),
2858                          (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10);
2859            }
2860            if ((persistType & PERSIST_LAST_AUDIBLE) != 0) {
2861                System.putInt(mContentResolver,
2862                        streamState.getSettingNameForDevice(true /* lastAudible */, device),
2863                        (streamState.getIndex(device, true  /* lastAudible */) + 5) / 10);
2864            }
2865        }
2866
2867        private void persistRingerMode(int ringerMode) {
2868            System.putInt(mContentResolver, System.MODE_RINGER, ringerMode);
2869        }
2870
2871        private void playSoundEffect(int effectType, int volume) {
2872            synchronized (mSoundEffectsLock) {
2873                if (mSoundPool == null) {
2874                    return;
2875                }
2876                float volFloat;
2877                // use default if volume is not specified by caller
2878                if (volume < 0) {
2879                    volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
2880                } else {
2881                    volFloat = (float) volume / 1000.0f;
2882                }
2883
2884                if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
2885                    mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
2886                } else {
2887                    MediaPlayer mediaPlayer = new MediaPlayer();
2888                    try {
2889                        String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
2890                        mediaPlayer.setDataSource(filePath);
2891                        mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
2892                        mediaPlayer.prepare();
2893                        mediaPlayer.setVolume(volFloat, volFloat);
2894                        mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
2895                            public void onCompletion(MediaPlayer mp) {
2896                                cleanupPlayer(mp);
2897                            }
2898                        });
2899                        mediaPlayer.setOnErrorListener(new OnErrorListener() {
2900                            public boolean onError(MediaPlayer mp, int what, int extra) {
2901                                cleanupPlayer(mp);
2902                                return true;
2903                            }
2904                        });
2905                        mediaPlayer.start();
2906                    } catch (IOException ex) {
2907                        Log.w(TAG, "MediaPlayer IOException: "+ex);
2908                    } catch (IllegalArgumentException ex) {
2909                        Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
2910                    } catch (IllegalStateException ex) {
2911                        Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2912                    }
2913                }
2914            }
2915        }
2916
2917        private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
2918            Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
2919                    receiver == null ? "" : receiver.flattenToString());
2920        }
2921
2922        private void cleanupPlayer(MediaPlayer mp) {
2923            if (mp != null) {
2924                try {
2925                    mp.stop();
2926                    mp.release();
2927                } catch (IllegalStateException ex) {
2928                    Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
2929                }
2930            }
2931        }
2932
2933        private void setForceUse(int usage, int config) {
2934            AudioSystem.setForceUse(usage, config);
2935        }
2936
2937        @Override
2938        public void handleMessage(Message msg) {
2939
2940            switch (msg.what) {
2941
2942                case MSG_SET_DEVICE_VOLUME:
2943                    setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1);
2944                    break;
2945
2946                case MSG_SET_ALL_VOLUMES:
2947                    setAllVolumes((VolumeStreamState) msg.obj);
2948                    break;
2949
2950                case MSG_PERSIST_VOLUME:
2951                    persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2);
2952                    break;
2953
2954                case MSG_PERSIST_MASTER_VOLUME:
2955                    Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER,
2956                            (float)msg.arg1 / (float)1000.0);
2957                    break;
2958
2959                case MSG_PERSIST_MASTER_VOLUME_MUTE:
2960                    Settings.System.putInt(mContentResolver, Settings.System.VOLUME_MASTER_MUTE,
2961                            msg.arg1);
2962                    break;
2963
2964                case MSG_PERSIST_RINGER_MODE:
2965                    // note that the value persisted is the current ringer mode, not the
2966                    // value of ringer mode as of the time the request was made to persist
2967                    persistRingerMode(getRingerMode());
2968                    break;
2969
2970                case MSG_MEDIA_SERVER_DIED:
2971                    if (!mMediaServerOk) {
2972                        Log.e(TAG, "Media server died.");
2973                        // Force creation of new IAudioFlinger interface so that we are notified
2974                        // when new media_server process is back to life.
2975                        AudioSystem.setErrorCallback(mAudioSystemCallback);
2976                        sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0,
2977                                null, 500);
2978                    }
2979                    break;
2980
2981                case MSG_MEDIA_SERVER_STARTED:
2982                    Log.e(TAG, "Media server started.");
2983                    // indicate to audio HAL that we start the reconfiguration phase after a media
2984                    // server crash
2985                    // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server
2986                    // process restarts after a crash, not the first time it is started.
2987                    AudioSystem.setParameters("restarting=true");
2988
2989                    // Restore device connection states
2990                    synchronized (mConnectedDevices) {
2991                        Set set = mConnectedDevices.entrySet();
2992                        Iterator i = set.iterator();
2993                        while (i.hasNext()) {
2994                            Map.Entry device = (Map.Entry)i.next();
2995                            AudioSystem.setDeviceConnectionState(
2996                                                            ((Integer)device.getKey()).intValue(),
2997                                                            AudioSystem.DEVICE_STATE_AVAILABLE,
2998                                                            (String)device.getValue());
2999                        }
3000                    }
3001                    // Restore call state
3002                    AudioSystem.setPhoneState(mMode);
3003
3004                    // Restore forced usage for communcations and record
3005                    AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
3006                    AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
3007
3008                    // Restore stream volumes
3009                    int numStreamTypes = AudioSystem.getNumStreamTypes();
3010                    for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
3011                        VolumeStreamState streamState = mStreamStates[streamType];
3012                        AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
3013
3014                        streamState.applyAllVolumes();
3015                    }
3016
3017                    // Restore ringer mode
3018                    setRingerModeInt(getRingerMode(), false);
3019
3020                    // Restore master volume
3021                    restoreMasterVolume();
3022
3023                    // Reset device orientation (if monitored for this device)
3024                    if (SystemProperties.getBoolean("ro.audio.monitorOrientation", false)) {
3025                        setOrientationForAudioSystem();
3026                    }
3027
3028                    synchronized (mBluetoothA2dpEnabledLock) {
3029                        AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
3030                                mBluetoothA2dpEnabled ?
3031                                        AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
3032                    }
3033                    // indicate the end of reconfiguration phase to audio HAL
3034                    AudioSystem.setParameters("restarting=false");
3035                    break;
3036
3037                case MSG_LOAD_SOUND_EFFECTS:
3038                    loadSoundEffects();
3039                    break;
3040
3041                case MSG_PLAY_SOUND_EFFECT:
3042                    playSoundEffect(msg.arg1, msg.arg2);
3043                    break;
3044
3045                case MSG_BTA2DP_DOCK_TIMEOUT:
3046                    // msg.obj  == address of BTA2DP device
3047                    synchronized (mConnectedDevices) {
3048                        makeA2dpDeviceUnavailableNow( (String) msg.obj );
3049                    }
3050                    break;
3051
3052                case MSG_SET_FORCE_USE:
3053                    setForceUse(msg.arg1, msg.arg2);
3054                    break;
3055
3056                case MSG_PERSIST_MEDIABUTTONRECEIVER:
3057                    onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj );
3058                    break;
3059
3060                case MSG_RCDISPLAY_CLEAR:
3061                    onRcDisplayClear();
3062                    break;
3063
3064                case MSG_RCDISPLAY_UPDATE:
3065                    // msg.obj is guaranteed to be non null
3066                    onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1);
3067                    break;
3068
3069                case MSG_BT_HEADSET_CNCT_FAILED:
3070                    resetBluetoothSco();
3071                    break;
3072
3073                case MSG_SET_WIRED_DEVICE_CONNECTION_STATE:
3074                    onSetWiredDeviceConnectionState(msg.arg1, msg.arg2, (String)msg.obj);
3075                    mMediaEventWakeLock.release();
3076                    break;
3077
3078                case MSG_SET_A2DP_CONNECTION_STATE:
3079                    onSetA2dpConnectionState((BluetoothDevice)msg.obj, msg.arg1);
3080                    mMediaEventWakeLock.release();
3081                    break;
3082
3083                case MSG_REPORT_NEW_ROUTES: {
3084                    int N = mRoutesObservers.beginBroadcast();
3085                    if (N > 0) {
3086                        AudioRoutesInfo routes;
3087                        synchronized (mCurAudioRoutes) {
3088                            routes = new AudioRoutesInfo(mCurAudioRoutes);
3089                        }
3090                        while (N > 0) {
3091                            N--;
3092                            IAudioRoutesObserver obs = mRoutesObservers.getBroadcastItem(N);
3093                            try {
3094                                obs.dispatchAudioRoutesChanged(routes);
3095                            } catch (RemoteException e) {
3096                            }
3097                        }
3098                    }
3099                    mRoutesObservers.finishBroadcast();
3100                    break;
3101                }
3102
3103                case MSG_REEVALUATE_REMOTE:
3104                    onReevaluateRemote();
3105                    break;
3106            }
3107        }
3108    }
3109
3110    private class SettingsObserver extends ContentObserver {
3111
3112        SettingsObserver() {
3113            super(new Handler());
3114            mContentResolver.registerContentObserver(Settings.System.getUriFor(
3115                Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
3116        }
3117
3118        @Override
3119        public void onChange(boolean selfChange) {
3120            super.onChange(selfChange);
3121            // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode.
3122            //       However there appear to be some missing locks around mRingerModeMutedStreams
3123            //       and mRingerModeAffectedStreams, so will leave this synchronized for now.
3124            //       mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once).
3125            synchronized (mSettingsLock) {
3126                int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
3127                       Settings.System.MODE_RINGER_STREAMS_AFFECTED,
3128                       ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
3129                       (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
3130                if (mVoiceCapable) {
3131                    ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
3132                } else {
3133                    ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
3134                }
3135                if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
3136                    /*
3137                     * Ensure all stream types that should be affected by ringer mode
3138                     * are in the proper state.
3139                     */
3140                    mRingerModeAffectedStreams = ringerModeAffectedStreams;
3141                    setRingerModeInt(getRingerMode(), false);
3142                }
3143            }
3144        }
3145    }
3146
3147    // must be called synchronized on mConnectedDevices
3148    private void makeA2dpDeviceAvailable(String address) {
3149        // enable A2DP before notifying A2DP connection to avoid unecessary processing in
3150        // audio policy manager
3151        setBluetoothA2dpOnInt(true);
3152        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
3153                AudioSystem.DEVICE_STATE_AVAILABLE,
3154                address);
3155        // Reset A2DP suspend state each time a new sink is connected
3156        AudioSystem.setParameters("A2dpSuspended=false");
3157        mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
3158                address);
3159    }
3160
3161    private void sendBecomingNoisyIntent() {
3162        mContext.sendBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
3163    }
3164
3165    // must be called synchronized on mConnectedDevices
3166    private void makeA2dpDeviceUnavailableNow(String address) {
3167        AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
3168                AudioSystem.DEVICE_STATE_UNAVAILABLE,
3169                address);
3170        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
3171    }
3172
3173    // must be called synchronized on mConnectedDevices
3174    private void makeA2dpDeviceUnavailableLater(String address) {
3175        // prevent any activity on the A2DP audio output to avoid unwanted
3176        // reconnection of the sink.
3177        AudioSystem.setParameters("A2dpSuspended=true");
3178        // the device will be made unavailable later, so consider it disconnected right away
3179        mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
3180        // send the delayed message to make the device unavailable later
3181        Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
3182        mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
3183
3184    }
3185
3186    // must be called synchronized on mConnectedDevices
3187    private void cancelA2dpDeviceTimeout() {
3188        mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
3189    }
3190
3191    // must be called synchronized on mConnectedDevices
3192    private boolean hasScheduledA2dpDockTimeout() {
3193        return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
3194    }
3195
3196    private void onSetA2dpConnectionState(BluetoothDevice btDevice, int state)
3197    {
3198        if (btDevice == null) {
3199            return;
3200        }
3201        String address = btDevice.getAddress();
3202        if (!BluetoothAdapter.checkBluetoothAddress(address)) {
3203            address = "";
3204        }
3205        synchronized (mConnectedDevices) {
3206            boolean isConnected =
3207                (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
3208                 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address));
3209
3210            if (isConnected && state != BluetoothProfile.STATE_CONNECTED) {
3211                if (btDevice.isBluetoothDock()) {
3212                    if (state == BluetoothProfile.STATE_DISCONNECTED) {
3213                        // introduction of a delay for transient disconnections of docks when
3214                        // power is rapidly turned off/on, this message will be canceled if
3215                        // we reconnect the dock under a preset delay
3216                        makeA2dpDeviceUnavailableLater(address);
3217                        // the next time isConnected is evaluated, it will be false for the dock
3218                    }
3219                } else {
3220                    makeA2dpDeviceUnavailableNow(address);
3221                }
3222                synchronized (mCurAudioRoutes) {
3223                    if (mCurAudioRoutes.mBluetoothName != null) {
3224                        mCurAudioRoutes.mBluetoothName = null;
3225                        sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3226                                SENDMSG_NOOP, 0, 0, null, 0);
3227                    }
3228                }
3229            } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) {
3230                if (btDevice.isBluetoothDock()) {
3231                    // this could be a reconnection after a transient disconnection
3232                    cancelA2dpDeviceTimeout();
3233                    mDockAddress = address;
3234                } else {
3235                    // this could be a connection of another A2DP device before the timeout of
3236                    // a dock: cancel the dock timeout, and make the dock unavailable now
3237                    if(hasScheduledA2dpDockTimeout()) {
3238                        cancelA2dpDeviceTimeout();
3239                        makeA2dpDeviceUnavailableNow(mDockAddress);
3240                    }
3241                }
3242                makeA2dpDeviceAvailable(address);
3243                synchronized (mCurAudioRoutes) {
3244                    String name = btDevice.getAliasName();
3245                    if (!TextUtils.equals(mCurAudioRoutes.mBluetoothName, name)) {
3246                        mCurAudioRoutes.mBluetoothName = name;
3247                        sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3248                                SENDMSG_NOOP, 0, 0, null, 0);
3249                    }
3250                }
3251            }
3252        }
3253    }
3254
3255    private boolean handleDeviceConnection(boolean connected, int device, String params) {
3256        synchronized (mConnectedDevices) {
3257            boolean isConnected = (mConnectedDevices.containsKey(device) &&
3258                    (params.isEmpty() || mConnectedDevices.get(device).equals(params)));
3259
3260            if (isConnected && !connected) {
3261                AudioSystem.setDeviceConnectionState(device,
3262                                              AudioSystem.DEVICE_STATE_UNAVAILABLE,
3263                                              mConnectedDevices.get(device));
3264                 mConnectedDevices.remove(device);
3265                 return true;
3266            } else if (!isConnected && connected) {
3267                 AudioSystem.setDeviceConnectionState(device,
3268                                                      AudioSystem.DEVICE_STATE_AVAILABLE,
3269                                                      params);
3270                 mConnectedDevices.put(new Integer(device), params);
3271                 return true;
3272            }
3273        }
3274        return false;
3275    }
3276
3277    // Devices which removal triggers intent ACTION_AUDIO_BECOMING_NOISY. The intent is only
3278    // sent if none of these devices is connected.
3279    int mBecomingNoisyIntentDevices =
3280            AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
3281            AudioSystem.DEVICE_OUT_ALL_A2DP;
3282
3283    // must be called before removing the device from mConnectedDevices
3284    private int checkSendBecomingNoisyIntent(int device, int state) {
3285        int delay = 0;
3286        if ((state == 0) && ((device & mBecomingNoisyIntentDevices) != 0)) {
3287            int devices = 0;
3288            for (int dev : mConnectedDevices.keySet()) {
3289                if ((dev & mBecomingNoisyIntentDevices) != 0) {
3290                   devices |= dev;
3291                }
3292            }
3293            if (devices == device) {
3294                delay = 1000;
3295                sendBecomingNoisyIntent();
3296            }
3297        }
3298
3299        if (mAudioHandler.hasMessages(MSG_SET_A2DP_CONNECTION_STATE) ||
3300                mAudioHandler.hasMessages(MSG_SET_WIRED_DEVICE_CONNECTION_STATE)) {
3301            delay = 1000;
3302        }
3303        return delay;
3304    }
3305
3306    private void sendDeviceConnectionIntent(int device, int state, String name)
3307    {
3308        Intent intent = new Intent();
3309
3310        intent.putExtra("state", state);
3311        intent.putExtra("name", name);
3312        intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
3313
3314        int connType = 0;
3315
3316        if (device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) {
3317            connType = AudioRoutesInfo.MAIN_HEADSET;
3318            intent.setAction(Intent.ACTION_HEADSET_PLUG);
3319            intent.putExtra("microphone", 1);
3320        } else if (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE) {
3321            connType = AudioRoutesInfo.MAIN_HEADPHONES;
3322            intent.setAction(Intent.ACTION_HEADSET_PLUG);
3323            intent.putExtra("microphone", 0);
3324        } else if (device == AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET) {
3325            connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
3326            intent.setAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG);
3327        } else if (device == AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET) {
3328            connType = AudioRoutesInfo.MAIN_DOCK_SPEAKERS;
3329            intent.setAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG);
3330        } else if (device == AudioSystem.DEVICE_OUT_AUX_DIGITAL) {
3331            connType = AudioRoutesInfo.MAIN_HDMI;
3332            intent.setAction(Intent.ACTION_HDMI_AUDIO_PLUG);
3333        }
3334
3335        synchronized (mCurAudioRoutes) {
3336            if (connType != 0) {
3337                int newConn = mCurAudioRoutes.mMainType;
3338                if (state != 0) {
3339                    newConn |= connType;
3340                } else {
3341                    newConn &= ~connType;
3342                }
3343                if (newConn != mCurAudioRoutes.mMainType) {
3344                    mCurAudioRoutes.mMainType = newConn;
3345                    sendMsg(mAudioHandler, MSG_REPORT_NEW_ROUTES,
3346                            SENDMSG_NOOP, 0, 0, null, 0);
3347                }
3348            }
3349        }
3350
3351        ActivityManagerNative.broadcastStickyIntent(intent, null);
3352    }
3353
3354    private void onSetWiredDeviceConnectionState(int device, int state, String name)
3355    {
3356        synchronized (mConnectedDevices) {
3357            if ((state == 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
3358                    (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
3359                setBluetoothA2dpOnInt(true);
3360            }
3361            handleDeviceConnection((state == 1), device, "");
3362            if ((state != 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
3363                    (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
3364                setBluetoothA2dpOnInt(false);
3365            }
3366            sendDeviceConnectionIntent(device, state, name);
3367        }
3368    }
3369
3370    /* cache of the address of the last dock the device was connected to */
3371    private String mDockAddress;
3372
3373    /**
3374     * Receiver for misc intent broadcasts the Phone app cares about.
3375     */
3376    private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
3377        @Override
3378        public void onReceive(Context context, Intent intent) {
3379            String action = intent.getAction();
3380            int device;
3381            int state;
3382
3383            if (action.equals(Intent.ACTION_DOCK_EVENT)) {
3384                int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
3385                        Intent.EXTRA_DOCK_STATE_UNDOCKED);
3386                int config;
3387                switch (dockState) {
3388                    case Intent.EXTRA_DOCK_STATE_DESK:
3389                        config = AudioSystem.FORCE_BT_DESK_DOCK;
3390                        break;
3391                    case Intent.EXTRA_DOCK_STATE_CAR:
3392                        config = AudioSystem.FORCE_BT_CAR_DOCK;
3393                        break;
3394                    case Intent.EXTRA_DOCK_STATE_LE_DESK:
3395                        config = AudioSystem.FORCE_ANALOG_DOCK;
3396                        break;
3397                    case Intent.EXTRA_DOCK_STATE_HE_DESK:
3398                        config = AudioSystem.FORCE_DIGITAL_DOCK;
3399                        break;
3400                    case Intent.EXTRA_DOCK_STATE_UNDOCKED:
3401                    default:
3402                        config = AudioSystem.FORCE_NONE;
3403                }
3404                AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
3405            } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
3406                state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
3407                                               BluetoothProfile.STATE_DISCONNECTED);
3408                device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
3409                String address = null;
3410
3411                BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
3412                if (btDevice == null) {
3413                    return;
3414                }
3415
3416                address = btDevice.getAddress();
3417                BluetoothClass btClass = btDevice.getBluetoothClass();
3418                if (btClass != null) {
3419                    switch (btClass.getDeviceClass()) {
3420                    case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
3421                    case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
3422                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
3423                        break;
3424                    case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
3425                        device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
3426                        break;
3427                    }
3428                }
3429
3430                if (!BluetoothAdapter.checkBluetoothAddress(address)) {
3431                    address = "";
3432                }
3433
3434                boolean connected = (state == BluetoothProfile.STATE_CONNECTED);
3435                if (handleDeviceConnection(connected, device, address)) {
3436                    synchronized (mScoClients) {
3437                        if (connected) {
3438                            mBluetoothHeadsetDevice = btDevice;
3439                        } else {
3440                            mBluetoothHeadsetDevice = null;
3441                            resetBluetoothSco();
3442                        }
3443                    }
3444                }
3445            } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ||
3446                           action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) {
3447                state = intent.getIntExtra("state", 0);
3448                int alsaCard = intent.getIntExtra("card", -1);
3449                int alsaDevice = intent.getIntExtra("device", -1);
3450                String params = (alsaCard == -1 && alsaDevice == -1 ? ""
3451                                    : "card=" + alsaCard + ";device=" + alsaDevice);
3452                device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
3453                        AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE;
3454                Log.v(TAG, "Broadcast Receiver: Got "
3455                        + (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
3456                              "ACTION_USB_AUDIO_ACCESSORY_PLUG" : "ACTION_USB_AUDIO_DEVICE_PLUG")
3457                        + ", state = " + state + ", card: " + alsaCard + ", device: " + alsaDevice);
3458                handleDeviceConnection((state == 1), device, params);
3459            } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
3460                boolean broadcast = false;
3461                int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
3462                synchronized (mScoClients) {
3463                    int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1);
3464                    // broadcast intent if the connection was initated by AudioService
3465                    if (!mScoClients.isEmpty() &&
3466                            (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL ||
3467                             mScoAudioState == SCO_STATE_ACTIVATE_REQ ||
3468                             mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) {
3469                        broadcast = true;
3470                    }
3471                    switch (btState) {
3472                    case BluetoothHeadset.STATE_AUDIO_CONNECTED:
3473                        scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED;
3474                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
3475                            mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
3476                            mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
3477                            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
3478                        }
3479                        break;
3480                    case BluetoothHeadset.STATE_AUDIO_DISCONNECTED:
3481                        scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
3482                        mScoAudioState = SCO_STATE_INACTIVE;
3483                        clearAllScoClients(0, false);
3484                        break;
3485                    case BluetoothHeadset.STATE_AUDIO_CONNECTING:
3486                        if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL &&
3487                            mScoAudioState != SCO_STATE_DEACTIVATE_REQ &&
3488                            mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) {
3489                            mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL;
3490                        }
3491                    default:
3492                        // do not broadcast CONNECTING or invalid state
3493                        broadcast = false;
3494                        break;
3495                    }
3496                }
3497                if (broadcast) {
3498                    broadcastScoConnectionState(scoAudioState);
3499                    //FIXME: this is to maintain compatibility with deprecated intent
3500                    // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
3501                    Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
3502                    newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
3503                    mContext.sendStickyBroadcast(newIntent);
3504                }
3505            } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
3506                mBootCompleted = true;
3507                sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP,
3508                        0, 0, null, 0);
3509
3510                mKeyguardManager =
3511                        (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
3512                mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR;
3513                resetBluetoothSco();
3514                getBluetoothHeadset();
3515                //FIXME: this is to maintain compatibility with deprecated intent
3516                // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
3517                Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
3518                newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
3519                        AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
3520                mContext.sendStickyBroadcast(newIntent);
3521
3522                BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
3523                if (adapter != null) {
3524                    adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
3525                                            BluetoothProfile.A2DP);
3526                }
3527            } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
3528                if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
3529                    // a package is being removed, not replaced
3530                    String packageName = intent.getData().getSchemeSpecificPart();
3531                    if (packageName != null) {
3532                        removeMediaButtonReceiverForPackage(packageName);
3533                    }
3534                }
3535            } else if (action.equals(Intent.ACTION_SCREEN_ON)) {
3536                AudioSystem.setParameters("screen_state=on");
3537            } else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
3538                AudioSystem.setParameters("screen_state=off");
3539            } else if (action.equalsIgnoreCase(Intent.ACTION_CONFIGURATION_CHANGED)) {
3540                handleConfigurationChanged(context);
3541            }
3542        }
3543    }
3544
3545    //==========================================================================================
3546    // AudioFocus
3547    //==========================================================================================
3548
3549    /* constant to identify focus stack entry that is used to hold the focus while the phone
3550     * is ringing or during a call. Used by com.android.internal.telephony.CallManager when
3551     * entering and exiting calls.
3552     */
3553    public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
3554
3555    private final static Object mAudioFocusLock = new Object();
3556
3557    private final static Object mRingingLock = new Object();
3558
3559    private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
3560        @Override
3561        public void onCallStateChanged(int state, String incomingNumber) {
3562            if (state == TelephonyManager.CALL_STATE_RINGING) {
3563                //Log.v(TAG, " CALL_STATE_RINGING");
3564                synchronized(mRingingLock) {
3565                    mIsRinging = true;
3566                }
3567            } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK)
3568                    || (state == TelephonyManager.CALL_STATE_IDLE)) {
3569                synchronized(mRingingLock) {
3570                    mIsRinging = false;
3571                }
3572            }
3573        }
3574    };
3575
3576    private void notifyTopOfAudioFocusStack() {
3577        // notify the top of the stack it gained focus
3578        if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
3579            if (canReassignAudioFocus()) {
3580                try {
3581                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
3582                            AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
3583                } catch (RemoteException e) {
3584                    Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
3585                    e.printStackTrace();
3586                }
3587            }
3588        }
3589    }
3590
3591    private static class FocusStackEntry {
3592        public int mStreamType = -1;// no stream type
3593        public IAudioFocusDispatcher mFocusDispatcher = null;
3594        public IBinder mSourceRef = null;
3595        public String mClientId;
3596        public int mFocusChangeType;
3597        public AudioFocusDeathHandler mHandler;
3598        public String mPackageName;
3599        public int mCallingUid;
3600
3601        public FocusStackEntry() {
3602        }
3603
3604        public FocusStackEntry(int streamType, int duration,
3605                IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr,
3606                String pn, int uid) {
3607            mStreamType = streamType;
3608            mFocusDispatcher = afl;
3609            mSourceRef = source;
3610            mClientId = id;
3611            mFocusChangeType = duration;
3612            mHandler = hdlr;
3613            mPackageName = pn;
3614            mCallingUid = uid;
3615        }
3616
3617        public void unlinkToDeath() {
3618            try {
3619                if (mSourceRef != null && mHandler != null) {
3620                    mSourceRef.unlinkToDeath(mHandler, 0);
3621                    mHandler = null;
3622                }
3623            } catch (java.util.NoSuchElementException e) {
3624                Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()");
3625            }
3626        }
3627
3628        @Override
3629        protected void finalize() throws Throwable {
3630            unlinkToDeath(); // unlink exception handled inside method
3631            super.finalize();
3632        }
3633    }
3634
3635    private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
3636
3637    /**
3638     * Helper function:
3639     * Display in the log the current entries in the audio focus stack
3640     */
3641    private void dumpFocusStack(PrintWriter pw) {
3642        pw.println("\nAudio Focus stack entries:");
3643        synchronized(mAudioFocusLock) {
3644            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3645            while(stackIterator.hasNext()) {
3646                FocusStackEntry fse = stackIterator.next();
3647                pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
3648                        + " -- duration: " + fse.mFocusChangeType
3649                        + " -- uid: " + fse.mCallingUid);
3650            }
3651        }
3652    }
3653
3654    /**
3655     * Helper function:
3656     * Called synchronized on mAudioFocusLock
3657     * Remove a focus listener from the focus stack.
3658     * @param focusListenerToRemove the focus listener
3659     * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
3660     *   focus, notify the next item in the stack it gained focus.
3661     */
3662    private void removeFocusStackEntry(String clientToRemove, boolean signal) {
3663        // is the current top of the focus stack abandoning focus? (because of request, not death)
3664        if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
3665        {
3666            //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
3667            FocusStackEntry fse = mFocusStack.pop();
3668            fse.unlinkToDeath();
3669            if (signal) {
3670                // notify the new top of the stack it gained focus
3671                notifyTopOfAudioFocusStack();
3672                // there's a new top of the stack, let the remote control know
3673                synchronized(mRCStack) {
3674                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3675                }
3676            }
3677        } else {
3678            // focus is abandoned by a client that's not at the top of the stack,
3679            // no need to update focus.
3680            Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3681            while(stackIterator.hasNext()) {
3682                FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
3683                if(fse.mClientId.equals(clientToRemove)) {
3684                    Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
3685                            + fse.mClientId);
3686                    stackIterator.remove();
3687                    fse.unlinkToDeath();
3688                }
3689            }
3690        }
3691    }
3692
3693    /**
3694     * Helper function:
3695     * Called synchronized on mAudioFocusLock
3696     * Remove focus listeners from the focus stack for a particular client when it has died.
3697     */
3698    private void removeFocusStackEntryForClient(IBinder cb) {
3699        // is the owner of the audio focus part of the client to remove?
3700        boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
3701                mFocusStack.peek().mSourceRef.equals(cb);
3702        Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
3703        while(stackIterator.hasNext()) {
3704            FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
3705            if(fse.mSourceRef.equals(cb)) {
3706                Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
3707                        + fse.mClientId);
3708                stackIterator.remove();
3709                // the client just died, no need to unlink to its death
3710            }
3711        }
3712        if (isTopOfStackForClientToRemove) {
3713            // we removed an entry at the top of the stack:
3714            //  notify the new top of the stack it gained focus.
3715            notifyTopOfAudioFocusStack();
3716            // there's a new top of the stack, let the remote control know
3717            synchronized(mRCStack) {
3718                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3719            }
3720        }
3721    }
3722
3723    /**
3724     * Helper function:
3725     * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
3726     */
3727    private boolean canReassignAudioFocus() {
3728        // focus requests are rejected during a phone call or when the phone is ringing
3729        // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
3730        if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
3731            return false;
3732        }
3733        return true;
3734    }
3735
3736    /**
3737     * Inner class to monitor audio focus client deaths, and remove them from the audio focus
3738     * stack if necessary.
3739     */
3740    private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
3741        private IBinder mCb; // To be notified of client's death
3742
3743        AudioFocusDeathHandler(IBinder cb) {
3744            mCb = cb;
3745        }
3746
3747        public void binderDied() {
3748            synchronized(mAudioFocusLock) {
3749                Log.w(TAG, "  AudioFocus   audio focus client died");
3750                removeFocusStackEntryForClient(mCb);
3751            }
3752        }
3753
3754        public IBinder getBinder() {
3755            return mCb;
3756        }
3757    }
3758
3759
3760    /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
3761    public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
3762            IAudioFocusDispatcher fd, String clientId, String callingPackageName) {
3763        Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
3764        // the main stream type for the audio focus request is currently not used. It may
3765        // potentially be used to handle multiple stream type-dependent audio focuses.
3766
3767        // we need a valid binder callback for clients
3768        if (!cb.pingBinder()) {
3769            Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting.");
3770            return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3771        }
3772
3773        synchronized(mAudioFocusLock) {
3774            if (!canReassignAudioFocus()) {
3775                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3776            }
3777
3778            // handle the potential premature death of the new holder of the focus
3779            // (premature death == death before abandoning focus)
3780            // Register for client death notification
3781            AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
3782            try {
3783                cb.linkToDeath(afdh, 0);
3784            } catch (RemoteException e) {
3785                // client has already died!
3786                Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
3787                return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
3788            }
3789
3790            if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
3791                // if focus is already owned by this client and the reason for acquiring the focus
3792                // hasn't changed, don't do anything
3793                if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
3794                    // unlink death handler so it can be gc'ed.
3795                    // linkToDeath() creates a JNI global reference preventing collection.
3796                    cb.unlinkToDeath(afdh, 0);
3797                    return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3798                }
3799                // the reason for the audio focus request has changed: remove the current top of
3800                // stack and respond as if we had a new focus owner
3801                FocusStackEntry fse = mFocusStack.pop();
3802                fse.unlinkToDeath();
3803            }
3804
3805            // notify current top of stack it is losing focus
3806            if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
3807                try {
3808                    mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
3809                            -1 * focusChangeHint, // loss and gain codes are inverse of each other
3810                            mFocusStack.peek().mClientId);
3811                } catch (RemoteException e) {
3812                    Log.e(TAG, " Failure to signal loss of focus due to "+ e);
3813                    e.printStackTrace();
3814                }
3815            }
3816
3817            // focus requester might already be somewhere below in the stack, remove it
3818            removeFocusStackEntry(clientId, false /* signal */);
3819
3820            // push focus requester at the top of the audio focus stack
3821            mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb,
3822                    clientId, afdh, callingPackageName, Binder.getCallingUid()));
3823
3824            // there's a new top of the stack, let the remote control know
3825            synchronized(mRCStack) {
3826                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
3827            }
3828        }//synchronized(mAudioFocusLock)
3829
3830        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3831    }
3832
3833    /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
3834    public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
3835        Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
3836        try {
3837            // this will take care of notifying the new focus owner if needed
3838            synchronized(mAudioFocusLock) {
3839                removeFocusStackEntry(clientId, true);
3840            }
3841        } catch (java.util.ConcurrentModificationException cme) {
3842            // Catching this exception here is temporary. It is here just to prevent
3843            // a crash seen when the "Silent" notification is played. This is believed to be fixed
3844            // but this try catch block is left just to be safe.
3845            Log.e(TAG, "FATAL EXCEPTION AudioFocus  abandonAudioFocus() caused " + cme);
3846            cme.printStackTrace();
3847        }
3848
3849        return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
3850    }
3851
3852
3853    public void unregisterAudioFocusClient(String clientId) {
3854        synchronized(mAudioFocusLock) {
3855            removeFocusStackEntry(clientId, false);
3856        }
3857    }
3858
3859
3860    //==========================================================================================
3861    // RemoteControl
3862    //==========================================================================================
3863    public void dispatchMediaKeyEvent(KeyEvent keyEvent) {
3864        filterMediaKeyEvent(keyEvent, false /*needWakeLock*/);
3865    }
3866
3867    public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) {
3868        filterMediaKeyEvent(keyEvent, true /*needWakeLock*/);
3869    }
3870
3871    private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
3872        // sanity check on the incoming key event
3873        if (!isValidMediaKeyEvent(keyEvent)) {
3874            Log.e(TAG, "not dispatching invalid media key event " + keyEvent);
3875            return;
3876        }
3877        // event filtering for telephony
3878        synchronized(mRingingLock) {
3879            synchronized(mRCStack) {
3880                if ((mMediaReceiverForCalls != null) &&
3881                        (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL))) {
3882                    dispatchMediaKeyEventForCalls(keyEvent, needWakeLock);
3883                    return;
3884                }
3885            }
3886        }
3887        // event filtering based on voice-based interactions
3888        if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) {
3889            filterVoiceInputKeyEvent(keyEvent, needWakeLock);
3890        } else {
3891            dispatchMediaKeyEvent(keyEvent, needWakeLock);
3892        }
3893    }
3894
3895    /**
3896     * Handles the dispatching of the media button events to the telephony package.
3897     * Precondition: mMediaReceiverForCalls != null
3898     * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
3899     * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
3900     *     is dispatched.
3901     */
3902    private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) {
3903        Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
3904        keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
3905        keyIntent.setPackage(mMediaReceiverForCalls.getPackageName());
3906        if (needWakeLock) {
3907            mMediaEventWakeLock.acquire();
3908            keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
3909        }
3910        mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
3911                mAudioHandler, Activity.RESULT_OK, null, null);
3912    }
3913
3914    /**
3915     * Handles the dispatching of the media button events to one of the registered listeners,
3916     * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system.
3917     * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons
3918     * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
3919     *     is dispatched.
3920     */
3921    private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
3922        if (needWakeLock) {
3923            mMediaEventWakeLock.acquire();
3924        }
3925        Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null);
3926        keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);
3927        synchronized(mRCStack) {
3928            if (!mRCStack.empty()) {
3929                // send the intent that was registered by the client
3930                try {
3931                    mRCStack.peek().mMediaIntent.send(mContext,
3932                            needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/,
3933                            keyIntent, AudioService.this, mAudioHandler);
3934                } catch (CanceledException e) {
3935                    Log.e(TAG, "Error sending pending intent " + mRCStack.peek());
3936                    e.printStackTrace();
3937                }
3938            } else {
3939                // legacy behavior when nobody registered their media button event receiver
3940                //    through AudioManager
3941                if (needWakeLock) {
3942                    keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
3943                }
3944                mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
3945                        mAudioHandler, Activity.RESULT_OK, null, null);
3946            }
3947        }
3948    }
3949
3950    /**
3951     * The different actions performed in response to a voice button key event.
3952     */
3953    private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1;
3954    private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2;
3955    private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3;
3956
3957    private final Object mVoiceEventLock = new Object();
3958    private boolean mVoiceButtonDown;
3959    private boolean mVoiceButtonHandled;
3960
3961    /**
3962     * Filter key events that may be used for voice-based interactions
3963     * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported
3964     *    media buttons that can be used to trigger voice-based interactions.
3965     * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event
3966     *     is dispatched.
3967     */
3968    private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) {
3969        if (DEBUG_RC) {
3970            Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock);
3971        }
3972
3973        int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS;
3974        int keyAction = keyEvent.getAction();
3975        synchronized (mVoiceEventLock) {
3976            if (keyAction == KeyEvent.ACTION_DOWN) {
3977                if (keyEvent.getRepeatCount() == 0) {
3978                    // initial down
3979                    mVoiceButtonDown = true;
3980                    mVoiceButtonHandled = false;
3981                } else if (mVoiceButtonDown && !mVoiceButtonHandled
3982                        && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) {
3983                    // long-press, start voice-based interactions
3984                    mVoiceButtonHandled = true;
3985                    voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT;
3986                }
3987            } else if (keyAction == KeyEvent.ACTION_UP) {
3988                if (mVoiceButtonDown) {
3989                    // voice button up
3990                    mVoiceButtonDown = false;
3991                    if (!mVoiceButtonHandled && !keyEvent.isCanceled()) {
3992                        voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS;
3993                    }
3994                }
3995            }
3996        }//synchronized (mVoiceEventLock)
3997
3998        // take action after media button event filtering for voice-based interactions
3999        switch (voiceButtonAction) {
4000            case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS:
4001                if (DEBUG_RC) Log.v(TAG, "   ignore key event");
4002                break;
4003            case VOICEBUTTON_ACTION_START_VOICE_INPUT:
4004                if (DEBUG_RC) Log.v(TAG, "   start voice-based interactions");
4005                // then start the voice-based interactions
4006                startVoiceBasedInteractions(needWakeLock);
4007                break;
4008            case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
4009                if (DEBUG_RC) Log.v(TAG, "   send simulated key event");
4010                sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
4011                break;
4012        }
4013    }
4014
4015    private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) {
4016        // send DOWN event
4017        KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN);
4018        dispatchMediaKeyEvent(keyEvent, needWakeLock);
4019        // send UP event
4020        keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP);
4021        dispatchMediaKeyEvent(keyEvent, needWakeLock);
4022
4023    }
4024
4025
4026    private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) {
4027        if (keyEvent == null) {
4028            return false;
4029        }
4030        final int keyCode = keyEvent.getKeyCode();
4031        switch (keyCode) {
4032            case KeyEvent.KEYCODE_MUTE:
4033            case KeyEvent.KEYCODE_HEADSETHOOK:
4034            case KeyEvent.KEYCODE_MEDIA_PLAY:
4035            case KeyEvent.KEYCODE_MEDIA_PAUSE:
4036            case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
4037            case KeyEvent.KEYCODE_MEDIA_STOP:
4038            case KeyEvent.KEYCODE_MEDIA_NEXT:
4039            case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
4040            case KeyEvent.KEYCODE_MEDIA_REWIND:
4041            case KeyEvent.KEYCODE_MEDIA_RECORD:
4042            case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
4043            case KeyEvent.KEYCODE_MEDIA_CLOSE:
4044            case KeyEvent.KEYCODE_MEDIA_EJECT:
4045                break;
4046            default:
4047                return false;
4048        }
4049        return true;
4050    }
4051
4052    /**
4053     * Checks whether the given key code is one that can trigger the launch of voice-based
4054     *   interactions.
4055     * @param keyCode the key code associated with the key event
4056     * @return true if the key is one of the supported voice-based interaction triggers
4057     */
4058    private static boolean isValidVoiceInputKeyCode(int keyCode) {
4059        if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) {
4060            return true;
4061        } else {
4062            return false;
4063        }
4064    }
4065
4066    /**
4067     * Tell the system to start voice-based interactions / voice commands
4068     */
4069    private void startVoiceBasedInteractions(boolean needWakeLock) {
4070        Intent voiceIntent = null;
4071        // select which type of search to launch:
4072        // - screen on and device unlocked: action is ACTION_WEB_SEARCH
4073        // - device locked or screen off: action is ACTION_VOICE_SEARCH_HANDS_FREE
4074        //    with EXTRA_SECURE set to true if the device is securely locked
4075        PowerManager pm = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
4076        boolean isLocked = mKeyguardManager != null && mKeyguardManager.isKeyguardLocked();
4077        if (!isLocked && pm.isScreenOn()) {
4078            voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH);
4079        } else {
4080            voiceIntent = new Intent(RecognizerIntent.ACTION_VOICE_SEARCH_HANDS_FREE);
4081            voiceIntent.putExtra(RecognizerIntent.EXTRA_SECURE,
4082                    isLocked && mKeyguardManager.isKeyguardSecure());
4083        }
4084        // start the search activity
4085        if (needWakeLock) {
4086            mMediaEventWakeLock.acquire();
4087        }
4088        try {
4089            if (voiceIntent != null) {
4090                voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
4091                        | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
4092                mContext.startActivity(voiceIntent);
4093            }
4094        } catch (ActivityNotFoundException e) {
4095            Log.w(TAG, "No activity for search: " + e);
4096        } finally {
4097            if (needWakeLock) {
4098                mMediaEventWakeLock.release();
4099            }
4100        }
4101    }
4102
4103    private PowerManager.WakeLock mMediaEventWakeLock;
4104
4105    private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number
4106
4107    // only set when wakelock was acquired, no need to check value when received
4108    private static final String EXTRA_WAKELOCK_ACQUIRED =
4109            "android.media.AudioService.WAKELOCK_ACQUIRED";
4110
4111    public void onSendFinished(PendingIntent pendingIntent, Intent intent,
4112            int resultCode, String resultData, Bundle resultExtras) {
4113        if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) {
4114            mMediaEventWakeLock.release();
4115        }
4116    }
4117
4118    BroadcastReceiver mKeyEventDone = new BroadcastReceiver() {
4119        public void onReceive(Context context, Intent intent) {
4120            if (intent == null) {
4121                return;
4122            }
4123            Bundle extras = intent.getExtras();
4124            if (extras == null) {
4125                return;
4126            }
4127            if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) {
4128                mMediaEventWakeLock.release();
4129            }
4130        }
4131    };
4132
4133    private final Object mCurrentRcLock = new Object();
4134    /**
4135     * The one remote control client which will receive a request for display information.
4136     * This object may be null.
4137     * Access protected by mCurrentRcLock.
4138     */
4139    private IRemoteControlClient mCurrentRcClient = null;
4140
4141    private final static int RC_INFO_NONE = 0;
4142    private final static int RC_INFO_ALL =
4143        RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART |
4144        RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA |
4145        RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA |
4146        RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE;
4147
4148    /**
4149     * A monotonically increasing generation counter for mCurrentRcClient.
4150     * Only accessed with a lock on mCurrentRcLock.
4151     * No value wrap-around issues as we only act on equal values.
4152     */
4153    private int mCurrentRcClientGen = 0;
4154
4155    /**
4156     * Inner class to monitor remote control client deaths, and remove the client for the
4157     * remote control stack if necessary.
4158     */
4159    private class RcClientDeathHandler implements IBinder.DeathRecipient {
4160        private IBinder mCb; // To be notified of client's death
4161        private PendingIntent mMediaIntent;
4162
4163        RcClientDeathHandler(IBinder cb, PendingIntent pi) {
4164            mCb = cb;
4165            mMediaIntent = pi;
4166        }
4167
4168        public void binderDied() {
4169            Log.w(TAG, "  RemoteControlClient died");
4170            // remote control client died, make sure the displays don't use it anymore
4171            //  by setting its remote control client to null
4172            registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/);
4173            // the dead client was maybe handling remote playback, reevaluate
4174            postReevaluateRemote();
4175        }
4176
4177        public IBinder getBinder() {
4178            return mCb;
4179        }
4180    }
4181
4182    /**
4183     * A global counter for RemoteControlClient identifiers
4184     */
4185    private static int sLastRccId = 0;
4186
4187    private class RemotePlaybackState {
4188        int mRccId;
4189        int mVolume;
4190        int mVolumeMax;
4191        int mVolumeHandling;
4192
4193        private RemotePlaybackState(int id, int vol, int volMax) {
4194            mRccId = id;
4195            mVolume = vol;
4196            mVolumeMax = volMax;
4197            mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
4198        }
4199    }
4200
4201    /**
4202     * Internal cache for the playback information of the RemoteControlClient whose volume gets to
4203     * be controlled by the volume keys ("main"), so we don't have to iterate over the RC stack
4204     * every time we need this info.
4205     */
4206    private RemotePlaybackState mMainRemote;
4207    /**
4208     * Indicates whether the "main" RemoteControlClient is considered active.
4209     * Use synchronized on mMainRemote.
4210     */
4211    private boolean mMainRemoteIsActive;
4212    /**
4213     * Indicates whether there is remote playback going on. True even if there is no "active"
4214     * remote playback (mMainRemoteIsActive is false), but a RemoteControlClient has declared it
4215     * handles remote playback.
4216     * Use synchronized on mMainRemote.
4217     */
4218    private boolean mHasRemotePlayback;
4219
4220    private static class RemoteControlStackEntry {
4221        public int mRccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
4222        /**
4223         * The target for the ACTION_MEDIA_BUTTON events.
4224         * Always non null.
4225         */
4226        public PendingIntent mMediaIntent;
4227        /**
4228         * The registered media button event receiver.
4229         * Always non null.
4230         */
4231        public ComponentName mReceiverComponent;
4232        public String mCallingPackageName;
4233        public int mCallingUid;
4234        /**
4235         * Provides access to the information to display on the remote control.
4236         * May be null (when a media button event receiver is registered,
4237         *     but no remote control client has been registered) */
4238        public IRemoteControlClient mRcClient;
4239        public RcClientDeathHandler mRcClientDeathHandler;
4240        /**
4241         * Information only used for non-local playback
4242         */
4243        public int mPlaybackType;
4244        public int mPlaybackVolume;
4245        public int mPlaybackVolumeMax;
4246        public int mPlaybackVolumeHandling;
4247        public int mPlaybackStream;
4248        public int mPlaybackState;
4249
4250        public void resetPlaybackInfo() {
4251            mPlaybackType = RemoteControlClient.PLAYBACK_TYPE_LOCAL;
4252            mPlaybackVolume = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
4253            mPlaybackVolumeMax = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME;
4254            mPlaybackVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
4255            mPlaybackStream = AudioManager.STREAM_MUSIC;
4256            mPlaybackState = RemoteControlClient.PLAYSTATE_STOPPED;
4257        }
4258
4259        /** precondition: mediaIntent != null, eventReceiver != null */
4260        public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) {
4261            mMediaIntent = mediaIntent;
4262            mReceiverComponent = eventReceiver;
4263            mCallingUid = -1;
4264            mRcClient = null;
4265            mRccId = ++sLastRccId;
4266
4267            resetPlaybackInfo();
4268        }
4269
4270        public void unlinkToRcClientDeath() {
4271            if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) {
4272                try {
4273                    mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0);
4274                    mRcClientDeathHandler = null;
4275                } catch (java.util.NoSuchElementException e) {
4276                    // not much we can do here
4277                    Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()");
4278                    e.printStackTrace();
4279                }
4280            }
4281        }
4282
4283        @Override
4284        protected void finalize() throws Throwable {
4285            unlinkToRcClientDeath();// unlink exception handled inside method
4286            super.finalize();
4287        }
4288    }
4289
4290    /**
4291     *  The stack of remote control event receivers.
4292     *  Code sections and methods that modify the remote control event receiver stack are
4293     *  synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either
4294     *  stack, audio focus or RC, can lead to a change in the remote control display
4295     */
4296    private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
4297
4298    /**
4299     * The component the telephony package can register so telephony calls have priority to
4300     * handle media button events
4301     */
4302    private ComponentName mMediaReceiverForCalls = null;
4303
4304    /**
4305     * Helper function:
4306     * Display in the log the current entries in the remote control focus stack
4307     */
4308    private void dumpRCStack(PrintWriter pw) {
4309        pw.println("\nRemote Control stack entries:");
4310        synchronized(mRCStack) {
4311            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4312            while(stackIterator.hasNext()) {
4313                RemoteControlStackEntry rcse = stackIterator.next();
4314                pw.println("  pi: " + rcse.mMediaIntent +
4315                        "  -- ercvr: " + rcse.mReceiverComponent +
4316                        "  -- client: " + rcse.mRcClient +
4317                        "  -- uid: " + rcse.mCallingUid +
4318                        "  -- type: " + rcse.mPlaybackType +
4319                        "  state: " + rcse.mPlaybackState);
4320            }
4321        }
4322    }
4323
4324    /**
4325     * Helper function:
4326     * Display in the log the current entries in the remote control stack, focusing
4327     * on RemoteControlClient data
4328     */
4329    private void dumpRCCStack(PrintWriter pw) {
4330        pw.println("\nRemote Control Client stack entries:");
4331        synchronized(mRCStack) {
4332            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4333            while(stackIterator.hasNext()) {
4334                RemoteControlStackEntry rcse = stackIterator.next();
4335                pw.println("  uid: " + rcse.mCallingUid +
4336                        "  -- id: " + rcse.mRccId +
4337                        "  -- type: " + rcse.mPlaybackType +
4338                        "  -- state: " + rcse.mPlaybackState +
4339                        "  -- vol handling: " + rcse.mPlaybackVolumeHandling +
4340                        "  -- vol: " + rcse.mPlaybackVolume +
4341                        "  -- volMax: " + rcse.mPlaybackVolumeMax);
4342            }
4343        }
4344        synchronized (mMainRemote) {
4345            pw.println("\nRemote Volume State:");
4346            pw.println("  has remote: " + mHasRemotePlayback);
4347            pw.println("  is remote active: " + mMainRemoteIsActive);
4348            pw.println("  rccId: " + mMainRemote.mRccId);
4349            pw.println("  volume handling: "
4350                    + ((mMainRemote.mVolumeHandling == RemoteControlClient.PLAYBACK_VOLUME_FIXED) ?
4351                            "PLAYBACK_VOLUME_FIXED(0)" : "PLAYBACK_VOLUME_VARIABLE(1)"));
4352            pw.println("  volume: " + mMainRemote.mVolume);
4353            pw.println("  volume steps: " + mMainRemote.mVolumeMax);
4354        }
4355    }
4356
4357    /**
4358     * Helper function:
4359     * Remove any entry in the remote control stack that has the same package name as packageName
4360     * Pre-condition: packageName != null
4361     */
4362    private void removeMediaButtonReceiverForPackage(String packageName) {
4363        synchronized(mRCStack) {
4364            if (mRCStack.empty()) {
4365                return;
4366            } else {
4367                RemoteControlStackEntry oldTop = mRCStack.peek();
4368                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4369                // iterate over the stack entries
4370                while(stackIterator.hasNext()) {
4371                    RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
4372                    if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) {
4373                        // a stack entry is from the package being removed, remove it from the stack
4374                        stackIterator.remove();
4375                        rcse.unlinkToRcClientDeath();
4376                    }
4377                }
4378                if (mRCStack.empty()) {
4379                    // no saved media button receiver
4380                    mAudioHandler.sendMessage(
4381                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
4382                                    null));
4383                } else if (oldTop != mRCStack.peek()) {
4384                    // the top of the stack has changed, save it in the system settings
4385                    // by posting a message to persist it
4386                    mAudioHandler.sendMessage(
4387                            mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0,
4388                                    mRCStack.peek().mReceiverComponent));
4389                }
4390            }
4391        }
4392    }
4393
4394    /**
4395     * Helper function:
4396     * Restore remote control receiver from the system settings.
4397     */
4398    private void restoreMediaButtonReceiver() {
4399        String receiverName = Settings.System.getString(mContentResolver,
4400                Settings.System.MEDIA_BUTTON_RECEIVER);
4401        if ((null != receiverName) && !receiverName.isEmpty()) {
4402            ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
4403            // construct a PendingIntent targeted to the restored component name
4404            // for the media button and register it
4405            Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
4406            //     the associated intent will be handled by the component being registered
4407            mediaButtonIntent.setComponent(eventReceiver);
4408            PendingIntent pi = PendingIntent.getBroadcast(mContext,
4409                    0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/);
4410            registerMediaButtonIntent(pi, eventReceiver);
4411        }
4412    }
4413
4414    /**
4415     * Helper function:
4416     * Set the new remote control receiver at the top of the RC focus stack.
4417     * precondition: mediaIntent != null, target != null
4418     */
4419    private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) {
4420        // already at top of stack?
4421        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) {
4422            return;
4423        }
4424        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4425        RemoteControlStackEntry rcse = null;
4426        boolean wasInsideStack = false;
4427        while(stackIterator.hasNext()) {
4428            rcse = (RemoteControlStackEntry)stackIterator.next();
4429            if(rcse.mMediaIntent.equals(mediaIntent)) {
4430                wasInsideStack = true;
4431                stackIterator.remove();
4432                break;
4433            }
4434        }
4435        if (!wasInsideStack) {
4436            rcse = new RemoteControlStackEntry(mediaIntent, target);
4437        }
4438        mRCStack.push(rcse);
4439
4440        // post message to persist the default media button receiver
4441        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(
4442                MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) );
4443    }
4444
4445    /**
4446     * Helper function:
4447     * Remove the remote control receiver from the RC focus stack.
4448     * precondition: pi != null
4449     */
4450    private void removeMediaButtonReceiver(PendingIntent pi) {
4451        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4452        while(stackIterator.hasNext()) {
4453            RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
4454            if(rcse.mMediaIntent.equals(pi)) {
4455                stackIterator.remove();
4456                rcse.unlinkToRcClientDeath();
4457                break;
4458            }
4459        }
4460    }
4461
4462    /**
4463     * Helper function:
4464     * Called synchronized on mRCStack
4465     */
4466    private boolean isCurrentRcController(PendingIntent pi) {
4467        if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) {
4468            return true;
4469        }
4470        return false;
4471    }
4472
4473    //==========================================================================================
4474    // Remote control display / client
4475    //==========================================================================================
4476    /**
4477     * Update the remote control displays with the new "focused" client generation
4478     */
4479    private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration,
4480            PendingIntent newMediaIntent, boolean clearing) {
4481        // NOTE: Only one IRemoteControlDisplay supported in this implementation
4482        if (mRcDisplay != null) {
4483            try {
4484                mRcDisplay.setCurrentClientId(
4485                        newClientGeneration, newMediaIntent, clearing);
4486            } catch (RemoteException e) {
4487                Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e);
4488                // if we had a display before, stop monitoring its death
4489                rcDisplay_stopDeathMonitor_syncRcStack();
4490                mRcDisplay = null;
4491            }
4492        }
4493    }
4494
4495    /**
4496     * Update the remote control clients with the new "focused" client generation
4497     */
4498    private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) {
4499        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4500        while(stackIterator.hasNext()) {
4501            RemoteControlStackEntry se = stackIterator.next();
4502            if ((se != null) && (se.mRcClient != null)) {
4503                try {
4504                    se.mRcClient.setCurrentClientGenerationId(newClientGeneration);
4505                } catch (RemoteException e) {
4506                    Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e);
4507                    stackIterator.remove();
4508                    se.unlinkToRcClientDeath();
4509                }
4510            }
4511        }
4512    }
4513
4514    /**
4515     * Update the displays and clients with the new "focused" client generation and name
4516     * @param newClientGeneration the new generation value matching a client update
4517     * @param newClientEventReceiver the media button event receiver associated with the client.
4518     *    May be null, which implies there is no registered media button event receiver.
4519     * @param clearing true if the new client generation value maps to a remote control update
4520     *    where the display should be cleared.
4521     */
4522    private void setNewRcClient_syncRcsCurrc(int newClientGeneration,
4523            PendingIntent newMediaIntent, boolean clearing) {
4524        // send the new valid client generation ID to all displays
4525        setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing);
4526        // send the new valid client generation ID to all clients
4527        setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration);
4528    }
4529
4530    /**
4531     * Called when processing MSG_RCDISPLAY_CLEAR event
4532     */
4533    private void onRcDisplayClear() {
4534        if (DEBUG_RC) Log.i(TAG, "Clear remote control display");
4535
4536        synchronized(mRCStack) {
4537            synchronized(mCurrentRcLock) {
4538                mCurrentRcClientGen++;
4539                // synchronously update the displays and clients with the new client generation
4540                setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
4541                        null /*newMediaIntent*/, true /*clearing*/);
4542            }
4543        }
4544    }
4545
4546    /**
4547     * Called when processing MSG_RCDISPLAY_UPDATE event
4548     */
4549    private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) {
4550        synchronized(mRCStack) {
4551            synchronized(mCurrentRcLock) {
4552                if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) {
4553                    if (DEBUG_RC) Log.i(TAG, "Display/update remote control ");
4554
4555                    mCurrentRcClientGen++;
4556                    // synchronously update the displays and clients with
4557                    //      the new client generation
4558                    setNewRcClient_syncRcsCurrc(mCurrentRcClientGen,
4559                            rcse.mMediaIntent /*newMediaIntent*/,
4560                            false /*clearing*/);
4561
4562                    // tell the current client that it needs to send info
4563                    try {
4564                        mCurrentRcClient.onInformationRequested(mCurrentRcClientGen,
4565                                flags, mArtworkExpectedWidth, mArtworkExpectedHeight);
4566                    } catch (RemoteException e) {
4567                        Log.e(TAG, "Current valid remote client is dead: "+e);
4568                        mCurrentRcClient = null;
4569                    }
4570                } else {
4571                    // the remote control display owner has changed between the
4572                    // the message to update the display was sent, and the time it
4573                    // gets to be processed (now)
4574                }
4575            }
4576        }
4577    }
4578
4579
4580    /**
4581     * Helper function:
4582     * Called synchronized on mRCStack
4583     */
4584    private void clearRemoteControlDisplay_syncAfRcs() {
4585        synchronized(mCurrentRcLock) {
4586            mCurrentRcClient = null;
4587        }
4588        // will cause onRcDisplayClear() to be called in AudioService's handler thread
4589        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) );
4590    }
4591
4592    /**
4593     * Helper function for code readability: only to be called from
4594     *    checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for
4595     *    this method.
4596     * Preconditions:
4597     *    - called synchronized mAudioFocusLock then on mRCStack
4598     *    - mRCStack.isEmpty() is false
4599     */
4600    private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
4601        RemoteControlStackEntry rcse = mRCStack.peek();
4602        int infoFlagsAboutToBeUsed = infoChangedFlags;
4603        // this is where we enforce opt-in for information display on the remote controls
4604        //   with the new AudioManager.registerRemoteControlClient() API
4605        if (rcse.mRcClient == null) {
4606            //Log.w(TAG, "Can't update remote control display with null remote control client");
4607            clearRemoteControlDisplay_syncAfRcs();
4608            return;
4609        }
4610        synchronized(mCurrentRcLock) {
4611            if (!rcse.mRcClient.equals(mCurrentRcClient)) {
4612                // new RC client, assume every type of information shall be queried
4613                infoFlagsAboutToBeUsed = RC_INFO_ALL;
4614            }
4615            mCurrentRcClient = rcse.mRcClient;
4616        }
4617        // will cause onRcDisplayUpdate() to be called in AudioService's handler thread
4618        mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE,
4619                infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) );
4620    }
4621
4622    /**
4623     * Helper function:
4624     * Called synchronized on mAudioFocusLock, then mRCStack
4625     * Check whether the remote control display should be updated, triggers the update if required
4626     * @param infoChangedFlags the flags corresponding to the remote control client information
4627     *     that has changed, if applicable (checking for the update conditions might trigger a
4628     *     clear, rather than an update event).
4629     */
4630    private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) {
4631        // determine whether the remote control display should be refreshed
4632        // if either stack is empty, there is a mismatch, so clear the RC display
4633        if (mRCStack.isEmpty() || mFocusStack.isEmpty()) {
4634            clearRemoteControlDisplay_syncAfRcs();
4635            return;
4636        }
4637        // if the top of the two stacks belong to different packages, there is a mismatch, clear
4638        if ((mRCStack.peek().mCallingPackageName != null)
4639                && (mFocusStack.peek().mPackageName != null)
4640                && !(mRCStack.peek().mCallingPackageName.compareTo(
4641                        mFocusStack.peek().mPackageName) == 0)) {
4642            clearRemoteControlDisplay_syncAfRcs();
4643            return;
4644        }
4645        // if the audio focus didn't originate from the same Uid as the one in which the remote
4646        //   control information will be retrieved, clear
4647        if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
4648            clearRemoteControlDisplay_syncAfRcs();
4649            return;
4650        }
4651        // refresh conditions were verified: update the remote controls
4652        // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty
4653        updateRemoteControlDisplay_syncAfRcs(infoChangedFlags);
4654    }
4655
4656    /**
4657     * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c)
4658     * precondition: mediaIntent != null, target != null
4659     */
4660    public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) {
4661        Log.i(TAG, "  Remote Control   registerMediaButtonIntent() for " + mediaIntent);
4662
4663        synchronized(mAudioFocusLock) {
4664            synchronized(mRCStack) {
4665                pushMediaButtonReceiver(mediaIntent, eventReceiver);
4666                // new RC client, assume every type of information shall be queried
4667                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
4668            }
4669        }
4670    }
4671
4672    /**
4673     * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent)
4674     * precondition: mediaIntent != null, eventReceiver != null
4675     */
4676    public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver)
4677    {
4678        Log.i(TAG, "  Remote Control   unregisterMediaButtonIntent() for " + mediaIntent);
4679
4680        synchronized(mAudioFocusLock) {
4681            synchronized(mRCStack) {
4682                boolean topOfStackWillChange = isCurrentRcController(mediaIntent);
4683                removeMediaButtonReceiver(mediaIntent);
4684                if (topOfStackWillChange) {
4685                    // current RC client will change, assume every type of info needs to be queried
4686                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
4687                }
4688            }
4689        }
4690    }
4691
4692    /**
4693     * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c)
4694     * precondition: c != null
4695     */
4696    public void registerMediaButtonEventReceiverForCalls(ComponentName c) {
4697        if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
4698                != PackageManager.PERMISSION_GRANTED) {
4699            Log.e(TAG, "Invalid permissions to register media button receiver for calls");
4700            return;
4701        }
4702        synchronized(mRCStack) {
4703            mMediaReceiverForCalls = c;
4704        }
4705    }
4706
4707    /**
4708     * see AudioManager.unregisterMediaButtonEventReceiverForCalls()
4709     */
4710    public void unregisterMediaButtonEventReceiverForCalls() {
4711        if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE")
4712                != PackageManager.PERMISSION_GRANTED) {
4713            Log.e(TAG, "Invalid permissions to unregister media button receiver for calls");
4714            return;
4715        }
4716        synchronized(mRCStack) {
4717            mMediaReceiverForCalls = null;
4718        }
4719    }
4720
4721    /**
4722     * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...)
4723     * @return the unique ID of the RemoteControlStackEntry associated with the RemoteControlClient
4724     * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient
4725     *     without modifying the RC stack, but while still causing the display to refresh (will
4726     *     become blank as a result of this)
4727     */
4728    public int registerRemoteControlClient(PendingIntent mediaIntent,
4729            IRemoteControlClient rcClient, String callingPackageName) {
4730        if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient);
4731        int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
4732        synchronized(mAudioFocusLock) {
4733            synchronized(mRCStack) {
4734                // store the new display information
4735                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4736                while(stackIterator.hasNext()) {
4737                    RemoteControlStackEntry rcse = stackIterator.next();
4738                    if(rcse.mMediaIntent.equals(mediaIntent)) {
4739                        // already had a remote control client?
4740                        if (rcse.mRcClientDeathHandler != null) {
4741                            // stop monitoring the old client's death
4742                            rcse.unlinkToRcClientDeath();
4743                        }
4744                        // save the new remote control client
4745                        rcse.mRcClient = rcClient;
4746                        rcse.mCallingPackageName = callingPackageName;
4747                        rcse.mCallingUid = Binder.getCallingUid();
4748                        if (rcClient == null) {
4749                            // here rcse.mRcClientDeathHandler is null;
4750                            rcse.resetPlaybackInfo();
4751                            break;
4752                        }
4753                        rccId = rcse.mRccId;
4754
4755                        // there is a new (non-null) client:
4756                        // 1/ give the new client the current display (if any)
4757                        if (mRcDisplay != null) {
4758                            try {
4759                                rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
4760                            } catch (RemoteException e) {
4761                                Log.e(TAG, "Error connecting remote control display to client: "+e);
4762                                e.printStackTrace();
4763                            }
4764                        }
4765                        // 2/ monitor the new client's death
4766                        IBinder b = rcse.mRcClient.asBinder();
4767                        RcClientDeathHandler rcdh =
4768                                new RcClientDeathHandler(b, rcse.mMediaIntent);
4769                        try {
4770                            b.linkToDeath(rcdh, 0);
4771                        } catch (RemoteException e) {
4772                            // remote control client is DOA, disqualify it
4773                            Log.w(TAG, "registerRemoteControlClient() has a dead client " + b);
4774                            rcse.mRcClient = null;
4775                        }
4776                        rcse.mRcClientDeathHandler = rcdh;
4777                        break;
4778                    }
4779                }
4780                // if the eventReceiver is at the top of the stack
4781                // then check for potential refresh of the remote controls
4782                if (isCurrentRcController(mediaIntent)) {
4783                    checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
4784                }
4785            }
4786        }
4787        return rccId;
4788    }
4789
4790    /**
4791     * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...)
4792     * rcClient is guaranteed non-null
4793     */
4794    public void unregisterRemoteControlClient(PendingIntent mediaIntent,
4795            IRemoteControlClient rcClient) {
4796        synchronized(mAudioFocusLock) {
4797            synchronized(mRCStack) {
4798                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4799                while(stackIterator.hasNext()) {
4800                    RemoteControlStackEntry rcse = stackIterator.next();
4801                    if ((rcse.mMediaIntent.equals(mediaIntent))
4802                            && rcClient.equals(rcse.mRcClient)) {
4803                        // we found the IRemoteControlClient to unregister
4804                        // stop monitoring its death
4805                        rcse.unlinkToRcClientDeath();
4806                        // reset the client-related fields
4807                        rcse.mRcClient = null;
4808                        rcse.mCallingPackageName = null;
4809                    }
4810                }
4811            }
4812        }
4813    }
4814
4815    /**
4816     * The remote control displays.
4817     * Access synchronized on mRCStack
4818     * NOTE: Only one IRemoteControlDisplay supported in this implementation
4819     */
4820    private IRemoteControlDisplay mRcDisplay;
4821    private RcDisplayDeathHandler mRcDisplayDeathHandler;
4822    private int mArtworkExpectedWidth = -1;
4823    private int mArtworkExpectedHeight = -1;
4824    /**
4825     * Inner class to monitor remote control display deaths, and unregister them from the list
4826     * of displays if necessary.
4827     */
4828    private class RcDisplayDeathHandler implements IBinder.DeathRecipient {
4829        private IBinder mCb; // To be notified of client's death
4830
4831        public RcDisplayDeathHandler(IBinder b) {
4832            if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b);
4833            mCb = b;
4834        }
4835
4836        public void binderDied() {
4837            synchronized(mRCStack) {
4838                Log.w(TAG, "RemoteControl: display died");
4839                mRcDisplay = null;
4840            }
4841        }
4842
4843        public void unlinkToRcDisplayDeath() {
4844            if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb);
4845            try {
4846                mCb.unlinkToDeath(this, 0);
4847            } catch (java.util.NoSuchElementException e) {
4848                // not much we can do here, the display was being unregistered anyway
4849                Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()");
4850                e.printStackTrace();
4851            }
4852        }
4853
4854    }
4855
4856    private void rcDisplay_stopDeathMonitor_syncRcStack() {
4857        if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null)
4858            // we had a display before, stop monitoring its death
4859            mRcDisplayDeathHandler.unlinkToRcDisplayDeath();
4860        }
4861    }
4862
4863    private void rcDisplay_startDeathMonitor_syncRcStack() {
4864        if (mRcDisplay != null) {
4865            // new non-null display, monitor its death
4866            IBinder b = mRcDisplay.asBinder();
4867            mRcDisplayDeathHandler = new RcDisplayDeathHandler(b);
4868            try {
4869                b.linkToDeath(mRcDisplayDeathHandler, 0);
4870            } catch (RemoteException e) {
4871                // remote control display is DOA, disqualify it
4872                Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b);
4873                mRcDisplay = null;
4874            }
4875        }
4876    }
4877
4878    /**
4879     * Register an IRemoteControlDisplay.
4880     * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient
4881     * at the top of the stack to update the new display with its information.
4882     * Since only one IRemoteControlDisplay is supported, this will unregister the previous display.
4883     * @param rcd the IRemoteControlDisplay to register. No effect if null.
4884     */
4885    public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) {
4886        if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")");
4887        synchronized(mAudioFocusLock) {
4888            synchronized(mRCStack) {
4889                if ((mRcDisplay == rcd) || (rcd == null)) {
4890                    return;
4891                }
4892                // if we had a display before, stop monitoring its death
4893                rcDisplay_stopDeathMonitor_syncRcStack();
4894                mRcDisplay = rcd;
4895                // new display, start monitoring its death
4896                rcDisplay_startDeathMonitor_syncRcStack();
4897
4898                // let all the remote control clients there is a new display
4899                // no need to unplug the previous because we only support one display
4900                // and the clients don't track the death of the display
4901                Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4902                while(stackIterator.hasNext()) {
4903                    RemoteControlStackEntry rcse = stackIterator.next();
4904                    if(rcse.mRcClient != null) {
4905                        try {
4906                            rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay);
4907                        } catch (RemoteException e) {
4908                            Log.e(TAG, "Error connecting remote control display to client: " + e);
4909                            e.printStackTrace();
4910                        }
4911                    }
4912                }
4913
4914                // we have a new display, of which all the clients are now aware: have it be updated
4915                checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL);
4916            }
4917        }
4918    }
4919
4920    /**
4921     * Unregister an IRemoteControlDisplay.
4922     * Since only one IRemoteControlDisplay is supported, this has no effect if the one to
4923     *    unregister is not the current one.
4924     * @param rcd the IRemoteControlDisplay to unregister. No effect if null.
4925     */
4926    public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) {
4927        if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")");
4928        synchronized(mRCStack) {
4929            // only one display here, so you can only unregister the current display
4930            if ((rcd == null) || (rcd != mRcDisplay)) {
4931                if (DEBUG_RC) Log.w(TAG, "    trying to unregister unregistered RCD");
4932                return;
4933            }
4934            // if we had a display before, stop monitoring its death
4935            rcDisplay_stopDeathMonitor_syncRcStack();
4936            mRcDisplay = null;
4937
4938            // disconnect this remote control display from all the clients
4939            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4940            while(stackIterator.hasNext()) {
4941                RemoteControlStackEntry rcse = stackIterator.next();
4942                if(rcse.mRcClient != null) {
4943                    try {
4944                        rcse.mRcClient.unplugRemoteControlDisplay(rcd);
4945                    } catch (RemoteException e) {
4946                        Log.e(TAG, "Error disconnecting remote control display to client: " + e);
4947                        e.printStackTrace();
4948                    }
4949                }
4950            }
4951        }
4952    }
4953
4954    public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) {
4955        synchronized(mRCStack) {
4956            // NOTE: Only one IRemoteControlDisplay supported in this implementation
4957            mArtworkExpectedWidth = w;
4958            mArtworkExpectedHeight = h;
4959        }
4960    }
4961
4962    // FIXME send a message instead of updating the stack synchronously
4963    public void setPlaybackInfoForRcc(int rccId, int what, int value) {
4964        if(DEBUG_RC) Log.d(TAG, "setPlaybackInfoForRcc(id="+rccId+", what="+what+",val="+value+")");
4965        synchronized(mRCStack) {
4966            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
4967            while(stackIterator.hasNext()) {
4968                RemoteControlStackEntry rcse = stackIterator.next();
4969                if (rcse.mRccId == rccId) {
4970                    switch (what) {
4971                        case RemoteControlClient.PLAYBACKINFO_PLAYBACK_TYPE:
4972                            rcse.mPlaybackType = value;
4973                            postReevaluateRemote();
4974                            break;
4975                        case RemoteControlClient.PLAYBACKINFO_VOLUME:
4976                            rcse.mPlaybackVolume = value;
4977                            synchronized (mMainRemote) {
4978                                if (rccId == mMainRemote.mRccId) {
4979                                    mMainRemote.mVolume = value;
4980                                    mVolumePanel.postHasNewRemotePlaybackInfo();
4981                                }
4982                            }
4983                            break;
4984                        case RemoteControlClient.PLAYBACKINFO_VOLUME_MAX:
4985                            rcse.mPlaybackVolumeMax = value;
4986                            synchronized (mMainRemote) {
4987                                if (rccId == mMainRemote.mRccId) {
4988                                    mMainRemote.mVolumeMax = value;
4989                                    mVolumePanel.postHasNewRemotePlaybackInfo();
4990                                }
4991                            }
4992                            break;
4993                        case RemoteControlClient.PLAYBACKINFO_VOLUME_HANDLING:
4994                            rcse.mPlaybackVolumeHandling = value;
4995                            synchronized (mMainRemote) {
4996                                if (rccId == mMainRemote.mRccId) {
4997                                    mMainRemote.mVolumeHandling = value;
4998                                    mVolumePanel.postHasNewRemotePlaybackInfo();
4999                                }
5000                            }
5001                            break;
5002                        case RemoteControlClient.PLAYBACKINFO_USES_STREAM:
5003                            rcse.mPlaybackStream = value;
5004                            break;
5005                        case RemoteControlClient.PLAYBACKINFO_PLAYSTATE:
5006                            rcse.mPlaybackState = value;
5007                            synchronized (mMainRemote) {
5008                                if (rccId == mMainRemote.mRccId) {
5009                                    mMainRemoteIsActive = isPlaystateActive(value);
5010                                    postReevaluateRemote();
5011                                }
5012                            }
5013                            break;
5014                        default:
5015                            Log.e(TAG, "unhandled key " + what + " for RCC " + rccId);
5016                            break;
5017                    }
5018                    return;
5019                }
5020            }
5021        }
5022    }
5023
5024    /**
5025     * Checks if a remote client is active on the supplied stream type. Update the remote stream
5026     * volume state if found and playing
5027     * @param streamType
5028     * @return false if no remote playing is currently playing
5029     */
5030    private boolean checkUpdateRemoteStateIfActive(int streamType) {
5031        synchronized(mRCStack) {
5032            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5033            while(stackIterator.hasNext()) {
5034                RemoteControlStackEntry rcse = stackIterator.next();
5035                if ((rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE)
5036                        && isPlaystateActive(rcse.mPlaybackState)
5037                        && (rcse.mPlaybackStream == streamType)) {
5038                    if (DEBUG_RC) Log.d(TAG, "remote playback active on stream " + streamType
5039                            + ", vol =" + rcse.mPlaybackVolume);
5040                    synchronized (mMainRemote) {
5041                        mMainRemote.mRccId = rcse.mRccId;
5042                        mMainRemote.mVolume = rcse.mPlaybackVolume;
5043                        mMainRemote.mVolumeMax = rcse.mPlaybackVolumeMax;
5044                        mMainRemote.mVolumeHandling = rcse.mPlaybackVolumeHandling;
5045                        mMainRemoteIsActive = true;
5046                    }
5047                    return true;
5048                }
5049            }
5050        }
5051        synchronized (mMainRemote) {
5052            mMainRemoteIsActive = false;
5053        }
5054        return false;
5055    }
5056
5057    /**
5058     * Returns true if the given playback state is considered "active", i.e. it describes a state
5059     * where playback is happening, or about to
5060     * @param playState the playback state to evaluate
5061     * @return true if active, false otherwise (inactive or unknown)
5062     */
5063    private static boolean isPlaystateActive(int playState) {
5064        switch (playState) {
5065            case RemoteControlClient.PLAYSTATE_PLAYING:
5066            case RemoteControlClient.PLAYSTATE_BUFFERING:
5067            case RemoteControlClient.PLAYSTATE_FAST_FORWARDING:
5068            case RemoteControlClient.PLAYSTATE_REWINDING:
5069            case RemoteControlClient.PLAYSTATE_SKIPPING_BACKWARDS:
5070            case RemoteControlClient.PLAYSTATE_SKIPPING_FORWARDS:
5071                return true;
5072            default:
5073                return false;
5074        }
5075    }
5076
5077    private void adjustRemoteVolume(int streamType, int direction, int flags) {
5078        int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
5079        boolean volFixed = false;
5080        synchronized (mMainRemote) {
5081            if (!mMainRemoteIsActive) {
5082                if (DEBUG_VOL) Log.w(TAG, "adjustRemoteVolume didn't find an active client");
5083                return;
5084            }
5085            rccId = mMainRemote.mRccId;
5086            volFixed = (mMainRemote.mVolumeHandling ==
5087                    RemoteControlClient.PLAYBACK_VOLUME_FIXED);
5088        }
5089        // unlike "local" stream volumes, we can't compute the new volume based on the direction,
5090        // we can only notify the remote that volume needs to be updated, and we'll get an async'
5091        // update through setPlaybackInfoForRcc()
5092        if (!volFixed) {
5093            sendVolumeUpdateToRemote(rccId, direction);
5094        }
5095
5096        // fire up the UI
5097        mVolumePanel.postRemoteVolumeChanged(streamType, flags);
5098    }
5099
5100    private void sendVolumeUpdateToRemote(int rccId, int direction) {
5101        if (DEBUG_VOL) { Log.d(TAG, "sendVolumeUpdateToRemote(rccId="+rccId+" , dir="+direction); }
5102        if (direction == 0) {
5103            // only handling discrete events
5104            return;
5105        }
5106        String packageForRcc = null;
5107        synchronized (mRCStack) {
5108            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5109            while(stackIterator.hasNext()) {
5110                RemoteControlStackEntry rcse = stackIterator.next();
5111                //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
5112                if (rcse.mRccId == rccId) {
5113                    packageForRcc = rcse.mReceiverComponent.getPackageName();
5114                    break;
5115                }
5116            }
5117        }
5118        if (packageForRcc != null) {
5119            Intent intent = new Intent(Intent.ACTION_VOLUME_UPDATE);
5120            intent.putExtra(Intent.EXTRA_VOLUME_UPDATE_DIRECTION, direction);
5121            intent.setPackage(packageForRcc);
5122            mContext.sendBroadcast(intent);
5123        }
5124    }
5125
5126    public int getRemoteStreamMaxVolume() {
5127        synchronized (mMainRemote) {
5128            if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
5129                return 0;
5130            }
5131            return mMainRemote.mVolumeMax;
5132        }
5133    }
5134
5135    public int getRemoteStreamVolume() {
5136        synchronized (mMainRemote) {
5137            if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
5138                return 0;
5139            }
5140            return mMainRemote.mVolume;
5141        }
5142    }
5143
5144    public void setRemoteStreamVolume(int vol) {
5145        if (DEBUG_VOL) { Log.d(TAG, "setRemoteStreamVolume(vol="+vol+")"); }
5146        int rccId = RemoteControlClient.RCSE_ID_UNREGISTERED;
5147        synchronized (mMainRemote) {
5148            if (mMainRemote.mRccId == RemoteControlClient.RCSE_ID_UNREGISTERED) {
5149                return;
5150            }
5151            rccId = mMainRemote.mRccId;
5152        }
5153        String packageForRcc = null;
5154        synchronized (mRCStack) {
5155            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5156            while(stackIterator.hasNext()) {
5157                RemoteControlStackEntry rcse = stackIterator.next();
5158                if (rcse.mRccId == rccId) {
5159                    //FIXME OPTIMIZE store this info in mMainRemote so we don't have to iterate?
5160                    packageForRcc = rcse.mReceiverComponent.getPackageName();
5161                    break;
5162                }
5163            }
5164        }
5165        if (packageForRcc != null) {
5166            Intent intent = new Intent(Intent.ACTION_VOLUME_UPDATE);
5167            intent.putExtra(Intent.EXTRA_VOLUME_UPDATE_VALUE, vol);
5168            intent.setPackage(packageForRcc);
5169            mContext.sendBroadcast(intent);
5170        }
5171    }
5172
5173    /**
5174     * Call to make AudioService reevaluate whether it's in a mode where remote players should
5175     * have their volume controlled. In this implementation this is only to reset whether
5176     * VolumePanel should display remote volumes
5177     */
5178    private void postReevaluateRemote() {
5179        sendMsg(mAudioHandler, MSG_REEVALUATE_REMOTE, SENDMSG_QUEUE, 0, 0, null, 0);
5180    }
5181
5182    private void onReevaluateRemote() {
5183        if (DEBUG_VOL) { Log.w(TAG, "onReevaluateRemote()"); }
5184        // is there a registered RemoteControlClient that is handling remote playback
5185        boolean hasRemotePlayback = false;
5186        synchronized (mRCStack) {
5187            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
5188            while(stackIterator.hasNext()) {
5189                RemoteControlStackEntry rcse = stackIterator.next();
5190                if (rcse.mPlaybackType == RemoteControlClient.PLAYBACK_TYPE_REMOTE) {
5191                    hasRemotePlayback = true;
5192                    break;
5193                }
5194            }
5195        }
5196        synchronized (mMainRemote) {
5197            if (mHasRemotePlayback != hasRemotePlayback) {
5198                mHasRemotePlayback = hasRemotePlayback;
5199                mVolumePanel.postRemoteSliderVisibility(hasRemotePlayback);
5200            }
5201        }
5202    }
5203
5204    //==========================================================================================
5205    // Device orientation
5206    //==========================================================================================
5207    /**
5208     * Handles device configuration changes that may map to a change in the orientation.
5209     * This feature is optional, and is defined by the definition and value of the
5210     * "ro.audio.monitorOrientation" system property.
5211     */
5212    private void handleConfigurationChanged(Context context) {
5213        try {
5214            // reading new orientation "safely" (i.e. under try catch) in case anything
5215            // goes wrong when obtaining resources and configuration
5216            int newOrientation = context.getResources().getConfiguration().orientation;
5217            if (newOrientation != mDeviceOrientation) {
5218                mDeviceOrientation = newOrientation;
5219                setOrientationForAudioSystem();
5220            }
5221        } catch (Exception e) {
5222            Log.e(TAG, "Error retrieving device orientation: " + e);
5223        }
5224    }
5225
5226    private void setOrientationForAudioSystem() {
5227        switch (mDeviceOrientation) {
5228            case Configuration.ORIENTATION_LANDSCAPE:
5229                //Log.i(TAG, "orientation is landscape");
5230                AudioSystem.setParameters("orientation=landscape");
5231                break;
5232            case Configuration.ORIENTATION_PORTRAIT:
5233                //Log.i(TAG, "orientation is portrait");
5234                AudioSystem.setParameters("orientation=portrait");
5235                break;
5236            case Configuration.ORIENTATION_SQUARE:
5237                //Log.i(TAG, "orientation is square");
5238                AudioSystem.setParameters("orientation=square");
5239                break;
5240            case Configuration.ORIENTATION_UNDEFINED:
5241                //Log.i(TAG, "orientation is undefined");
5242                AudioSystem.setParameters("orientation=undefined");
5243                break;
5244            default:
5245                Log.e(TAG, "Unknown orientation");
5246        }
5247    }
5248
5249
5250    // Handles request to override default use of A2DP for media.
5251    public void setBluetoothA2dpOnInt(boolean on) {
5252        synchronized (mBluetoothA2dpEnabledLock) {
5253            mBluetoothA2dpEnabled = on;
5254            sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
5255                    AudioSystem.FOR_MEDIA,
5256                    mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
5257                    null, 0);
5258        }
5259    }
5260
5261    @Override
5262    public void setRingtonePlayer(IRingtonePlayer player) {
5263        mContext.enforceCallingOrSelfPermission(REMOTE_AUDIO_PLAYBACK, null);
5264        mRingtonePlayer = player;
5265    }
5266
5267    @Override
5268    public IRingtonePlayer getRingtonePlayer() {
5269        return mRingtonePlayer;
5270    }
5271
5272    @Override
5273    public AudioRoutesInfo startWatchingRoutes(IAudioRoutesObserver observer) {
5274        synchronized (mCurAudioRoutes) {
5275            AudioRoutesInfo routes = new AudioRoutesInfo(mCurAudioRoutes);
5276            mRoutesObservers.register(observer);
5277            return routes;
5278        }
5279    }
5280
5281    @Override
5282    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
5283        mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
5284
5285        dumpFocusStack(pw);
5286        dumpRCStack(pw);
5287        dumpRCCStack(pw);
5288        dumpStreamStates(pw);
5289        pw.println("\nAudio routes:");
5290        pw.print("  mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
5291        pw.print("  mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
5292    }
5293}
5294