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