AudioService.java revision 25101b0b9a84571ead15b26e9f4cd9c4298d7823
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.bluetooth.BluetoothA2dp; 21import android.bluetooth.BluetoothAdapter; 22import android.bluetooth.BluetoothClass; 23import android.bluetooth.BluetoothDevice; 24import android.bluetooth.BluetoothHeadset; 25import android.bluetooth.BluetoothProfile; 26import android.content.BroadcastReceiver; 27import android.content.ComponentName; 28import android.content.ContentResolver; 29import android.content.Context; 30import android.content.Intent; 31import android.content.IntentFilter; 32import android.content.pm.PackageManager; 33import android.database.ContentObserver; 34import android.media.MediaPlayer.OnCompletionListener; 35import android.media.MediaPlayer.OnErrorListener; 36import android.os.Binder; 37import android.os.Environment; 38import android.os.Handler; 39import android.os.IBinder; 40import android.os.Looper; 41import android.os.Message; 42import android.os.RemoteException; 43import android.os.ServiceManager; 44import android.os.SystemProperties; 45import android.provider.Settings; 46import android.provider.Settings.System; 47import android.telephony.PhoneStateListener; 48import android.telephony.TelephonyManager; 49import android.util.Log; 50import android.view.KeyEvent; 51import android.view.VolumePanel; 52 53import com.android.internal.telephony.ITelephony; 54 55import java.io.FileDescriptor; 56import java.io.IOException; 57import java.io.PrintWriter; 58import java.util.ArrayList; 59import java.util.HashMap; 60import java.util.Iterator; 61import java.util.List; 62import java.util.Map; 63import java.util.NoSuchElementException; 64import java.util.Set; 65import java.util.Stack; 66 67/** 68 * The implementation of the volume manager service. 69 * <p> 70 * This implementation focuses on delivering a responsive UI. Most methods are 71 * asynchronous to external calls. For example, the task of setting a volume 72 * will update our internal state, but in a separate thread will set the system 73 * volume and later persist to the database. Similarly, setting the ringer mode 74 * will update the state and broadcast a change and in a separate thread later 75 * persist the ringer mode. 76 * 77 * @hide 78 */ 79public class AudioService extends IAudioService.Stub { 80 81 private static final String TAG = "AudioService"; 82 83 /** How long to delay before persisting a change in volume/ringer mode. */ 84 private static final int PERSIST_DELAY = 3000; 85 86 private Context mContext; 87 private ContentResolver mContentResolver; 88 private boolean mVoiceCapable; 89 90 /** The UI */ 91 private VolumePanel mVolumePanel; 92 93 // sendMsg() flags 94 /** Used when a message should be shared across all stream types. */ 95 private static final int SHARED_MSG = -1; 96 /** If the msg is already queued, replace it with this one. */ 97 private static final int SENDMSG_REPLACE = 0; 98 /** If the msg is already queued, ignore this one and leave the old. */ 99 private static final int SENDMSG_NOOP = 1; 100 /** If the msg is already queued, queue this one and leave the old. */ 101 private static final int SENDMSG_QUEUE = 2; 102 103 // AudioHandler message.whats 104 private static final int MSG_SET_SYSTEM_VOLUME = 0; 105 private static final int MSG_PERSIST_VOLUME = 1; 106 private static final int MSG_PERSIST_RINGER_MODE = 3; 107 private static final int MSG_PERSIST_VIBRATE_SETTING = 4; 108 private static final int MSG_MEDIA_SERVER_DIED = 5; 109 private static final int MSG_MEDIA_SERVER_STARTED = 6; 110 private static final int MSG_PLAY_SOUND_EFFECT = 7; 111 private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8; 112 private static final int MSG_LOAD_SOUND_EFFECTS = 9; 113 114 private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000; 115 116 /** @see AudioSystemThread */ 117 private AudioSystemThread mAudioSystemThread; 118 /** @see AudioHandler */ 119 private AudioHandler mAudioHandler; 120 /** @see VolumeStreamState */ 121 private VolumeStreamState[] mStreamStates; 122 private SettingsObserver mSettingsObserver; 123 124 private int mMode; 125 private Object mSettingsLock = new Object(); 126 private boolean mMediaServerOk; 127 128 private SoundPool mSoundPool; 129 private Object mSoundEffectsLock = new Object(); 130 private static final int NUM_SOUNDPOOL_CHANNELS = 4; 131 private static final int SOUND_EFFECT_VOLUME = 1000; 132 133 /* Sound effect file names */ 134 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/"; 135 private static final String[] SOUND_EFFECT_FILES = new String[] { 136 "Effect_Tick.ogg", 137 "KeypressStandard.ogg", 138 "KeypressSpacebar.ogg", 139 "KeypressDelete.ogg", 140 "KeypressReturn.ogg" 141 }; 142 143 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to 144 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect 145 * uses soundpool (second column) */ 146 private int[][] SOUND_EFFECT_FILES_MAP = new int[][] { 147 {0, -1}, // FX_KEY_CLICK 148 {0, -1}, // FX_FOCUS_NAVIGATION_UP 149 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN 150 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT 151 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT 152 {1, -1}, // FX_KEYPRESS_STANDARD 153 {2, -1}, // FX_KEYPRESS_SPACEBAR 154 {3, -1}, // FX_FOCUS_DELETE 155 {4, -1} // FX_FOCUS_RETURN 156 }; 157 158 /** @hide Maximum volume index values for audio streams */ 159 private int[] MAX_STREAM_VOLUME = new int[] { 160 5, // STREAM_VOICE_CALL 161 7, // STREAM_SYSTEM 162 7, // STREAM_RING 163 15, // STREAM_MUSIC 164 7, // STREAM_ALARM 165 7, // STREAM_NOTIFICATION 166 15, // STREAM_BLUETOOTH_SCO 167 7, // STREAM_SYSTEM_ENFORCED 168 15, // STREAM_DTMF 169 15 // STREAM_TTS 170 }; 171 /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings 172 * of another stream: This avoids multiplying the volume settings for hidden 173 * stream types that follow other stream behavior for volume settings 174 * NOTE: do not create loops in aliases! */ 175 private int[] STREAM_VOLUME_ALIAS = new int[] { 176 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL 177 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM 178 AudioSystem.STREAM_RING, // STREAM_RING 179 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC 180 AudioSystem.STREAM_ALARM, // STREAM_ALARM 181 AudioSystem.STREAM_NOTIFICATION, // STREAM_NOTIFICATION 182 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO 183 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM_ENFORCED 184 AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF 185 AudioSystem.STREAM_MUSIC // STREAM_TTS 186 }; 187 188 private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() { 189 public void onError(int error) { 190 switch (error) { 191 case AudioSystem.AUDIO_STATUS_SERVER_DIED: 192 if (mMediaServerOk) { 193 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0, 194 null, 1500); 195 mMediaServerOk = false; 196 } 197 break; 198 case AudioSystem.AUDIO_STATUS_OK: 199 if (!mMediaServerOk) { 200 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0, 201 null, 0); 202 mMediaServerOk = true; 203 } 204 break; 205 default: 206 break; 207 } 208 } 209 }; 210 211 /** 212 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL}, 213 * {@link AudioManager#RINGER_MODE_SILENT}, or 214 * {@link AudioManager#RINGER_MODE_VIBRATE}. 215 */ 216 private int mRingerMode; 217 218 /** @see System#MODE_RINGER_STREAMS_AFFECTED */ 219 private int mRingerModeAffectedStreams; 220 221 // Streams currently muted by ringer mode 222 private int mRingerModeMutedStreams; 223 224 /** @see System#MUTE_STREAMS_AFFECTED */ 225 private int mMuteAffectedStreams; 226 227 /** 228 * Has multiple bits per vibrate type to indicate the type's vibrate 229 * setting. See {@link #setVibrateSetting(int, int)}. 230 * <p> 231 * NOTE: This is not the final decision of whether vibrate is on/off for the 232 * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}. 233 */ 234 private int mVibrateSetting; 235 236 /** @see System#NOTIFICATIONS_USE_RING_VOLUME */ 237 private int mNotificationsUseRingVolume; 238 239 // Broadcast receiver for device connections intent broadcasts 240 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver(); 241 242 // Broadcast receiver for media button broadcasts (separate from mReceiver to 243 // independently change its priority) 244 private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver(); 245 246 // Used to alter media button redirection when the phone is ringing. 247 private boolean mIsRinging = false; 248 249 // Devices currently connected 250 private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>(); 251 252 // Forced device usage for communications 253 private int mForcedUseForComm; 254 255 // List of binder death handlers for setMode() client processes. 256 // The last process to have called setMode() is at the top of the list. 257 private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>(); 258 259 // List of clients having issued a SCO start request 260 private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>(); 261 262 // BluetoothHeadset API to control SCO connection 263 private BluetoothHeadset mBluetoothHeadset; 264 265 // Bluetooth headset device 266 private BluetoothDevice mBluetoothHeadsetDevice; 267 268 // Indicate if SCO audio connection is currently active and if the initiator is 269 // audio service (internal) or bluetooth headset (external) 270 private int mScoAudioState; 271 // SCO audio state is not active 272 private static final int SCO_STATE_INACTIVE = 0; 273 // SCO audio state is active or starting due to a local request to start a virtual call 274 private static final int SCO_STATE_ACTIVE_INTERNAL = 1; 275 // SCO audio state is active due to an action in BT handsfree (either voice recognition or 276 // in call audio) 277 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; 278 279 // true if boot sequence has been completed 280 private boolean mBootCompleted; 281 // listener for SoundPool sample load completion indication 282 private SoundPoolCallback mSoundPoolCallBack; 283 // thread for SoundPool listener 284 private SoundPoolListenerThread mSoundPoolListenerThread; 285 // message looper for SoundPool listener 286 private Looper mSoundPoolLooper = null; 287 // default volume applied to sound played with playSoundEffect() 288 private static final int SOUND_EFFECT_DEFAULT_VOLUME_DB = -20; 289 // volume applied to sound played with playSoundEffect() read from ro.config.sound_fx_volume 290 private int SOUND_EFFECT_VOLUME_DB; 291 // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification 292 // stopped 293 private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000; 294 // previous volume adjustment direction received by checkForRingerModeChange() 295 private int mPrevVolDirection = AudioManager.ADJUST_SAME; 296 297 /////////////////////////////////////////////////////////////////////////// 298 // Construction 299 /////////////////////////////////////////////////////////////////////////// 300 301 /** @hide */ 302 public AudioService(Context context) { 303 mContext = context; 304 mContentResolver = context.getContentResolver(); 305 mVoiceCapable = mContext.getResources().getBoolean( 306 com.android.internal.R.bool.config_voice_capable); 307 308 // Intialized volume 309 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt( 310 "ro.config.vc_call_vol_steps", 311 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]); 312 313 SOUND_EFFECT_VOLUME_DB = SystemProperties.getInt( 314 "ro.config.sound_fx_volume", 315 SOUND_EFFECT_DEFAULT_VOLUME_DB); 316 317 mVolumePanel = new VolumePanel(context, this); 318 mSettingsObserver = new SettingsObserver(); 319 mForcedUseForComm = AudioSystem.FORCE_NONE; 320 createAudioSystemThread(); 321 readPersistedSettings(); 322 createStreamStates(); 323 // Call setMode() to initialize mSetModeDeathHandlers 324 mMode = AudioSystem.MODE_INVALID; 325 setMode(AudioSystem.MODE_NORMAL, null); 326 mMediaServerOk = true; 327 328 // Call setRingerModeInt() to apply correct mute 329 // state on streams affected by ringer mode. 330 mRingerModeMutedStreams = 0; 331 setRingerModeInt(getRingerMode(), false); 332 333 AudioSystem.setErrorCallback(mAudioSystemCallback); 334 335 mBluetoothHeadsetDevice = null; 336 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 337 if (adapter != null) { 338 adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, 339 BluetoothProfile.HEADSET); 340 } 341 342 // Register for device connection intent broadcasts. 343 IntentFilter intentFilter = 344 new IntentFilter(Intent.ACTION_HEADSET_PLUG); 345 346 intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 347 intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 348 intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 349 intentFilter.addAction(Intent.ACTION_DOCK_EVENT); 350 intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG); 351 intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG); 352 intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG); 353 intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); 354 context.registerReceiver(mReceiver, intentFilter); 355 356 // Register for media button intent broadcasts. 357 intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON); 358 intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 359 context.registerReceiver(mMediaButtonReceiver, intentFilter); 360 361 // Register for phone state monitoring 362 TelephonyManager tmgr = (TelephonyManager) 363 context.getSystemService(Context.TELEPHONY_SERVICE); 364 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 365 } 366 367 private void createAudioSystemThread() { 368 mAudioSystemThread = new AudioSystemThread(); 369 mAudioSystemThread.start(); 370 waitForAudioHandlerCreation(); 371 } 372 373 /** Waits for the volume handler to be created by the other thread. */ 374 private void waitForAudioHandlerCreation() { 375 synchronized(this) { 376 while (mAudioHandler == null) { 377 try { 378 // Wait for mAudioHandler to be set by the other thread 379 wait(); 380 } catch (InterruptedException e) { 381 Log.e(TAG, "Interrupted while waiting on volume handler."); 382 } 383 } 384 } 385 } 386 387 private void createStreamStates() { 388 int numStreamTypes = AudioSystem.getNumStreamTypes(); 389 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes]; 390 391 for (int i = 0; i < numStreamTypes; i++) { 392 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i); 393 } 394 395 // Correct stream index values for streams with aliases 396 for (int i = 0; i < numStreamTypes; i++) { 397 if (STREAM_VOLUME_ALIAS[i] != i) { 398 int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i); 399 streams[i].mIndex = streams[i].getValidIndex(index); 400 setStreamVolumeIndex(i, index); 401 index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i); 402 streams[i].mLastAudibleIndex = streams[i].getValidIndex(index); 403 } 404 } 405 } 406 407 private void readPersistedSettings() { 408 final ContentResolver cr = mContentResolver; 409 410 mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL); 411 412 mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0); 413 414 mRingerModeAffectedStreams = Settings.System.getInt(cr, 415 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 416 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)| 417 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)| 418 (1 << AudioSystem.STREAM_MUSIC))); 419 420 mMuteAffectedStreams = System.getInt(cr, 421 System.MUTE_STREAMS_AFFECTED, 422 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM))); 423 424 if (mVoiceCapable) { 425 mNotificationsUseRingVolume = System.getInt(cr, 426 Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1); 427 } else { 428 mNotificationsUseRingVolume = 1; 429 } 430 431 if (mNotificationsUseRingVolume == 1) { 432 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING; 433 } 434 // Each stream will read its own persisted settings 435 436 // Broadcast the sticky intent 437 broadcastRingerMode(); 438 439 // Broadcast vibrate settings 440 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); 441 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); 442 } 443 444 private void setStreamVolumeIndex(int stream, int index) { 445 AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10); 446 } 447 448 private int rescaleIndex(int index, int srcStream, int dstStream) { 449 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex(); 450 } 451 452 /////////////////////////////////////////////////////////////////////////// 453 // IPC methods 454 /////////////////////////////////////////////////////////////////////////// 455 456 /** @see AudioManager#adjustVolume(int, int) */ 457 public void adjustVolume(int direction, int flags) { 458 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags); 459 } 460 461 /** @see AudioManager#adjustVolume(int, int, int) */ 462 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { 463 464 int streamType = getActiveStreamType(suggestedStreamType); 465 466 // Don't play sound on other streams 467 if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) { 468 flags &= ~AudioManager.FLAG_PLAY_SOUND; 469 } 470 471 adjustStreamVolume(streamType, direction, flags); 472 } 473 474 /** @see AudioManager#adjustStreamVolume(int, int, int) */ 475 public void adjustStreamVolume(int streamType, int direction, int flags) { 476 ensureValidDirection(direction); 477 ensureValidStreamType(streamType); 478 479 480 VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]]; 481 final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex; 482 boolean adjustVolume = true; 483 484 // If either the client forces allowing ringer modes for this adjustment, 485 // or the stream type is one that is affected by ringer modes 486 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || 487 (!mVoiceCapable && streamType != AudioSystem.STREAM_VOICE_CALL && 488 streamType != AudioSystem.STREAM_BLUETOOTH_SCO) || 489 (mVoiceCapable && streamType == AudioSystem.STREAM_RING)) { 490 // Check if the ringer mode changes with this volume adjustment. If 491 // it does, it will handle adjusting the volume, so we won't below 492 adjustVolume = checkForRingerModeChange(oldIndex, direction); 493 } 494 495 // If stream is muted, adjust last audible index only 496 int index; 497 if (streamState.muteCount() != 0) { 498 if (adjustVolume) { 499 streamState.adjustLastAudibleIndex(direction); 500 // Post a persist volume msg 501 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType, 502 SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY); 503 } 504 index = streamState.mLastAudibleIndex; 505 } else { 506 if (adjustVolume && streamState.adjustIndex(direction)) { 507 // Post message to set system volume (it in turn will post a message 508 // to persist). Do not change volume if stream is muted. 509 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0, 510 streamState, 0); 511 } 512 index = streamState.mIndex; 513 } 514 515 sendVolumeUpdate(streamType, oldIndex, index, flags); 516 } 517 518 /** @see AudioManager#setStreamVolume(int, int, int) */ 519 public void setStreamVolume(int streamType, int index, int flags) { 520 ensureValidStreamType(streamType); 521 VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]]; 522 523 final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex; 524 525 index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]); 526 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true); 527 528 index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex; 529 530 sendVolumeUpdate(streamType, oldIndex, index, flags); 531 } 532 533 // UI update and Broadcast Intent 534 private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) { 535 if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) { 536 streamType = AudioSystem.STREAM_NOTIFICATION; 537 } 538 539 mVolumePanel.postVolumeChanged(streamType, flags); 540 541 oldIndex = (oldIndex + 5) / 10; 542 index = (index + 5) / 10; 543 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION); 544 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType); 545 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index); 546 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex); 547 mContext.sendBroadcast(intent); 548 } 549 550 /** 551 * Sets the stream state's index, and posts a message to set system volume. 552 * This will not call out to the UI. Assumes a valid stream type. 553 * 554 * @param streamType Type of the stream 555 * @param index Desired volume index of the stream 556 * @param force If true, set the volume even if the desired volume is same 557 * as the current volume. 558 * @param lastAudible If true, stores new index as last audible one 559 */ 560 private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) { 561 VolumeStreamState streamState = mStreamStates[streamType]; 562 563 // If stream is muted, set last audible index only 564 if (streamState.muteCount() != 0) { 565 // Do not allow last audible index to be 0 566 if (index != 0) { 567 streamState.setLastAudibleIndex(index); 568 // Post a persist volume msg 569 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType, 570 SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY); 571 } 572 } else { 573 if (streamState.setIndex(index, lastAudible) || force) { 574 // Post message to set system volume (it in turn will post a message 575 // to persist). 576 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0, 577 streamState, 0); 578 } 579 } 580 } 581 582 /** @see AudioManager#setStreamSolo(int, boolean) */ 583 public void setStreamSolo(int streamType, boolean state, IBinder cb) { 584 for (int stream = 0; stream < mStreamStates.length; stream++) { 585 if (!isStreamAffectedByMute(stream) || stream == streamType) continue; 586 // Bring back last audible volume 587 mStreamStates[stream].mute(cb, state); 588 } 589 } 590 591 /** @see AudioManager#setStreamMute(int, boolean) */ 592 public void setStreamMute(int streamType, boolean state, IBinder cb) { 593 if (isStreamAffectedByMute(streamType)) { 594 mStreamStates[streamType].mute(cb, state); 595 } 596 } 597 598 /** get stream mute state. */ 599 public boolean isStreamMute(int streamType) { 600 return (mStreamStates[streamType].muteCount() != 0); 601 } 602 603 /** @see AudioManager#getStreamVolume(int) */ 604 public int getStreamVolume(int streamType) { 605 ensureValidStreamType(streamType); 606 return (mStreamStates[streamType].mIndex + 5) / 10; 607 } 608 609 /** @see AudioManager#getStreamMaxVolume(int) */ 610 public int getStreamMaxVolume(int streamType) { 611 ensureValidStreamType(streamType); 612 return (mStreamStates[streamType].getMaxIndex() + 5) / 10; 613 } 614 615 616 /** Get last audible volume before stream was muted. */ 617 public int getLastAudibleStreamVolume(int streamType) { 618 ensureValidStreamType(streamType); 619 return (mStreamStates[streamType].mLastAudibleIndex + 5) / 10; 620 } 621 622 /** @see AudioManager#getRingerMode() */ 623 public int getRingerMode() { 624 return mRingerMode; 625 } 626 627 /** @see AudioManager#setRingerMode(int) */ 628 public void setRingerMode(int ringerMode) { 629 synchronized (mSettingsLock) { 630 if (ringerMode != mRingerMode) { 631 setRingerModeInt(ringerMode, true); 632 // Send sticky broadcast 633 broadcastRingerMode(); 634 } 635 } 636 } 637 638 private void setRingerModeInt(int ringerMode, boolean persist) { 639 mRingerMode = ringerMode; 640 641 // Mute stream if not previously muted by ringer mode and ringer mode 642 // is not RINGER_MODE_NORMAL and stream is affected by ringer mode. 643 // Unmute stream if previously muted by ringer mode and ringer mode 644 // is RINGER_MODE_NORMAL or stream is not affected by ringer mode. 645 int numStreamTypes = AudioSystem.getNumStreamTypes(); 646 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 647 if (isStreamMutedByRingerMode(streamType)) { 648 if (!isStreamAffectedByRingerMode(streamType) || 649 mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 650 mStreamStates[streamType].mute(null, false); 651 mRingerModeMutedStreams &= ~(1 << streamType); 652 } 653 } else { 654 if (isStreamAffectedByRingerMode(streamType) && 655 mRingerMode != AudioManager.RINGER_MODE_NORMAL) { 656 mStreamStates[streamType].mute(null, true); 657 mRingerModeMutedStreams |= (1 << streamType); 658 } 659 } 660 } 661 662 // Post a persist ringer mode msg 663 if (persist) { 664 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG, 665 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY); 666 } 667 } 668 669 /** @see AudioManager#shouldVibrate(int) */ 670 public boolean shouldVibrate(int vibrateType) { 671 672 switch (getVibrateSetting(vibrateType)) { 673 674 case AudioManager.VIBRATE_SETTING_ON: 675 return mRingerMode != AudioManager.RINGER_MODE_SILENT; 676 677 case AudioManager.VIBRATE_SETTING_ONLY_SILENT: 678 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE; 679 680 case AudioManager.VIBRATE_SETTING_OFF: 681 // return false, even for incoming calls 682 return false; 683 684 default: 685 return false; 686 } 687 } 688 689 /** @see AudioManager#getVibrateSetting(int) */ 690 public int getVibrateSetting(int vibrateType) { 691 return (mVibrateSetting >> (vibrateType * 2)) & 3; 692 } 693 694 /** @see AudioManager#setVibrateSetting(int, int) */ 695 public void setVibrateSetting(int vibrateType, int vibrateSetting) { 696 697 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting); 698 699 // Broadcast change 700 broadcastVibrateSetting(vibrateType); 701 702 // Post message to set ringer mode (it in turn will post a message 703 // to persist) 704 sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0, 705 null, 0); 706 } 707 708 /** 709 * @see #setVibrateSetting(int, int) 710 */ 711 public static int getValueForVibrateSetting(int existingValue, int vibrateType, 712 int vibrateSetting) { 713 714 // First clear the existing setting. Each vibrate type has two bits in 715 // the value. Note '3' is '11' in binary. 716 existingValue &= ~(3 << (vibrateType * 2)); 717 718 // Set into the old value 719 existingValue |= (vibrateSetting & 3) << (vibrateType * 2); 720 721 return existingValue; 722 } 723 724 private class SetModeDeathHandler implements IBinder.DeathRecipient { 725 private IBinder mCb; // To be notified of client's death 726 private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client 727 728 SetModeDeathHandler(IBinder cb) { 729 mCb = cb; 730 } 731 732 public void binderDied() { 733 synchronized(mSetModeDeathHandlers) { 734 Log.w(TAG, "setMode() client died"); 735 int index = mSetModeDeathHandlers.indexOf(this); 736 if (index < 0) { 737 Log.w(TAG, "unregistered setMode() client died"); 738 } else { 739 mSetModeDeathHandlers.remove(this); 740 // If dead client was a the top of client list, 741 // apply next mode in the stack 742 if (index == 0) { 743 // mSetModeDeathHandlers is never empty as the initial entry 744 // created when AudioService starts is never removed 745 SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0); 746 int mode = hdlr.getMode(); 747 if (AudioService.this.mMode != mode) { 748 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { 749 AudioService.this.mMode = mode; 750 if (mode != AudioSystem.MODE_NORMAL) { 751 synchronized(mScoClients) { 752 checkScoAudioState(); 753 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { 754 mBluetoothHeadset.stopVoiceRecognition( 755 mBluetoothHeadsetDevice); 756 mBluetoothHeadset.stopScoUsingVirtualVoiceCall( 757 mBluetoothHeadsetDevice); 758 } else { 759 clearAllScoClients(mCb, true); 760 } 761 } 762 } 763 } 764 } 765 } 766 } 767 } 768 } 769 770 public void setMode(int mode) { 771 mMode = mode; 772 } 773 774 public int getMode() { 775 return mMode; 776 } 777 778 public IBinder getBinder() { 779 return mCb; 780 } 781 } 782 783 /** @see AudioManager#setMode(int) */ 784 public void setMode(int mode, IBinder cb) { 785 if (!checkAudioSettingsPermission("setMode()")) { 786 return; 787 } 788 789 if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) { 790 return; 791 } 792 793 synchronized (mSettingsLock) { 794 if (mode == AudioSystem.MODE_CURRENT) { 795 mode = mMode; 796 } 797 if (mode != mMode) { 798 799 // automatically handle audio focus for mode changes 800 handleFocusForCalls(mMode, mode); 801 802 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { 803 mMode = mode; 804 805 synchronized(mSetModeDeathHandlers) { 806 SetModeDeathHandler hdlr = null; 807 Iterator iter = mSetModeDeathHandlers.iterator(); 808 while (iter.hasNext()) { 809 SetModeDeathHandler h = (SetModeDeathHandler)iter.next(); 810 if (h.getBinder() == cb) { 811 hdlr = h; 812 // Remove from client list so that it is re-inserted at top of list 813 iter.remove(); 814 break; 815 } 816 } 817 if (hdlr == null) { 818 hdlr = new SetModeDeathHandler(cb); 819 // cb is null when setMode() is called by AudioService constructor 820 if (cb != null) { 821 // Register for client death notification 822 try { 823 cb.linkToDeath(hdlr, 0); 824 } catch (RemoteException e) { 825 // Client has died! 826 Log.w(TAG, "setMode() could not link to "+cb+" binder death"); 827 } 828 } 829 } 830 // Last client to call setMode() is always at top of client list 831 // as required by SetModeDeathHandler.binderDied() 832 mSetModeDeathHandlers.add(0, hdlr); 833 hdlr.setMode(mode); 834 } 835 836 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all 837 // SCO connections not started by the application changing the mode 838 if (mode != AudioSystem.MODE_NORMAL) { 839 synchronized(mScoClients) { 840 checkScoAudioState(); 841 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL) { 842 mBluetoothHeadset.stopVoiceRecognition(mBluetoothHeadsetDevice); 843 mBluetoothHeadset.stopScoUsingVirtualVoiceCall( 844 mBluetoothHeadsetDevice); 845 } else { 846 clearAllScoClients(cb, true); 847 } 848 } 849 } 850 } 851 } 852 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); 853 int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex; 854 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false); 855 } 856 } 857 858 /** pre-condition: oldMode != newMode */ 859 private void handleFocusForCalls(int oldMode, int newMode) { 860 // if ringing 861 if (newMode == AudioSystem.MODE_RINGTONE) { 862 // if not ringing silently 863 int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING); 864 if (ringVolume > 0) { 865 // request audio focus for the communication focus entry 866 requestAudioFocus(AudioManager.STREAM_RING, 867 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, 868 null, null /* both allowed to be null only for this clientId */, 869 IN_VOICE_COMM_FOCUS_ID /*clientId*/); 870 871 } 872 } 873 // if entering call 874 else if ((newMode == AudioSystem.MODE_IN_CALL) 875 || (newMode == AudioSystem.MODE_IN_COMMUNICATION)) { 876 // request audio focus for the communication focus entry 877 // (it's ok if focus was already requested during ringing) 878 requestAudioFocus(AudioManager.STREAM_RING, 879 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, 880 null, null /* both allowed to be null only for this clientId */, 881 IN_VOICE_COMM_FOCUS_ID /*clientId*/); 882 } 883 // if exiting call 884 else if (newMode == AudioSystem.MODE_NORMAL) { 885 // abandon audio focus for communication focus entry 886 abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID); 887 } 888 } 889 890 /** @see AudioManager#getMode() */ 891 public int getMode() { 892 return mMode; 893 } 894 895 /** @see AudioManager#playSoundEffect(int) */ 896 public void playSoundEffect(int effectType) { 897 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP, 898 effectType, -1, null, 0); 899 } 900 901 /** @see AudioManager#playSoundEffect(int, float) */ 902 public void playSoundEffectVolume(int effectType, float volume) { 903 loadSoundEffects(); 904 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP, 905 effectType, (int) (volume * 1000), null, 0); 906 } 907 908 /** 909 * Loads samples into the soundpool. 910 * This method must be called at when sound effects are enabled 911 */ 912 public boolean loadSoundEffects() { 913 int status; 914 915 synchronized (mSoundEffectsLock) { 916 if (!mBootCompleted) { 917 Log.w(TAG, "loadSoundEffects() called before boot complete"); 918 return false; 919 } 920 921 if (mSoundPool != null) { 922 return true; 923 } 924 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0); 925 if (mSoundPool == null) { 926 Log.w(TAG, "loadSoundEffects() could not allocate sound pool"); 927 return false; 928 } 929 930 try { 931 mSoundPoolCallBack = null; 932 mSoundPoolListenerThread = new SoundPoolListenerThread(); 933 mSoundPoolListenerThread.start(); 934 // Wait for mSoundPoolCallBack to be set by the other thread 935 mSoundEffectsLock.wait(); 936 } catch (InterruptedException e) { 937 Log.w(TAG, "Interrupted while waiting sound pool listener thread."); 938 } 939 940 if (mSoundPoolCallBack == null) { 941 Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread"); 942 if (mSoundPoolLooper != null) { 943 mSoundPoolLooper.quit(); 944 mSoundPoolLooper = null; 945 } 946 mSoundPoolListenerThread = null; 947 mSoundPool.release(); 948 mSoundPool = null; 949 return false; 950 } 951 /* 952 * poolId table: The value -1 in this table indicates that corresponding 953 * file (same index in SOUND_EFFECT_FILES[] has not been loaded. 954 * Once loaded, the value in poolId is the sample ID and the same 955 * sample can be reused for another effect using the same file. 956 */ 957 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 958 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 959 poolId[fileIdx] = -1; 960 } 961 /* 962 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded. 963 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0: 964 * this indicates we have a valid sample loaded for this effect. 965 */ 966 967 int lastSample = 0; 968 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 969 // Do not load sample if this effect uses the MediaPlayer 970 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) { 971 continue; 972 } 973 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) { 974 String filePath = Environment.getRootDirectory() 975 + SOUND_EFFECTS_PATH 976 + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]]; 977 int sampleId = mSoundPool.load(filePath, 0); 978 if (sampleId <= 0) { 979 Log.w(TAG, "Soundpool could not load file: "+filePath); 980 } else { 981 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId; 982 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId; 983 lastSample = sampleId; 984 } 985 } else { 986 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]]; 987 } 988 } 989 // wait for all samples to be loaded 990 if (lastSample != 0) { 991 mSoundPoolCallBack.setLastSample(lastSample); 992 993 try { 994 mSoundEffectsLock.wait(); 995 status = mSoundPoolCallBack.status(); 996 } catch (java.lang.InterruptedException e) { 997 Log.w(TAG, "Interrupted while waiting sound pool callback."); 998 status = -1; 999 } 1000 } else { 1001 status = -1; 1002 } 1003 1004 if (mSoundPoolLooper != null) { 1005 mSoundPoolLooper.quit(); 1006 mSoundPoolLooper = null; 1007 } 1008 mSoundPoolListenerThread = null; 1009 if (status != 0) { 1010 Log.w(TAG, 1011 "loadSoundEffects(), Error " 1012 + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1) 1013 + " while loading samples"); 1014 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 1015 if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) { 1016 SOUND_EFFECT_FILES_MAP[effect][1] = -1; 1017 } 1018 } 1019 1020 mSoundPool.release(); 1021 mSoundPool = null; 1022 } 1023 } 1024 return (status == 0); 1025 } 1026 1027 /** 1028 * Unloads samples from the sound pool. 1029 * This method can be called to free some memory when 1030 * sound effects are disabled. 1031 */ 1032 public void unloadSoundEffects() { 1033 synchronized (mSoundEffectsLock) { 1034 if (mSoundPool == null) { 1035 return; 1036 } 1037 1038 mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS); 1039 mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT); 1040 1041 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 1042 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 1043 poolId[fileIdx] = 0; 1044 } 1045 1046 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 1047 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) { 1048 continue; 1049 } 1050 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) { 1051 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]); 1052 SOUND_EFFECT_FILES_MAP[effect][1] = -1; 1053 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1; 1054 } 1055 } 1056 mSoundPool.release(); 1057 mSoundPool = null; 1058 } 1059 } 1060 1061 class SoundPoolListenerThread extends Thread { 1062 public SoundPoolListenerThread() { 1063 super("SoundPoolListenerThread"); 1064 } 1065 1066 @Override 1067 public void run() { 1068 1069 Looper.prepare(); 1070 mSoundPoolLooper = Looper.myLooper(); 1071 1072 synchronized (mSoundEffectsLock) { 1073 if (mSoundPool != null) { 1074 mSoundPoolCallBack = new SoundPoolCallback(); 1075 mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack); 1076 } 1077 mSoundEffectsLock.notify(); 1078 } 1079 Looper.loop(); 1080 } 1081 } 1082 1083 private final class SoundPoolCallback implements 1084 android.media.SoundPool.OnLoadCompleteListener { 1085 1086 int mStatus; 1087 int mLastSample; 1088 1089 public int status() { 1090 return mStatus; 1091 } 1092 1093 public void setLastSample(int sample) { 1094 mLastSample = sample; 1095 } 1096 1097 public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { 1098 synchronized (mSoundEffectsLock) { 1099 if (status != 0) { 1100 mStatus = status; 1101 } 1102 if (sampleId == mLastSample) { 1103 mSoundEffectsLock.notify(); 1104 } 1105 } 1106 } 1107 } 1108 1109 /** @see AudioManager#reloadAudioSettings() */ 1110 public void reloadAudioSettings() { 1111 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings 1112 readPersistedSettings(); 1113 1114 // restore volume settings 1115 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1116 for (int streamType = 0; streamType < numStreamTypes; streamType++) { 1117 VolumeStreamState streamState = mStreamStates[streamType]; 1118 1119 String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]]; 1120 String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; 1121 int index = Settings.System.getInt(mContentResolver, 1122 settingName, 1123 AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1124 if (STREAM_VOLUME_ALIAS[streamType] != streamType) { 1125 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType); 1126 } else { 1127 index *= 10; 1128 } 1129 streamState.mIndex = streamState.getValidIndex(index); 1130 1131 index = (index + 5) / 10; 1132 index = Settings.System.getInt(mContentResolver, 1133 lastAudibleSettingName, 1134 (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1135 if (STREAM_VOLUME_ALIAS[streamType] != streamType) { 1136 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType); 1137 } else { 1138 index *= 10; 1139 } 1140 streamState.mLastAudibleIndex = streamState.getValidIndex(index); 1141 1142 // unmute stream that was muted but is not affect by mute anymore 1143 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) { 1144 int size = streamState.mDeathHandlers.size(); 1145 for (int i = 0; i < size; i++) { 1146 streamState.mDeathHandlers.get(i).mMuteCount = 1; 1147 streamState.mDeathHandlers.get(i).mute(false); 1148 } 1149 } 1150 // apply stream volume 1151 if (streamState.muteCount() == 0) { 1152 setStreamVolumeIndex(streamType, streamState.mIndex); 1153 } 1154 } 1155 1156 // apply new ringer mode 1157 setRingerModeInt(getRingerMode(), false); 1158 } 1159 1160 /** @see AudioManager#setSpeakerphoneOn() */ 1161 public void setSpeakerphoneOn(boolean on){ 1162 if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { 1163 return; 1164 } 1165 if (on) { 1166 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER); 1167 mForcedUseForComm = AudioSystem.FORCE_SPEAKER; 1168 } else { 1169 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE); 1170 mForcedUseForComm = AudioSystem.FORCE_NONE; 1171 } 1172 } 1173 1174 /** @see AudioManager#isSpeakerphoneOn() */ 1175 public boolean isSpeakerphoneOn() { 1176 if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) { 1177 return true; 1178 } else { 1179 return false; 1180 } 1181 } 1182 1183 /** @see AudioManager#setBluetoothScoOn() */ 1184 public void setBluetoothScoOn(boolean on){ 1185 if (!checkAudioSettingsPermission("setBluetoothScoOn()")) { 1186 return; 1187 } 1188 if (on) { 1189 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO); 1190 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO); 1191 mForcedUseForComm = AudioSystem.FORCE_BT_SCO; 1192 } else { 1193 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE); 1194 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE); 1195 mForcedUseForComm = AudioSystem.FORCE_NONE; 1196 } 1197 } 1198 1199 /** @see AudioManager#isBluetoothScoOn() */ 1200 public boolean isBluetoothScoOn() { 1201 if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { 1202 return true; 1203 } else { 1204 return false; 1205 } 1206 } 1207 1208 /** @see AudioManager#startBluetoothSco() */ 1209 public void startBluetoothSco(IBinder cb){ 1210 if (!checkAudioSettingsPermission("startBluetoothSco()")) { 1211 return; 1212 } 1213 ScoClient client = getScoClient(cb); 1214 client.incCount(); 1215 } 1216 1217 /** @see AudioManager#stopBluetoothSco() */ 1218 public void stopBluetoothSco(IBinder cb){ 1219 if (!checkAudioSettingsPermission("stopBluetoothSco()")) { 1220 return; 1221 } 1222 ScoClient client = getScoClient(cb); 1223 client.decCount(); 1224 } 1225 1226 private class ScoClient implements IBinder.DeathRecipient { 1227 private IBinder mCb; // To be notified of client's death 1228 private int mStartcount; // number of SCO connections started by this client 1229 1230 ScoClient(IBinder cb) { 1231 mCb = cb; 1232 mStartcount = 0; 1233 } 1234 1235 public void binderDied() { 1236 synchronized(mScoClients) { 1237 Log.w(TAG, "SCO client died"); 1238 int index = mScoClients.indexOf(this); 1239 if (index < 0) { 1240 Log.w(TAG, "unregistered SCO client died"); 1241 } else { 1242 clearCount(true); 1243 mScoClients.remove(this); 1244 } 1245 } 1246 } 1247 1248 public void incCount() { 1249 synchronized(mScoClients) { 1250 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED); 1251 if (mStartcount == 0) { 1252 try { 1253 mCb.linkToDeath(this, 0); 1254 } catch (RemoteException e) { 1255 // client has already died! 1256 Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death"); 1257 } 1258 } 1259 mStartcount++; 1260 } 1261 } 1262 1263 public void decCount() { 1264 synchronized(mScoClients) { 1265 if (mStartcount == 0) { 1266 Log.w(TAG, "ScoClient.decCount() already 0"); 1267 } else { 1268 mStartcount--; 1269 if (mStartcount == 0) { 1270 try { 1271 mCb.unlinkToDeath(this, 0); 1272 } catch (NoSuchElementException e) { 1273 Log.w(TAG, "decCount() going to 0 but not registered to binder"); 1274 } 1275 } 1276 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1277 } 1278 } 1279 } 1280 1281 public void clearCount(boolean stopSco) { 1282 synchronized(mScoClients) { 1283 if (mStartcount != 0) { 1284 try { 1285 mCb.unlinkToDeath(this, 0); 1286 } catch (NoSuchElementException e) { 1287 Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder"); 1288 } 1289 } 1290 mStartcount = 0; 1291 if (stopSco) { 1292 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1293 } 1294 } 1295 } 1296 1297 public int getCount() { 1298 return mStartcount; 1299 } 1300 1301 public IBinder getBinder() { 1302 return mCb; 1303 } 1304 1305 public int totalCount() { 1306 synchronized(mScoClients) { 1307 int count = 0; 1308 int size = mScoClients.size(); 1309 for (int i = 0; i < size; i++) { 1310 count += mScoClients.get(i).getCount(); 1311 } 1312 return count; 1313 } 1314 } 1315 1316 private void requestScoState(int state) { 1317 if (mBluetoothHeadset == null) { 1318 return; 1319 } 1320 1321 checkScoAudioState(); 1322 1323 if (totalCount() == 0 && 1324 mBluetoothHeadsetDevice != null) { 1325 // Accept SCO audio activation only in NORMAL audio mode or if the mode is 1326 // currently controlled by the same client. 1327 if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL || 1328 mSetModeDeathHandlers.get(0).getBinder() == mCb) && 1329 state == BluetoothHeadset.STATE_AUDIO_CONNECTED && 1330 mScoAudioState == SCO_STATE_INACTIVE) { 1331 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1332 mBluetoothHeadset.startScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice); 1333 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED && 1334 mScoAudioState == SCO_STATE_ACTIVE_INTERNAL){ 1335 mBluetoothHeadset.stopScoUsingVirtualVoiceCall(mBluetoothHeadsetDevice); 1336 } 1337 } 1338 } 1339 } 1340 1341 private void checkScoAudioState() { 1342 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null && 1343 mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && 1344 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) 1345 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1346 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 1347 } 1348 } 1349 1350 public ScoClient getScoClient(IBinder cb) { 1351 synchronized(mScoClients) { 1352 ScoClient client; 1353 int size = mScoClients.size(); 1354 for (int i = 0; i < size; i++) { 1355 client = mScoClients.get(i); 1356 if (client.getBinder() == cb) 1357 return client; 1358 } 1359 client = new ScoClient(cb); 1360 mScoClients.add(client); 1361 return client; 1362 } 1363 } 1364 1365 public void clearAllScoClients(IBinder exceptBinder, boolean stopSco) { 1366 synchronized(mScoClients) { 1367 int size = mScoClients.size(); 1368 for (int i = 0; i < size; i++) { 1369 if (mScoClients.get(i).getBinder() != exceptBinder) 1370 mScoClients.get(i).clearCount(stopSco); 1371 } 1372 } 1373 } 1374 1375 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = 1376 new BluetoothProfile.ServiceListener() { 1377 public void onServiceConnected(int profile, BluetoothProfile proxy) { 1378 synchronized (mScoClients) { 1379 mBluetoothHeadset = (BluetoothHeadset) proxy; 1380 List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); 1381 if (deviceList.size() > 0) { 1382 mBluetoothHeadsetDevice = deviceList.get(0); 1383 } else { 1384 mBluetoothHeadsetDevice = null; 1385 } 1386 } 1387 } 1388 public void onServiceDisconnected(int profile) { 1389 synchronized (mScoClients) { 1390 if (mBluetoothHeadset != null) { 1391 List<BluetoothDevice> devices = mBluetoothHeadset.getConnectedDevices(); 1392 if (devices.size() == 0) { 1393 mBluetoothHeadsetDevice = null; 1394 clearAllScoClients(null, false); 1395 mScoAudioState = SCO_STATE_INACTIVE; 1396 } 1397 mBluetoothHeadset = null; 1398 } 1399 } 1400 } 1401 }; 1402 1403 /////////////////////////////////////////////////////////////////////////// 1404 // Internal methods 1405 /////////////////////////////////////////////////////////////////////////// 1406 1407 /** 1408 * Checks if the adjustment should change ringer mode instead of just 1409 * adjusting volume. If so, this will set the proper ringer mode and volume 1410 * indices on the stream states. 1411 */ 1412 private boolean checkForRingerModeChange(int oldIndex, int direction) { 1413 boolean adjustVolumeIndex = true; 1414 int newRingerMode = mRingerMode; 1415 1416 if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 1417 // audible mode, at the bottom of the scale 1418 if ((direction == AudioManager.ADJUST_LOWER && 1419 mPrevVolDirection != AudioManager.ADJUST_LOWER) && 1420 ((oldIndex + 5) / 10 == 0)) { 1421 // "silent mode", but which one? 1422 newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1 1423 ? AudioManager.RINGER_MODE_VIBRATE 1424 : AudioManager.RINGER_MODE_SILENT; 1425 } 1426 } else { 1427 if (direction == AudioManager.ADJUST_RAISE) { 1428 // exiting silent mode 1429 newRingerMode = AudioManager.RINGER_MODE_NORMAL; 1430 } else { 1431 // prevent last audible index to reach 0 1432 adjustVolumeIndex = false; 1433 } 1434 } 1435 1436 if (newRingerMode != mRingerMode) { 1437 setRingerMode(newRingerMode); 1438 1439 /* 1440 * If we are changing ringer modes, do not increment/decrement the 1441 * volume index. Instead, the handler for the message above will 1442 * take care of changing the index. 1443 */ 1444 adjustVolumeIndex = false; 1445 } 1446 1447 mPrevVolDirection = direction; 1448 1449 return adjustVolumeIndex; 1450 } 1451 1452 public boolean isStreamAffectedByRingerMode(int streamType) { 1453 return (mRingerModeAffectedStreams & (1 << streamType)) != 0; 1454 } 1455 1456 private boolean isStreamMutedByRingerMode(int streamType) { 1457 return (mRingerModeMutedStreams & (1 << streamType)) != 0; 1458 } 1459 1460 public boolean isStreamAffectedByMute(int streamType) { 1461 return (mMuteAffectedStreams & (1 << streamType)) != 0; 1462 } 1463 1464 private void ensureValidDirection(int direction) { 1465 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) { 1466 throw new IllegalArgumentException("Bad direction " + direction); 1467 } 1468 } 1469 1470 private void ensureValidStreamType(int streamType) { 1471 if (streamType < 0 || streamType >= mStreamStates.length) { 1472 throw new IllegalArgumentException("Bad stream type " + streamType); 1473 } 1474 } 1475 1476 private int getActiveStreamType(int suggestedStreamType) { 1477 1478 if (mVoiceCapable) { 1479 boolean isOffhook = false; 1480 try { 1481 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 1482 if (phone != null) isOffhook = phone.isOffhook(); 1483 } catch (RemoteException e) { 1484 Log.w(TAG, "Couldn't connect to phone service", e); 1485 } 1486 1487 if (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION) { 1488 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) 1489 == AudioSystem.FORCE_BT_SCO) { 1490 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); 1491 return AudioSystem.STREAM_BLUETOOTH_SCO; 1492 } else { 1493 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); 1494 return AudioSystem.STREAM_VOICE_CALL; 1495 } 1496 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) { 1497 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC..."); 1498 return AudioSystem.STREAM_MUSIC; 1499 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { 1500 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..." 1501 // + " b/c USE_DEFAULT_STREAM_TYPE..."); 1502 return AudioSystem.STREAM_RING; 1503 } else { 1504 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType); 1505 return suggestedStreamType; 1506 } 1507 } else { 1508 if (getMode() == AudioManager.MODE_IN_COMMUNICATION) { 1509 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) 1510 == AudioSystem.FORCE_BT_SCO) { 1511 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); 1512 return AudioSystem.STREAM_BLUETOOTH_SCO; 1513 } else { 1514 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); 1515 return AudioSystem.STREAM_VOICE_CALL; 1516 } 1517 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION, 1518 NOTIFICATION_VOLUME_DELAY_MS) || 1519 AudioSystem.isStreamActive(AudioSystem.STREAM_RING, 1520 NOTIFICATION_VOLUME_DELAY_MS)) { 1521 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION..."); 1522 return AudioSystem.STREAM_NOTIFICATION; 1523 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) || 1524 (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) { 1525 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC " 1526 // + " b/c USE_DEFAULT_STREAM_TYPE..."); 1527 return AudioSystem.STREAM_MUSIC; 1528 } else { 1529 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType); 1530 return suggestedStreamType; 1531 } 1532 } 1533 } 1534 1535 private void broadcastRingerMode() { 1536 // Send sticky broadcast 1537 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION); 1538 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode); 1539 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1540 | Intent.FLAG_RECEIVER_REPLACE_PENDING); 1541 long origCallerIdentityToken = Binder.clearCallingIdentity(); 1542 mContext.sendStickyBroadcast(broadcast); 1543 Binder.restoreCallingIdentity(origCallerIdentityToken); 1544 } 1545 1546 private void broadcastVibrateSetting(int vibrateType) { 1547 // Send broadcast 1548 if (ActivityManagerNative.isSystemReady()) { 1549 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); 1550 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); 1551 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); 1552 mContext.sendBroadcast(broadcast); 1553 } 1554 } 1555 1556 // Message helper methods 1557 private static int getMsg(int baseMsg, int streamType) { 1558 return (baseMsg & 0xffff) | streamType << 16; 1559 } 1560 1561 private static int getMsgBase(int msg) { 1562 return msg & 0xffff; 1563 } 1564 1565 private static void sendMsg(Handler handler, int baseMsg, int streamType, 1566 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) { 1567 int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType); 1568 1569 if (existingMsgPolicy == SENDMSG_REPLACE) { 1570 handler.removeMessages(msg); 1571 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) { 1572 return; 1573 } 1574 1575 handler 1576 .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay); 1577 } 1578 1579 boolean checkAudioSettingsPermission(String method) { 1580 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS") 1581 == PackageManager.PERMISSION_GRANTED) { 1582 return true; 1583 } 1584 String msg = "Audio Settings Permission Denial: " + method + " from pid=" 1585 + Binder.getCallingPid() 1586 + ", uid=" + Binder.getCallingUid(); 1587 Log.w(TAG, msg); 1588 return false; 1589 } 1590 1591 1592 /////////////////////////////////////////////////////////////////////////// 1593 // Inner classes 1594 /////////////////////////////////////////////////////////////////////////// 1595 1596 public class VolumeStreamState { 1597 private final int mStreamType; 1598 1599 private String mVolumeIndexSettingName; 1600 private String mLastAudibleVolumeIndexSettingName; 1601 private int mIndexMax; 1602 private int mIndex; 1603 private int mLastAudibleIndex; 1604 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death 1605 1606 private VolumeStreamState(String settingName, int streamType) { 1607 1608 setVolumeIndexSettingName(settingName); 1609 1610 mStreamType = streamType; 1611 1612 final ContentResolver cr = mContentResolver; 1613 mIndexMax = MAX_STREAM_VOLUME[streamType]; 1614 mIndex = Settings.System.getInt(cr, 1615 mVolumeIndexSettingName, 1616 AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1617 mLastAudibleIndex = Settings.System.getInt(cr, 1618 mLastAudibleVolumeIndexSettingName, 1619 (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1620 AudioSystem.initStreamVolume(streamType, 0, mIndexMax); 1621 mIndexMax *= 10; 1622 mIndex = getValidIndex(10 * mIndex); 1623 mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex); 1624 setStreamVolumeIndex(streamType, mIndex); 1625 mDeathHandlers = new ArrayList<VolumeDeathHandler>(); 1626 } 1627 1628 public void setVolumeIndexSettingName(String settingName) { 1629 mVolumeIndexSettingName = settingName; 1630 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; 1631 } 1632 1633 public boolean adjustIndex(int deltaIndex) { 1634 return setIndex(mIndex + deltaIndex * 10, true); 1635 } 1636 1637 public boolean setIndex(int index, boolean lastAudible) { 1638 int oldIndex = mIndex; 1639 mIndex = getValidIndex(index); 1640 1641 if (oldIndex != mIndex) { 1642 if (lastAudible) { 1643 mLastAudibleIndex = mIndex; 1644 } 1645 // Apply change to all streams using this one as alias 1646 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1647 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1648 if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) { 1649 mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible); 1650 } 1651 } 1652 return true; 1653 } else { 1654 return false; 1655 } 1656 } 1657 1658 public void setLastAudibleIndex(int index) { 1659 mLastAudibleIndex = getValidIndex(index); 1660 } 1661 1662 public void adjustLastAudibleIndex(int deltaIndex) { 1663 setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10); 1664 } 1665 1666 public int getMaxIndex() { 1667 return mIndexMax; 1668 } 1669 1670 public void mute(IBinder cb, boolean state) { 1671 VolumeDeathHandler handler = getDeathHandler(cb, state); 1672 if (handler == null) { 1673 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType); 1674 return; 1675 } 1676 handler.mute(state); 1677 } 1678 1679 private int getValidIndex(int index) { 1680 if (index < 0) { 1681 return 0; 1682 } else if (index > mIndexMax) { 1683 return mIndexMax; 1684 } 1685 1686 return index; 1687 } 1688 1689 private class VolumeDeathHandler implements IBinder.DeathRecipient { 1690 private IBinder mICallback; // To be notified of client's death 1691 private int mMuteCount; // Number of active mutes for this client 1692 1693 VolumeDeathHandler(IBinder cb) { 1694 mICallback = cb; 1695 } 1696 1697 public void mute(boolean state) { 1698 synchronized(mDeathHandlers) { 1699 if (state) { 1700 if (mMuteCount == 0) { 1701 // Register for client death notification 1702 try { 1703 // mICallback can be 0 if muted by AudioService 1704 if (mICallback != null) { 1705 mICallback.linkToDeath(this, 0); 1706 } 1707 mDeathHandlers.add(this); 1708 // If the stream is not yet muted by any client, set lvel to 0 1709 if (muteCount() == 0) { 1710 setIndex(0, false); 1711 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, 1712 VolumeStreamState.this, 0); 1713 } 1714 } catch (RemoteException e) { 1715 // Client has died! 1716 binderDied(); 1717 mDeathHandlers.notify(); 1718 return; 1719 } 1720 } else { 1721 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client"); 1722 } 1723 mMuteCount++; 1724 } else { 1725 if (mMuteCount == 0) { 1726 Log.e(TAG, "unexpected unmute for stream: "+mStreamType); 1727 } else { 1728 mMuteCount--; 1729 if (mMuteCount == 0) { 1730 // Unregistr from client death notification 1731 mDeathHandlers.remove(this); 1732 // mICallback can be 0 if muted by AudioService 1733 if (mICallback != null) { 1734 mICallback.unlinkToDeath(this, 0); 1735 } 1736 if (muteCount() == 0) { 1737 // If the stream is not muted any more, restore it's volume if 1738 // ringer mode allows it 1739 if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 1740 setIndex(mLastAudibleIndex, false); 1741 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, 1742 VolumeStreamState.this, 0); 1743 } 1744 } 1745 } 1746 } 1747 } 1748 mDeathHandlers.notify(); 1749 } 1750 } 1751 1752 public void binderDied() { 1753 Log.w(TAG, "Volume service client died for stream: "+mStreamType); 1754 if (mMuteCount != 0) { 1755 // Reset all active mute requests from this client. 1756 mMuteCount = 1; 1757 mute(false); 1758 } 1759 } 1760 } 1761 1762 private int muteCount() { 1763 int count = 0; 1764 int size = mDeathHandlers.size(); 1765 for (int i = 0; i < size; i++) { 1766 count += mDeathHandlers.get(i).mMuteCount; 1767 } 1768 return count; 1769 } 1770 1771 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) { 1772 synchronized(mDeathHandlers) { 1773 VolumeDeathHandler handler; 1774 int size = mDeathHandlers.size(); 1775 for (int i = 0; i < size; i++) { 1776 handler = mDeathHandlers.get(i); 1777 if (cb == handler.mICallback) { 1778 return handler; 1779 } 1780 } 1781 // If this is the first mute request for this client, create a new 1782 // client death handler. Otherwise, it is an out of sequence unmute request. 1783 if (state) { 1784 handler = new VolumeDeathHandler(cb); 1785 } else { 1786 Log.w(TAG, "stream was not muted by this client"); 1787 handler = null; 1788 } 1789 return handler; 1790 } 1791 } 1792 } 1793 1794 /** Thread that handles native AudioSystem control. */ 1795 private class AudioSystemThread extends Thread { 1796 AudioSystemThread() { 1797 super("AudioService"); 1798 } 1799 1800 @Override 1801 public void run() { 1802 // Set this thread up so the handler will work on it 1803 Looper.prepare(); 1804 1805 synchronized(AudioService.this) { 1806 mAudioHandler = new AudioHandler(); 1807 1808 // Notify that the handler has been created 1809 AudioService.this.notify(); 1810 } 1811 1812 // Listen for volume change requests that are set by VolumePanel 1813 Looper.loop(); 1814 } 1815 } 1816 1817 /** Handles internal volume messages in separate volume thread. */ 1818 private class AudioHandler extends Handler { 1819 1820 private void setSystemVolume(VolumeStreamState streamState) { 1821 1822 // Adjust volume 1823 setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex); 1824 1825 // Apply change to all streams using this one as alias 1826 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1827 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1828 if (streamType != streamState.mStreamType && 1829 STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) { 1830 setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex); 1831 } 1832 } 1833 1834 // Post a persist volume msg 1835 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType, 1836 SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY); 1837 } 1838 1839 private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) { 1840 if (current) { 1841 System.putInt(mContentResolver, streamState.mVolumeIndexSettingName, 1842 (streamState.mIndex + 5)/ 10); 1843 } 1844 if (lastAudible) { 1845 System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName, 1846 (streamState.mLastAudibleIndex + 5) / 10); 1847 } 1848 } 1849 1850 private void persistRingerMode() { 1851 System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode); 1852 } 1853 1854 private void persistVibrateSetting() { 1855 System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting); 1856 } 1857 1858 private void playSoundEffect(int effectType, int volume) { 1859 synchronized (mSoundEffectsLock) { 1860 if (mSoundPool == null) { 1861 return; 1862 } 1863 float volFloat; 1864 // use default if volume is not specified by caller 1865 if (volume < 0) { 1866 volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20); 1867 } else { 1868 volFloat = (float) volume / 1000.0f; 1869 } 1870 1871 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) { 1872 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f); 1873 } else { 1874 MediaPlayer mediaPlayer = new MediaPlayer(); 1875 if (mediaPlayer != null) { 1876 try { 1877 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]]; 1878 mediaPlayer.setDataSource(filePath); 1879 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM); 1880 mediaPlayer.prepare(); 1881 mediaPlayer.setVolume(volFloat, volFloat); 1882 mediaPlayer.setOnCompletionListener(new OnCompletionListener() { 1883 public void onCompletion(MediaPlayer mp) { 1884 cleanupPlayer(mp); 1885 } 1886 }); 1887 mediaPlayer.setOnErrorListener(new OnErrorListener() { 1888 public boolean onError(MediaPlayer mp, int what, int extra) { 1889 cleanupPlayer(mp); 1890 return true; 1891 } 1892 }); 1893 mediaPlayer.start(); 1894 } catch (IOException ex) { 1895 Log.w(TAG, "MediaPlayer IOException: "+ex); 1896 } catch (IllegalArgumentException ex) { 1897 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex); 1898 } catch (IllegalStateException ex) { 1899 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 1900 } 1901 } 1902 } 1903 } 1904 } 1905 1906 private void cleanupPlayer(MediaPlayer mp) { 1907 if (mp != null) { 1908 try { 1909 mp.stop(); 1910 mp.release(); 1911 } catch (IllegalStateException ex) { 1912 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 1913 } 1914 } 1915 } 1916 1917 @Override 1918 public void handleMessage(Message msg) { 1919 int baseMsgWhat = getMsgBase(msg.what); 1920 1921 switch (baseMsgWhat) { 1922 1923 case MSG_SET_SYSTEM_VOLUME: 1924 setSystemVolume((VolumeStreamState) msg.obj); 1925 break; 1926 1927 case MSG_PERSIST_VOLUME: 1928 persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0)); 1929 break; 1930 1931 case MSG_PERSIST_RINGER_MODE: 1932 persistRingerMode(); 1933 break; 1934 1935 case MSG_PERSIST_VIBRATE_SETTING: 1936 persistVibrateSetting(); 1937 break; 1938 1939 case MSG_MEDIA_SERVER_DIED: 1940 // Force creation of new IAudioflinger interface 1941 if (!mMediaServerOk) { 1942 Log.e(TAG, "Media server died."); 1943 AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0); 1944 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0, 1945 null, 500); 1946 } 1947 break; 1948 1949 case MSG_MEDIA_SERVER_STARTED: 1950 Log.e(TAG, "Media server started."); 1951 // indicate to audio HAL that we start the reconfiguration phase after a media 1952 // server crash 1953 // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server 1954 // process restarts after a crash, not the first time it is started. 1955 AudioSystem.setParameters("restarting=true"); 1956 1957 // Restore device connection states 1958 Set set = mConnectedDevices.entrySet(); 1959 Iterator i = set.iterator(); 1960 while(i.hasNext()){ 1961 Map.Entry device = (Map.Entry)i.next(); 1962 AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(), 1963 AudioSystem.DEVICE_STATE_AVAILABLE, 1964 (String)device.getValue()); 1965 } 1966 1967 // Restore call state 1968 AudioSystem.setPhoneState(mMode); 1969 1970 // Restore forced usage for communcations and record 1971 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm); 1972 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm); 1973 1974 // Restore stream volumes 1975 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1976 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1977 int index; 1978 VolumeStreamState streamState = mStreamStates[streamType]; 1979 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10); 1980 if (streamState.muteCount() == 0) { 1981 index = streamState.mIndex; 1982 } else { 1983 index = 0; 1984 } 1985 setStreamVolumeIndex(streamType, index); 1986 } 1987 1988 // Restore ringer mode 1989 setRingerModeInt(getRingerMode(), false); 1990 1991 // indicate the end of reconfiguration phase to audio HAL 1992 AudioSystem.setParameters("restarting=false"); 1993 break; 1994 1995 case MSG_LOAD_SOUND_EFFECTS: 1996 loadSoundEffects(); 1997 break; 1998 1999 case MSG_PLAY_SOUND_EFFECT: 2000 playSoundEffect(msg.arg1, msg.arg2); 2001 break; 2002 2003 case MSG_BTA2DP_DOCK_TIMEOUT: 2004 // msg.obj == address of BTA2DP device 2005 makeA2dpDeviceUnavailableNow( (String) msg.obj ); 2006 break; 2007 } 2008 } 2009 } 2010 2011 private class SettingsObserver extends ContentObserver { 2012 2013 SettingsObserver() { 2014 super(new Handler()); 2015 mContentResolver.registerContentObserver(Settings.System.getUriFor( 2016 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this); 2017 mContentResolver.registerContentObserver(Settings.System.getUriFor( 2018 Settings.System.NOTIFICATIONS_USE_RING_VOLUME), false, this); 2019 } 2020 2021 @Override 2022 public void onChange(boolean selfChange) { 2023 super.onChange(selfChange); 2024 synchronized (mSettingsLock) { 2025 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver, 2026 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 2027 0); 2028 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) { 2029 /* 2030 * Ensure all stream types that should be affected by ringer mode 2031 * are in the proper state. 2032 */ 2033 mRingerModeAffectedStreams = ringerModeAffectedStreams; 2034 setRingerModeInt(getRingerMode(), false); 2035 } 2036 2037 int notificationsUseRingVolume = Settings.System.getInt(mContentResolver, 2038 Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 2039 1); 2040 if (mVoiceCapable) { 2041 if (notificationsUseRingVolume != mNotificationsUseRingVolume) { 2042 mNotificationsUseRingVolume = notificationsUseRingVolume; 2043 if (mNotificationsUseRingVolume == 1) { 2044 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING; 2045 mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName( 2046 System.VOLUME_SETTINGS[AudioSystem.STREAM_RING]); 2047 } else { 2048 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION; 2049 mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName( 2050 System.VOLUME_SETTINGS[AudioSystem.STREAM_NOTIFICATION]); 2051 // Persist notification volume volume as it was not persisted while aliased to ring volume 2052 // and persist with no delay as there might be registered observers of the persisted 2053 // notification volume. 2054 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION, 2055 SENDMSG_REPLACE, 1, 1, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0); 2056 } 2057 } 2058 } 2059 } 2060 } 2061 } 2062 2063 private void makeA2dpDeviceAvailable(String address) { 2064 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 2065 AudioSystem.DEVICE_STATE_AVAILABLE, 2066 address); 2067 // Reset A2DP suspend state each time a new sink is connected 2068 AudioSystem.setParameters("A2dpSuspended=false"); 2069 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), 2070 address); 2071 } 2072 2073 private void makeA2dpDeviceUnavailableNow(String address) { 2074 Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); 2075 mContext.sendBroadcast(noisyIntent); 2076 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 2077 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2078 address); 2079 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 2080 } 2081 2082 private void makeA2dpDeviceUnavailableLater(String address) { 2083 // prevent any activity on the A2DP audio output to avoid unwanted 2084 // reconnection of the sink. 2085 AudioSystem.setParameters("A2dpSuspended=true"); 2086 // the device will be made unavailable later, so consider it disconnected right away 2087 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 2088 // send the delayed message to make the device unavailable later 2089 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address); 2090 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS); 2091 2092 } 2093 2094 private void cancelA2dpDeviceTimeout() { 2095 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT); 2096 } 2097 2098 private boolean hasScheduledA2dpDockTimeout() { 2099 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT); 2100 } 2101 2102 /* cache of the address of the last dock the device was connected to */ 2103 private String mDockAddress; 2104 2105 /** 2106 * Receiver for misc intent broadcasts the Phone app cares about. 2107 */ 2108 private class AudioServiceBroadcastReceiver extends BroadcastReceiver { 2109 @Override 2110 public void onReceive(Context context, Intent intent) { 2111 String action = intent.getAction(); 2112 2113 if (action.equals(Intent.ACTION_DOCK_EVENT)) { 2114 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 2115 Intent.EXTRA_DOCK_STATE_UNDOCKED); 2116 int config; 2117 switch (dockState) { 2118 case Intent.EXTRA_DOCK_STATE_DESK: 2119 config = AudioSystem.FORCE_BT_DESK_DOCK; 2120 break; 2121 case Intent.EXTRA_DOCK_STATE_CAR: 2122 config = AudioSystem.FORCE_BT_CAR_DOCK; 2123 break; 2124 case Intent.EXTRA_DOCK_STATE_LE_DESK: 2125 config = AudioSystem.FORCE_ANALOG_DOCK; 2126 break; 2127 case Intent.EXTRA_DOCK_STATE_HE_DESK: 2128 config = AudioSystem.FORCE_DIGITAL_DOCK; 2129 break; 2130 case Intent.EXTRA_DOCK_STATE_UNDOCKED: 2131 default: 2132 config = AudioSystem.FORCE_NONE; 2133 } 2134 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); 2135 } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { 2136 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 2137 BluetoothProfile.STATE_DISCONNECTED); 2138 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 2139 String address = btDevice.getAddress(); 2140 boolean isConnected = 2141 (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) && 2142 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address)); 2143 2144 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { 2145 if (btDevice.isBluetoothDock()) { 2146 if (state == BluetoothProfile.STATE_DISCONNECTED) { 2147 // introduction of a delay for transient disconnections of docks when 2148 // power is rapidly turned off/on, this message will be canceled if 2149 // we reconnect the dock under a preset delay 2150 makeA2dpDeviceUnavailableLater(address); 2151 // the next time isConnected is evaluated, it will be false for the dock 2152 } 2153 } else { 2154 makeA2dpDeviceUnavailableNow(address); 2155 } 2156 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { 2157 if (btDevice.isBluetoothDock()) { 2158 // this could be a reconnection after a transient disconnection 2159 cancelA2dpDeviceTimeout(); 2160 mDockAddress = address; 2161 } else { 2162 // this could be a connection of another A2DP device before the timeout of 2163 // a dock: cancel the dock timeout, and make the dock unavailable now 2164 if(hasScheduledA2dpDockTimeout()) { 2165 cancelA2dpDeviceTimeout(); 2166 makeA2dpDeviceUnavailableNow(mDockAddress); 2167 } 2168 } 2169 makeA2dpDeviceAvailable(address); 2170 } 2171 } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { 2172 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 2173 BluetoothProfile.STATE_DISCONNECTED); 2174 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO; 2175 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 2176 String address = null; 2177 if (btDevice != null) { 2178 address = btDevice.getAddress(); 2179 BluetoothClass btClass = btDevice.getBluetoothClass(); 2180 if (btClass != null) { 2181 switch (btClass.getDeviceClass()) { 2182 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 2183 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: 2184 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET; 2185 break; 2186 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 2187 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT; 2188 break; 2189 } 2190 } 2191 } 2192 2193 boolean isConnected = (mConnectedDevices.containsKey(device) && 2194 mConnectedDevices.get(device).equals(address)); 2195 2196 synchronized (mScoClients) { 2197 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { 2198 AudioSystem.setDeviceConnectionState(device, 2199 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2200 address); 2201 mConnectedDevices.remove(device); 2202 mBluetoothHeadsetDevice = null; 2203 clearAllScoClients(null, false); 2204 mScoAudioState = SCO_STATE_INACTIVE; 2205 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { 2206 AudioSystem.setDeviceConnectionState(device, 2207 AudioSystem.DEVICE_STATE_AVAILABLE, 2208 address); 2209 mConnectedDevices.put(new Integer(device), address); 2210 mBluetoothHeadsetDevice = btDevice; 2211 } 2212 } 2213 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { 2214 int state = intent.getIntExtra("state", 0); 2215 int microphone = intent.getIntExtra("microphone", 0); 2216 2217 if (microphone != 0) { 2218 boolean isConnected = 2219 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 2220 if (state == 0 && isConnected) { 2221 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, 2222 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2223 ""); 2224 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 2225 } else if (state == 1 && !isConnected) { 2226 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, 2227 AudioSystem.DEVICE_STATE_AVAILABLE, 2228 ""); 2229 mConnectedDevices.put( 2230 new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), ""); 2231 } 2232 } else { 2233 boolean isConnected = 2234 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 2235 if (state == 0 && isConnected) { 2236 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, 2237 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2238 ""); 2239 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 2240 } else if (state == 1 && !isConnected) { 2241 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, 2242 AudioSystem.DEVICE_STATE_AVAILABLE, 2243 ""); 2244 mConnectedDevices.put( 2245 new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), ""); 2246 } 2247 } 2248 } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) { 2249 int state = intent.getIntExtra("state", 0); 2250 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state); 2251 boolean isConnected = 2252 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET); 2253 if (state == 0 && isConnected) { 2254 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, 2255 AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); 2256 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET); 2257 } else if (state == 1 && !isConnected) { 2258 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, 2259 AudioSystem.DEVICE_STATE_AVAILABLE, ""); 2260 mConnectedDevices.put( 2261 new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), ""); 2262 } 2263 } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) { 2264 int state = intent.getIntExtra("state", 0); 2265 Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state); 2266 boolean isConnected = 2267 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL); 2268 if (state == 0 && isConnected) { 2269 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL, 2270 AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); 2271 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL); 2272 } else if (state == 1 && !isConnected) { 2273 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL, 2274 AudioSystem.DEVICE_STATE_AVAILABLE, ""); 2275 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), ""); 2276 } 2277 } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) { 2278 int state = intent.getIntExtra("state", 0); 2279 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state); 2280 boolean isConnected = 2281 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET); 2282 if (state == 0 && isConnected) { 2283 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, 2284 AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); 2285 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET); 2286 } else if (state == 1 && !isConnected) { 2287 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, 2288 AudioSystem.DEVICE_STATE_AVAILABLE, ""); 2289 mConnectedDevices.put( 2290 new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), ""); 2291 } 2292 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { 2293 boolean broadcast = false; 2294 int audioState = AudioManager.SCO_AUDIO_STATE_ERROR; 2295 synchronized (mScoClients) { 2296 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 2297 if (!mScoClients.isEmpty() && mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) { 2298 broadcast = true; 2299 } 2300 switch (btState) { 2301 case BluetoothHeadset.STATE_AUDIO_CONNECTED: 2302 audioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; 2303 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) { 2304 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 2305 } 2306 break; 2307 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: 2308 audioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; 2309 mScoAudioState = SCO_STATE_INACTIVE; 2310 break; 2311 case BluetoothHeadset.STATE_AUDIO_CONNECTING: 2312 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL) { 2313 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 2314 } 2315 default: 2316 // do not broadcast CONNECTING or invalid state 2317 broadcast = false; 2318 break; 2319 } 2320 } 2321 if (broadcast) { 2322 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 2323 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, audioState); 2324 mContext.sendStickyBroadcast(newIntent); 2325 } 2326 } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { 2327 mBootCompleted = true; 2328 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP, 2329 0, 0, null, 0); 2330 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 2331 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 2332 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 2333 mContext.sendStickyBroadcast(newIntent); 2334 } 2335 } 2336 } 2337 2338 //========================================================================================== 2339 // AudioFocus 2340 //========================================================================================== 2341 2342 /* constant to identify focus stack entry that is used to hold the focus while the phone 2343 * is ringing or during a call 2344 */ 2345 private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls"; 2346 2347 private final static Object mAudioFocusLock = new Object(); 2348 2349 private final static Object mRingingLock = new Object(); 2350 2351 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 2352 @Override 2353 public void onCallStateChanged(int state, String incomingNumber) { 2354 if (state == TelephonyManager.CALL_STATE_RINGING) { 2355 //Log.v(TAG, " CALL_STATE_RINGING"); 2356 synchronized(mRingingLock) { 2357 mIsRinging = true; 2358 } 2359 } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK) 2360 || (state == TelephonyManager.CALL_STATE_IDLE)) { 2361 synchronized(mRingingLock) { 2362 mIsRinging = false; 2363 } 2364 } 2365 } 2366 }; 2367 2368 private void notifyTopOfAudioFocusStack() { 2369 // notify the top of the stack it gained focus 2370 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 2371 if (canReassignAudioFocus()) { 2372 try { 2373 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 2374 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId); 2375 } catch (RemoteException e) { 2376 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e); 2377 e.printStackTrace(); 2378 } 2379 } 2380 } 2381 } 2382 2383 private static class FocusStackEntry { 2384 public int mStreamType = -1;// no stream type 2385 public boolean mIsTransportControlReceiver = false; 2386 public IAudioFocusDispatcher mFocusDispatcher = null; 2387 public IBinder mSourceRef = null; 2388 public String mClientId; 2389 public int mFocusChangeType; 2390 2391 public FocusStackEntry() { 2392 } 2393 2394 public FocusStackEntry(int streamType, int duration, boolean isTransportControlReceiver, 2395 IAudioFocusDispatcher afl, IBinder source, String id) { 2396 mStreamType = streamType; 2397 mIsTransportControlReceiver = isTransportControlReceiver; 2398 mFocusDispatcher = afl; 2399 mSourceRef = source; 2400 mClientId = id; 2401 mFocusChangeType = duration; 2402 } 2403 } 2404 2405 private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>(); 2406 2407 /** 2408 * Helper function: 2409 * Display in the log the current entries in the audio focus stack 2410 */ 2411 private void dumpFocusStack(PrintWriter pw) { 2412 pw.println("\nAudio Focus stack entries:"); 2413 synchronized(mAudioFocusLock) { 2414 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2415 while(stackIterator.hasNext()) { 2416 FocusStackEntry fse = stackIterator.next(); 2417 pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId 2418 + " -- duration: " +fse.mFocusChangeType); 2419 } 2420 } 2421 } 2422 2423 /** 2424 * Helper function: 2425 * Remove a focus listener from the focus stack. 2426 * @param focusListenerToRemove the focus listener 2427 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding 2428 * focus, notify the next item in the stack it gained focus. 2429 */ 2430 private void removeFocusStackEntry(String clientToRemove, boolean signal) { 2431 // is the current top of the focus stack abandoning focus? (because of death or request) 2432 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove)) 2433 { 2434 //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); 2435 mFocusStack.pop(); 2436 if (signal) { 2437 // notify the new top of the stack it gained focus 2438 notifyTopOfAudioFocusStack(); 2439 } 2440 } else { 2441 // focus is abandoned by a client that's not at the top of the stack, 2442 // no need to update focus. 2443 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2444 while(stackIterator.hasNext()) { 2445 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 2446 if(fse.mClientId.equals(clientToRemove)) { 2447 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 2448 + fse.mClientId); 2449 mFocusStack.remove(fse); 2450 } 2451 } 2452 } 2453 } 2454 2455 /** 2456 * Helper function: 2457 * Remove focus listeners from the focus stack for a particular client. 2458 */ 2459 private void removeFocusStackEntryForClient(IBinder cb) { 2460 // is the owner of the audio focus part of the client to remove? 2461 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && 2462 mFocusStack.peek().mSourceRef.equals(cb); 2463 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2464 while(stackIterator.hasNext()) { 2465 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 2466 if(fse.mSourceRef.equals(cb)) { 2467 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 2468 + fse.mClientId); 2469 mFocusStack.remove(fse); 2470 } 2471 } 2472 if (isTopOfStackForClientToRemove) { 2473 // we removed an entry at the top of the stack: 2474 // notify the new top of the stack it gained focus. 2475 notifyTopOfAudioFocusStack(); 2476 } 2477 } 2478 2479 /** 2480 * Helper function: 2481 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. 2482 */ 2483 private boolean canReassignAudioFocus() { 2484 // focus requests are rejected during a phone call or when the phone is ringing 2485 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus 2486 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) { 2487 return false; 2488 } 2489 return true; 2490 } 2491 2492 /** 2493 * Inner class to monitor audio focus client deaths, and remove them from the audio focus 2494 * stack if necessary. 2495 */ 2496 private class AudioFocusDeathHandler implements IBinder.DeathRecipient { 2497 private IBinder mCb; // To be notified of client's death 2498 2499 AudioFocusDeathHandler(IBinder cb) { 2500 mCb = cb; 2501 } 2502 2503 public void binderDied() { 2504 synchronized(mAudioFocusLock) { 2505 Log.w(TAG, " AudioFocus audio focus client died"); 2506 removeFocusStackEntryForClient(mCb); 2507 } 2508 } 2509 2510 public IBinder getBinder() { 2511 return mCb; 2512 } 2513 } 2514 2515 2516 /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */ 2517 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb, 2518 IAudioFocusDispatcher fd, String clientId) { 2519 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId); 2520 // the main stream type for the audio focus request is currently not used. It may 2521 // potentially be used to handle multiple stream type-dependent audio focuses. 2522 2523 // we need a valid binder callback for clients other than the AudioService's phone 2524 // state listener 2525 if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId) && ((cb == null) || !cb.pingBinder())) { 2526 Log.i(TAG, " AudioFocus DOA client for requestAudioFocus(), exiting"); 2527 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 2528 } 2529 2530 synchronized(mAudioFocusLock) { 2531 if (!canReassignAudioFocus()) { 2532 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 2533 } 2534 2535 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) { 2536 // if focus is already owned by this client and the reason for acquiring the focus 2537 // hasn't changed, don't do anything 2538 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) { 2539 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2540 } 2541 // the reason for the audio focus request has changed: remove the current top of 2542 // stack and respond as if we had a new focus owner 2543 mFocusStack.pop(); 2544 } 2545 2546 // notify current top of stack it is losing focus 2547 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 2548 try { 2549 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 2550 -1 * focusChangeHint, // loss and gain codes are inverse of each other 2551 mFocusStack.peek().mClientId); 2552 } catch (RemoteException e) { 2553 Log.e(TAG, " Failure to signal loss of focus due to "+ e); 2554 e.printStackTrace(); 2555 } 2556 } 2557 2558 // focus requester might already be somewhere below in the stack, remove it 2559 removeFocusStackEntry(clientId, false); 2560 2561 // push focus requester at the top of the audio focus stack 2562 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb, 2563 clientId)); 2564 }//synchronized(mAudioFocusLock) 2565 2566 // handle the potential premature death of the new holder of the focus 2567 // (premature death == death before abandoning focus) for a client which is not the 2568 // AudioService's phone state listener 2569 if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId)) { 2570 // Register for client death notification 2571 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); 2572 try { 2573 cb.linkToDeath(afdh, 0); 2574 } catch (RemoteException e) { 2575 // client has already died! 2576 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); 2577 } 2578 } 2579 2580 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2581 } 2582 2583 /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */ 2584 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) { 2585 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId); 2586 try { 2587 // this will take care of notifying the new focus owner if needed 2588 synchronized(mAudioFocusLock) { 2589 removeFocusStackEntry(clientId, true); 2590 } 2591 } catch (java.util.ConcurrentModificationException cme) { 2592 // Catching this exception here is temporary. It is here just to prevent 2593 // a crash seen when the "Silent" notification is played. This is believed to be fixed 2594 // but this try catch block is left just to be safe. 2595 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme); 2596 cme.printStackTrace(); 2597 } 2598 2599 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2600 } 2601 2602 2603 public void unregisterAudioFocusClient(String clientId) { 2604 synchronized(mAudioFocusLock) { 2605 removeFocusStackEntry(clientId, false); 2606 } 2607 } 2608 2609 2610 //========================================================================================== 2611 // RemoteControl 2612 //========================================================================================== 2613 /** 2614 * Receiver for media button intents. Handles the dispatching of the media button event 2615 * to one of the registered listeners, or if there was none, resumes the intent broadcast 2616 * to the rest of the system. 2617 */ 2618 private class MediaButtonBroadcastReceiver extends BroadcastReceiver { 2619 @Override 2620 public void onReceive(Context context, Intent intent) { 2621 String action = intent.getAction(); 2622 if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) { 2623 return; 2624 } 2625 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 2626 if (event != null) { 2627 // if in a call or ringing, do not break the current phone app behavior 2628 // TODO modify this to let the phone app specifically get the RC focus 2629 // add modify the phone app to take advantage of the new API 2630 synchronized(mRingingLock) { 2631 if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) || 2632 (getMode() == AudioSystem.MODE_IN_COMMUNICATION) || 2633 (getMode() == AudioSystem.MODE_RINGTONE) ) { 2634 return; 2635 } 2636 } 2637 synchronized(mRCStack) { 2638 if (!mRCStack.empty()) { 2639 // create a new intent specifically aimed at the current registered listener 2640 Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 2641 targetedIntent.putExtras(intent.getExtras()); 2642 targetedIntent.setComponent(mRCStack.peek().mReceiverComponent); 2643 // trap the current broadcast 2644 abortBroadcast(); 2645 //Log.v(TAG, " Sending intent" + targetedIntent); 2646 context.sendBroadcast(targetedIntent, null); 2647 } 2648 } 2649 } 2650 } 2651 } 2652 2653 private static class RemoteControlStackEntry { 2654 public ComponentName mReceiverComponent;// always non null 2655 // TODO implement registration expiration? 2656 //public int mRegistrationTime; 2657 2658 public RemoteControlStackEntry() { 2659 } 2660 2661 public RemoteControlStackEntry(ComponentName r) { 2662 mReceiverComponent = r; 2663 } 2664 } 2665 2666 private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>(); 2667 2668 /** 2669 * Helper function: 2670 * Display in the log the current entries in the remote control focus stack 2671 */ 2672 private void dumpRCStack(PrintWriter pw) { 2673 pw.println("\nRemote Control stack entries:"); 2674 synchronized(mRCStack) { 2675 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2676 while(stackIterator.hasNext()) { 2677 RemoteControlStackEntry fse = stackIterator.next(); 2678 pw.println(" receiver:" + fse.mReceiverComponent); 2679 } 2680 } 2681 } 2682 2683 /** 2684 * Helper function: 2685 * Set the new remote control receiver at the top of the RC focus stack 2686 */ 2687 private void pushMediaButtonReceiver(ComponentName newReceiver) { 2688 // already at top of stack? 2689 if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) { 2690 return; 2691 } 2692 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2693 while(stackIterator.hasNext()) { 2694 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 2695 if(rcse.mReceiverComponent.equals(newReceiver)) { 2696 mRCStack.remove(rcse); 2697 break; 2698 } 2699 } 2700 mRCStack.push(new RemoteControlStackEntry(newReceiver)); 2701 } 2702 2703 /** 2704 * Helper function: 2705 * Remove the remote control receiver from the RC focus stack 2706 */ 2707 private void removeMediaButtonReceiver(ComponentName newReceiver) { 2708 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2709 while(stackIterator.hasNext()) { 2710 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 2711 if(rcse.mReceiverComponent.equals(newReceiver)) { 2712 mRCStack.remove(rcse); 2713 break; 2714 } 2715 } 2716 } 2717 2718 2719 /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */ 2720 public void registerMediaButtonEventReceiver(ComponentName eventReceiver) { 2721 Log.i(TAG, " Remote Control registerMediaButtonEventReceiver() for " + eventReceiver); 2722 2723 synchronized(mRCStack) { 2724 pushMediaButtonReceiver(eventReceiver); 2725 } 2726 } 2727 2728 /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */ 2729 public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) { 2730 Log.i(TAG, " Remote Control unregisterMediaButtonEventReceiver() for " + eventReceiver); 2731 2732 synchronized(mRCStack) { 2733 removeMediaButtonReceiver(eventReceiver); 2734 } 2735 } 2736 2737 2738 @Override 2739 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2740 // TODO probably a lot more to do here than just the audio focus and remote control stacks 2741 dumpFocusStack(pw); 2742 dumpRCStack(pw); 2743 } 2744 2745 2746} 2747