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