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