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