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