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