OSDN Git Service

Fix bug 2619062 Music is routed to Phone speaker, though it is
[android-x86/frameworks-base.git] / media / java / android / media / AudioService.java
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
17 package android.media;
18
19 import android.app.ActivityManagerNative;
20 import android.content.BroadcastReceiver;
21 import android.content.ComponentName;
22 import android.content.ContentResolver;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.IntentFilter;
26 import android.bluetooth.BluetoothA2dp;
27 import android.bluetooth.BluetoothClass;
28 import android.bluetooth.BluetoothDevice;
29 import android.bluetooth.BluetoothHeadset;
30 import android.content.pm.PackageManager;
31 import android.database.ContentObserver;
32 import android.media.MediaPlayer.OnCompletionListener;
33 import android.media.MediaPlayer.OnErrorListener;
34 import android.os.Binder;
35 import android.os.Environment;
36 import android.os.Handler;
37 import android.os.IBinder;
38 import android.os.Looper;
39 import android.os.Message;
40 import android.os.RemoteException;
41 import android.os.ServiceManager;
42 import android.provider.Settings;
43 import android.provider.Settings.System;
44 import android.telephony.PhoneStateListener;
45 import android.telephony.TelephonyManager;
46 import android.util.Log;
47 import android.view.KeyEvent;
48 import android.view.VolumePanel;
49 import android.os.SystemProperties;
50
51 import com.android.internal.telephony.ITelephony;
52
53 import java.io.FileDescriptor;
54 import java.io.IOException;
55 import java.io.PrintWriter;
56 import java.util.ArrayList;
57 import java.util.HashMap;
58 import java.util.Iterator;
59 import java.util.Map;
60 import java.util.Set;
61 import java.util.Stack;
62
63 /**
64  * The implementation of the volume manager service.
65  * <p>
66  * This implementation focuses on delivering a responsive UI. Most methods are
67  * asynchronous to external calls. For example, the task of setting a volume
68  * will update our internal state, but in a separate thread will set the system
69  * volume and later persist to the database. Similarly, setting the ringer mode
70  * will update the state and broadcast a change and in a separate thread later
71  * persist the ringer mode.
72  *
73  * @hide
74  */
75 public class AudioService extends IAudioService.Stub {
76
77     private static final String TAG = "AudioService";
78
79     /** How long to delay before persisting a change in volume/ringer mode. */
80     private static final int PERSIST_DELAY = 3000;
81
82     private Context mContext;
83     private ContentResolver mContentResolver;
84
85
86     /** The UI */
87     private VolumePanel mVolumePanel;
88
89     // sendMsg() flags
90     /** Used when a message should be shared across all stream types. */
91     private static final int SHARED_MSG = -1;
92     /** If the msg is already queued, replace it with this one. */
93     private static final int SENDMSG_REPLACE = 0;
94     /** If the msg is already queued, ignore this one and leave the old. */
95     private static final int SENDMSG_NOOP = 1;
96     /** If the msg is already queued, queue this one and leave the old. */
97     private static final int SENDMSG_QUEUE = 2;
98
99     // AudioHandler message.whats
100     private static final int MSG_SET_SYSTEM_VOLUME = 0;
101     private static final int MSG_PERSIST_VOLUME = 1;
102     private static final int MSG_PERSIST_RINGER_MODE = 3;
103     private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
104     private static final int MSG_MEDIA_SERVER_DIED = 5;
105     private static final int MSG_MEDIA_SERVER_STARTED = 6;
106     private static final int MSG_PLAY_SOUND_EFFECT = 7;
107     private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8;
108
109     private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000;
110
111     /** @see AudioSystemThread */
112     private AudioSystemThread mAudioSystemThread;
113     /** @see AudioHandler */
114     private AudioHandler mAudioHandler;
115     /** @see VolumeStreamState */
116     private VolumeStreamState[] mStreamStates;
117     private SettingsObserver mSettingsObserver;
118
119     private int mMode;
120     private Object mSettingsLock = new Object();
121     private boolean mMediaServerOk;
122
123     private SoundPool mSoundPool;
124     private Object mSoundEffectsLock = new Object();
125     private static final int NUM_SOUNDPOOL_CHANNELS = 4;
126     private static final int SOUND_EFFECT_VOLUME = 1000;
127
128     /* Sound effect file names  */
129     private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
130     private static final String[] SOUND_EFFECT_FILES = new String[] {
131         "Effect_Tick.ogg",
132         "KeypressStandard.ogg",
133         "KeypressSpacebar.ogg",
134         "KeypressDelete.ogg",
135         "KeypressReturn.ogg"
136     };
137
138     /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
139      * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
140      * uses soundpool (second column) */
141     private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
142         {0, -1},  // FX_KEY_CLICK
143         {0, -1},  // FX_FOCUS_NAVIGATION_UP
144         {0, -1},  // FX_FOCUS_NAVIGATION_DOWN
145         {0, -1},  // FX_FOCUS_NAVIGATION_LEFT
146         {0, -1},  // FX_FOCUS_NAVIGATION_RIGHT
147         {1, -1},  // FX_KEYPRESS_STANDARD
148         {2, -1},  // FX_KEYPRESS_SPACEBAR
149         {3, -1},  // FX_FOCUS_DELETE
150         {4, -1}   // FX_FOCUS_RETURN
151     };
152
153    /** @hide Maximum volume index values for audio streams */
154     private int[] MAX_STREAM_VOLUME = new int[] {
155         5,  // STREAM_VOICE_CALL
156         7,  // STREAM_SYSTEM
157         7,  // STREAM_RING
158         15, // STREAM_MUSIC
159         7,  // STREAM_ALARM
160         7,  // STREAM_NOTIFICATION
161         15, // STREAM_BLUETOOTH_SCO
162         7,  // STREAM_SYSTEM_ENFORCED
163         15, // STREAM_DTMF
164         15  // STREAM_TTS
165     };
166     /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
167      * of another stream: This avoids multiplying the volume settings for hidden
168      * stream types that follow other stream behavior for volume settings
169      * NOTE: do not create loops in aliases! */
170     private int[] STREAM_VOLUME_ALIAS = new int[] {
171         AudioSystem.STREAM_VOICE_CALL,  // STREAM_VOICE_CALL
172         AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM
173         AudioSystem.STREAM_RING,  // STREAM_RING
174         AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
175         AudioSystem.STREAM_ALARM,  // STREAM_ALARM
176         AudioSystem.STREAM_NOTIFICATION,  // STREAM_NOTIFICATION
177         AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO
178         AudioSystem.STREAM_SYSTEM,  // STREAM_SYSTEM_ENFORCED
179         AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
180         AudioSystem.STREAM_MUSIC  // STREAM_TTS
181     };
182
183     private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
184         public void onError(int error) {
185             switch (error) {
186             case AudioSystem.AUDIO_STATUS_SERVER_DIED:
187                 if (mMediaServerOk) {
188                     sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
189                             null, 1500);
190                     mMediaServerOk = false;
191                 }
192                 break;
193             case AudioSystem.AUDIO_STATUS_OK:
194                 if (!mMediaServerOk) {
195                     sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
196                             null, 0);
197                     mMediaServerOk = true;
198                 }
199                 break;
200             default:
201                 break;
202             }
203        }
204     };
205
206     /**
207      * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
208      * {@link AudioManager#RINGER_MODE_SILENT}, or
209      * {@link AudioManager#RINGER_MODE_VIBRATE}.
210      */
211     private int mRingerMode;
212
213     /** @see System#MODE_RINGER_STREAMS_AFFECTED */
214     private int mRingerModeAffectedStreams;
215
216     // Streams currently muted by ringer mode
217     private int mRingerModeMutedStreams;
218
219     /** @see System#MUTE_STREAMS_AFFECTED */
220     private int mMuteAffectedStreams;
221
222     /**
223      * Has multiple bits per vibrate type to indicate the type's vibrate
224      * setting. See {@link #setVibrateSetting(int, int)}.
225      * <p>
226      * NOTE: This is not the final decision of whether vibrate is on/off for the
227      * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
228      */
229     private int mVibrateSetting;
230
231     /** @see System#NOTIFICATIONS_USE_RING_VOLUME */
232     private int mNotificationsUseRingVolume;
233
234     // Broadcast receiver for device connections intent broadcasts
235     private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
236
237     //  Broadcast receiver for media button broadcasts (separate from mReceiver to
238     //  independently change its priority)
239     private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver();
240
241     // Devices currently connected
242     private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
243
244     // Forced device usage for communications
245     private int mForcedUseForComm;
246
247     // List of binder death handlers for setMode() client processes.
248     // The last process to have called setMode() is at the top of the list.
249     private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>();
250
251     // List of clients having issued a SCO start request
252     private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>();
253
254     // BluetoothHeadset API to control SCO connection
255     private BluetoothHeadset mBluetoothHeadset;
256
257     // Bluetooth headset connection state
258     private boolean mBluetoothHeadsetConnected;
259
260     ///////////////////////////////////////////////////////////////////////////
261     // Construction
262     ///////////////////////////////////////////////////////////////////////////
263
264     /** @hide */
265     public AudioService(Context context) {
266         mContext = context;
267         mContentResolver = context.getContentResolver();
268
269        // Intialized volume
270         MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
271             "ro.config.vc_call_vol_steps",
272            MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
273
274         mVolumePanel = new VolumePanel(context, this);
275         mSettingsObserver = new SettingsObserver();
276         mForcedUseForComm = AudioSystem.FORCE_NONE;
277         createAudioSystemThread();
278         readPersistedSettings();
279         createStreamStates();
280         // Call setMode() to initialize mSetModeDeathHandlers
281         mMode = AudioSystem.MODE_INVALID;
282         setMode(AudioSystem.MODE_NORMAL, null);
283         mMediaServerOk = true;
284
285         // Call setRingerModeInt() to apply correct mute
286         // state on streams affected by ringer mode.
287         mRingerModeMutedStreams = 0;
288         setRingerModeInt(getRingerMode(), false);
289
290         AudioSystem.setErrorCallback(mAudioSystemCallback);
291         loadSoundEffects();
292
293         mBluetoothHeadsetConnected = false;
294         mBluetoothHeadset = new BluetoothHeadset(context,
295                                                  mBluetoothHeadsetServiceListener);
296
297         // Register for device connection intent broadcasts.
298         IntentFilter intentFilter =
299                 new IntentFilter(Intent.ACTION_HEADSET_PLUG);
300         intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED);
301         intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED);
302         intentFilter.addAction(Intent.ACTION_DOCK_EVENT);
303         intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED);
304         context.registerReceiver(mReceiver, intentFilter);
305
306         // Register for media button intent broadcasts.
307         intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON);
308         intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
309         context.registerReceiver(mMediaButtonReceiver, intentFilter);
310
311         // Register for phone state monitoring
312         TelephonyManager tmgr = (TelephonyManager)
313                 context.getSystemService(Context.TELEPHONY_SERVICE);
314         tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
315     }
316
317     private void createAudioSystemThread() {
318         mAudioSystemThread = new AudioSystemThread();
319         mAudioSystemThread.start();
320         waitForAudioHandlerCreation();
321     }
322
323     /** Waits for the volume handler to be created by the other thread. */
324     private void waitForAudioHandlerCreation() {
325         synchronized(this) {
326             while (mAudioHandler == null) {
327                 try {
328                     // Wait for mAudioHandler to be set by the other thread
329                     wait();
330                 } catch (InterruptedException e) {
331                     Log.e(TAG, "Interrupted while waiting on volume handler.");
332                 }
333             }
334         }
335     }
336
337     private void createStreamStates() {
338         int numStreamTypes = AudioSystem.getNumStreamTypes();
339         VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
340
341         for (int i = 0; i < numStreamTypes; i++) {
342             streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
343         }
344
345         // Correct stream index values for streams with aliases
346         for (int i = 0; i < numStreamTypes; i++) {
347             if (STREAM_VOLUME_ALIAS[i] != i) {
348                 int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
349                 streams[i].mIndex = streams[i].getValidIndex(index);
350                 setStreamVolumeIndex(i, index);
351                 index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
352                 streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
353             }
354         }
355     }
356
357     private void readPersistedSettings() {
358         final ContentResolver cr = mContentResolver;
359
360         mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
361
362         mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
363
364         mRingerModeAffectedStreams = Settings.System.getInt(cr,
365                 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
366                 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
367                  (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
368
369         mMuteAffectedStreams = System.getInt(cr,
370                 System.MUTE_STREAMS_AFFECTED,
371                 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
372
373         mNotificationsUseRingVolume = System.getInt(cr,
374                 Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1);
375
376         if (mNotificationsUseRingVolume == 1) {
377             STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
378         }
379         // Each stream will read its own persisted settings
380
381         // Broadcast the sticky intent
382         broadcastRingerMode();
383
384         // Broadcast vibrate settings
385         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
386         broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
387     }
388
389     private void setStreamVolumeIndex(int stream, int index) {
390         AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
391     }
392
393     private int rescaleIndex(int index, int srcStream, int dstStream) {
394         return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
395     }
396
397     ///////////////////////////////////////////////////////////////////////////
398     // IPC methods
399     ///////////////////////////////////////////////////////////////////////////
400
401     /** @see AudioManager#adjustVolume(int, int) */
402     public void adjustVolume(int direction, int flags) {
403         adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
404     }
405
406     /** @see AudioManager#adjustVolume(int, int, int) */
407     public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
408
409         int streamType = getActiveStreamType(suggestedStreamType);
410
411         // Don't play sound on other streams
412         if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
413             flags &= ~AudioManager.FLAG_PLAY_SOUND;
414         }
415
416         adjustStreamVolume(streamType, direction, flags);
417     }
418
419     /** @see AudioManager#adjustStreamVolume(int, int, int) */
420     public void adjustStreamVolume(int streamType, int direction, int flags) {
421         ensureValidDirection(direction);
422         ensureValidStreamType(streamType);
423
424
425         VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
426         final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
427         boolean adjustVolume = true;
428
429         // If either the client forces allowing ringer modes for this adjustment,
430         // or the stream type is one that is affected by ringer modes
431         if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0
432                 || streamType == AudioSystem.STREAM_RING) {
433             // Check if the ringer mode changes with this volume adjustment. If
434             // it does, it will handle adjusting the volume, so we won't below
435             adjustVolume = checkForRingerModeChange(oldIndex, direction);
436         }
437
438         // If stream is muted, adjust last audible index only
439         int index;
440         if (streamState.muteCount() != 0) {
441             if (adjustVolume) {
442                 streamState.adjustLastAudibleIndex(direction);
443                 // Post a persist volume msg
444                 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
445                         SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
446             }
447             index = streamState.mLastAudibleIndex;
448         } else {
449             if (adjustVolume && streamState.adjustIndex(direction)) {
450                 // Post message to set system volume (it in turn will post a message
451                 // to persist). Do not change volume if stream is muted.
452                 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,
453                         streamState, 0);
454             }
455             index = streamState.mIndex;
456         }
457         // UI
458         mVolumePanel.postVolumeChanged(streamType, flags);
459         // Broadcast Intent
460         sendVolumeUpdate(streamType, oldIndex, index);
461     }
462
463     /** @see AudioManager#setStreamVolume(int, int, int) */
464     public void setStreamVolume(int streamType, int index, int flags) {
465         ensureValidStreamType(streamType);
466         VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
467
468         final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
469
470         index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
471         setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
472
473         index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex;
474
475         // UI, etc.
476         mVolumePanel.postVolumeChanged(streamType, flags);
477         // Broadcast Intent
478         sendVolumeUpdate(streamType, oldIndex, index);
479     }
480
481     private void sendVolumeUpdate(int streamType, int oldIndex, int index) {
482         oldIndex = (oldIndex + 5) / 10;
483         index = (index + 5) / 10;
484
485         Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
486         intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
487         intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
488         intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
489
490         mContext.sendBroadcast(intent);
491     }
492
493     /**
494      * Sets the stream state's index, and posts a message to set system volume.
495      * This will not call out to the UI. Assumes a valid stream type.
496      *
497      * @param streamType Type of the stream
498      * @param index Desired volume index of the stream
499      * @param force If true, set the volume even if the desired volume is same
500      * as the current volume.
501      * @param lastAudible If true, stores new index as last audible one
502      */
503     private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
504         VolumeStreamState streamState = mStreamStates[streamType];
505
506         // If stream is muted, set last audible index only
507         if (streamState.muteCount() != 0) {
508             // Do not allow last audible index to be 0
509             if (index != 0) {
510                 streamState.setLastAudibleIndex(index);
511                 // Post a persist volume msg
512                 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType,
513                         SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY);
514             }
515         } else {
516             if (streamState.setIndex(index, lastAudible) || force) {
517                 // Post message to set system volume (it in turn will post a message
518                 // to persist).
519                 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
520                         streamState, 0);
521             }
522         }
523     }
524
525     /** @see AudioManager#setStreamSolo(int, boolean) */
526     public void setStreamSolo(int streamType, boolean state, IBinder cb) {
527         for (int stream = 0; stream < mStreamStates.length; stream++) {
528             if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
529             // Bring back last audible volume
530             mStreamStates[stream].mute(cb, state);
531          }
532     }
533
534     /** @see AudioManager#setStreamMute(int, boolean) */
535     public void setStreamMute(int streamType, boolean state, IBinder cb) {
536         if (isStreamAffectedByMute(streamType)) {
537             mStreamStates[streamType].mute(cb, state);
538         }
539     }
540
541     /** @see AudioManager#getStreamVolume(int) */
542     public int getStreamVolume(int streamType) {
543         ensureValidStreamType(streamType);
544         return (mStreamStates[streamType].mIndex + 5) / 10;
545     }
546
547     /** @see AudioManager#getStreamMaxVolume(int) */
548     public int getStreamMaxVolume(int streamType) {
549         ensureValidStreamType(streamType);
550         return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
551     }
552
553     /** @see AudioManager#getRingerMode() */
554     public int getRingerMode() {
555         return mRingerMode;
556     }
557
558     /** @see AudioManager#setRingerMode(int) */
559     public void setRingerMode(int ringerMode) {
560         synchronized (mSettingsLock) {
561             if (ringerMode != mRingerMode) {
562                 setRingerModeInt(ringerMode, true);
563                 // Send sticky broadcast
564                 broadcastRingerMode();
565             }
566         }
567     }
568
569     private void setRingerModeInt(int ringerMode, boolean persist) {
570         mRingerMode = ringerMode;
571
572         // Mute stream if not previously muted by ringer mode and ringer mode
573         // is not RINGER_MODE_NORMAL and stream is affected by ringer mode.
574         // Unmute stream if previously muted by ringer mode and ringer mode
575         // is RINGER_MODE_NORMAL or stream is not affected by ringer mode.
576         int numStreamTypes = AudioSystem.getNumStreamTypes();
577         for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
578             if (isStreamMutedByRingerMode(streamType)) {
579                 if (!isStreamAffectedByRingerMode(streamType) ||
580                     mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
581                     mStreamStates[streamType].mute(null, false);
582                     mRingerModeMutedStreams &= ~(1 << streamType);
583                 }
584             } else {
585                 if (isStreamAffectedByRingerMode(streamType) &&
586                     mRingerMode != AudioManager.RINGER_MODE_NORMAL) {
587                    mStreamStates[streamType].mute(null, true);
588                    mRingerModeMutedStreams |= (1 << streamType);
589                }
590             }
591         }
592
593         // Post a persist ringer mode msg
594         if (persist) {
595             sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
596                     SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
597         }
598     }
599
600     /** @see AudioManager#shouldVibrate(int) */
601     public boolean shouldVibrate(int vibrateType) {
602
603         switch (getVibrateSetting(vibrateType)) {
604
605             case AudioManager.VIBRATE_SETTING_ON:
606                 return mRingerMode != AudioManager.RINGER_MODE_SILENT;
607
608             case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
609                 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
610
611             case AudioManager.VIBRATE_SETTING_OFF:
612                 // return false, even for incoming calls
613                 return false;
614
615             default:
616                 return false;
617         }
618     }
619
620     /** @see AudioManager#getVibrateSetting(int) */
621     public int getVibrateSetting(int vibrateType) {
622         return (mVibrateSetting >> (vibrateType * 2)) & 3;
623     }
624
625     /** @see AudioManager#setVibrateSetting(int, int) */
626     public void setVibrateSetting(int vibrateType, int vibrateSetting) {
627
628         mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
629
630         // Broadcast change
631         broadcastVibrateSetting(vibrateType);
632
633         // Post message to set ringer mode (it in turn will post a message
634         // to persist)
635         sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
636                 null, 0);
637     }
638
639     /**
640      * @see #setVibrateSetting(int, int)
641      */
642     public static int getValueForVibrateSetting(int existingValue, int vibrateType,
643             int vibrateSetting) {
644
645         // First clear the existing setting. Each vibrate type has two bits in
646         // the value. Note '3' is '11' in binary.
647         existingValue &= ~(3 << (vibrateType * 2));
648
649         // Set into the old value
650         existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
651
652         return existingValue;
653     }
654
655     private class SetModeDeathHandler implements IBinder.DeathRecipient {
656         private IBinder mCb; // To be notified of client's death
657         private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client
658
659         SetModeDeathHandler(IBinder cb) {
660             mCb = cb;
661         }
662
663         public void binderDied() {
664             synchronized(mSetModeDeathHandlers) {
665                 Log.w(TAG, "setMode() client died");
666                 int index = mSetModeDeathHandlers.indexOf(this);
667                 if (index < 0) {
668                     Log.w(TAG, "unregistered setMode() client died");
669                 } else {
670                     mSetModeDeathHandlers.remove(this);
671                     // If dead client was a the top of client list,
672                     // apply next mode in the stack
673                     if (index == 0) {
674                         // mSetModeDeathHandlers is never empty as the initial entry
675                         // created when AudioService starts is never removed
676                         SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0);
677                         int mode = hdlr.getMode();
678                         if (AudioService.this.mMode != mode) {
679                             if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
680                                 AudioService.this.mMode = mode;
681                             }
682                         }
683                     }
684                 }
685             }
686         }
687
688         public void setMode(int mode) {
689             mMode = mode;
690         }
691
692         public int getMode() {
693             return mMode;
694         }
695
696         public IBinder getBinder() {
697             return mCb;
698         }
699     }
700
701     /** @see AudioManager#setMode(int) */
702     public void setMode(int mode, IBinder cb) {
703         if (!checkAudioSettingsPermission("setMode()")) {
704             return;
705         }
706
707         if (mode < AudioSystem.MODE_CURRENT || mode > AudioSystem.MODE_IN_CALL) {
708             return;
709         }
710
711         synchronized (mSettingsLock) {
712             if (mode == AudioSystem.MODE_CURRENT) {
713                 mode = mMode;
714             }
715             if (mode != mMode) {
716                 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
717                     mMode = mode;
718
719                     synchronized(mSetModeDeathHandlers) {
720                         SetModeDeathHandler hdlr = null;
721                         Iterator iter = mSetModeDeathHandlers.iterator();
722                         while (iter.hasNext()) {
723                             SetModeDeathHandler h = (SetModeDeathHandler)iter.next();
724                             if (h.getBinder() == cb) {
725                                 hdlr = h;
726                                 // Remove from client list so that it is re-inserted at top of list
727                                 iter.remove();
728                                 break;
729                             }
730                         }
731                         if (hdlr == null) {
732                             hdlr = new SetModeDeathHandler(cb);
733                             // cb is null when setMode() is called by AudioService constructor
734                             if (cb != null) {
735                                 // Register for client death notification
736                                 try {
737                                     cb.linkToDeath(hdlr, 0);
738                                 } catch (RemoteException e) {
739                                     // Client has died!
740                                     Log.w(TAG, "setMode() could not link to "+cb+" binder death");
741                                 }
742                             }
743                         }
744                         // Last client to call setMode() is always at top of client list
745                         // as required by SetModeDeathHandler.binderDied()
746                         mSetModeDeathHandlers.add(0, hdlr);
747                         hdlr.setMode(mode);
748                     }
749
750                     if (mode != AudioSystem.MODE_NORMAL) {
751                         clearAllScoClients();
752                     }
753                 }
754             }
755             int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
756             int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
757             setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false);
758         }
759     }
760
761     /** @see AudioManager#getMode() */
762     public int getMode() {
763         return mMode;
764     }
765
766     /** @see AudioManager#playSoundEffect(int) */
767     public void playSoundEffect(int effectType) {
768         sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
769                 effectType, -1, null, 0);
770     }
771
772     /** @see AudioManager#playSoundEffect(int, float) */
773     public void playSoundEffectVolume(int effectType, float volume) {
774         loadSoundEffects();
775         sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
776                 effectType, (int) (volume * 1000), null, 0);
777     }
778
779     /**
780      * Loads samples into the soundpool.
781      * This method must be called at when sound effects are enabled
782      */
783     public boolean loadSoundEffects() {
784         synchronized (mSoundEffectsLock) {
785             if (mSoundPool != null) {
786                 return true;
787             }
788             mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
789             if (mSoundPool == null) {
790                 return false;
791             }
792             /*
793              * poolId table: The value -1 in this table indicates that corresponding
794              * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
795              * Once loaded, the value in poolId is the sample ID and the same
796              * sample can be reused for another effect using the same file.
797              */
798             int[] poolId = new int[SOUND_EFFECT_FILES.length];
799             for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
800                 poolId[fileIdx] = -1;
801             }
802             /*
803              * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
804              * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
805              * this indicates we have a valid sample loaded for this effect.
806              */
807             for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
808                 // Do not load sample if this effect uses the MediaPlayer
809                 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
810                     continue;
811                 }
812                 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
813                     String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
814                     int sampleId = mSoundPool.load(filePath, 0);
815                     SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
816                     poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
817                     if (sampleId <= 0) {
818                         Log.w(TAG, "Soundpool could not load file: "+filePath);
819                     }
820                 } else {
821                     SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
822                 }
823             }
824         }
825
826         return true;
827     }
828
829     /**
830      *  Unloads samples from the sound pool.
831      *  This method can be called to free some memory when
832      *  sound effects are disabled.
833      */
834     public void unloadSoundEffects() {
835         synchronized (mSoundEffectsLock) {
836             if (mSoundPool == null) {
837                 return;
838             }
839             int[] poolId = new int[SOUND_EFFECT_FILES.length];
840             for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
841                 poolId[fileIdx] = 0;
842             }
843
844             for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
845                 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
846                     continue;
847                 }
848                 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
849                     mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
850                     SOUND_EFFECT_FILES_MAP[effect][1] = -1;
851                     poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
852                 }
853             }
854             mSoundPool = null;
855         }
856     }
857
858     /** @see AudioManager#reloadAudioSettings() */
859     public void reloadAudioSettings() {
860         // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
861         readPersistedSettings();
862
863         // restore volume settings
864         int numStreamTypes = AudioSystem.getNumStreamTypes();
865         for (int streamType = 0; streamType < numStreamTypes; streamType++) {
866             VolumeStreamState streamState = mStreamStates[streamType];
867
868             String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
869             String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
870             int index = Settings.System.getInt(mContentResolver,
871                                            settingName,
872                                            AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
873             if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
874                 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
875             } else {
876                 index *= 10;
877             }
878             streamState.mIndex = streamState.getValidIndex(index);
879
880             index = (index + 5) / 10;
881             index = Settings.System.getInt(mContentResolver,
882                                             lastAudibleSettingName,
883                                             (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
884             if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
885                 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
886             } else {
887                 index *= 10;
888             }
889             streamState.mLastAudibleIndex = streamState.getValidIndex(index);
890
891             // unmute stream that was muted but is not affect by mute anymore
892             if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
893                 int size = streamState.mDeathHandlers.size();
894                 for (int i = 0; i < size; i++) {
895                     streamState.mDeathHandlers.get(i).mMuteCount = 1;
896                     streamState.mDeathHandlers.get(i).mute(false);
897                 }
898             }
899             // apply stream volume
900             if (streamState.muteCount() == 0) {
901                 setStreamVolumeIndex(streamType, streamState.mIndex);
902             }
903         }
904
905         // apply new ringer mode
906         setRingerModeInt(getRingerMode(), false);
907     }
908
909     /** @see AudioManager#setSpeakerphoneOn() */
910     public void setSpeakerphoneOn(boolean on){
911         if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) {
912             return;
913         }
914         if (on) {
915             AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER);
916             mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
917         } else {
918             AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
919             mForcedUseForComm = AudioSystem.FORCE_NONE;
920         }
921     }
922
923     /** @see AudioManager#isSpeakerphoneOn() */
924     public boolean isSpeakerphoneOn() {
925         if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
926             return true;
927         } else {
928             return false;
929         }
930     }
931
932     /** @see AudioManager#setBluetoothScoOn() */
933     public void setBluetoothScoOn(boolean on){
934         if (!checkAudioSettingsPermission("setBluetoothScoOn()")) {
935             return;
936         }
937         if (on) {
938             AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO);
939             AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO);
940             mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
941         } else {
942             AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
943             AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE);
944             mForcedUseForComm = AudioSystem.FORCE_NONE;
945         }
946     }
947
948     /** @see AudioManager#isBluetoothScoOn() */
949     public boolean isBluetoothScoOn() {
950         if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
951             return true;
952         } else {
953             return false;
954         }
955     }
956
957     /** @see AudioManager#startBluetoothSco() */
958     public void startBluetoothSco(IBinder cb){
959         if (!checkAudioSettingsPermission("startBluetoothSco()")) {
960             return;
961         }
962         ScoClient client = getScoClient(cb);
963         client.incCount();
964     }
965
966     /** @see AudioManager#stopBluetoothSco() */
967     public void stopBluetoothSco(IBinder cb){
968         if (!checkAudioSettingsPermission("stopBluetoothSco()")) {
969             return;
970         }
971         ScoClient client = getScoClient(cb);
972         client.decCount();
973     }
974
975     private class ScoClient implements IBinder.DeathRecipient {
976         private IBinder mCb; // To be notified of client's death
977         private int mStartcount; // number of SCO connections started by this client
978
979         ScoClient(IBinder cb) {
980             mCb = cb;
981             mStartcount = 0;
982         }
983
984         public void binderDied() {
985             synchronized(mScoClients) {
986                 Log.w(TAG, "SCO client died");
987                 int index = mScoClients.indexOf(this);
988                 if (index < 0) {
989                     Log.w(TAG, "unregistered SCO client died");
990                 } else {
991                     clearCount(true);
992                     mScoClients.remove(this);
993                 }
994             }
995         }
996
997         public void incCount() {
998             synchronized(mScoClients) {
999                 requestScoState(BluetoothHeadset.AUDIO_STATE_CONNECTED);
1000                 if (mStartcount == 0) {
1001                     try {
1002                         mCb.linkToDeath(this, 0);
1003                     } catch (RemoteException e) {
1004                         // client has already died!
1005                         Log.w(TAG, "ScoClient  incCount() could not link to "+mCb+" binder death");
1006                     }
1007                 }
1008                 mStartcount++;
1009             }
1010         }
1011
1012         public void decCount() {
1013             synchronized(mScoClients) {
1014                 if (mStartcount == 0) {
1015                     Log.w(TAG, "ScoClient.decCount() already 0");
1016                 } else {
1017                     mStartcount--;
1018                     if (mStartcount == 0) {
1019                         mCb.unlinkToDeath(this, 0);
1020                     }
1021                     requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
1022                 }
1023             }
1024         }
1025
1026         public void clearCount(boolean stopSco) {
1027             synchronized(mScoClients) {
1028                 mStartcount = 0;
1029                 mCb.unlinkToDeath(this, 0);
1030                 if (stopSco) {
1031                     requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED);
1032                 }
1033             }
1034         }
1035
1036         public int getCount() {
1037             return mStartcount;
1038         }
1039
1040         public IBinder getBinder() {
1041             return mCb;
1042         }
1043
1044         public int totalCount() {
1045             synchronized(mScoClients) {
1046                 int count = 0;
1047                 int size = mScoClients.size();
1048                 for (int i = 0; i < size; i++) {
1049                     count += mScoClients.get(i).getCount();
1050                 }
1051                 return count;
1052             }
1053         }
1054
1055         private void requestScoState(int state) {
1056             if (totalCount() == 0 &&
1057                 mBluetoothHeadsetConnected &&
1058                 AudioService.this.mMode == AudioSystem.MODE_NORMAL) {
1059                 if (state == BluetoothHeadset.AUDIO_STATE_CONNECTED) {
1060                     mBluetoothHeadset.startVoiceRecognition();
1061                 } else {
1062                     mBluetoothHeadset.stopVoiceRecognition();
1063                 }
1064             }
1065         }
1066     }
1067
1068     public ScoClient getScoClient(IBinder cb) {
1069         synchronized(mScoClients) {
1070             ScoClient client;
1071             int size = mScoClients.size();
1072             for (int i = 0; i < size; i++) {
1073                 client = mScoClients.get(i);
1074                 if (client.getBinder() == cb)
1075                     return client;
1076             }
1077             client = new ScoClient(cb);
1078             mScoClients.add(client);
1079             return client;
1080         }
1081     }
1082
1083     public void clearAllScoClients() {
1084         synchronized(mScoClients) {
1085             int size = mScoClients.size();
1086             for (int i = 0; i < size; i++) {
1087                 mScoClients.get(i).clearCount(false);
1088             }
1089         }
1090     }
1091
1092     private BluetoothHeadset.ServiceListener mBluetoothHeadsetServiceListener =
1093         new BluetoothHeadset.ServiceListener() {
1094         public void onServiceConnected() {
1095             if (mBluetoothHeadset != null &&
1096                 mBluetoothHeadset.getState() == BluetoothHeadset.STATE_CONNECTED) {
1097                 mBluetoothHeadsetConnected = true;
1098             }
1099         }
1100         public void onServiceDisconnected() {
1101             if (mBluetoothHeadset != null &&
1102                 mBluetoothHeadset.getState() == BluetoothHeadset.STATE_DISCONNECTED) {
1103                 mBluetoothHeadsetConnected = false;
1104                 clearAllScoClients();
1105             }
1106         }
1107     };
1108
1109     ///////////////////////////////////////////////////////////////////////////
1110     // Internal methods
1111     ///////////////////////////////////////////////////////////////////////////
1112
1113     /**
1114      * Checks if the adjustment should change ringer mode instead of just
1115      * adjusting volume. If so, this will set the proper ringer mode and volume
1116      * indices on the stream states.
1117      */
1118     private boolean checkForRingerModeChange(int oldIndex, int direction) {
1119         boolean adjustVolumeIndex = true;
1120         int newRingerMode = mRingerMode;
1121
1122         if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
1123             // audible mode, at the bottom of the scale
1124             if (direction == AudioManager.ADJUST_LOWER
1125                     && (oldIndex + 5) / 10 == 1) {
1126                 // "silent mode", but which one?
1127                 newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1
1128                     ? AudioManager.RINGER_MODE_VIBRATE
1129                     : AudioManager.RINGER_MODE_SILENT;
1130             }
1131         } else {
1132             if (direction == AudioManager.ADJUST_RAISE) {
1133                 // exiting silent mode
1134                 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
1135             } else {
1136                 // prevent last audible index to reach 0
1137                 adjustVolumeIndex = false;
1138             }
1139         }
1140
1141         if (newRingerMode != mRingerMode) {
1142             setRingerMode(newRingerMode);
1143
1144             /*
1145              * If we are changing ringer modes, do not increment/decrement the
1146              * volume index. Instead, the handler for the message above will
1147              * take care of changing the index.
1148              */
1149             adjustVolumeIndex = false;
1150         }
1151
1152         return adjustVolumeIndex;
1153     }
1154
1155     public boolean isStreamAffectedByRingerMode(int streamType) {
1156         return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
1157     }
1158
1159     private boolean isStreamMutedByRingerMode(int streamType) {
1160         return (mRingerModeMutedStreams & (1 << streamType)) != 0;
1161     }
1162
1163     public boolean isStreamAffectedByMute(int streamType) {
1164         return (mMuteAffectedStreams & (1 << streamType)) != 0;
1165     }
1166
1167     private void ensureValidDirection(int direction) {
1168         if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
1169             throw new IllegalArgumentException("Bad direction " + direction);
1170         }
1171     }
1172
1173     private void ensureValidStreamType(int streamType) {
1174         if (streamType < 0 || streamType >= mStreamStates.length) {
1175             throw new IllegalArgumentException("Bad stream type " + streamType);
1176         }
1177     }
1178
1179     private int getActiveStreamType(int suggestedStreamType) {
1180         boolean isOffhook = false;
1181         try {
1182             ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1183             if (phone != null) isOffhook = phone.isOffhook();
1184         } catch (RemoteException e) {
1185             Log.w(TAG, "Couldn't connect to phone service", e);
1186         }
1187
1188         if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) {
1189             // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1190             return AudioSystem.STREAM_BLUETOOTH_SCO;
1191         } else if (isOffhook || AudioSystem.isStreamActive(AudioSystem.STREAM_VOICE_CALL)) {
1192             // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1193             return AudioSystem.STREAM_VOICE_CALL;
1194         } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC)) {
1195             // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
1196             return AudioSystem.STREAM_MUSIC;
1197         } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
1198             // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING...");
1199             return AudioSystem.STREAM_RING;
1200         } else {
1201             // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1202             return suggestedStreamType;
1203         }
1204     }
1205
1206     private void broadcastRingerMode() {
1207         // Send sticky broadcast
1208         Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
1209         broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
1210         broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
1211                 | Intent.FLAG_RECEIVER_REPLACE_PENDING);
1212         long origCallerIdentityToken = Binder.clearCallingIdentity();
1213         mContext.sendStickyBroadcast(broadcast);
1214         Binder.restoreCallingIdentity(origCallerIdentityToken);
1215     }
1216
1217     private void broadcastVibrateSetting(int vibrateType) {
1218         // Send broadcast
1219         if (ActivityManagerNative.isSystemReady()) {
1220             Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
1221             broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
1222             broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
1223             mContext.sendBroadcast(broadcast);
1224         }
1225     }
1226
1227     // Message helper methods
1228     private static int getMsg(int baseMsg, int streamType) {
1229         return (baseMsg & 0xffff) | streamType << 16;
1230     }
1231
1232     private static int getMsgBase(int msg) {
1233         return msg & 0xffff;
1234     }
1235
1236     private static void sendMsg(Handler handler, int baseMsg, int streamType,
1237             int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
1238         int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
1239
1240         if (existingMsgPolicy == SENDMSG_REPLACE) {
1241             handler.removeMessages(msg);
1242         } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
1243             return;
1244         }
1245
1246         handler
1247                 .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
1248     }
1249
1250     boolean checkAudioSettingsPermission(String method) {
1251         if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
1252                 == PackageManager.PERMISSION_GRANTED) {
1253             return true;
1254         }
1255         String msg = "Audio Settings Permission Denial: " + method + " from pid="
1256                 + Binder.getCallingPid()
1257                 + ", uid=" + Binder.getCallingUid();
1258         Log.w(TAG, msg);
1259         return false;
1260     }
1261
1262
1263     ///////////////////////////////////////////////////////////////////////////
1264     // Inner classes
1265     ///////////////////////////////////////////////////////////////////////////
1266
1267     public class VolumeStreamState {
1268         private final int mStreamType;
1269
1270         private String mVolumeIndexSettingName;
1271         private String mLastAudibleVolumeIndexSettingName;
1272         private int mIndexMax;
1273         private int mIndex;
1274         private int mLastAudibleIndex;
1275         private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
1276
1277         private VolumeStreamState(String settingName, int streamType) {
1278
1279             setVolumeIndexSettingName(settingName);
1280
1281             mStreamType = streamType;
1282
1283             final ContentResolver cr = mContentResolver;
1284             mIndexMax = MAX_STREAM_VOLUME[streamType];
1285             mIndex = Settings.System.getInt(cr,
1286                                             mVolumeIndexSettingName,
1287                                             AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1288             mLastAudibleIndex = Settings.System.getInt(cr,
1289                                                        mLastAudibleVolumeIndexSettingName,
1290                                                        (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
1291             AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
1292             mIndexMax *= 10;
1293             mIndex = getValidIndex(10 * mIndex);
1294             mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
1295             setStreamVolumeIndex(streamType, mIndex);
1296             mDeathHandlers = new ArrayList<VolumeDeathHandler>();
1297         }
1298
1299         public void setVolumeIndexSettingName(String settingName) {
1300             mVolumeIndexSettingName = settingName;
1301             mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1302         }
1303
1304         public boolean adjustIndex(int deltaIndex) {
1305             return setIndex(mIndex + deltaIndex * 10, true);
1306         }
1307
1308         public boolean setIndex(int index, boolean lastAudible) {
1309             int oldIndex = mIndex;
1310             mIndex = getValidIndex(index);
1311
1312             if (oldIndex != mIndex) {
1313                 if (lastAudible) {
1314                     mLastAudibleIndex = mIndex;
1315                 }
1316                 // Apply change to all streams using this one as alias
1317                 int numStreamTypes = AudioSystem.getNumStreamTypes();
1318                 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1319                     if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
1320                         mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
1321                     }
1322                 }
1323                 return true;
1324             } else {
1325                 return false;
1326             }
1327         }
1328
1329         public void setLastAudibleIndex(int index) {
1330             mLastAudibleIndex = getValidIndex(index);
1331         }
1332
1333         public void adjustLastAudibleIndex(int deltaIndex) {
1334             setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10);
1335         }
1336
1337         public int getMaxIndex() {
1338             return mIndexMax;
1339         }
1340
1341         public void mute(IBinder cb, boolean state) {
1342             VolumeDeathHandler handler = getDeathHandler(cb, state);
1343             if (handler == null) {
1344                 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
1345                 return;
1346             }
1347             handler.mute(state);
1348         }
1349
1350         private int getValidIndex(int index) {
1351             if (index < 0) {
1352                 return 0;
1353             } else if (index > mIndexMax) {
1354                 return mIndexMax;
1355             }
1356
1357             return index;
1358         }
1359
1360         private class VolumeDeathHandler implements IBinder.DeathRecipient {
1361             private IBinder mICallback; // To be notified of client's death
1362             private int mMuteCount; // Number of active mutes for this client
1363
1364             VolumeDeathHandler(IBinder cb) {
1365                 mICallback = cb;
1366             }
1367
1368             public void mute(boolean state) {
1369                 synchronized(mDeathHandlers) {
1370                     if (state) {
1371                         if (mMuteCount == 0) {
1372                             // Register for client death notification
1373                             try {
1374                                 // mICallback can be 0 if muted by AudioService
1375                                 if (mICallback != null) {
1376                                     mICallback.linkToDeath(this, 0);
1377                                 }
1378                                 mDeathHandlers.add(this);
1379                                 // If the stream is not yet muted by any client, set lvel to 0
1380                                 if (muteCount() == 0) {
1381                                     setIndex(0, false);
1382                                     sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1383                                             VolumeStreamState.this, 0);
1384                                 }
1385                             } catch (RemoteException e) {
1386                                 // Client has died!
1387                                 binderDied();
1388                                 mDeathHandlers.notify();
1389                                 return;
1390                             }
1391                         } else {
1392                             Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
1393                         }
1394                         mMuteCount++;
1395                     } else {
1396                         if (mMuteCount == 0) {
1397                             Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
1398                         } else {
1399                             mMuteCount--;
1400                             if (mMuteCount == 0) {
1401                                 // Unregistr from client death notification
1402                                 mDeathHandlers.remove(this);
1403                                 // mICallback can be 0 if muted by AudioService
1404                                 if (mICallback != null) {
1405                                     mICallback.unlinkToDeath(this, 0);
1406                                 }
1407                                 if (muteCount() == 0) {
1408                                     // If the stream is not muted any more, restore it's volume if
1409                                     // ringer mode allows it
1410                                     if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
1411                                         setIndex(mLastAudibleIndex, false);
1412                                         sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1413                                                 VolumeStreamState.this, 0);
1414                                     }
1415                                 }
1416                             }
1417                         }
1418                     }
1419                     mDeathHandlers.notify();
1420                 }
1421             }
1422
1423             public void binderDied() {
1424                 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
1425                 if (mMuteCount != 0) {
1426                     // Reset all active mute requests from this client.
1427                     mMuteCount = 1;
1428                     mute(false);
1429                 }
1430             }
1431         }
1432
1433         private int muteCount() {
1434             int count = 0;
1435             int size = mDeathHandlers.size();
1436             for (int i = 0; i < size; i++) {
1437                 count += mDeathHandlers.get(i).mMuteCount;
1438             }
1439             return count;
1440         }
1441
1442         private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
1443             synchronized(mDeathHandlers) {
1444                 VolumeDeathHandler handler;
1445                 int size = mDeathHandlers.size();
1446                 for (int i = 0; i < size; i++) {
1447                     handler = mDeathHandlers.get(i);
1448                     if (cb == handler.mICallback) {
1449                         return handler;
1450                     }
1451                 }
1452                 // If this is the first mute request for this client, create a new
1453                 // client death handler. Otherwise, it is an out of sequence unmute request.
1454                 if (state) {
1455                     handler = new VolumeDeathHandler(cb);
1456                 } else {
1457                     Log.w(TAG, "stream was not muted by this client");
1458                     handler = null;
1459                 }
1460                 return handler;
1461             }
1462         }
1463     }
1464
1465     /** Thread that handles native AudioSystem control. */
1466     private class AudioSystemThread extends Thread {
1467         AudioSystemThread() {
1468             super("AudioService");
1469         }
1470
1471         @Override
1472         public void run() {
1473             // Set this thread up so the handler will work on it
1474             Looper.prepare();
1475
1476             synchronized(AudioService.this) {
1477                 mAudioHandler = new AudioHandler();
1478
1479                 // Notify that the handler has been created
1480                 AudioService.this.notify();
1481             }
1482
1483             // Listen for volume change requests that are set by VolumePanel
1484             Looper.loop();
1485         }
1486     }
1487
1488     /** Handles internal volume messages in separate volume thread. */
1489     private class AudioHandler extends Handler {
1490
1491         private void setSystemVolume(VolumeStreamState streamState) {
1492
1493             // Adjust volume
1494             setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
1495
1496             // Apply change to all streams using this one as alias
1497             int numStreamTypes = AudioSystem.getNumStreamTypes();
1498             for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1499                 if (streamType != streamState.mStreamType &&
1500                     STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
1501                     setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
1502                 }
1503             }
1504
1505             // Post a persist volume msg
1506             sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
1507                     SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY);
1508         }
1509
1510         private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) {
1511             if (current) {
1512                 System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
1513                               (streamState.mIndex + 5)/ 10);
1514             }
1515             if (lastAudible) {
1516                 System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
1517                     (streamState.mLastAudibleIndex + 5) / 10);
1518             }
1519         }
1520
1521         private void persistRingerMode() {
1522             System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
1523         }
1524
1525         private void persistVibrateSetting() {
1526             System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
1527         }
1528
1529         private void playSoundEffect(int effectType, int volume) {
1530             synchronized (mSoundEffectsLock) {
1531                 if (mSoundPool == null) {
1532                     return;
1533                 }
1534                 float volFloat;
1535                 // use STREAM_MUSIC volume attenuated by 3 dB if volume is not specified by caller
1536                 if (volume < 0) {
1537                     // Same linear to log conversion as in native AudioSystem::linearToLog() (AudioSystem.cpp)
1538                     float dBPerStep = (float)((0.5 * 100) / MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
1539                     int musicVolIndex = (mStreamStates[AudioSystem.STREAM_MUSIC].mIndex + 5) / 10;
1540                     float musicVoldB = dBPerStep * (musicVolIndex - MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]);
1541                     volFloat = (float)Math.pow(10, (musicVoldB - 3)/20);
1542                 } else {
1543                     volFloat = (float) volume / 1000.0f;
1544                 }
1545
1546                 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
1547                     mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f);
1548                 } else {
1549                     MediaPlayer mediaPlayer = new MediaPlayer();
1550                     if (mediaPlayer != null) {
1551                         try {
1552                             String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
1553                             mediaPlayer.setDataSource(filePath);
1554                             mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
1555                             mediaPlayer.prepare();
1556                             mediaPlayer.setVolume(volFloat, volFloat);
1557                             mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
1558                                 public void onCompletion(MediaPlayer mp) {
1559                                     cleanupPlayer(mp);
1560                                 }
1561                             });
1562                             mediaPlayer.setOnErrorListener(new OnErrorListener() {
1563                                 public boolean onError(MediaPlayer mp, int what, int extra) {
1564                                     cleanupPlayer(mp);
1565                                     return true;
1566                                 }
1567                             });
1568                             mediaPlayer.start();
1569                         } catch (IOException ex) {
1570                             Log.w(TAG, "MediaPlayer IOException: "+ex);
1571                         } catch (IllegalArgumentException ex) {
1572                             Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
1573                         } catch (IllegalStateException ex) {
1574                             Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1575                         }
1576                     }
1577                 }
1578             }
1579         }
1580
1581         private void cleanupPlayer(MediaPlayer mp) {
1582             if (mp != null) {
1583                 try {
1584                     mp.stop();
1585                     mp.release();
1586                 } catch (IllegalStateException ex) {
1587                     Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1588                 }
1589             }
1590         }
1591
1592         @Override
1593         public void handleMessage(Message msg) {
1594             int baseMsgWhat = getMsgBase(msg.what);
1595
1596             switch (baseMsgWhat) {
1597
1598                 case MSG_SET_SYSTEM_VOLUME:
1599                     setSystemVolume((VolumeStreamState) msg.obj);
1600                     break;
1601
1602                 case MSG_PERSIST_VOLUME:
1603                     persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0));
1604                     break;
1605
1606                 case MSG_PERSIST_RINGER_MODE:
1607                     persistRingerMode();
1608                     break;
1609
1610                 case MSG_PERSIST_VIBRATE_SETTING:
1611                     persistVibrateSetting();
1612                     break;
1613
1614                 case MSG_MEDIA_SERVER_DIED:
1615                     // Force creation of new IAudioflinger interface
1616                     if (!mMediaServerOk) {
1617                         Log.e(TAG, "Media server died.");
1618                         AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC);
1619                         sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
1620                                 null, 500);
1621                     }
1622                     break;
1623
1624                 case MSG_MEDIA_SERVER_STARTED:
1625                     Log.e(TAG, "Media server started.");
1626                     // Restore device connection states
1627                     Set set = mConnectedDevices.entrySet();
1628                     Iterator i = set.iterator();
1629                     while(i.hasNext()){
1630                         Map.Entry device = (Map.Entry)i.next();
1631                         AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
1632                                                              AudioSystem.DEVICE_STATE_AVAILABLE,
1633                                                              (String)device.getValue());
1634                     }
1635
1636                     // Restore call state
1637                     AudioSystem.setPhoneState(mMode);
1638
1639                     // Restore forced usage for communcations and record
1640                     AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
1641                     AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
1642
1643                     // Restore stream volumes
1644                     int numStreamTypes = AudioSystem.getNumStreamTypes();
1645                     for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1646                         int index;
1647                         VolumeStreamState streamState = mStreamStates[streamType];
1648                         AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
1649                         if (streamState.muteCount() == 0) {
1650                             index = streamState.mIndex;
1651                         } else {
1652                             index = 0;
1653                         }
1654                         setStreamVolumeIndex(streamType, index);
1655                     }
1656
1657                     // Restore ringer mode
1658                     setRingerModeInt(getRingerMode(), false);
1659                     break;
1660
1661                 case MSG_PLAY_SOUND_EFFECT:
1662                     playSoundEffect(msg.arg1, msg.arg2);
1663                     break;
1664
1665                 case MSG_BTA2DP_DOCK_TIMEOUT:
1666                     // msg.obj  == address of BTA2DP device
1667                     makeA2dpDeviceUnavailableNow( (String) msg.obj );
1668                     break;
1669             }
1670         }
1671     }
1672
1673     private class SettingsObserver extends ContentObserver {
1674
1675         SettingsObserver() {
1676             super(new Handler());
1677             mContentResolver.registerContentObserver(Settings.System.getUriFor(
1678                 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
1679             mContentResolver.registerContentObserver(Settings.System.getUriFor(
1680                     Settings.System.NOTIFICATIONS_USE_RING_VOLUME), false, this);
1681         }
1682
1683         @Override
1684         public void onChange(boolean selfChange) {
1685             super.onChange(selfChange);
1686             synchronized (mSettingsLock) {
1687                 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
1688                         Settings.System.MODE_RINGER_STREAMS_AFFECTED,
1689                         0);
1690                 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
1691                     /*
1692                      * Ensure all stream types that should be affected by ringer mode
1693                      * are in the proper state.
1694                      */
1695                     mRingerModeAffectedStreams = ringerModeAffectedStreams;
1696                     setRingerModeInt(getRingerMode(), false);
1697                 }
1698
1699                 int notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
1700                         Settings.System.NOTIFICATIONS_USE_RING_VOLUME,
1701                         1);
1702                 if (notificationsUseRingVolume != mNotificationsUseRingVolume) {
1703                     mNotificationsUseRingVolume = notificationsUseRingVolume;
1704                     if (mNotificationsUseRingVolume == 1) {
1705                         STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
1706                         mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
1707                                 System.VOLUME_SETTINGS[AudioSystem.STREAM_RING]);
1708                     } else {
1709                         STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
1710                         mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName(
1711                                 System.VOLUME_SETTINGS[AudioSystem.STREAM_NOTIFICATION]);
1712                         // Persist notification volume volume as it was not persisted while aliased to ring volume
1713                         //  and persist with no delay as there might be registered observers of the persisted
1714                         //  notification volume.
1715                         sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
1716                                 SENDMSG_REPLACE, 1, 1, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0);
1717                     }
1718                 }
1719             }
1720         }
1721     }
1722
1723     private void makeA2dpDeviceAvailable(String address) {
1724         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1725                 AudioSystem.DEVICE_STATE_AVAILABLE,
1726                 address);
1727         // Reset A2DP suspend state each time a new sink is connected
1728         AudioSystem.setParameters("A2dpSuspended=false");
1729         mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP),
1730                 address);
1731     }
1732
1733     private void makeA2dpDeviceUnavailableNow(String address) {
1734         Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY);
1735         mContext.sendBroadcast(noisyIntent);
1736         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1737                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1738                 address);
1739         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
1740     }
1741
1742     private void makeA2dpDeviceUnavailableLater(String address) {
1743         // prevent any activity on the A2DP audio output to avoid unwanted
1744         // reconnection of the sink.
1745         AudioSystem.setParameters("A2dpSuspended=true");
1746         // the device will be made unavailable later, so consider it disconnected right away
1747         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
1748         // send the delayed message to make the device unavailable later
1749         Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address);
1750         mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS);
1751
1752     }
1753
1754     private void cancelA2dpDeviceTimeout() {
1755         mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT);
1756     }
1757
1758     private boolean hasScheduledA2dpDockTimeout() {
1759         return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT);
1760     }
1761
1762     /* cache of the address of the last dock the device was connected to */
1763     private String mDockAddress;
1764
1765     /**
1766      * Receiver for misc intent broadcasts the Phone app cares about.
1767      */
1768     private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
1769         @Override
1770         public void onReceive(Context context, Intent intent) {
1771             String action = intent.getAction();
1772
1773             if (action.equals(Intent.ACTION_DOCK_EVENT)) {
1774                 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE,
1775                         Intent.EXTRA_DOCK_STATE_UNDOCKED);
1776                 int config;
1777                 switch (dockState) {
1778                     case Intent.EXTRA_DOCK_STATE_DESK:
1779                         config = AudioSystem.FORCE_BT_DESK_DOCK;
1780                         break;
1781                     case Intent.EXTRA_DOCK_STATE_CAR:
1782                         config = AudioSystem.FORCE_BT_CAR_DOCK;
1783                         break;
1784                     case Intent.EXTRA_DOCK_STATE_UNDOCKED:
1785                     default:
1786                         config = AudioSystem.FORCE_NONE;
1787                 }
1788                 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
1789             } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) {
1790                 int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE,
1791                                                BluetoothA2dp.STATE_DISCONNECTED);
1792                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1793                 String address = btDevice.getAddress();
1794                 boolean isConnected = (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
1795                                        ((String)mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)).equals(address));
1796
1797                 if (isConnected &&
1798                     state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) {
1799                     if (btDevice.isBluetoothDock()) {
1800                         if (state == BluetoothA2dp.STATE_DISCONNECTED) {
1801                             // introduction of a delay for transient disconnections of docks when
1802                             // power is rapidly turned off/on, this message will be canceled if
1803                             // we reconnect the dock under a preset delay
1804                             makeA2dpDeviceUnavailableLater(address);
1805                             // the next time isConnected is evaluated, it will be false for the dock
1806                         }
1807                     } else {
1808                         makeA2dpDeviceUnavailableNow(address);
1809                     }
1810                 } else if (!isConnected &&
1811                              (state == BluetoothA2dp.STATE_CONNECTED ||
1812                               state == BluetoothA2dp.STATE_PLAYING)) {
1813                     if (btDevice.isBluetoothDock()) {
1814                         // this could be a reconnection after a transient disconnection
1815                         cancelA2dpDeviceTimeout();
1816                         mDockAddress = address;
1817                     } else {
1818                         // this could be a connection of another A2DP device before the timeout of
1819                         // a dock: cancel the dock timeout, and make the dock unavailable now
1820                         if(hasScheduledA2dpDockTimeout()) {
1821                             cancelA2dpDeviceTimeout();
1822                             makeA2dpDeviceUnavailableNow(mDockAddress);
1823                         }
1824                     }
1825                     makeA2dpDeviceAvailable(address);
1826                 }
1827             } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) {
1828                 int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE,
1829                                                BluetoothHeadset.STATE_ERROR);
1830                 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
1831                 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE);
1832                 String address = null;
1833                 if (btDevice != null) {
1834                     address = btDevice.getAddress();
1835                     BluetoothClass btClass = btDevice.getBluetoothClass();
1836                     if (btClass != null) {
1837                         switch (btClass.getDeviceClass()) {
1838                         case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
1839                         case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
1840                             device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
1841                             break;
1842                         case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
1843                             device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
1844                             break;
1845                         }
1846                     }
1847                 }
1848
1849                 boolean isConnected = (mConnectedDevices.containsKey(device) &&
1850                                        ((String)mConnectedDevices.get(device)).equals(address));
1851
1852                 if (isConnected && state != BluetoothHeadset.STATE_CONNECTED) {
1853                     AudioSystem.setDeviceConnectionState(device,
1854                                                          AudioSystem.DEVICE_STATE_UNAVAILABLE,
1855                                                          address);
1856                     mConnectedDevices.remove(device);
1857                     mBluetoothHeadsetConnected = false;
1858                     clearAllScoClients();
1859                 } else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) {
1860                     AudioSystem.setDeviceConnectionState(device,
1861                                                          AudioSystem.DEVICE_STATE_AVAILABLE,
1862                                                          address);
1863                     mConnectedDevices.put(new Integer(device), address);
1864                     mBluetoothHeadsetConnected = true;
1865                 }
1866             } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
1867                 int state = intent.getIntExtra("state", 0);
1868                 int microphone = intent.getIntExtra("microphone", 0);
1869
1870                 if (microphone != 0) {
1871                     boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
1872                     if (state == 0 && isConnected) {
1873                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1874                                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1875                                 "");
1876                         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
1877                     } else if (state == 1 && !isConnected)  {
1878                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1879                                 AudioSystem.DEVICE_STATE_AVAILABLE,
1880                                 "");
1881                         mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
1882                     }
1883                 } else {
1884                     boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
1885                     if (state == 0 && isConnected) {
1886                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1887                                 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1888                                 "");
1889                         mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
1890                     } else if (state == 1 && !isConnected)  {
1891                         AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1892                                 AudioSystem.DEVICE_STATE_AVAILABLE,
1893                                 "");
1894                         mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
1895                     }
1896                 }
1897             } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
1898                 int state = intent.getIntExtra(BluetoothHeadset.EXTRA_AUDIO_STATE,
1899                                                BluetoothHeadset.STATE_ERROR);
1900                 synchronized (mScoClients) {
1901                     if (!mScoClients.isEmpty()) {
1902                         switch (state) {
1903                         case BluetoothHeadset.AUDIO_STATE_CONNECTED:
1904                             state = AudioManager.SCO_AUDIO_STATE_CONNECTED;
1905                             break;
1906                         case BluetoothHeadset.AUDIO_STATE_DISCONNECTED:
1907                             state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED;
1908                             break;
1909                         default:
1910                             state = AudioManager.SCO_AUDIO_STATE_ERROR;
1911                             break;
1912                         }
1913                         if (state != AudioManager.SCO_AUDIO_STATE_ERROR) {
1914                             Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
1915                             newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
1916                             mContext.sendStickyBroadcast(newIntent);
1917                         }
1918                     }
1919                 }
1920             }
1921         }
1922     }
1923
1924     //==========================================================================================
1925     // AudioFocus
1926     //==========================================================================================
1927
1928     /* constant to identify focus stack entry that is used to hold the focus while the phone
1929      * is ringing or during a call
1930      */
1931     private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls";
1932
1933     private PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
1934         @Override
1935         public void onCallStateChanged(int state, String incomingNumber) {
1936             if (state == TelephonyManager.CALL_STATE_RINGING) {
1937                 //Log.v(TAG, " CALL_STATE_RINGING");
1938                 int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING);
1939                 if (ringVolume > 0) {
1940                     requestAudioFocus(AudioManager.STREAM_RING,
1941                                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
1942                                 null, null /* both allowed to be null only for this clientId */,
1943                                 IN_VOICE_COMM_FOCUS_ID /*clientId*/);
1944                 }
1945             } else if (state == TelephonyManager.CALL_STATE_OFFHOOK) {
1946                 //Log.v(TAG, " CALL_STATE_OFFHOOK");
1947                 requestAudioFocus(AudioManager.STREAM_RING,
1948                         AudioManager.AUDIOFOCUS_GAIN_TRANSIENT,
1949                         null, null /* both allowed to be null only for this clientId */,
1950                         IN_VOICE_COMM_FOCUS_ID /*clientId*/);
1951             } else if (state == TelephonyManager.CALL_STATE_IDLE) {
1952                 //Log.v(TAG, " CALL_STATE_IDLE");
1953                 abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID);
1954             }
1955         }
1956     };
1957
1958     private void notifyTopOfAudioFocusStack() {
1959         // notify the top of the stack it gained focus
1960         if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
1961             if (canReassignAudioFocus()) {
1962                 try {
1963                     mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
1964                             AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId);
1965                 } catch (RemoteException e) {
1966                     Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e);
1967                     e.printStackTrace();
1968                 }
1969             }
1970         }
1971     }
1972
1973     private static class FocusStackEntry {
1974         public int mStreamType = -1;// no stream type
1975         public boolean mIsTransportControlReceiver = false;
1976         public IAudioFocusDispatcher mFocusDispatcher = null;
1977         public IBinder mSourceRef = null;
1978         public String mClientId;
1979         public int mFocusChangeType;
1980
1981         public FocusStackEntry() {
1982         }
1983
1984         public FocusStackEntry(int streamType, int duration, boolean isTransportControlReceiver,
1985                 IAudioFocusDispatcher afl, IBinder source, String id) {
1986             mStreamType = streamType;
1987             mIsTransportControlReceiver = isTransportControlReceiver;
1988             mFocusDispatcher = afl;
1989             mSourceRef = source;
1990             mClientId = id;
1991             mFocusChangeType = duration;
1992         }
1993     }
1994
1995     private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>();
1996
1997     /**
1998      * Helper function:
1999      * Display in the log the current entries in the audio focus stack
2000      */
2001     private void dumpFocusStack(PrintWriter pw) {
2002         pw.println("\nAudio Focus stack entries:");
2003         synchronized(mFocusStack) {
2004             Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2005             while(stackIterator.hasNext()) {
2006                 FocusStackEntry fse = stackIterator.next();
2007                 pw.println("     source:" + fse.mSourceRef + " -- client: " + fse.mClientId
2008                         + " -- duration: " +fse.mFocusChangeType);
2009             }
2010         }
2011     }
2012
2013     /**
2014      * Helper function:
2015      * Remove a focus listener from the focus stack.
2016      * @param focusListenerToRemove the focus listener
2017      * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding
2018      *   focus, notify the next item in the stack it gained focus.
2019      */
2020     private void removeFocusStackEntry(String clientToRemove, boolean signal) {
2021         // is the current top of the focus stack abandoning focus? (because of death or request)
2022         if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove))
2023         {
2024             //Log.i(TAG, "   removeFocusStackEntry() removing top of stack");
2025             mFocusStack.pop();
2026             if (signal) {
2027                 // notify the new top of the stack it gained focus
2028                 notifyTopOfAudioFocusStack();
2029             }
2030         } else {
2031             // focus is abandoned by a client that's not at the top of the stack,
2032             // no need to update focus.
2033             Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2034             while(stackIterator.hasNext()) {
2035                 FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
2036                 if(fse.mClientId.equals(clientToRemove)) {
2037                     Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
2038                             + fse.mClientId);
2039                     mFocusStack.remove(fse);
2040                 }
2041             }
2042         }
2043     }
2044
2045     /**
2046      * Helper function:
2047      * Remove focus listeners from the focus stack for a particular client.
2048      */
2049     private void removeFocusStackEntryForClient(IBinder cb) {
2050         // is the owner of the audio focus part of the client to remove?
2051         boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() &&
2052                 mFocusStack.peek().mSourceRef.equals(cb);
2053         Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
2054         while(stackIterator.hasNext()) {
2055             FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
2056             if(fse.mSourceRef.equals(cb)) {
2057                 Log.i(TAG, " AudioFocus  abandonAudioFocus(): removing entry for "
2058                         + fse.mClientId);
2059                 mFocusStack.remove(fse);
2060             }
2061         }
2062         if (isTopOfStackForClientToRemove) {
2063             // we removed an entry at the top of the stack:
2064             //  notify the new top of the stack it gained focus.
2065             notifyTopOfAudioFocusStack();
2066         }
2067     }
2068
2069     /**
2070      * Helper function:
2071      * Returns true if the system is in a state where the focus can be reevaluated, false otherwise.
2072      */
2073     private boolean canReassignAudioFocus() {
2074         // focus requests are rejected during a phone call or when the phone is ringing
2075         // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus
2076         if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) {
2077             return false;
2078         }
2079         return true;
2080     }
2081
2082     /**
2083      * Inner class to monitor audio focus client deaths, and remove them from the audio focus
2084      * stack if necessary.
2085      */
2086     private class AudioFocusDeathHandler implements IBinder.DeathRecipient {
2087         private IBinder mCb; // To be notified of client's death
2088
2089         AudioFocusDeathHandler(IBinder cb) {
2090             mCb = cb;
2091         }
2092
2093         public void binderDied() {
2094             synchronized(mFocusStack) {
2095                 Log.w(TAG, "  AudioFocus   audio focus client died");
2096                 removeFocusStackEntryForClient(mCb);
2097             }
2098         }
2099
2100         public IBinder getBinder() {
2101             return mCb;
2102         }
2103     }
2104
2105
2106     /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */
2107     public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb,
2108             IAudioFocusDispatcher fd, String clientId) {
2109         Log.i(TAG, " AudioFocus  requestAudioFocus() from " + clientId);
2110         // the main stream type for the audio focus request is currently not used. It may
2111         // potentially be used to handle multiple stream type-dependent audio focuses.
2112
2113         // we need a valid binder callback for clients other than the AudioService's phone
2114         // state listener
2115         if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId) && ((cb == null) || !cb.pingBinder())) {
2116             Log.i(TAG, " AudioFocus  DOA client for requestAudioFocus(), exiting");
2117             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
2118         }
2119
2120         if (!canReassignAudioFocus()) {
2121             return AudioManager.AUDIOFOCUS_REQUEST_FAILED;
2122         }
2123
2124         synchronized(mFocusStack) {
2125             if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) {
2126                 // if focus is already owned by this client and the reason for acquiring the focus
2127                 // hasn't changed, don't do anything
2128                 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) {
2129                     return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2130                 }
2131                 // the reason for the audio focus request has changed: remove the current top of
2132                 // stack and respond as if we had a new focus owner
2133                 mFocusStack.pop();
2134             }
2135
2136             // notify current top of stack it is losing focus
2137             if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
2138                 try {
2139                     mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange(
2140                             -1 * focusChangeHint, // loss and gain codes are inverse of each other
2141                             mFocusStack.peek().mClientId);
2142                 } catch (RemoteException e) {
2143                     Log.e(TAG, " Failure to signal loss of focus due to "+ e);
2144                     e.printStackTrace();
2145                 }
2146             }
2147
2148             // focus requester might already be somewhere below in the stack, remove it
2149             removeFocusStackEntry(clientId, false);
2150
2151             // push focus requester at the top of the audio focus stack
2152             mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb,
2153                     clientId));
2154         }//synchronized(mFocusStack)
2155
2156         // handle the potential premature death of the new holder of the focus
2157         // (premature death == death before abandoning focus) for a client which is not the
2158         // AudioService's phone state listener
2159         if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId)) {
2160             // Register for client death notification
2161             AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb);
2162             try {
2163                 cb.linkToDeath(afdh, 0);
2164             } catch (RemoteException e) {
2165                 // client has already died!
2166                 Log.w(TAG, "AudioFocus  requestAudioFocus() could not link to "+cb+" binder death");
2167             }
2168         }
2169
2170         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2171     }
2172
2173     /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */
2174     public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) {
2175         Log.i(TAG, " AudioFocus  abandonAudioFocus() from " + clientId);
2176
2177         // this will take care of notifying the new focus owner if needed
2178         synchronized(mFocusStack) {
2179             removeFocusStackEntry(clientId, true);
2180         }
2181
2182         return AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
2183     }
2184
2185
2186     public void unregisterAudioFocusClient(String clientId) {
2187         synchronized(mFocusStack) {
2188             removeFocusStackEntry(clientId, false);
2189         }
2190     }
2191
2192
2193     //==========================================================================================
2194     // RemoteControl
2195     //==========================================================================================
2196     /**
2197      * Receiver for media button intents. Handles the dispatching of the media button event
2198      * to one of the registered listeners, or if there was none, resumes the intent broadcast
2199      * to the rest of the system.
2200      */
2201     private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
2202         @Override
2203         public void onReceive(Context context, Intent intent) {
2204             String action = intent.getAction();
2205             if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
2206                 return;
2207             }
2208             KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
2209             if (event != null) {
2210                 // if in a call or ringing, do not break the current phone app behavior
2211                 // TODO modify this to let the phone app specifically get the RC focus
2212                 //      add modify the phone app to take advantage of the new API
2213                 if ((getMode() == AudioSystem.MODE_IN_CALL) ||
2214                         (getMode() == AudioSystem.MODE_RINGTONE)) {
2215                     return;
2216                 }
2217                 synchronized(mRCStack) {
2218                     if (!mRCStack.empty()) {
2219                         // create a new intent specifically aimed at the current registered listener
2220                         Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
2221                         targetedIntent.putExtras(intent.getExtras());
2222                         targetedIntent.setComponent(mRCStack.peek().mReceiverComponent);
2223                         // trap the current broadcast
2224                         abortBroadcast();
2225                         //Log.v(TAG, " Sending intent" + targetedIntent);
2226                         context.sendBroadcast(targetedIntent, null);
2227                     }
2228                 }
2229             }
2230         }
2231     }
2232
2233     private static class RemoteControlStackEntry {
2234         public ComponentName mReceiverComponent;// always non null
2235         // TODO implement registration expiration?
2236         //public int mRegistrationTime;
2237
2238         public RemoteControlStackEntry() {
2239         }
2240
2241         public RemoteControlStackEntry(ComponentName r) {
2242             mReceiverComponent = r;
2243         }
2244     }
2245
2246     private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();
2247
2248     /**
2249      * Helper function:
2250      * Display in the log the current entries in the remote control focus stack
2251      */
2252     private void dumpRCStack(PrintWriter pw) {
2253         pw.println("\nRemote Control stack entries:");
2254         synchronized(mRCStack) {
2255             Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2256             while(stackIterator.hasNext()) {
2257                 RemoteControlStackEntry fse = stackIterator.next();
2258                 pw.println("     receiver:" + fse.mReceiverComponent);
2259             }
2260         }
2261     }
2262
2263     /**
2264      * Helper function:
2265      * Set the new remote control receiver at the top of the RC focus stack
2266      */
2267     private void pushMediaButtonReceiver(ComponentName newReceiver) {
2268         // already at top of stack?
2269         if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) {
2270             return;
2271         }
2272         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2273         while(stackIterator.hasNext()) {
2274             RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
2275             if(rcse.mReceiverComponent.equals(newReceiver)) {
2276                 mRCStack.remove(rcse);
2277                 break;
2278             }
2279         }
2280         mRCStack.push(new RemoteControlStackEntry(newReceiver));
2281     }
2282
2283     /**
2284      * Helper function:
2285      * Remove the remote control receiver from the RC focus stack
2286      */
2287     private void removeMediaButtonReceiver(ComponentName newReceiver) {
2288         Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
2289         while(stackIterator.hasNext()) {
2290             RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
2291             if(rcse.mReceiverComponent.equals(newReceiver)) {
2292                 mRCStack.remove(rcse);
2293                 break;
2294             }
2295         }
2296     }
2297
2298
2299     /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */
2300     public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
2301         Log.i(TAG, "  Remote Control   registerMediaButtonEventReceiver() for " + eventReceiver);
2302
2303         synchronized(mRCStack) {
2304             pushMediaButtonReceiver(eventReceiver);
2305         }
2306     }
2307
2308     /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */
2309     public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
2310         Log.i(TAG, "  Remote Control   unregisterMediaButtonEventReceiver() for " + eventReceiver);
2311
2312         synchronized(mRCStack) {
2313             removeMediaButtonReceiver(eventReceiver);
2314         }
2315     }
2316
2317
2318     @Override
2319     protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
2320         // TODO probably a lot more to do here than just the audio focus and remote control stacks
2321         dumpFocusStack(pw);
2322         dumpRCStack(pw);
2323     }
2324
2325
2326 }