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