AudioService.java revision 30c918ce7fbe171944b28fc91b3f22b3d631872d
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 static android.media.AudioManager.RINGER_MODE_NORMAL; 20import static android.media.AudioManager.RINGER_MODE_SILENT; 21import static android.media.AudioManager.RINGER_MODE_VIBRATE; 22 23import android.app.ActivityManagerNative; 24import android.app.KeyguardManager; 25import android.app.PendingIntent; 26import android.app.PendingIntent.CanceledException; 27import android.bluetooth.BluetoothA2dp; 28import android.bluetooth.BluetoothAdapter; 29import android.bluetooth.BluetoothClass; 30import android.bluetooth.BluetoothDevice; 31import android.bluetooth.BluetoothHeadset; 32import android.bluetooth.BluetoothProfile; 33import android.content.BroadcastReceiver; 34import android.content.ComponentName; 35import android.content.ContentResolver; 36import android.content.Context; 37import android.content.Intent; 38import android.content.IntentFilter; 39import android.content.pm.PackageManager; 40import android.database.ContentObserver; 41import android.media.MediaPlayer.OnCompletionListener; 42import android.media.MediaPlayer.OnErrorListener; 43import android.os.Binder; 44import android.os.Bundle; 45import android.os.Environment; 46import android.os.Handler; 47import android.os.IBinder; 48import android.os.Looper; 49import android.os.Message; 50import android.os.RemoteException; 51import android.os.ServiceManager; 52import android.os.SystemProperties; 53import android.provider.Settings; 54import android.provider.Settings.System; 55import android.telephony.PhoneStateListener; 56import android.telephony.TelephonyManager; 57import android.util.Log; 58import android.view.KeyEvent; 59import android.view.VolumePanel; 60 61import com.android.internal.telephony.ITelephony; 62 63import java.io.FileDescriptor; 64import java.io.IOException; 65import java.io.PrintWriter; 66import java.util.ArrayList; 67import java.util.HashMap; 68import java.util.Iterator; 69import java.util.List; 70import java.util.Map; 71import java.util.NoSuchElementException; 72import java.util.Set; 73import java.util.Stack; 74 75/** 76 * The implementation of the volume manager service. 77 * <p> 78 * This implementation focuses on delivering a responsive UI. Most methods are 79 * asynchronous to external calls. For example, the task of setting a volume 80 * will update our internal state, but in a separate thread will set the system 81 * volume and later persist to the database. Similarly, setting the ringer mode 82 * will update the state and broadcast a change and in a separate thread later 83 * persist the ringer mode. 84 * 85 * @hide 86 */ 87public class AudioService extends IAudioService.Stub { 88 89 private static final String TAG = "AudioService"; 90 91 /** Debug remote control client/display feature */ 92 protected static final boolean DEBUG_RC = false; 93 94 /** How long to delay before persisting a change in volume/ringer mode. */ 95 private static final int PERSIST_DELAY = 3000; 96 97 private Context mContext; 98 private ContentResolver mContentResolver; 99 private boolean mVoiceCapable; 100 101 /** The UI */ 102 private VolumePanel mVolumePanel; 103 104 // sendMsg() flags 105 /** If the msg is already queued, replace it with this one. */ 106 private static final int SENDMSG_REPLACE = 0; 107 /** If the msg is already queued, ignore this one and leave the old. */ 108 private static final int SENDMSG_NOOP = 1; 109 /** If the msg is already queued, queue this one and leave the old. */ 110 private static final int SENDMSG_QUEUE = 2; 111 112 // AudioHandler message.whats 113 private static final int MSG_SET_DEVICE_VOLUME = 0; 114 private static final int MSG_PERSIST_VOLUME = 1; 115 private static final int MSG_PERSIST_RINGER_MODE = 3; 116 private static final int MSG_PERSIST_VIBRATE_SETTING = 4; 117 private static final int MSG_MEDIA_SERVER_DIED = 5; 118 private static final int MSG_MEDIA_SERVER_STARTED = 6; 119 private static final int MSG_PLAY_SOUND_EFFECT = 7; 120 private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8; 121 private static final int MSG_LOAD_SOUND_EFFECTS = 9; 122 private static final int MSG_SET_FORCE_USE = 10; 123 private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 11; 124 private static final int MSG_BT_HEADSET_CNCT_FAILED = 12; 125 private static final int MSG_RCDISPLAY_CLEAR = 13; 126 private static final int MSG_RCDISPLAY_UPDATE = 14; 127 private static final int MSG_SET_ALL_VOLUMES = 15; 128 129 130 // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be 131 // persisted 132 private static final int PERSIST_CURRENT = 0x1; 133 private static final int PERSIST_LAST_AUDIBLE = 0x2; 134 135 private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000; 136 // Timeout for connection to bluetooth headset service 137 private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000; 138 139 140 /** @see AudioSystemThread */ 141 private AudioSystemThread mAudioSystemThread; 142 /** @see AudioHandler */ 143 private AudioHandler mAudioHandler; 144 /** @see VolumeStreamState */ 145 private VolumeStreamState[] mStreamStates; 146 private SettingsObserver mSettingsObserver; 147 148 private int mMode; 149 // protects mRingerMode 150 private final Object mSettingsLock = new Object(); 151 private boolean mMediaServerOk; 152 153 private SoundPool mSoundPool; 154 private final Object mSoundEffectsLock = new Object(); 155 private static final int NUM_SOUNDPOOL_CHANNELS = 4; 156 private static final int SOUND_EFFECT_VOLUME = 1000; 157 158 /* Sound effect file names */ 159 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/"; 160 private static final String[] SOUND_EFFECT_FILES = new String[] { 161 "Effect_Tick.ogg", 162 "KeypressStandard.ogg", 163 "KeypressSpacebar.ogg", 164 "KeypressDelete.ogg", 165 "KeypressReturn.ogg" 166 }; 167 168 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to 169 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect 170 * uses soundpool (second column) */ 171 private final int[][] SOUND_EFFECT_FILES_MAP = new int[][] { 172 {0, -1}, // FX_KEY_CLICK 173 {0, -1}, // FX_FOCUS_NAVIGATION_UP 174 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN 175 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT 176 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT 177 {1, -1}, // FX_KEYPRESS_STANDARD 178 {2, -1}, // FX_KEYPRESS_SPACEBAR 179 {3, -1}, // FX_FOCUS_DELETE 180 {4, -1} // FX_FOCUS_RETURN 181 }; 182 183 /** @hide Maximum volume index values for audio streams */ 184 private final int[] MAX_STREAM_VOLUME = new int[] { 185 5, // STREAM_VOICE_CALL 186 7, // STREAM_SYSTEM 187 7, // STREAM_RING 188 15, // STREAM_MUSIC 189 7, // STREAM_ALARM 190 7, // STREAM_NOTIFICATION 191 15, // STREAM_BLUETOOTH_SCO 192 7, // STREAM_SYSTEM_ENFORCED 193 15, // STREAM_DTMF 194 15 // STREAM_TTS 195 }; 196 /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings 197 * of another stream: This avoids multiplying the volume settings for hidden 198 * stream types that follow other stream behavior for volume settings 199 * NOTE: do not create loops in aliases! */ 200 private final int[] STREAM_VOLUME_ALIAS = new int[] { 201 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL 202 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM 203 AudioSystem.STREAM_RING, // STREAM_RING 204 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC 205 AudioSystem.STREAM_ALARM, // STREAM_ALARM 206 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION 207 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO 208 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM_ENFORCED 209 AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF 210 AudioSystem.STREAM_MUSIC // STREAM_TTS 211 }; 212 213 private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() { 214 public void onError(int error) { 215 switch (error) { 216 case AudioSystem.AUDIO_STATUS_SERVER_DIED: 217 if (mMediaServerOk) { 218 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0, 219 null, 1500); 220 mMediaServerOk = false; 221 } 222 break; 223 case AudioSystem.AUDIO_STATUS_OK: 224 if (!mMediaServerOk) { 225 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SENDMSG_NOOP, 0, 0, 226 null, 0); 227 mMediaServerOk = true; 228 } 229 break; 230 default: 231 break; 232 } 233 } 234 }; 235 236 /** 237 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL}, 238 * {@link AudioManager#RINGER_MODE_SILENT}, or 239 * {@link AudioManager#RINGER_MODE_VIBRATE}. 240 */ 241 // protected by mSettingsLock 242 private int mRingerMode; 243 244 /** @see System#MODE_RINGER_STREAMS_AFFECTED */ 245 private int mRingerModeAffectedStreams; 246 247 // Streams currently muted by ringer mode 248 private int mRingerModeMutedStreams; 249 250 /** @see System#MUTE_STREAMS_AFFECTED */ 251 private int mMuteAffectedStreams; 252 253 /** 254 * Has multiple bits per vibrate type to indicate the type's vibrate 255 * setting. See {@link #setVibrateSetting(int, int)}. 256 * <p> 257 * NOTE: This is not the final decision of whether vibrate is on/off for the 258 * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}. 259 */ 260 private int mVibrateSetting; 261 262 // Broadcast receiver for device connections intent broadcasts 263 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver(); 264 265 // Broadcast receiver for media button broadcasts (separate from mReceiver to 266 // independently change its priority) 267 private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver(); 268 269 // Used to alter media button redirection when the phone is ringing. 270 private boolean mIsRinging = false; 271 272 // Devices currently connected 273 private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>(); 274 275 // Forced device usage for communications 276 private int mForcedUseForComm; 277 278 // List of binder death handlers for setMode() client processes. 279 // The last process to have called setMode() is at the top of the list. 280 private final ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>(); 281 282 // List of clients having issued a SCO start request 283 private final ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>(); 284 285 // BluetoothHeadset API to control SCO connection 286 private BluetoothHeadset mBluetoothHeadset; 287 288 // Bluetooth headset device 289 private BluetoothDevice mBluetoothHeadsetDevice; 290 291 // Indicate if SCO audio connection is currently active and if the initiator is 292 // audio service (internal) or bluetooth headset (external) 293 private int mScoAudioState; 294 // SCO audio state is not active 295 private static final int SCO_STATE_INACTIVE = 0; 296 // SCO audio activation request waiting for headset service to connect 297 private static final int SCO_STATE_ACTIVATE_REQ = 1; 298 // SCO audio state is active or starting due to a local request to start a virtual call 299 private static final int SCO_STATE_ACTIVE_INTERNAL = 3; 300 // SCO audio deactivation request waiting for headset service to connect 301 private static final int SCO_STATE_DEACTIVATE_REQ = 5; 302 303 // SCO audio state is active due to an action in BT handsfree (either voice recognition or 304 // in call audio) 305 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; 306 // Deactivation request for all SCO connections (initiated by audio mode change) 307 // waiting for headset service to connect 308 private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4; 309 310 // Current connection state indicated by bluetooth headset 311 private int mScoConnectionState; 312 313 // true if boot sequence has been completed 314 private boolean mBootCompleted; 315 // listener for SoundPool sample load completion indication 316 private SoundPoolCallback mSoundPoolCallBack; 317 // thread for SoundPool listener 318 private SoundPoolListenerThread mSoundPoolListenerThread; 319 // message looper for SoundPool listener 320 private Looper mSoundPoolLooper = null; 321 // default volume applied to sound played with playSoundEffect() 322 private static final int SOUND_EFFECT_DEFAULT_VOLUME_DB = -20; 323 // volume applied to sound played with playSoundEffect() read from ro.config.sound_fx_volume 324 private int SOUND_EFFECT_VOLUME_DB; 325 // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification 326 // stopped 327 private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000; 328 // previous volume adjustment direction received by checkForRingerModeChange() 329 private int mPrevVolDirection = AudioManager.ADJUST_SAME; 330 // Keyguard manager proxy 331 private KeyguardManager mKeyguardManager; 332 333 334 /////////////////////////////////////////////////////////////////////////// 335 // Construction 336 /////////////////////////////////////////////////////////////////////////// 337 338 /** @hide */ 339 public AudioService(Context context) { 340 mContext = context; 341 mContentResolver = context.getContentResolver(); 342 mVoiceCapable = mContext.getResources().getBoolean( 343 com.android.internal.R.bool.config_voice_capable); 344 345 // Intialized volume 346 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt( 347 "ro.config.vc_call_vol_steps", 348 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]); 349 350 SOUND_EFFECT_VOLUME_DB = SystemProperties.getInt( 351 "ro.config.sound_fx_volume", 352 SOUND_EFFECT_DEFAULT_VOLUME_DB); 353 354 mVolumePanel = new VolumePanel(context, this); 355 mForcedUseForComm = AudioSystem.FORCE_NONE; 356 createAudioSystemThread(); 357 readPersistedSettings(); 358 mSettingsObserver = new SettingsObserver(); 359 createStreamStates(); 360 361 mMode = AudioSystem.MODE_NORMAL; 362 mMediaServerOk = true; 363 364 // Call setRingerModeInt() to apply correct mute 365 // state on streams affected by ringer mode. 366 mRingerModeMutedStreams = 0; 367 setRingerModeInt(getRingerMode(), false); 368 369 AudioSystem.setErrorCallback(mAudioSystemCallback); 370 371 // Register for device connection intent broadcasts. 372 IntentFilter intentFilter = 373 new IntentFilter(Intent.ACTION_HEADSET_PLUG); 374 375 intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 376 intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 377 intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 378 intentFilter.addAction(Intent.ACTION_DOCK_EVENT); 379 intentFilter.addAction(Intent.ACTION_USB_ANLG_HEADSET_PLUG); 380 intentFilter.addAction(Intent.ACTION_USB_DGTL_HEADSET_PLUG); 381 intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG); 382 intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); 383 intentFilter.addAction(Intent.ACTION_SCREEN_ON); 384 intentFilter.addAction(Intent.ACTION_SCREEN_OFF); 385 context.registerReceiver(mReceiver, intentFilter); 386 387 // Register for package removal intent broadcasts for media button receiver persistence 388 IntentFilter pkgFilter = new IntentFilter(); 389 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 390 pkgFilter.addDataScheme("package"); 391 context.registerReceiver(mReceiver, pkgFilter); 392 393 // Register for media button intent broadcasts. 394 intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON); 395 // Workaround for bug on priority setting 396 //intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 397 intentFilter.setPriority(Integer.MAX_VALUE); 398 context.registerReceiver(mMediaButtonReceiver, intentFilter); 399 400 // Register for phone state monitoring 401 TelephonyManager tmgr = (TelephonyManager) 402 context.getSystemService(Context.TELEPHONY_SERVICE); 403 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 404 } 405 406 private void createAudioSystemThread() { 407 mAudioSystemThread = new AudioSystemThread(); 408 mAudioSystemThread.start(); 409 waitForAudioHandlerCreation(); 410 } 411 412 /** Waits for the volume handler to be created by the other thread. */ 413 private void waitForAudioHandlerCreation() { 414 synchronized(this) { 415 while (mAudioHandler == null) { 416 try { 417 // Wait for mAudioHandler to be set by the other thread 418 wait(); 419 } catch (InterruptedException e) { 420 Log.e(TAG, "Interrupted while waiting on volume handler."); 421 } 422 } 423 } 424 } 425 426 private void createStreamStates() { 427 int numStreamTypes = AudioSystem.getNumStreamTypes(); 428 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes]; 429 430 for (int i = 0; i < numStreamTypes; i++) { 431 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i); 432 } 433 434 // Correct stream index values for streams with aliases 435 for (int i = 0; i < numStreamTypes; i++) { 436 int device = getDeviceForStream(i); 437 if (STREAM_VOLUME_ALIAS[i] != i) { 438 int index = rescaleIndex(streams[i].getIndex(device, false /* lastAudible */), 439 STREAM_VOLUME_ALIAS[i], 440 i); 441 streams[i].mIndex.put(device, streams[i].getValidIndex(index)); 442 streams[i].applyDeviceVolume(device); 443 index = rescaleIndex(streams[i].getIndex(device, true /* lastAudible */), 444 STREAM_VOLUME_ALIAS[i], 445 i); 446 streams[i].mLastAudibleIndex.put(device, streams[i].getValidIndex(index)); 447 } 448 } 449 } 450 451 private void readPersistedSettings() { 452 final ContentResolver cr = mContentResolver; 453 454 int ringerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL); 455 // sanity check in case the settings are restored from a device with incompatible 456 // ringer modes 457 if (!AudioManager.isValidRingerMode(ringerMode)) { 458 ringerMode = AudioManager.RINGER_MODE_NORMAL; 459 System.putInt(cr, System.MODE_RINGER, ringerMode); 460 } 461 synchronized(mSettingsLock) { 462 mRingerMode = ringerMode; 463 } 464 465 mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0); 466 467 // make sure settings for ringer mode are consistent with device type: non voice capable 468 // devices (tablets) include media stream in silent mode whereas phones don't. 469 mRingerModeAffectedStreams = Settings.System.getInt(cr, 470 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 471 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)| 472 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED))); 473 if (mVoiceCapable) { 474 mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC); 475 } else { 476 mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC); 477 } 478 Settings.System.putInt(cr, 479 Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams); 480 481 mMuteAffectedStreams = System.getInt(cr, 482 System.MUTE_STREAMS_AFFECTED, 483 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM))); 484 485 // Each stream will read its own persisted settings 486 487 // Broadcast the sticky intent 488 broadcastRingerMode(ringerMode); 489 490 // Broadcast vibrate settings 491 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); 492 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); 493 494 // Restore the default media button receiver from the system settings 495 restoreMediaButtonReceiver(); 496 } 497 498 private int rescaleIndex(int index, int srcStream, int dstStream) { 499 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex(); 500 } 501 502 /////////////////////////////////////////////////////////////////////////// 503 // IPC methods 504 /////////////////////////////////////////////////////////////////////////// 505 506 /** @see AudioManager#adjustVolume(int, int) */ 507 public void adjustVolume(int direction, int flags) { 508 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags); 509 } 510 511 /** @see AudioManager#adjustVolume(int, int, int) */ 512 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { 513 514 int streamType; 515 if ((flags & AudioManager.FLAG_FORCE_STREAM) != 0) { 516 streamType = suggestedStreamType; 517 } else { 518 streamType = getActiveStreamType(suggestedStreamType); 519 } 520 521 // Play sounds on STREAM_RING only and if lock screen is not on. 522 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && 523 ((STREAM_VOLUME_ALIAS[streamType] != AudioSystem.STREAM_RING) 524 || (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) { 525 flags &= ~AudioManager.FLAG_PLAY_SOUND; 526 } 527 528 adjustStreamVolume(streamType, direction, flags); 529 } 530 531 /** @see AudioManager#adjustStreamVolume(int, int, int) */ 532 public void adjustStreamVolume(int streamType, int direction, int flags) { 533 ensureValidDirection(direction); 534 ensureValidStreamType(streamType); 535 536 // use stream type alias here so that streams with same alias have the same behavior, 537 // including with regard to silent mode control (e.g the use of STREAM_RING below and in 538 // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION) 539 int streamTypeAlias = STREAM_VOLUME_ALIAS[streamType]; 540 VolumeStreamState streamState = mStreamStates[streamTypeAlias]; 541 542 final int device = getDeviceForStream(streamTypeAlias); 543 // get last audible index if stream is muted, current index otherwise 544 final int oldIndex = streamState.getIndex(device, 545 (streamState.muteCount() != 0) /* lastAudible */); 546 boolean adjustVolume = true; 547 548 // If either the client forces allowing ringer modes for this adjustment, 549 // or the stream type is one that is affected by ringer modes 550 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || 551 streamTypeAlias == AudioSystem.STREAM_RING || 552 (!mVoiceCapable && streamTypeAlias == AudioSystem.STREAM_MUSIC)) { 553 int ringerMode = getRingerMode(); 554 // do not vibrate if already in vibrate mode 555 if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { 556 flags &= ~AudioManager.FLAG_VIBRATE; 557 } 558 // Check if the ringer mode changes with this volume adjustment. If 559 // it does, it will handle adjusting the volume, so we won't below 560 adjustVolume = checkForRingerModeChange(oldIndex, direction, streamTypeAlias); 561 } 562 563 // If stream is muted, adjust last audible index only 564 int index; 565 if (streamState.muteCount() != 0) { 566 if (adjustVolume) { 567 // adjust volume on all stream types sharing the same alias otherwise a query 568 // on last audible index for an alias would not give the correct value 569 int numStreamTypes = AudioSystem.getNumStreamTypes(); 570 for (int i = numStreamTypes - 1; i >= 0; i--) { 571 if (STREAM_VOLUME_ALIAS[i] == streamTypeAlias) { 572 VolumeStreamState s = mStreamStates[i]; 573 574 s.adjustLastAudibleIndex(direction, device); 575 // Post a persist volume msg 576 sendMsg(mAudioHandler, 577 MSG_PERSIST_VOLUME, 578 SENDMSG_REPLACE, 579 PERSIST_LAST_AUDIBLE, 580 device, 581 s, 582 PERSIST_DELAY); 583 } 584 } 585 } 586 index = streamState.getIndex(device, true /* lastAudible */); 587 } else { 588 if (adjustVolume && streamState.adjustIndex(direction, device)) { 589 // Post message to set system volume (it in turn will post a message 590 // to persist). Do not change volume if stream is muted. 591 sendMsg(mAudioHandler, 592 MSG_SET_DEVICE_VOLUME, 593 SENDMSG_NOOP, 594 device, 595 0, 596 streamState, 597 0); 598 } 599 index = streamState.getIndex(device, false /* lastAudible */); 600 } 601 602 sendVolumeUpdate(streamType, oldIndex, index, flags); 603 } 604 605 /** @see AudioManager#setStreamVolume(int, int, int) */ 606 public void setStreamVolume(int streamType, int index, int flags) { 607 ensureValidStreamType(streamType); 608 VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]]; 609 610 final int device = getDeviceForStream(streamType); 611 // get last audible index if stream is muted, current index otherwise 612 final int oldIndex = streamState.getIndex(device, 613 (streamState.muteCount() != 0) /* lastAudible */); 614 615 // setting ring or notifications volume to 0 on voice capable devices enters silent mode 616 if (mVoiceCapable && (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || 617 (STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING))) { 618 int newRingerMode; 619 if (index == 0) { 620 newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1 621 ? AudioManager.RINGER_MODE_VIBRATE 622 : AudioManager.RINGER_MODE_SILENT; 623 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], 624 index, 625 device, 626 false, 627 true); 628 } else { 629 newRingerMode = AudioManager.RINGER_MODE_NORMAL; 630 } 631 setRingerMode(newRingerMode); 632 } 633 634 index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]); 635 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, device, false, true); 636 // get last audible index if stream is muted, current index otherwise 637 index = streamState.getIndex(device, 638 (streamState.muteCount() != 0) /* lastAudible */); 639 640 sendVolumeUpdate(streamType, oldIndex, index, flags); 641 } 642 643 // UI update and Broadcast Intent 644 private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) { 645 if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) { 646 streamType = AudioSystem.STREAM_NOTIFICATION; 647 } 648 649 mVolumePanel.postVolumeChanged(streamType, flags); 650 651 oldIndex = (oldIndex + 5) / 10; 652 index = (index + 5) / 10; 653 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION); 654 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType); 655 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index); 656 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex); 657 mContext.sendBroadcast(intent); 658 } 659 660 /** 661 * Sets the stream state's index, and posts a message to set system volume. 662 * This will not call out to the UI. Assumes a valid stream type. 663 * 664 * @param streamType Type of the stream 665 * @param index Desired volume index of the stream 666 * @param device the device whose volume must be changed 667 * @param force If true, set the volume even if the desired volume is same 668 * as the current volume. 669 * @param lastAudible If true, stores new index as last audible one 670 */ 671 private void setStreamVolumeInt(int streamType, 672 int index, 673 int device, 674 boolean force, 675 boolean lastAudible) { 676 VolumeStreamState streamState = mStreamStates[streamType]; 677 678 // If stream is muted, set last audible index only 679 if (streamState.muteCount() != 0) { 680 // Do not allow last audible index to be 0 681 if (index != 0) { 682 streamState.setLastAudibleIndex(index, device); 683 // Post a persist volume msg 684 sendMsg(mAudioHandler, 685 MSG_PERSIST_VOLUME, 686 SENDMSG_REPLACE, 687 PERSIST_LAST_AUDIBLE, 688 device, 689 streamState, 690 PERSIST_DELAY); 691 } 692 } else { 693 if (streamState.setIndex(index, device, lastAudible) || force) { 694 // Post message to set system volume (it in turn will post a message 695 // to persist). 696 sendMsg(mAudioHandler, 697 MSG_SET_DEVICE_VOLUME, 698 SENDMSG_NOOP, 699 device, 700 0, 701 streamState, 702 0); 703 } 704 } 705 } 706 707 /** @see AudioManager#setStreamSolo(int, boolean) */ 708 public void setStreamSolo(int streamType, boolean state, IBinder cb) { 709 for (int stream = 0; stream < mStreamStates.length; stream++) { 710 if (!isStreamAffectedByMute(stream) || stream == streamType) continue; 711 // Bring back last audible volume 712 mStreamStates[stream].mute(cb, state); 713 } 714 } 715 716 /** @see AudioManager#setStreamMute(int, boolean) */ 717 public void setStreamMute(int streamType, boolean state, IBinder cb) { 718 if (isStreamAffectedByMute(streamType)) { 719 mStreamStates[streamType].mute(cb, state); 720 } 721 } 722 723 /** get stream mute state. */ 724 public boolean isStreamMute(int streamType) { 725 return (mStreamStates[streamType].muteCount() != 0); 726 } 727 728 /** @see AudioManager#getStreamVolume(int) */ 729 public int getStreamVolume(int streamType) { 730 ensureValidStreamType(streamType); 731 int device = getDeviceForStream(streamType); 732 return (mStreamStates[streamType].getIndex(device, false /* lastAudible */) + 5) / 10; 733 } 734 735 /** @see AudioManager#getStreamMaxVolume(int) */ 736 public int getStreamMaxVolume(int streamType) { 737 ensureValidStreamType(streamType); 738 return (mStreamStates[streamType].getMaxIndex() + 5) / 10; 739 } 740 741 742 /** Get last audible volume before stream was muted. */ 743 public int getLastAudibleStreamVolume(int streamType) { 744 ensureValidStreamType(streamType); 745 int device = getDeviceForStream(streamType); 746 return (mStreamStates[streamType].getIndex(device, true /* lastAudible */) + 5) / 10; 747 } 748 749 /** @see AudioManager#getRingerMode() */ 750 public int getRingerMode() { 751 synchronized(mSettingsLock) { 752 return mRingerMode; 753 } 754 } 755 756 private void ensureValidRingerMode(int ringerMode) { 757 if (!AudioManager.isValidRingerMode(ringerMode)) { 758 throw new IllegalArgumentException("Bad ringer mode " + ringerMode); 759 } 760 } 761 762 /** @see AudioManager#setRingerMode(int) */ 763 public void setRingerMode(int ringerMode) { 764 ensureValidRingerMode(ringerMode); 765 if (ringerMode != getRingerMode()) { 766 setRingerModeInt(ringerMode, true); 767 // Send sticky broadcast 768 broadcastRingerMode(ringerMode); 769 } 770 } 771 772 private void setRingerModeInt(int ringerMode, boolean persist) { 773 synchronized(mSettingsLock) { 774 mRingerMode = ringerMode; 775 } 776 777 // Mute stream if not previously muted by ringer mode and ringer mode 778 // is not RINGER_MODE_NORMAL and stream is affected by ringer mode. 779 // Unmute stream if previously muted by ringer mode and ringer mode 780 // is RINGER_MODE_NORMAL or stream is not affected by ringer mode. 781 int numStreamTypes = AudioSystem.getNumStreamTypes(); 782 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 783 if (isStreamMutedByRingerMode(streamType)) { 784 if (!isStreamAffectedByRingerMode(streamType) || 785 ringerMode == AudioManager.RINGER_MODE_NORMAL) { 786 // ring and notifications volume should never be 0 when not silenced 787 // on voice capable devices 788 if (mVoiceCapable && 789 STREAM_VOLUME_ALIAS[streamType] == AudioSystem.STREAM_RING) { 790 791 Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet(); 792 Iterator i = set.iterator(); 793 while (i.hasNext()) { 794 Map.Entry entry = (Map.Entry)i.next(); 795 if ((Integer)entry.getValue() == 0) { 796 entry.setValue(10); 797 } 798 } 799 } 800 mStreamStates[streamType].mute(null, false); 801 mRingerModeMutedStreams &= ~(1 << streamType); 802 } 803 } else { 804 if (isStreamAffectedByRingerMode(streamType) && 805 ringerMode != AudioManager.RINGER_MODE_NORMAL) { 806 mStreamStates[streamType].mute(null, true); 807 mRingerModeMutedStreams |= (1 << streamType); 808 } 809 } 810 } 811 812 // Post a persist ringer mode msg 813 if (persist) { 814 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, 815 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY); 816 } 817 } 818 819 /** @see AudioManager#shouldVibrate(int) */ 820 public boolean shouldVibrate(int vibrateType) { 821 822 switch (getVibrateSetting(vibrateType)) { 823 824 case AudioManager.VIBRATE_SETTING_ON: 825 return getRingerMode() != AudioManager.RINGER_MODE_SILENT; 826 827 case AudioManager.VIBRATE_SETTING_ONLY_SILENT: 828 return getRingerMode() == AudioManager.RINGER_MODE_VIBRATE; 829 830 case AudioManager.VIBRATE_SETTING_OFF: 831 // return false, even for incoming calls 832 return false; 833 834 default: 835 return false; 836 } 837 } 838 839 /** @see AudioManager#getVibrateSetting(int) */ 840 public int getVibrateSetting(int vibrateType) { 841 return (mVibrateSetting >> (vibrateType * 2)) & 3; 842 } 843 844 /** @see AudioManager#setVibrateSetting(int, int) */ 845 public void setVibrateSetting(int vibrateType, int vibrateSetting) { 846 847 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting); 848 849 // Broadcast change 850 broadcastVibrateSetting(vibrateType); 851 852 // Post message to set ringer mode (it in turn will post a message 853 // to persist) 854 sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SENDMSG_NOOP, 0, 0, 855 null, 0); 856 } 857 858 /** 859 * @see #setVibrateSetting(int, int) 860 */ 861 public static int getValueForVibrateSetting(int existingValue, int vibrateType, 862 int vibrateSetting) { 863 864 // First clear the existing setting. Each vibrate type has two bits in 865 // the value. Note '3' is '11' in binary. 866 existingValue &= ~(3 << (vibrateType * 2)); 867 868 // Set into the old value 869 existingValue |= (vibrateSetting & 3) << (vibrateType * 2); 870 871 return existingValue; 872 } 873 874 private class SetModeDeathHandler implements IBinder.DeathRecipient { 875 private IBinder mCb; // To be notified of client's death 876 private int mPid; 877 private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client 878 879 SetModeDeathHandler(IBinder cb, int pid) { 880 mCb = cb; 881 mPid = pid; 882 } 883 884 public void binderDied() { 885 int newModeOwnerPid = 0; 886 synchronized(mSetModeDeathHandlers) { 887 Log.w(TAG, "setMode() client died"); 888 int index = mSetModeDeathHandlers.indexOf(this); 889 if (index < 0) { 890 Log.w(TAG, "unregistered setMode() client died"); 891 } else { 892 newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid); 893 } 894 } 895 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all 896 // SCO connections not started by the application changing the mode 897 if (newModeOwnerPid != 0) { 898 disconnectBluetoothSco(newModeOwnerPid); 899 } 900 } 901 902 public int getPid() { 903 return mPid; 904 } 905 906 public void setMode(int mode) { 907 mMode = mode; 908 } 909 910 public int getMode() { 911 return mMode; 912 } 913 914 public IBinder getBinder() { 915 return mCb; 916 } 917 } 918 919 /** @see AudioManager#setMode(int) */ 920 public void setMode(int mode, IBinder cb) { 921 if (!checkAudioSettingsPermission("setMode()")) { 922 return; 923 } 924 925 if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) { 926 return; 927 } 928 929 int newModeOwnerPid = 0; 930 synchronized(mSetModeDeathHandlers) { 931 if (mode == AudioSystem.MODE_CURRENT) { 932 mode = mMode; 933 } 934 newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid()); 935 } 936 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all 937 // SCO connections not started by the application changing the mode 938 if (newModeOwnerPid != 0) { 939 disconnectBluetoothSco(newModeOwnerPid); 940 } 941 } 942 943 // must be called synchronized on mSetModeDeathHandlers 944 // setModeInt() returns a valid PID if the audio mode was successfully set to 945 // any mode other than NORMAL. 946 int setModeInt(int mode, IBinder cb, int pid) { 947 int newModeOwnerPid = 0; 948 if (cb == null) { 949 Log.e(TAG, "setModeInt() called with null binder"); 950 return newModeOwnerPid; 951 } 952 953 SetModeDeathHandler hdlr = null; 954 Iterator iter = mSetModeDeathHandlers.iterator(); 955 while (iter.hasNext()) { 956 SetModeDeathHandler h = (SetModeDeathHandler)iter.next(); 957 if (h.getPid() == pid) { 958 hdlr = h; 959 // Remove from client list so that it is re-inserted at top of list 960 iter.remove(); 961 hdlr.getBinder().unlinkToDeath(hdlr, 0); 962 break; 963 } 964 } 965 int status = AudioSystem.AUDIO_STATUS_OK; 966 do { 967 if (mode == AudioSystem.MODE_NORMAL) { 968 // get new mode from client at top the list if any 969 if (!mSetModeDeathHandlers.isEmpty()) { 970 hdlr = mSetModeDeathHandlers.get(0); 971 cb = hdlr.getBinder(); 972 mode = hdlr.getMode(); 973 } 974 } else { 975 if (hdlr == null) { 976 hdlr = new SetModeDeathHandler(cb, pid); 977 } 978 // Register for client death notification 979 try { 980 cb.linkToDeath(hdlr, 0); 981 } catch (RemoteException e) { 982 // Client has died! 983 Log.w(TAG, "setMode() could not link to "+cb+" binder death"); 984 } 985 986 // Last client to call setMode() is always at top of client list 987 // as required by SetModeDeathHandler.binderDied() 988 mSetModeDeathHandlers.add(0, hdlr); 989 hdlr.setMode(mode); 990 } 991 992 if (mode != mMode) { 993 status = AudioSystem.setPhoneState(mode); 994 if (status == AudioSystem.AUDIO_STATUS_OK) { 995 mMode = mode; 996 } else { 997 if (hdlr != null) { 998 mSetModeDeathHandlers.remove(hdlr); 999 cb.unlinkToDeath(hdlr, 0); 1000 } 1001 // force reading new top of mSetModeDeathHandlers stack 1002 mode = AudioSystem.MODE_NORMAL; 1003 } 1004 } else { 1005 status = AudioSystem.AUDIO_STATUS_OK; 1006 } 1007 } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty()); 1008 1009 if (status == AudioSystem.AUDIO_STATUS_OK) { 1010 if (mode != AudioSystem.MODE_NORMAL) { 1011 if (mSetModeDeathHandlers.isEmpty()) { 1012 Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack"); 1013 } else { 1014 newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid(); 1015 } 1016 } 1017 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); 1018 int device = getDeviceForStream(streamType); 1019 int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].getIndex(device, false); 1020 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, device, true, false); 1021 } 1022 return newModeOwnerPid; 1023 } 1024 1025 /** @see AudioManager#getMode() */ 1026 public int getMode() { 1027 return mMode; 1028 } 1029 1030 /** @see AudioManager#playSoundEffect(int) */ 1031 public void playSoundEffect(int effectType) { 1032 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP, 1033 effectType, -1, null, 0); 1034 } 1035 1036 /** @see AudioManager#playSoundEffect(int, float) */ 1037 public void playSoundEffectVolume(int effectType, float volume) { 1038 loadSoundEffects(); 1039 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP, 1040 effectType, (int) (volume * 1000), null, 0); 1041 } 1042 1043 /** 1044 * Loads samples into the soundpool. 1045 * This method must be called at first when sound effects are enabled 1046 */ 1047 public boolean loadSoundEffects() { 1048 int status; 1049 1050 synchronized (mSoundEffectsLock) { 1051 if (!mBootCompleted) { 1052 Log.w(TAG, "loadSoundEffects() called before boot complete"); 1053 return false; 1054 } 1055 1056 if (mSoundPool != null) { 1057 return true; 1058 } 1059 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0); 1060 1061 try { 1062 mSoundPoolCallBack = null; 1063 mSoundPoolListenerThread = new SoundPoolListenerThread(); 1064 mSoundPoolListenerThread.start(); 1065 // Wait for mSoundPoolCallBack to be set by the other thread 1066 mSoundEffectsLock.wait(); 1067 } catch (InterruptedException e) { 1068 Log.w(TAG, "Interrupted while waiting sound pool listener thread."); 1069 } 1070 1071 if (mSoundPoolCallBack == null) { 1072 Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread"); 1073 if (mSoundPoolLooper != null) { 1074 mSoundPoolLooper.quit(); 1075 mSoundPoolLooper = null; 1076 } 1077 mSoundPoolListenerThread = null; 1078 mSoundPool.release(); 1079 mSoundPool = null; 1080 return false; 1081 } 1082 /* 1083 * poolId table: The value -1 in this table indicates that corresponding 1084 * file (same index in SOUND_EFFECT_FILES[] has not been loaded. 1085 * Once loaded, the value in poolId is the sample ID and the same 1086 * sample can be reused for another effect using the same file. 1087 */ 1088 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 1089 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 1090 poolId[fileIdx] = -1; 1091 } 1092 /* 1093 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded. 1094 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0: 1095 * this indicates we have a valid sample loaded for this effect. 1096 */ 1097 1098 int lastSample = 0; 1099 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 1100 // Do not load sample if this effect uses the MediaPlayer 1101 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) { 1102 continue; 1103 } 1104 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) { 1105 String filePath = Environment.getRootDirectory() 1106 + SOUND_EFFECTS_PATH 1107 + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]]; 1108 int sampleId = mSoundPool.load(filePath, 0); 1109 if (sampleId <= 0) { 1110 Log.w(TAG, "Soundpool could not load file: "+filePath); 1111 } else { 1112 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId; 1113 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId; 1114 lastSample = sampleId; 1115 } 1116 } else { 1117 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]]; 1118 } 1119 } 1120 // wait for all samples to be loaded 1121 if (lastSample != 0) { 1122 mSoundPoolCallBack.setLastSample(lastSample); 1123 1124 try { 1125 mSoundEffectsLock.wait(); 1126 status = mSoundPoolCallBack.status(); 1127 } catch (java.lang.InterruptedException e) { 1128 Log.w(TAG, "Interrupted while waiting sound pool callback."); 1129 status = -1; 1130 } 1131 } else { 1132 status = -1; 1133 } 1134 1135 if (mSoundPoolLooper != null) { 1136 mSoundPoolLooper.quit(); 1137 mSoundPoolLooper = null; 1138 } 1139 mSoundPoolListenerThread = null; 1140 if (status != 0) { 1141 Log.w(TAG, 1142 "loadSoundEffects(), Error " 1143 + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1) 1144 + " while loading samples"); 1145 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 1146 if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) { 1147 SOUND_EFFECT_FILES_MAP[effect][1] = -1; 1148 } 1149 } 1150 1151 mSoundPool.release(); 1152 mSoundPool = null; 1153 } 1154 } 1155 return (status == 0); 1156 } 1157 1158 /** 1159 * Unloads samples from the sound pool. 1160 * This method can be called to free some memory when 1161 * sound effects are disabled. 1162 */ 1163 public void unloadSoundEffects() { 1164 synchronized (mSoundEffectsLock) { 1165 if (mSoundPool == null) { 1166 return; 1167 } 1168 1169 mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS); 1170 mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT); 1171 1172 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 1173 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 1174 poolId[fileIdx] = 0; 1175 } 1176 1177 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 1178 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) { 1179 continue; 1180 } 1181 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) { 1182 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]); 1183 SOUND_EFFECT_FILES_MAP[effect][1] = -1; 1184 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1; 1185 } 1186 } 1187 mSoundPool.release(); 1188 mSoundPool = null; 1189 } 1190 } 1191 1192 class SoundPoolListenerThread extends Thread { 1193 public SoundPoolListenerThread() { 1194 super("SoundPoolListenerThread"); 1195 } 1196 1197 @Override 1198 public void run() { 1199 1200 Looper.prepare(); 1201 mSoundPoolLooper = Looper.myLooper(); 1202 1203 synchronized (mSoundEffectsLock) { 1204 if (mSoundPool != null) { 1205 mSoundPoolCallBack = new SoundPoolCallback(); 1206 mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack); 1207 } 1208 mSoundEffectsLock.notify(); 1209 } 1210 Looper.loop(); 1211 } 1212 } 1213 1214 private final class SoundPoolCallback implements 1215 android.media.SoundPool.OnLoadCompleteListener { 1216 1217 int mStatus; 1218 int mLastSample; 1219 1220 public int status() { 1221 return mStatus; 1222 } 1223 1224 public void setLastSample(int sample) { 1225 mLastSample = sample; 1226 } 1227 1228 public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { 1229 synchronized (mSoundEffectsLock) { 1230 if (status != 0) { 1231 mStatus = status; 1232 } 1233 if (sampleId == mLastSample) { 1234 mSoundEffectsLock.notify(); 1235 } 1236 } 1237 } 1238 } 1239 1240 /** @see AudioManager#reloadAudioSettings() */ 1241 public void reloadAudioSettings() { 1242 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings 1243 readPersistedSettings(); 1244 1245 // restore volume settings 1246 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1247 for (int streamType = 0; streamType < numStreamTypes; streamType++) { 1248 VolumeStreamState streamState = mStreamStates[streamType]; 1249 1250 streamState.readSettings(); 1251 1252 // unmute stream that was muted but is not affect by mute anymore 1253 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) { 1254 int size = streamState.mDeathHandlers.size(); 1255 for (int i = 0; i < size; i++) { 1256 streamState.mDeathHandlers.get(i).mMuteCount = 1; 1257 streamState.mDeathHandlers.get(i).mute(false); 1258 } 1259 } 1260 // apply stream volume 1261 if (streamState.muteCount() == 0) { 1262 streamState.applyAllVolumes(); 1263 } 1264 } 1265 1266 // apply new ringer mode 1267 setRingerModeInt(getRingerMode(), false); 1268 } 1269 1270 /** @see AudioManager#setSpeakerphoneOn() */ 1271 public void setSpeakerphoneOn(boolean on){ 1272 if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { 1273 return; 1274 } 1275 mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE; 1276 1277 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, 1278 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0); 1279 } 1280 1281 /** @see AudioManager#isSpeakerphoneOn() */ 1282 public boolean isSpeakerphoneOn() { 1283 return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER); 1284 } 1285 1286 /** @see AudioManager#setBluetoothScoOn() */ 1287 public void setBluetoothScoOn(boolean on){ 1288 if (!checkAudioSettingsPermission("setBluetoothScoOn()")) { 1289 return; 1290 } 1291 mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE; 1292 1293 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, 1294 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0); 1295 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, 1296 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0); 1297 } 1298 1299 /** @see AudioManager#isBluetoothScoOn() */ 1300 public boolean isBluetoothScoOn() { 1301 return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO); 1302 } 1303 1304 /** @see AudioManager#startBluetoothSco() */ 1305 public void startBluetoothSco(IBinder cb){ 1306 if (!checkAudioSettingsPermission("startBluetoothSco()") || 1307 !mBootCompleted) { 1308 return; 1309 } 1310 ScoClient client = getScoClient(cb, true); 1311 client.incCount(); 1312 } 1313 1314 /** @see AudioManager#stopBluetoothSco() */ 1315 public void stopBluetoothSco(IBinder cb){ 1316 if (!checkAudioSettingsPermission("stopBluetoothSco()") || 1317 !mBootCompleted) { 1318 return; 1319 } 1320 ScoClient client = getScoClient(cb, false); 1321 if (client != null) { 1322 client.decCount(); 1323 } 1324 } 1325 1326 private class ScoClient implements IBinder.DeathRecipient { 1327 private IBinder mCb; // To be notified of client's death 1328 private int mCreatorPid; 1329 private int mStartcount; // number of SCO connections started by this client 1330 1331 ScoClient(IBinder cb) { 1332 mCb = cb; 1333 mCreatorPid = Binder.getCallingPid(); 1334 mStartcount = 0; 1335 } 1336 1337 public void binderDied() { 1338 synchronized(mScoClients) { 1339 Log.w(TAG, "SCO client died"); 1340 int index = mScoClients.indexOf(this); 1341 if (index < 0) { 1342 Log.w(TAG, "unregistered SCO client died"); 1343 } else { 1344 clearCount(true); 1345 mScoClients.remove(this); 1346 } 1347 } 1348 } 1349 1350 public void incCount() { 1351 synchronized(mScoClients) { 1352 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED); 1353 if (mStartcount == 0) { 1354 try { 1355 mCb.linkToDeath(this, 0); 1356 } catch (RemoteException e) { 1357 // client has already died! 1358 Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death"); 1359 } 1360 } 1361 mStartcount++; 1362 } 1363 } 1364 1365 public void decCount() { 1366 synchronized(mScoClients) { 1367 if (mStartcount == 0) { 1368 Log.w(TAG, "ScoClient.decCount() already 0"); 1369 } else { 1370 mStartcount--; 1371 if (mStartcount == 0) { 1372 try { 1373 mCb.unlinkToDeath(this, 0); 1374 } catch (NoSuchElementException e) { 1375 Log.w(TAG, "decCount() going to 0 but not registered to binder"); 1376 } 1377 } 1378 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1379 } 1380 } 1381 } 1382 1383 public void clearCount(boolean stopSco) { 1384 synchronized(mScoClients) { 1385 if (mStartcount != 0) { 1386 try { 1387 mCb.unlinkToDeath(this, 0); 1388 } catch (NoSuchElementException e) { 1389 Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder"); 1390 } 1391 } 1392 mStartcount = 0; 1393 if (stopSco) { 1394 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1395 } 1396 } 1397 } 1398 1399 public int getCount() { 1400 return mStartcount; 1401 } 1402 1403 public IBinder getBinder() { 1404 return mCb; 1405 } 1406 1407 public int getPid() { 1408 return mCreatorPid; 1409 } 1410 1411 public int totalCount() { 1412 synchronized(mScoClients) { 1413 int count = 0; 1414 int size = mScoClients.size(); 1415 for (int i = 0; i < size; i++) { 1416 count += mScoClients.get(i).getCount(); 1417 } 1418 return count; 1419 } 1420 } 1421 1422 private void requestScoState(int state) { 1423 checkScoAudioState(); 1424 if (totalCount() == 0) { 1425 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 1426 // Make sure that the state transitions to CONNECTING even if we cannot initiate 1427 // the connection. 1428 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); 1429 // Accept SCO audio activation only in NORMAL audio mode or if the mode is 1430 // currently controlled by the same client process. 1431 synchronized(mSetModeDeathHandlers) { 1432 if ((mSetModeDeathHandlers.isEmpty() || 1433 mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) && 1434 (mScoAudioState == SCO_STATE_INACTIVE || 1435 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) { 1436 if (mScoAudioState == SCO_STATE_INACTIVE) { 1437 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) { 1438 if (mBluetoothHeadset.startScoUsingVirtualVoiceCall( 1439 mBluetoothHeadsetDevice)) { 1440 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1441 } else { 1442 broadcastScoConnectionState( 1443 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1444 } 1445 } else if (getBluetoothHeadset()) { 1446 mScoAudioState = SCO_STATE_ACTIVATE_REQ; 1447 } 1448 } else { 1449 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1450 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); 1451 } 1452 } else { 1453 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1454 } 1455 } 1456 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED && 1457 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL || 1458 mScoAudioState == SCO_STATE_ACTIVATE_REQ)) { 1459 if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) { 1460 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) { 1461 if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall( 1462 mBluetoothHeadsetDevice)) { 1463 mScoAudioState = SCO_STATE_INACTIVE; 1464 broadcastScoConnectionState( 1465 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1466 } 1467 } else if (getBluetoothHeadset()) { 1468 mScoAudioState = SCO_STATE_DEACTIVATE_REQ; 1469 } 1470 } else { 1471 mScoAudioState = SCO_STATE_INACTIVE; 1472 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1473 } 1474 } 1475 } 1476 } 1477 } 1478 1479 private void checkScoAudioState() { 1480 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null && 1481 mScoAudioState == SCO_STATE_INACTIVE && 1482 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) 1483 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1484 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 1485 } 1486 } 1487 1488 private ScoClient getScoClient(IBinder cb, boolean create) { 1489 synchronized(mScoClients) { 1490 ScoClient client = null; 1491 int size = mScoClients.size(); 1492 for (int i = 0; i < size; i++) { 1493 client = mScoClients.get(i); 1494 if (client.getBinder() == cb) 1495 return client; 1496 } 1497 if (create) { 1498 client = new ScoClient(cb); 1499 mScoClients.add(client); 1500 } 1501 return client; 1502 } 1503 } 1504 1505 public void clearAllScoClients(int exceptPid, boolean stopSco) { 1506 synchronized(mScoClients) { 1507 ScoClient savedClient = null; 1508 int size = mScoClients.size(); 1509 for (int i = 0; i < size; i++) { 1510 ScoClient cl = mScoClients.get(i); 1511 if (cl.getPid() != exceptPid) { 1512 cl.clearCount(stopSco); 1513 } else { 1514 savedClient = cl; 1515 } 1516 } 1517 mScoClients.clear(); 1518 if (savedClient != null) { 1519 mScoClients.add(savedClient); 1520 } 1521 } 1522 } 1523 1524 private boolean getBluetoothHeadset() { 1525 boolean result = false; 1526 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 1527 if (adapter != null) { 1528 result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, 1529 BluetoothProfile.HEADSET); 1530 } 1531 // If we could not get a bluetooth headset proxy, send a failure message 1532 // without delay to reset the SCO audio state and clear SCO clients. 1533 // If we could get a proxy, send a delayed failure message that will reset our state 1534 // in case we don't receive onServiceConnected(). 1535 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 1536 SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0); 1537 return result; 1538 } 1539 1540 private void disconnectBluetoothSco(int exceptPid) { 1541 synchronized(mScoClients) { 1542 checkScoAudioState(); 1543 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL || 1544 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) { 1545 if (mBluetoothHeadsetDevice != null) { 1546 if (mBluetoothHeadset != null) { 1547 if (!mBluetoothHeadset.stopVoiceRecognition( 1548 mBluetoothHeadsetDevice)) { 1549 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 1550 SENDMSG_REPLACE, 0, 0, null, 0); 1551 } 1552 } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL && 1553 getBluetoothHeadset()) { 1554 mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ; 1555 } 1556 } 1557 } else { 1558 clearAllScoClients(exceptPid, true); 1559 } 1560 } 1561 } 1562 1563 private void resetBluetoothSco() { 1564 synchronized(mScoClients) { 1565 clearAllScoClients(0, false); 1566 mScoAudioState = SCO_STATE_INACTIVE; 1567 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1568 } 1569 } 1570 1571 private void broadcastScoConnectionState(int state) { 1572 if (state != mScoConnectionState) { 1573 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); 1574 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); 1575 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, 1576 mScoConnectionState); 1577 mContext.sendStickyBroadcast(newIntent); 1578 mScoConnectionState = state; 1579 } 1580 } 1581 1582 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = 1583 new BluetoothProfile.ServiceListener() { 1584 public void onServiceConnected(int profile, BluetoothProfile proxy) { 1585 BluetoothDevice btDevice; 1586 List<BluetoothDevice> deviceList; 1587 switch(profile) { 1588 case BluetoothProfile.A2DP: 1589 BluetoothA2dp a2dp = (BluetoothA2dp) proxy; 1590 deviceList = a2dp.getConnectedDevices(); 1591 if (deviceList.size() > 0) { 1592 btDevice = deviceList.get(0); 1593 handleA2dpConnectionStateChange(btDevice, a2dp.getConnectionState(btDevice)); 1594 } 1595 break; 1596 1597 case BluetoothProfile.HEADSET: 1598 synchronized (mScoClients) { 1599 // Discard timeout message 1600 mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED); 1601 mBluetoothHeadset = (BluetoothHeadset) proxy; 1602 deviceList = mBluetoothHeadset.getConnectedDevices(); 1603 if (deviceList.size() > 0) { 1604 mBluetoothHeadsetDevice = deviceList.get(0); 1605 } else { 1606 mBluetoothHeadsetDevice = null; 1607 } 1608 // Refresh SCO audio state 1609 checkScoAudioState(); 1610 // Continue pending action if any 1611 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ || 1612 mScoAudioState == SCO_STATE_DEACTIVATE_REQ || 1613 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) { 1614 boolean status = false; 1615 if (mBluetoothHeadsetDevice != null) { 1616 switch (mScoAudioState) { 1617 case SCO_STATE_ACTIVATE_REQ: 1618 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1619 status = mBluetoothHeadset.startScoUsingVirtualVoiceCall( 1620 mBluetoothHeadsetDevice); 1621 break; 1622 case SCO_STATE_DEACTIVATE_REQ: 1623 status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall( 1624 mBluetoothHeadsetDevice); 1625 break; 1626 case SCO_STATE_DEACTIVATE_EXT_REQ: 1627 status = mBluetoothHeadset.stopVoiceRecognition( 1628 mBluetoothHeadsetDevice); 1629 } 1630 } 1631 if (!status) { 1632 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 1633 SENDMSG_REPLACE, 0, 0, null, 0); 1634 } 1635 } 1636 } 1637 break; 1638 1639 default: 1640 break; 1641 } 1642 } 1643 public void onServiceDisconnected(int profile) { 1644 switch(profile) { 1645 case BluetoothProfile.A2DP: 1646 synchronized (mConnectedDevices) { 1647 if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) { 1648 makeA2dpDeviceUnavailableNow( 1649 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); 1650 } 1651 } 1652 break; 1653 1654 case BluetoothProfile.HEADSET: 1655 synchronized (mScoClients) { 1656 mBluetoothHeadset = null; 1657 } 1658 break; 1659 1660 default: 1661 break; 1662 } 1663 } 1664 }; 1665 1666 /////////////////////////////////////////////////////////////////////////// 1667 // Internal methods 1668 /////////////////////////////////////////////////////////////////////////// 1669 1670 /** 1671 * Checks if the adjustment should change ringer mode instead of just 1672 * adjusting volume. If so, this will set the proper ringer mode and volume 1673 * indices on the stream states. 1674 */ 1675 private boolean checkForRingerModeChange(int oldIndex, int direction, int streamType) { 1676 boolean adjustVolumeIndex = true; 1677 int ringerMode = getRingerMode(); 1678 int newRingerMode = ringerMode; 1679 int uiIndex = (oldIndex + 5) / 10; 1680 boolean vibeInSilent = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1; 1681 1682 if (ringerMode == RINGER_MODE_NORMAL) { 1683 if ((direction == AudioManager.ADJUST_LOWER) && (uiIndex <= 1)) { 1684 // enter silent mode if current index is the last audible one and not repeating a 1685 // volume key down 1686 if (vibeInSilent || mPrevVolDirection != AudioManager.ADJUST_LOWER) { 1687 // "silent mode", but which one? 1688 newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_SILENT; 1689 } 1690 if (uiIndex == 0 || 1691 (!vibeInSilent && 1692 mPrevVolDirection == AudioManager.ADJUST_LOWER && 1693 mVoiceCapable && streamType == AudioSystem.STREAM_RING)) { 1694 adjustVolumeIndex = false; 1695 } 1696 } 1697 } else if (ringerMode == RINGER_MODE_VIBRATE) { 1698 if ((direction == AudioManager.ADJUST_LOWER)) { 1699 // Set it to silent, if it wasn't a long-press 1700 if (mPrevVolDirection != AudioManager.ADJUST_LOWER) { 1701 newRingerMode = RINGER_MODE_SILENT; 1702 } 1703 } else if (direction == AudioManager.ADJUST_RAISE) { 1704 newRingerMode = RINGER_MODE_NORMAL; 1705 } 1706 adjustVolumeIndex = false; 1707 } else { 1708 if (direction == AudioManager.ADJUST_RAISE) { 1709 // exiting silent mode 1710 // If VIBRATE_IN_SILENT, then go into vibrate mode 1711 newRingerMode = vibeInSilent ? RINGER_MODE_VIBRATE : RINGER_MODE_NORMAL; 1712 } 1713 adjustVolumeIndex = false; 1714 } 1715 1716 setRingerMode(newRingerMode); 1717 1718 mPrevVolDirection = direction; 1719 1720 return adjustVolumeIndex; 1721 } 1722 1723 public boolean isStreamAffectedByRingerMode(int streamType) { 1724 return (mRingerModeAffectedStreams & (1 << streamType)) != 0; 1725 } 1726 1727 private boolean isStreamMutedByRingerMode(int streamType) { 1728 return (mRingerModeMutedStreams & (1 << streamType)) != 0; 1729 } 1730 1731 public boolean isStreamAffectedByMute(int streamType) { 1732 return (mMuteAffectedStreams & (1 << streamType)) != 0; 1733 } 1734 1735 private void ensureValidDirection(int direction) { 1736 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) { 1737 throw new IllegalArgumentException("Bad direction " + direction); 1738 } 1739 } 1740 1741 private void ensureValidStreamType(int streamType) { 1742 if (streamType < 0 || streamType >= mStreamStates.length) { 1743 throw new IllegalArgumentException("Bad stream type " + streamType); 1744 } 1745 } 1746 1747 private int getActiveStreamType(int suggestedStreamType) { 1748 1749 if (mVoiceCapable) { 1750 boolean isOffhook = false; 1751 try { 1752 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 1753 if (phone != null) isOffhook = phone.isOffhook(); 1754 } catch (RemoteException e) { 1755 Log.w(TAG, "Couldn't connect to phone service", e); 1756 } 1757 1758 if (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION) { 1759 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) 1760 == AudioSystem.FORCE_BT_SCO) { 1761 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); 1762 return AudioSystem.STREAM_BLUETOOTH_SCO; 1763 } else { 1764 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); 1765 return AudioSystem.STREAM_VOICE_CALL; 1766 } 1767 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) { 1768 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC..."); 1769 return AudioSystem.STREAM_MUSIC; 1770 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { 1771 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..." 1772 // + " b/c USE_DEFAULT_STREAM_TYPE..."); 1773 return AudioSystem.STREAM_RING; 1774 } else { 1775 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType); 1776 return suggestedStreamType; 1777 } 1778 } else { 1779 if (getMode() == AudioManager.MODE_IN_COMMUNICATION) { 1780 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) 1781 == AudioSystem.FORCE_BT_SCO) { 1782 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); 1783 return AudioSystem.STREAM_BLUETOOTH_SCO; 1784 } else { 1785 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); 1786 return AudioSystem.STREAM_VOICE_CALL; 1787 } 1788 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION, 1789 NOTIFICATION_VOLUME_DELAY_MS) || 1790 AudioSystem.isStreamActive(AudioSystem.STREAM_RING, 1791 NOTIFICATION_VOLUME_DELAY_MS)) { 1792 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION..."); 1793 return AudioSystem.STREAM_NOTIFICATION; 1794 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) || 1795 (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) { 1796 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC " 1797 // + " b/c USE_DEFAULT_STREAM_TYPE..."); 1798 return AudioSystem.STREAM_MUSIC; 1799 } else { 1800 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType); 1801 return suggestedStreamType; 1802 } 1803 } 1804 } 1805 1806 private void broadcastRingerMode(int ringerMode) { 1807 // Send sticky broadcast 1808 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION); 1809 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode); 1810 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1811 | Intent.FLAG_RECEIVER_REPLACE_PENDING); 1812 long origCallerIdentityToken = Binder.clearCallingIdentity(); 1813 mContext.sendStickyBroadcast(broadcast); 1814 Binder.restoreCallingIdentity(origCallerIdentityToken); 1815 } 1816 1817 private void broadcastVibrateSetting(int vibrateType) { 1818 // Send broadcast 1819 if (ActivityManagerNative.isSystemReady()) { 1820 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); 1821 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); 1822 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); 1823 mContext.sendBroadcast(broadcast); 1824 } 1825 } 1826 1827 // Message helper methods 1828 1829 private static void sendMsg(Handler handler, int msg, 1830 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) { 1831 1832 if (existingMsgPolicy == SENDMSG_REPLACE) { 1833 handler.removeMessages(msg); 1834 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) { 1835 return; 1836 } 1837 1838 handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay); 1839 } 1840 1841 boolean checkAudioSettingsPermission(String method) { 1842 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS") 1843 == PackageManager.PERMISSION_GRANTED) { 1844 return true; 1845 } 1846 String msg = "Audio Settings Permission Denial: " + method + " from pid=" 1847 + Binder.getCallingPid() 1848 + ", uid=" + Binder.getCallingUid(); 1849 Log.w(TAG, msg); 1850 return false; 1851 } 1852 1853 private int getDeviceForStream(int stream) { 1854 int device = AudioSystem.getDevicesForStream(stream); 1855 if ((device & (device - 1)) != 0) { 1856 // Multiple device selection is either: 1857 // - speaker + one other device: give priority to speaker in this case. 1858 // - one A2DP device + another device: happens with duplicated output. In this case 1859 // retain the device on the A2DP output as the other must not correspond to an active 1860 // selection if not the speaker. 1861 if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) { 1862 device = AudioSystem.DEVICE_OUT_SPEAKER; 1863 } else { 1864 device &= AudioSystem.DEVICE_OUT_ALL_A2DP; 1865 } 1866 } 1867 return device; 1868 } 1869 1870 /////////////////////////////////////////////////////////////////////////// 1871 // Inner classes 1872 /////////////////////////////////////////////////////////////////////////// 1873 1874 public class VolumeStreamState { 1875 private final int mStreamType; 1876 1877 private String mVolumeIndexSettingName; 1878 private String mLastAudibleVolumeIndexSettingName; 1879 private int mIndexMax; 1880 private final HashMap <Integer, Integer> mIndex = new HashMap <Integer, Integer>(); 1881 private final HashMap <Integer, Integer> mLastAudibleIndex = 1882 new HashMap <Integer, Integer>(); 1883 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death 1884 1885 private VolumeStreamState(String settingName, int streamType) { 1886 1887 mVolumeIndexSettingName = settingName; 1888 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; 1889 1890 mStreamType = streamType; 1891 mIndexMax = MAX_STREAM_VOLUME[streamType]; 1892 AudioSystem.initStreamVolume(streamType, 0, mIndexMax); 1893 mIndexMax *= 10; 1894 1895 readSettings(); 1896 1897 applyAllVolumes(); 1898 1899 mDeathHandlers = new ArrayList<VolumeDeathHandler>(); 1900 } 1901 1902 public String getSettingNameForDevice(boolean lastAudible, int device) { 1903 String name = lastAudible ? 1904 mLastAudibleVolumeIndexSettingName : 1905 mVolumeIndexSettingName; 1906 String suffix = AudioSystem.getDeviceName(device); 1907 if (suffix.isEmpty()) { 1908 return name; 1909 } 1910 return name + "_" + suffix; 1911 } 1912 1913 public void readSettings() { 1914 int index = Settings.System.getInt(mContentResolver, 1915 mVolumeIndexSettingName, 1916 AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]); 1917 1918 mIndex.clear(); 1919 mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index); 1920 1921 index = Settings.System.getInt(mContentResolver, 1922 mLastAudibleVolumeIndexSettingName, 1923 (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]); 1924 mLastAudibleIndex.clear(); 1925 mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index); 1926 1927 int remainingDevices = AudioSystem.DEVICE_OUT_ALL; 1928 for (int i = 0; remainingDevices != 0; i++) { 1929 int device = (1 << i); 1930 if ((device & remainingDevices) == 0) { 1931 continue; 1932 } 1933 remainingDevices &= ~device; 1934 1935 // retrieve current volume for device 1936 String name = getSettingNameForDevice(false, device); 1937 index = Settings.System.getInt(mContentResolver, name, -1); 1938 if (index == -1) { 1939 continue; 1940 } 1941 mIndex.put(device, getValidIndex(10 * index)); 1942 1943 // retrieve last audible volume for device 1944 name = getSettingNameForDevice(true, device); 1945 index = Settings.System.getInt(mContentResolver, name, -1); 1946 mLastAudibleIndex.put(device, getValidIndex(10 * index)); 1947 } 1948 } 1949 1950 public void applyDeviceVolume(int device) { 1951 AudioSystem.setStreamVolumeIndex(mStreamType, 1952 (getIndex(device, false /* lastAudible */) + 5)/10, 1953 device); 1954 } 1955 1956 public void applyAllVolumes() { 1957 // apply default volume first: by convention this will reset all 1958 // devices volumes in audio policy manager to the supplied value 1959 AudioSystem.setStreamVolumeIndex(mStreamType, 1960 (getIndex(AudioSystem.DEVICE_OUT_DEFAULT, false /* lastAudible */) + 5)/10, 1961 AudioSystem.DEVICE_OUT_DEFAULT); 1962 // then apply device specific volumes 1963 Set set = mIndex.entrySet(); 1964 Iterator i = set.iterator(); 1965 while (i.hasNext()) { 1966 Map.Entry entry = (Map.Entry)i.next(); 1967 int device = ((Integer)entry.getKey()).intValue(); 1968 if (device != AudioSystem.DEVICE_OUT_DEFAULT) { 1969 AudioSystem.setStreamVolumeIndex(mStreamType, 1970 ((Integer)entry.getValue() + 5)/10, 1971 device); 1972 } 1973 } 1974 } 1975 1976 public boolean adjustIndex(int deltaIndex, int device) { 1977 return setIndex(getIndex(device, 1978 false /* lastAudible */) + deltaIndex * 10, 1979 device, 1980 true /* lastAudible */); 1981 } 1982 1983 public boolean setIndex(int index, int device, boolean lastAudible) { 1984 int oldIndex = getIndex(device, false /* lastAudible */); 1985 index = getValidIndex(index); 1986 mIndex.put(device, getValidIndex(index)); 1987 1988 if (oldIndex != index) { 1989 if (lastAudible) { 1990 mLastAudibleIndex.put(device, index); 1991 } 1992 // Apply change to all streams using this one as alias 1993 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1994 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1995 if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) { 1996 mStreamStates[streamType].setIndex(rescaleIndex(index, 1997 mStreamType, 1998 streamType), 1999 device, 2000 lastAudible); 2001 } 2002 } 2003 return true; 2004 } else { 2005 return false; 2006 } 2007 } 2008 2009 public int getIndex(int device, boolean lastAudible) { 2010 HashMap <Integer, Integer> indexes; 2011 if (lastAudible) { 2012 indexes = mLastAudibleIndex; 2013 } else { 2014 indexes = mIndex; 2015 } 2016 Integer index = indexes.get(device); 2017 if (index == null) { 2018 // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT 2019 index = indexes.get(AudioSystem.DEVICE_OUT_DEFAULT); 2020 } 2021 return index.intValue(); 2022 } 2023 2024 public void setLastAudibleIndex(int index, int device) { 2025 mLastAudibleIndex.put(device, getValidIndex(index)); 2026 } 2027 2028 public void adjustLastAudibleIndex(int deltaIndex, int device) { 2029 setLastAudibleIndex(getIndex(device, 2030 true /* lastAudible */) + deltaIndex * 10, 2031 device); 2032 } 2033 2034 public int getMaxIndex() { 2035 return mIndexMax; 2036 } 2037 2038 public void mute(IBinder cb, boolean state) { 2039 VolumeDeathHandler handler = getDeathHandler(cb, state); 2040 if (handler == null) { 2041 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType); 2042 return; 2043 } 2044 handler.mute(state); 2045 } 2046 2047 private int getValidIndex(int index) { 2048 if (index < 0) { 2049 return 0; 2050 } else if (index > mIndexMax) { 2051 return mIndexMax; 2052 } 2053 2054 return index; 2055 } 2056 2057 private class VolumeDeathHandler implements IBinder.DeathRecipient { 2058 private IBinder mICallback; // To be notified of client's death 2059 private int mMuteCount; // Number of active mutes for this client 2060 2061 VolumeDeathHandler(IBinder cb) { 2062 mICallback = cb; 2063 } 2064 2065 public void mute(boolean state) { 2066 synchronized(mDeathHandlers) { 2067 if (state) { 2068 if (mMuteCount == 0) { 2069 // Register for client death notification 2070 try { 2071 // mICallback can be 0 if muted by AudioService 2072 if (mICallback != null) { 2073 mICallback.linkToDeath(this, 0); 2074 } 2075 mDeathHandlers.add(this); 2076 // If the stream is not yet muted by any client, set level to 0 2077 if (muteCount() == 0) { 2078 Set set = mIndex.entrySet(); 2079 Iterator i = set.iterator(); 2080 while (i.hasNext()) { 2081 Map.Entry entry = (Map.Entry)i.next(); 2082 int device = ((Integer)entry.getKey()).intValue(); 2083 setIndex(0, device, false /* lastAudible */); 2084 } 2085 sendMsg(mAudioHandler, 2086 MSG_SET_ALL_VOLUMES, 2087 SENDMSG_NOOP, 2088 0, 2089 0, 2090 VolumeStreamState.this, 0); 2091 } 2092 } catch (RemoteException e) { 2093 // Client has died! 2094 binderDied(); 2095 mDeathHandlers.notify(); 2096 return; 2097 } 2098 } else { 2099 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client"); 2100 } 2101 mMuteCount++; 2102 } else { 2103 if (mMuteCount == 0) { 2104 Log.e(TAG, "unexpected unmute for stream: "+mStreamType); 2105 } else { 2106 mMuteCount--; 2107 if (mMuteCount == 0) { 2108 // Unregistr from client death notification 2109 mDeathHandlers.remove(this); 2110 // mICallback can be 0 if muted by AudioService 2111 if (mICallback != null) { 2112 mICallback.unlinkToDeath(this, 0); 2113 } 2114 if (muteCount() == 0) { 2115 // If the stream is not muted any more, restore it's volume if 2116 // ringer mode allows it 2117 if (!isStreamAffectedByRingerMode(mStreamType) || 2118 mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 2119 Set set = mIndex.entrySet(); 2120 Iterator i = set.iterator(); 2121 while (i.hasNext()) { 2122 Map.Entry entry = (Map.Entry)i.next(); 2123 int device = ((Integer)entry.getKey()).intValue(); 2124 setIndex(getIndex(device, 2125 true /* lastAudible */), 2126 device, 2127 false /* lastAudible */); 2128 } 2129 sendMsg(mAudioHandler, 2130 MSG_SET_ALL_VOLUMES, 2131 SENDMSG_NOOP, 2132 0, 2133 0, 2134 VolumeStreamState.this, 0); 2135 } 2136 } 2137 } 2138 } 2139 } 2140 mDeathHandlers.notify(); 2141 } 2142 } 2143 2144 public void binderDied() { 2145 Log.w(TAG, "Volume service client died for stream: "+mStreamType); 2146 if (mMuteCount != 0) { 2147 // Reset all active mute requests from this client. 2148 mMuteCount = 1; 2149 mute(false); 2150 } 2151 } 2152 } 2153 2154 private int muteCount() { 2155 int count = 0; 2156 int size = mDeathHandlers.size(); 2157 for (int i = 0; i < size; i++) { 2158 count += mDeathHandlers.get(i).mMuteCount; 2159 } 2160 return count; 2161 } 2162 2163 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) { 2164 synchronized(mDeathHandlers) { 2165 VolumeDeathHandler handler; 2166 int size = mDeathHandlers.size(); 2167 for (int i = 0; i < size; i++) { 2168 handler = mDeathHandlers.get(i); 2169 if (cb == handler.mICallback) { 2170 return handler; 2171 } 2172 } 2173 // If this is the first mute request for this client, create a new 2174 // client death handler. Otherwise, it is an out of sequence unmute request. 2175 if (state) { 2176 handler = new VolumeDeathHandler(cb); 2177 } else { 2178 Log.w(TAG, "stream was not muted by this client"); 2179 handler = null; 2180 } 2181 return handler; 2182 } 2183 } 2184 } 2185 2186 /** Thread that handles native AudioSystem control. */ 2187 private class AudioSystemThread extends Thread { 2188 AudioSystemThread() { 2189 super("AudioService"); 2190 } 2191 2192 @Override 2193 public void run() { 2194 // Set this thread up so the handler will work on it 2195 Looper.prepare(); 2196 2197 synchronized(AudioService.this) { 2198 mAudioHandler = new AudioHandler(); 2199 2200 // Notify that the handler has been created 2201 AudioService.this.notify(); 2202 } 2203 2204 // Listen for volume change requests that are set by VolumePanel 2205 Looper.loop(); 2206 } 2207 } 2208 2209 /** Handles internal volume messages in separate volume thread. */ 2210 private class AudioHandler extends Handler { 2211 2212 private void setDeviceVolume(VolumeStreamState streamState, int device) { 2213 2214 // Apply volume 2215 streamState.applyDeviceVolume(device); 2216 2217 // Apply change to all streams using this one as alias 2218 int numStreamTypes = AudioSystem.getNumStreamTypes(); 2219 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 2220 if (streamType != streamState.mStreamType && 2221 STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) { 2222 mStreamStates[streamType].applyDeviceVolume(device); 2223 } 2224 } 2225 2226 // Post a persist volume msg 2227 sendMsg(mAudioHandler, 2228 MSG_PERSIST_VOLUME, 2229 SENDMSG_REPLACE, 2230 PERSIST_CURRENT|PERSIST_LAST_AUDIBLE, 2231 device, 2232 streamState, 2233 PERSIST_DELAY); 2234 2235 } 2236 2237 private void setAllVolumes(VolumeStreamState streamState) { 2238 2239 // Apply volume 2240 streamState.applyAllVolumes(); 2241 2242 // Apply change to all streams using this one as alias 2243 int numStreamTypes = AudioSystem.getNumStreamTypes(); 2244 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 2245 if (streamType != streamState.mStreamType && 2246 STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) { 2247 mStreamStates[streamType].applyAllVolumes(); 2248 } 2249 } 2250 } 2251 2252 private void persistVolume(VolumeStreamState streamState, 2253 int persistType, 2254 int device) { 2255 if ((persistType & PERSIST_CURRENT) != 0) { 2256 System.putInt(mContentResolver, 2257 streamState.getSettingNameForDevice(false /* lastAudible */, device), 2258 (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10); 2259 } 2260 if ((persistType & PERSIST_LAST_AUDIBLE) != 0) { 2261 System.putInt(mContentResolver, 2262 streamState.getSettingNameForDevice(true /* lastAudible */, device), 2263 (streamState.getIndex(device, true /* lastAudible */) + 5) / 10); 2264 } 2265 } 2266 2267 private void persistRingerMode(int ringerMode) { 2268 System.putInt(mContentResolver, System.MODE_RINGER, ringerMode); 2269 } 2270 2271 private void persistVibrateSetting() { 2272 System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting); 2273 } 2274 2275 private void playSoundEffect(int effectType, int volume) { 2276 synchronized (mSoundEffectsLock) { 2277 if (mSoundPool == null) { 2278 return; 2279 } 2280 float volFloat; 2281 // use default if volume is not specified by caller 2282 if (volume < 0) { 2283 volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20); 2284 } else { 2285 volFloat = (float) volume / 1000.0f; 2286 } 2287 2288 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) { 2289 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f); 2290 } else { 2291 MediaPlayer mediaPlayer = new MediaPlayer(); 2292 try { 2293 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]]; 2294 mediaPlayer.setDataSource(filePath); 2295 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM); 2296 mediaPlayer.prepare(); 2297 mediaPlayer.setVolume(volFloat, volFloat); 2298 mediaPlayer.setOnCompletionListener(new OnCompletionListener() { 2299 public void onCompletion(MediaPlayer mp) { 2300 cleanupPlayer(mp); 2301 } 2302 }); 2303 mediaPlayer.setOnErrorListener(new OnErrorListener() { 2304 public boolean onError(MediaPlayer mp, int what, int extra) { 2305 cleanupPlayer(mp); 2306 return true; 2307 } 2308 }); 2309 mediaPlayer.start(); 2310 } catch (IOException ex) { 2311 Log.w(TAG, "MediaPlayer IOException: "+ex); 2312 } catch (IllegalArgumentException ex) { 2313 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex); 2314 } catch (IllegalStateException ex) { 2315 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 2316 } 2317 } 2318 } 2319 } 2320 2321 private void onHandlePersistMediaButtonReceiver(ComponentName receiver) { 2322 Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER, 2323 receiver == null ? "" : receiver.flattenToString()); 2324 } 2325 2326 private void cleanupPlayer(MediaPlayer mp) { 2327 if (mp != null) { 2328 try { 2329 mp.stop(); 2330 mp.release(); 2331 } catch (IllegalStateException ex) { 2332 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 2333 } 2334 } 2335 } 2336 2337 private void setForceUse(int usage, int config) { 2338 AudioSystem.setForceUse(usage, config); 2339 } 2340 2341 @Override 2342 public void handleMessage(Message msg) { 2343 2344 switch (msg.what) { 2345 2346 case MSG_SET_DEVICE_VOLUME: 2347 setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1); 2348 break; 2349 2350 case MSG_SET_ALL_VOLUMES: 2351 setAllVolumes((VolumeStreamState) msg.obj); 2352 break; 2353 2354 case MSG_PERSIST_VOLUME: 2355 persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2); 2356 break; 2357 2358 case MSG_PERSIST_RINGER_MODE: 2359 // note that the value persisted is the current ringer mode, not the 2360 // value of ringer mode as of the time the request was made to persist 2361 persistRingerMode(getRingerMode()); 2362 break; 2363 2364 case MSG_PERSIST_VIBRATE_SETTING: 2365 persistVibrateSetting(); 2366 break; 2367 2368 case MSG_MEDIA_SERVER_DIED: 2369 if (!mMediaServerOk) { 2370 Log.e(TAG, "Media server died."); 2371 // Force creation of new IAudioFlinger interface so that we are notified 2372 // when new media_server process is back to life. 2373 AudioSystem.setErrorCallback(mAudioSystemCallback); 2374 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0, 2375 null, 500); 2376 } 2377 break; 2378 2379 case MSG_MEDIA_SERVER_STARTED: 2380 Log.e(TAG, "Media server started."); 2381 // indicate to audio HAL that we start the reconfiguration phase after a media 2382 // server crash 2383 // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server 2384 // process restarts after a crash, not the first time it is started. 2385 AudioSystem.setParameters("restarting=true"); 2386 2387 // Restore device connection states 2388 synchronized (mConnectedDevices) { 2389 Set set = mConnectedDevices.entrySet(); 2390 Iterator i = set.iterator(); 2391 while (i.hasNext()) { 2392 Map.Entry device = (Map.Entry)i.next(); 2393 AudioSystem.setDeviceConnectionState( 2394 ((Integer)device.getKey()).intValue(), 2395 AudioSystem.DEVICE_STATE_AVAILABLE, 2396 (String)device.getValue()); 2397 } 2398 } 2399 // Restore call state 2400 AudioSystem.setPhoneState(mMode); 2401 2402 // Restore forced usage for communcations and record 2403 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm); 2404 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm); 2405 2406 // Restore stream volumes 2407 int numStreamTypes = AudioSystem.getNumStreamTypes(); 2408 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 2409 VolumeStreamState streamState = mStreamStates[streamType]; 2410 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10); 2411 2412 streamState.applyAllVolumes(); 2413 } 2414 2415 // Restore ringer mode 2416 setRingerModeInt(getRingerMode(), false); 2417 2418 // indicate the end of reconfiguration phase to audio HAL 2419 AudioSystem.setParameters("restarting=false"); 2420 break; 2421 2422 case MSG_LOAD_SOUND_EFFECTS: 2423 loadSoundEffects(); 2424 break; 2425 2426 case MSG_PLAY_SOUND_EFFECT: 2427 playSoundEffect(msg.arg1, msg.arg2); 2428 break; 2429 2430 case MSG_BTA2DP_DOCK_TIMEOUT: 2431 // msg.obj == address of BTA2DP device 2432 synchronized (mConnectedDevices) { 2433 makeA2dpDeviceUnavailableNow( (String) msg.obj ); 2434 } 2435 break; 2436 2437 case MSG_SET_FORCE_USE: 2438 setForceUse(msg.arg1, msg.arg2); 2439 break; 2440 2441 case MSG_PERSIST_MEDIABUTTONRECEIVER: 2442 onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj ); 2443 break; 2444 2445 case MSG_RCDISPLAY_CLEAR: 2446 onRcDisplayClear(); 2447 break; 2448 2449 case MSG_RCDISPLAY_UPDATE: 2450 // msg.obj is guaranteed to be non null 2451 onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1); 2452 break; 2453 2454 case MSG_BT_HEADSET_CNCT_FAILED: 2455 resetBluetoothSco(); 2456 break; 2457 } 2458 } 2459 } 2460 2461 private class SettingsObserver extends ContentObserver { 2462 2463 SettingsObserver() { 2464 super(new Handler()); 2465 mContentResolver.registerContentObserver(Settings.System.getUriFor( 2466 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this); 2467 } 2468 2469 @Override 2470 public void onChange(boolean selfChange) { 2471 super.onChange(selfChange); 2472 // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode. 2473 // However there appear to be some missing locks around mRingerModeMutedStreams 2474 // and mRingerModeAffectedStreams, so will leave this synchronized for now. 2475 // mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once). 2476 synchronized (mSettingsLock) { 2477 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver, 2478 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 2479 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)| 2480 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED))); 2481 if (mVoiceCapable) { 2482 ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC); 2483 } else { 2484 ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC); 2485 } 2486 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) { 2487 /* 2488 * Ensure all stream types that should be affected by ringer mode 2489 * are in the proper state. 2490 */ 2491 mRingerModeAffectedStreams = ringerModeAffectedStreams; 2492 setRingerModeInt(getRingerMode(), false); 2493 } 2494 } 2495 } 2496 } 2497 2498 // must be called synchronized on mConnectedDevices 2499 private void makeA2dpDeviceAvailable(String address) { 2500 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 2501 AudioSystem.DEVICE_STATE_AVAILABLE, 2502 address); 2503 // Reset A2DP suspend state each time a new sink is connected 2504 AudioSystem.setParameters("A2dpSuspended=false"); 2505 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), 2506 address); 2507 } 2508 2509 // must be called synchronized on mConnectedDevices 2510 private void makeA2dpDeviceUnavailableNow(String address) { 2511 Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); 2512 mContext.sendBroadcast(noisyIntent); 2513 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 2514 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2515 address); 2516 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 2517 } 2518 2519 // must be called synchronized on mConnectedDevices 2520 private void makeA2dpDeviceUnavailableLater(String address) { 2521 // prevent any activity on the A2DP audio output to avoid unwanted 2522 // reconnection of the sink. 2523 AudioSystem.setParameters("A2dpSuspended=true"); 2524 // the device will be made unavailable later, so consider it disconnected right away 2525 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 2526 // send the delayed message to make the device unavailable later 2527 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address); 2528 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS); 2529 2530 } 2531 2532 // must be called synchronized on mConnectedDevices 2533 private void cancelA2dpDeviceTimeout() { 2534 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT); 2535 } 2536 2537 // must be called synchronized on mConnectedDevices 2538 private boolean hasScheduledA2dpDockTimeout() { 2539 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT); 2540 } 2541 2542 private void handleA2dpConnectionStateChange(BluetoothDevice btDevice, int state) 2543 { 2544 if (btDevice == null) { 2545 return; 2546 } 2547 String address = btDevice.getAddress(); 2548 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 2549 address = ""; 2550 } 2551 synchronized (mConnectedDevices) { 2552 boolean isConnected = 2553 (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) && 2554 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address)); 2555 2556 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { 2557 if (btDevice.isBluetoothDock()) { 2558 if (state == BluetoothProfile.STATE_DISCONNECTED) { 2559 // introduction of a delay for transient disconnections of docks when 2560 // power is rapidly turned off/on, this message will be canceled if 2561 // we reconnect the dock under a preset delay 2562 makeA2dpDeviceUnavailableLater(address); 2563 // the next time isConnected is evaluated, it will be false for the dock 2564 } 2565 } else { 2566 makeA2dpDeviceUnavailableNow(address); 2567 } 2568 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { 2569 if (btDevice.isBluetoothDock()) { 2570 // this could be a reconnection after a transient disconnection 2571 cancelA2dpDeviceTimeout(); 2572 mDockAddress = address; 2573 } else { 2574 // this could be a connection of another A2DP device before the timeout of 2575 // a dock: cancel the dock timeout, and make the dock unavailable now 2576 if(hasScheduledA2dpDockTimeout()) { 2577 cancelA2dpDeviceTimeout(); 2578 makeA2dpDeviceUnavailableNow(mDockAddress); 2579 } 2580 } 2581 makeA2dpDeviceAvailable(address); 2582 } 2583 } 2584 } 2585 2586 /* cache of the address of the last dock the device was connected to */ 2587 private String mDockAddress; 2588 2589 /** 2590 * Receiver for misc intent broadcasts the Phone app cares about. 2591 */ 2592 private class AudioServiceBroadcastReceiver extends BroadcastReceiver { 2593 @Override 2594 public void onReceive(Context context, Intent intent) { 2595 String action = intent.getAction(); 2596 2597 if (action.equals(Intent.ACTION_DOCK_EVENT)) { 2598 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 2599 Intent.EXTRA_DOCK_STATE_UNDOCKED); 2600 int config; 2601 switch (dockState) { 2602 case Intent.EXTRA_DOCK_STATE_DESK: 2603 config = AudioSystem.FORCE_BT_DESK_DOCK; 2604 break; 2605 case Intent.EXTRA_DOCK_STATE_CAR: 2606 config = AudioSystem.FORCE_BT_CAR_DOCK; 2607 break; 2608 case Intent.EXTRA_DOCK_STATE_LE_DESK: 2609 config = AudioSystem.FORCE_ANALOG_DOCK; 2610 break; 2611 case Intent.EXTRA_DOCK_STATE_HE_DESK: 2612 config = AudioSystem.FORCE_DIGITAL_DOCK; 2613 break; 2614 case Intent.EXTRA_DOCK_STATE_UNDOCKED: 2615 default: 2616 config = AudioSystem.FORCE_NONE; 2617 } 2618 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); 2619 } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { 2620 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 2621 BluetoothProfile.STATE_DISCONNECTED); 2622 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 2623 2624 handleA2dpConnectionStateChange(btDevice, state); 2625 } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { 2626 int state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 2627 BluetoothProfile.STATE_DISCONNECTED); 2628 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO; 2629 String address = null; 2630 2631 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 2632 if (btDevice == null) { 2633 return; 2634 } 2635 2636 address = btDevice.getAddress(); 2637 BluetoothClass btClass = btDevice.getBluetoothClass(); 2638 if (btClass != null) { 2639 switch (btClass.getDeviceClass()) { 2640 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 2641 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: 2642 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET; 2643 break; 2644 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 2645 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT; 2646 break; 2647 } 2648 } 2649 2650 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 2651 address = ""; 2652 } 2653 2654 synchronized (mConnectedDevices) { 2655 boolean isConnected = (mConnectedDevices.containsKey(device) && 2656 mConnectedDevices.get(device).equals(address)); 2657 2658 synchronized (mScoClients) { 2659 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { 2660 AudioSystem.setDeviceConnectionState(device, 2661 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2662 address); 2663 mConnectedDevices.remove(device); 2664 mBluetoothHeadsetDevice = null; 2665 resetBluetoothSco(); 2666 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { 2667 AudioSystem.setDeviceConnectionState(device, 2668 AudioSystem.DEVICE_STATE_AVAILABLE, 2669 address); 2670 mConnectedDevices.put(new Integer(device), address); 2671 mBluetoothHeadsetDevice = btDevice; 2672 } 2673 } 2674 } 2675 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { 2676 int state = intent.getIntExtra("state", 0); 2677 int microphone = intent.getIntExtra("microphone", 0); 2678 2679 synchronized (mConnectedDevices) { 2680 if (microphone != 0) { 2681 boolean isConnected = 2682 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 2683 if (state == 0 && isConnected) { 2684 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, 2685 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2686 ""); 2687 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 2688 } else if (state == 1 && !isConnected) { 2689 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, 2690 AudioSystem.DEVICE_STATE_AVAILABLE, 2691 ""); 2692 mConnectedDevices.put( 2693 new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), ""); 2694 } 2695 } else { 2696 boolean isConnected = 2697 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 2698 if (state == 0 && isConnected) { 2699 AudioSystem.setDeviceConnectionState( 2700 AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, 2701 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2702 ""); 2703 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 2704 } else if (state == 1 && !isConnected) { 2705 AudioSystem.setDeviceConnectionState( 2706 AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, 2707 AudioSystem.DEVICE_STATE_AVAILABLE, 2708 ""); 2709 mConnectedDevices.put( 2710 new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), ""); 2711 } 2712 } 2713 } 2714 } else if (action.equals(Intent.ACTION_USB_ANLG_HEADSET_PLUG)) { 2715 int state = intent.getIntExtra("state", 0); 2716 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_ANLG_HEADSET_PLUG, state = "+state); 2717 synchronized (mConnectedDevices) { 2718 boolean isConnected = 2719 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET); 2720 if (state == 0 && isConnected) { 2721 AudioSystem.setDeviceConnectionState( 2722 AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, 2723 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2724 ""); 2725 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET); 2726 } else if (state == 1 && !isConnected) { 2727 AudioSystem.setDeviceConnectionState( 2728 AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, 2729 AudioSystem.DEVICE_STATE_AVAILABLE, 2730 ""); 2731 mConnectedDevices.put( 2732 new Integer(AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET), ""); 2733 } 2734 } 2735 } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) { 2736 int state = intent.getIntExtra("state", 0); 2737 Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state); 2738 synchronized (mConnectedDevices) { 2739 boolean isConnected = 2740 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_AUX_DIGITAL); 2741 if (state == 0 && isConnected) { 2742 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL, 2743 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2744 ""); 2745 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_AUX_DIGITAL); 2746 } else if (state == 1 && !isConnected) { 2747 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_AUX_DIGITAL, 2748 AudioSystem.DEVICE_STATE_AVAILABLE, 2749 ""); 2750 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_AUX_DIGITAL), ""); 2751 } 2752 } 2753 } else if (action.equals(Intent.ACTION_USB_DGTL_HEADSET_PLUG)) { 2754 int state = intent.getIntExtra("state", 0); 2755 Log.v(TAG, "Broadcast Receiver: Got ACTION_USB_DGTL_HEADSET_PLUG, state = "+state); 2756 synchronized (mConnectedDevices) { 2757 boolean isConnected = 2758 mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET); 2759 if (state == 0 && isConnected) { 2760 AudioSystem.setDeviceConnectionState( 2761 AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, 2762 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2763 ""); 2764 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET); 2765 } else if (state == 1 && !isConnected) { 2766 AudioSystem.setDeviceConnectionState( 2767 AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, 2768 AudioSystem.DEVICE_STATE_AVAILABLE, 2769 ""); 2770 mConnectedDevices.put( 2771 new Integer(AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET), ""); 2772 } 2773 } 2774 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { 2775 boolean broadcast = false; 2776 int state = AudioManager.SCO_AUDIO_STATE_ERROR; 2777 synchronized (mScoClients) { 2778 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 2779 // broadcast intent if the connection was initated by AudioService 2780 if (!mScoClients.isEmpty() && 2781 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL || 2782 mScoAudioState == SCO_STATE_ACTIVATE_REQ || 2783 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) { 2784 broadcast = true; 2785 } 2786 switch (btState) { 2787 case BluetoothHeadset.STATE_AUDIO_CONNECTED: 2788 state = AudioManager.SCO_AUDIO_STATE_CONNECTED; 2789 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && 2790 mScoAudioState != SCO_STATE_DEACTIVATE_REQ && 2791 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) { 2792 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 2793 } 2794 break; 2795 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: 2796 state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; 2797 mScoAudioState = SCO_STATE_INACTIVE; 2798 clearAllScoClients(0, false); 2799 break; 2800 case BluetoothHeadset.STATE_AUDIO_CONNECTING: 2801 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && 2802 mScoAudioState != SCO_STATE_DEACTIVATE_REQ && 2803 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) { 2804 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 2805 } 2806 default: 2807 // do not broadcast CONNECTING or invalid state 2808 broadcast = false; 2809 break; 2810 } 2811 } 2812 if (broadcast) { 2813 broadcastScoConnectionState(state); 2814 //FIXME: this is to maintain compatibility with deprecated intent 2815 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. 2816 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 2817 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); 2818 mContext.sendStickyBroadcast(newIntent); 2819 } 2820 } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { 2821 mBootCompleted = true; 2822 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP, 2823 0, 0, null, 0); 2824 2825 mKeyguardManager = 2826 (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); 2827 mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR; 2828 resetBluetoothSco(); 2829 getBluetoothHeadset(); 2830 //FIXME: this is to maintain compatibility with deprecated intent 2831 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. 2832 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 2833 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 2834 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 2835 mContext.sendStickyBroadcast(newIntent); 2836 2837 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 2838 if (adapter != null) { 2839 adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, 2840 BluetoothProfile.A2DP); 2841 } 2842 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) { 2843 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 2844 // a package is being removed, not replaced 2845 String packageName = intent.getData().getSchemeSpecificPart(); 2846 if (packageName != null) { 2847 removeMediaButtonReceiverForPackage(packageName); 2848 } 2849 } 2850 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 2851 AudioSystem.setParameters("screen_state=on"); 2852 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 2853 AudioSystem.setParameters("screen_state=off"); 2854 } 2855 } 2856 } 2857 2858 //========================================================================================== 2859 // AudioFocus 2860 //========================================================================================== 2861 2862 /* constant to identify focus stack entry that is used to hold the focus while the phone 2863 * is ringing or during a call. Used by com.android.internal.telephony.CallManager when 2864 * entering and exiting calls. 2865 */ 2866 public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls"; 2867 2868 private final static Object mAudioFocusLock = new Object(); 2869 2870 private final static Object mRingingLock = new Object(); 2871 2872 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 2873 @Override 2874 public void onCallStateChanged(int state, String incomingNumber) { 2875 if (state == TelephonyManager.CALL_STATE_RINGING) { 2876 //Log.v(TAG, " CALL_STATE_RINGING"); 2877 synchronized(mRingingLock) { 2878 mIsRinging = true; 2879 } 2880 } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK) 2881 || (state == TelephonyManager.CALL_STATE_IDLE)) { 2882 synchronized(mRingingLock) { 2883 mIsRinging = false; 2884 } 2885 } 2886 } 2887 }; 2888 2889 private void notifyTopOfAudioFocusStack() { 2890 // notify the top of the stack it gained focus 2891 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 2892 if (canReassignAudioFocus()) { 2893 try { 2894 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 2895 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId); 2896 } catch (RemoteException e) { 2897 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e); 2898 e.printStackTrace(); 2899 } 2900 } 2901 } 2902 } 2903 2904 private static class FocusStackEntry { 2905 public int mStreamType = -1;// no stream type 2906 public IAudioFocusDispatcher mFocusDispatcher = null; 2907 public IBinder mSourceRef = null; 2908 public String mClientId; 2909 public int mFocusChangeType; 2910 public AudioFocusDeathHandler mHandler; 2911 public String mPackageName; 2912 public int mCallingUid; 2913 2914 public FocusStackEntry() { 2915 } 2916 2917 public FocusStackEntry(int streamType, int duration, 2918 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, 2919 String pn, int uid) { 2920 mStreamType = streamType; 2921 mFocusDispatcher = afl; 2922 mSourceRef = source; 2923 mClientId = id; 2924 mFocusChangeType = duration; 2925 mHandler = hdlr; 2926 mPackageName = pn; 2927 mCallingUid = uid; 2928 } 2929 2930 public void unlinkToDeath() { 2931 try { 2932 if (mSourceRef != null && mHandler != null) { 2933 mSourceRef.unlinkToDeath(mHandler, 0); 2934 mHandler = null; 2935 } 2936 } catch (java.util.NoSuchElementException e) { 2937 Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()"); 2938 } 2939 } 2940 2941 @Override 2942 protected void finalize() throws Throwable { 2943 unlinkToDeath(); // unlink exception handled inside method 2944 super.finalize(); 2945 } 2946 } 2947 2948 private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>(); 2949 2950 /** 2951 * Helper function: 2952 * Display in the log the current entries in the audio focus stack 2953 */ 2954 private void dumpFocusStack(PrintWriter pw) { 2955 pw.println("\nAudio Focus stack entries:"); 2956 synchronized(mAudioFocusLock) { 2957 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2958 while(stackIterator.hasNext()) { 2959 FocusStackEntry fse = stackIterator.next(); 2960 pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId 2961 + " -- duration: " + fse.mFocusChangeType 2962 + " -- uid: " + fse.mCallingUid); 2963 } 2964 } 2965 } 2966 2967 /** 2968 * Helper function: 2969 * Called synchronized on mAudioFocusLock 2970 * Remove a focus listener from the focus stack. 2971 * @param focusListenerToRemove the focus listener 2972 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding 2973 * focus, notify the next item in the stack it gained focus. 2974 */ 2975 private void removeFocusStackEntry(String clientToRemove, boolean signal) { 2976 // is the current top of the focus stack abandoning focus? (because of request, not death) 2977 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove)) 2978 { 2979 //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); 2980 FocusStackEntry fse = mFocusStack.pop(); 2981 fse.unlinkToDeath(); 2982 if (signal) { 2983 // notify the new top of the stack it gained focus 2984 notifyTopOfAudioFocusStack(); 2985 // there's a new top of the stack, let the remote control know 2986 synchronized(mRCStack) { 2987 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 2988 } 2989 } 2990 } else { 2991 // focus is abandoned by a client that's not at the top of the stack, 2992 // no need to update focus. 2993 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2994 while(stackIterator.hasNext()) { 2995 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 2996 if(fse.mClientId.equals(clientToRemove)) { 2997 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 2998 + fse.mClientId); 2999 stackIterator.remove(); 3000 fse.unlinkToDeath(); 3001 } 3002 } 3003 } 3004 } 3005 3006 /** 3007 * Helper function: 3008 * Called synchronized on mAudioFocusLock 3009 * Remove focus listeners from the focus stack for a particular client when it has died. 3010 */ 3011 private void removeFocusStackEntryForClient(IBinder cb) { 3012 // is the owner of the audio focus part of the client to remove? 3013 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && 3014 mFocusStack.peek().mSourceRef.equals(cb); 3015 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 3016 while(stackIterator.hasNext()) { 3017 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 3018 if(fse.mSourceRef.equals(cb)) { 3019 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 3020 + fse.mClientId); 3021 stackIterator.remove(); 3022 // the client just died, no need to unlink to its death 3023 } 3024 } 3025 if (isTopOfStackForClientToRemove) { 3026 // we removed an entry at the top of the stack: 3027 // notify the new top of the stack it gained focus. 3028 notifyTopOfAudioFocusStack(); 3029 // there's a new top of the stack, let the remote control know 3030 synchronized(mRCStack) { 3031 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 3032 } 3033 } 3034 } 3035 3036 /** 3037 * Helper function: 3038 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. 3039 */ 3040 private boolean canReassignAudioFocus() { 3041 // focus requests are rejected during a phone call or when the phone is ringing 3042 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus 3043 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) { 3044 return false; 3045 } 3046 return true; 3047 } 3048 3049 /** 3050 * Inner class to monitor audio focus client deaths, and remove them from the audio focus 3051 * stack if necessary. 3052 */ 3053 private class AudioFocusDeathHandler implements IBinder.DeathRecipient { 3054 private IBinder mCb; // To be notified of client's death 3055 3056 AudioFocusDeathHandler(IBinder cb) { 3057 mCb = cb; 3058 } 3059 3060 public void binderDied() { 3061 synchronized(mAudioFocusLock) { 3062 Log.w(TAG, " AudioFocus audio focus client died"); 3063 removeFocusStackEntryForClient(mCb); 3064 } 3065 } 3066 3067 public IBinder getBinder() { 3068 return mCb; 3069 } 3070 } 3071 3072 3073 /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */ 3074 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb, 3075 IAudioFocusDispatcher fd, String clientId, String callingPackageName) { 3076 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId); 3077 // the main stream type for the audio focus request is currently not used. It may 3078 // potentially be used to handle multiple stream type-dependent audio focuses. 3079 3080 // we need a valid binder callback for clients 3081 if (!cb.pingBinder()) { 3082 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); 3083 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 3084 } 3085 3086 synchronized(mAudioFocusLock) { 3087 if (!canReassignAudioFocus()) { 3088 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 3089 } 3090 3091 // handle the potential premature death of the new holder of the focus 3092 // (premature death == death before abandoning focus) 3093 // Register for client death notification 3094 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); 3095 try { 3096 cb.linkToDeath(afdh, 0); 3097 } catch (RemoteException e) { 3098 // client has already died! 3099 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); 3100 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 3101 } 3102 3103 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) { 3104 // if focus is already owned by this client and the reason for acquiring the focus 3105 // hasn't changed, don't do anything 3106 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) { 3107 // unlink death handler so it can be gc'ed. 3108 // linkToDeath() creates a JNI global reference preventing collection. 3109 cb.unlinkToDeath(afdh, 0); 3110 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 3111 } 3112 // the reason for the audio focus request has changed: remove the current top of 3113 // stack and respond as if we had a new focus owner 3114 FocusStackEntry fse = mFocusStack.pop(); 3115 fse.unlinkToDeath(); 3116 } 3117 3118 // notify current top of stack it is losing focus 3119 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 3120 try { 3121 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 3122 -1 * focusChangeHint, // loss and gain codes are inverse of each other 3123 mFocusStack.peek().mClientId); 3124 } catch (RemoteException e) { 3125 Log.e(TAG, " Failure to signal loss of focus due to "+ e); 3126 e.printStackTrace(); 3127 } 3128 } 3129 3130 // focus requester might already be somewhere below in the stack, remove it 3131 removeFocusStackEntry(clientId, false /* signal */); 3132 3133 // push focus requester at the top of the audio focus stack 3134 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb, 3135 clientId, afdh, callingPackageName, Binder.getCallingUid())); 3136 3137 // there's a new top of the stack, let the remote control know 3138 synchronized(mRCStack) { 3139 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 3140 } 3141 }//synchronized(mAudioFocusLock) 3142 3143 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 3144 } 3145 3146 /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */ 3147 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) { 3148 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId); 3149 try { 3150 // this will take care of notifying the new focus owner if needed 3151 synchronized(mAudioFocusLock) { 3152 removeFocusStackEntry(clientId, true); 3153 } 3154 } catch (java.util.ConcurrentModificationException cme) { 3155 // Catching this exception here is temporary. It is here just to prevent 3156 // a crash seen when the "Silent" notification is played. This is believed to be fixed 3157 // but this try catch block is left just to be safe. 3158 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme); 3159 cme.printStackTrace(); 3160 } 3161 3162 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 3163 } 3164 3165 3166 public void unregisterAudioFocusClient(String clientId) { 3167 synchronized(mAudioFocusLock) { 3168 removeFocusStackEntry(clientId, false); 3169 } 3170 } 3171 3172 3173 //========================================================================================== 3174 // RemoteControl 3175 //========================================================================================== 3176 /** 3177 * Receiver for media button intents. Handles the dispatching of the media button event 3178 * to one of the registered listeners, or if there was none, resumes the intent broadcast 3179 * to the rest of the system. 3180 */ 3181 private class MediaButtonBroadcastReceiver extends BroadcastReceiver { 3182 @Override 3183 public void onReceive(Context context, Intent intent) { 3184 String action = intent.getAction(); 3185 if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) { 3186 return; 3187 } 3188 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 3189 if (event != null) { 3190 // if in a call or ringing, do not break the current phone app behavior 3191 // TODO modify this to let the phone app specifically get the RC focus 3192 // add modify the phone app to take advantage of the new API 3193 synchronized(mRingingLock) { 3194 if (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL) || 3195 (getMode() == AudioSystem.MODE_IN_COMMUNICATION) || 3196 (getMode() == AudioSystem.MODE_RINGTONE) ) { 3197 return; 3198 } 3199 } 3200 synchronized(mRCStack) { 3201 if (!mRCStack.empty()) { 3202 // create a new intent to fill in the extras of the registered PendingIntent 3203 Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 3204 Bundle extras = intent.getExtras(); 3205 if (extras != null) { 3206 targetedIntent.putExtras(extras); 3207 // trap the current broadcast 3208 abortBroadcast(); 3209 //Log.v(TAG, " Sending intent" + targetedIntent); 3210 // send the intent that was registered by the client 3211 try { 3212 mRCStack.peek().mMediaIntent.send(context, 0, targetedIntent); 3213 } catch (CanceledException e) { 3214 Log.e(TAG, "Error sending pending intent " + mRCStack.peek()); 3215 e.printStackTrace(); 3216 } 3217 } 3218 } 3219 } 3220 } 3221 } 3222 } 3223 3224 private final Object mCurrentRcLock = new Object(); 3225 /** 3226 * The one remote control client which will receive a request for display information. 3227 * This object may be null. 3228 * Access protected by mCurrentRcLock. 3229 */ 3230 private IRemoteControlClient mCurrentRcClient = null; 3231 3232 private final static int RC_INFO_NONE = 0; 3233 private final static int RC_INFO_ALL = 3234 RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART | 3235 RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA | 3236 RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA | 3237 RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE; 3238 3239 /** 3240 * A monotonically increasing generation counter for mCurrentRcClient. 3241 * Only accessed with a lock on mCurrentRcLock. 3242 * No value wrap-around issues as we only act on equal values. 3243 */ 3244 private int mCurrentRcClientGen = 0; 3245 3246 /** 3247 * Inner class to monitor remote control client deaths, and remove the client for the 3248 * remote control stack if necessary. 3249 */ 3250 private class RcClientDeathHandler implements IBinder.DeathRecipient { 3251 private IBinder mCb; // To be notified of client's death 3252 private PendingIntent mMediaIntent; 3253 3254 RcClientDeathHandler(IBinder cb, PendingIntent pi) { 3255 mCb = cb; 3256 mMediaIntent = pi; 3257 } 3258 3259 public void binderDied() { 3260 Log.w(TAG, " RemoteControlClient died"); 3261 // remote control client died, make sure the displays don't use it anymore 3262 // by setting its remote control client to null 3263 registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/); 3264 } 3265 3266 public IBinder getBinder() { 3267 return mCb; 3268 } 3269 } 3270 3271 private static class RemoteControlStackEntry { 3272 /** 3273 * The target for the ACTION_MEDIA_BUTTON events. 3274 * Always non null. 3275 */ 3276 public PendingIntent mMediaIntent; 3277 /** 3278 * The registered media button event receiver. 3279 * Always non null. 3280 */ 3281 public ComponentName mReceiverComponent; 3282 public String mCallingPackageName; 3283 public int mCallingUid; 3284 /** 3285 * Provides access to the information to display on the remote control. 3286 * May be null (when a media button event receiver is registered, 3287 * but no remote control client has been registered) */ 3288 public IRemoteControlClient mRcClient; 3289 public RcClientDeathHandler mRcClientDeathHandler; 3290 3291 /** precondition: mediaIntent != null, eventReceiver != null */ 3292 public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) { 3293 mMediaIntent = mediaIntent; 3294 mReceiverComponent = eventReceiver; 3295 mCallingUid = -1; 3296 mRcClient = null; 3297 } 3298 3299 public void unlinkToRcClientDeath() { 3300 if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) { 3301 try { 3302 mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0); 3303 mRcClientDeathHandler = null; 3304 } catch (java.util.NoSuchElementException e) { 3305 // not much we can do here 3306 Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()"); 3307 e.printStackTrace(); 3308 } 3309 } 3310 } 3311 3312 @Override 3313 protected void finalize() throws Throwable { 3314 unlinkToRcClientDeath();// unlink exception handled inside method 3315 super.finalize(); 3316 } 3317 } 3318 3319 /** 3320 * The stack of remote control event receivers. 3321 * Code sections and methods that modify the remote control event receiver stack are 3322 * synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either 3323 * stack, audio focus or RC, can lead to a change in the remote control display 3324 */ 3325 private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>(); 3326 3327 /** 3328 * Helper function: 3329 * Display in the log the current entries in the remote control focus stack 3330 */ 3331 private void dumpRCStack(PrintWriter pw) { 3332 pw.println("\nRemote Control stack entries:"); 3333 synchronized(mRCStack) { 3334 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3335 while(stackIterator.hasNext()) { 3336 RemoteControlStackEntry rcse = stackIterator.next(); 3337 pw.println(" pi: " + rcse.mMediaIntent + 3338 " -- ercvr: " + rcse.mReceiverComponent + 3339 " -- client: " + rcse.mRcClient + 3340 " -- uid: " + rcse.mCallingUid); 3341 } 3342 } 3343 } 3344 3345 /** 3346 * Helper function: 3347 * Remove any entry in the remote control stack that has the same package name as packageName 3348 * Pre-condition: packageName != null 3349 */ 3350 private void removeMediaButtonReceiverForPackage(String packageName) { 3351 synchronized(mRCStack) { 3352 if (mRCStack.empty()) { 3353 return; 3354 } else { 3355 RemoteControlStackEntry oldTop = mRCStack.peek(); 3356 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3357 // iterate over the stack entries 3358 while(stackIterator.hasNext()) { 3359 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 3360 if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) { 3361 // a stack entry is from the package being removed, remove it from the stack 3362 stackIterator.remove(); 3363 rcse.unlinkToRcClientDeath(); 3364 } 3365 } 3366 if (mRCStack.empty()) { 3367 // no saved media button receiver 3368 mAudioHandler.sendMessage( 3369 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, 3370 null)); 3371 } else if (oldTop != mRCStack.peek()) { 3372 // the top of the stack has changed, save it in the system settings 3373 // by posting a message to persist it 3374 mAudioHandler.sendMessage( 3375 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, 3376 mRCStack.peek().mReceiverComponent)); 3377 } 3378 } 3379 } 3380 } 3381 3382 /** 3383 * Helper function: 3384 * Restore remote control receiver from the system settings. 3385 */ 3386 private void restoreMediaButtonReceiver() { 3387 String receiverName = Settings.System.getString(mContentResolver, 3388 Settings.System.MEDIA_BUTTON_RECEIVER); 3389 if ((null != receiverName) && !receiverName.isEmpty()) { 3390 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName); 3391 // construct a PendingIntent targeted to the restored component name 3392 // for the media button and register it 3393 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 3394 // the associated intent will be handled by the component being registered 3395 mediaButtonIntent.setComponent(eventReceiver); 3396 PendingIntent pi = PendingIntent.getBroadcast(mContext, 3397 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/); 3398 registerMediaButtonIntent(pi, eventReceiver); 3399 } 3400 } 3401 3402 /** 3403 * Helper function: 3404 * Set the new remote control receiver at the top of the RC focus stack. 3405 * precondition: mediaIntent != null, target != null 3406 */ 3407 private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) { 3408 // already at top of stack? 3409 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) { 3410 return; 3411 } 3412 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3413 RemoteControlStackEntry rcse = null; 3414 boolean wasInsideStack = false; 3415 while(stackIterator.hasNext()) { 3416 rcse = (RemoteControlStackEntry)stackIterator.next(); 3417 if(rcse.mMediaIntent.equals(mediaIntent)) { 3418 wasInsideStack = true; 3419 stackIterator.remove(); 3420 break; 3421 } 3422 } 3423 if (!wasInsideStack) { 3424 rcse = new RemoteControlStackEntry(mediaIntent, target); 3425 } 3426 mRCStack.push(rcse); 3427 3428 // post message to persist the default media button receiver 3429 mAudioHandler.sendMessage( mAudioHandler.obtainMessage( 3430 MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) ); 3431 } 3432 3433 /** 3434 * Helper function: 3435 * Remove the remote control receiver from the RC focus stack. 3436 * precondition: pi != null 3437 */ 3438 private void removeMediaButtonReceiver(PendingIntent pi) { 3439 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3440 while(stackIterator.hasNext()) { 3441 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 3442 if(rcse.mMediaIntent.equals(pi)) { 3443 stackIterator.remove(); 3444 rcse.unlinkToRcClientDeath(); 3445 break; 3446 } 3447 } 3448 } 3449 3450 /** 3451 * Helper function: 3452 * Called synchronized on mRCStack 3453 */ 3454 private boolean isCurrentRcController(PendingIntent pi) { 3455 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) { 3456 return true; 3457 } 3458 return false; 3459 } 3460 3461 //========================================================================================== 3462 // Remote control display / client 3463 //========================================================================================== 3464 /** 3465 * Update the remote control displays with the new "focused" client generation 3466 */ 3467 private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration, 3468 PendingIntent newMediaIntent, boolean clearing) { 3469 // NOTE: Only one IRemoteControlDisplay supported in this implementation 3470 if (mRcDisplay != null) { 3471 try { 3472 mRcDisplay.setCurrentClientId( 3473 newClientGeneration, newMediaIntent, clearing); 3474 } catch (RemoteException e) { 3475 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e); 3476 // if we had a display before, stop monitoring its death 3477 rcDisplay_stopDeathMonitor_syncRcStack(); 3478 mRcDisplay = null; 3479 } 3480 } 3481 } 3482 3483 /** 3484 * Update the remote control clients with the new "focused" client generation 3485 */ 3486 private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) { 3487 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3488 while(stackIterator.hasNext()) { 3489 RemoteControlStackEntry se = stackIterator.next(); 3490 if ((se != null) && (se.mRcClient != null)) { 3491 try { 3492 se.mRcClient.setCurrentClientGenerationId(newClientGeneration); 3493 } catch (RemoteException e) { 3494 Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e); 3495 stackIterator.remove(); 3496 se.unlinkToRcClientDeath(); 3497 } 3498 } 3499 } 3500 } 3501 3502 /** 3503 * Update the displays and clients with the new "focused" client generation and name 3504 * @param newClientGeneration the new generation value matching a client update 3505 * @param newClientEventReceiver the media button event receiver associated with the client. 3506 * May be null, which implies there is no registered media button event receiver. 3507 * @param clearing true if the new client generation value maps to a remote control update 3508 * where the display should be cleared. 3509 */ 3510 private void setNewRcClient_syncRcsCurrc(int newClientGeneration, 3511 PendingIntent newMediaIntent, boolean clearing) { 3512 // send the new valid client generation ID to all displays 3513 setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing); 3514 // send the new valid client generation ID to all clients 3515 setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration); 3516 } 3517 3518 /** 3519 * Called when processing MSG_RCDISPLAY_CLEAR event 3520 */ 3521 private void onRcDisplayClear() { 3522 if (DEBUG_RC) Log.i(TAG, "Clear remote control display"); 3523 3524 synchronized(mRCStack) { 3525 synchronized(mCurrentRcLock) { 3526 mCurrentRcClientGen++; 3527 // synchronously update the displays and clients with the new client generation 3528 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen, 3529 null /*newMediaIntent*/, true /*clearing*/); 3530 } 3531 } 3532 } 3533 3534 /** 3535 * Called when processing MSG_RCDISPLAY_UPDATE event 3536 */ 3537 private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) { 3538 synchronized(mRCStack) { 3539 synchronized(mCurrentRcLock) { 3540 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) { 3541 if (DEBUG_RC) Log.i(TAG, "Display/update remote control "); 3542 3543 mCurrentRcClientGen++; 3544 // synchronously update the displays and clients with 3545 // the new client generation 3546 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen, 3547 rcse.mMediaIntent /*newMediaIntent*/, 3548 false /*clearing*/); 3549 3550 // tell the current client that it needs to send info 3551 try { 3552 mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, 3553 flags, mArtworkExpectedWidth, mArtworkExpectedHeight); 3554 } catch (RemoteException e) { 3555 Log.e(TAG, "Current valid remote client is dead: "+e); 3556 mCurrentRcClient = null; 3557 } 3558 } else { 3559 // the remote control display owner has changed between the 3560 // the message to update the display was sent, and the time it 3561 // gets to be processed (now) 3562 } 3563 } 3564 } 3565 } 3566 3567 3568 /** 3569 * Helper function: 3570 * Called synchronized on mRCStack 3571 */ 3572 private void clearRemoteControlDisplay_syncAfRcs() { 3573 synchronized(mCurrentRcLock) { 3574 mCurrentRcClient = null; 3575 } 3576 // will cause onRcDisplayClear() to be called in AudioService's handler thread 3577 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) ); 3578 } 3579 3580 /** 3581 * Helper function for code readability: only to be called from 3582 * checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for 3583 * this method. 3584 * Preconditions: 3585 * - called synchronized mAudioFocusLock then on mRCStack 3586 * - mRCStack.isEmpty() is false 3587 */ 3588 private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) { 3589 RemoteControlStackEntry rcse = mRCStack.peek(); 3590 int infoFlagsAboutToBeUsed = infoChangedFlags; 3591 // this is where we enforce opt-in for information display on the remote controls 3592 // with the new AudioManager.registerRemoteControlClient() API 3593 if (rcse.mRcClient == null) { 3594 //Log.w(TAG, "Can't update remote control display with null remote control client"); 3595 clearRemoteControlDisplay_syncAfRcs(); 3596 return; 3597 } 3598 synchronized(mCurrentRcLock) { 3599 if (!rcse.mRcClient.equals(mCurrentRcClient)) { 3600 // new RC client, assume every type of information shall be queried 3601 infoFlagsAboutToBeUsed = RC_INFO_ALL; 3602 } 3603 mCurrentRcClient = rcse.mRcClient; 3604 } 3605 // will cause onRcDisplayUpdate() to be called in AudioService's handler thread 3606 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE, 3607 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) ); 3608 } 3609 3610 /** 3611 * Helper function: 3612 * Called synchronized on mAudioFocusLock, then mRCStack 3613 * Check whether the remote control display should be updated, triggers the update if required 3614 * @param infoChangedFlags the flags corresponding to the remote control client information 3615 * that has changed, if applicable (checking for the update conditions might trigger a 3616 * clear, rather than an update event). 3617 */ 3618 private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) { 3619 // determine whether the remote control display should be refreshed 3620 // if either stack is empty, there is a mismatch, so clear the RC display 3621 if (mRCStack.isEmpty() || mFocusStack.isEmpty()) { 3622 clearRemoteControlDisplay_syncAfRcs(); 3623 return; 3624 } 3625 // if the top of the two stacks belong to different packages, there is a mismatch, clear 3626 if ((mRCStack.peek().mCallingPackageName != null) 3627 && (mFocusStack.peek().mPackageName != null) 3628 && !(mRCStack.peek().mCallingPackageName.compareTo( 3629 mFocusStack.peek().mPackageName) == 0)) { 3630 clearRemoteControlDisplay_syncAfRcs(); 3631 return; 3632 } 3633 // if the audio focus didn't originate from the same Uid as the one in which the remote 3634 // control information will be retrieved, clear 3635 if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) { 3636 clearRemoteControlDisplay_syncAfRcs(); 3637 return; 3638 } 3639 // refresh conditions were verified: update the remote controls 3640 // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty 3641 updateRemoteControlDisplay_syncAfRcs(infoChangedFlags); 3642 } 3643 3644 /** 3645 * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c) 3646 * precondition: mediaIntent != null, target != null 3647 */ 3648 public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) { 3649 Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent); 3650 3651 synchronized(mAudioFocusLock) { 3652 synchronized(mRCStack) { 3653 pushMediaButtonReceiver(mediaIntent, eventReceiver); 3654 // new RC client, assume every type of information shall be queried 3655 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 3656 } 3657 } 3658 } 3659 3660 /** 3661 * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent) 3662 * precondition: mediaIntent != null, eventReceiver != null 3663 */ 3664 public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) 3665 { 3666 Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent); 3667 3668 synchronized(mAudioFocusLock) { 3669 synchronized(mRCStack) { 3670 boolean topOfStackWillChange = isCurrentRcController(mediaIntent); 3671 removeMediaButtonReceiver(mediaIntent); 3672 if (topOfStackWillChange) { 3673 // current RC client will change, assume every type of info needs to be queried 3674 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 3675 } 3676 } 3677 } 3678 } 3679 3680 /** 3681 * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) 3682 * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient 3683 * without modifying the RC stack, but while still causing the display to refresh (will 3684 * become blank as a result of this) 3685 */ 3686 public void registerRemoteControlClient(PendingIntent mediaIntent, 3687 IRemoteControlClient rcClient, String callingPackageName) { 3688 if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient); 3689 synchronized(mAudioFocusLock) { 3690 synchronized(mRCStack) { 3691 // store the new display information 3692 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3693 while(stackIterator.hasNext()) { 3694 RemoteControlStackEntry rcse = stackIterator.next(); 3695 if(rcse.mMediaIntent.equals(mediaIntent)) { 3696 // already had a remote control client? 3697 if (rcse.mRcClientDeathHandler != null) { 3698 // stop monitoring the old client's death 3699 rcse.unlinkToRcClientDeath(); 3700 } 3701 // save the new remote control client 3702 rcse.mRcClient = rcClient; 3703 rcse.mCallingPackageName = callingPackageName; 3704 rcse.mCallingUid = Binder.getCallingUid(); 3705 if (rcClient == null) { 3706 // here rcse.mRcClientDeathHandler is null; 3707 break; 3708 } 3709 3710 // there is a new (non-null) client: 3711 // 1/ give the new client the current display (if any) 3712 if (mRcDisplay != null) { 3713 try { 3714 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay); 3715 } catch (RemoteException e) { 3716 Log.e(TAG, "Error connecting remote control display to client: "+e); 3717 e.printStackTrace(); 3718 } 3719 } 3720 // 2/ monitor the new client's death 3721 IBinder b = rcse.mRcClient.asBinder(); 3722 RcClientDeathHandler rcdh = 3723 new RcClientDeathHandler(b, rcse.mMediaIntent); 3724 try { 3725 b.linkToDeath(rcdh, 0); 3726 } catch (RemoteException e) { 3727 // remote control client is DOA, disqualify it 3728 Log.w(TAG, "registerRemoteControlClient() has a dead client " + b); 3729 rcse.mRcClient = null; 3730 } 3731 rcse.mRcClientDeathHandler = rcdh; 3732 break; 3733 } 3734 } 3735 // if the eventReceiver is at the top of the stack 3736 // then check for potential refresh of the remote controls 3737 if (isCurrentRcController(mediaIntent)) { 3738 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 3739 } 3740 } 3741 } 3742 } 3743 3744 /** 3745 * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...) 3746 * rcClient is guaranteed non-null 3747 */ 3748 public void unregisterRemoteControlClient(PendingIntent mediaIntent, 3749 IRemoteControlClient rcClient) { 3750 synchronized(mAudioFocusLock) { 3751 synchronized(mRCStack) { 3752 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3753 while(stackIterator.hasNext()) { 3754 RemoteControlStackEntry rcse = stackIterator.next(); 3755 if ((rcse.mMediaIntent.equals(mediaIntent)) 3756 && rcClient.equals(rcse.mRcClient)) { 3757 // we found the IRemoteControlClient to unregister 3758 // stop monitoring its death 3759 rcse.unlinkToRcClientDeath(); 3760 // reset the client-related fields 3761 rcse.mRcClient = null; 3762 rcse.mCallingPackageName = null; 3763 } 3764 } 3765 } 3766 } 3767 } 3768 3769 /** 3770 * The remote control displays. 3771 * Access synchronized on mRCStack 3772 * NOTE: Only one IRemoteControlDisplay supported in this implementation 3773 */ 3774 private IRemoteControlDisplay mRcDisplay; 3775 private RcDisplayDeathHandler mRcDisplayDeathHandler; 3776 private int mArtworkExpectedWidth = -1; 3777 private int mArtworkExpectedHeight = -1; 3778 /** 3779 * Inner class to monitor remote control display deaths, and unregister them from the list 3780 * of displays if necessary. 3781 */ 3782 private class RcDisplayDeathHandler implements IBinder.DeathRecipient { 3783 private IBinder mCb; // To be notified of client's death 3784 3785 public RcDisplayDeathHandler(IBinder b) { 3786 if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b); 3787 mCb = b; 3788 } 3789 3790 public void binderDied() { 3791 synchronized(mRCStack) { 3792 Log.w(TAG, "RemoteControl: display died"); 3793 mRcDisplay = null; 3794 } 3795 } 3796 3797 public void unlinkToRcDisplayDeath() { 3798 if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb); 3799 try { 3800 mCb.unlinkToDeath(this, 0); 3801 } catch (java.util.NoSuchElementException e) { 3802 // not much we can do here, the display was being unregistered anyway 3803 Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()"); 3804 e.printStackTrace(); 3805 } 3806 } 3807 3808 } 3809 3810 private void rcDisplay_stopDeathMonitor_syncRcStack() { 3811 if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null) 3812 // we had a display before, stop monitoring its death 3813 mRcDisplayDeathHandler.unlinkToRcDisplayDeath(); 3814 } 3815 } 3816 3817 private void rcDisplay_startDeathMonitor_syncRcStack() { 3818 if (mRcDisplay != null) { 3819 // new non-null display, monitor its death 3820 IBinder b = mRcDisplay.asBinder(); 3821 mRcDisplayDeathHandler = new RcDisplayDeathHandler(b); 3822 try { 3823 b.linkToDeath(mRcDisplayDeathHandler, 0); 3824 } catch (RemoteException e) { 3825 // remote control display is DOA, disqualify it 3826 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b); 3827 mRcDisplay = null; 3828 } 3829 } 3830 } 3831 3832 /** 3833 * Register an IRemoteControlDisplay. 3834 * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient 3835 * at the top of the stack to update the new display with its information. 3836 * Since only one IRemoteControlDisplay is supported, this will unregister the previous display. 3837 * @param rcd the IRemoteControlDisplay to register. No effect if null. 3838 */ 3839 public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) { 3840 if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")"); 3841 synchronized(mAudioFocusLock) { 3842 synchronized(mRCStack) { 3843 if ((mRcDisplay == rcd) || (rcd == null)) { 3844 return; 3845 } 3846 // if we had a display before, stop monitoring its death 3847 rcDisplay_stopDeathMonitor_syncRcStack(); 3848 mRcDisplay = rcd; 3849 // new display, start monitoring its death 3850 rcDisplay_startDeathMonitor_syncRcStack(); 3851 3852 // let all the remote control clients there is a new display 3853 // no need to unplug the previous because we only support one display 3854 // and the clients don't track the death of the display 3855 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3856 while(stackIterator.hasNext()) { 3857 RemoteControlStackEntry rcse = stackIterator.next(); 3858 if(rcse.mRcClient != null) { 3859 try { 3860 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay); 3861 } catch (RemoteException e) { 3862 Log.e(TAG, "Error connecting remote control display to client: " + e); 3863 e.printStackTrace(); 3864 } 3865 } 3866 } 3867 3868 // we have a new display, of which all the clients are now aware: have it be updated 3869 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 3870 } 3871 } 3872 } 3873 3874 /** 3875 * Unregister an IRemoteControlDisplay. 3876 * Since only one IRemoteControlDisplay is supported, this has no effect if the one to 3877 * unregister is not the current one. 3878 * @param rcd the IRemoteControlDisplay to unregister. No effect if null. 3879 */ 3880 public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) { 3881 if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")"); 3882 synchronized(mRCStack) { 3883 // only one display here, so you can only unregister the current display 3884 if ((rcd == null) || (rcd != mRcDisplay)) { 3885 if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD"); 3886 return; 3887 } 3888 // if we had a display before, stop monitoring its death 3889 rcDisplay_stopDeathMonitor_syncRcStack(); 3890 mRcDisplay = null; 3891 3892 // disconnect this remote control display from all the clients 3893 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 3894 while(stackIterator.hasNext()) { 3895 RemoteControlStackEntry rcse = stackIterator.next(); 3896 if(rcse.mRcClient != null) { 3897 try { 3898 rcse.mRcClient.unplugRemoteControlDisplay(rcd); 3899 } catch (RemoteException e) { 3900 Log.e(TAG, "Error disconnecting remote control display to client: " + e); 3901 e.printStackTrace(); 3902 } 3903 } 3904 } 3905 } 3906 } 3907 3908 public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) { 3909 synchronized(mRCStack) { 3910 // NOTE: Only one IRemoteControlDisplay supported in this implementation 3911 mArtworkExpectedWidth = w; 3912 mArtworkExpectedHeight = h; 3913 } 3914 } 3915 3916 @Override 3917 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 3918 // TODO probably a lot more to do here than just the audio focus and remote control stacks 3919 dumpFocusStack(pw); 3920 dumpRCStack(pw); 3921 } 3922 3923 3924} 3925