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