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