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