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