OSDN Git Service

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