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