blob: 5a59712df0209fb3e7d8f999c6e876171068a1fe [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.media;
18
19import android.app.ActivityManagerNative;
20import android.content.ContentResolver;
21import android.content.Context;
22import android.content.Intent;
Eric Laurenta553c252009-07-17 12:17:14 -070023import android.content.IntentFilter;
24import android.bluetooth.BluetoothIntent;
25import android.content.BroadcastReceiver;
26import android.bluetooth.BluetoothHeadset;
27import android.bluetooth.BluetoothA2dp;
Eric Laurentd5603c12009-08-06 08:49:39 -070028import android.bluetooth.BluetoothDevice;
29import android.bluetooth.BluetoothClass;
Eric Laurenta553c252009-07-17 12:17:14 -070030
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.content.pm.PackageManager;
Jason Parekhb1096152009-03-24 17:48:25 -070032import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.media.MediaPlayer.OnCompletionListener;
34import android.media.MediaPlayer.OnErrorListener;
35import android.os.Binder;
36import android.os.Environment;
37import android.os.Handler;
38import android.os.IBinder;
39import android.os.Looper;
40import android.os.Message;
41import android.os.RemoteException;
42import android.os.ServiceManager;
43import android.provider.Settings;
44import android.provider.Settings.System;
45import android.util.Log;
46import android.view.VolumePanel;
Jared Suttles59820132009-08-13 21:50:52 -050047import android.os.SystemProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048
49import com.android.internal.telephony.ITelephony;
50
51import java.io.IOException;
52import java.util.ArrayList;
Eric Laurentc42ac9d2009-07-29 08:53:03 -070053import java.util.HashMap;
54import java.util.Iterator;
55import java.util.Map;
56import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057
Jared Suttles59820132009-08-13 21:50:52 -050058
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059/**
60 * The implementation of the volume manager service.
61 * <p>
62 * This implementation focuses on delivering a responsive UI. Most methods are
63 * asynchronous to external calls. For example, the task of setting a volume
64 * will update our internal state, but in a separate thread will set the system
65 * volume and later persist to the database. Similarly, setting the ringer mode
66 * will update the state and broadcast a change and in a separate thread later
67 * persist the ringer mode.
68 *
69 * @hide
70 */
71public class AudioService extends IAudioService.Stub {
72
73 private static final String TAG = "AudioService";
74
75 /** How long to delay before persisting a change in volume/ringer mode. */
76 private static final int PERSIST_DELAY = 3000;
77
78 private Context mContext;
79 private ContentResolver mContentResolver;
80
81 /** The UI */
82 private VolumePanel mVolumePanel;
83
84 // sendMsg() flags
85 /** Used when a message should be shared across all stream types. */
86 private static final int SHARED_MSG = -1;
87 /** If the msg is already queued, replace it with this one. */
88 private static final int SENDMSG_REPLACE = 0;
89 /** If the msg is already queued, ignore this one and leave the old. */
90 private static final int SENDMSG_NOOP = 1;
91 /** If the msg is already queued, queue this one and leave the old. */
92 private static final int SENDMSG_QUEUE = 2;
93
94 // AudioHandler message.whats
95 private static final int MSG_SET_SYSTEM_VOLUME = 0;
96 private static final int MSG_PERSIST_VOLUME = 1;
97 private static final int MSG_PERSIST_RINGER_MODE = 3;
98 private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
99 private static final int MSG_MEDIA_SERVER_DIED = 5;
100 private static final int MSG_MEDIA_SERVER_STARTED = 6;
101 private static final int MSG_PLAY_SOUND_EFFECT = 7;
102
103 /** @see AudioSystemThread */
104 private AudioSystemThread mAudioSystemThread;
105 /** @see AudioHandler */
106 private AudioHandler mAudioHandler;
107 /** @see VolumeStreamState */
108 private VolumeStreamState[] mStreamStates;
Jason Parekhb1096152009-03-24 17:48:25 -0700109 private SettingsObserver mSettingsObserver;
Eric Laurenta553c252009-07-17 12:17:14 -0700110
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 private int mMode;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112 private Object mSettingsLock = new Object();
113 private boolean mMediaServerOk;
114
115 private SoundPool mSoundPool;
116 private Object mSoundEffectsLock = new Object();
117 private static final int NUM_SOUNDPOOL_CHANNELS = 4;
118 private static final int SOUND_EFFECT_VOLUME = 1000;
119
120 /* Sound effect file names */
121 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
122 private static final String[] SOUND_EFFECT_FILES = new String[] {
123 "Effect_Tick.ogg",
124 "KeypressStandard.ogg",
125 "KeypressSpacebar.ogg",
126 "KeypressDelete.ogg",
127 "KeypressReturn.ogg"
128 };
129
130 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
131 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
132 * uses soundpool (second column) */
133 private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
134 {0, -1}, // FX_KEY_CLICK
135 {0, -1}, // FX_FOCUS_NAVIGATION_UP
136 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN
137 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT
138 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT
139 {1, -1}, // FX_KEYPRESS_STANDARD
140 {2, -1}, // FX_KEYPRESS_SPACEBAR
141 {3, -1}, // FX_FOCUS_DELETE
142 {4, -1} // FX_FOCUS_RETURN
143 };
144
Jared Suttles59820132009-08-13 21:50:52 -0500145 /** @hide Maximum volume index values for audio streams */
146 private int[] MAX_STREAM_VOLUME = new int[] {
147 6, // STREAM_VOICE_CALL
148 8, // STREAM_SYSTEM
149 8, // STREAM_RING
150 16, // STREAM_MUSIC
151 8, // STREAM_ALARM
152 8, // STREAM_NOTIFICATION
153 16, // STREAM_BLUETOOTH_SCO
154 8, // STREAM_SYSTEM_ENFORCED
155 16, // STREAM_DTMF
156 16 // STREAM_TTS
157 };
Eric Laurenta553c252009-07-17 12:17:14 -0700158 /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings
159 * of another stream: This avoids multiplying the volume settings for hidden
160 * stream types that follow other stream behavior for volume settings
161 * NOTE: do not create loops in aliases! */
162 private int[] STREAM_VOLUME_ALIAS = new int[] {
163 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL
164 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM
165 AudioSystem.STREAM_RING, // STREAM_RING
166 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC
167 AudioSystem.STREAM_ALARM, // STREAM_ALARM
168 AudioSystem.STREAM_NOTIFICATION, // STREAM_NOTIFICATION
169 AudioSystem.STREAM_VOICE_CALL, // STREAM_BLUETOOTH_SCO
170 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM_ENFORCED
171 AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF
172 AudioSystem.STREAM_MUSIC // STREAM_TTS
173 };
174
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800175 private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
176 public void onError(int error) {
177 switch (error) {
178 case AudioSystem.AUDIO_STATUS_SERVER_DIED:
179 if (mMediaServerOk) {
180 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
181 null, 1500);
182 }
183 break;
184 case AudioSystem.AUDIO_STATUS_OK:
185 if (!mMediaServerOk) {
186 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
187 null, 0);
188 }
189 break;
190 default:
191 break;
192 }
193 }
194 };
195
196 /**
197 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
198 * {@link AudioManager#RINGER_MODE_SILENT}, or
199 * {@link AudioManager#RINGER_MODE_VIBRATE}.
200 */
201 private int mRingerMode;
202
Eric Laurent9bcf4012009-06-12 06:09:28 -0700203 /** @see System#MODE_RINGER_STREAMS_AFFECTED */
204 private int mRingerModeAffectedStreams;
205
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800206 /** @see System#MUTE_STREAMS_AFFECTED */
207 private int mMuteAffectedStreams;
208
209 /**
210 * Has multiple bits per vibrate type to indicate the type's vibrate
211 * setting. See {@link #setVibrateSetting(int, int)}.
212 * <p>
213 * NOTE: This is not the final decision of whether vibrate is on/off for the
214 * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
215 */
216 private int mVibrateSetting;
217
Eric Laurenta553c252009-07-17 12:17:14 -0700218 /** @see System#NOTIFICATIONS_USE_RING_VOLUME */
219 private int mNotificationsUseRingVolume;
220
221 // Broadcast receiver for device connections intent broadcasts
222 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver();
223
224 //TODO: use common definitions with HeadsetObserver
225 private static final int BIT_HEADSET = (1 << 0);
226 private static final int BIT_HEADSET_NO_MIC = (1 << 1);
227 private static final int BIT_TTY = (1 << 2);
228 private static final int BIT_FM_HEADSET = (1 << 3);
229 private static final int BIT_FM_SPEAKER = (1 << 4);
230
231 private int mHeadsetState;
232
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700233 // Devices currently connected
234 private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>();
235
236 // Forced device usage for communications
237 private int mForcedUseForComm;
238
Eric Laurentd5603c12009-08-06 08:49:39 -0700239 private BluetoothDevice mBluetoothDevice = null;
240
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800241 ///////////////////////////////////////////////////////////////////////////
242 // Construction
243 ///////////////////////////////////////////////////////////////////////////
244
245 /** @hide */
246 public AudioService(Context context) {
247 mContext = context;
248 mContentResolver = context.getContentResolver();
Jared Suttles59820132009-08-13 21:50:52 -0500249
250 // Intialized volume
251 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt(
252 "ro.config.vc_call_vol_steps",
253 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
254
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 mVolumePanel = new VolumePanel(context, this);
Jason Parekhb1096152009-03-24 17:48:25 -0700256 mSettingsObserver = new SettingsObserver();
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700257 mMode = AudioSystem.MODE_NORMAL;
258 mHeadsetState = 0;
259 mForcedUseForComm = AudioSystem.FORCE_NONE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800260 createAudioSystemThread();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800261 readPersistedSettings();
Eric Laurenta553c252009-07-17 12:17:14 -0700262 createStreamStates();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263 mMediaServerOk = true;
264 AudioSystem.setErrorCallback(mAudioSystemCallback);
265 loadSoundEffects();
Eric Laurenta553c252009-07-17 12:17:14 -0700266
267 // Register for device connection intent broadcasts.
268 IntentFilter intentFilter =
269 new IntentFilter(Intent.ACTION_HEADSET_PLUG);
270 intentFilter.addAction(BluetoothA2dp.SINK_STATE_CHANGED_ACTION);
Eric Laurentd5603c12009-08-06 08:49:39 -0700271 intentFilter.addAction(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION);
Eric Laurenta553c252009-07-17 12:17:14 -0700272 context.registerReceiver(mReceiver, intentFilter);
Jared Suttles59820132009-08-13 21:50:52 -0500273
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800274 }
275
276 private void createAudioSystemThread() {
277 mAudioSystemThread = new AudioSystemThread();
278 mAudioSystemThread.start();
279 waitForAudioHandlerCreation();
280 }
281
282 /** Waits for the volume handler to be created by the other thread. */
283 private void waitForAudioHandlerCreation() {
284 synchronized(this) {
285 while (mAudioHandler == null) {
286 try {
287 // Wait for mAudioHandler to be set by the other thread
288 wait();
289 } catch (InterruptedException e) {
290 Log.e(TAG, "Interrupted while waiting on volume handler.");
291 }
292 }
293 }
294 }
295
296 private void createStreamStates() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 int numStreamTypes = AudioSystem.getNumStreamTypes();
298 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
299
300 for (int i = 0; i < numStreamTypes; i++) {
Eric Laurenta553c252009-07-17 12:17:14 -0700301 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i);
302 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303
Eric Laurenta553c252009-07-17 12:17:14 -0700304 // Correct stream index values for streams with aliases
305 for (int i = 0; i < numStreamTypes; i++) {
306 if (STREAM_VOLUME_ALIAS[i] != i) {
307 int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i);
308 streams[i].mIndex = streams[i].getValidIndex(index);
309 setStreamVolumeIndex(i, index);
310 index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i);
311 streams[i].mLastAudibleIndex = streams[i].getValidIndex(index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 }
313 }
314 }
315
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 private void readPersistedSettings() {
317 final ContentResolver cr = mContentResolver;
318
319 mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800320
321 mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
322
Eric Laurent9bcf4012009-06-12 06:09:28 -0700323 mRingerModeAffectedStreams = Settings.System.getInt(cr,
324 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
Eric Laurenta553c252009-07-17 12:17:14 -0700325 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
326 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
Eric Laurent9bcf4012009-06-12 06:09:28 -0700327
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800328 mMuteAffectedStreams = System.getInt(cr,
329 System.MUTE_STREAMS_AFFECTED,
330 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
331
Eric Laurenta553c252009-07-17 12:17:14 -0700332 mNotificationsUseRingVolume = System.getInt(cr,
333 Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1);
334
335 if (mNotificationsUseRingVolume == 1) {
336 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
337 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800338 // Each stream will read its own persisted settings
339
340 // Broadcast the sticky intent
341 broadcastRingerMode();
342
343 // Broadcast vibrate settings
344 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
345 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
346 }
347
Eric Laurenta553c252009-07-17 12:17:14 -0700348 private void setStreamVolumeIndex(int stream, int index) {
349 AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800350 }
351
Eric Laurenta553c252009-07-17 12:17:14 -0700352 private int rescaleIndex(int index, int srcStream, int dstStream) {
353 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex();
354 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355
356 ///////////////////////////////////////////////////////////////////////////
357 // IPC methods
358 ///////////////////////////////////////////////////////////////////////////
359
360 /** @see AudioManager#adjustVolume(int, int) */
361 public void adjustVolume(int direction, int flags) {
362 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
363 }
364
365 /** @see AudioManager#adjustVolume(int, int, int) */
366 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
367
368 int streamType = getActiveStreamType(suggestedStreamType);
369
370 // Don't play sound on other streams
371 if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
372 flags &= ~AudioManager.FLAG_PLAY_SOUND;
373 }
374
375 adjustStreamVolume(streamType, direction, flags);
376 }
377
378 /** @see AudioManager#adjustStreamVolume(int, int, int) */
379 public void adjustStreamVolume(int streamType, int direction, int flags) {
380 ensureValidDirection(direction);
381 ensureValidStreamType(streamType);
382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383
Eric Laurenta553c252009-07-17 12:17:14 -0700384 VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800385 final int oldIndex = streamState.mIndex;
386 boolean adjustVolume = true;
387
388 // If either the client forces allowing ringer modes for this adjustment,
389 // or the stream type is one that is affected by ringer modes
390 if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0
Eric Laurenta553c252009-07-17 12:17:14 -0700391 || streamType == AudioSystem.STREAM_RING) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 // Check if the ringer mode changes with this volume adjustment. If
393 // it does, it will handle adjusting the volume, so we won't below
394 adjustVolume = checkForRingerModeChange(oldIndex, direction);
395 }
396
397 if (adjustVolume && streamState.adjustIndex(direction)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 // Post message to set system volume (it in turn will post a message
399 // to persist). Do not change volume if stream is muted.
400 if (streamState.muteCount() == 0) {
Eric Laurenta553c252009-07-17 12:17:14 -0700401 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 streamState, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 }
404 }
405
406 // UI
407 mVolumePanel.postVolumeChanged(streamType, flags);
408 // Broadcast Intent
409 sendVolumeUpdate(streamType);
410 }
411
412 /** @see AudioManager#setStreamVolume(int, int, int) */
413 public void setStreamVolume(int streamType, int index, int flags) {
414 ensureValidStreamType(streamType);
Eric Laurenta553c252009-07-17 12:17:14 -0700415 index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]);
416 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417
418 // UI, etc.
419 mVolumePanel.postVolumeChanged(streamType, flags);
420 // Broadcast Intent
421 sendVolumeUpdate(streamType);
422 }
423
424 private void sendVolumeUpdate(int streamType) {
425 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
426 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
427 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, getStreamVolume(streamType));
428
429 // Currently, sending the intent only when the stream is BLUETOOTH_SCO
Eric Laurenta553c252009-07-17 12:17:14 -0700430 if (streamType == AudioSystem.STREAM_BLUETOOTH_SCO) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800431 mContext.sendBroadcast(intent);
432 }
433 }
434
435 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 * Sets the stream state's index, and posts a message to set system volume.
437 * This will not call out to the UI. Assumes a valid stream type.
438 *
439 * @param streamType Type of the stream
440 * @param index Desired volume index of the stream
441 * @param force If true, set the volume even if the desired volume is same
442 * as the current volume.
Eric Laurent9bcf4012009-06-12 06:09:28 -0700443 * @param lastAudible If true, stores new index as last audible one
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 */
Eric Laurent9bcf4012009-06-12 06:09:28 -0700445 private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 VolumeStreamState streamState = mStreamStates[streamType];
Eric Laurent9bcf4012009-06-12 06:09:28 -0700447 if (streamState.setIndex(index, lastAudible) || force) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 // Post message to set system volume (it in turn will post a message
449 // to persist). Do not change volume if stream is muted.
450 if (streamState.muteCount() == 0) {
451 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
452 streamState, 0);
453 }
454 }
455 }
456
457 /** @see AudioManager#setStreamSolo(int, boolean) */
458 public void setStreamSolo(int streamType, boolean state, IBinder cb) {
459 for (int stream = 0; stream < mStreamStates.length; stream++) {
460 if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
461 // Bring back last audible volume
462 mStreamStates[stream].mute(cb, state);
463 }
464 }
465
466 /** @see AudioManager#setStreamMute(int, boolean) */
467 public void setStreamMute(int streamType, boolean state, IBinder cb) {
468 if (isStreamAffectedByMute(streamType)) {
469 mStreamStates[streamType].mute(cb, state);
470 }
471 }
472
473 /** @see AudioManager#getStreamVolume(int) */
474 public int getStreamVolume(int streamType) {
475 ensureValidStreamType(streamType);
Eric Laurenta553c252009-07-17 12:17:14 -0700476 return (mStreamStates[streamType].mIndex + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 }
478
479 /** @see AudioManager#getStreamMaxVolume(int) */
480 public int getStreamMaxVolume(int streamType) {
481 ensureValidStreamType(streamType);
Eric Laurenta553c252009-07-17 12:17:14 -0700482 return (mStreamStates[streamType].getMaxIndex() + 5) / 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800483 }
484
485 /** @see AudioManager#getRingerMode() */
486 public int getRingerMode() {
487 return mRingerMode;
488 }
489
490 /** @see AudioManager#setRingerMode(int) */
491 public void setRingerMode(int ringerMode) {
Eric Laurenta553c252009-07-17 12:17:14 -0700492 synchronized (mSettingsLock) {
493 if (ringerMode != mRingerMode) {
494 setRingerModeInt(ringerMode, true);
495 // Send sticky broadcast
496 broadcastRingerMode();
497 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 }
499 }
500
Eric Laurent4050c932009-07-08 02:52:14 -0700501 private void setRingerModeInt(int ringerMode, boolean persist) {
Jason Parekhb1096152009-03-24 17:48:25 -0700502 mRingerMode = ringerMode;
503
504 // Adjust volumes via posting message
505 int numStreamTypes = AudioSystem.getNumStreamTypes();
506 if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
507 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
508 if (!isStreamAffectedByRingerMode(streamType)) continue;
509 // Bring back last audible volume
510 setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
Eric Laurent9bcf4012009-06-12 06:09:28 -0700511 false, false);
Jason Parekhb1096152009-03-24 17:48:25 -0700512 }
513 } else {
514 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
Eric Laurent9bcf4012009-06-12 06:09:28 -0700515 if (isStreamAffectedByRingerMode(streamType)) {
516 // Either silent or vibrate, either way volume is 0
517 setStreamVolumeInt(streamType, 0, false, false);
518 } else {
519 // restore stream volume in the case the stream changed from affected
520 // to non affected by ringer mode. Does not arm to do it for streams that
521 // are not affected as well.
522 setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
523 false, false);
524 }
Jason Parekhb1096152009-03-24 17:48:25 -0700525 }
526 }
Eric Laurenta553c252009-07-17 12:17:14 -0700527
Jason Parekhb1096152009-03-24 17:48:25 -0700528 // Post a persist ringer mode msg
Eric Laurent4050c932009-07-08 02:52:14 -0700529 if (persist) {
530 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
531 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
532 }
Jason Parekhb1096152009-03-24 17:48:25 -0700533 }
534
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800535 /** @see AudioManager#shouldVibrate(int) */
536 public boolean shouldVibrate(int vibrateType) {
537
538 switch (getVibrateSetting(vibrateType)) {
539
540 case AudioManager.VIBRATE_SETTING_ON:
541 return mRingerMode != AudioManager.RINGER_MODE_SILENT;
542
543 case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
544 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
545
546 case AudioManager.VIBRATE_SETTING_OFF:
547 // Phone ringer should always vibrate in vibrate mode
548 if (vibrateType == AudioManager.VIBRATE_TYPE_RINGER) {
549 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
550 }
551
552 default:
553 return false;
554 }
555 }
556
557 /** @see AudioManager#getVibrateSetting(int) */
558 public int getVibrateSetting(int vibrateType) {
559 return (mVibrateSetting >> (vibrateType * 2)) & 3;
560 }
561
562 /** @see AudioManager#setVibrateSetting(int, int) */
563 public void setVibrateSetting(int vibrateType, int vibrateSetting) {
564
565 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
566
567 // Broadcast change
568 broadcastVibrateSetting(vibrateType);
569
570 // Post message to set ringer mode (it in turn will post a message
571 // to persist)
572 sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
573 null, 0);
574 }
575
576 /**
577 * @see #setVibrateSetting(int, int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 */
579 public static int getValueForVibrateSetting(int existingValue, int vibrateType,
580 int vibrateSetting) {
581
582 // First clear the existing setting. Each vibrate type has two bits in
583 // the value. Note '3' is '11' in binary.
584 existingValue &= ~(3 << (vibrateType * 2));
585
586 // Set into the old value
587 existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
588
589 return existingValue;
590 }
591
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800592 /** @see AudioManager#setMode(int) */
593 public void setMode(int mode) {
594 if (!checkAudioSettingsPermission("setMode()")) {
595 return;
596 }
Eric Laurenta553c252009-07-17 12:17:14 -0700597
598 if (mode < AudioSystem.MODE_CURRENT || mode > AudioSystem.MODE_IN_CALL) {
599 return;
600 }
601
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 synchronized (mSettingsLock) {
Eric Laurenta553c252009-07-17 12:17:14 -0700603 if (mode == AudioSystem.MODE_CURRENT) {
604 mode = mMode;
605 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606 if (mode != mMode) {
Eric Laurenta553c252009-07-17 12:17:14 -0700607 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) {
Eric Laurentb9c9d262009-05-06 08:13:20 -0700608 mMode = mode;
609 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 }
611 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
Eric Laurenta553c252009-07-17 12:17:14 -0700612 int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex;
613 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800614 }
615 }
616
617 /** @see AudioManager#getMode() */
618 public int getMode() {
619 return mMode;
620 }
621
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 /** @see AudioManager#playSoundEffect(int) */
623 public void playSoundEffect(int effectType) {
624 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
625 effectType, SOUND_EFFECT_VOLUME, null, 0);
626 }
627
628 /** @see AudioManager#playSoundEffect(int, float) */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 public void playSoundEffectVolume(int effectType, float volume) {
630 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
631 effectType, (int) (volume * 1000), null, 0);
632 }
633
634 /**
635 * Loads samples into the soundpool.
636 * This method must be called at when sound effects are enabled
637 */
638 public boolean loadSoundEffects() {
639 synchronized (mSoundEffectsLock) {
640 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
641 if (mSoundPool == null) {
642 return false;
643 }
644 /*
645 * poolId table: The value -1 in this table indicates that corresponding
646 * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
647 * Once loaded, the value in poolId is the sample ID and the same
648 * sample can be reused for another effect using the same file.
649 */
650 int[] poolId = new int[SOUND_EFFECT_FILES.length];
651 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
652 poolId[fileIdx] = -1;
653 }
654 /*
655 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
656 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
657 * this indicates we have a valid sample loaded for this effect.
658 */
659 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
660 // Do not load sample if this effect uses the MediaPlayer
661 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
662 continue;
663 }
664 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
665 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
666 int sampleId = mSoundPool.load(filePath, 0);
667 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
668 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
669 if (sampleId <= 0) {
670 Log.w(TAG, "Soundpool could not load file: "+filePath);
671 }
672 } else {
673 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
674 }
675 }
676 }
677
678 return true;
679 }
680
681 /**
682 * Unloads samples from the sound pool.
683 * This method can be called to free some memory when
684 * sound effects are disabled.
685 */
686 public void unloadSoundEffects() {
687 synchronized (mSoundEffectsLock) {
688 if (mSoundPool == null) {
689 return;
690 }
691 int[] poolId = new int[SOUND_EFFECT_FILES.length];
692 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
693 poolId[fileIdx] = 0;
694 }
695
696 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
697 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
698 continue;
699 }
700 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
701 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
702 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
703 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
704 }
705 }
706 mSoundPool = null;
707 }
708 }
709
Eric Laurent4050c932009-07-08 02:52:14 -0700710 /** @see AudioManager#reloadAudioSettings() */
711 public void reloadAudioSettings() {
712 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
713 readPersistedSettings();
714
715 // restore volume settings
716 int numStreamTypes = AudioSystem.getNumStreamTypes();
717 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
718 VolumeStreamState streamState = mStreamStates[streamType];
719
Eric Laurentd25ae672009-07-27 02:12:30 -0700720 String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]];
721 String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
722 int index = Settings.System.getInt(mContentResolver,
723 settingName,
724 AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
725 if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
726 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
727 } else {
728 index *= 10;
Eric Laurent4050c932009-07-08 02:52:14 -0700729 }
Eric Laurentd25ae672009-07-27 02:12:30 -0700730 streamState.mIndex = streamState.getValidIndex(index);
731
732 index = (index + 5) / 10;
733 index = Settings.System.getInt(mContentResolver,
734 lastAudibleSettingName,
735 (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
736 if (STREAM_VOLUME_ALIAS[streamType] != streamType) {
737 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType);
738 } else {
739 index *= 10;
740 }
741 streamState.mLastAudibleIndex = streamState.getValidIndex(index);
Eric Laurenta553c252009-07-17 12:17:14 -0700742
Eric Laurent4050c932009-07-08 02:52:14 -0700743 // unmute stream that whas muted but is not affect by mute anymore
744 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
745 int size = streamState.mDeathHandlers.size();
746 for (int i = 0; i < size; i++) {
747 streamState.mDeathHandlers.get(i).mMuteCount = 1;
748 streamState.mDeathHandlers.get(i).mute(false);
749 }
750 }
751 // apply stream volume
752 if (streamState.muteCount() == 0) {
Eric Laurenta553c252009-07-17 12:17:14 -0700753 setStreamVolumeIndex(streamType, streamState.mIndex);
Eric Laurent4050c932009-07-08 02:52:14 -0700754 }
755 }
756
757 // apply new ringer mode
758 setRingerModeInt(getRingerMode(), false);
759 }
760
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700761 /** @see AudioManager#setSpeakerphoneOn() */
762 public void setSpeakerphoneOn(boolean on){
763 if (on) {
764 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER);
765 mForcedUseForComm = AudioSystem.FORCE_SPEAKER;
766 } else {
767 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
768 mForcedUseForComm = AudioSystem.FORCE_NONE;
769 }
770 }
771
772 /** @see AudioManager#isSpeakerphoneOn() */
773 public boolean isSpeakerphoneOn() {
774 if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) {
775 return true;
776 } else {
777 return false;
778 }
779 }
780
781 /** @see AudioManager#setBluetoothScoOn() */
782 public void setBluetoothScoOn(boolean on){
783 if (on) {
784 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO);
Eric Laurentd5603c12009-08-06 08:49:39 -0700785 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO);
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700786 mForcedUseForComm = AudioSystem.FORCE_BT_SCO;
787 } else {
788 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE);
Eric Laurentd5603c12009-08-06 08:49:39 -0700789 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE);
Eric Laurentc42ac9d2009-07-29 08:53:03 -0700790 mForcedUseForComm = AudioSystem.FORCE_NONE;
791 }
792 }
793
794 /** @see AudioManager#isBluetoothScoOn() */
795 public boolean isBluetoothScoOn() {
796 if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) {
797 return true;
798 } else {
799 return false;
800 }
801 }
802
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800803 ///////////////////////////////////////////////////////////////////////////
804 // Internal methods
805 ///////////////////////////////////////////////////////////////////////////
806
807 /**
808 * Checks if the adjustment should change ringer mode instead of just
809 * adjusting volume. If so, this will set the proper ringer mode and volume
810 * indices on the stream states.
811 */
812 private boolean checkForRingerModeChange(int oldIndex, int direction) {
813 boolean adjustVolumeIndex = true;
814 int newRingerMode = mRingerMode;
815
Eric Laurenta553c252009-07-17 12:17:14 -0700816 if (mRingerMode == AudioManager.RINGER_MODE_NORMAL && (oldIndex + 5) / 10 == 1
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 && direction == AudioManager.ADJUST_LOWER) {
818 newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
819 } else if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
820 if (direction == AudioManager.ADJUST_RAISE) {
821 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
822 } else if (direction == AudioManager.ADJUST_LOWER) {
823 newRingerMode = AudioManager.RINGER_MODE_SILENT;
824 }
825 } else if (direction == AudioManager.ADJUST_RAISE
826 && mRingerMode == AudioManager.RINGER_MODE_SILENT) {
827 newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
828 }
829
830 if (newRingerMode != mRingerMode) {
831 setRingerMode(newRingerMode);
832
833 /*
834 * If we are changing ringer modes, do not increment/decrement the
835 * volume index. Instead, the handler for the message above will
836 * take care of changing the index.
837 */
838 adjustVolumeIndex = false;
839 }
840
841 return adjustVolumeIndex;
842 }
843
844 public boolean isStreamAffectedByRingerMode(int streamType) {
Eric Laurent9bcf4012009-06-12 06:09:28 -0700845 return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 }
847
848 public boolean isStreamAffectedByMute(int streamType) {
849 return (mMuteAffectedStreams & (1 << streamType)) != 0;
850 }
851
852 private void ensureValidDirection(int direction) {
853 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
854 throw new IllegalArgumentException("Bad direction " + direction);
855 }
856 }
857
858 private void ensureValidStreamType(int streamType) {
859 if (streamType < 0 || streamType >= mStreamStates.length) {
860 throw new IllegalArgumentException("Bad stream type " + streamType);
861 }
862 }
863
864 private int getActiveStreamType(int suggestedStreamType) {
865 boolean isOffhook = false;
866 try {
867 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
868 if (phone != null) isOffhook = phone.isOffhook();
869 } catch (RemoteException e) {
870 Log.w(TAG, "Couldn't connect to phone service", e);
871 }
872
Eric Laurenta553c252009-07-17 12:17:14 -0700873 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800874 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
875 return AudioSystem.STREAM_BLUETOOTH_SCO;
876 } else if (isOffhook) {
877 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
878 return AudioSystem.STREAM_VOICE_CALL;
879 } else if (AudioSystem.isMusicActive()) {
880 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
881 return AudioSystem.STREAM_MUSIC;
882 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
883 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING...");
884 return AudioSystem.STREAM_RING;
885 } else {
886 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
887 return suggestedStreamType;
888 }
889 }
890
891 private void broadcastRingerMode() {
892 // Send sticky broadcast
893 if (ActivityManagerNative.isSystemReady()) {
894 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
895 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
896 long origCallerIdentityToken = Binder.clearCallingIdentity();
897 mContext.sendStickyBroadcast(broadcast);
898 Binder.restoreCallingIdentity(origCallerIdentityToken);
899 }
900 }
901
902 private void broadcastVibrateSetting(int vibrateType) {
903 // Send broadcast
904 if (ActivityManagerNative.isSystemReady()) {
905 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
906 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
907 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
908 mContext.sendBroadcast(broadcast);
909 }
910 }
911
912 // Message helper methods
913 private static int getMsg(int baseMsg, int streamType) {
914 return (baseMsg & 0xffff) | streamType << 16;
915 }
916
917 private static int getMsgBase(int msg) {
918 return msg & 0xffff;
919 }
920
921 private static void sendMsg(Handler handler, int baseMsg, int streamType,
922 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
923 int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
924
925 if (existingMsgPolicy == SENDMSG_REPLACE) {
926 handler.removeMessages(msg);
927 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
928 return;
929 }
930
931 handler
932 .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
933 }
934
935 boolean checkAudioSettingsPermission(String method) {
936 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
937 == PackageManager.PERMISSION_GRANTED) {
938 return true;
939 }
940 String msg = "Audio Settings Permission Denial: " + method + " from pid="
941 + Binder.getCallingPid()
942 + ", uid=" + Binder.getCallingUid();
943 Log.w(TAG, msg);
944 return false;
945 }
946
947
948 ///////////////////////////////////////////////////////////////////////////
949 // Inner classes
950 ///////////////////////////////////////////////////////////////////////////
951
952 public class VolumeStreamState {
953 private final String mVolumeIndexSettingName;
954 private final String mLastAudibleVolumeIndexSettingName;
955 private final int mStreamType;
956
Eric Laurenta553c252009-07-17 12:17:14 -0700957 private int mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 private int mIndex;
959 private int mLastAudibleIndex;
960 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
961
Eric Laurenta553c252009-07-17 12:17:14 -0700962 private VolumeStreamState(String settingName, int streamType) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963
964 mVolumeIndexSettingName = settingName;
965 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
966
967 mStreamType = streamType;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968
969 final ContentResolver cr = mContentResolver;
Jared Suttles59820132009-08-13 21:50:52 -0500970 mIndexMax = MAX_STREAM_VOLUME[streamType];
Eric Laurenta553c252009-07-17 12:17:14 -0700971 mIndex = Settings.System.getInt(cr,
972 mVolumeIndexSettingName,
973 AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
974 mLastAudibleIndex = Settings.System.getInt(cr,
975 mLastAudibleVolumeIndexSettingName,
976 (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]);
977 AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
978 mIndexMax *= 10;
979 mIndex = getValidIndex(10 * mIndex);
980 mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex);
981 setStreamVolumeIndex(streamType, mIndex);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800982 mDeathHandlers = new ArrayList<VolumeDeathHandler>();
983 }
984
985 public boolean adjustIndex(int deltaIndex) {
Eric Laurenta553c252009-07-17 12:17:14 -0700986 return setIndex(mIndex + deltaIndex * 10, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800987 }
988
Eric Laurent9bcf4012009-06-12 06:09:28 -0700989 public boolean setIndex(int index, boolean lastAudible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 int oldIndex = mIndex;
991 mIndex = getValidIndex(index);
992
993 if (oldIndex != mIndex) {
Eric Laurent9bcf4012009-06-12 06:09:28 -0700994 if (lastAudible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 mLastAudibleIndex = mIndex;
996 }
Eric Laurenta553c252009-07-17 12:17:14 -0700997 // Apply change to all streams using this one as alias
998 int numStreamTypes = AudioSystem.getNumStreamTypes();
999 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1000 if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) {
1001 mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible);
1002 }
1003 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 return true;
1005 } else {
1006 return false;
1007 }
1008 }
1009
1010 public int getMaxIndex() {
Eric Laurenta553c252009-07-17 12:17:14 -07001011 return mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 }
1013
1014 public void mute(IBinder cb, boolean state) {
1015 VolumeDeathHandler handler = getDeathHandler(cb, state);
1016 if (handler == null) {
1017 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
1018 return;
1019 }
1020 handler.mute(state);
1021 }
1022
1023 private int getValidIndex(int index) {
1024 if (index < 0) {
1025 return 0;
Eric Laurenta553c252009-07-17 12:17:14 -07001026 } else if (index > mIndexMax) {
1027 return mIndexMax;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 }
1029
1030 return index;
1031 }
1032
1033 private class VolumeDeathHandler implements IBinder.DeathRecipient {
1034 private IBinder mICallback; // To be notified of client's death
1035 private int mMuteCount; // Number of active mutes for this client
1036
1037 VolumeDeathHandler(IBinder cb) {
1038 mICallback = cb;
1039 }
1040
1041 public void mute(boolean state) {
1042 synchronized(mDeathHandlers) {
1043 if (state) {
1044 if (mMuteCount == 0) {
1045 // Register for client death notification
1046 try {
1047 mICallback.linkToDeath(this, 0);
1048 mDeathHandlers.add(this);
1049 // If the stream is not yet muted by any client, set lvel to 0
1050 if (muteCount() == 0) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07001051 setIndex(0, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1053 VolumeStreamState.this, 0);
1054 }
1055 } catch (RemoteException e) {
1056 // Client has died!
1057 binderDied();
1058 mDeathHandlers.notify();
1059 return;
1060 }
1061 } else {
1062 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
1063 }
1064 mMuteCount++;
1065 } else {
1066 if (mMuteCount == 0) {
1067 Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
1068 } else {
1069 mMuteCount--;
1070 if (mMuteCount == 0) {
1071 // Unregistr from client death notification
1072 mDeathHandlers.remove(this);
1073 mICallback.unlinkToDeath(this, 0);
1074 if (muteCount() == 0) {
1075 // If the stream is not mut any more, restore it's volume if
1076 // ringer mode allows it
1077 if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07001078 setIndex(mLastAudibleIndex, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1080 VolumeStreamState.this, 0);
1081 }
1082 }
1083 }
1084 }
1085 }
1086 mDeathHandlers.notify();
1087 }
1088 }
1089
1090 public void binderDied() {
1091 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
1092 if (mMuteCount != 0) {
1093 // Reset all active mute requests from this client.
1094 mMuteCount = 1;
1095 mute(false);
1096 }
1097 }
1098 }
1099
1100 private int muteCount() {
1101 int count = 0;
1102 int size = mDeathHandlers.size();
1103 for (int i = 0; i < size; i++) {
1104 count += mDeathHandlers.get(i).mMuteCount;
1105 }
1106 return count;
1107 }
1108
1109 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
1110 synchronized(mDeathHandlers) {
1111 VolumeDeathHandler handler;
1112 int size = mDeathHandlers.size();
1113 for (int i = 0; i < size; i++) {
1114 handler = mDeathHandlers.get(i);
1115 if (cb.equals(handler.mICallback)) {
1116 return handler;
1117 }
1118 }
1119 // If this is the first mute request for this client, create a new
1120 // client death handler. Otherwise, it is an out of sequence unmute request.
1121 if (state) {
1122 handler = new VolumeDeathHandler(cb);
1123 } else {
1124 Log.w(TAG, "stream was not muted by this client");
1125 handler = null;
1126 }
1127 return handler;
1128 }
1129 }
1130 }
1131
1132 /** Thread that handles native AudioSystem control. */
1133 private class AudioSystemThread extends Thread {
1134 AudioSystemThread() {
1135 super("AudioService");
1136 }
1137
1138 @Override
1139 public void run() {
1140 // Set this thread up so the handler will work on it
1141 Looper.prepare();
1142
1143 synchronized(AudioService.this) {
1144 mAudioHandler = new AudioHandler();
1145
1146 // Notify that the handler has been created
1147 AudioService.this.notify();
1148 }
1149
1150 // Listen for volume change requests that are set by VolumePanel
1151 Looper.loop();
1152 }
1153 }
1154
1155 /** Handles internal volume messages in separate volume thread. */
1156 private class AudioHandler extends Handler {
1157
1158 private void setSystemVolume(VolumeStreamState streamState) {
1159
1160 // Adjust volume
Eric Laurenta553c252009-07-17 12:17:14 -07001161 setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex);
1162
1163 // Apply change to all streams using this one as alias
1164 int numStreamTypes = AudioSystem.getNumStreamTypes();
1165 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1166 if (streamType != streamState.mStreamType &&
1167 STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) {
1168 setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex);
1169 }
1170 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171
1172 // Post a persist volume msg
1173 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
1174 SENDMSG_REPLACE, 0, 0, streamState, PERSIST_DELAY);
1175 }
1176
1177 private void persistVolume(VolumeStreamState streamState) {
Eric Laurenta553c252009-07-17 12:17:14 -07001178 System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
1179 (streamState.mIndex + 5)/ 10);
1180 System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
1181 (streamState.mLastAudibleIndex + 5) / 10);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 }
1183
1184 private void persistRingerMode() {
1185 System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
1186 }
1187
1188 private void persistVibrateSetting() {
1189 System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
1190 }
1191
1192 private void playSoundEffect(int effectType, int volume) {
1193 synchronized (mSoundEffectsLock) {
1194 if (mSoundPool == null) {
1195 return;
1196 }
1197
1198 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
1199 float v = (float) volume / 1000.0f;
1200 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], v, v, 0, 0, 1.0f);
1201 } else {
1202 MediaPlayer mediaPlayer = new MediaPlayer();
1203 if (mediaPlayer != null) {
1204 try {
1205 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
1206 mediaPlayer.setDataSource(filePath);
1207 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
1208 mediaPlayer.prepare();
1209 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
1210 public void onCompletion(MediaPlayer mp) {
1211 cleanupPlayer(mp);
1212 }
1213 });
1214 mediaPlayer.setOnErrorListener(new OnErrorListener() {
1215 public boolean onError(MediaPlayer mp, int what, int extra) {
1216 cleanupPlayer(mp);
1217 return true;
1218 }
1219 });
1220 mediaPlayer.start();
1221 } catch (IOException ex) {
1222 Log.w(TAG, "MediaPlayer IOException: "+ex);
1223 } catch (IllegalArgumentException ex) {
1224 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
1225 } catch (IllegalStateException ex) {
1226 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1227 }
1228 }
1229 }
1230 }
1231 }
1232
1233 private void cleanupPlayer(MediaPlayer mp) {
1234 if (mp != null) {
1235 try {
1236 mp.stop();
1237 mp.release();
1238 } catch (IllegalStateException ex) {
1239 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1240 }
1241 }
1242 }
1243
1244 @Override
1245 public void handleMessage(Message msg) {
1246 int baseMsgWhat = getMsgBase(msg.what);
1247
1248 switch (baseMsgWhat) {
1249
1250 case MSG_SET_SYSTEM_VOLUME:
1251 setSystemVolume((VolumeStreamState) msg.obj);
1252 break;
1253
1254 case MSG_PERSIST_VOLUME:
1255 persistVolume((VolumeStreamState) msg.obj);
1256 break;
1257
1258 case MSG_PERSIST_RINGER_MODE:
1259 persistRingerMode();
1260 break;
1261
1262 case MSG_PERSIST_VIBRATE_SETTING:
1263 persistVibrateSetting();
1264 break;
1265
1266 case MSG_MEDIA_SERVER_DIED:
1267 Log.e(TAG, "Media server died.");
1268 // Force creation of new IAudioflinger interface
1269 mMediaServerOk = false;
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001270 AudioSystem.isMusicActive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 break;
1272
1273 case MSG_MEDIA_SERVER_STARTED:
1274 Log.e(TAG, "Media server started.");
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001275 // Restore device connection states
1276 Set set = mConnectedDevices.entrySet();
1277 Iterator i = set.iterator();
1278 while(i.hasNext()){
1279 Map.Entry device = (Map.Entry)i.next();
1280 AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(),
1281 AudioSystem.DEVICE_STATE_AVAILABLE,
1282 (String)device.getValue());
1283 }
1284
1285 // Restore call state
1286 AudioSystem.setPhoneState(mMode);
1287
Eric Laurentd5603c12009-08-06 08:49:39 -07001288 // Restore forced usage for communcations and record
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001289 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
Eric Laurentd5603c12009-08-06 08:49:39 -07001290 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001291
Eric Laurenta553c252009-07-17 12:17:14 -07001292 // Restore stream volumes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001293 int numStreamTypes = AudioSystem.getNumStreamTypes();
1294 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
Eric Laurenta553c252009-07-17 12:17:14 -07001295 int index;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 VolumeStreamState streamState = mStreamStates[streamType];
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001297 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 if (streamState.muteCount() == 0) {
Eric Laurenta553c252009-07-17 12:17:14 -07001299 index = streamState.mIndex;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300 } else {
Eric Laurenta553c252009-07-17 12:17:14 -07001301 index = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001302 }
Eric Laurenta553c252009-07-17 12:17:14 -07001303 setStreamVolumeIndex(streamType, index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001304 }
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001305
1306 // Restore ringer mode
1307 setRingerModeInt(getRingerMode(), false);
1308
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001309 mMediaServerOk = true;
1310 break;
1311
1312 case MSG_PLAY_SOUND_EFFECT:
1313 playSoundEffect(msg.arg1, msg.arg2);
1314 break;
1315 }
1316 }
1317 }
1318
Jason Parekhb1096152009-03-24 17:48:25 -07001319 private class SettingsObserver extends ContentObserver {
Eric Laurenta553c252009-07-17 12:17:14 -07001320
Jason Parekhb1096152009-03-24 17:48:25 -07001321 SettingsObserver() {
1322 super(new Handler());
1323 mContentResolver.registerContentObserver(Settings.System.getUriFor(
1324 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
Eric Laurenta553c252009-07-17 12:17:14 -07001325 mContentResolver.registerContentObserver(Settings.System.getUriFor(
1326 Settings.System.NOTIFICATIONS_USE_RING_VOLUME), false, this);
Jason Parekhb1096152009-03-24 17:48:25 -07001327 }
1328
1329 @Override
1330 public void onChange(boolean selfChange) {
1331 super.onChange(selfChange);
Eric Laurenta553c252009-07-17 12:17:14 -07001332 synchronized (mSettingsLock) {
1333 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
1334 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
1335 0);
1336 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
1337 /*
1338 * Ensure all stream types that should be affected by ringer mode
1339 * are in the proper state.
1340 */
1341 mRingerModeAffectedStreams = ringerModeAffectedStreams;
1342 setRingerModeInt(getRingerMode(), false);
1343 }
Eric Laurent9bcf4012009-06-12 06:09:28 -07001344
Eric Laurenta553c252009-07-17 12:17:14 -07001345 int notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
1346 Settings.System.NOTIFICATIONS_USE_RING_VOLUME,
1347 1);
1348 if (notificationsUseRingVolume != mNotificationsUseRingVolume) {
1349 mNotificationsUseRingVolume = notificationsUseRingVolume;
1350 if (mNotificationsUseRingVolume == 1) {
1351 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING;
1352 } else {
1353 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION;
1354 // Persist notification volume volume as it was not persisted while aliased to ring volume
1355 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION,
1356 SENDMSG_REPLACE, 0, 0, mStreamStates[AudioSystem.STREAM_NOTIFICATION], PERSIST_DELAY);
1357 }
1358 }
1359 }
Jason Parekhb1096152009-03-24 17:48:25 -07001360 }
Jason Parekhb1096152009-03-24 17:48:25 -07001361 }
Eric Laurenta553c252009-07-17 12:17:14 -07001362
1363 /**
1364 * Receiver for misc intent broadcasts the Phone app cares about.
1365 */
1366 private class AudioServiceBroadcastReceiver extends BroadcastReceiver {
1367 @Override
1368 public void onReceive(Context context, Intent intent) {
1369 String action = intent.getAction();
1370
1371 if (action.equals(BluetoothA2dp.SINK_STATE_CHANGED_ACTION)) {
1372 int state = intent.getIntExtra(BluetoothA2dp.SINK_STATE,
1373 BluetoothA2dp.STATE_DISCONNECTED);
1374 String address = intent.getStringExtra(BluetoothIntent.ADDRESS);
Eric Laurentd5603c12009-08-06 08:49:39 -07001375
1376 boolean isConnected = (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) &&
1377 ((String)mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)).equals(address));
1378
1379 if (isConnected &&
1380 state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) {
Eric Laurenta553c252009-07-17 12:17:14 -07001381 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1382 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1383 address);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001384 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP);
Eric Laurentd5603c12009-08-06 08:49:39 -07001385 } else if (!isConnected &&
1386 (state == BluetoothA2dp.STATE_CONNECTED || state != BluetoothA2dp.STATE_PLAYING)){
Eric Laurenta553c252009-07-17 12:17:14 -07001387 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP,
1388 AudioSystem.DEVICE_STATE_AVAILABLE,
1389 address);
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001390 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), address);
Eric Laurenta553c252009-07-17 12:17:14 -07001391 }
Eric Laurentd5603c12009-08-06 08:49:39 -07001392 } else if (action.equals(BluetoothIntent.HEADSET_STATE_CHANGED_ACTION)) {
1393 int state = intent.getIntExtra(BluetoothIntent.HEADSET_STATE,
Eric Laurenta553c252009-07-17 12:17:14 -07001394 BluetoothHeadset.STATE_ERROR);
1395 String address = intent.getStringExtra(BluetoothIntent.ADDRESS);
Eric Laurentd5603c12009-08-06 08:49:39 -07001396 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO;
1397 if (mBluetoothDevice == null) {
1398 mBluetoothDevice = (BluetoothDevice)mContext.getSystemService(Context.BLUETOOTH_SERVICE);
1399 }
1400 if (mBluetoothDevice != null) {
1401 int btClass = mBluetoothDevice.getRemoteClass(address);
1402 if (BluetoothClass.Device.Major.getDeviceMajor(btClass) == BluetoothClass.Device.Major.AUDIO_VIDEO) {
1403 switch (BluetoothClass.Device.getDevice(btClass)) {
1404 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET:
1405 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE:
1406 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET;
1407 break;
1408 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO:
1409 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT;
1410 break;
1411 default:
1412 break;
1413 }
1414 }
1415 }
1416
1417 boolean isConnected = (mConnectedDevices.containsKey(device) &&
1418 ((String)mConnectedDevices.get(device)).equals(address));
1419
1420 if (isConnected && state != BluetoothHeadset.STATE_CONNECTED) {
1421 AudioSystem.setDeviceConnectionState(device,
Eric Laurenta553c252009-07-17 12:17:14 -07001422 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1423 address);
Eric Laurentd5603c12009-08-06 08:49:39 -07001424 mConnectedDevices.remove(device);
1425 } else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) {
1426 AudioSystem.setDeviceConnectionState(device,
Eric Laurenta553c252009-07-17 12:17:14 -07001427 AudioSystem.DEVICE_STATE_AVAILABLE,
1428 address);
Eric Laurentd5603c12009-08-06 08:49:39 -07001429 mConnectedDevices.put( new Integer(device), address);
Eric Laurenta553c252009-07-17 12:17:14 -07001430 }
1431 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) {
1432 int state = intent.getIntExtra("state", 0);
1433 if ((state & BIT_HEADSET) == 0 &&
1434 (mHeadsetState & BIT_HEADSET) != 0) {
1435 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1436 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1437 "");
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001438 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET);
Eric Laurenta553c252009-07-17 12:17:14 -07001439 } else if ((state & BIT_HEADSET) != 0 &&
1440 (mHeadsetState & BIT_HEADSET) == 0) {
1441 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET,
1442 AudioSystem.DEVICE_STATE_AVAILABLE,
1443 "");
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001444 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), "");
Eric Laurenta553c252009-07-17 12:17:14 -07001445 }
1446 if ((state & BIT_HEADSET_NO_MIC) == 0 &&
1447 (mHeadsetState & BIT_HEADSET_NO_MIC) != 0) {
1448 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1449 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1450 "");
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001451 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE);
Eric Laurenta553c252009-07-17 12:17:14 -07001452 } else if ((state & BIT_HEADSET_NO_MIC) != 0 &&
1453 (mHeadsetState & BIT_HEADSET_NO_MIC) == 0) {
1454 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE,
1455 AudioSystem.DEVICE_STATE_AVAILABLE,
1456 "");
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001457 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), "");
Eric Laurenta553c252009-07-17 12:17:14 -07001458 }
1459 if ((state & BIT_TTY) == 0 &&
1460 (mHeadsetState & BIT_TTY) != 0) {
1461 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_TTY,
1462 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1463 "");
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001464 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_TTY);
Eric Laurenta553c252009-07-17 12:17:14 -07001465 } else if ((state & BIT_TTY) != 0 &&
1466 (mHeadsetState & BIT_TTY) == 0) {
1467 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_TTY,
1468 AudioSystem.DEVICE_STATE_AVAILABLE,
1469 "");
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001470 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_TTY), "");
Eric Laurenta553c252009-07-17 12:17:14 -07001471 }
1472 if ((state & BIT_FM_HEADSET) == 0 &&
1473 (mHeadsetState & BIT_FM_HEADSET) != 0) {
1474 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_HEADPHONE,
1475 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1476 "");
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001477 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_FM_HEADPHONE);
Eric Laurenta553c252009-07-17 12:17:14 -07001478 } else if ((state & BIT_FM_HEADSET) != 0 &&
1479 (mHeadsetState & BIT_FM_HEADSET) == 0) {
1480 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_HEADPHONE,
1481 AudioSystem.DEVICE_STATE_AVAILABLE,
1482 "");
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001483 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_FM_HEADPHONE), "");
Eric Laurenta553c252009-07-17 12:17:14 -07001484 }
1485 if ((state & BIT_FM_SPEAKER) == 0 &&
1486 (mHeadsetState & BIT_FM_SPEAKER) != 0) {
1487 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_SPEAKER,
1488 AudioSystem.DEVICE_STATE_UNAVAILABLE,
1489 "");
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001490 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_FM_SPEAKER);
Eric Laurenta553c252009-07-17 12:17:14 -07001491 } else if ((state & BIT_FM_SPEAKER) != 0 &&
1492 (mHeadsetState & BIT_FM_SPEAKER) == 0) {
1493 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_FM_SPEAKER,
1494 AudioSystem.DEVICE_STATE_AVAILABLE,
1495 "");
Eric Laurentc42ac9d2009-07-29 08:53:03 -07001496 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_FM_SPEAKER), "");
Eric Laurenta553c252009-07-17 12:17:14 -07001497 }
1498 mHeadsetState = state;
1499 }
1500 }
1501 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502}