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