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