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