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