AudioService.java revision baf674eedd5ecff9da0cf8cabe9868f7699b1695
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 private static final int MSG_SET_FORCE_USE = 10; 114 private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11; 115 private static final int MSG_BT_HEADSET_CNCT_FAILED = 12; 116 private static final int MSG_RCDISPLAY_CLEAR = 13; 117 private static final int MSG_RCDISPLAY_UPDATE = 14; 118 119 private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000; 120 // Timeout for connection to bluetooth headset service 121 private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000; 122 123 124 /** @see AudioSystemThread */ 125 private AudioSystemThread mAudioSystemThread; 126 /** @see AudioHandler */ 127 private AudioHandler mAudioHandler; 128 /** @see VolumeStreamState */ 129 private VolumeStreamState[] mStreamStates; 130 private SettingsObserver mSettingsObserver; 131 132 private int mMode; 133 private Object mSettingsLock = new Object(); 134 private boolean mMediaServerOk; 135 136 private SoundPool mSoundPool; 137 private Object mSoundEffectsLock = new Object(); 138 private static final int NUM_SOUNDPOOL_CHANNELS = 4; 139 private static final int SOUND_EFFECT_VOLUME = 1000; 140 141 /* Sound effect file names */ 142 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/"; 143 private static final String[] SOUND_EFFECT_FILES = new String[] { 144 "Effect_Tick.ogg", 145 "KeypressStandard.ogg", 146 "KeypressSpacebar.ogg", 147 "KeypressDelete.ogg", 148 "KeypressReturn.ogg" 149 }; 150 151 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to 152 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect 153 * uses soundpool (second column) */ 154 private int[][] SOUND_EFFECT_FILES_MAP = new int[][] { 155 {0, -1}, // FX_KEY_CLICK 156 {0, -1}, // FX_FOCUS_NAVIGATION_UP 157 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN 158 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT 159 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT 160 {1, -1}, // FX_KEYPRESS_STANDARD 161 {2, -1}, // FX_KEYPRESS_SPACEBAR 162 {3, -1}, // FX_FOCUS_DELETE 163 {4, -1} // FX_FOCUS_RETURN 164 }; 165 166 /** @hide Maximum volume index values for audio streams */ 167 private int[] MAX_STREAM_VOLUME = new int[] { 168 5, // STREAM_VOICE_CALL 169 7, // STREAM_SYSTEM 170 7, // STREAM_RING 171 15, // STREAM_MUSIC 172 7, // STREAM_ALARM 173 7, // STREAM_NOTIFICATION 174 15, // STREAM_BLUETOOTH_SCO 175 7, // STREAM_SYSTEM_ENFORCED 176 15, // STREAM_DTMF 177 15 // STREAM_TTS 178 }; 179 /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings 180 * of another stream: This avoids multiplying the volume settings for hidden 181 * stream types that follow other stream behavior for volume settings 182 * NOTE: do not create loops in aliases! */ 183 private int[] STREAM_VOLUME_ALIAS = new int[] { 184 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL 185 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM 186 AudioSystem.STREAM_RING, // STREAM_RING 187 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC 188 AudioSystem.STREAM_ALARM, // STREAM_ALARM 189 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION 190 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO 191 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM_ENFORCED 192 AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF 193 AudioSystem.STREAM_MUSIC // STREAM_TTS 194 }; 195 196 private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() { 197 public void onError(int error) { 198 switch (error) { 199 case AudioSystem.AUDIO_STATUS_SERVER_DIED: 200 if (mMediaServerOk) { 201 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0, 202 null, 1500); 203 mMediaServerOk = false; 204 } 205 break; 206 case AudioSystem.AUDIO_STATUS_OK: 207 if (!mMediaServerOk) { 208 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0, 209 null, 0); 210 mMediaServerOk = true; 211 } 212 break; 213 default: 214 break; 215 } 216 } 217 }; 218 219 /** 220 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL}, 221 * {@link AudioManager#RINGER_MODE_SILENT}, or 222 * {@link AudioManager#RINGER_MODE_VIBRATE}. 223 */ 224 private int mRingerMode; 225 226 /** @see System#MODE_RINGER_STREAMS_AFFECTED */ 227 private int mRingerModeAffectedStreams; 228 229 // Streams currently muted by ringer mode 230 private int mRingerModeMutedStreams; 231 232 /** @see System#MUTE_STREAMS_AFFECTED */ 233 private int mMuteAffectedStreams; 234 235 /** 236 * Has multiple bits per vibrate type to indicate the type's vibrate 237 * setting. See {@link #setVibrateSetting(int, int)}. 238 * <p> 239 * NOTE: This is not the final decision of whether vibrate is on/off for the 240 * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}. 241 */ 242 private int mVibrateSetting; 243 244 // Broadcast receiver for device connections intent broadcasts 245 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver(); 246 247 // Broadcast receiver for media button broadcasts (separate from mReceiver to 248 // independently change its priority) 249 private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver(); 250 251 // Used to alter media button redirection when the phone is ringing. 252 private boolean mIsRinging = false; 253 254 // Devices currently connected 255 private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>(); 256 257 // Forced device usage for communications 258 private int mForcedUseForComm; 259 260 // List of binder death handlers for setMode() client processes. 261 // The last process to have called setMode() is at the top of the list. 262 private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>(); 263 264 // List of clients having issued a SCO start request 265 private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>(); 266 267 // BluetoothHeadset API to control SCO connection 268 private BluetoothHeadset mBluetoothHeadset; 269 270 // Bluetooth headset device 271 private BluetoothDevice mBluetoothHeadsetDevice; 272 273 // Indicate if SCO audio connection is currently active and if the initiator is 274 // audio service (internal) or bluetooth headset (external) 275 private int mScoAudioState; 276 // SCO audio state is not active 277 private static final int SCO_STATE_INACTIVE = 0; 278 // SCO audio activation request waiting for headset service to connect 279 private static final int SCO_STATE_ACTIVATE_REQ = 1; 280 // SCO audio state is active or starting due to a local request to start a virtual call 281 private static final int SCO_STATE_ACTIVE_INTERNAL = 3; 282 // SCO audio deactivation request waiting for headset service to connect 283 private static final int SCO_STATE_DEACTIVATE_REQ = 5; 284 285 // SCO audio state is active due to an action in BT handsfree (either voice recognition or 286 // in call audio) 287 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; 288 // Deactivation request for all SCO connections (initiated by audio mode change) 289 // waiting for headset service to connect 290 private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4; 291 292 // Current connection state indicated by bluetooth headset 293 private int mScoConnectionState; 294 295 // true if boot sequence has been completed 296 private boolean mBootCompleted; 297 // listener for SoundPool sample load completion indication 298 private SoundPoolCallback mSoundPoolCallBack; 299 // thread for SoundPool listener 300 private SoundPoolListenerThread mSoundPoolListenerThread; 301 // message looper for SoundPool listener 302 private Looper mSoundPoolLooper = null; 303 // default volume applied to sound played with playSoundEffect() 304 private static final int SOUND_EFFECT_DEFAULT_VOLUME_DB = -20; 305 // volume applied to sound played with playSoundEffect() read from ro.config.sound_fx_volume 306 private int SOUND_EFFECT_VOLUME_DB; 307 // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification 308 // stopped 309 private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000; 310 // previous volume adjustment direction received by checkForRingerModeChange() 311 private int mPrevVolDirection = AudioManager.ADJUST_SAME; 312 313 /////////////////////////////////////////////////////////////////////////// 314 // Construction 315 /////////////////////////////////////////////////////////////////////////// 316 317 /** @hide */ 318 public AudioService(Context context) { 319 mContext = context; 320 mContentResolver = context.getContentResolver(); 321 mVoiceCapable = mContext.getResources().getBoolean( 322 com.android.internal.R.bool.config_voice_capable); 323 324 // Intialized volume 325 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt( 326 "ro.config.vc_call_vol_steps", 327 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]); 328 329 SOUND_EFFECT_VOLUME_DB = SystemProperties.getInt( 330 "ro.config.sound_fx_volume", 331 SOUND_EFFECT_DEFAULT_VOLUME_DB); 332 333 mVolumePanel = new VolumePanel(context, this); 334 mForcedUseForComm = AudioSystem.FORCE_NONE; 335 createAudioSystemThread(); 336 readPersistedSettings(); 337 mSettingsObserver = new SettingsObserver(); 338 createStreamStates(); 339 // Call setMode() to initialize mSetModeDeathHandlers 340 mMode = AudioSystem.MODE_INVALID; 341 setMode(AudioSystem.MODE_NORMAL, null); 342 mMediaServerOk = true; 343 344 // Call setRingerModeInt() to apply correct mute 345 // state on streams affected by ringer mode. 346 mRingerModeMutedStreams = 0; 347 setRingerModeInt(getRingerMode(), false); 348 349 AudioSystem.setErrorCallback(mAudioSystemCallback); 350 351 // Register for device connection intent broadcasts. 352 IntentFilter intentFilter = 353 new IntentFilter(Intent.ACTION_HEADSET_PLUG); 354 355 intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 356 intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 357 intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 358 intentFilter.addAction(Intent.ACTION_DOCK_EVENT); 359 intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG); 360 intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG); 361 intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG); 362 intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); 363 context.registerReceiver(mReceiver, intentFilter); 364 365 // Register for package removal intent broadcasts for media button receiver persistence 366 IntentFilter pkgFilter = new IntentFilter(); 367 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 368 pkgFilter.addDataScheme("package"); 369 context.registerReceiver(mReceiver, pkgFilter); 370 371 // Register for media button intent broadcasts. 372 intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON); 373 // Workaround for bug on priority setting 374 //intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 375 intentFilter.setPriority(Integer.MAX_VALUE); 376 context.registerReceiver(mMediaButtonReceiver, intentFilter); 377 378 // Register for phone state monitoring 379 TelephonyManager tmgr = (TelephonyManager) 380 context.getSystemService(Context.TELEPHONY_SERVICE); 381 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 382 } 383 384 private void createAudioSystemThread() { 385 mAudioSystemThread = new AudioSystemThread(); 386 mAudioSystemThread.start(); 387 waitForAudioHandlerCreation(); 388 } 389 390 /** Waits for the volume handler to be created by the other thread. */ 391 private void waitForAudioHandlerCreation() { 392 synchronized(this) { 393 while (mAudioHandler == null) { 394 try { 395 // Wait for mAudioHandler to be set by the other thread 396 wait(); 397 } catch (InterruptedException e) { 398 Log.e(TAG, "Interrupted while waiting on volume handler."); 399 } 400 } 401 } 402 } 403 404 private void createStreamStates() { 405 int numStreamTypes = AudioSystem.getNumStreamTypes(); 406 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes]; 407 408 for (int i = 0; i < numStreamTypes; i++) { 409 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i); 410 } 411 412 // Correct stream index values for streams with aliases 413 for (int i = 0; i < numStreamTypes; i++) { 414 if (STREAM_VOLUME_ALIAS[i] != i) { 415 int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i); 416 streams[i].mIndex = streams[i].getValidIndex(index); 417 setStreamVolumeIndex(i, index); 418 index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i); 419 streams[i].mLastAudibleIndex = streams[i].getValidIndex(index); 420 } 421 } 422 } 423 424 private void readPersistedSettings() { 425 final ContentResolver cr = mContentResolver; 426 427 mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL); 428 // sanity check in case the settings are restored from a device with incompatible 429 // ringer modes 430 if (!AudioManager.isValidRingerMode(mRingerMode)) { 431 mRingerMode = AudioManager.RINGER_MODE_NORMAL; 432 System.putInt(cr, System.MODE_RINGER, mRingerMode); 433 } 434 435 mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0); 436 437 // make sure settings for ringer mode are consistent with device type: non voice capable 438 // devices (tablets) include media stream in silent mode whereas phones don't. 439 mRingerModeAffectedStreams = Settings.System.getInt(cr, 440 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 441 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)| 442 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED))); 443 if (mVoiceCapable) { 444 mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC); 445 } else { 446 mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC); 447 } 448 Settings.System.putInt(cr, 449 Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams); 450 451 mMuteAffectedStreams = System.getInt(cr, 452 System.MUTE_STREAMS_AFFECTED, 453 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM))); 454 455 // Each stream will read its own persisted settings 456 457 // Broadcast the sticky intent 458 broadcastRingerMode(); 459 460 // Broadcast vibrate settings 461 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); 462 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); 463 464 // Restore the default media button receiver from the system settings 465 restoreMediaButtonReceiver(); 466 } 467 468 private void setStreamVolumeIndex(int stream, int index) { 469 AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10); 470 } 471 472 private int rescaleIndex(int index, int srcStream, int dstStream) { 473 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex(); 474 } 475 476 /////////////////////////////////////////////////////////////////////////// 477 // IPC methods 478 /////////////////////////////////////////////////////////////////////////// 479 480 /** @see AudioManager#adjustVolume(int, int) */ 481 public void adjustVolume(int direction, int flags) { 482 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags); 483 } 484 485 /** @see AudioManager#adjustVolume(int, int, int) */ 486 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { 487 488 int streamType; 489 if ((flags & AudioManager.FLAG_FORCE_STREAM) != 0) { 490 streamType = suggestedStreamType; 491 } else { 492 streamType = getActiveStreamType(suggestedStreamType); 493 } 494 495 // Don't play sound on other streams 496 if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) { 497 flags &= ~AudioManager.FLAG_PLAY_SOUND; 498 } 499 500 adjustStreamVolume(streamType, direction, flags); 501 } 502 503 /** @see AudioManager#adjustStreamVolume(int, int, int) */ 504 public void adjustStreamVolume(int streamType, int direction, int flags) { 505 ensureValidDirection(direction); 506 ensureValidStreamType(streamType); 507 508 509 VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]]; 510 final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex; 511 boolean adjustVolume = true; 512 513 // If either the client forces allowing ringer modes for this adjustment, 514 // or the stream type is one that is affected by ringer modes 515 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || 516 (!mVoiceCapable && streamType != AudioSystem.STREAM_VOICE_CALL && 517 streamType != AudioSystem.STREAM_BLUETOOTH_SCO) || 518 (mVoiceCapable && streamType == AudioSystem.STREAM_RING)) { 519 // Check if the ringer mode changes with this volume adjustment. If 520 // it does, it will handle adjusting the volume, so we won't below 521 adjustVolume = checkForRingerModeChange(oldIndex, direction); 522 } 523 524 // If stream is muted, adjust last audible index only 525 int index; 526 if (streamState.muteCount() != 0) { 527 if (adjustVolume) { 528 streamState.adjustLastAudibleIndex(direction); 529 // Post a persist volume msg 530 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType, 531 SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY); 532 } 533 index = streamState.mLastAudibleIndex; 534 } else { 535 if (adjustVolume && streamState.adjustIndex(direction)) { 536 // Post message to set system volume (it in turn will post a message 537 // to persist). Do not change volume if stream is muted. 538 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0, 539 streamState, 0); 540 } 541 index = streamState.mIndex; 542 } 543 544 sendVolumeUpdate(streamType, oldIndex, index, flags); 545 } 546 547 /** @see AudioManager#setStreamVolume(int, int, int) */ 548 public void setStreamVolume(int streamType, int index, int flags) { 549 ensureValidStreamType(streamType); 550 VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]]; 551 552 final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex; 553 554 index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]); 555 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true); 556 557 index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex; 558 559 sendVolumeUpdate(streamType, oldIndex, index, flags); 560 } 561 562 // UI update and Broadcast Intent 563 private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) { 564 if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) { 565 streamType = AudioSystem.STREAM_NOTIFICATION; 566 } 567 568 mVolumePanel.postVolumeChanged(streamType, flags); 569 570 oldIndex = (oldIndex + 5) / 10; 571 index = (index + 5) / 10; 572 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION); 573 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType); 574 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index); 575 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex); 576 mContext.sendBroadcast(intent); 577 } 578 579 /** 580 * Sets the stream state's index, and posts a message to set system volume. 581 * This will not call out to the UI. Assumes a valid stream type. 582 * 583 * @param streamType Type of the stream 584 * @param index Desired volume index of the stream 585 * @param force If true, set the volume even if the desired volume is same 586 * as the current volume. 587 * @param lastAudible If true, stores new index as last audible one 588 */ 589 private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) { 590 VolumeStreamState streamState = mStreamStates[streamType]; 591 592 // If stream is muted, set last audible index only 593 if (streamState.muteCount() != 0) { 594 // Do not allow last audible index to be 0 595 if (index != 0) { 596 streamState.setLastAudibleIndex(index); 597 // Post a persist volume msg 598 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType, 599 SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY); 600 } 601 } else { 602 if (streamState.setIndex(index, lastAudible) || force) { 603 // Post message to set system volume (it in turn will post a message 604 // to persist). 605 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0, 606 streamState, 0); 607 } 608 } 609 } 610 611 /** @see AudioManager#setStreamSolo(int, boolean) */ 612 public void setStreamSolo(int streamType, boolean state, IBinder cb) { 613 for (int stream = 0; stream < mStreamStates.length; stream++) { 614 if (!isStreamAffectedByMute(stream) || stream == streamType) continue; 615 // Bring back last audible volume 616 mStreamStates[stream].mute(cb, state); 617 } 618 } 619 620 /** @see AudioManager#setStreamMute(int, boolean) */ 621 public void setStreamMute(int streamType, boolean state, IBinder cb) { 622 if (isStreamAffectedByMute(streamType)) { 623 mStreamStates[streamType].mute(cb, state); 624 } 625 } 626 627 /** get stream mute state. */ 628 public boolean isStreamMute(int streamType) { 629 return (mStreamStates[streamType].muteCount() != 0); 630 } 631 632 /** @see AudioManager#getStreamVolume(int) */ 633 public int getStreamVolume(int streamType) { 634 ensureValidStreamType(streamType); 635 return (mStreamStates[streamType].mIndex + 5) / 10; 636 } 637 638 /** @see AudioManager#getStreamMaxVolume(int) */ 639 public int getStreamMaxVolume(int streamType) { 640 ensureValidStreamType(streamType); 641 return (mStreamStates[streamType].getMaxIndex() + 5) / 10; 642 } 643 644 645 /** Get last audible volume before stream was muted. */ 646 public int getLastAudibleStreamVolume(int streamType) { 647 ensureValidStreamType(streamType); 648 return (mStreamStates[streamType].mLastAudibleIndex + 5) / 10; 649 } 650 651 /** @see AudioManager#getRingerMode() */ 652 public int getRingerMode() { 653 return mRingerMode; 654 } 655 656 /** @see AudioManager#setRingerMode(int) */ 657 public void setRingerMode(int ringerMode) { 658 synchronized (mSettingsLock) { 659 if (ringerMode != mRingerMode) { 660 setRingerModeInt(ringerMode, true); 661 // Send sticky broadcast 662 broadcastRingerMode(); 663 } 664 } 665 } 666 667 private void setRingerModeInt(int ringerMode, boolean persist) { 668 mRingerMode = ringerMode; 669 670 // Mute stream if not previously muted by ringer mode and ringer mode 671 // is not RINGER_MODE_NORMAL and stream is affected by ringer mode. 672 // Unmute stream if previously muted by ringer mode and ringer mode 673 // is RINGER_MODE_NORMAL or stream is not affected by ringer mode. 674 int numStreamTypes = AudioSystem.getNumStreamTypes(); 675 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 676 if (isStreamMutedByRingerMode(streamType)) { 677 if (!isStreamAffectedByRingerMode(streamType) || 678 mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 679 mStreamStates[streamType].mute(null, false); 680 mRingerModeMutedStreams &= ~(1 << streamType); 681 } 682 } else { 683 if (isStreamAffectedByRingerMode(streamType) && 684 mRingerMode != AudioManager.RINGER_MODE_NORMAL) { 685 mStreamStates[streamType].mute(null, true); 686 mRingerModeMutedStreams |= (1 << streamType); 687 } 688 } 689 } 690 691 // Post a persist ringer mode msg 692 if (persist) { 693 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG, 694 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY); 695 } 696 } 697 698 /** @see AudioManager#shouldVibrate(int) */ 699 public boolean shouldVibrate(int vibrateType) { 700 701 switch (getVibrateSetting(vibrateType)) { 702 703 case AudioManager.VIBRATE_SETTING_ON: 704 return mRingerMode != AudioManager.RINGER_MODE_SILENT; 705 706 case AudioManager.VIBRATE_SETTING_ONLY_SILENT: 707 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE; 708 709 case AudioManager.VIBRATE_SETTING_OFF: 710 // return false, even for incoming calls 711 return false; 712 713 default: 714 return false; 715 } 716 } 717 718 /** @see AudioManager#getVibrateSetting(int) */ 719 public int getVibrateSetting(int vibrateType) { 720 return (mVibrateSetting >> (vibrateType * 2)) & 3; 721 } 722 723 /** @see AudioManager#setVibrateSetting(int, int) */ 724 public void setVibrateSetting(int vibrateType, int vibrateSetting) { 725 726 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting); 727 728 // Broadcast change 729 broadcastVibrateSetting(vibrateType); 730 731 // Post message to set ringer mode (it in turn will post a message 732 // to persist) 733 sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0, 734 null, 0); 735 } 736 737 /** 738 * @see #setVibrateSetting(int, int) 739 */ 740 public static int getValueForVibrateSetting(int existingValue, int vibrateType, 741 int vibrateSetting) { 742 743 // First clear the existing setting. Each vibrate type has two bits in 744 // the value. Note '3' is '11' in binary. 745 existingValue &= ~(3 << (vibrateType * 2)); 746 747 // Set into the old value 748 existingValue |= (vibrateSetting & 3) << (vibrateType * 2); 749 750 return existingValue; 751 } 752 753 private class SetModeDeathHandler implements IBinder.DeathRecipient { 754 private IBinder mCb; // To be notified of client's death 755 private int mPid; 756 private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client 757 758 SetModeDeathHandler(IBinder cb) { 759 mCb = cb; 760 mPid = Binder.getCallingPid(); 761 } 762 763 public void binderDied() { 764 synchronized(mSetModeDeathHandlers) { 765 Log.w(TAG, "setMode() client died"); 766 int index = mSetModeDeathHandlers.indexOf(this); 767 if (index < 0) { 768 Log.w(TAG, "unregistered setMode() client died"); 769 } else { 770 mSetModeDeathHandlers.remove(this); 771 // If dead client was a the top of client list, 772 // apply next mode in the stack 773 if (index == 0) { 774 // mSetModeDeathHandlers is never empty as the initial entry 775 // created when AudioService starts is never removed 776 SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0); 777 int mode = hdlr.getMode(); 778 if (AudioService.this.mMode != mode) { 779 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { 780 AudioService.this.mMode = mode; 781 if (mode != AudioSystem.MODE_NORMAL) { 782 disconnectBluetoothSco(mCb); 783 } 784 } 785 } 786 } 787 } 788 } 789 } 790 791 public int getPid() { 792 return mPid; 793 } 794 795 public void setMode(int mode) { 796 mMode = mode; 797 } 798 799 public int getMode() { 800 return mMode; 801 } 802 803 public IBinder getBinder() { 804 return mCb; 805 } 806 } 807 808 /** @see AudioManager#setMode(int) */ 809 public void setMode(int mode, IBinder cb) { 810 if (!checkAudioSettingsPermission("setMode()")) { 811 return; 812 } 813 814 if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) { 815 return; 816 } 817 818 synchronized (mSettingsLock) { 819 if (mode == AudioSystem.MODE_CURRENT) { 820 mode = mMode; 821 } 822 if (mode != mMode) { 823 824 // automatically handle audio focus for mode changes 825 handleFocusForCalls(mMode, mode, cb); 826 827 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { 828 mMode = mode; 829 830 synchronized(mSetModeDeathHandlers) { 831 SetModeDeathHandler hdlr = null; 832 Iterator iter = mSetModeDeathHandlers.iterator(); 833 while (iter.hasNext()) { 834 SetModeDeathHandler h = (SetModeDeathHandler)iter.next(); 835 if (h.getBinder() == cb) { 836 hdlr = h; 837 // Remove from client list so that it is re-inserted at top of list 838 iter.remove(); 839 break; 840 } 841 } 842 if (hdlr == null) { 843 hdlr = new SetModeDeathHandler(cb); 844 // cb is null when setMode() is called by AudioService constructor 845 if (cb != null) { 846 // Register for client death notification 847 try { 848 cb.linkToDeath(hdlr, 0); 849 } catch (RemoteException e) { 850 // Client has died! 851 Log.w(TAG, "setMode() could not link to "+cb+" binder death"); 852 } 853 } 854 } 855 // Last client to call setMode() is always at top of client list 856 // as required by SetModeDeathHandler.binderDied() 857 mSetModeDeathHandlers.add(0, hdlr); 858 hdlr.setMode(mode); 859 } 860 861 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all 862 // SCO connections not started by the application changing the mode 863 if (mode != AudioSystem.MODE_NORMAL) { 864 disconnectBluetoothSco(cb); 865 } 866 } 867 } 868 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); 869 int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex; 870 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false); 871 } 872 } 873 874 /** pre-condition: oldMode != newMode */ 875 private void handleFocusForCalls(int oldMode, int newMode, IBinder cb) { 876 // if ringing 877 if (newMode == AudioSystem.MODE_RINGTONE) { 878 // if not ringing silently 879 int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING); 880 if (ringVolume > 0) { 881 // request audio focus for the communication focus entry 882 requestAudioFocus(AudioManager.STREAM_RING, 883 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb, 884 null /* IAudioFocusDispatcher allowed to be null only for this clientId */, 885 IN_VOICE_COMM_FOCUS_ID /*clientId*/, 886 "system"); 887 888 } 889 } 890 // if entering call 891 else if ((newMode == AudioSystem.MODE_IN_CALL) 892 || (newMode == AudioSystem.MODE_IN_COMMUNICATION)) { 893 // request audio focus for the communication focus entry 894 // (it's ok if focus was already requested during ringing) 895 requestAudioFocus(AudioManager.STREAM_RING, 896 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, cb, 897 null /* IAudioFocusDispatcher allowed to be null only for this clientId */, 898 IN_VOICE_COMM_FOCUS_ID /*clientId*/, 899 "system"); 900 } 901 // if exiting call 902 else if (newMode == AudioSystem.MODE_NORMAL) { 903 // abandon audio focus for communication focus entry 904 abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID); 905 } 906 } 907 908 /** @see AudioManager#getMode() */ 909 public int getMode() { 910 return mMode; 911 } 912 913 /** @see AudioManager#playSoundEffect(int) */ 914 public void playSoundEffect(int effectType) { 915 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP, 916 effectType, -1, null, 0); 917 } 918 919 /** @see AudioManager#playSoundEffect(int, float) */ 920 public void playSoundEffectVolume(int effectType, float volume) { 921 loadSoundEffects(); 922 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP, 923 effectType, (int) (volume * 1000), null, 0); 924 } 925 926 /** 927 * Loads samples into the soundpool. 928 * This method must be called at when sound effects are enabled 929 */ 930 public boolean loadSoundEffects() { 931 int status; 932 933 synchronized (mSoundEffectsLock) { 934 if (!mBootCompleted) { 935 Log.w(TAG, "loadSoundEffects() called before boot complete"); 936 return false; 937 } 938 939 if (mSoundPool != null) { 940 return true; 941 } 942 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0); 943 if (mSoundPool == null) { 944 Log.w(TAG, "loadSoundEffects() could not allocate sound pool"); 945 return false; 946 } 947 948 try { 949 mSoundPoolCallBack = null; 950 mSoundPoolListenerThread = new SoundPoolListenerThread(); 951 mSoundPoolListenerThread.start(); 952 // Wait for mSoundPoolCallBack to be set by the other thread 953 mSoundEffectsLock.wait(); 954 } catch (InterruptedException e) { 955 Log.w(TAG, "Interrupted while waiting sound pool listener thread."); 956 } 957 958 if (mSoundPoolCallBack == null) { 959 Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread"); 960 if (mSoundPoolLooper != null) { 961 mSoundPoolLooper.quit(); 962 mSoundPoolLooper = null; 963 } 964 mSoundPoolListenerThread = null; 965 mSoundPool.release(); 966 mSoundPool = null; 967 return false; 968 } 969 /* 970 * poolId table: The value -1 in this table indicates that corresponding 971 * file (same index in SOUND_EFFECT_FILES[] has not been loaded. 972 * Once loaded, the value in poolId is the sample ID and the same 973 * sample can be reused for another effect using the same file. 974 */ 975 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 976 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 977 poolId[fileIdx] = -1; 978 } 979 /* 980 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded. 981 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0: 982 * this indicates we have a valid sample loaded for this effect. 983 */ 984 985 int lastSample = 0; 986 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 987 // Do not load sample if this effect uses the MediaPlayer 988 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) { 989 continue; 990 } 991 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) { 992 String filePath = Environment.getRootDirectory() 993 + SOUND_EFFECTS_PATH 994 + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]]; 995 int sampleId = mSoundPool.load(filePath, 0); 996 if (sampleId <= 0) { 997 Log.w(TAG, "Soundpool could not load file: "+filePath); 998 } else { 999 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId; 1000 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId; 1001 lastSample = sampleId; 1002 } 1003 } else { 1004 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]]; 1005 } 1006 } 1007 // wait for all samples to be loaded 1008 if (lastSample != 0) { 1009 mSoundPoolCallBack.setLastSample(lastSample); 1010 1011 try { 1012 mSoundEffectsLock.wait(); 1013 status = mSoundPoolCallBack.status(); 1014 } catch (java.lang.InterruptedException e) { 1015 Log.w(TAG, "Interrupted while waiting sound pool callback."); 1016 status = -1; 1017 } 1018 } else { 1019 status = -1; 1020 } 1021 1022 if (mSoundPoolLooper != null) { 1023 mSoundPoolLooper.quit(); 1024 mSoundPoolLooper = null; 1025 } 1026 mSoundPoolListenerThread = null; 1027 if (status != 0) { 1028 Log.w(TAG, 1029 "loadSoundEffects(), Error " 1030 + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1) 1031 + " while loading samples"); 1032 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 1033 if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) { 1034 SOUND_EFFECT_FILES_MAP[effect][1] = -1; 1035 } 1036 } 1037 1038 mSoundPool.release(); 1039 mSoundPool = null; 1040 } 1041 } 1042 return (status == 0); 1043 } 1044 1045 /** 1046 * Unloads samples from the sound pool. 1047 * This method can be called to free some memory when 1048 * sound effects are disabled. 1049 */ 1050 public void unloadSoundEffects() { 1051 synchronized (mSoundEffectsLock) { 1052 if (mSoundPool == null) { 1053 return; 1054 } 1055 1056 mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS); 1057 mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT); 1058 1059 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 1060 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 1061 poolId[fileIdx] = 0; 1062 } 1063 1064 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 1065 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) { 1066 continue; 1067 } 1068 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) { 1069 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]); 1070 SOUND_EFFECT_FILES_MAP[effect][1] = -1; 1071 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1; 1072 } 1073 } 1074 mSoundPool.release(); 1075 mSoundPool = null; 1076 } 1077 } 1078 1079 class SoundPoolListenerThread extends Thread { 1080 public SoundPoolListenerThread() { 1081 super("SoundPoolListenerThread"); 1082 } 1083 1084 @Override 1085 public void run() { 1086 1087 Looper.prepare(); 1088 mSoundPoolLooper = Looper.myLooper(); 1089 1090 synchronized (mSoundEffectsLock) { 1091 if (mSoundPool != null) { 1092 mSoundPoolCallBack = new SoundPoolCallback(); 1093 mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack); 1094 } 1095 mSoundEffectsLock.notify(); 1096 } 1097 Looper.loop(); 1098 } 1099 } 1100 1101 private final class SoundPoolCallback implements 1102 android.media.SoundPool.OnLoadCompleteListener { 1103 1104 int mStatus; 1105 int mLastSample; 1106 1107 public int status() { 1108 return mStatus; 1109 } 1110 1111 public void setLastSample(int sample) { 1112 mLastSample = sample; 1113 } 1114 1115 public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { 1116 synchronized (mSoundEffectsLock) { 1117 if (status != 0) { 1118 mStatus = status; 1119 } 1120 if (sampleId == mLastSample) { 1121 mSoundEffectsLock.notify(); 1122 } 1123 } 1124 } 1125 } 1126 1127 /** @see AudioManager#reloadAudioSettings() */ 1128 public void reloadAudioSettings() { 1129 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings 1130 readPersistedSettings(); 1131 1132 // restore volume settings 1133 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1134 for (int streamType = 0; streamType < numStreamTypes; streamType++) { 1135 VolumeStreamState streamState = mStreamStates[streamType]; 1136 1137 String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]]; 1138 String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; 1139 int index = Settings.System.getInt(mContentResolver, 1140 settingName, 1141 AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1142 if (STREAM_VOLUME_ALIAS[streamType] != streamType) { 1143 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType); 1144 } else { 1145 index *= 10; 1146 } 1147 streamState.mIndex = streamState.getValidIndex(index); 1148 1149 index = (index + 5) / 10; 1150 index = Settings.System.getInt(mContentResolver, 1151 lastAudibleSettingName, 1152 (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1153 if (STREAM_VOLUME_ALIAS[streamType] != streamType) { 1154 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType); 1155 } else { 1156 index *= 10; 1157 } 1158 streamState.mLastAudibleIndex = streamState.getValidIndex(index); 1159 1160 // unmute stream that was muted but is not affect by mute anymore 1161 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) { 1162 int size = streamState.mDeathHandlers.size(); 1163 for (int i = 0; i < size; i++) { 1164 streamState.mDeathHandlers.get(i).mMuteCount = 1; 1165 streamState.mDeathHandlers.get(i).mute(false); 1166 } 1167 } 1168 // apply stream volume 1169 if (streamState.muteCount() == 0) { 1170 setStreamVolumeIndex(streamType, streamState.mIndex); 1171 } 1172 } 1173 1174 // apply new ringer mode 1175 setRingerModeInt(getRingerMode(), false); 1176 } 1177 1178 /** @see AudioManager#setSpeakerphoneOn() */ 1179 public void setSpeakerphoneOn(boolean on){ 1180 if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { 1181 return; 1182 } 1183 mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE; 1184 1185 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE, 1186 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0); 1187 } 1188 1189 /** @see AudioManager#isSpeakerphoneOn() */ 1190 public boolean isSpeakerphoneOn() { 1191 return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER); 1192 } 1193 1194 /** @see AudioManager#setBluetoothScoOn() */ 1195 public void setBluetoothScoOn(boolean on){ 1196 if (!checkAudioSettingsPermission("setBluetoothScoOn()")) { 1197 return; 1198 } 1199 mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE; 1200 1201 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE, 1202 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0); 1203 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SHARED_MSG, SENDMSG_QUEUE, 1204 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0); 1205 } 1206 1207 /** @see AudioManager#isBluetoothScoOn() */ 1208 public boolean isBluetoothScoOn() { 1209 return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO); 1210 } 1211 1212 /** @see AudioManager#startBluetoothSco() */ 1213 public void startBluetoothSco(IBinder cb){ 1214 if (!checkAudioSettingsPermission("startBluetoothSco()") || 1215 !mBootCompleted) { 1216 return; 1217 } 1218 ScoClient client = getScoClient(cb, true); 1219 client.incCount(); 1220 } 1221 1222 /** @see AudioManager#stopBluetoothSco() */ 1223 public void stopBluetoothSco(IBinder cb){ 1224 if (!checkAudioSettingsPermission("stopBluetoothSco()") || 1225 !mBootCompleted) { 1226 return; 1227 } 1228 ScoClient client = getScoClient(cb, false); 1229 if (client != null) { 1230 client.decCount(); 1231 } 1232 } 1233 1234 private class ScoClient implements IBinder.DeathRecipient { 1235 private IBinder mCb; // To be notified of client's death 1236 private int mCreatorPid; 1237 private int mStartcount; // number of SCO connections started by this client 1238 1239 ScoClient(IBinder cb) { 1240 mCb = cb; 1241 mCreatorPid = Binder.getCallingPid(); 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 checkScoAudioState(); 1328 if (totalCount() == 0) { 1329 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 1330 // Make sure that the state transitions to CONNECTING even if we cannot initiate 1331 // the connection. 1332 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); 1333 // Accept SCO audio activation only in NORMAL audio mode or if the mode is 1334 // currently controlled by the same client process. 1335 if ((AudioService.this.mMode == AudioSystem.MODE_NORMAL || 1336 mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) && 1337 mBluetoothHeadsetDevice != null && 1338 (mScoAudioState == SCO_STATE_INACTIVE || 1339 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) { 1340 if (mScoAudioState == SCO_STATE_INACTIVE) { 1341 if (mBluetoothHeadset != null) { 1342 if (mBluetoothHeadset.startScoUsingVirtualVoiceCall( 1343 mBluetoothHeadsetDevice)) { 1344 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1345 } else { 1346 broadcastScoConnectionState( 1347 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1348 } 1349 } else if (getBluetoothHeadset()) { 1350 mScoAudioState = SCO_STATE_ACTIVATE_REQ; 1351 } 1352 } else { 1353 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1354 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); 1355 } 1356 } else { 1357 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1358 } 1359 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED && 1360 mBluetoothHeadsetDevice != null && 1361 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL || 1362 mScoAudioState == SCO_STATE_ACTIVATE_REQ)) { 1363 if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) { 1364 if (mBluetoothHeadset != null) { 1365 if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall( 1366 mBluetoothHeadsetDevice)) { 1367 mScoAudioState = SCO_STATE_INACTIVE; 1368 broadcastScoConnectionState( 1369 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1370 } 1371 } else if (getBluetoothHeadset()) { 1372 mScoAudioState = SCO_STATE_DEACTIVATE_REQ; 1373 } 1374 } else { 1375 mScoAudioState = SCO_STATE_INACTIVE; 1376 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1377 } 1378 } 1379 } 1380 } 1381 } 1382 1383 private void checkScoAudioState() { 1384 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null && 1385 mScoAudioState == SCO_STATE_INACTIVE && 1386 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) 1387 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1388 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 1389 } 1390 } 1391 1392 private ScoClient getScoClient(IBinder cb, boolean create) { 1393 synchronized(mScoClients) { 1394 ScoClient client = null; 1395 int size = mScoClients.size(); 1396 for (int i = 0; i < size; i++) { 1397 client = mScoClients.get(i); 1398 if (client.getBinder() == cb) 1399 return client; 1400 } 1401 if (create) { 1402 client = new ScoClient(cb); 1403 mScoClients.add(client); 1404 } 1405 return client; 1406 } 1407 } 1408 1409 public void clearAllScoClients(IBinder exceptBinder, boolean stopSco) { 1410 synchronized(mScoClients) { 1411 ScoClient savedClient = null; 1412 int size = mScoClients.size(); 1413 for (int i = 0; i < size; i++) { 1414 ScoClient cl = mScoClients.get(i); 1415 if (cl.getBinder() != exceptBinder) { 1416 cl.clearCount(stopSco); 1417 } else { 1418 savedClient = cl; 1419 } 1420 } 1421 mScoClients.clear(); 1422 if (savedClient != null) { 1423 mScoClients.add(savedClient); 1424 } 1425 } 1426 } 1427 1428 private boolean getBluetoothHeadset() { 1429 boolean result = false; 1430 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 1431 if (adapter != null) { 1432 result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, 1433 BluetoothProfile.HEADSET); 1434 } 1435 // If we could not get a bluetooth headset proxy, send a failure message 1436 // without delay to reset the SCO audio state and clear SCO clients. 1437 // If we could get a proxy, send a delayed failure message that will reset our state 1438 // in case we don't receive onServiceConnected(). 1439 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0, 1440 SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0); 1441 return result; 1442 } 1443 1444 private void disconnectBluetoothSco(IBinder exceptBinder) { 1445 synchronized(mScoClients) { 1446 checkScoAudioState(); 1447 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL || 1448 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) { 1449 if (mBluetoothHeadsetDevice != null) { 1450 if (mBluetoothHeadset != null) { 1451 if (!mBluetoothHeadset.stopVoiceRecognition( 1452 mBluetoothHeadsetDevice)) { 1453 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0, 1454 SENDMSG_REPLACE, 0, 0, null, 0); 1455 } 1456 } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL && 1457 getBluetoothHeadset()) { 1458 mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ; 1459 } 1460 } 1461 } else { 1462 clearAllScoClients(exceptBinder, true); 1463 } 1464 } 1465 } 1466 1467 private void resetBluetoothSco() { 1468 synchronized(mScoClients) { 1469 clearAllScoClients(null, false); 1470 mScoAudioState = SCO_STATE_INACTIVE; 1471 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1472 } 1473 } 1474 1475 private void broadcastScoConnectionState(int state) { 1476 if (state != mScoConnectionState) { 1477 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); 1478 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); 1479 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, 1480 mScoConnectionState); 1481 mContext.sendStickyBroadcast(newIntent); 1482 mScoConnectionState = state; 1483 } 1484 } 1485 1486 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = 1487 new BluetoothProfile.ServiceListener() { 1488 public void onServiceConnected(int profile, BluetoothProfile proxy) { 1489 synchronized (mScoClients) { 1490 // Discard timeout message 1491 mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED); 1492 mBluetoothHeadset = (BluetoothHeadset) proxy; 1493 List<BluetoothDevice> deviceList = mBluetoothHeadset.getConnectedDevices(); 1494 if (deviceList.size() > 0) { 1495 mBluetoothHeadsetDevice = deviceList.get(0); 1496 } else { 1497 mBluetoothHeadsetDevice = null; 1498 } 1499 // Refresh SCO audio state 1500 checkScoAudioState(); 1501 // Continue pending action if any 1502 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ || 1503 mScoAudioState == SCO_STATE_DEACTIVATE_REQ || 1504 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) { 1505 boolean status = false; 1506 if (mBluetoothHeadsetDevice != null) { 1507 switch (mScoAudioState) { 1508 case SCO_STATE_ACTIVATE_REQ: 1509 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1510 status = mBluetoothHeadset.startScoUsingVirtualVoiceCall( 1511 mBluetoothHeadsetDevice); 1512 break; 1513 case SCO_STATE_DEACTIVATE_REQ: 1514 status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall( 1515 mBluetoothHeadsetDevice); 1516 break; 1517 case SCO_STATE_DEACTIVATE_EXT_REQ: 1518 status = mBluetoothHeadset.stopVoiceRecognition( 1519 mBluetoothHeadsetDevice); 1520 } 1521 } 1522 if (!status) { 1523 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 0, 1524 SENDMSG_REPLACE, 0, 0, null, 0); 1525 } 1526 } 1527 } 1528 } 1529 public void onServiceDisconnected(int profile) { 1530 synchronized (mScoClients) { 1531 mBluetoothHeadset = null; 1532 } 1533 } 1534 }; 1535 1536 /////////////////////////////////////////////////////////////////////////// 1537 // Internal methods 1538 /////////////////////////////////////////////////////////////////////////// 1539 1540 /** 1541 * Checks if the adjustment should change ringer mode instead of just 1542 * adjusting volume. If so, this will set the proper ringer mode and volume 1543 * indices on the stream states. 1544 */ 1545 private boolean checkForRingerModeChange(int oldIndex, int direction) { 1546 boolean adjustVolumeIndex = true; 1547 int newRingerMode = mRingerMode; 1548 1549 if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 1550 // audible mode, at the bottom of the scale 1551 if ((direction == AudioManager.ADJUST_LOWER && 1552 mPrevVolDirection != AudioManager.ADJUST_LOWER) && 1553 ((oldIndex + 5) / 10 == 0)) { 1554 // "silent mode", but which one? 1555 newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1 1556 ? AudioManager.RINGER_MODE_VIBRATE 1557 : AudioManager.RINGER_MODE_SILENT; 1558 } 1559 } else { 1560 if (direction == AudioManager.ADJUST_RAISE) { 1561 // exiting silent mode 1562 newRingerMode = AudioManager.RINGER_MODE_NORMAL; 1563 } else { 1564 // prevent last audible index to reach 0 1565 adjustVolumeIndex = false; 1566 } 1567 } 1568 1569 if (newRingerMode != mRingerMode) { 1570 setRingerMode(newRingerMode); 1571 1572 /* 1573 * If we are changing ringer modes, do not increment/decrement the 1574 * volume index. Instead, the handler for the message above will 1575 * take care of changing the index. 1576 */ 1577 adjustVolumeIndex = false; 1578 } 1579 1580 mPrevVolDirection = direction; 1581 1582 return adjustVolumeIndex; 1583 } 1584 1585 public boolean isStreamAffectedByRingerMode(int streamType) { 1586 return (mRingerModeAffectedStreams & (1 << streamType)) != 0; 1587 } 1588 1589 private boolean isStreamMutedByRingerMode(int streamType) { 1590 return (mRingerModeMutedStreams & (1 << streamType)) != 0; 1591 } 1592 1593 public boolean isStreamAffectedByMute(int streamType) { 1594 return (mMuteAffectedStreams & (1 << streamType)) != 0; 1595 } 1596 1597 private void ensureValidDirection(int direction) { 1598 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) { 1599 throw new IllegalArgumentException("Bad direction " + direction); 1600 } 1601 } 1602 1603 private void ensureValidStreamType(int streamType) { 1604 if (streamType < 0 || streamType >= mStreamStates.length) { 1605 throw new IllegalArgumentException("Bad stream type " + streamType); 1606 } 1607 } 1608 1609 private int getActiveStreamType(int suggestedStreamType) { 1610 1611 if (mVoiceCapable) { 1612 boolean isOffhook = false; 1613 try { 1614 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 1615 if (phone != null) isOffhook = phone.isOffhook(); 1616 } catch (RemoteException e) { 1617 Log.w(TAG, "Couldn't connect to phone service", e); 1618 } 1619 1620 if (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION) { 1621 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) 1622 == AudioSystem.FORCE_BT_SCO) { 1623 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); 1624 return AudioSystem.STREAM_BLUETOOTH_SCO; 1625 } else { 1626 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); 1627 return AudioSystem.STREAM_VOICE_CALL; 1628 } 1629 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) { 1630 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC..."); 1631 return AudioSystem.STREAM_MUSIC; 1632 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { 1633 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..." 1634 // + " b/c USE_DEFAULT_STREAM_TYPE..."); 1635 return AudioSystem.STREAM_RING; 1636 } else { 1637 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType); 1638 return suggestedStreamType; 1639 } 1640 } else { 1641 if (getMode() == AudioManager.MODE_IN_COMMUNICATION) { 1642 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) 1643 == AudioSystem.FORCE_BT_SCO) { 1644 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); 1645 return AudioSystem.STREAM_BLUETOOTH_SCO; 1646 } else { 1647 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); 1648 return AudioSystem.STREAM_VOICE_CALL; 1649 } 1650 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION, 1651 NOTIFICATION_VOLUME_DELAY_MS) || 1652 AudioSystem.isStreamActive(AudioSystem.STREAM_RING, 1653 NOTIFICATION_VOLUME_DELAY_MS)) { 1654 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION..."); 1655 return AudioSystem.STREAM_NOTIFICATION; 1656 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) || 1657 (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) { 1658 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC " 1659 // + " b/c USE_DEFAULT_STREAM_TYPE..."); 1660 return AudioSystem.STREAM_MUSIC; 1661 } else { 1662 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType); 1663 return suggestedStreamType; 1664 } 1665 } 1666 } 1667 1668 private void broadcastRingerMode() { 1669 // Send sticky broadcast 1670 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION); 1671 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode); 1672 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1673 | Intent.FLAG_RECEIVER_REPLACE_PENDING); 1674 long origCallerIdentityToken = Binder.clearCallingIdentity(); 1675 mContext.sendStickyBroadcast(broadcast); 1676 Binder.restoreCallingIdentity(origCallerIdentityToken); 1677 } 1678 1679 private void broadcastVibrateSetting(int vibrateType) { 1680 // Send broadcast 1681 if (ActivityManagerNative.isSystemReady()) { 1682 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); 1683 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); 1684 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); 1685 mContext.sendBroadcast(broadcast); 1686 } 1687 } 1688 1689 // Message helper methods 1690 private static int getMsg(int baseMsg, int streamType) { 1691 return (baseMsg & 0xffff) | streamType << 16; 1692 } 1693 1694 private static int getMsgBase(int msg) { 1695 return msg & 0xffff; 1696 } 1697 1698 private static void sendMsg(Handler handler, int baseMsg, int streamType, 1699 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) { 1700 int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType); 1701 1702 if (existingMsgPolicy == SENDMSG_REPLACE) { 1703 handler.removeMessages(msg); 1704 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) { 1705 return; 1706 } 1707 1708 handler 1709 .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay); 1710 } 1711 1712 boolean checkAudioSettingsPermission(String method) { 1713 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS") 1714 == PackageManager.PERMISSION_GRANTED) { 1715 return true; 1716 } 1717 String msg = "Audio Settings Permission Denial: " + method + " from pid=" 1718 + Binder.getCallingPid() 1719 + ", uid=" + Binder.getCallingUid(); 1720 Log.w(TAG, msg); 1721 return false; 1722 } 1723 1724 1725 /////////////////////////////////////////////////////////////////////////// 1726 // Inner classes 1727 /////////////////////////////////////////////////////////////////////////// 1728 1729 public class VolumeStreamState { 1730 private final int mStreamType; 1731 1732 private String mVolumeIndexSettingName; 1733 private String mLastAudibleVolumeIndexSettingName; 1734 private int mIndexMax; 1735 private int mIndex; 1736 private int mLastAudibleIndex; 1737 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death 1738 1739 private VolumeStreamState(String settingName, int streamType) { 1740 1741 setVolumeIndexSettingName(settingName); 1742 1743 mStreamType = streamType; 1744 1745 final ContentResolver cr = mContentResolver; 1746 mIndexMax = MAX_STREAM_VOLUME[streamType]; 1747 mIndex = Settings.System.getInt(cr, 1748 mVolumeIndexSettingName, 1749 AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1750 mLastAudibleIndex = Settings.System.getInt(cr, 1751 mLastAudibleVolumeIndexSettingName, 1752 (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1753 AudioSystem.initStreamVolume(streamType, 0, mIndexMax); 1754 mIndexMax *= 10; 1755 mIndex = getValidIndex(10 * mIndex); 1756 mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex); 1757 setStreamVolumeIndex(streamType, mIndex); 1758 mDeathHandlers = new ArrayList<VolumeDeathHandler>(); 1759 } 1760 1761 public void setVolumeIndexSettingName(String settingName) { 1762 mVolumeIndexSettingName = settingName; 1763 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; 1764 } 1765 1766 public boolean adjustIndex(int deltaIndex) { 1767 return setIndex(mIndex + deltaIndex * 10, true); 1768 } 1769 1770 public boolean setIndex(int index, boolean lastAudible) { 1771 int oldIndex = mIndex; 1772 mIndex = getValidIndex(index); 1773 1774 if (oldIndex != mIndex) { 1775 if (lastAudible) { 1776 mLastAudibleIndex = mIndex; 1777 } 1778 // Apply change to all streams using this one as alias 1779 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1780 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1781 if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) { 1782 mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible); 1783 } 1784 } 1785 return true; 1786 } else { 1787 return false; 1788 } 1789 } 1790 1791 public void setLastAudibleIndex(int index) { 1792 mLastAudibleIndex = getValidIndex(index); 1793 } 1794 1795 public void adjustLastAudibleIndex(int deltaIndex) { 1796 setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10); 1797 } 1798 1799 public int getMaxIndex() { 1800 return mIndexMax; 1801 } 1802 1803 public void mute(IBinder cb, boolean state) { 1804 VolumeDeathHandler handler = getDeathHandler(cb, state); 1805 if (handler == null) { 1806 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType); 1807 return; 1808 } 1809 handler.mute(state); 1810 } 1811 1812 private int getValidIndex(int index) { 1813 if (index < 0) { 1814 return 0; 1815 } else if (index > mIndexMax) { 1816 return mIndexMax; 1817 } 1818 1819 return index; 1820 } 1821 1822 private class VolumeDeathHandler implements IBinder.DeathRecipient { 1823 private IBinder mICallback; // To be notified of client's death 1824 private int mMuteCount; // Number of active mutes for this client 1825 1826 VolumeDeathHandler(IBinder cb) { 1827 mICallback = cb; 1828 } 1829 1830 public void mute(boolean state) { 1831 synchronized(mDeathHandlers) { 1832 if (state) { 1833 if (mMuteCount == 0) { 1834 // Register for client death notification 1835 try { 1836 // mICallback can be 0 if muted by AudioService 1837 if (mICallback != null) { 1838 mICallback.linkToDeath(this, 0); 1839 } 1840 mDeathHandlers.add(this); 1841 // If the stream is not yet muted by any client, set lvel to 0 1842 if (muteCount() == 0) { 1843 setIndex(0, false); 1844 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, 1845 VolumeStreamState.this, 0); 1846 } 1847 } catch (RemoteException e) { 1848 // Client has died! 1849 binderDied(); 1850 mDeathHandlers.notify(); 1851 return; 1852 } 1853 } else { 1854 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client"); 1855 } 1856 mMuteCount++; 1857 } else { 1858 if (mMuteCount == 0) { 1859 Log.e(TAG, "unexpected unmute for stream: "+mStreamType); 1860 } else { 1861 mMuteCount--; 1862 if (mMuteCount == 0) { 1863 // Unregistr from client death notification 1864 mDeathHandlers.remove(this); 1865 // mICallback can be 0 if muted by AudioService 1866 if (mICallback != null) { 1867 mICallback.unlinkToDeath(this, 0); 1868 } 1869 if (muteCount() == 0) { 1870 // If the stream is not muted any more, restore it's volume if 1871 // ringer mode allows it 1872 if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 1873 setIndex(mLastAudibleIndex, false); 1874 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, 1875 VolumeStreamState.this, 0); 1876 } 1877 } 1878 } 1879 } 1880 } 1881 mDeathHandlers.notify(); 1882 } 1883 } 1884 1885 public void binderDied() { 1886 Log.w(TAG, "Volume service client died for stream: "+mStreamType); 1887 if (mMuteCount != 0) { 1888 // Reset all active mute requests from this client. 1889 mMuteCount = 1; 1890 mute(false); 1891 } 1892 } 1893 } 1894 1895 private int muteCount() { 1896 int count = 0; 1897 int size = mDeathHandlers.size(); 1898 for (int i = 0; i < size; i++) { 1899 count += mDeathHandlers.get(i).mMuteCount; 1900 } 1901 return count; 1902 } 1903 1904 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) { 1905 synchronized(mDeathHandlers) { 1906 VolumeDeathHandler handler; 1907 int size = mDeathHandlers.size(); 1908 for (int i = 0; i < size; i++) { 1909 handler = mDeathHandlers.get(i); 1910 if (cb == handler.mICallback) { 1911 return handler; 1912 } 1913 } 1914 // If this is the first mute request for this client, create a new 1915 // client death handler. Otherwise, it is an out of sequence unmute request. 1916 if (state) { 1917 handler = new VolumeDeathHandler(cb); 1918 } else { 1919 Log.w(TAG, "stream was not muted by this client"); 1920 handler = null; 1921 } 1922 return handler; 1923 } 1924 } 1925 } 1926 1927 /** Thread that handles native AudioSystem control. */ 1928 private class AudioSystemThread extends Thread { 1929 AudioSystemThread() { 1930 super("AudioService"); 1931 } 1932 1933 @Override 1934 public void run() { 1935 // Set this thread up so the handler will work on it 1936 Looper.prepare(); 1937 1938 synchronized(AudioService.this) { 1939 mAudioHandler = new AudioHandler(); 1940 1941 // Notify that the handler has been created 1942 AudioService.this.notify(); 1943 } 1944 1945 // Listen for volume change requests that are set by VolumePanel 1946 Looper.loop(); 1947 } 1948 } 1949 1950 /** Handles internal volume messages in separate volume thread. */ 1951 private class AudioHandler extends Handler { 1952 1953 private void setSystemVolume(VolumeStreamState streamState) { 1954 1955 // Adjust volume 1956 setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex); 1957 1958 // Apply change to all streams using this one as alias 1959 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1960 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1961 if (streamType != streamState.mStreamType && 1962 STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) { 1963 setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex); 1964 } 1965 } 1966 1967 // Post a persist volume msg 1968 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType, 1969 SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY); 1970 } 1971 1972 private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) { 1973 if (current) { 1974 System.putInt(mContentResolver, streamState.mVolumeIndexSettingName, 1975 (streamState.mIndex + 5)/ 10); 1976 } 1977 if (lastAudible) { 1978 System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName, 1979 (streamState.mLastAudibleIndex + 5) / 10); 1980 } 1981 } 1982 1983 private void persistRingerMode() { 1984 System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode); 1985 } 1986 1987 private void persistVibrateSetting() { 1988 System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting); 1989 } 1990 1991 private void playSoundEffect(int effectType, int volume) { 1992 synchronized (mSoundEffectsLock) { 1993 if (mSoundPool == null) { 1994 return; 1995 } 1996 float volFloat; 1997 // use default if volume is not specified by caller 1998 if (volume < 0) { 1999 volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20); 2000 } else { 2001 volFloat = (float) volume / 1000.0f; 2002 } 2003 2004 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) { 2005 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f); 2006 } else { 2007 MediaPlayer mediaPlayer = new MediaPlayer(); 2008 if (mediaPlayer != null) { 2009 try { 2010 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]]; 2011 mediaPlayer.setDataSource(filePath); 2012 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM); 2013 mediaPlayer.prepare(); 2014 mediaPlayer.setVolume(volFloat, volFloat); 2015 mediaPlayer.setOnCompletionListener(new OnCompletionListener() { 2016 public void onCompletion(MediaPlayer mp) { 2017 cleanupPlayer(mp); 2018 } 2019 }); 2020 mediaPlayer.setOnErrorListener(new OnErrorListener() { 2021 public boolean onError(MediaPlayer mp, int what, int extra) { 2022 cleanupPlayer(mp); 2023 return true; 2024 } 2025 }); 2026 mediaPlayer.start(); 2027 } catch (IOException ex) { 2028 Log.w(TAG, "MediaPlayer IOException: "+ex); 2029 } catch (IllegalArgumentException ex) { 2030 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex); 2031 } catch (IllegalStateException ex) { 2032 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 2033 } 2034 } 2035 } 2036 } 2037 } 2038 2039 private void persistMediaButtonReceiver(ComponentName receiver) { 2040 Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER, 2041 receiver == null ? "" : receiver.flattenToString()); 2042 } 2043 2044 private void cleanupPlayer(MediaPlayer mp) { 2045 if (mp != null) { 2046 try { 2047 mp.stop(); 2048 mp.release(); 2049 } catch (IllegalStateException ex) { 2050 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 2051 } 2052 } 2053 } 2054 2055 private void setForceUse(int usage, int config) { 2056 AudioSystem.setForceUse(usage, config); 2057 } 2058 2059 @Override 2060 public void handleMessage(Message msg) { 2061 int baseMsgWhat = getMsgBase(msg.what); 2062 2063 switch (baseMsgWhat) { 2064 2065 case MSG_SET_SYSTEM_VOLUME: 2066 setSystemVolume((VolumeStreamState) msg.obj); 2067 break; 2068 2069 case MSG_PERSIST_VOLUME: 2070 persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0)); 2071 break; 2072 2073 case MSG_PERSIST_RINGER_MODE: 2074 persistRingerMode(); 2075 break; 2076 2077 case MSG_PERSIST_VIBRATE_SETTING: 2078 persistVibrateSetting(); 2079 break; 2080 2081 case MSG_MEDIA_SERVER_DIED: 2082 if (!mMediaServerOk) { 2083 Log.e(TAG, "Media server died."); 2084 // Force creation of new IAudioFlinger interface so that we are notified 2085 // when new media_server process is back to life. 2086 AudioSystem.setErrorCallback(mAudioSystemCallback); 2087 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0, 2088 null, 500); 2089 } 2090 break; 2091 2092 case MSG_MEDIA_SERVER_STARTED: 2093 Log.e(TAG, "Media server started."); 2094 // indicate to audio HAL that we start the reconfiguration phase after a media 2095 // server crash 2096 // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server 2097 // process restarts after a crash, not the first time it is started. 2098 AudioSystem.setParameters("restarting=true"); 2099 2100 // Restore device connection states 2101 Set set = mConnectedDevices.entrySet(); 2102 Iterator i = set.iterator(); 2103 while(i.hasNext()){ 2104 Map.Entry device = (Map.Entry)i.next(); 2105 AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(), 2106 AudioSystem.DEVICE_STATE_AVAILABLE, 2107 (String)device.getValue()); 2108 } 2109 2110 // Restore call state 2111 AudioSystem.setPhoneState(mMode); 2112 2113 // Restore forced usage for communcations and record 2114 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm); 2115 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm); 2116 2117 // Restore stream volumes 2118 int numStreamTypes = AudioSystem.getNumStreamTypes(); 2119 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 2120 int index; 2121 VolumeStreamState streamState = mStreamStates[streamType]; 2122 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10); 2123 if (streamState.muteCount() == 0) { 2124 index = streamState.mIndex; 2125 } else { 2126 index = 0; 2127 } 2128 setStreamVolumeIndex(streamType, index); 2129 } 2130 2131 // Restore ringer mode 2132 setRingerModeInt(getRingerMode(), false); 2133 2134 // indicate the end of reconfiguration phase to audio HAL 2135 AudioSystem.setParameters("restarting=false"); 2136 break; 2137 2138 case MSG_LOAD_SOUND_EFFECTS: 2139 loadSoundEffects(); 2140 break; 2141 2142 case MSG_PLAY_SOUND_EFFECT: 2143 playSoundEffect(msg.arg1, msg.arg2); 2144 break; 2145 2146 case MSG_BTA2DP_DOCK_TIMEOUT: 2147 // msg.obj == address of BTA2DP device 2148 makeA2dpDeviceUnavailableNow( (String) msg.obj ); 2149 break; 2150 2151 case MSG_SET_FORCE_USE: 2152 setForceUse(msg.arg1, msg.arg2); 2153 break; 2154 2155 case MSG_PERSIST_MEDIABUTTONRECEIVER: 2156 persistMediaButtonReceiver( (ComponentName) msg.obj ); 2157 break; 2158 2159 case MSG_RCDISPLAY_CLEAR: 2160 // TODO remove log before release 2161 Log.i(TAG, "Clear remote control display"); 2162 Intent clearIntent = new Intent(AudioManager.REMOTE_CONTROL_CLIENT_CHANGED); 2163 // no extra means no IRemoteControlClient, which is a request to clear 2164 clearIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 2165 mContext.sendBroadcast(clearIntent); 2166 break; 2167 2168 case MSG_RCDISPLAY_UPDATE: 2169 synchronized(mCurrentRcLock) { 2170 if ((mCurrentRcClient == null) || 2171 (!mCurrentRcClient.equals((IRemoteControlClient)msg.obj))) { 2172 // the remote control display owner has changed between the 2173 // the message to update the display was sent, and the time it 2174 // gets to be processed (now) 2175 } else { 2176 mCurrentRcClientGen++; 2177 // TODO remove log before release 2178 Log.i(TAG, "Display/update remote control "); 2179 Intent rcClientIntent = new Intent( 2180 AudioManager.REMOTE_CONTROL_CLIENT_CHANGED); 2181 rcClientIntent.putExtra(AudioManager.EXTRA_REMOTE_CONTROL_CLIENT, 2182 mCurrentRcClientGen); 2183 rcClientIntent.putExtra( 2184 AudioManager.EXTRA_REMOTE_CONTROL_CLIENT_INFO_CHANGED, 2185 msg.arg1); 2186 rcClientIntent.setFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 2187 mContext.sendBroadcast(rcClientIntent); 2188 } 2189 } 2190 break; 2191 2192 case MSG_BT_HEADSET_CNCT_FAILED: 2193 resetBluetoothSco(); 2194 break; 2195 } 2196 } 2197 } 2198 2199 private class SettingsObserver extends ContentObserver { 2200 2201 SettingsObserver() { 2202 super(new Handler()); 2203 mContentResolver.registerContentObserver(Settings.System.getUriFor( 2204 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this); 2205 } 2206 2207 @Override 2208 public void onChange(boolean selfChange) { 2209 super.onChange(selfChange); 2210 synchronized (mSettingsLock) { 2211 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver, 2212 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 2213 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)| 2214 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED))); 2215 if (mVoiceCapable) { 2216 ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC); 2217 } else { 2218 ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC); 2219 } 2220 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) { 2221 /* 2222 * Ensure all stream types that should be affected by ringer mode 2223 * are in the proper state. 2224 */ 2225 mRingerModeAffectedStreams = ringerModeAffectedStreams; 2226 setRingerModeInt(getRingerMode(), false); 2227 } 2228 } 2229 } 2230 } 2231 2232 private void makeA2dpDeviceAvailable(String address) { 2233 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 2234 AudioSystem.DEVICE_STATE_AVAILABLE, 2235 address); 2236 // Reset A2DP suspend state each time a new sink is connected 2237 AudioSystem.setParameters("A2dpSuspended=false"); 2238 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), 2239 address); 2240 } 2241 2242 private void makeA2dpDeviceUnavailableNow(String address) { 2243 Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); 2244 mContext.sendBroadcast(noisyIntent); 2245 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 2246 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2247 address); 2248 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 2249 } 2250 2251 private void makeA2dpDeviceUnavailableLater(String address) { 2252 // prevent any activity on the A2DP audio output to avoid unwanted 2253 // reconnection of the sink. 2254 AudioSystem.setParameters("A2dpSuspended=true"); 2255 // the device will be made unavailable later, so consider it disconnected right away 2256 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 2257 // send the delayed message to make the device unavailable later 2258 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address); 2259 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS); 2260 2261 } 2262 2263 private void cancelA2dpDeviceTimeout() { 2264 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT); 2265 } 2266 2267 private boolean hasScheduledA2dpDockTimeout() { 2268 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT); 2269 } 2270 2271 /* cache of the address of the last dock the device was connected to */ 2272 private String mDockAddress; 2273 2274 /** 2275 * Receiver for misc intent broadcasts the Phone app cares about. 2276 */ 2277 private class AudioServiceBroadcastReceiver extends BroadcastReceiver { 2278 @Override 2279 public void onReceive(Context context, Intent intent) { 2280 String action = intent.getAction(); 2281 2282 if (action.equals(Intent.ACTION_DOCK_EVENT)) { 2283 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 2284 Intent.EXTRA_DOCK_STATE_UNDOCKED); 2285 int config; 2286 switch (dockState) { 2287 case Intent.EXTRA_DOCK_STATE_DESK: 2288 config = AudioSystem.FORCE_BT_DESK_DOCK; 2289 break; 2290 case Intent.EXTRA_DOCK_STATE_CAR: 2291 config = AudioSystem.FORCE_BT_CAR_DOCK; 2292 break; 2293 case Intent.EXTRA_DOCK_STATE_LE_DESK: 2294 config = AudioSystem.FORCE_ANALOG_DOCK; 2295 break; 2296 case Intent.EXTRA_DOCK_STATE_HE_DESK: 2297 config = AudioSystem.FORCE_DIGITAL_DOCK; 2298 break; 2299 case Intent.EXTRA_DOCK_STATE_UNDOCKED: 2300 default: 2301 config = AudioSystem.FORCE_NONE; 2302 } 2303 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); 2304 } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { 2305 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 2306 BluetoothProfile.STATE_DISCONNECTED); 2307 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 2308 String address = btDevice.getAddress(); 2309 boolean isConnected = 2310 (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) && 2311 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address)); 2312 2313 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { 2314 if (btDevice.isBluetoothDock()) { 2315 if (state == BluetoothProfile.STATE_DISCONNECTED) { 2316 // introduction of a delay for transient disconnections of docks when 2317 // power is rapidly turned off/on, this message will be canceled if 2318 // we reconnect the dock under a preset delay 2319 makeA2dpDeviceUnavailableLater(address); 2320 // the next time isConnected is evaluated, it will be false for the dock 2321 } 2322 } else { 2323 makeA2dpDeviceUnavailableNow(address); 2324 } 2325 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { 2326 if (btDevice.isBluetoothDock()) { 2327 // this could be a reconnection after a transient disconnection 2328 cancelA2dpDeviceTimeout(); 2329 mDockAddress = address; 2330 } else { 2331 // this could be a connection of another A2DP device before the timeout of 2332 // a dock: cancel the dock timeout, and make the dock unavailable now 2333 if(hasScheduledA2dpDockTimeout()) { 2334 cancelA2dpDeviceTimeout(); 2335 makeA2dpDeviceUnavailableNow(mDockAddress); 2336 } 2337 } 2338 makeA2dpDeviceAvailable(address); 2339 } 2340 } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { 2341 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 2342 BluetoothProfile.STATE_DISCONNECTED); 2343 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO; 2344 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 2345 String address = null; 2346 if (btDevice != null) { 2347 address = btDevice.getAddress(); 2348 BluetoothClass btClass = btDevice.getBluetoothClass(); 2349 if (btClass != null) { 2350 switch (btClass.getDeviceClass()) { 2351 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 2352 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: 2353 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET; 2354 break; 2355 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 2356 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT; 2357 break; 2358 } 2359 } 2360 } 2361 2362 boolean isConnected = (mConnectedDevices.containsKey(device) && 2363 mConnectedDevices.get(device).equals(address)); 2364 2365 synchronized (mScoClients) { 2366 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { 2367 AudioSystem.setDeviceConnectionState(device, 2368 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2369 address); 2370 mConnectedDevices.remove(device); 2371 mBluetoothHeadsetDevice = null; 2372 resetBluetoothSco(); 2373 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { 2374 AudioSystem.setDeviceConnectionState(device, 2375 AudioSystem.DEVICE_STATE_AVAILABLE, 2376 address); 2377 mConnectedDevices.put(new Integer(device), address); 2378 mBluetoothHeadsetDevice = btDevice; 2379 } 2380 } 2381 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { 2382 int state = intent.getIntExtra("state", 0); 2383 int microphone = intent.getIntExtra("microphone", 0); 2384 2385 if (microphone != 0) { 2386 boolean isConnected = 2387 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 2388 if (state == 0 && isConnected) { 2389 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, 2390 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2391 ""); 2392 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 2393 } else if (state == 1 && !isConnected) { 2394 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, 2395 AudioSystem.DEVICE_STATE_AVAILABLE, 2396 ""); 2397 mConnectedDevices.put( 2398 new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), ""); 2399 } 2400 } else { 2401 boolean isConnected = 2402 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 2403 if (state == 0 && isConnected) { 2404 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, 2405 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2406 ""); 2407 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 2408 } else if (state == 1 && !isConnected) { 2409 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, 2410 AudioSystem.DEVICE_STATE_AVAILABLE, 2411 ""); 2412 mConnectedDevices.put( 2413 new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), ""); 2414 } 2415 } 2416 } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) { 2417 int state = intent.getIntExtra("state", 0); 2418 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state); 2419 boolean isConnected = 2420 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET); 2421 if (state == 0 && isConnected) { 2422 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, 2423 AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); 2424 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET); 2425 } else if (state == 1 && !isConnected) { 2426 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, 2427 AudioSystem.DEVICE_STATE_AVAILABLE, ""); 2428 mConnectedDevices.put( 2429 new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), ""); 2430 } 2431 } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) { 2432 int state = intent.getIntExtra("state", 0); 2433 Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state); 2434 boolean isConnected = 2435 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL); 2436 if (state == 0 && isConnected) { 2437 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL, 2438 AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); 2439 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL); 2440 } else if (state == 1 && !isConnected) { 2441 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL, 2442 AudioSystem.DEVICE_STATE_AVAILABLE, ""); 2443 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), ""); 2444 } 2445 } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) { 2446 int state = intent.getIntExtra("state", 0); 2447 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state); 2448 boolean isConnected = 2449 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET); 2450 if (state == 0 && isConnected) { 2451 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, 2452 AudioSystem.DEVICE_STATE_UNAVAILABLE, ""); 2453 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET); 2454 } else if (state == 1 && !isConnected) { 2455 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, 2456 AudioSystem.DEVICE_STATE_AVAILABLE, ""); 2457 mConnectedDevices.put( 2458 new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), ""); 2459 } 2460 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { 2461 boolean broadcast = false; 2462 int state = AudioManager.SCO_AUDIO_STATE_ERROR; 2463 synchronized (mScoClients) { 2464 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 2465 // broadcast intent if the connection was initated by AudioService 2466 if (!mScoClients.isEmpty() && 2467 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL || 2468 mScoAudioState == SCO_STATE_ACTIVATE_REQ || 2469 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) { 2470 broadcast = true; 2471 } 2472 switch (btState) { 2473 case BluetoothHeadset.STATE_AUDIO_CONNECTED: 2474 state = AudioManager.SCO_AUDIO_STATE_CONNECTED; 2475 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && 2476 mScoAudioState != SCO_STATE_DEACTIVATE_REQ && 2477 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) { 2478 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 2479 } 2480 break; 2481 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: 2482 state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; 2483 mScoAudioState = SCO_STATE_INACTIVE; 2484 clearAllScoClients(null, false); 2485 break; 2486 case BluetoothHeadset.STATE_AUDIO_CONNECTING: 2487 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && 2488 mScoAudioState != SCO_STATE_DEACTIVATE_REQ && 2489 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) { 2490 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 2491 } 2492 default: 2493 // do not broadcast CONNECTING or invalid state 2494 broadcast = false; 2495 break; 2496 } 2497 } 2498 if (broadcast) { 2499 broadcastScoConnectionState(state); 2500 //FIXME: this is to maintain compatibility with deprecated intent 2501 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. 2502 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 2503 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); 2504 mContext.sendStickyBroadcast(newIntent); 2505 } 2506 } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { 2507 mBootCompleted = true; 2508 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SHARED_MSG, SENDMSG_NOOP, 2509 0, 0, null, 0); 2510 2511 mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR; 2512 resetBluetoothSco(); 2513 getBluetoothHeadset(); 2514 //FIXME: this is to maintain compatibility with deprecated intent 2515 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. 2516 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 2517 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 2518 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 2519 mContext.sendStickyBroadcast(newIntent); 2520 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) { 2521 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 2522 // a package is being removed, not replaced 2523 String packageName = intent.getData().getSchemeSpecificPart(); 2524 if (packageName != null) { 2525 removeMediaButtonReceiverForPackage(packageName); 2526 } 2527 } 2528 } 2529 } 2530 } 2531 2532 //========================================================================================== 2533 // AudioFocus 2534 //========================================================================================== 2535 2536 /* constant to identify focus stack entry that is used to hold the focus while the phone 2537 * is ringing or during a call 2538 */ 2539 private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls"; 2540 2541 private final static Object mAudioFocusLock = new Object(); 2542 2543 private final static Object mRingingLock = new Object(); 2544 2545 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 2546 @Override 2547 public void onCallStateChanged(int state, String incomingNumber) { 2548 if (state == TelephonyManager.CALL_STATE_RINGING) { 2549 //Log.v(TAG, " CALL_STATE_RINGING"); 2550 synchronized(mRingingLock) { 2551 mIsRinging = true; 2552 } 2553 } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK) 2554 || (state == TelephonyManager.CALL_STATE_IDLE)) { 2555 synchronized(mRingingLock) { 2556 mIsRinging = false; 2557 } 2558 } 2559 } 2560 }; 2561 2562 private void notifyTopOfAudioFocusStack() { 2563 // notify the top of the stack it gained focus 2564 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 2565 if (canReassignAudioFocus()) { 2566 try { 2567 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 2568 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId); 2569 } catch (RemoteException e) { 2570 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e); 2571 e.printStackTrace(); 2572 } 2573 } 2574 } 2575 } 2576 2577 private static class FocusStackEntry { 2578 public int mStreamType = -1;// no stream type 2579 public IAudioFocusDispatcher mFocusDispatcher = null; 2580 public IBinder mSourceRef = null; 2581 public String mClientId; 2582 public int mFocusChangeType; 2583 public AudioFocusDeathHandler mHandler; 2584 public String mPackageName; 2585 public int mCallingUid; 2586 2587 public FocusStackEntry() { 2588 } 2589 2590 public FocusStackEntry(int streamType, int duration, 2591 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, 2592 String pn, int uid) { 2593 mStreamType = streamType; 2594 mFocusDispatcher = afl; 2595 mSourceRef = source; 2596 mClientId = id; 2597 mFocusChangeType = duration; 2598 mHandler = hdlr; 2599 mPackageName = pn; 2600 mCallingUid = uid; 2601 } 2602 2603 public void unlinkToDeath() { 2604 if (mSourceRef != null && mHandler != null) { 2605 mSourceRef.unlinkToDeath(mHandler, 0); 2606 } 2607 } 2608 } 2609 2610 private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>(); 2611 2612 /** 2613 * Helper function: 2614 * Display in the log the current entries in the audio focus stack 2615 */ 2616 private void dumpFocusStack(PrintWriter pw) { 2617 pw.println("\nAudio Focus stack entries:"); 2618 synchronized(mAudioFocusLock) { 2619 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2620 while(stackIterator.hasNext()) { 2621 FocusStackEntry fse = stackIterator.next(); 2622 pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId 2623 + " -- duration: " + fse.mFocusChangeType 2624 + " -- uid: " + fse.mCallingUid); 2625 } 2626 } 2627 } 2628 2629 /** 2630 * Helper function: 2631 * Called synchronized on mAudioFocusLock 2632 * Remove a focus listener from the focus stack. 2633 * @param focusListenerToRemove the focus listener 2634 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding 2635 * focus, notify the next item in the stack it gained focus. 2636 */ 2637 private void removeFocusStackEntry(String clientToRemove, boolean signal) { 2638 // is the current top of the focus stack abandoning focus? (because of death or request) 2639 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove)) 2640 { 2641 //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); 2642 FocusStackEntry fse = mFocusStack.pop(); 2643 fse.unlinkToDeath(); 2644 if (signal) { 2645 // notify the new top of the stack it gained focus 2646 notifyTopOfAudioFocusStack(); 2647 // there's a new top of the stack, let the remote control know 2648 synchronized(mRCStack) { 2649 checkUpdateRemoteControlDisplay(RC_INFO_ALL); 2650 } 2651 } 2652 } else { 2653 // focus is abandoned by a client that's not at the top of the stack, 2654 // no need to update focus. 2655 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2656 while(stackIterator.hasNext()) { 2657 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 2658 if(fse.mClientId.equals(clientToRemove)) { 2659 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 2660 + fse.mClientId); 2661 stackIterator.remove(); 2662 fse.unlinkToDeath(); 2663 } 2664 } 2665 } 2666 } 2667 2668 /** 2669 * Helper function: 2670 * Called synchronized on mAudioFocusLock 2671 * Remove focus listeners from the focus stack for a particular client. 2672 */ 2673 private void removeFocusStackEntryForClient(IBinder cb) { 2674 // is the owner of the audio focus part of the client to remove? 2675 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && 2676 mFocusStack.peek().mSourceRef.equals(cb); 2677 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2678 while(stackIterator.hasNext()) { 2679 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 2680 if(fse.mSourceRef.equals(cb)) { 2681 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 2682 + fse.mClientId); 2683 stackIterator.remove(); 2684 } 2685 } 2686 if (isTopOfStackForClientToRemove) { 2687 // we removed an entry at the top of the stack: 2688 // notify the new top of the stack it gained focus. 2689 notifyTopOfAudioFocusStack(); 2690 // there's a new top of the stack, let the remote control know 2691 synchronized(mRCStack) { 2692 checkUpdateRemoteControlDisplay(RC_INFO_ALL); 2693 } 2694 } 2695 } 2696 2697 /** 2698 * Helper function: 2699 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. 2700 */ 2701 private boolean canReassignAudioFocus() { 2702 // focus requests are rejected during a phone call or when the phone is ringing 2703 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus 2704 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) { 2705 return false; 2706 } 2707 return true; 2708 } 2709 2710 /** 2711 * Inner class to monitor audio focus client deaths, and remove them from the audio focus 2712 * stack if necessary. 2713 */ 2714 private class AudioFocusDeathHandler implements IBinder.DeathRecipient { 2715 private IBinder mCb; // To be notified of client's death 2716 2717 AudioFocusDeathHandler(IBinder cb) { 2718 mCb = cb; 2719 } 2720 2721 public void binderDied() { 2722 synchronized(mAudioFocusLock) { 2723 Log.w(TAG, " AudioFocus audio focus client died"); 2724 removeFocusStackEntryForClient(mCb); 2725 } 2726 } 2727 2728 public IBinder getBinder() { 2729 return mCb; 2730 } 2731 } 2732 2733 2734 /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */ 2735 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb, 2736 IAudioFocusDispatcher fd, String clientId, String callingPackageName) { 2737 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId); 2738 // the main stream type for the audio focus request is currently not used. It may 2739 // potentially be used to handle multiple stream type-dependent audio focuses. 2740 2741 // we need a valid binder callback for clients 2742 if (!cb.pingBinder()) { 2743 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); 2744 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 2745 } 2746 2747 synchronized(mAudioFocusLock) { 2748 if (!canReassignAudioFocus()) { 2749 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 2750 } 2751 2752 // handle the potential premature death of the new holder of the focus 2753 // (premature death == death before abandoning focus) 2754 // Register for client death notification 2755 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); 2756 try { 2757 cb.linkToDeath(afdh, 0); 2758 } catch (RemoteException e) { 2759 // client has already died! 2760 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); 2761 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 2762 } 2763 2764 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) { 2765 // if focus is already owned by this client and the reason for acquiring the focus 2766 // hasn't changed, don't do anything 2767 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) { 2768 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2769 } 2770 // the reason for the audio focus request has changed: remove the current top of 2771 // stack and respond as if we had a new focus owner 2772 mFocusStack.pop(); 2773 } 2774 2775 // notify current top of stack it is losing focus 2776 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 2777 try { 2778 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 2779 -1 * focusChangeHint, // loss and gain codes are inverse of each other 2780 mFocusStack.peek().mClientId); 2781 } catch (RemoteException e) { 2782 Log.e(TAG, " Failure to signal loss of focus due to "+ e); 2783 e.printStackTrace(); 2784 } 2785 } 2786 2787 // focus requester might already be somewhere below in the stack, remove it 2788 removeFocusStackEntry(clientId, false /* signal */); 2789 2790 // push focus requester at the top of the audio focus stack 2791 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb, 2792 clientId, afdh, callingPackageName, Binder.getCallingUid())); 2793 2794 // there's a new top of the stack, let the remote control know 2795 synchronized(mRCStack) { 2796 checkUpdateRemoteControlDisplay(RC_INFO_ALL); 2797 } 2798 }//synchronized(mAudioFocusLock) 2799 2800 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2801 } 2802 2803 /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */ 2804 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) { 2805 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId); 2806 try { 2807 // this will take care of notifying the new focus owner if needed 2808 synchronized(mAudioFocusLock) { 2809 removeFocusStackEntry(clientId, true); 2810 } 2811 } catch (java.util.ConcurrentModificationException cme) { 2812 // Catching this exception here is temporary. It is here just to prevent 2813 // a crash seen when the "Silent" notification is played. This is believed to be fixed 2814 // but this try catch block is left just to be safe. 2815 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme); 2816 cme.printStackTrace(); 2817 } 2818 2819 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2820 } 2821 2822 2823 public void unregisterAudioFocusClient(String clientId) { 2824 synchronized(mAudioFocusLock) { 2825 removeFocusStackEntry(clientId, false); 2826 } 2827 } 2828 2829 2830 //========================================================================================== 2831 // RemoteControl 2832 //========================================================================================== 2833 /** 2834 * Receiver for media button intents. Handles the dispatching of the media button event 2835 * to one of the registered listeners, or if there was none, resumes the intent broadcast 2836 * to the rest of the system. 2837 */ 2838 private class MediaButtonBroadcastReceiver extends BroadcastReceiver { 2839 @Override 2840 public void onReceive(Context context, Intent intent) { 2841 String action = intent.getAction(); 2842 if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) { 2843 return; 2844 } 2845 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 2846 if (event != null) { 2847 // if in a call or ringing, do not break the current phone app behavior 2848 // TODO modify this to let the phone app specifically get the RC focus 2849 // add modify the phone app to take advantage of the new API 2850 synchronized(mRingingLock) { 2851 if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) || 2852 (getMode() == AudioSystem.MODE_IN_COMMUNICATION) || 2853 (getMode() == AudioSystem.MODE_RINGTONE) ) { 2854 return; 2855 } 2856 } 2857 synchronized(mRCStack) { 2858 if (!mRCStack.empty()) { 2859 // create a new intent specifically aimed at the current registered listener 2860 Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 2861 targetedIntent.putExtras(intent.getExtras()); 2862 targetedIntent.setComponent(mRCStack.peek().mReceiverComponent); 2863 // trap the current broadcast 2864 abortBroadcast(); 2865 //Log.v(TAG, " Sending intent" + targetedIntent); 2866 context.sendBroadcast(targetedIntent, null); 2867 } 2868 } 2869 } 2870 } 2871 } 2872 2873 private final Object mCurrentRcLock = new Object(); 2874 /** 2875 * The one remote control client to be polled for display information. 2876 * This object may be null. 2877 * Access protected by mCurrentRcLock. 2878 */ 2879 private IRemoteControlClient mCurrentRcClient = null; 2880 2881 private final static int RC_INFO_NONE = 0; 2882 private final static int RC_INFO_ALL = 2883 AudioManager.RemoteControlParameters.FLAG_INFORMATION_CHANGED_ALBUM_ART | 2884 AudioManager.RemoteControlParameters.FLAG_INFORMATION_CHANGED_KEY_MEDIA | 2885 AudioManager.RemoteControlParameters.FLAG_INFORMATION_CHANGED_METADATA | 2886 AudioManager.RemoteControlParameters.FLAG_INFORMATION_CHANGED_PLAYSTATE; 2887 2888 /** 2889 * A monotonically increasing generation counter for mCurrentRcClient. 2890 * Only accessed with a lock on mCurrentRcLock. 2891 * No value wrap-around issues as we only act on equal values. 2892 */ 2893 private int mCurrentRcClientGen = 0; 2894 2895 /** 2896 * Returns the current remote control client. 2897 * @param rcClientId the counter value that matches the extra 2898 * {@link AudioManager#EXTRA_REMOTE_CONTROL_CLIENT} in the 2899 * {@link AudioManager#REMOTE_CONTROL_CLIENT_CHANGED} event 2900 * @return the current IRemoteControlClient from which information to display on the remote 2901 * control can be retrieved, or null if rcClientId doesn't match the current generation 2902 * counter. 2903 */ 2904 public IRemoteControlClient getRemoteControlClient(int rcClientId) { 2905 synchronized(mCurrentRcLock) { 2906 if (rcClientId == mCurrentRcClientGen) { 2907 return mCurrentRcClient; 2908 } else { 2909 return null; 2910 } 2911 } 2912 } 2913 2914 /** 2915 * Inner class to monitor remote control client deaths, and remove the client for the 2916 * remote control stack if necessary. 2917 */ 2918 private class RcClientDeathHandler implements IBinder.DeathRecipient { 2919 private IBinder mCb; // To be notified of client's death 2920 private ComponentName mRcEventReceiver; 2921 2922 RcClientDeathHandler(IBinder cb, ComponentName eventReceiver) { 2923 mCb = cb; 2924 mRcEventReceiver = eventReceiver; 2925 } 2926 2927 public void binderDied() { 2928 Log.w(TAG, " RemoteControlClient died"); 2929 // remote control client died, make sure the displays don't use it anymore 2930 // by setting its remote control client to null 2931 registerRemoteControlClient(mRcEventReceiver, null, null/*ignored*/); 2932 } 2933 2934 public IBinder getBinder() { 2935 return mCb; 2936 } 2937 } 2938 2939 private static class RemoteControlStackEntry { 2940 /** the target for the ACTION_MEDIA_BUTTON events */ 2941 public ComponentName mReceiverComponent;// always non null 2942 public String mCallingPackageName; 2943 public int mCallingUid; 2944 2945 /** provides access to the information to display on the remote control */ 2946 public IRemoteControlClient mRcClient; 2947 public RcClientDeathHandler mRcClientDeathHandler; 2948 2949 public RemoteControlStackEntry(ComponentName r) { 2950 mReceiverComponent = r; 2951 mCallingUid = -1; 2952 mRcClient = null; 2953 } 2954 2955 public void unlinkToRcClientDeath() { 2956 if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) { 2957 try { 2958 mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0); 2959 } catch (java.util.NoSuchElementException e) { 2960 // not much we can do here 2961 Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()"); 2962 e.printStackTrace(); 2963 } 2964 } 2965 } 2966 } 2967 2968 /** 2969 * The stack of remote control event receivers. 2970 * Code sections and methods that modify the remote control event receiver stack are 2971 * synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either 2972 * stack, audio focus or RC, can lead to a change in the remote control display 2973 */ 2974 private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>(); 2975 2976 /** 2977 * Helper function: 2978 * Display in the log the current entries in the remote control focus stack 2979 */ 2980 private void dumpRCStack(PrintWriter pw) { 2981 pw.println("\nRemote Control stack entries:"); 2982 synchronized(mRCStack) { 2983 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2984 while(stackIterator.hasNext()) { 2985 RemoteControlStackEntry rcse = stackIterator.next(); 2986 pw.println(" receiver: " + rcse.mReceiverComponent + 2987 " -- client: " + rcse.mRcClient + 2988 " -- uid: " + rcse.mCallingUid); 2989 } 2990 } 2991 } 2992 2993 /** 2994 * Helper function: 2995 * Remove any entry in the remote control stack that has the same package name as packageName 2996 * Pre-condition: packageName != null 2997 */ 2998 private void removeMediaButtonReceiverForPackage(String packageName) { 2999 synchronized(mRCStack) { 3000 if (mRCStack.empty()) { 3001 return; 3002 } else { 3003 RemoteControlStackEntry oldTop = mRCStack.peek(); 3004 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3005 // iterate over the stack entries 3006 while(stackIterator.hasNext()) { 3007 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 3008 if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) { 3009 // a stack entry is from the package being removed, remove it from the stack 3010 stackIterator.remove(); 3011 } 3012 } 3013 if (mRCStack.empty()) { 3014 // no saved media button receiver 3015 mAudioHandler.sendMessage( 3016 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, 3017 null)); 3018 return; 3019 } else if (oldTop != mRCStack.peek()) { 3020 // the top of the stack has changed, save it in the system settings 3021 // by posting a message to persist it 3022 mAudioHandler.sendMessage( 3023 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, 3024 mRCStack.peek().mReceiverComponent)); 3025 } 3026 } 3027 } 3028 } 3029 3030 /** 3031 * Helper function: 3032 * Restore remote control receiver from the system settings 3033 */ 3034 private void restoreMediaButtonReceiver() { 3035 String receiverName = Settings.System.getString(mContentResolver, 3036 Settings.System.MEDIA_BUTTON_RECEIVER); 3037 if ((null != receiverName) && !receiverName.isEmpty()) { 3038 ComponentName receiverComponentName = ComponentName.unflattenFromString(receiverName); 3039 registerMediaButtonEventReceiver(receiverComponentName); 3040 } 3041 // upon restoring (e.g. after boot), do we want to refresh all remotes? 3042 } 3043 3044 /** 3045 * Helper function: 3046 * Set the new remote control receiver at the top of the RC focus stack 3047 */ 3048 private void pushMediaButtonReceiver(ComponentName newReceiver) { 3049 // already at top of stack? 3050 if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) { 3051 return; 3052 } 3053 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3054 RemoteControlStackEntry rcse = null; 3055 boolean wasInsideStack = false; 3056 while(stackIterator.hasNext()) { 3057 rcse = (RemoteControlStackEntry)stackIterator.next(); 3058 if(rcse.mReceiverComponent.equals(newReceiver)) { 3059 wasInsideStack = true; 3060 stackIterator.remove(); 3061 break; 3062 } 3063 } 3064 if (!wasInsideStack) { 3065 rcse = new RemoteControlStackEntry(newReceiver); 3066 } 3067 mRCStack.push(rcse); 3068 3069 // post message to persist the default media button receiver 3070 mAudioHandler.sendMessage( mAudioHandler.obtainMessage( 3071 MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, newReceiver/*obj*/) ); 3072 } 3073 3074 /** 3075 * Helper function: 3076 * Remove the remote control receiver from the RC focus stack 3077 */ 3078 private void removeMediaButtonReceiver(ComponentName newReceiver) { 3079 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3080 while(stackIterator.hasNext()) { 3081 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 3082 if(rcse.mReceiverComponent.equals(newReceiver)) { 3083 stackIterator.remove(); 3084 break; 3085 } 3086 } 3087 } 3088 3089 /** 3090 * Helper function: 3091 * Called synchronized on mRCStack 3092 */ 3093 private boolean isCurrentRcController(ComponentName eventReceiver) { 3094 if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(eventReceiver)) { 3095 return true; 3096 } 3097 return false; 3098 } 3099 3100 /** 3101 * Helper function: 3102 * Called synchronized on mRCStack 3103 */ 3104 private void clearRemoteControlDisplay() { 3105 synchronized(mCurrentRcLock) { 3106 mCurrentRcClient = null; 3107 } 3108 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) ); 3109 } 3110 3111 /** 3112 * Helper function: 3113 * Called synchronized on mRCStack 3114 * mRCStack.empty() is false 3115 */ 3116 private void updateRemoteControlDisplay(int infoChangedFlags) { 3117 RemoteControlStackEntry rcse = mRCStack.peek(); 3118 int infoFlagsAboutToBeUsed = infoChangedFlags; 3119 // this is where we enforce opt-in for information display on the remote controls 3120 // with the new AudioManager.registerRemoteControlClient() API 3121 if (rcse.mRcClient == null) { 3122 //Log.w(TAG, "Can't update remote control display with null remote control client"); 3123 clearRemoteControlDisplay(); 3124 return; 3125 } 3126 synchronized(mCurrentRcLock) { 3127 if (!rcse.mRcClient.equals(mCurrentRcClient)) { 3128 // new RC client, assume every type of information shall be queried 3129 infoFlagsAboutToBeUsed = RC_INFO_ALL; 3130 } 3131 mCurrentRcClient = rcse.mRcClient; 3132 } 3133 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE, 3134 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse.mRcClient /* obj */) ); 3135 } 3136 3137 /** 3138 * Helper function: 3139 * Called synchronized on mFocusLock, then mRCStack 3140 * Check whether the remote control display should be updated, triggers the update if required 3141 * @param infoChangedFlags the flags corresponding to the remote control client information 3142 * that has changed, if applicable (checking for the update conditions might trigger a 3143 * clear, rather than an update event). 3144 */ 3145 private void checkUpdateRemoteControlDisplay(int infoChangedFlags) { 3146 // determine whether the remote control display should be refreshed 3147 // if either stack is empty, there is a mismatch, so clear the RC display 3148 if (mRCStack.isEmpty() || mFocusStack.isEmpty()) { 3149 clearRemoteControlDisplay(); 3150 return; 3151 } 3152 // if the top of the two stacks belong to different packages, there is a mismatch, clear 3153 if ((mRCStack.peek().mCallingPackageName != null) 3154 && (mFocusStack.peek().mPackageName != null) 3155 && !(mRCStack.peek().mCallingPackageName.compareTo( 3156 mFocusStack.peek().mPackageName) == 0)) { 3157 clearRemoteControlDisplay(); 3158 return; 3159 } 3160 // if the audio focus didn't originate from the same Uid as the one in which the remote 3161 // control information will be retrieved, clear 3162 if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) { 3163 clearRemoteControlDisplay(); 3164 return; 3165 } 3166 // refresh conditions were verified: update the remote controls 3167 updateRemoteControlDisplay(infoChangedFlags); 3168 } 3169 3170 /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */ 3171 public void registerMediaButtonEventReceiver(ComponentName eventReceiver) { 3172 Log.i(TAG, " Remote Control registerMediaButtonEventReceiver() for " + eventReceiver); 3173 3174 synchronized(mAudioFocusLock) { 3175 synchronized(mRCStack) { 3176 pushMediaButtonReceiver(eventReceiver); 3177 // new RC client, assume every type of information shall be queried 3178 checkUpdateRemoteControlDisplay(RC_INFO_ALL); 3179 } 3180 } 3181 } 3182 3183 /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */ 3184 public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) { 3185 Log.i(TAG, " Remote Control unregisterMediaButtonEventReceiver() for " + eventReceiver); 3186 3187 synchronized(mAudioFocusLock) { 3188 synchronized(mRCStack) { 3189 boolean topOfStackWillChange = isCurrentRcController(eventReceiver); 3190 removeMediaButtonReceiver(eventReceiver); 3191 if (topOfStackWillChange) { 3192 // current RC client will change, assume every type of info needs to be queried 3193 checkUpdateRemoteControlDisplay(RC_INFO_ALL); 3194 } 3195 } 3196 } 3197 } 3198 3199 /** see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) */ 3200 public void registerRemoteControlClient(ComponentName eventReceiver, 3201 IRemoteControlClient rcClient, String callingPackageName) { 3202 synchronized(mAudioFocusLock) { 3203 synchronized(mRCStack) { 3204 // store the new display information 3205 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3206 while(stackIterator.hasNext()) { 3207 RemoteControlStackEntry rcse = stackIterator.next(); 3208 if(rcse.mReceiverComponent.equals(eventReceiver)) { 3209 // already had a remote control client? 3210 if (rcse.mRcClientDeathHandler != null) { 3211 // stop monitoring the old client's death 3212 rcse.unlinkToRcClientDeath(); 3213 } 3214 // save the new remote control client 3215 rcse.mRcClient = rcClient; 3216 rcse.mCallingPackageName = callingPackageName; 3217 rcse.mCallingUid = Binder.getCallingUid(); 3218 if (rcClient == null) { 3219 break; 3220 } 3221 // monitor the new client's death 3222 IBinder b = rcClient.asBinder(); 3223 RcClientDeathHandler rcdh = 3224 new RcClientDeathHandler(b, rcse.mReceiverComponent); 3225 try { 3226 b.linkToDeath(rcdh, 0); 3227 } catch (RemoteException e) { 3228 // remote control client is DOA, disqualify it 3229 Log.w(TAG, "registerRemoteControlClient() has a dead client " + b); 3230 rcse.mRcClient = null; 3231 } 3232 rcse.mRcClientDeathHandler = rcdh; 3233 break; 3234 } 3235 } 3236 // if the eventReceiver is at the top of the stack 3237 // then check for potential refresh of the remote controls 3238 if (isCurrentRcController(eventReceiver)) { 3239 checkUpdateRemoteControlDisplay(RC_INFO_ALL); 3240 } 3241 } 3242 } 3243 } 3244 3245 /** see AudioManager.notifyRemoteControlInformationChanged(ComponentName er, int infoFlag) */ 3246 public void notifyRemoteControlInformationChanged(ComponentName eventReceiver, int infoFlag) { 3247 synchronized(mAudioFocusLock) { 3248 synchronized(mRCStack) { 3249 // only refresh if the eventReceiver is at the top of the stack 3250 if (isCurrentRcController(eventReceiver)) { 3251 checkUpdateRemoteControlDisplay(infoFlag); 3252 } 3253 } 3254 } 3255 } 3256 3257 @Override 3258 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3259 // TODO probably a lot more to do here than just the audio focus and remote control stacks 3260 dumpFocusStack(pw); 3261 dumpRCStack(pw); 3262 } 3263 3264 3265} 3266