blob: ee41021d0a051e43a800b9c265032fca46e42fad [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;
23import android.content.pm.PackageManager;
Jason Parekhb1096152009-03-24 17:48:25 -070024import android.database.ContentObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.media.MediaPlayer.OnCompletionListener;
26import android.media.MediaPlayer.OnErrorListener;
27import android.os.Binder;
28import android.os.Environment;
29import android.os.Handler;
30import android.os.IBinder;
31import android.os.Looper;
32import android.os.Message;
33import android.os.RemoteException;
34import android.os.ServiceManager;
35import android.provider.Settings;
36import android.provider.Settings.System;
37import android.util.Log;
38import android.view.VolumePanel;
39
40import com.android.internal.telephony.ITelephony;
41
42import java.io.IOException;
43import java.util.ArrayList;
44
45
46/**
47 * The implementation of the volume manager service.
48 * <p>
49 * This implementation focuses on delivering a responsive UI. Most methods are
50 * asynchronous to external calls. For example, the task of setting a volume
51 * will update our internal state, but in a separate thread will set the system
52 * volume and later persist to the database. Similarly, setting the ringer mode
53 * will update the state and broadcast a change and in a separate thread later
54 * persist the ringer mode.
55 *
56 * @hide
57 */
58public class AudioService extends IAudioService.Stub {
59
60 private static final String TAG = "AudioService";
61
62 /** How long to delay before persisting a change in volume/ringer mode. */
63 private static final int PERSIST_DELAY = 3000;
64
65 private Context mContext;
66 private ContentResolver mContentResolver;
67
68 /** The UI */
69 private VolumePanel mVolumePanel;
70
71 // sendMsg() flags
72 /** Used when a message should be shared across all stream types. */
73 private static final int SHARED_MSG = -1;
74 /** If the msg is already queued, replace it with this one. */
75 private static final int SENDMSG_REPLACE = 0;
76 /** If the msg is already queued, ignore this one and leave the old. */
77 private static final int SENDMSG_NOOP = 1;
78 /** If the msg is already queued, queue this one and leave the old. */
79 private static final int SENDMSG_QUEUE = 2;
80
81 // AudioHandler message.whats
82 private static final int MSG_SET_SYSTEM_VOLUME = 0;
83 private static final int MSG_PERSIST_VOLUME = 1;
84 private static final int MSG_PERSIST_RINGER_MODE = 3;
85 private static final int MSG_PERSIST_VIBRATE_SETTING = 4;
86 private static final int MSG_MEDIA_SERVER_DIED = 5;
87 private static final int MSG_MEDIA_SERVER_STARTED = 6;
88 private static final int MSG_PLAY_SOUND_EFFECT = 7;
89
90 /** @see AudioSystemThread */
91 private AudioSystemThread mAudioSystemThread;
92 /** @see AudioHandler */
93 private AudioHandler mAudioHandler;
94 /** @see VolumeStreamState */
95 private VolumeStreamState[] mStreamStates;
Jason Parekhb1096152009-03-24 17:48:25 -070096 private SettingsObserver mSettingsObserver;
97
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 private boolean mMicMute;
99 private int mMode;
100 private int[] mRoutes = new int[AudioSystem.NUM_MODES];
101 private Object mSettingsLock = new Object();
102 private boolean mMediaServerOk;
Eric Laurentb9c9d262009-05-06 08:13:20 -0700103 private boolean mSpeakerIsOn;
104 private boolean mBluetoothScoIsConnected;
105 private boolean mHeadsetIsConnected;
106 private boolean mBluetoothA2dpIsConnected;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107
108 private SoundPool mSoundPool;
109 private Object mSoundEffectsLock = new Object();
110 private static final int NUM_SOUNDPOOL_CHANNELS = 4;
111 private static final int SOUND_EFFECT_VOLUME = 1000;
112
113 /* Sound effect file names */
114 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/";
115 private static final String[] SOUND_EFFECT_FILES = new String[] {
116 "Effect_Tick.ogg",
117 "KeypressStandard.ogg",
118 "KeypressSpacebar.ogg",
119 "KeypressDelete.ogg",
120 "KeypressReturn.ogg"
121 };
122
123 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to
124 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect
125 * uses soundpool (second column) */
126 private int[][] SOUND_EFFECT_FILES_MAP = new int[][] {
127 {0, -1}, // FX_KEY_CLICK
128 {0, -1}, // FX_FOCUS_NAVIGATION_UP
129 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN
130 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT
131 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT
132 {1, -1}, // FX_KEYPRESS_STANDARD
133 {2, -1}, // FX_KEYPRESS_SPACEBAR
134 {3, -1}, // FX_FOCUS_DELETE
135 {4, -1} // FX_FOCUS_RETURN
136 };
137
138 private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() {
139 public void onError(int error) {
140 switch (error) {
141 case AudioSystem.AUDIO_STATUS_SERVER_DIED:
142 if (mMediaServerOk) {
143 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
144 null, 1500);
145 }
146 break;
147 case AudioSystem.AUDIO_STATUS_OK:
148 if (!mMediaServerOk) {
149 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0,
150 null, 0);
151 }
152 break;
153 default:
154 break;
155 }
156 }
157 };
158
159 /**
160 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL},
161 * {@link AudioManager#RINGER_MODE_SILENT}, or
162 * {@link AudioManager#RINGER_MODE_VIBRATE}.
163 */
164 private int mRingerMode;
165
Eric Laurent9bcf4012009-06-12 06:09:28 -0700166 /** @see System#MODE_RINGER_STREAMS_AFFECTED */
167 private int mRingerModeAffectedStreams;
168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 /** @see System#MUTE_STREAMS_AFFECTED */
170 private int mMuteAffectedStreams;
171
172 /**
173 * Has multiple bits per vibrate type to indicate the type's vibrate
174 * setting. See {@link #setVibrateSetting(int, int)}.
175 * <p>
176 * NOTE: This is not the final decision of whether vibrate is on/off for the
177 * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}.
178 */
179 private int mVibrateSetting;
180
181 ///////////////////////////////////////////////////////////////////////////
182 // Construction
183 ///////////////////////////////////////////////////////////////////////////
184
185 /** @hide */
186 public AudioService(Context context) {
187 mContext = context;
188 mContentResolver = context.getContentResolver();
189 mVolumePanel = new VolumePanel(context, this);
Jason Parekhb1096152009-03-24 17:48:25 -0700190 mSettingsObserver = new SettingsObserver();
191
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 createAudioSystemThread();
193 createStreamStates();
194 readPersistedSettings();
195 readAudioSettings();
196 mMediaServerOk = true;
197 AudioSystem.setErrorCallback(mAudioSystemCallback);
198 loadSoundEffects();
Eric Laurentb9c9d262009-05-06 08:13:20 -0700199 mSpeakerIsOn = false;
200 mBluetoothScoIsConnected = false;
201 mHeadsetIsConnected = false;
202 mBluetoothA2dpIsConnected = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 }
204
205 private void createAudioSystemThread() {
206 mAudioSystemThread = new AudioSystemThread();
207 mAudioSystemThread.start();
208 waitForAudioHandlerCreation();
209 }
210
211 /** Waits for the volume handler to be created by the other thread. */
212 private void waitForAudioHandlerCreation() {
213 synchronized(this) {
214 while (mAudioHandler == null) {
215 try {
216 // Wait for mAudioHandler to be set by the other thread
217 wait();
218 } catch (InterruptedException e) {
219 Log.e(TAG, "Interrupted while waiting on volume handler.");
220 }
221 }
222 }
223 }
224
225 private void createStreamStates() {
226 final int[] volumeLevelsPhone =
227 createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_VOICE_CALL]);
228 final int[] volumeLevelsCoarse =
229 createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_SYSTEM]);
230 final int[] volumeLevelsFine =
231 createVolumeLevels(0, AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_MUSIC]);
232 final int[] volumeLevelsBtPhone =
233 createVolumeLevels(0,
234 AudioManager.MAX_STREAM_VOLUME[AudioManager.STREAM_BLUETOOTH_SCO]);
235
236 int numStreamTypes = AudioSystem.getNumStreamTypes();
237 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes];
238
239 for (int i = 0; i < numStreamTypes; i++) {
240 final int[] levels;
241
242 switch (i) {
243
244 case AudioSystem.STREAM_MUSIC:
245 levels = volumeLevelsFine;
246 break;
247
248 case AudioSystem.STREAM_VOICE_CALL:
249 levels = volumeLevelsPhone;
250 break;
251
252 case AudioSystem.STREAM_BLUETOOTH_SCO:
253 levels = volumeLevelsBtPhone;
254 break;
255
256 default:
257 levels = volumeLevelsCoarse;
258 break;
259 }
260
261 if (i == AudioSystem.STREAM_BLUETOOTH_SCO) {
262 streams[i] = new VolumeStreamState(AudioManager.DEFAULT_STREAM_VOLUME[i], i,levels);
263 } else {
264 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[i], i, levels);
265 }
266 }
267 }
268
269 private static int[] createVolumeLevels(int offset, int numlevels) {
270 double curve = 1.0f; // 1.4f
271 int [] volumes = new int[numlevels + offset];
272 for (int i = 0; i < offset; i++) {
273 volumes[i] = 0;
274 }
275
276 double val = 0;
277 double max = Math.pow(numlevels - 1, curve);
278 for (int i = 0; i < numlevels; i++) {
279 val = Math.pow(i, curve) / max;
280 volumes[offset + i] = (int) (val * 100.0f);
281 }
282 return volumes;
283 }
284
285 private void readPersistedSettings() {
286 final ContentResolver cr = mContentResolver;
287
288 mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800289
290 mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0);
291
Eric Laurent9bcf4012009-06-12 06:09:28 -0700292 mRingerModeAffectedStreams = Settings.System.getInt(cr,
293 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
294 ((1 << AudioManager.STREAM_RING)|(1 << AudioManager.STREAM_NOTIFICATION)|(1 << AudioManager.STREAM_SYSTEM)));
295
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800296 mMuteAffectedStreams = System.getInt(cr,
297 System.MUTE_STREAMS_AFFECTED,
298 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
299
300 // Each stream will read its own persisted settings
301
302 // Broadcast the sticky intent
303 broadcastRingerMode();
304
305 // Broadcast vibrate settings
306 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER);
307 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION);
308 }
309
310 private void readAudioSettings() {
311 synchronized (mSettingsLock) {
312 mMicMute = AudioSystem.isMicrophoneMuted();
313 mMode = AudioSystem.getMode();
314 for (int mode = 0; mode < AudioSystem.NUM_MODES; mode++) {
315 mRoutes[mode] = AudioSystem.getRouting(mode);
316 }
317 }
318 }
319
320 private void applyAudioSettings() {
321 synchronized (mSettingsLock) {
322 AudioSystem.muteMicrophone(mMicMute);
323 AudioSystem.setMode(mMode);
324 for (int mode = 0; mode < AudioSystem.NUM_MODES; mode++) {
325 AudioSystem.setRouting(mode, mRoutes[mode], AudioSystem.ROUTE_ALL);
326 }
327 }
328 }
329
330 ///////////////////////////////////////////////////////////////////////////
331 // IPC methods
332 ///////////////////////////////////////////////////////////////////////////
333
334 /** @see AudioManager#adjustVolume(int, int) */
335 public void adjustVolume(int direction, int flags) {
336 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags);
337 }
338
339 /** @see AudioManager#adjustVolume(int, int, int) */
340 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) {
341
342 int streamType = getActiveStreamType(suggestedStreamType);
343
344 // Don't play sound on other streams
345 if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
346 flags &= ~AudioManager.FLAG_PLAY_SOUND;
347 }
348
349 adjustStreamVolume(streamType, direction, flags);
350 }
351
352 /** @see AudioManager#adjustStreamVolume(int, int, int) */
353 public void adjustStreamVolume(int streamType, int direction, int flags) {
354 ensureValidDirection(direction);
355 ensureValidStreamType(streamType);
356
357 boolean notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
358 Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1;
359 if (notificationsUseRingVolume && streamType == AudioManager.STREAM_NOTIFICATION) {
360 // Redirect the volume change to the ring stream
361 streamType = AudioManager.STREAM_RING;
362 }
363
364 VolumeStreamState streamState = mStreamStates[streamType];
365 final int oldIndex = streamState.mIndex;
366 boolean adjustVolume = true;
367
368 // If either the client forces allowing ringer modes for this adjustment,
369 // or the stream type is one that is affected by ringer modes
370 if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0
371 || streamType == AudioManager.STREAM_RING) {
372 // Check if the ringer mode changes with this volume adjustment. If
373 // it does, it will handle adjusting the volume, so we won't below
374 adjustVolume = checkForRingerModeChange(oldIndex, direction);
375 }
376
377 if (adjustVolume && streamState.adjustIndex(direction)) {
378
379 boolean alsoUpdateNotificationVolume = notificationsUseRingVolume &&
380 streamType == AudioManager.STREAM_RING;
381 if (alsoUpdateNotificationVolume) {
382 mStreamStates[AudioManager.STREAM_NOTIFICATION].adjustIndex(direction);
383 }
384
385 // Post message to set system volume (it in turn will post a message
386 // to persist). Do not change volume if stream is muted.
387 if (streamState.muteCount() == 0) {
388 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
389 streamState, 0);
390
391 if (alsoUpdateNotificationVolume) {
392 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, AudioManager.STREAM_NOTIFICATION,
393 SENDMSG_NOOP, 0, 0, mStreamStates[AudioManager.STREAM_NOTIFICATION], 0);
394 }
395 }
396 }
397
398 // UI
399 mVolumePanel.postVolumeChanged(streamType, flags);
400 // Broadcast Intent
401 sendVolumeUpdate(streamType);
402 }
403
404 /** @see AudioManager#setStreamVolume(int, int, int) */
405 public void setStreamVolume(int streamType, int index, int flags) {
406 ensureValidStreamType(streamType);
407 syncRingerAndNotificationStreamVolume(streamType, index, false);
408
Eric Laurent9bcf4012009-06-12 06:09:28 -0700409 setStreamVolumeInt(streamType, index, false, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410
411 // UI, etc.
412 mVolumePanel.postVolumeChanged(streamType, flags);
413 // Broadcast Intent
414 sendVolumeUpdate(streamType);
415 }
416
417 private void sendVolumeUpdate(int streamType) {
418 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
419 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
420 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, getStreamVolume(streamType));
421
422 // Currently, sending the intent only when the stream is BLUETOOTH_SCO
423 if (streamType == AudioManager.STREAM_BLUETOOTH_SCO) {
424 mContext.sendBroadcast(intent);
425 }
426 }
427
428 /**
429 * Sync the STREAM_RING and STREAM_NOTIFICATION volumes if mandated by the
430 * value in Settings.
431 *
432 * @param streamType Type of the stream
433 * @param index Volume index for the stream
434 * @param force If true, set the volume even if the current and desired
435 * volume as same
436 */
437 private void syncRingerAndNotificationStreamVolume(int streamType, int index, boolean force) {
438 boolean notificationsUseRingVolume = Settings.System.getInt(mContentResolver,
439 Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1) == 1;
440 if (notificationsUseRingVolume) {
441 if (streamType == AudioManager.STREAM_NOTIFICATION) {
442 // Redirect the volume change to the ring stream
443 streamType = AudioManager.STREAM_RING;
444 }
445 if (streamType == AudioManager.STREAM_RING) {
446 // One-off to sync notification volume to ringer volume
Eric Laurent9bcf4012009-06-12 06:09:28 -0700447 setStreamVolumeInt(AudioManager.STREAM_NOTIFICATION, index, force, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 }
449 }
450 }
451
452
453 /**
454 * Sets the stream state's index, and posts a message to set system volume.
455 * This will not call out to the UI. Assumes a valid stream type.
456 *
457 * @param streamType Type of the stream
458 * @param index Desired volume index of the stream
459 * @param force If true, set the volume even if the desired volume is same
460 * as the current volume.
Eric Laurent9bcf4012009-06-12 06:09:28 -0700461 * @param lastAudible If true, stores new index as last audible one
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 */
Eric Laurent9bcf4012009-06-12 06:09:28 -0700463 private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 VolumeStreamState streamState = mStreamStates[streamType];
Eric Laurent9bcf4012009-06-12 06:09:28 -0700465 if (streamState.setIndex(index, lastAudible) || force) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800466 // Post message to set system volume (it in turn will post a message
467 // to persist). Do not change volume if stream is muted.
468 if (streamState.muteCount() == 0) {
469 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0,
470 streamState, 0);
471 }
472 }
473 }
474
475 /** @see AudioManager#setStreamSolo(int, boolean) */
476 public void setStreamSolo(int streamType, boolean state, IBinder cb) {
477 for (int stream = 0; stream < mStreamStates.length; stream++) {
478 if (!isStreamAffectedByMute(stream) || stream == streamType) continue;
479 // Bring back last audible volume
480 mStreamStates[stream].mute(cb, state);
481 }
482 }
483
484 /** @see AudioManager#setStreamMute(int, boolean) */
485 public void setStreamMute(int streamType, boolean state, IBinder cb) {
486 if (isStreamAffectedByMute(streamType)) {
487 mStreamStates[streamType].mute(cb, state);
488 }
489 }
490
491 /** @see AudioManager#getStreamVolume(int) */
492 public int getStreamVolume(int streamType) {
493 ensureValidStreamType(streamType);
494 return mStreamStates[streamType].mIndex;
495 }
496
497 /** @see AudioManager#getStreamMaxVolume(int) */
498 public int getStreamMaxVolume(int streamType) {
499 ensureValidStreamType(streamType);
500 return mStreamStates[streamType].getMaxIndex();
501 }
502
503 /** @see AudioManager#getRingerMode() */
504 public int getRingerMode() {
505 return mRingerMode;
506 }
507
508 /** @see AudioManager#setRingerMode(int) */
509 public void setRingerMode(int ringerMode) {
510 if (ringerMode != mRingerMode) {
Eric Laurent4050c932009-07-08 02:52:14 -0700511 setRingerModeInt(ringerMode, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512
513 // Send sticky broadcast
514 broadcastRingerMode();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515 }
516 }
517
Eric Laurent4050c932009-07-08 02:52:14 -0700518 private void setRingerModeInt(int ringerMode, boolean persist) {
Jason Parekhb1096152009-03-24 17:48:25 -0700519 mRingerMode = ringerMode;
520
521 // Adjust volumes via posting message
522 int numStreamTypes = AudioSystem.getNumStreamTypes();
523 if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
524 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
525 if (!isStreamAffectedByRingerMode(streamType)) continue;
526 // Bring back last audible volume
527 setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
Eric Laurent9bcf4012009-06-12 06:09:28 -0700528 false, false);
Jason Parekhb1096152009-03-24 17:48:25 -0700529 }
530 } else {
531 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
Eric Laurent9bcf4012009-06-12 06:09:28 -0700532 if (isStreamAffectedByRingerMode(streamType)) {
533 // Either silent or vibrate, either way volume is 0
534 setStreamVolumeInt(streamType, 0, false, false);
535 } else {
536 // restore stream volume in the case the stream changed from affected
537 // to non affected by ringer mode. Does not arm to do it for streams that
538 // are not affected as well.
539 setStreamVolumeInt(streamType, mStreamStates[streamType].mLastAudibleIndex,
540 false, false);
541 }
Jason Parekhb1096152009-03-24 17:48:25 -0700542 }
543 }
544
545 // Post a persist ringer mode msg
Eric Laurent4050c932009-07-08 02:52:14 -0700546 if (persist) {
547 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG,
548 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY);
549 }
Jason Parekhb1096152009-03-24 17:48:25 -0700550 }
551
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 /** @see AudioManager#shouldVibrate(int) */
553 public boolean shouldVibrate(int vibrateType) {
554
555 switch (getVibrateSetting(vibrateType)) {
556
557 case AudioManager.VIBRATE_SETTING_ON:
558 return mRingerMode != AudioManager.RINGER_MODE_SILENT;
559
560 case AudioManager.VIBRATE_SETTING_ONLY_SILENT:
561 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
562
563 case AudioManager.VIBRATE_SETTING_OFF:
564 // Phone ringer should always vibrate in vibrate mode
565 if (vibrateType == AudioManager.VIBRATE_TYPE_RINGER) {
566 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE;
567 }
568
569 default:
570 return false;
571 }
572 }
573
574 /** @see AudioManager#getVibrateSetting(int) */
575 public int getVibrateSetting(int vibrateType) {
576 return (mVibrateSetting >> (vibrateType * 2)) & 3;
577 }
578
579 /** @see AudioManager#setVibrateSetting(int, int) */
580 public void setVibrateSetting(int vibrateType, int vibrateSetting) {
581
582 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting);
583
584 // Broadcast change
585 broadcastVibrateSetting(vibrateType);
586
587 // Post message to set ringer mode (it in turn will post a message
588 // to persist)
589 sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0,
590 null, 0);
591 }
592
593 /**
594 * @see #setVibrateSetting(int, int)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 */
596 public static int getValueForVibrateSetting(int existingValue, int vibrateType,
597 int vibrateSetting) {
598
599 // First clear the existing setting. Each vibrate type has two bits in
600 // the value. Note '3' is '11' in binary.
601 existingValue &= ~(3 << (vibrateType * 2));
602
603 // Set into the old value
604 existingValue |= (vibrateSetting & 3) << (vibrateType * 2);
605
606 return existingValue;
607 }
608
609 /** @see AudioManager#setMicrophoneMute(boolean) */
610 public void setMicrophoneMute(boolean on) {
611 if (!checkAudioSettingsPermission("setMicrophoneMute()")) {
612 return;
613 }
614 synchronized (mSettingsLock) {
615 if (on != mMicMute) {
616 AudioSystem.muteMicrophone(on);
617 mMicMute = on;
618 }
619 }
620 }
621
622 /** @see AudioManager#isMicrophoneMute() */
623 public boolean isMicrophoneMute() {
624 return mMicMute;
625 }
626
627 /** @see AudioManager#setMode(int) */
628 public void setMode(int mode) {
629 if (!checkAudioSettingsPermission("setMode()")) {
630 return;
631 }
632 synchronized (mSettingsLock) {
633 if (mode != mMode) {
Eric Laurentb9c9d262009-05-06 08:13:20 -0700634 if (AudioSystem.setMode(mode) == AudioSystem.AUDIO_STATUS_OK) {
635 mMode = mode;
636 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 }
638 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
639 int index = mStreamStates[streamType].mIndex;
640 syncRingerAndNotificationStreamVolume(streamType, index, true);
Eric Laurent9bcf4012009-06-12 06:09:28 -0700641 setStreamVolumeInt(streamType, index, true, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 }
643 }
644
645 /** @see AudioManager#getMode() */
646 public int getMode() {
647 return mMode;
648 }
649
650 /** @see AudioManager#setRouting(int, int, int) */
651 public void setRouting(int mode, int routes, int mask) {
Eric Laurentb9c9d262009-05-06 08:13:20 -0700652 int incallMask = 0;
653 int ringtoneMask = 0;
654 int normalMask = 0;
655
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 if (!checkAudioSettingsPermission("setRouting()")) {
657 return;
658 }
659 synchronized (mSettingsLock) {
Eric Laurentb9c9d262009-05-06 08:13:20 -0700660 // Temporary fix for issue #1713090 until audio routing is refactored in eclair release.
661 // mode AudioSystem.MODE_INVALID is used only by the following AudioManager methods:
662 // setWiredHeadsetOn(), setBluetoothA2dpOn(), setBluetoothScoOn() and setSpeakerphoneOn().
663 // If applications are using AudioManager.setRouting() that is now deprecated, the routing
664 // command will be ignored.
665 if (mode == AudioSystem.MODE_INVALID) {
666 switch (mask) {
667 case AudioSystem.ROUTE_SPEAKER:
668 // handle setSpeakerphoneOn()
669 if (routes != 0 && !mSpeakerIsOn) {
670 mSpeakerIsOn = true;
671 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER;
672 incallMask = AudioSystem.ROUTE_ALL;
Mike Lockwoodd1f362f2009-06-16 08:23:13 -0400673 } else if (routes == 0 && mSpeakerIsOn) {
Eric Laurentb9c9d262009-05-06 08:13:20 -0700674 mSpeakerIsOn = false;
675 if (mBluetoothScoIsConnected) {
676 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_BLUETOOTH_SCO;
677 } else if (mHeadsetIsConnected) {
678 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET;
679 } else {
680 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE;
681 }
682 incallMask = AudioSystem.ROUTE_ALL;
683 }
684 break;
685
686 case AudioSystem.ROUTE_BLUETOOTH_SCO:
687 // handle setBluetoothScoOn()
688 if (routes != 0 && !mBluetoothScoIsConnected) {
689 mBluetoothScoIsConnected = true;
690 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_BLUETOOTH_SCO;
691 mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
692 AudioSystem.ROUTE_BLUETOOTH_SCO;
693 mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
694 AudioSystem.ROUTE_BLUETOOTH_SCO;
695 incallMask = AudioSystem.ROUTE_ALL;
696 // A2DP has higher priority than SCO headset, so headset connect/disconnect events
697 // should not affect A2DP routing
698 ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
699 normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
Mike Lockwoodd1f362f2009-06-16 08:23:13 -0400700 } else if (routes == 0 && mBluetoothScoIsConnected) {
Eric Laurentb9c9d262009-05-06 08:13:20 -0700701 mBluetoothScoIsConnected = false;
702 if (mHeadsetIsConnected) {
703 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET;
704 mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
705 (AudioSystem.ROUTE_HEADSET|AudioSystem.ROUTE_SPEAKER);
706 mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
707 AudioSystem.ROUTE_HEADSET;
708 } else {
709 if (mSpeakerIsOn) {
710 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER;
711 } else {
712 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE;
713 }
714 mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
715 AudioSystem.ROUTE_SPEAKER;
716 mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
717 AudioSystem.ROUTE_SPEAKER;
718 }
719 incallMask = AudioSystem.ROUTE_ALL;
720 // A2DP has higher priority than SCO headset, so headset connect/disconnect events
721 // should not affect A2DP routing
722 ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
723 normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
724 }
725 break;
726
727 case AudioSystem.ROUTE_HEADSET:
728 // handle setWiredHeadsetOn()
729 if (routes != 0 && !mHeadsetIsConnected) {
730 mHeadsetIsConnected = true;
731 // do not act upon headset connection if bluetooth SCO is connected to match phone app behavior
732 if (!mBluetoothScoIsConnected) {
733 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET;
734 mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
735 (AudioSystem.ROUTE_HEADSET|AudioSystem.ROUTE_SPEAKER);
736 mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
737 AudioSystem.ROUTE_HEADSET;
738 incallMask = AudioSystem.ROUTE_ALL;
739 // A2DP has higher priority than wired headset, so headset connect/disconnect events
740 // should not affect A2DP routing
741 ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
742 normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
743 }
Mike Lockwoodd1f362f2009-06-16 08:23:13 -0400744 } else if (routes == 0 && mHeadsetIsConnected) {
Eric Laurentb9c9d262009-05-06 08:13:20 -0700745 mHeadsetIsConnected = false;
746 // do not act upon headset disconnection if bluetooth SCO is connected to match phone app behavior
747 if (!mBluetoothScoIsConnected) {
748 if (mSpeakerIsOn) {
749 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER;
750 } else {
751 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE;
752 }
753 mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
754 AudioSystem.ROUTE_SPEAKER;
755 mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) |
756 AudioSystem.ROUTE_SPEAKER;
757
758 incallMask = AudioSystem.ROUTE_ALL;
759 // A2DP has higher priority than wired headset, so headset connect/disconnect events
760 // should not affect A2DP routing
761 ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
762 normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
763 }
764 }
765 break;
766
767 case AudioSystem.ROUTE_BLUETOOTH_A2DP:
768 // handle setBluetoothA2dpOn()
769 if (routes != 0 && !mBluetoothA2dpIsConnected) {
770 mBluetoothA2dpIsConnected = true;
771 mRoutes[AudioSystem.MODE_RINGTONE] |= AudioSystem.ROUTE_BLUETOOTH_A2DP;
772 mRoutes[AudioSystem.MODE_NORMAL] |= AudioSystem.ROUTE_BLUETOOTH_A2DP;
773 // the audio flinger chooses A2DP as a higher priority,
774 // so there is no need to disable other routes.
775 ringtoneMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
776 normalMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
Mike Lockwoodd1f362f2009-06-16 08:23:13 -0400777 } else if (routes == 0 && mBluetoothA2dpIsConnected) {
Eric Laurentb9c9d262009-05-06 08:13:20 -0700778 mBluetoothA2dpIsConnected = false;
779 mRoutes[AudioSystem.MODE_RINGTONE] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
780 mRoutes[AudioSystem.MODE_NORMAL] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP;
781 // the audio flinger chooses A2DP as a higher priority,
782 // so there is no need to disable other routes.
783 ringtoneMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
784 normalMask = AudioSystem.ROUTE_BLUETOOTH_A2DP;
785 }
786 break;
787 }
788
789 // incallMask is != 0 means we must apply ne routing to MODE_IN_CALL mode
790 if (incallMask != 0) {
791 AudioSystem.setRouting(AudioSystem.MODE_IN_CALL,
792 mRoutes[AudioSystem.MODE_IN_CALL],
793 incallMask);
794 }
795 // ringtoneMask is != 0 means we must apply ne routing to MODE_RINGTONE mode
796 if (ringtoneMask != 0) {
797 AudioSystem.setRouting(AudioSystem.MODE_RINGTONE,
798 mRoutes[AudioSystem.MODE_RINGTONE],
799 ringtoneMask);
800 }
801 // normalMask is != 0 means we must apply ne routing to MODE_NORMAL mode
802 if (normalMask != 0) {
803 AudioSystem.setRouting(AudioSystem.MODE_NORMAL,
804 mRoutes[AudioSystem.MODE_NORMAL],
805 normalMask);
806 }
807
808 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE);
809 int index = mStreamStates[streamType].mIndex;
810 syncRingerAndNotificationStreamVolume(streamType, index, true);
Eric Laurent9bcf4012009-06-12 06:09:28 -0700811 setStreamVolumeInt(streamType, index, true, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 }
814 }
815
816 /** @see AudioManager#getRouting(int) */
817 public int getRouting(int mode) {
818 return mRoutes[mode];
819 }
820
821 /** @see AudioManager#isMusicActive() */
822 public boolean isMusicActive() {
823 return AudioSystem.isMusicActive();
824 }
825
826 /** @see AudioManager#setParameter(String, String) */
827 public void setParameter(String key, String value) {
828 AudioSystem.setParameter(key, value);
829 }
830
831 /** @see AudioManager#playSoundEffect(int) */
832 public void playSoundEffect(int effectType) {
833 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
834 effectType, SOUND_EFFECT_VOLUME, null, 0);
835 }
836
837 /** @see AudioManager#playSoundEffect(int, float) */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 public void playSoundEffectVolume(int effectType, float volume) {
839 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP,
840 effectType, (int) (volume * 1000), null, 0);
841 }
842
843 /**
844 * Loads samples into the soundpool.
845 * This method must be called at when sound effects are enabled
846 */
847 public boolean loadSoundEffects() {
848 synchronized (mSoundEffectsLock) {
849 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0);
850 if (mSoundPool == null) {
851 return false;
852 }
853 /*
854 * poolId table: The value -1 in this table indicates that corresponding
855 * file (same index in SOUND_EFFECT_FILES[] has not been loaded.
856 * Once loaded, the value in poolId is the sample ID and the same
857 * sample can be reused for another effect using the same file.
858 */
859 int[] poolId = new int[SOUND_EFFECT_FILES.length];
860 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
861 poolId[fileIdx] = -1;
862 }
863 /*
864 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded.
865 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0:
866 * this indicates we have a valid sample loaded for this effect.
867 */
868 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
869 // Do not load sample if this effect uses the MediaPlayer
870 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) {
871 continue;
872 }
873 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) {
874 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]];
875 int sampleId = mSoundPool.load(filePath, 0);
876 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId;
877 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId;
878 if (sampleId <= 0) {
879 Log.w(TAG, "Soundpool could not load file: "+filePath);
880 }
881 } else {
882 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]];
883 }
884 }
885 }
886
887 return true;
888 }
889
890 /**
891 * Unloads samples from the sound pool.
892 * This method can be called to free some memory when
893 * sound effects are disabled.
894 */
895 public void unloadSoundEffects() {
896 synchronized (mSoundEffectsLock) {
897 if (mSoundPool == null) {
898 return;
899 }
900 int[] poolId = new int[SOUND_EFFECT_FILES.length];
901 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) {
902 poolId[fileIdx] = 0;
903 }
904
905 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) {
906 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) {
907 continue;
908 }
909 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) {
910 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]);
911 SOUND_EFFECT_FILES_MAP[effect][1] = -1;
912 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1;
913 }
914 }
915 mSoundPool = null;
916 }
917 }
918
Eric Laurent4050c932009-07-08 02:52:14 -0700919 /** @see AudioManager#reloadAudioSettings() */
920 public void reloadAudioSettings() {
921 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
922 readPersistedSettings();
923
924 // restore volume settings
925 int numStreamTypes = AudioSystem.getNumStreamTypes();
926 for (int streamType = 0; streamType < numStreamTypes; streamType++) {
927 VolumeStreamState streamState = mStreamStates[streamType];
928
929 // there is no volume setting for STREAM_BLUETOOTH_SCO
930 if (streamType != AudioSystem.STREAM_BLUETOOTH_SCO) {
931 String settingName = System.VOLUME_SETTINGS[streamType];
932 String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
933
934 streamState.mIndex = streamState.getValidIndex(Settings.System.getInt(mContentResolver,
935 settingName,
936 AudioManager.DEFAULT_STREAM_VOLUME[streamType]));
937 streamState.mLastAudibleIndex = streamState.getValidIndex(Settings.System.getInt(mContentResolver,
938 lastAudibleSettingName,
939 streamState.mIndex > 0 ? streamState.mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]));
940 }
941 // unmute stream that whas muted but is not affect by mute anymore
942 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
943 int size = streamState.mDeathHandlers.size();
944 for (int i = 0; i < size; i++) {
945 streamState.mDeathHandlers.get(i).mMuteCount = 1;
946 streamState.mDeathHandlers.get(i).mute(false);
947 }
948 }
949 // apply stream volume
950 if (streamState.muteCount() == 0) {
951 AudioSystem.setVolume(streamType, streamState.mVolumes[streamState.mIndex]);
952 }
953 }
954
955 // apply new ringer mode
956 setRingerModeInt(getRingerMode(), false);
957 }
958
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 ///////////////////////////////////////////////////////////////////////////
960 // Internal methods
961 ///////////////////////////////////////////////////////////////////////////
962
963 /**
964 * Checks if the adjustment should change ringer mode instead of just
965 * adjusting volume. If so, this will set the proper ringer mode and volume
966 * indices on the stream states.
967 */
968 private boolean checkForRingerModeChange(int oldIndex, int direction) {
969 boolean adjustVolumeIndex = true;
970 int newRingerMode = mRingerMode;
971
972 if (mRingerMode == AudioManager.RINGER_MODE_NORMAL && oldIndex == 1
973 && direction == AudioManager.ADJUST_LOWER) {
974 newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
975 } else if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) {
976 if (direction == AudioManager.ADJUST_RAISE) {
977 newRingerMode = AudioManager.RINGER_MODE_NORMAL;
978 } else if (direction == AudioManager.ADJUST_LOWER) {
979 newRingerMode = AudioManager.RINGER_MODE_SILENT;
980 }
981 } else if (direction == AudioManager.ADJUST_RAISE
982 && mRingerMode == AudioManager.RINGER_MODE_SILENT) {
983 newRingerMode = AudioManager.RINGER_MODE_VIBRATE;
984 }
985
986 if (newRingerMode != mRingerMode) {
987 setRingerMode(newRingerMode);
988
989 /*
990 * If we are changing ringer modes, do not increment/decrement the
991 * volume index. Instead, the handler for the message above will
992 * take care of changing the index.
993 */
994 adjustVolumeIndex = false;
995 }
996
997 return adjustVolumeIndex;
998 }
999
1000 public boolean isStreamAffectedByRingerMode(int streamType) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07001001 return (mRingerModeAffectedStreams & (1 << streamType)) != 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 }
1003
1004 public boolean isStreamAffectedByMute(int streamType) {
1005 return (mMuteAffectedStreams & (1 << streamType)) != 0;
1006 }
1007
1008 private void ensureValidDirection(int direction) {
1009 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) {
1010 throw new IllegalArgumentException("Bad direction " + direction);
1011 }
1012 }
1013
1014 private void ensureValidStreamType(int streamType) {
1015 if (streamType < 0 || streamType >= mStreamStates.length) {
1016 throw new IllegalArgumentException("Bad stream type " + streamType);
1017 }
1018 }
1019
1020 private int getActiveStreamType(int suggestedStreamType) {
1021 boolean isOffhook = false;
1022 try {
1023 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone"));
1024 if (phone != null) isOffhook = phone.isOffhook();
1025 } catch (RemoteException e) {
1026 Log.w(TAG, "Couldn't connect to phone service", e);
1027 }
1028
1029 if ((getRouting(AudioSystem.MODE_IN_CALL) & AudioSystem.ROUTE_BLUETOOTH_SCO) != 0) {
1030 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO...");
1031 return AudioSystem.STREAM_BLUETOOTH_SCO;
1032 } else if (isOffhook) {
1033 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL...");
1034 return AudioSystem.STREAM_VOICE_CALL;
1035 } else if (AudioSystem.isMusicActive()) {
1036 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC...");
1037 return AudioSystem.STREAM_MUSIC;
1038 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
1039 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING...");
1040 return AudioSystem.STREAM_RING;
1041 } else {
1042 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType);
1043 return suggestedStreamType;
1044 }
1045 }
1046
1047 private void broadcastRingerMode() {
1048 // Send sticky broadcast
1049 if (ActivityManagerNative.isSystemReady()) {
1050 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION);
1051 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode);
1052 long origCallerIdentityToken = Binder.clearCallingIdentity();
1053 mContext.sendStickyBroadcast(broadcast);
1054 Binder.restoreCallingIdentity(origCallerIdentityToken);
1055 }
1056 }
1057
1058 private void broadcastVibrateSetting(int vibrateType) {
1059 // Send broadcast
1060 if (ActivityManagerNative.isSystemReady()) {
1061 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
1062 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
1063 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
1064 mContext.sendBroadcast(broadcast);
1065 }
1066 }
1067
1068 // Message helper methods
1069 private static int getMsg(int baseMsg, int streamType) {
1070 return (baseMsg & 0xffff) | streamType << 16;
1071 }
1072
1073 private static int getMsgBase(int msg) {
1074 return msg & 0xffff;
1075 }
1076
1077 private static void sendMsg(Handler handler, int baseMsg, int streamType,
1078 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) {
1079 int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType);
1080
1081 if (existingMsgPolicy == SENDMSG_REPLACE) {
1082 handler.removeMessages(msg);
1083 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) {
1084 return;
1085 }
1086
1087 handler
1088 .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay);
1089 }
1090
1091 boolean checkAudioSettingsPermission(String method) {
1092 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS")
1093 == PackageManager.PERMISSION_GRANTED) {
1094 return true;
1095 }
1096 String msg = "Audio Settings Permission Denial: " + method + " from pid="
1097 + Binder.getCallingPid()
1098 + ", uid=" + Binder.getCallingUid();
1099 Log.w(TAG, msg);
1100 return false;
1101 }
1102
1103
1104 ///////////////////////////////////////////////////////////////////////////
1105 // Inner classes
1106 ///////////////////////////////////////////////////////////////////////////
1107
1108 public class VolumeStreamState {
1109 private final String mVolumeIndexSettingName;
1110 private final String mLastAudibleVolumeIndexSettingName;
1111 private final int mStreamType;
1112
1113 private final int[] mVolumes;
1114 private int mIndex;
1115 private int mLastAudibleIndex;
1116 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death
1117
1118 private VolumeStreamState(String settingName, int streamType, int[] volumes) {
1119
1120 mVolumeIndexSettingName = settingName;
1121 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE;
1122
1123 mStreamType = streamType;
1124 mVolumes = volumes;
1125
1126 final ContentResolver cr = mContentResolver;
1127 mIndex = getValidIndex(Settings.System.getInt(cr, mVolumeIndexSettingName, AudioManager.DEFAULT_STREAM_VOLUME[streamType]));
1128 mLastAudibleIndex = getValidIndex(Settings.System.getInt(cr,
1129 mLastAudibleVolumeIndexSettingName, mIndex > 0 ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]));
1130
1131 AudioSystem.setVolume(streamType, volumes[mIndex]);
1132 mDeathHandlers = new ArrayList<VolumeDeathHandler>();
1133 }
1134
1135 /**
1136 * Constructor to be used when there is no setting associated with the VolumeStreamState.
1137 *
1138 * @param defaultVolume Default volume of the stream to use.
1139 * @param streamType Type of the stream.
1140 * @param volumes Volumes levels associated with this stream.
1141 */
1142 private VolumeStreamState(int defaultVolume, int streamType, int[] volumes) {
1143 mVolumeIndexSettingName = null;
1144 mLastAudibleVolumeIndexSettingName = null;
1145 mIndex = mLastAudibleIndex = defaultVolume;
1146 mStreamType = streamType;
1147 mVolumes = volumes;
1148 AudioSystem.setVolume(mStreamType, defaultVolume);
1149 mDeathHandlers = new ArrayList<VolumeDeathHandler>();
1150 }
1151
1152 public boolean adjustIndex(int deltaIndex) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07001153 return setIndex(mIndex + deltaIndex, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001154 }
1155
Eric Laurent9bcf4012009-06-12 06:09:28 -07001156 public boolean setIndex(int index, boolean lastAudible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157 int oldIndex = mIndex;
1158 mIndex = getValidIndex(index);
1159
1160 if (oldIndex != mIndex) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07001161 if (lastAudible) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 mLastAudibleIndex = mIndex;
1163 }
1164 return true;
1165 } else {
1166 return false;
1167 }
1168 }
1169
1170 public int getMaxIndex() {
1171 return mVolumes.length - 1;
1172 }
1173
1174 public void mute(IBinder cb, boolean state) {
1175 VolumeDeathHandler handler = getDeathHandler(cb, state);
1176 if (handler == null) {
1177 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType);
1178 return;
1179 }
1180 handler.mute(state);
1181 }
1182
1183 private int getValidIndex(int index) {
1184 if (index < 0) {
1185 return 0;
1186 } else if (index >= mVolumes.length) {
1187 return mVolumes.length - 1;
1188 }
1189
1190 return index;
1191 }
1192
1193 private class VolumeDeathHandler implements IBinder.DeathRecipient {
1194 private IBinder mICallback; // To be notified of client's death
1195 private int mMuteCount; // Number of active mutes for this client
1196
1197 VolumeDeathHandler(IBinder cb) {
1198 mICallback = cb;
1199 }
1200
1201 public void mute(boolean state) {
1202 synchronized(mDeathHandlers) {
1203 if (state) {
1204 if (mMuteCount == 0) {
1205 // Register for client death notification
1206 try {
1207 mICallback.linkToDeath(this, 0);
1208 mDeathHandlers.add(this);
1209 // If the stream is not yet muted by any client, set lvel to 0
1210 if (muteCount() == 0) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07001211 setIndex(0, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1213 VolumeStreamState.this, 0);
1214 }
1215 } catch (RemoteException e) {
1216 // Client has died!
1217 binderDied();
1218 mDeathHandlers.notify();
1219 return;
1220 }
1221 } else {
1222 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client");
1223 }
1224 mMuteCount++;
1225 } else {
1226 if (mMuteCount == 0) {
1227 Log.e(TAG, "unexpected unmute for stream: "+mStreamType);
1228 } else {
1229 mMuteCount--;
1230 if (mMuteCount == 0) {
1231 // Unregistr from client death notification
1232 mDeathHandlers.remove(this);
1233 mICallback.unlinkToDeath(this, 0);
1234 if (muteCount() == 0) {
1235 // If the stream is not mut any more, restore it's volume if
1236 // ringer mode allows it
1237 if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) {
Eric Laurent9bcf4012009-06-12 06:09:28 -07001238 setIndex(mLastAudibleIndex, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001239 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0,
1240 VolumeStreamState.this, 0);
1241 }
1242 }
1243 }
1244 }
1245 }
1246 mDeathHandlers.notify();
1247 }
1248 }
1249
1250 public void binderDied() {
1251 Log.w(TAG, "Volume service client died for stream: "+mStreamType);
1252 if (mMuteCount != 0) {
1253 // Reset all active mute requests from this client.
1254 mMuteCount = 1;
1255 mute(false);
1256 }
1257 }
1258 }
1259
1260 private int muteCount() {
1261 int count = 0;
1262 int size = mDeathHandlers.size();
1263 for (int i = 0; i < size; i++) {
1264 count += mDeathHandlers.get(i).mMuteCount;
1265 }
1266 return count;
1267 }
1268
1269 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) {
1270 synchronized(mDeathHandlers) {
1271 VolumeDeathHandler handler;
1272 int size = mDeathHandlers.size();
1273 for (int i = 0; i < size; i++) {
1274 handler = mDeathHandlers.get(i);
1275 if (cb.equals(handler.mICallback)) {
1276 return handler;
1277 }
1278 }
1279 // If this is the first mute request for this client, create a new
1280 // client death handler. Otherwise, it is an out of sequence unmute request.
1281 if (state) {
1282 handler = new VolumeDeathHandler(cb);
1283 } else {
1284 Log.w(TAG, "stream was not muted by this client");
1285 handler = null;
1286 }
1287 return handler;
1288 }
1289 }
1290 }
1291
1292 /** Thread that handles native AudioSystem control. */
1293 private class AudioSystemThread extends Thread {
1294 AudioSystemThread() {
1295 super("AudioService");
1296 }
1297
1298 @Override
1299 public void run() {
1300 // Set this thread up so the handler will work on it
1301 Looper.prepare();
1302
1303 synchronized(AudioService.this) {
1304 mAudioHandler = new AudioHandler();
1305
1306 // Notify that the handler has been created
1307 AudioService.this.notify();
1308 }
1309
1310 // Listen for volume change requests that are set by VolumePanel
1311 Looper.loop();
1312 }
1313 }
1314
1315 /** Handles internal volume messages in separate volume thread. */
1316 private class AudioHandler extends Handler {
1317
1318 private void setSystemVolume(VolumeStreamState streamState) {
1319
1320 // Adjust volume
1321 AudioSystem
1322 .setVolume(streamState.mStreamType, streamState.mVolumes[streamState.mIndex]);
1323
1324 // Post a persist volume msg
1325 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType,
1326 SENDMSG_REPLACE, 0, 0, streamState, PERSIST_DELAY);
1327 }
1328
1329 private void persistVolume(VolumeStreamState streamState) {
1330 System.putInt(mContentResolver, streamState.mVolumeIndexSettingName,
1331 streamState.mIndex);
1332 System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName,
1333 streamState.mLastAudibleIndex);
1334 }
1335
1336 private void persistRingerMode() {
1337 System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode);
1338 }
1339
1340 private void persistVibrateSetting() {
1341 System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting);
1342 }
1343
1344 private void playSoundEffect(int effectType, int volume) {
1345 synchronized (mSoundEffectsLock) {
1346 if (mSoundPool == null) {
1347 return;
1348 }
1349
1350 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) {
1351 float v = (float) volume / 1000.0f;
1352 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], v, v, 0, 0, 1.0f);
1353 } else {
1354 MediaPlayer mediaPlayer = new MediaPlayer();
1355 if (mediaPlayer != null) {
1356 try {
1357 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]];
1358 mediaPlayer.setDataSource(filePath);
1359 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM);
1360 mediaPlayer.prepare();
1361 mediaPlayer.setOnCompletionListener(new OnCompletionListener() {
1362 public void onCompletion(MediaPlayer mp) {
1363 cleanupPlayer(mp);
1364 }
1365 });
1366 mediaPlayer.setOnErrorListener(new OnErrorListener() {
1367 public boolean onError(MediaPlayer mp, int what, int extra) {
1368 cleanupPlayer(mp);
1369 return true;
1370 }
1371 });
1372 mediaPlayer.start();
1373 } catch (IOException ex) {
1374 Log.w(TAG, "MediaPlayer IOException: "+ex);
1375 } catch (IllegalArgumentException ex) {
1376 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex);
1377 } catch (IllegalStateException ex) {
1378 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1379 }
1380 }
1381 }
1382 }
1383 }
1384
1385 private void cleanupPlayer(MediaPlayer mp) {
1386 if (mp != null) {
1387 try {
1388 mp.stop();
1389 mp.release();
1390 } catch (IllegalStateException ex) {
1391 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex);
1392 }
1393 }
1394 }
1395
1396 @Override
1397 public void handleMessage(Message msg) {
1398 int baseMsgWhat = getMsgBase(msg.what);
1399
1400 switch (baseMsgWhat) {
1401
1402 case MSG_SET_SYSTEM_VOLUME:
1403 setSystemVolume((VolumeStreamState) msg.obj);
1404 break;
1405
1406 case MSG_PERSIST_VOLUME:
1407 persistVolume((VolumeStreamState) msg.obj);
1408 break;
1409
1410 case MSG_PERSIST_RINGER_MODE:
1411 persistRingerMode();
1412 break;
1413
1414 case MSG_PERSIST_VIBRATE_SETTING:
1415 persistVibrateSetting();
1416 break;
1417
1418 case MSG_MEDIA_SERVER_DIED:
1419 Log.e(TAG, "Media server died.");
1420 // Force creation of new IAudioflinger interface
1421 mMediaServerOk = false;
1422 AudioSystem.getMode();
1423 break;
1424
1425 case MSG_MEDIA_SERVER_STARTED:
1426 Log.e(TAG, "Media server started.");
1427 // Restore audio routing and stream volumes
1428 applyAudioSettings();
1429 int numStreamTypes = AudioSystem.getNumStreamTypes();
1430 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) {
1431 int volume;
1432 VolumeStreamState streamState = mStreamStates[streamType];
1433 if (streamState.muteCount() == 0) {
1434 volume = streamState.mVolumes[streamState.mIndex];
1435 } else {
1436 volume = streamState.mVolumes[0];
1437 }
1438 AudioSystem.setVolume(streamType, volume);
1439 }
1440 setRingerMode(mRingerMode);
1441 mMediaServerOk = true;
1442 break;
1443
1444 case MSG_PLAY_SOUND_EFFECT:
1445 playSoundEffect(msg.arg1, msg.arg2);
1446 break;
1447 }
1448 }
1449 }
1450
Jason Parekhb1096152009-03-24 17:48:25 -07001451 private class SettingsObserver extends ContentObserver {
1452
1453 SettingsObserver() {
1454 super(new Handler());
1455 mContentResolver.registerContentObserver(Settings.System.getUriFor(
1456 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
1457 }
1458
1459 @Override
1460 public void onChange(boolean selfChange) {
1461 super.onChange(selfChange);
1462
Eric Laurent9bcf4012009-06-12 06:09:28 -07001463 mRingerModeAffectedStreams = Settings.System.getInt(mContentResolver,
1464 Settings.System.MODE_RINGER_STREAMS_AFFECTED,
1465 0);
1466
Jason Parekhb1096152009-03-24 17:48:25 -07001467 /*
1468 * Ensure all stream types that should be affected by ringer mode
1469 * are in the proper state.
1470 */
Eric Laurent4050c932009-07-08 02:52:14 -07001471 setRingerModeInt(getRingerMode(), false);
Jason Parekhb1096152009-03-24 17:48:25 -07001472 }
1473
1474 }
1475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476}