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