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