AudioService.java revision cf4550c3198d6b3d92cdc52707fe70d7cc0caa9f
1/* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 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; 24import android.database.ContentObserver; 25import 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; 96 private SettingsObserver mSettingsObserver; 97 98 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; 103 private boolean mSpeakerIsOn; 104 private boolean mBluetoothScoIsConnected; 105 private boolean mHeadsetIsConnected; 106 private boolean mBluetoothA2dpIsConnected; 107 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 166 /** @see System#MODE_RINGER_STREAMS_AFFECTED */ 167 private int mRingerModeAffectedStreams; 168 169 /** @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); 190 mSettingsObserver = new SettingsObserver(); 191 192 createAudioSystemThread(); 193 createStreamStates(); 194 readPersistedSettings(); 195 readAudioSettings(); 196 mMediaServerOk = true; 197 AudioSystem.setErrorCallback(mAudioSystemCallback); 198 loadSoundEffects(); 199 mSpeakerIsOn = false; 200 mBluetoothScoIsConnected = false; 201 mHeadsetIsConnected = false; 202 mBluetoothA2dpIsConnected = false; 203 } 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); 289 290 mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0); 291 292 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 296 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 409 setStreamVolumeInt(streamType, index, false, true); 410 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 447 setStreamVolumeInt(AudioManager.STREAM_NOTIFICATION, index, force, true); 448 } 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. 461 * @param lastAudible If true, stores new index as last audible one 462 */ 463 private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) { 464 VolumeStreamState streamState = mStreamStates[streamType]; 465 if (streamState.setIndex(index, lastAudible) || force) { 466 // 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) { 511 setRingerModeInt(ringerMode); 512 513 // Send sticky broadcast 514 broadcastRingerMode(); 515 } 516 } 517 518 private void setRingerModeInt(int ringerMode) { 519 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, 528 false, false); 529 } 530 } else { 531 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 532 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 } 542 } 543 } 544 545 // Post a persist ringer mode msg 546 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG, 547 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY); 548 } 549 550 /** @see AudioManager#shouldVibrate(int) */ 551 public boolean shouldVibrate(int vibrateType) { 552 553 switch (getVibrateSetting(vibrateType)) { 554 555 case AudioManager.VIBRATE_SETTING_ON: 556 return mRingerMode != AudioManager.RINGER_MODE_SILENT; 557 558 case AudioManager.VIBRATE_SETTING_ONLY_SILENT: 559 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE; 560 561 case AudioManager.VIBRATE_SETTING_OFF: 562 // Phone ringer should always vibrate in vibrate mode 563 if (vibrateType == AudioManager.VIBRATE_TYPE_RINGER) { 564 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE; 565 } 566 567 default: 568 return false; 569 } 570 } 571 572 /** @see AudioManager#getVibrateSetting(int) */ 573 public int getVibrateSetting(int vibrateType) { 574 return (mVibrateSetting >> (vibrateType * 2)) & 3; 575 } 576 577 /** @see AudioManager#setVibrateSetting(int, int) */ 578 public void setVibrateSetting(int vibrateType, int vibrateSetting) { 579 580 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting); 581 582 // Broadcast change 583 broadcastVibrateSetting(vibrateType); 584 585 // Post message to set ringer mode (it in turn will post a message 586 // to persist) 587 sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0, 588 null, 0); 589 } 590 591 /** 592 * @see #setVibrateSetting(int, int) 593 */ 594 public static int getValueForVibrateSetting(int existingValue, int vibrateType, 595 int vibrateSetting) { 596 597 // First clear the existing setting. Each vibrate type has two bits in 598 // the value. Note '3' is '11' in binary. 599 existingValue &= ~(3 << (vibrateType * 2)); 600 601 // Set into the old value 602 existingValue |= (vibrateSetting & 3) << (vibrateType * 2); 603 604 return existingValue; 605 } 606 607 /** @see AudioManager#setMicrophoneMute(boolean) */ 608 public void setMicrophoneMute(boolean on) { 609 if (!checkAudioSettingsPermission("setMicrophoneMute()")) { 610 return; 611 } 612 synchronized (mSettingsLock) { 613 if (on != mMicMute) { 614 AudioSystem.muteMicrophone(on); 615 mMicMute = on; 616 } 617 } 618 } 619 620 /** @see AudioManager#isMicrophoneMute() */ 621 public boolean isMicrophoneMute() { 622 return mMicMute; 623 } 624 625 /** @see AudioManager#setMode(int) */ 626 public void setMode(int mode) { 627 if (!checkAudioSettingsPermission("setMode()")) { 628 return; 629 } 630 synchronized (mSettingsLock) { 631 if (mode != mMode) { 632 if (AudioSystem.setMode(mode) == AudioSystem.AUDIO_STATUS_OK) { 633 mMode = mode; 634 } 635 } 636 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); 637 int index = mStreamStates[streamType].mIndex; 638 syncRingerAndNotificationStreamVolume(streamType, index, true); 639 setStreamVolumeInt(streamType, index, true, true); 640 } 641 } 642 643 /** @see AudioManager#getMode() */ 644 public int getMode() { 645 return mMode; 646 } 647 648 /** @see AudioManager#setRouting(int, int, int) */ 649 public void setRouting(int mode, int routes, int mask) { 650 int incallMask = 0; 651 int ringtoneMask = 0; 652 int normalMask = 0; 653 654 if (!checkAudioSettingsPermission("setRouting()")) { 655 return; 656 } 657 synchronized (mSettingsLock) { 658 // Temporary fix for issue #1713090 until audio routing is refactored in eclair release. 659 // mode AudioSystem.MODE_INVALID is used only by the following AudioManager methods: 660 // setWiredHeadsetOn(), setBluetoothA2dpOn(), setBluetoothScoOn() and setSpeakerphoneOn(). 661 // If applications are using AudioManager.setRouting() that is now deprecated, the routing 662 // command will be ignored. 663 if (mode == AudioSystem.MODE_INVALID) { 664 switch (mask) { 665 case AudioSystem.ROUTE_SPEAKER: 666 // handle setSpeakerphoneOn() 667 if (routes != 0 && !mSpeakerIsOn) { 668 mSpeakerIsOn = true; 669 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER; 670 incallMask = AudioSystem.ROUTE_ALL; 671 } else if (routes == 0 && mSpeakerIsOn) { 672 mSpeakerIsOn = false; 673 if (mBluetoothScoIsConnected) { 674 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_BLUETOOTH_SCO; 675 } else if (mHeadsetIsConnected) { 676 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET; 677 } else { 678 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE; 679 } 680 incallMask = AudioSystem.ROUTE_ALL; 681 } 682 break; 683 684 case AudioSystem.ROUTE_BLUETOOTH_SCO: 685 // handle setBluetoothScoOn() 686 if (routes != 0 && !mBluetoothScoIsConnected) { 687 mBluetoothScoIsConnected = true; 688 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_BLUETOOTH_SCO; 689 mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | 690 AudioSystem.ROUTE_BLUETOOTH_SCO; 691 mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | 692 AudioSystem.ROUTE_BLUETOOTH_SCO; 693 incallMask = AudioSystem.ROUTE_ALL; 694 // A2DP has higher priority than SCO headset, so headset connect/disconnect events 695 // should not affect A2DP routing 696 ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; 697 normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; 698 } else if (routes == 0 && mBluetoothScoIsConnected) { 699 mBluetoothScoIsConnected = false; 700 if (mHeadsetIsConnected) { 701 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET; 702 mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | 703 (AudioSystem.ROUTE_HEADSET|AudioSystem.ROUTE_SPEAKER); 704 mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | 705 AudioSystem.ROUTE_HEADSET; 706 } else { 707 if (mSpeakerIsOn) { 708 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER; 709 } else { 710 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE; 711 } 712 mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | 713 AudioSystem.ROUTE_SPEAKER; 714 mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | 715 AudioSystem.ROUTE_SPEAKER; 716 } 717 incallMask = AudioSystem.ROUTE_ALL; 718 // A2DP has higher priority than SCO headset, so headset connect/disconnect events 719 // should not affect A2DP routing 720 ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; 721 normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; 722 } 723 break; 724 725 case AudioSystem.ROUTE_HEADSET: 726 // handle setWiredHeadsetOn() 727 if (routes != 0 && !mHeadsetIsConnected) { 728 mHeadsetIsConnected = true; 729 // do not act upon headset connection if bluetooth SCO is connected to match phone app behavior 730 if (!mBluetoothScoIsConnected) { 731 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_HEADSET; 732 mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | 733 (AudioSystem.ROUTE_HEADSET|AudioSystem.ROUTE_SPEAKER); 734 mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | 735 AudioSystem.ROUTE_HEADSET; 736 incallMask = AudioSystem.ROUTE_ALL; 737 // A2DP has higher priority than wired headset, so headset connect/disconnect events 738 // should not affect A2DP routing 739 ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; 740 normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; 741 } 742 } else if (routes == 0 && mHeadsetIsConnected) { 743 mHeadsetIsConnected = false; 744 // do not act upon headset disconnection if bluetooth SCO is connected to match phone app behavior 745 if (!mBluetoothScoIsConnected) { 746 if (mSpeakerIsOn) { 747 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_SPEAKER; 748 } else { 749 mRoutes[AudioSystem.MODE_IN_CALL] = AudioSystem.ROUTE_EARPIECE; 750 } 751 mRoutes[AudioSystem.MODE_RINGTONE] = (mRoutes[AudioSystem.MODE_RINGTONE] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | 752 AudioSystem.ROUTE_SPEAKER; 753 mRoutes[AudioSystem.MODE_NORMAL] = (mRoutes[AudioSystem.MODE_NORMAL] & AudioSystem.ROUTE_BLUETOOTH_A2DP) | 754 AudioSystem.ROUTE_SPEAKER; 755 756 incallMask = AudioSystem.ROUTE_ALL; 757 // A2DP has higher priority than wired headset, so headset connect/disconnect events 758 // should not affect A2DP routing 759 ringtoneMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; 760 normalMask = AudioSystem.ROUTE_ALL & ~AudioSystem.ROUTE_BLUETOOTH_A2DP; 761 } 762 } 763 break; 764 765 case AudioSystem.ROUTE_BLUETOOTH_A2DP: 766 // handle setBluetoothA2dpOn() 767 if (routes != 0 && !mBluetoothA2dpIsConnected) { 768 mBluetoothA2dpIsConnected = true; 769 mRoutes[AudioSystem.MODE_RINGTONE] |= AudioSystem.ROUTE_BLUETOOTH_A2DP; 770 mRoutes[AudioSystem.MODE_NORMAL] |= AudioSystem.ROUTE_BLUETOOTH_A2DP; 771 // the audio flinger chooses A2DP as a higher priority, 772 // so there is no need to disable other routes. 773 ringtoneMask = AudioSystem.ROUTE_BLUETOOTH_A2DP; 774 normalMask = AudioSystem.ROUTE_BLUETOOTH_A2DP; 775 } else if (routes == 0 && mBluetoothA2dpIsConnected) { 776 mBluetoothA2dpIsConnected = false; 777 mRoutes[AudioSystem.MODE_RINGTONE] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP; 778 mRoutes[AudioSystem.MODE_NORMAL] &= ~AudioSystem.ROUTE_BLUETOOTH_A2DP; 779 // the audio flinger chooses A2DP as a higher priority, 780 // so there is no need to disable other routes. 781 ringtoneMask = AudioSystem.ROUTE_BLUETOOTH_A2DP; 782 normalMask = AudioSystem.ROUTE_BLUETOOTH_A2DP; 783 } 784 break; 785 } 786 787 // incallMask is != 0 means we must apply ne routing to MODE_IN_CALL mode 788 if (incallMask != 0) { 789 AudioSystem.setRouting(AudioSystem.MODE_IN_CALL, 790 mRoutes[AudioSystem.MODE_IN_CALL], 791 incallMask); 792 } 793 // ringtoneMask is != 0 means we must apply ne routing to MODE_RINGTONE mode 794 if (ringtoneMask != 0) { 795 AudioSystem.setRouting(AudioSystem.MODE_RINGTONE, 796 mRoutes[AudioSystem.MODE_RINGTONE], 797 ringtoneMask); 798 } 799 // normalMask is != 0 means we must apply ne routing to MODE_NORMAL mode 800 if (normalMask != 0) { 801 AudioSystem.setRouting(AudioSystem.MODE_NORMAL, 802 mRoutes[AudioSystem.MODE_NORMAL], 803 normalMask); 804 } 805 806 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); 807 int index = mStreamStates[streamType].mIndex; 808 syncRingerAndNotificationStreamVolume(streamType, index, true); 809 setStreamVolumeInt(streamType, index, true, true); 810 } 811 } 812 } 813 814 /** @see AudioManager#getRouting(int) */ 815 public int getRouting(int mode) { 816 return mRoutes[mode]; 817 } 818 819 /** @see AudioManager#isMusicActive() */ 820 public boolean isMusicActive() { 821 return AudioSystem.isMusicActive(); 822 } 823 824 /** @see AudioManager#setParameter(String, String) */ 825 public void setParameter(String key, String value) { 826 AudioSystem.setParameter(key, value); 827 } 828 829 /** @see AudioManager#playSoundEffect(int) */ 830 public void playSoundEffect(int effectType) { 831 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP, 832 effectType, SOUND_EFFECT_VOLUME, null, 0); 833 } 834 835 /** @see AudioManager#playSoundEffect(int, float) */ 836 public void playSoundEffectVolume(int effectType, float volume) { 837 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP, 838 effectType, (int) (volume * 1000), null, 0); 839 } 840 841 /** 842 * Loads samples into the soundpool. 843 * This method must be called at when sound effects are enabled 844 */ 845 public boolean loadSoundEffects() { 846 synchronized (mSoundEffectsLock) { 847 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0); 848 if (mSoundPool == null) { 849 return false; 850 } 851 /* 852 * poolId table: The value -1 in this table indicates that corresponding 853 * file (same index in SOUND_EFFECT_FILES[] has not been loaded. 854 * Once loaded, the value in poolId is the sample ID and the same 855 * sample can be reused for another effect using the same file. 856 */ 857 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 858 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 859 poolId[fileIdx] = -1; 860 } 861 /* 862 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded. 863 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0: 864 * this indicates we have a valid sample loaded for this effect. 865 */ 866 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 867 // Do not load sample if this effect uses the MediaPlayer 868 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) { 869 continue; 870 } 871 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) { 872 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]]; 873 int sampleId = mSoundPool.load(filePath, 0); 874 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId; 875 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId; 876 if (sampleId <= 0) { 877 Log.w(TAG, "Soundpool could not load file: "+filePath); 878 } 879 } else { 880 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]]; 881 } 882 } 883 } 884 885 return true; 886 } 887 888 /** 889 * Unloads samples from the sound pool. 890 * This method can be called to free some memory when 891 * sound effects are disabled. 892 */ 893 public void unloadSoundEffects() { 894 synchronized (mSoundEffectsLock) { 895 if (mSoundPool == null) { 896 return; 897 } 898 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 899 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 900 poolId[fileIdx] = 0; 901 } 902 903 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 904 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) { 905 continue; 906 } 907 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) { 908 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]); 909 SOUND_EFFECT_FILES_MAP[effect][1] = -1; 910 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1; 911 } 912 } 913 mSoundPool = null; 914 } 915 } 916 917 /////////////////////////////////////////////////////////////////////////// 918 // Internal methods 919 /////////////////////////////////////////////////////////////////////////// 920 921 /** 922 * Checks if the adjustment should change ringer mode instead of just 923 * adjusting volume. If so, this will set the proper ringer mode and volume 924 * indices on the stream states. 925 */ 926 private boolean checkForRingerModeChange(int oldIndex, int direction) { 927 boolean adjustVolumeIndex = true; 928 int newRingerMode = mRingerMode; 929 930 if (mRingerMode == AudioManager.RINGER_MODE_NORMAL && oldIndex == 1 931 && direction == AudioManager.ADJUST_LOWER) { 932 newRingerMode = AudioManager.RINGER_MODE_VIBRATE; 933 } else if (mRingerMode == AudioManager.RINGER_MODE_VIBRATE) { 934 if (direction == AudioManager.ADJUST_RAISE) { 935 newRingerMode = AudioManager.RINGER_MODE_NORMAL; 936 } else if (direction == AudioManager.ADJUST_LOWER) { 937 newRingerMode = AudioManager.RINGER_MODE_SILENT; 938 } 939 } else if (direction == AudioManager.ADJUST_RAISE 940 && mRingerMode == AudioManager.RINGER_MODE_SILENT) { 941 newRingerMode = AudioManager.RINGER_MODE_VIBRATE; 942 } 943 944 if (newRingerMode != mRingerMode) { 945 setRingerMode(newRingerMode); 946 947 /* 948 * If we are changing ringer modes, do not increment/decrement the 949 * volume index. Instead, the handler for the message above will 950 * take care of changing the index. 951 */ 952 adjustVolumeIndex = false; 953 } 954 955 return adjustVolumeIndex; 956 } 957 958 public boolean isStreamAffectedByRingerMode(int streamType) { 959 return (mRingerModeAffectedStreams & (1 << streamType)) != 0; 960 } 961 962 public boolean isStreamAffectedByMute(int streamType) { 963 return (mMuteAffectedStreams & (1 << streamType)) != 0; 964 } 965 966 private void ensureValidDirection(int direction) { 967 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) { 968 throw new IllegalArgumentException("Bad direction " + direction); 969 } 970 } 971 972 private void ensureValidStreamType(int streamType) { 973 if (streamType < 0 || streamType >= mStreamStates.length) { 974 throw new IllegalArgumentException("Bad stream type " + streamType); 975 } 976 } 977 978 private int getActiveStreamType(int suggestedStreamType) { 979 boolean isOffhook = false; 980 try { 981 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 982 if (phone != null) isOffhook = phone.isOffhook(); 983 } catch (RemoteException e) { 984 Log.w(TAG, "Couldn't connect to phone service", e); 985 } 986 987 if ((getRouting(AudioSystem.MODE_IN_CALL) & AudioSystem.ROUTE_BLUETOOTH_SCO) != 0) { 988 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); 989 return AudioSystem.STREAM_BLUETOOTH_SCO; 990 } else if (isOffhook) { 991 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); 992 return AudioSystem.STREAM_VOICE_CALL; 993 } else if (AudioSystem.isMusicActive()) { 994 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC..."); 995 return AudioSystem.STREAM_MUSIC; 996 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { 997 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."); 998 return AudioSystem.STREAM_RING; 999 } else { 1000 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType); 1001 return suggestedStreamType; 1002 } 1003 } 1004 1005 private void broadcastRingerMode() { 1006 // Send sticky broadcast 1007 if (ActivityManagerNative.isSystemReady()) { 1008 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION); 1009 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode); 1010 long origCallerIdentityToken = Binder.clearCallingIdentity(); 1011 mContext.sendStickyBroadcast(broadcast); 1012 Binder.restoreCallingIdentity(origCallerIdentityToken); 1013 } 1014 } 1015 1016 private void broadcastVibrateSetting(int vibrateType) { 1017 // Send broadcast 1018 if (ActivityManagerNative.isSystemReady()) { 1019 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); 1020 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); 1021 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); 1022 mContext.sendBroadcast(broadcast); 1023 } 1024 } 1025 1026 // Message helper methods 1027 private static int getMsg(int baseMsg, int streamType) { 1028 return (baseMsg & 0xffff) | streamType << 16; 1029 } 1030 1031 private static int getMsgBase(int msg) { 1032 return msg & 0xffff; 1033 } 1034 1035 private static void sendMsg(Handler handler, int baseMsg, int streamType, 1036 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) { 1037 int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType); 1038 1039 if (existingMsgPolicy == SENDMSG_REPLACE) { 1040 handler.removeMessages(msg); 1041 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) { 1042 return; 1043 } 1044 1045 handler 1046 .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay); 1047 } 1048 1049 boolean checkAudioSettingsPermission(String method) { 1050 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS") 1051 == PackageManager.PERMISSION_GRANTED) { 1052 return true; 1053 } 1054 String msg = "Audio Settings Permission Denial: " + method + " from pid=" 1055 + Binder.getCallingPid() 1056 + ", uid=" + Binder.getCallingUid(); 1057 Log.w(TAG, msg); 1058 return false; 1059 } 1060 1061 1062 /////////////////////////////////////////////////////////////////////////// 1063 // Inner classes 1064 /////////////////////////////////////////////////////////////////////////// 1065 1066 public class VolumeStreamState { 1067 private final String mVolumeIndexSettingName; 1068 private final String mLastAudibleVolumeIndexSettingName; 1069 private final int mStreamType; 1070 1071 private final int[] mVolumes; 1072 private int mIndex; 1073 private int mLastAudibleIndex; 1074 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death 1075 1076 private VolumeStreamState(String settingName, int streamType, int[] volumes) { 1077 1078 mVolumeIndexSettingName = settingName; 1079 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; 1080 1081 mStreamType = streamType; 1082 mVolumes = volumes; 1083 1084 final ContentResolver cr = mContentResolver; 1085 mIndex = getValidIndex(Settings.System.getInt(cr, mVolumeIndexSettingName, AudioManager.DEFAULT_STREAM_VOLUME[streamType])); 1086 mLastAudibleIndex = getValidIndex(Settings.System.getInt(cr, 1087 mLastAudibleVolumeIndexSettingName, mIndex > 0 ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType])); 1088 1089 AudioSystem.setVolume(streamType, volumes[mIndex]); 1090 mDeathHandlers = new ArrayList<VolumeDeathHandler>(); 1091 } 1092 1093 /** 1094 * Constructor to be used when there is no setting associated with the VolumeStreamState. 1095 * 1096 * @param defaultVolume Default volume of the stream to use. 1097 * @param streamType Type of the stream. 1098 * @param volumes Volumes levels associated with this stream. 1099 */ 1100 private VolumeStreamState(int defaultVolume, int streamType, int[] volumes) { 1101 mVolumeIndexSettingName = null; 1102 mLastAudibleVolumeIndexSettingName = null; 1103 mIndex = mLastAudibleIndex = defaultVolume; 1104 mStreamType = streamType; 1105 mVolumes = volumes; 1106 AudioSystem.setVolume(mStreamType, defaultVolume); 1107 mDeathHandlers = new ArrayList<VolumeDeathHandler>(); 1108 } 1109 1110 public boolean adjustIndex(int deltaIndex) { 1111 return setIndex(mIndex + deltaIndex, true); 1112 } 1113 1114 public boolean setIndex(int index, boolean lastAudible) { 1115 int oldIndex = mIndex; 1116 mIndex = getValidIndex(index); 1117 1118 if (oldIndex != mIndex) { 1119 if (lastAudible) { 1120 mLastAudibleIndex = mIndex; 1121 } 1122 return true; 1123 } else { 1124 return false; 1125 } 1126 } 1127 1128 public int getMaxIndex() { 1129 return mVolumes.length - 1; 1130 } 1131 1132 public void mute(IBinder cb, boolean state) { 1133 VolumeDeathHandler handler = getDeathHandler(cb, state); 1134 if (handler == null) { 1135 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType); 1136 return; 1137 } 1138 handler.mute(state); 1139 } 1140 1141 private int getValidIndex(int index) { 1142 if (index < 0) { 1143 return 0; 1144 } else if (index >= mVolumes.length) { 1145 return mVolumes.length - 1; 1146 } 1147 1148 return index; 1149 } 1150 1151 private class VolumeDeathHandler implements IBinder.DeathRecipient { 1152 private IBinder mICallback; // To be notified of client's death 1153 private int mMuteCount; // Number of active mutes for this client 1154 1155 VolumeDeathHandler(IBinder cb) { 1156 mICallback = cb; 1157 } 1158 1159 public void mute(boolean state) { 1160 synchronized(mDeathHandlers) { 1161 if (state) { 1162 if (mMuteCount == 0) { 1163 // Register for client death notification 1164 try { 1165 mICallback.linkToDeath(this, 0); 1166 mDeathHandlers.add(this); 1167 // If the stream is not yet muted by any client, set lvel to 0 1168 if (muteCount() == 0) { 1169 setIndex(0, false); 1170 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, 1171 VolumeStreamState.this, 0); 1172 } 1173 } catch (RemoteException e) { 1174 // Client has died! 1175 binderDied(); 1176 mDeathHandlers.notify(); 1177 return; 1178 } 1179 } else { 1180 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client"); 1181 } 1182 mMuteCount++; 1183 } else { 1184 if (mMuteCount == 0) { 1185 Log.e(TAG, "unexpected unmute for stream: "+mStreamType); 1186 } else { 1187 mMuteCount--; 1188 if (mMuteCount == 0) { 1189 // Unregistr from client death notification 1190 mDeathHandlers.remove(this); 1191 mICallback.unlinkToDeath(this, 0); 1192 if (muteCount() == 0) { 1193 // If the stream is not mut any more, restore it's volume if 1194 // ringer mode allows it 1195 if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 1196 setIndex(mLastAudibleIndex, false); 1197 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, 1198 VolumeStreamState.this, 0); 1199 } 1200 } 1201 } 1202 } 1203 } 1204 mDeathHandlers.notify(); 1205 } 1206 } 1207 1208 public void binderDied() { 1209 Log.w(TAG, "Volume service client died for stream: "+mStreamType); 1210 if (mMuteCount != 0) { 1211 // Reset all active mute requests from this client. 1212 mMuteCount = 1; 1213 mute(false); 1214 } 1215 } 1216 } 1217 1218 private int muteCount() { 1219 int count = 0; 1220 int size = mDeathHandlers.size(); 1221 for (int i = 0; i < size; i++) { 1222 count += mDeathHandlers.get(i).mMuteCount; 1223 } 1224 return count; 1225 } 1226 1227 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) { 1228 synchronized(mDeathHandlers) { 1229 VolumeDeathHandler handler; 1230 int size = mDeathHandlers.size(); 1231 for (int i = 0; i < size; i++) { 1232 handler = mDeathHandlers.get(i); 1233 if (cb.equals(handler.mICallback)) { 1234 return handler; 1235 } 1236 } 1237 // If this is the first mute request for this client, create a new 1238 // client death handler. Otherwise, it is an out of sequence unmute request. 1239 if (state) { 1240 handler = new VolumeDeathHandler(cb); 1241 } else { 1242 Log.w(TAG, "stream was not muted by this client"); 1243 handler = null; 1244 } 1245 return handler; 1246 } 1247 } 1248 } 1249 1250 /** Thread that handles native AudioSystem control. */ 1251 private class AudioSystemThread extends Thread { 1252 AudioSystemThread() { 1253 super("AudioService"); 1254 } 1255 1256 @Override 1257 public void run() { 1258 // Set this thread up so the handler will work on it 1259 Looper.prepare(); 1260 1261 synchronized(AudioService.this) { 1262 mAudioHandler = new AudioHandler(); 1263 1264 // Notify that the handler has been created 1265 AudioService.this.notify(); 1266 } 1267 1268 // Listen for volume change requests that are set by VolumePanel 1269 Looper.loop(); 1270 } 1271 } 1272 1273 /** Handles internal volume messages in separate volume thread. */ 1274 private class AudioHandler extends Handler { 1275 1276 private void setSystemVolume(VolumeStreamState streamState) { 1277 1278 // Adjust volume 1279 AudioSystem 1280 .setVolume(streamState.mStreamType, streamState.mVolumes[streamState.mIndex]); 1281 1282 // Post a persist volume msg 1283 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType, 1284 SENDMSG_REPLACE, 0, 0, streamState, PERSIST_DELAY); 1285 } 1286 1287 private void persistVolume(VolumeStreamState streamState) { 1288 System.putInt(mContentResolver, streamState.mVolumeIndexSettingName, 1289 streamState.mIndex); 1290 System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName, 1291 streamState.mLastAudibleIndex); 1292 } 1293 1294 private void persistRingerMode() { 1295 System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode); 1296 } 1297 1298 private void persistVibrateSetting() { 1299 System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting); 1300 } 1301 1302 private void playSoundEffect(int effectType, int volume) { 1303 synchronized (mSoundEffectsLock) { 1304 if (mSoundPool == null) { 1305 return; 1306 } 1307 1308 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) { 1309 float v = (float) volume / 1000.0f; 1310 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], v, v, 0, 0, 1.0f); 1311 } else { 1312 MediaPlayer mediaPlayer = new MediaPlayer(); 1313 if (mediaPlayer != null) { 1314 try { 1315 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]]; 1316 mediaPlayer.setDataSource(filePath); 1317 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM); 1318 mediaPlayer.prepare(); 1319 mediaPlayer.setOnCompletionListener(new OnCompletionListener() { 1320 public void onCompletion(MediaPlayer mp) { 1321 cleanupPlayer(mp); 1322 } 1323 }); 1324 mediaPlayer.setOnErrorListener(new OnErrorListener() { 1325 public boolean onError(MediaPlayer mp, int what, int extra) { 1326 cleanupPlayer(mp); 1327 return true; 1328 } 1329 }); 1330 mediaPlayer.start(); 1331 } catch (IOException ex) { 1332 Log.w(TAG, "MediaPlayer IOException: "+ex); 1333 } catch (IllegalArgumentException ex) { 1334 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex); 1335 } catch (IllegalStateException ex) { 1336 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 1337 } 1338 } 1339 } 1340 } 1341 } 1342 1343 private void cleanupPlayer(MediaPlayer mp) { 1344 if (mp != null) { 1345 try { 1346 mp.stop(); 1347 mp.release(); 1348 } catch (IllegalStateException ex) { 1349 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 1350 } 1351 } 1352 } 1353 1354 @Override 1355 public void handleMessage(Message msg) { 1356 int baseMsgWhat = getMsgBase(msg.what); 1357 1358 switch (baseMsgWhat) { 1359 1360 case MSG_SET_SYSTEM_VOLUME: 1361 setSystemVolume((VolumeStreamState) msg.obj); 1362 break; 1363 1364 case MSG_PERSIST_VOLUME: 1365 persistVolume((VolumeStreamState) msg.obj); 1366 break; 1367 1368 case MSG_PERSIST_RINGER_MODE: 1369 persistRingerMode(); 1370 break; 1371 1372 case MSG_PERSIST_VIBRATE_SETTING: 1373 persistVibrateSetting(); 1374 break; 1375 1376 case MSG_MEDIA_SERVER_DIED: 1377 Log.e(TAG, "Media server died."); 1378 // Force creation of new IAudioflinger interface 1379 mMediaServerOk = false; 1380 AudioSystem.getMode(); 1381 break; 1382 1383 case MSG_MEDIA_SERVER_STARTED: 1384 Log.e(TAG, "Media server started."); 1385 // Restore audio routing and stream volumes 1386 applyAudioSettings(); 1387 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1388 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1389 int volume; 1390 VolumeStreamState streamState = mStreamStates[streamType]; 1391 if (streamState.muteCount() == 0) { 1392 volume = streamState.mVolumes[streamState.mIndex]; 1393 } else { 1394 volume = streamState.mVolumes[0]; 1395 } 1396 AudioSystem.setVolume(streamType, volume); 1397 } 1398 setRingerMode(mRingerMode); 1399 mMediaServerOk = true; 1400 break; 1401 1402 case MSG_PLAY_SOUND_EFFECT: 1403 playSoundEffect(msg.arg1, msg.arg2); 1404 break; 1405 } 1406 } 1407 } 1408 1409 private class SettingsObserver extends ContentObserver { 1410 1411 SettingsObserver() { 1412 super(new Handler()); 1413 mContentResolver.registerContentObserver(Settings.System.getUriFor( 1414 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this); 1415 } 1416 1417 @Override 1418 public void onChange(boolean selfChange) { 1419 super.onChange(selfChange); 1420 1421 mRingerModeAffectedStreams = Settings.System.getInt(mContentResolver, 1422 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 1423 0); 1424 1425 /* 1426 * Ensure all stream types that should be affected by ringer mode 1427 * are in the proper state. 1428 */ 1429 setRingerModeInt(getRingerMode()); 1430 } 1431 1432 } 1433 1434} 1435