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