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