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