AudioService.java revision 3c652ca5a546e7227a5f4bdbf0f81552b2c91383
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.content.BroadcastReceiver; 21import android.content.ComponentName; 22import android.content.ContentResolver; 23import android.content.Context; 24import android.content.Intent; 25import android.content.IntentFilter; 26import android.bluetooth.BluetoothA2dp; 27import android.bluetooth.BluetoothClass; 28import android.bluetooth.BluetoothDevice; 29import android.bluetooth.BluetoothHeadset; 30import android.content.pm.PackageManager; 31import android.database.ContentObserver; 32import android.media.MediaPlayer.OnCompletionListener; 33import android.media.MediaPlayer.OnErrorListener; 34import android.os.Binder; 35import android.os.Environment; 36import android.os.Handler; 37import android.os.IBinder; 38import android.os.Looper; 39import android.os.Message; 40import android.os.RemoteException; 41import android.os.ServiceManager; 42import android.provider.Settings; 43import android.provider.Settings.System; 44import android.telephony.PhoneStateListener; 45import android.telephony.TelephonyManager; 46import android.util.Log; 47import android.view.KeyEvent; 48import android.view.VolumePanel; 49import android.os.SystemProperties; 50 51import com.android.internal.telephony.ITelephony; 52 53import java.io.FileDescriptor; 54import java.io.IOException; 55import java.io.PrintWriter; 56import java.util.ArrayList; 57import java.util.HashMap; 58import java.util.Iterator; 59import java.util.Map; 60import java.util.Set; 61import java.util.Stack; 62 63/** 64 * The implementation of the volume manager service. 65 * <p> 66 * This implementation focuses on delivering a responsive UI. Most methods are 67 * asynchronous to external calls. For example, the task of setting a volume 68 * will update our internal state, but in a separate thread will set the system 69 * volume and later persist to the database. Similarly, setting the ringer mode 70 * will update the state and broadcast a change and in a separate thread later 71 * persist the ringer mode. 72 * 73 * @hide 74 */ 75public class AudioService extends IAudioService.Stub { 76 77 private static final String TAG = "AudioService"; 78 79 /** How long to delay before persisting a change in volume/ringer mode. */ 80 private static final int PERSIST_DELAY = 3000; 81 82 private Context mContext; 83 private ContentResolver mContentResolver; 84 85 86 /** The UI */ 87 private VolumePanel mVolumePanel; 88 89 // sendMsg() flags 90 /** Used when a message should be shared across all stream types. */ 91 private static final int SHARED_MSG = -1; 92 /** If the msg is already queued, replace it with this one. */ 93 private static final int SENDMSG_REPLACE = 0; 94 /** If the msg is already queued, ignore this one and leave the old. */ 95 private static final int SENDMSG_NOOP = 1; 96 /** If the msg is already queued, queue this one and leave the old. */ 97 private static final int SENDMSG_QUEUE = 2; 98 99 // AudioHandler message.whats 100 private static final int MSG_SET_SYSTEM_VOLUME = 0; 101 private static final int MSG_PERSIST_VOLUME = 1; 102 private static final int MSG_PERSIST_RINGER_MODE = 3; 103 private static final int MSG_PERSIST_VIBRATE_SETTING = 4; 104 private static final int MSG_MEDIA_SERVER_DIED = 5; 105 private static final int MSG_MEDIA_SERVER_STARTED = 6; 106 private static final int MSG_PLAY_SOUND_EFFECT = 7; 107 private static final int MSG_BTA2DP_DOCK_TIMEOUT = 8; 108 109 private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000; 110 111 /** @see AudioSystemThread */ 112 private AudioSystemThread mAudioSystemThread; 113 /** @see AudioHandler */ 114 private AudioHandler mAudioHandler; 115 /** @see VolumeStreamState */ 116 private VolumeStreamState[] mStreamStates; 117 private SettingsObserver mSettingsObserver; 118 119 private int mMode; 120 private Object mSettingsLock = new Object(); 121 private boolean mMediaServerOk; 122 123 private SoundPool mSoundPool; 124 private Object mSoundEffectsLock = new Object(); 125 private static final int NUM_SOUNDPOOL_CHANNELS = 4; 126 private static final int SOUND_EFFECT_VOLUME = 1000; 127 128 /* Sound effect file names */ 129 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/"; 130 private static final String[] SOUND_EFFECT_FILES = new String[] { 131 "Effect_Tick.ogg", 132 "KeypressStandard.ogg", 133 "KeypressSpacebar.ogg", 134 "KeypressDelete.ogg", 135 "KeypressReturn.ogg" 136 }; 137 138 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to 139 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect 140 * uses soundpool (second column) */ 141 private int[][] SOUND_EFFECT_FILES_MAP = new int[][] { 142 {0, -1}, // FX_KEY_CLICK 143 {0, -1}, // FX_FOCUS_NAVIGATION_UP 144 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN 145 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT 146 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT 147 {1, -1}, // FX_KEYPRESS_STANDARD 148 {2, -1}, // FX_KEYPRESS_SPACEBAR 149 {3, -1}, // FX_FOCUS_DELETE 150 {4, -1} // FX_FOCUS_RETURN 151 }; 152 153 /** @hide Maximum volume index values for audio streams */ 154 private int[] MAX_STREAM_VOLUME = new int[] { 155 5, // STREAM_VOICE_CALL 156 7, // STREAM_SYSTEM 157 7, // STREAM_RING 158 15, // STREAM_MUSIC 159 7, // STREAM_ALARM 160 7, // STREAM_NOTIFICATION 161 15, // STREAM_BLUETOOTH_SCO 162 7, // STREAM_SYSTEM_ENFORCED 163 15, // STREAM_DTMF 164 15 // STREAM_TTS 165 }; 166 /* STREAM_VOLUME_ALIAS[] indicates for each stream if it uses the volume settings 167 * of another stream: This avoids multiplying the volume settings for hidden 168 * stream types that follow other stream behavior for volume settings 169 * NOTE: do not create loops in aliases! */ 170 private int[] STREAM_VOLUME_ALIAS = new int[] { 171 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL 172 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM 173 AudioSystem.STREAM_RING, // STREAM_RING 174 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC 175 AudioSystem.STREAM_ALARM, // STREAM_ALARM 176 AudioSystem.STREAM_NOTIFICATION, // STREAM_NOTIFICATION 177 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO 178 AudioSystem.STREAM_SYSTEM, // STREAM_SYSTEM_ENFORCED 179 AudioSystem.STREAM_VOICE_CALL, // STREAM_DTMF 180 AudioSystem.STREAM_MUSIC // STREAM_TTS 181 }; 182 183 private AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() { 184 public void onError(int error) { 185 switch (error) { 186 case AudioSystem.AUDIO_STATUS_SERVER_DIED: 187 if (mMediaServerOk) { 188 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0, 189 null, 1500); 190 mMediaServerOk = false; 191 } 192 break; 193 case AudioSystem.AUDIO_STATUS_OK: 194 if (!mMediaServerOk) { 195 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SHARED_MSG, SENDMSG_NOOP, 0, 0, 196 null, 0); 197 mMediaServerOk = true; 198 } 199 break; 200 default: 201 break; 202 } 203 } 204 }; 205 206 /** 207 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL}, 208 * {@link AudioManager#RINGER_MODE_SILENT}, or 209 * {@link AudioManager#RINGER_MODE_VIBRATE}. 210 */ 211 private int mRingerMode; 212 213 /** @see System#MODE_RINGER_STREAMS_AFFECTED */ 214 private int mRingerModeAffectedStreams; 215 216 // Streams currently muted by ringer mode 217 private int mRingerModeMutedStreams; 218 219 /** @see System#MUTE_STREAMS_AFFECTED */ 220 private int mMuteAffectedStreams; 221 222 /** 223 * Has multiple bits per vibrate type to indicate the type's vibrate 224 * setting. See {@link #setVibrateSetting(int, int)}. 225 * <p> 226 * NOTE: This is not the final decision of whether vibrate is on/off for the 227 * type since it depends on the ringer mode. See {@link #shouldVibrate(int)}. 228 */ 229 private int mVibrateSetting; 230 231 /** @see System#NOTIFICATIONS_USE_RING_VOLUME */ 232 private int mNotificationsUseRingVolume; 233 234 // Broadcast receiver for device connections intent broadcasts 235 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver(); 236 237 // Broadcast receiver for media button broadcasts (separate from mReceiver to 238 // independently change its priority) 239 private final BroadcastReceiver mMediaButtonReceiver = new MediaButtonBroadcastReceiver(); 240 241 // Devices currently connected 242 private HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>(); 243 244 // Forced device usage for communications 245 private int mForcedUseForComm; 246 247 // List of binder death handlers for setMode() client processes. 248 // The last process to have called setMode() is at the top of the list. 249 private ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>(); 250 251 // List of clients having issued a SCO start request 252 private ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>(); 253 254 // BluetoothHeadset API to control SCO connection 255 private BluetoothHeadset mBluetoothHeadset; 256 257 // Bluetooth headset connection state 258 private boolean mBluetoothHeadsetConnected; 259 260 /////////////////////////////////////////////////////////////////////////// 261 // Construction 262 /////////////////////////////////////////////////////////////////////////// 263 264 /** @hide */ 265 public AudioService(Context context) { 266 mContext = context; 267 mContentResolver = context.getContentResolver(); 268 269 // Intialized volume 270 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt( 271 "ro.config.vc_call_vol_steps", 272 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]); 273 274 mVolumePanel = new VolumePanel(context, this); 275 mSettingsObserver = new SettingsObserver(); 276 mForcedUseForComm = AudioSystem.FORCE_NONE; 277 createAudioSystemThread(); 278 readPersistedSettings(); 279 createStreamStates(); 280 // Call setMode() to initialize mSetModeDeathHandlers 281 mMode = AudioSystem.MODE_INVALID; 282 setMode(AudioSystem.MODE_NORMAL, null); 283 mMediaServerOk = true; 284 285 // Call setRingerModeInt() to apply correct mute 286 // state on streams affected by ringer mode. 287 mRingerModeMutedStreams = 0; 288 setRingerModeInt(getRingerMode(), false); 289 290 AudioSystem.setErrorCallback(mAudioSystemCallback); 291 loadSoundEffects(); 292 293 mBluetoothHeadsetConnected = false; 294 mBluetoothHeadset = new BluetoothHeadset(context, 295 mBluetoothHeadsetServiceListener); 296 297 // Register for device connection intent broadcasts. 298 IntentFilter intentFilter = 299 new IntentFilter(Intent.ACTION_HEADSET_PLUG); 300 intentFilter.addAction(BluetoothA2dp.ACTION_SINK_STATE_CHANGED); 301 intentFilter.addAction(BluetoothHeadset.ACTION_STATE_CHANGED); 302 intentFilter.addAction(Intent.ACTION_DOCK_EVENT); 303 intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 304 context.registerReceiver(mReceiver, intentFilter); 305 306 // Register for media button intent broadcasts. 307 intentFilter = new IntentFilter(Intent.ACTION_MEDIA_BUTTON); 308 intentFilter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY); 309 context.registerReceiver(mMediaButtonReceiver, intentFilter); 310 311 // Register for phone state monitoring 312 TelephonyManager tmgr = (TelephonyManager) 313 context.getSystemService(Context.TELEPHONY_SERVICE); 314 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 315 } 316 317 private void createAudioSystemThread() { 318 mAudioSystemThread = new AudioSystemThread(); 319 mAudioSystemThread.start(); 320 waitForAudioHandlerCreation(); 321 } 322 323 /** Waits for the volume handler to be created by the other thread. */ 324 private void waitForAudioHandlerCreation() { 325 synchronized(this) { 326 while (mAudioHandler == null) { 327 try { 328 // Wait for mAudioHandler to be set by the other thread 329 wait(); 330 } catch (InterruptedException e) { 331 Log.e(TAG, "Interrupted while waiting on volume handler."); 332 } 333 } 334 } 335 } 336 337 private void createStreamStates() { 338 int numStreamTypes = AudioSystem.getNumStreamTypes(); 339 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes]; 340 341 for (int i = 0; i < numStreamTypes; i++) { 342 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[i]], i); 343 } 344 345 // Correct stream index values for streams with aliases 346 for (int i = 0; i < numStreamTypes; i++) { 347 if (STREAM_VOLUME_ALIAS[i] != i) { 348 int index = rescaleIndex(streams[i].mIndex, STREAM_VOLUME_ALIAS[i], i); 349 streams[i].mIndex = streams[i].getValidIndex(index); 350 setStreamVolumeIndex(i, index); 351 index = rescaleIndex(streams[i].mLastAudibleIndex, STREAM_VOLUME_ALIAS[i], i); 352 streams[i].mLastAudibleIndex = streams[i].getValidIndex(index); 353 } 354 } 355 } 356 357 private void readPersistedSettings() { 358 final ContentResolver cr = mContentResolver; 359 360 mRingerMode = System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL); 361 362 mVibrateSetting = System.getInt(cr, System.VIBRATE_ON, 0); 363 364 mRingerModeAffectedStreams = Settings.System.getInt(cr, 365 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 366 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)| 367 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED))); 368 369 mMuteAffectedStreams = System.getInt(cr, 370 System.MUTE_STREAMS_AFFECTED, 371 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM))); 372 373 mNotificationsUseRingVolume = System.getInt(cr, 374 Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1); 375 376 if (mNotificationsUseRingVolume == 1) { 377 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING; 378 } 379 // Each stream will read its own persisted settings 380 381 // Broadcast the sticky intent 382 broadcastRingerMode(); 383 384 // Broadcast vibrate settings 385 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); 386 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); 387 } 388 389 private void setStreamVolumeIndex(int stream, int index) { 390 AudioSystem.setStreamVolumeIndex(stream, (index + 5)/10); 391 } 392 393 private int rescaleIndex(int index, int srcStream, int dstStream) { 394 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex(); 395 } 396 397 /////////////////////////////////////////////////////////////////////////// 398 // IPC methods 399 /////////////////////////////////////////////////////////////////////////// 400 401 /** @see AudioManager#adjustVolume(int, int) */ 402 public void adjustVolume(int direction, int flags) { 403 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags); 404 } 405 406 /** @see AudioManager#adjustVolume(int, int, int) */ 407 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { 408 409 int streamType = getActiveStreamType(suggestedStreamType); 410 411 // Don't play sound on other streams 412 if (streamType != AudioSystem.STREAM_RING && (flags & AudioManager.FLAG_PLAY_SOUND) != 0) { 413 flags &= ~AudioManager.FLAG_PLAY_SOUND; 414 } 415 416 adjustStreamVolume(streamType, direction, flags); 417 } 418 419 /** @see AudioManager#adjustStreamVolume(int, int, int) */ 420 public void adjustStreamVolume(int streamType, int direction, int flags) { 421 ensureValidDirection(direction); 422 ensureValidStreamType(streamType); 423 424 425 VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]]; 426 final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex; 427 boolean adjustVolume = true; 428 429 // If either the client forces allowing ringer modes for this adjustment, 430 // or the stream type is one that is affected by ringer modes 431 if ((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0 432 || streamType == AudioSystem.STREAM_RING) { 433 // Check if the ringer mode changes with this volume adjustment. If 434 // it does, it will handle adjusting the volume, so we won't below 435 adjustVolume = checkForRingerModeChange(oldIndex, direction); 436 } 437 438 // If stream is muted, adjust last audible index only 439 int index; 440 if (streamState.muteCount() != 0) { 441 if (adjustVolume) { 442 streamState.adjustLastAudibleIndex(direction); 443 // Post a persist volume msg 444 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType, 445 SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY); 446 } 447 index = streamState.mLastAudibleIndex; 448 } else { 449 if (adjustVolume && streamState.adjustIndex(direction)) { 450 // Post message to set system volume (it in turn will post a message 451 // to persist). Do not change volume if stream is muted. 452 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, STREAM_VOLUME_ALIAS[streamType], SENDMSG_NOOP, 0, 0, 453 streamState, 0); 454 } 455 index = streamState.mIndex; 456 } 457 // UI 458 mVolumePanel.postVolumeChanged(streamType, flags); 459 // Broadcast Intent 460 sendVolumeUpdate(streamType, oldIndex, index); 461 } 462 463 /** @see AudioManager#setStreamVolume(int, int, int) */ 464 public void setStreamVolume(int streamType, int index, int flags) { 465 ensureValidStreamType(streamType); 466 VolumeStreamState streamState = mStreamStates[STREAM_VOLUME_ALIAS[streamType]]; 467 468 final int oldIndex = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex; 469 470 index = rescaleIndex(index * 10, streamType, STREAM_VOLUME_ALIAS[streamType]); 471 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, false, true); 472 473 index = (streamState.muteCount() != 0) ? streamState.mLastAudibleIndex : streamState.mIndex; 474 475 // UI, etc. 476 mVolumePanel.postVolumeChanged(streamType, flags); 477 // Broadcast Intent 478 sendVolumeUpdate(streamType, oldIndex, index); 479 } 480 481 private void sendVolumeUpdate(int streamType, int oldIndex, int index) { 482 oldIndex = (oldIndex + 5) / 10; 483 index = (index + 5) / 10; 484 485 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION); 486 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType); 487 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index); 488 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex); 489 490 mContext.sendBroadcast(intent); 491 } 492 493 /** 494 * Sets the stream state's index, and posts a message to set system volume. 495 * This will not call out to the UI. Assumes a valid stream type. 496 * 497 * @param streamType Type of the stream 498 * @param index Desired volume index of the stream 499 * @param force If true, set the volume even if the desired volume is same 500 * as the current volume. 501 * @param lastAudible If true, stores new index as last audible one 502 */ 503 private void setStreamVolumeInt(int streamType, int index, boolean force, boolean lastAudible) { 504 VolumeStreamState streamState = mStreamStates[streamType]; 505 506 // If stream is muted, set last audible index only 507 if (streamState.muteCount() != 0) { 508 // Do not allow last audible index to be 0 509 if (index != 0) { 510 streamState.setLastAudibleIndex(index); 511 // Post a persist volume msg 512 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamType, 513 SENDMSG_REPLACE, 0, 1, streamState, PERSIST_DELAY); 514 } 515 } else { 516 if (streamState.setIndex(index, lastAudible) || force) { 517 // Post message to set system volume (it in turn will post a message 518 // to persist). 519 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, streamType, SENDMSG_NOOP, 0, 0, 520 streamState, 0); 521 } 522 } 523 } 524 525 /** @see AudioManager#setStreamSolo(int, boolean) */ 526 public void setStreamSolo(int streamType, boolean state, IBinder cb) { 527 for (int stream = 0; stream < mStreamStates.length; stream++) { 528 if (!isStreamAffectedByMute(stream) || stream == streamType) continue; 529 // Bring back last audible volume 530 mStreamStates[stream].mute(cb, state); 531 } 532 } 533 534 /** @see AudioManager#setStreamMute(int, boolean) */ 535 public void setStreamMute(int streamType, boolean state, IBinder cb) { 536 if (isStreamAffectedByMute(streamType)) { 537 mStreamStates[streamType].mute(cb, state); 538 } 539 } 540 541 /** @see AudioManager#getStreamVolume(int) */ 542 public int getStreamVolume(int streamType) { 543 ensureValidStreamType(streamType); 544 return (mStreamStates[streamType].mIndex + 5) / 10; 545 } 546 547 /** @see AudioManager#getStreamMaxVolume(int) */ 548 public int getStreamMaxVolume(int streamType) { 549 ensureValidStreamType(streamType); 550 return (mStreamStates[streamType].getMaxIndex() + 5) / 10; 551 } 552 553 /** @see AudioManager#getRingerMode() */ 554 public int getRingerMode() { 555 return mRingerMode; 556 } 557 558 /** @see AudioManager#setRingerMode(int) */ 559 public void setRingerMode(int ringerMode) { 560 synchronized (mSettingsLock) { 561 if (ringerMode != mRingerMode) { 562 setRingerModeInt(ringerMode, true); 563 // Send sticky broadcast 564 broadcastRingerMode(); 565 } 566 } 567 } 568 569 private void setRingerModeInt(int ringerMode, boolean persist) { 570 mRingerMode = ringerMode; 571 572 // Mute stream if not previously muted by ringer mode and ringer mode 573 // is not RINGER_MODE_NORMAL and stream is affected by ringer mode. 574 // Unmute stream if previously muted by ringer mode and ringer mode 575 // is RINGER_MODE_NORMAL or stream is not affected by ringer mode. 576 int numStreamTypes = AudioSystem.getNumStreamTypes(); 577 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 578 if (isStreamMutedByRingerMode(streamType)) { 579 if (!isStreamAffectedByRingerMode(streamType) || 580 mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 581 mStreamStates[streamType].mute(null, false); 582 mRingerModeMutedStreams &= ~(1 << streamType); 583 } 584 } else { 585 if (isStreamAffectedByRingerMode(streamType) && 586 mRingerMode != AudioManager.RINGER_MODE_NORMAL) { 587 mStreamStates[streamType].mute(null, true); 588 mRingerModeMutedStreams |= (1 << streamType); 589 } 590 } 591 } 592 593 // Post a persist ringer mode msg 594 if (persist) { 595 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, SHARED_MSG, 596 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY); 597 } 598 } 599 600 /** @see AudioManager#shouldVibrate(int) */ 601 public boolean shouldVibrate(int vibrateType) { 602 603 switch (getVibrateSetting(vibrateType)) { 604 605 case AudioManager.VIBRATE_SETTING_ON: 606 return mRingerMode != AudioManager.RINGER_MODE_SILENT; 607 608 case AudioManager.VIBRATE_SETTING_ONLY_SILENT: 609 return mRingerMode == AudioManager.RINGER_MODE_VIBRATE; 610 611 case AudioManager.VIBRATE_SETTING_OFF: 612 // return false, even for incoming calls 613 return false; 614 615 default: 616 return false; 617 } 618 } 619 620 /** @see AudioManager#getVibrateSetting(int) */ 621 public int getVibrateSetting(int vibrateType) { 622 return (mVibrateSetting >> (vibrateType * 2)) & 3; 623 } 624 625 /** @see AudioManager#setVibrateSetting(int, int) */ 626 public void setVibrateSetting(int vibrateType, int vibrateSetting) { 627 628 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting); 629 630 // Broadcast change 631 broadcastVibrateSetting(vibrateType); 632 633 // Post message to set ringer mode (it in turn will post a message 634 // to persist) 635 sendMsg(mAudioHandler, MSG_PERSIST_VIBRATE_SETTING, SHARED_MSG, SENDMSG_NOOP, 0, 0, 636 null, 0); 637 } 638 639 /** 640 * @see #setVibrateSetting(int, int) 641 */ 642 public static int getValueForVibrateSetting(int existingValue, int vibrateType, 643 int vibrateSetting) { 644 645 // First clear the existing setting. Each vibrate type has two bits in 646 // the value. Note '3' is '11' in binary. 647 existingValue &= ~(3 << (vibrateType * 2)); 648 649 // Set into the old value 650 existingValue |= (vibrateSetting & 3) << (vibrateType * 2); 651 652 return existingValue; 653 } 654 655 private class SetModeDeathHandler implements IBinder.DeathRecipient { 656 private IBinder mCb; // To be notified of client's death 657 private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client 658 659 SetModeDeathHandler(IBinder cb) { 660 mCb = cb; 661 } 662 663 public void binderDied() { 664 synchronized(mSetModeDeathHandlers) { 665 Log.w(TAG, "setMode() client died"); 666 int index = mSetModeDeathHandlers.indexOf(this); 667 if (index < 0) { 668 Log.w(TAG, "unregistered setMode() client died"); 669 } else { 670 mSetModeDeathHandlers.remove(this); 671 // If dead client was a the top of client list, 672 // apply next mode in the stack 673 if (index == 0) { 674 // mSetModeDeathHandlers is never empty as the initial entry 675 // created when AudioService starts is never removed 676 SetModeDeathHandler hdlr = mSetModeDeathHandlers.get(0); 677 int mode = hdlr.getMode(); 678 if (AudioService.this.mMode != mode) { 679 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { 680 AudioService.this.mMode = mode; 681 } 682 } 683 } 684 } 685 } 686 } 687 688 public void setMode(int mode) { 689 mMode = mode; 690 } 691 692 public int getMode() { 693 return mMode; 694 } 695 696 public IBinder getBinder() { 697 return mCb; 698 } 699 } 700 701 /** @see AudioManager#setMode(int) */ 702 public void setMode(int mode, IBinder cb) { 703 if (!checkAudioSettingsPermission("setMode()")) { 704 return; 705 } 706 707 if (mode < AudioSystem.MODE_CURRENT || mode > AudioSystem.MODE_IN_CALL) { 708 return; 709 } 710 711 synchronized (mSettingsLock) { 712 if (mode == AudioSystem.MODE_CURRENT) { 713 mode = mMode; 714 } 715 if (mode != mMode) { 716 if (AudioSystem.setPhoneState(mode) == AudioSystem.AUDIO_STATUS_OK) { 717 mMode = mode; 718 719 synchronized(mSetModeDeathHandlers) { 720 SetModeDeathHandler hdlr = null; 721 Iterator iter = mSetModeDeathHandlers.iterator(); 722 while (iter.hasNext()) { 723 SetModeDeathHandler h = (SetModeDeathHandler)iter.next(); 724 if (h.getBinder() == cb) { 725 hdlr = h; 726 // Remove from client list so that it is re-inserted at top of list 727 iter.remove(); 728 break; 729 } 730 } 731 if (hdlr == null) { 732 hdlr = new SetModeDeathHandler(cb); 733 // cb is null when setMode() is called by AudioService constructor 734 if (cb != null) { 735 // Register for client death notification 736 try { 737 cb.linkToDeath(hdlr, 0); 738 } catch (RemoteException e) { 739 // Client has died! 740 Log.w(TAG, "setMode() could not link to "+cb+" binder death"); 741 } 742 } 743 } 744 // Last client to call setMode() is always at top of client list 745 // as required by SetModeDeathHandler.binderDied() 746 mSetModeDeathHandlers.add(0, hdlr); 747 hdlr.setMode(mode); 748 } 749 750 if (mode != AudioSystem.MODE_NORMAL) { 751 clearAllScoClients(); 752 } 753 } 754 } 755 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); 756 int index = mStreamStates[STREAM_VOLUME_ALIAS[streamType]].mIndex; 757 setStreamVolumeInt(STREAM_VOLUME_ALIAS[streamType], index, true, false); 758 } 759 } 760 761 /** @see AudioManager#getMode() */ 762 public int getMode() { 763 return mMode; 764 } 765 766 /** @see AudioManager#playSoundEffect(int) */ 767 public void playSoundEffect(int effectType) { 768 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP, 769 effectType, -1, null, 0); 770 } 771 772 /** @see AudioManager#playSoundEffect(int, float) */ 773 public void playSoundEffectVolume(int effectType, float volume) { 774 loadSoundEffects(); 775 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SHARED_MSG, SENDMSG_NOOP, 776 effectType, (int) (volume * 1000), null, 0); 777 } 778 779 /** 780 * Loads samples into the soundpool. 781 * This method must be called at when sound effects are enabled 782 */ 783 public boolean loadSoundEffects() { 784 synchronized (mSoundEffectsLock) { 785 if (mSoundPool != null) { 786 return true; 787 } 788 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0); 789 if (mSoundPool == null) { 790 return false; 791 } 792 /* 793 * poolId table: The value -1 in this table indicates that corresponding 794 * file (same index in SOUND_EFFECT_FILES[] has not been loaded. 795 * Once loaded, the value in poolId is the sample ID and the same 796 * sample can be reused for another effect using the same file. 797 */ 798 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 799 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 800 poolId[fileIdx] = -1; 801 } 802 /* 803 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded. 804 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0: 805 * this indicates we have a valid sample loaded for this effect. 806 */ 807 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 808 // Do not load sample if this effect uses the MediaPlayer 809 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) { 810 continue; 811 } 812 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) { 813 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]]; 814 int sampleId = mSoundPool.load(filePath, 0); 815 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId; 816 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId; 817 if (sampleId <= 0) { 818 Log.w(TAG, "Soundpool could not load file: "+filePath); 819 } 820 } else { 821 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]]; 822 } 823 } 824 } 825 826 return true; 827 } 828 829 /** 830 * Unloads samples from the sound pool. 831 * This method can be called to free some memory when 832 * sound effects are disabled. 833 */ 834 public void unloadSoundEffects() { 835 synchronized (mSoundEffectsLock) { 836 if (mSoundPool == null) { 837 return; 838 } 839 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 840 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 841 poolId[fileIdx] = 0; 842 } 843 844 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 845 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) { 846 continue; 847 } 848 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) { 849 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]); 850 SOUND_EFFECT_FILES_MAP[effect][1] = -1; 851 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1; 852 } 853 } 854 mSoundPool = null; 855 } 856 } 857 858 /** @see AudioManager#reloadAudioSettings() */ 859 public void reloadAudioSettings() { 860 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings 861 readPersistedSettings(); 862 863 // restore volume settings 864 int numStreamTypes = AudioSystem.getNumStreamTypes(); 865 for (int streamType = 0; streamType < numStreamTypes; streamType++) { 866 VolumeStreamState streamState = mStreamStates[streamType]; 867 868 String settingName = System.VOLUME_SETTINGS[STREAM_VOLUME_ALIAS[streamType]]; 869 String lastAudibleSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; 870 int index = Settings.System.getInt(mContentResolver, 871 settingName, 872 AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 873 if (STREAM_VOLUME_ALIAS[streamType] != streamType) { 874 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType); 875 } else { 876 index *= 10; 877 } 878 streamState.mIndex = streamState.getValidIndex(index); 879 880 index = (index + 5) / 10; 881 index = Settings.System.getInt(mContentResolver, 882 lastAudibleSettingName, 883 (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 884 if (STREAM_VOLUME_ALIAS[streamType] != streamType) { 885 index = rescaleIndex(index * 10, STREAM_VOLUME_ALIAS[streamType], streamType); 886 } else { 887 index *= 10; 888 } 889 streamState.mLastAudibleIndex = streamState.getValidIndex(index); 890 891 // unmute stream that was muted but is not affect by mute anymore 892 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) { 893 int size = streamState.mDeathHandlers.size(); 894 for (int i = 0; i < size; i++) { 895 streamState.mDeathHandlers.get(i).mMuteCount = 1; 896 streamState.mDeathHandlers.get(i).mute(false); 897 } 898 } 899 // apply stream volume 900 if (streamState.muteCount() == 0) { 901 setStreamVolumeIndex(streamType, streamState.mIndex); 902 } 903 } 904 905 // apply new ringer mode 906 setRingerModeInt(getRingerMode(), false); 907 } 908 909 /** @see AudioManager#setSpeakerphoneOn() */ 910 public void setSpeakerphoneOn(boolean on){ 911 if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { 912 return; 913 } 914 if (on) { 915 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_SPEAKER); 916 mForcedUseForComm = AudioSystem.FORCE_SPEAKER; 917 } else { 918 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE); 919 mForcedUseForComm = AudioSystem.FORCE_NONE; 920 } 921 } 922 923 /** @see AudioManager#isSpeakerphoneOn() */ 924 public boolean isSpeakerphoneOn() { 925 if (mForcedUseForComm == AudioSystem.FORCE_SPEAKER) { 926 return true; 927 } else { 928 return false; 929 } 930 } 931 932 /** @see AudioManager#setBluetoothScoOn() */ 933 public void setBluetoothScoOn(boolean on){ 934 if (!checkAudioSettingsPermission("setBluetoothScoOn()")) { 935 return; 936 } 937 if (on) { 938 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_BT_SCO); 939 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_BT_SCO); 940 mForcedUseForComm = AudioSystem.FORCE_BT_SCO; 941 } else { 942 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, AudioSystem.FORCE_NONE); 943 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, AudioSystem.FORCE_NONE); 944 mForcedUseForComm = AudioSystem.FORCE_NONE; 945 } 946 } 947 948 /** @see AudioManager#isBluetoothScoOn() */ 949 public boolean isBluetoothScoOn() { 950 if (mForcedUseForComm == AudioSystem.FORCE_BT_SCO) { 951 return true; 952 } else { 953 return false; 954 } 955 } 956 957 /** @see AudioManager#startBluetoothSco() */ 958 public void startBluetoothSco(IBinder cb){ 959 if (!checkAudioSettingsPermission("startBluetoothSco()")) { 960 return; 961 } 962 ScoClient client = getScoClient(cb); 963 client.incCount(); 964 } 965 966 /** @see AudioManager#stopBluetoothSco() */ 967 public void stopBluetoothSco(IBinder cb){ 968 if (!checkAudioSettingsPermission("stopBluetoothSco()")) { 969 return; 970 } 971 ScoClient client = getScoClient(cb); 972 client.decCount(); 973 } 974 975 private class ScoClient implements IBinder.DeathRecipient { 976 private IBinder mCb; // To be notified of client's death 977 private int mStartcount; // number of SCO connections started by this client 978 979 ScoClient(IBinder cb) { 980 mCb = cb; 981 mStartcount = 0; 982 } 983 984 public void binderDied() { 985 synchronized(mScoClients) { 986 Log.w(TAG, "SCO client died"); 987 int index = mScoClients.indexOf(this); 988 if (index < 0) { 989 Log.w(TAG, "unregistered SCO client died"); 990 } else { 991 clearCount(true); 992 mScoClients.remove(this); 993 } 994 } 995 } 996 997 public void incCount() { 998 synchronized(mScoClients) { 999 requestScoState(BluetoothHeadset.AUDIO_STATE_CONNECTED); 1000 if (mStartcount == 0) { 1001 try { 1002 mCb.linkToDeath(this, 0); 1003 } catch (RemoteException e) { 1004 // client has already died! 1005 Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death"); 1006 } 1007 } 1008 mStartcount++; 1009 } 1010 } 1011 1012 public void decCount() { 1013 synchronized(mScoClients) { 1014 if (mStartcount == 0) { 1015 Log.w(TAG, "ScoClient.decCount() already 0"); 1016 } else { 1017 mStartcount--; 1018 if (mStartcount == 0) { 1019 mCb.unlinkToDeath(this, 0); 1020 } 1021 requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED); 1022 } 1023 } 1024 } 1025 1026 public void clearCount(boolean stopSco) { 1027 synchronized(mScoClients) { 1028 mStartcount = 0; 1029 mCb.unlinkToDeath(this, 0); 1030 if (stopSco) { 1031 requestScoState(BluetoothHeadset.AUDIO_STATE_DISCONNECTED); 1032 } 1033 } 1034 } 1035 1036 public int getCount() { 1037 return mStartcount; 1038 } 1039 1040 public IBinder getBinder() { 1041 return mCb; 1042 } 1043 1044 public int totalCount() { 1045 synchronized(mScoClients) { 1046 int count = 0; 1047 int size = mScoClients.size(); 1048 for (int i = 0; i < size; i++) { 1049 count += mScoClients.get(i).getCount(); 1050 } 1051 return count; 1052 } 1053 } 1054 1055 private void requestScoState(int state) { 1056 if (totalCount() == 0 && 1057 mBluetoothHeadsetConnected && 1058 AudioService.this.mMode == AudioSystem.MODE_NORMAL) { 1059 if (state == BluetoothHeadset.AUDIO_STATE_CONNECTED) { 1060 mBluetoothHeadset.startVoiceRecognition(); 1061 } else { 1062 mBluetoothHeadset.stopVoiceRecognition(); 1063 } 1064 } 1065 } 1066 } 1067 1068 public ScoClient getScoClient(IBinder cb) { 1069 synchronized(mScoClients) { 1070 ScoClient client; 1071 int size = mScoClients.size(); 1072 for (int i = 0; i < size; i++) { 1073 client = mScoClients.get(i); 1074 if (client.getBinder() == cb) 1075 return client; 1076 } 1077 client = new ScoClient(cb); 1078 mScoClients.add(client); 1079 return client; 1080 } 1081 } 1082 1083 public void clearAllScoClients() { 1084 synchronized(mScoClients) { 1085 int size = mScoClients.size(); 1086 for (int i = 0; i < size; i++) { 1087 mScoClients.get(i).clearCount(false); 1088 } 1089 } 1090 } 1091 1092 private BluetoothHeadset.ServiceListener mBluetoothHeadsetServiceListener = 1093 new BluetoothHeadset.ServiceListener() { 1094 public void onServiceConnected() { 1095 if (mBluetoothHeadset != null) { 1096 BluetoothDevice device = mBluetoothHeadset.getCurrentHeadset(); 1097 if (mBluetoothHeadset.getState(device) == BluetoothHeadset.STATE_CONNECTED) { 1098 mBluetoothHeadsetConnected = true; 1099 } 1100 } 1101 } 1102 public void onServiceDisconnected() { 1103 if (mBluetoothHeadset != null) { 1104 BluetoothDevice device = mBluetoothHeadset.getCurrentHeadset(); 1105 if (mBluetoothHeadset.getState(device) == BluetoothHeadset.STATE_DISCONNECTED) { 1106 mBluetoothHeadsetConnected = false; 1107 clearAllScoClients(); 1108 } 1109 } 1110 } 1111 }; 1112 1113 /////////////////////////////////////////////////////////////////////////// 1114 // Internal methods 1115 /////////////////////////////////////////////////////////////////////////// 1116 1117 /** 1118 * Checks if the adjustment should change ringer mode instead of just 1119 * adjusting volume. If so, this will set the proper ringer mode and volume 1120 * indices on the stream states. 1121 */ 1122 private boolean checkForRingerModeChange(int oldIndex, int direction) { 1123 boolean adjustVolumeIndex = true; 1124 int newRingerMode = mRingerMode; 1125 1126 if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 1127 // audible mode, at the bottom of the scale 1128 if (direction == AudioManager.ADJUST_LOWER 1129 && (oldIndex + 5) / 10 == 1) { 1130 // "silent mode", but which one? 1131 newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1 1132 ? AudioManager.RINGER_MODE_VIBRATE 1133 : AudioManager.RINGER_MODE_SILENT; 1134 } 1135 } else { 1136 if (direction == AudioManager.ADJUST_RAISE) { 1137 // exiting silent mode 1138 newRingerMode = AudioManager.RINGER_MODE_NORMAL; 1139 } else { 1140 // prevent last audible index to reach 0 1141 adjustVolumeIndex = false; 1142 } 1143 } 1144 1145 if (newRingerMode != mRingerMode) { 1146 setRingerMode(newRingerMode); 1147 1148 /* 1149 * If we are changing ringer modes, do not increment/decrement the 1150 * volume index. Instead, the handler for the message above will 1151 * take care of changing the index. 1152 */ 1153 adjustVolumeIndex = false; 1154 } 1155 1156 return adjustVolumeIndex; 1157 } 1158 1159 public boolean isStreamAffectedByRingerMode(int streamType) { 1160 return (mRingerModeAffectedStreams & (1 << streamType)) != 0; 1161 } 1162 1163 private boolean isStreamMutedByRingerMode(int streamType) { 1164 return (mRingerModeMutedStreams & (1 << streamType)) != 0; 1165 } 1166 1167 public boolean isStreamAffectedByMute(int streamType) { 1168 return (mMuteAffectedStreams & (1 << streamType)) != 0; 1169 } 1170 1171 private void ensureValidDirection(int direction) { 1172 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) { 1173 throw new IllegalArgumentException("Bad direction " + direction); 1174 } 1175 } 1176 1177 private void ensureValidStreamType(int streamType) { 1178 if (streamType < 0 || streamType >= mStreamStates.length) { 1179 throw new IllegalArgumentException("Bad stream type " + streamType); 1180 } 1181 } 1182 1183 private int getActiveStreamType(int suggestedStreamType) { 1184 boolean isOffhook = false; 1185 try { 1186 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 1187 if (phone != null) isOffhook = phone.isOffhook(); 1188 } catch (RemoteException e) { 1189 Log.w(TAG, "Couldn't connect to phone service", e); 1190 } 1191 1192 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) { 1193 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); 1194 return AudioSystem.STREAM_BLUETOOTH_SCO; 1195 } else if (isOffhook || AudioSystem.isStreamActive(AudioSystem.STREAM_VOICE_CALL)) { 1196 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); 1197 return AudioSystem.STREAM_VOICE_CALL; 1198 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC)) { 1199 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC..."); 1200 return AudioSystem.STREAM_MUSIC; 1201 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { 1202 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."); 1203 return AudioSystem.STREAM_RING; 1204 } else { 1205 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType); 1206 return suggestedStreamType; 1207 } 1208 } 1209 1210 private void broadcastRingerMode() { 1211 // Send sticky broadcast 1212 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION); 1213 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode); 1214 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1215 | Intent.FLAG_RECEIVER_REPLACE_PENDING); 1216 long origCallerIdentityToken = Binder.clearCallingIdentity(); 1217 mContext.sendStickyBroadcast(broadcast); 1218 Binder.restoreCallingIdentity(origCallerIdentityToken); 1219 } 1220 1221 private void broadcastVibrateSetting(int vibrateType) { 1222 // Send broadcast 1223 if (ActivityManagerNative.isSystemReady()) { 1224 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); 1225 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); 1226 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); 1227 mContext.sendBroadcast(broadcast); 1228 } 1229 } 1230 1231 // Message helper methods 1232 private static int getMsg(int baseMsg, int streamType) { 1233 return (baseMsg & 0xffff) | streamType << 16; 1234 } 1235 1236 private static int getMsgBase(int msg) { 1237 return msg & 0xffff; 1238 } 1239 1240 private static void sendMsg(Handler handler, int baseMsg, int streamType, 1241 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) { 1242 int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType); 1243 1244 if (existingMsgPolicy == SENDMSG_REPLACE) { 1245 handler.removeMessages(msg); 1246 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) { 1247 return; 1248 } 1249 1250 handler 1251 .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay); 1252 } 1253 1254 boolean checkAudioSettingsPermission(String method) { 1255 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS") 1256 == PackageManager.PERMISSION_GRANTED) { 1257 return true; 1258 } 1259 String msg = "Audio Settings Permission Denial: " + method + " from pid=" 1260 + Binder.getCallingPid() 1261 + ", uid=" + Binder.getCallingUid(); 1262 Log.w(TAG, msg); 1263 return false; 1264 } 1265 1266 1267 /////////////////////////////////////////////////////////////////////////// 1268 // Inner classes 1269 /////////////////////////////////////////////////////////////////////////// 1270 1271 public class VolumeStreamState { 1272 private final int mStreamType; 1273 1274 private String mVolumeIndexSettingName; 1275 private String mLastAudibleVolumeIndexSettingName; 1276 private int mIndexMax; 1277 private int mIndex; 1278 private int mLastAudibleIndex; 1279 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death 1280 1281 private VolumeStreamState(String settingName, int streamType) { 1282 1283 setVolumeIndexSettingName(settingName); 1284 1285 mStreamType = streamType; 1286 1287 final ContentResolver cr = mContentResolver; 1288 mIndexMax = MAX_STREAM_VOLUME[streamType]; 1289 mIndex = Settings.System.getInt(cr, 1290 mVolumeIndexSettingName, 1291 AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1292 mLastAudibleIndex = Settings.System.getInt(cr, 1293 mLastAudibleVolumeIndexSettingName, 1294 (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1295 AudioSystem.initStreamVolume(streamType, 0, mIndexMax); 1296 mIndexMax *= 10; 1297 mIndex = getValidIndex(10 * mIndex); 1298 mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex); 1299 setStreamVolumeIndex(streamType, mIndex); 1300 mDeathHandlers = new ArrayList<VolumeDeathHandler>(); 1301 } 1302 1303 public void setVolumeIndexSettingName(String settingName) { 1304 mVolumeIndexSettingName = settingName; 1305 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; 1306 } 1307 1308 public boolean adjustIndex(int deltaIndex) { 1309 return setIndex(mIndex + deltaIndex * 10, true); 1310 } 1311 1312 public boolean setIndex(int index, boolean lastAudible) { 1313 int oldIndex = mIndex; 1314 mIndex = getValidIndex(index); 1315 1316 if (oldIndex != mIndex) { 1317 if (lastAudible) { 1318 mLastAudibleIndex = mIndex; 1319 } 1320 // Apply change to all streams using this one as alias 1321 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1322 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1323 if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) { 1324 mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible); 1325 } 1326 } 1327 return true; 1328 } else { 1329 return false; 1330 } 1331 } 1332 1333 public void setLastAudibleIndex(int index) { 1334 mLastAudibleIndex = getValidIndex(index); 1335 } 1336 1337 public void adjustLastAudibleIndex(int deltaIndex) { 1338 setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10); 1339 } 1340 1341 public int getMaxIndex() { 1342 return mIndexMax; 1343 } 1344 1345 public void mute(IBinder cb, boolean state) { 1346 VolumeDeathHandler handler = getDeathHandler(cb, state); 1347 if (handler == null) { 1348 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType); 1349 return; 1350 } 1351 handler.mute(state); 1352 } 1353 1354 private int getValidIndex(int index) { 1355 if (index < 0) { 1356 return 0; 1357 } else if (index > mIndexMax) { 1358 return mIndexMax; 1359 } 1360 1361 return index; 1362 } 1363 1364 private class VolumeDeathHandler implements IBinder.DeathRecipient { 1365 private IBinder mICallback; // To be notified of client's death 1366 private int mMuteCount; // Number of active mutes for this client 1367 1368 VolumeDeathHandler(IBinder cb) { 1369 mICallback = cb; 1370 } 1371 1372 public void mute(boolean state) { 1373 synchronized(mDeathHandlers) { 1374 if (state) { 1375 if (mMuteCount == 0) { 1376 // Register for client death notification 1377 try { 1378 // mICallback can be 0 if muted by AudioService 1379 if (mICallback != null) { 1380 mICallback.linkToDeath(this, 0); 1381 } 1382 mDeathHandlers.add(this); 1383 // If the stream is not yet muted by any client, set lvel to 0 1384 if (muteCount() == 0) { 1385 setIndex(0, false); 1386 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, 1387 VolumeStreamState.this, 0); 1388 } 1389 } catch (RemoteException e) { 1390 // Client has died! 1391 binderDied(); 1392 mDeathHandlers.notify(); 1393 return; 1394 } 1395 } else { 1396 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client"); 1397 } 1398 mMuteCount++; 1399 } else { 1400 if (mMuteCount == 0) { 1401 Log.e(TAG, "unexpected unmute for stream: "+mStreamType); 1402 } else { 1403 mMuteCount--; 1404 if (mMuteCount == 0) { 1405 // Unregistr from client death notification 1406 mDeathHandlers.remove(this); 1407 // mICallback can be 0 if muted by AudioService 1408 if (mICallback != null) { 1409 mICallback.unlinkToDeath(this, 0); 1410 } 1411 if (muteCount() == 0) { 1412 // If the stream is not muted any more, restore it's volume if 1413 // ringer mode allows it 1414 if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 1415 setIndex(mLastAudibleIndex, false); 1416 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, 1417 VolumeStreamState.this, 0); 1418 } 1419 } 1420 } 1421 } 1422 } 1423 mDeathHandlers.notify(); 1424 } 1425 } 1426 1427 public void binderDied() { 1428 Log.w(TAG, "Volume service client died for stream: "+mStreamType); 1429 if (mMuteCount != 0) { 1430 // Reset all active mute requests from this client. 1431 mMuteCount = 1; 1432 mute(false); 1433 } 1434 } 1435 } 1436 1437 private int muteCount() { 1438 int count = 0; 1439 int size = mDeathHandlers.size(); 1440 for (int i = 0; i < size; i++) { 1441 count += mDeathHandlers.get(i).mMuteCount; 1442 } 1443 return count; 1444 } 1445 1446 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) { 1447 synchronized(mDeathHandlers) { 1448 VolumeDeathHandler handler; 1449 int size = mDeathHandlers.size(); 1450 for (int i = 0; i < size; i++) { 1451 handler = mDeathHandlers.get(i); 1452 if (cb == handler.mICallback) { 1453 return handler; 1454 } 1455 } 1456 // If this is the first mute request for this client, create a new 1457 // client death handler. Otherwise, it is an out of sequence unmute request. 1458 if (state) { 1459 handler = new VolumeDeathHandler(cb); 1460 } else { 1461 Log.w(TAG, "stream was not muted by this client"); 1462 handler = null; 1463 } 1464 return handler; 1465 } 1466 } 1467 } 1468 1469 /** Thread that handles native AudioSystem control. */ 1470 private class AudioSystemThread extends Thread { 1471 AudioSystemThread() { 1472 super("AudioService"); 1473 } 1474 1475 @Override 1476 public void run() { 1477 // Set this thread up so the handler will work on it 1478 Looper.prepare(); 1479 1480 synchronized(AudioService.this) { 1481 mAudioHandler = new AudioHandler(); 1482 1483 // Notify that the handler has been created 1484 AudioService.this.notify(); 1485 } 1486 1487 // Listen for volume change requests that are set by VolumePanel 1488 Looper.loop(); 1489 } 1490 } 1491 1492 /** Handles internal volume messages in separate volume thread. */ 1493 private class AudioHandler extends Handler { 1494 1495 private void setSystemVolume(VolumeStreamState streamState) { 1496 1497 // Adjust volume 1498 setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex); 1499 1500 // Apply change to all streams using this one as alias 1501 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1502 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1503 if (streamType != streamState.mStreamType && 1504 STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) { 1505 setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex); 1506 } 1507 } 1508 1509 // Post a persist volume msg 1510 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType, 1511 SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY); 1512 } 1513 1514 private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) { 1515 if (current) { 1516 System.putInt(mContentResolver, streamState.mVolumeIndexSettingName, 1517 (streamState.mIndex + 5)/ 10); 1518 } 1519 if (lastAudible) { 1520 System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName, 1521 (streamState.mLastAudibleIndex + 5) / 10); 1522 } 1523 } 1524 1525 private void persistRingerMode() { 1526 System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode); 1527 } 1528 1529 private void persistVibrateSetting() { 1530 System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting); 1531 } 1532 1533 private void playSoundEffect(int effectType, int volume) { 1534 synchronized (mSoundEffectsLock) { 1535 if (mSoundPool == null) { 1536 return; 1537 } 1538 float volFloat; 1539 // use STREAM_MUSIC volume attenuated by 3 dB if volume is not specified by caller 1540 if (volume < 0) { 1541 // Same linear to log conversion as in native AudioSystem::linearToLog() (AudioSystem.cpp) 1542 float dBPerStep = (float)((0.5 * 100) / MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]); 1543 int musicVolIndex = (mStreamStates[AudioSystem.STREAM_MUSIC].mIndex + 5) / 10; 1544 float musicVoldB = dBPerStep * (musicVolIndex - MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]); 1545 volFloat = (float)Math.pow(10, (musicVoldB - 3)/20); 1546 } else { 1547 volFloat = (float) volume / 1000.0f; 1548 } 1549 1550 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) { 1551 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f); 1552 } else { 1553 MediaPlayer mediaPlayer = new MediaPlayer(); 1554 if (mediaPlayer != null) { 1555 try { 1556 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]]; 1557 mediaPlayer.setDataSource(filePath); 1558 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM); 1559 mediaPlayer.prepare(); 1560 mediaPlayer.setVolume(volFloat, volFloat); 1561 mediaPlayer.setOnCompletionListener(new OnCompletionListener() { 1562 public void onCompletion(MediaPlayer mp) { 1563 cleanupPlayer(mp); 1564 } 1565 }); 1566 mediaPlayer.setOnErrorListener(new OnErrorListener() { 1567 public boolean onError(MediaPlayer mp, int what, int extra) { 1568 cleanupPlayer(mp); 1569 return true; 1570 } 1571 }); 1572 mediaPlayer.start(); 1573 } catch (IOException ex) { 1574 Log.w(TAG, "MediaPlayer IOException: "+ex); 1575 } catch (IllegalArgumentException ex) { 1576 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex); 1577 } catch (IllegalStateException ex) { 1578 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 1579 } 1580 } 1581 } 1582 } 1583 } 1584 1585 private void cleanupPlayer(MediaPlayer mp) { 1586 if (mp != null) { 1587 try { 1588 mp.stop(); 1589 mp.release(); 1590 } catch (IllegalStateException ex) { 1591 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 1592 } 1593 } 1594 } 1595 1596 @Override 1597 public void handleMessage(Message msg) { 1598 int baseMsgWhat = getMsgBase(msg.what); 1599 1600 switch (baseMsgWhat) { 1601 1602 case MSG_SET_SYSTEM_VOLUME: 1603 setSystemVolume((VolumeStreamState) msg.obj); 1604 break; 1605 1606 case MSG_PERSIST_VOLUME: 1607 persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0)); 1608 break; 1609 1610 case MSG_PERSIST_RINGER_MODE: 1611 persistRingerMode(); 1612 break; 1613 1614 case MSG_PERSIST_VIBRATE_SETTING: 1615 persistVibrateSetting(); 1616 break; 1617 1618 case MSG_MEDIA_SERVER_DIED: 1619 // Force creation of new IAudioflinger interface 1620 if (!mMediaServerOk) { 1621 Log.e(TAG, "Media server died."); 1622 AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC); 1623 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0, 1624 null, 500); 1625 } 1626 break; 1627 1628 case MSG_MEDIA_SERVER_STARTED: 1629 Log.e(TAG, "Media server started."); 1630 // indicate to audio HAL that we start the reconfiguration phase after a media 1631 // server crash 1632 // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server 1633 // process restarts after a crash, not the first time it is started. 1634 AudioSystem.setParameters("restarting=true"); 1635 1636 // Restore device connection states 1637 Set set = mConnectedDevices.entrySet(); 1638 Iterator i = set.iterator(); 1639 while(i.hasNext()){ 1640 Map.Entry device = (Map.Entry)i.next(); 1641 AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(), 1642 AudioSystem.DEVICE_STATE_AVAILABLE, 1643 (String)device.getValue()); 1644 } 1645 1646 // Restore call state 1647 AudioSystem.setPhoneState(mMode); 1648 1649 // Restore forced usage for communcations and record 1650 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm); 1651 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm); 1652 1653 // Restore stream volumes 1654 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1655 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1656 int index; 1657 VolumeStreamState streamState = mStreamStates[streamType]; 1658 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10); 1659 if (streamState.muteCount() == 0) { 1660 index = streamState.mIndex; 1661 } else { 1662 index = 0; 1663 } 1664 setStreamVolumeIndex(streamType, index); 1665 } 1666 1667 // Restore ringer mode 1668 setRingerModeInt(getRingerMode(), false); 1669 1670 // indicate the end of reconfiguration phase to audio HAL 1671 AudioSystem.setParameters("restarting=false"); 1672 break; 1673 1674 case MSG_PLAY_SOUND_EFFECT: 1675 playSoundEffect(msg.arg1, msg.arg2); 1676 break; 1677 1678 case MSG_BTA2DP_DOCK_TIMEOUT: 1679 // msg.obj == address of BTA2DP device 1680 makeA2dpDeviceUnavailableNow( (String) msg.obj ); 1681 break; 1682 } 1683 } 1684 } 1685 1686 private class SettingsObserver extends ContentObserver { 1687 1688 SettingsObserver() { 1689 super(new Handler()); 1690 mContentResolver.registerContentObserver(Settings.System.getUriFor( 1691 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this); 1692 mContentResolver.registerContentObserver(Settings.System.getUriFor( 1693 Settings.System.NOTIFICATIONS_USE_RING_VOLUME), false, this); 1694 } 1695 1696 @Override 1697 public void onChange(boolean selfChange) { 1698 super.onChange(selfChange); 1699 synchronized (mSettingsLock) { 1700 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver, 1701 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 1702 0); 1703 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) { 1704 /* 1705 * Ensure all stream types that should be affected by ringer mode 1706 * are in the proper state. 1707 */ 1708 mRingerModeAffectedStreams = ringerModeAffectedStreams; 1709 setRingerModeInt(getRingerMode(), false); 1710 } 1711 1712 int notificationsUseRingVolume = Settings.System.getInt(mContentResolver, 1713 Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1714 1); 1715 if (notificationsUseRingVolume != mNotificationsUseRingVolume) { 1716 mNotificationsUseRingVolume = notificationsUseRingVolume; 1717 if (mNotificationsUseRingVolume == 1) { 1718 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING; 1719 mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName( 1720 System.VOLUME_SETTINGS[AudioSystem.STREAM_RING]); 1721 } else { 1722 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION; 1723 mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName( 1724 System.VOLUME_SETTINGS[AudioSystem.STREAM_NOTIFICATION]); 1725 // Persist notification volume volume as it was not persisted while aliased to ring volume 1726 // and persist with no delay as there might be registered observers of the persisted 1727 // notification volume. 1728 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION, 1729 SENDMSG_REPLACE, 1, 1, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0); 1730 } 1731 } 1732 } 1733 } 1734 } 1735 1736 private void makeA2dpDeviceAvailable(String address) { 1737 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 1738 AudioSystem.DEVICE_STATE_AVAILABLE, 1739 address); 1740 // Reset A2DP suspend state each time a new sink is connected 1741 AudioSystem.setParameters("A2dpSuspended=false"); 1742 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), 1743 address); 1744 } 1745 1746 private void makeA2dpDeviceUnavailableNow(String address) { 1747 Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); 1748 mContext.sendBroadcast(noisyIntent); 1749 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 1750 AudioSystem.DEVICE_STATE_UNAVAILABLE, 1751 address); 1752 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 1753 } 1754 1755 private void makeA2dpDeviceUnavailableLater(String address) { 1756 // prevent any activity on the A2DP audio output to avoid unwanted 1757 // reconnection of the sink. 1758 AudioSystem.setParameters("A2dpSuspended=true"); 1759 // the device will be made unavailable later, so consider it disconnected right away 1760 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 1761 // send the delayed message to make the device unavailable later 1762 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address); 1763 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS); 1764 1765 } 1766 1767 private void cancelA2dpDeviceTimeout() { 1768 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT); 1769 } 1770 1771 private boolean hasScheduledA2dpDockTimeout() { 1772 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT); 1773 } 1774 1775 /* cache of the address of the last dock the device was connected to */ 1776 private String mDockAddress; 1777 1778 /** 1779 * Receiver for misc intent broadcasts the Phone app cares about. 1780 */ 1781 private class AudioServiceBroadcastReceiver extends BroadcastReceiver { 1782 @Override 1783 public void onReceive(Context context, Intent intent) { 1784 String action = intent.getAction(); 1785 1786 if (action.equals(Intent.ACTION_DOCK_EVENT)) { 1787 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 1788 Intent.EXTRA_DOCK_STATE_UNDOCKED); 1789 int config; 1790 switch (dockState) { 1791 case Intent.EXTRA_DOCK_STATE_DESK: 1792 config = AudioSystem.FORCE_BT_DESK_DOCK; 1793 break; 1794 case Intent.EXTRA_DOCK_STATE_CAR: 1795 config = AudioSystem.FORCE_BT_CAR_DOCK; 1796 break; 1797 case Intent.EXTRA_DOCK_STATE_UNDOCKED: 1798 default: 1799 config = AudioSystem.FORCE_NONE; 1800 } 1801 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); 1802 } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) { 1803 int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 1804 BluetoothA2dp.STATE_DISCONNECTED); 1805 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 1806 String address = btDevice.getAddress(); 1807 boolean isConnected = (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) && 1808 ((String)mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)).equals(address)); 1809 1810 if (isConnected && 1811 state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) { 1812 if (btDevice.isBluetoothDock()) { 1813 if (state == BluetoothA2dp.STATE_DISCONNECTED) { 1814 // introduction of a delay for transient disconnections of docks when 1815 // power is rapidly turned off/on, this message will be canceled if 1816 // we reconnect the dock under a preset delay 1817 makeA2dpDeviceUnavailableLater(address); 1818 // the next time isConnected is evaluated, it will be false for the dock 1819 } 1820 } else { 1821 makeA2dpDeviceUnavailableNow(address); 1822 } 1823 } else if (!isConnected && 1824 (state == BluetoothA2dp.STATE_CONNECTED || 1825 state == BluetoothA2dp.STATE_PLAYING)) { 1826 if (btDevice.isBluetoothDock()) { 1827 // this could be a reconnection after a transient disconnection 1828 cancelA2dpDeviceTimeout(); 1829 mDockAddress = address; 1830 } else { 1831 // this could be a connection of another A2DP device before the timeout of 1832 // a dock: cancel the dock timeout, and make the dock unavailable now 1833 if(hasScheduledA2dpDockTimeout()) { 1834 cancelA2dpDeviceTimeout(); 1835 makeA2dpDeviceUnavailableNow(mDockAddress); 1836 } 1837 } 1838 makeA2dpDeviceAvailable(address); 1839 } 1840 } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) { 1841 int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 1842 BluetoothHeadset.STATE_ERROR); 1843 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO; 1844 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 1845 String address = null; 1846 if (btDevice != null) { 1847 address = btDevice.getAddress(); 1848 BluetoothClass btClass = btDevice.getBluetoothClass(); 1849 if (btClass != null) { 1850 switch (btClass.getDeviceClass()) { 1851 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 1852 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: 1853 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET; 1854 break; 1855 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 1856 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT; 1857 break; 1858 } 1859 } 1860 } 1861 1862 boolean isConnected = (mConnectedDevices.containsKey(device) && 1863 ((String)mConnectedDevices.get(device)).equals(address)); 1864 1865 if (isConnected && state != BluetoothHeadset.STATE_CONNECTED) { 1866 AudioSystem.setDeviceConnectionState(device, 1867 AudioSystem.DEVICE_STATE_UNAVAILABLE, 1868 address); 1869 mConnectedDevices.remove(device); 1870 mBluetoothHeadsetConnected = false; 1871 clearAllScoClients(); 1872 } else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) { 1873 AudioSystem.setDeviceConnectionState(device, 1874 AudioSystem.DEVICE_STATE_AVAILABLE, 1875 address); 1876 mConnectedDevices.put(new Integer(device), address); 1877 mBluetoothHeadsetConnected = true; 1878 } 1879 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { 1880 int state = intent.getIntExtra("state", 0); 1881 int microphone = intent.getIntExtra("microphone", 0); 1882 1883 if (microphone != 0) { 1884 boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 1885 if (state == 0 && isConnected) { 1886 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, 1887 AudioSystem.DEVICE_STATE_UNAVAILABLE, 1888 ""); 1889 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 1890 } else if (state == 1 && !isConnected) { 1891 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, 1892 AudioSystem.DEVICE_STATE_AVAILABLE, 1893 ""); 1894 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), ""); 1895 } 1896 } else { 1897 boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 1898 if (state == 0 && isConnected) { 1899 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, 1900 AudioSystem.DEVICE_STATE_UNAVAILABLE, 1901 ""); 1902 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 1903 } else if (state == 1 && !isConnected) { 1904 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, 1905 AudioSystem.DEVICE_STATE_AVAILABLE, 1906 ""); 1907 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), ""); 1908 } 1909 } 1910 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { 1911 int state = intent.getIntExtra(BluetoothHeadset.EXTRA_AUDIO_STATE, 1912 BluetoothHeadset.STATE_ERROR); 1913 synchronized (mScoClients) { 1914 if (!mScoClients.isEmpty()) { 1915 switch (state) { 1916 case BluetoothHeadset.AUDIO_STATE_CONNECTED: 1917 state = AudioManager.SCO_AUDIO_STATE_CONNECTED; 1918 break; 1919 case BluetoothHeadset.AUDIO_STATE_DISCONNECTED: 1920 state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; 1921 break; 1922 default: 1923 state = AudioManager.SCO_AUDIO_STATE_ERROR; 1924 break; 1925 } 1926 if (state != AudioManager.SCO_AUDIO_STATE_ERROR) { 1927 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 1928 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); 1929 mContext.sendStickyBroadcast(newIntent); 1930 } 1931 } 1932 } 1933 } 1934 } 1935 } 1936 1937 //========================================================================================== 1938 // AudioFocus 1939 //========================================================================================== 1940 1941 /* constant to identify focus stack entry that is used to hold the focus while the phone 1942 * is ringing or during a call 1943 */ 1944 private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls"; 1945 1946 private final static Object mAudioFocusLock = new Object(); 1947 1948 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 1949 @Override 1950 public void onCallStateChanged(int state, String incomingNumber) { 1951 if (state == TelephonyManager.CALL_STATE_RINGING) { 1952 //Log.v(TAG, " CALL_STATE_RINGING"); 1953 int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING); 1954 if (ringVolume > 0) { 1955 requestAudioFocus(AudioManager.STREAM_RING, 1956 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, 1957 null, null /* both allowed to be null only for this clientId */, 1958 IN_VOICE_COMM_FOCUS_ID /*clientId*/); 1959 } 1960 } else if (state == TelephonyManager.CALL_STATE_OFFHOOK) { 1961 //Log.v(TAG, " CALL_STATE_OFFHOOK"); 1962 requestAudioFocus(AudioManager.STREAM_RING, 1963 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, 1964 null, null /* both allowed to be null only for this clientId */, 1965 IN_VOICE_COMM_FOCUS_ID /*clientId*/); 1966 } else if (state == TelephonyManager.CALL_STATE_IDLE) { 1967 //Log.v(TAG, " CALL_STATE_IDLE"); 1968 abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID); 1969 } 1970 } 1971 }; 1972 1973 private void notifyTopOfAudioFocusStack() { 1974 // notify the top of the stack it gained focus 1975 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 1976 if (canReassignAudioFocus()) { 1977 try { 1978 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 1979 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId); 1980 } catch (RemoteException e) { 1981 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e); 1982 e.printStackTrace(); 1983 } 1984 } 1985 } 1986 } 1987 1988 private static class FocusStackEntry { 1989 public int mStreamType = -1;// no stream type 1990 public boolean mIsTransportControlReceiver = false; 1991 public IAudioFocusDispatcher mFocusDispatcher = null; 1992 public IBinder mSourceRef = null; 1993 public String mClientId; 1994 public int mFocusChangeType; 1995 1996 public FocusStackEntry() { 1997 } 1998 1999 public FocusStackEntry(int streamType, int duration, boolean isTransportControlReceiver, 2000 IAudioFocusDispatcher afl, IBinder source, String id) { 2001 mStreamType = streamType; 2002 mIsTransportControlReceiver = isTransportControlReceiver; 2003 mFocusDispatcher = afl; 2004 mSourceRef = source; 2005 mClientId = id; 2006 mFocusChangeType = duration; 2007 } 2008 } 2009 2010 private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>(); 2011 2012 /** 2013 * Helper function: 2014 * Display in the log the current entries in the audio focus stack 2015 */ 2016 private void dumpFocusStack(PrintWriter pw) { 2017 pw.println("\nAudio Focus stack entries:"); 2018 synchronized(mAudioFocusLock) { 2019 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2020 while(stackIterator.hasNext()) { 2021 FocusStackEntry fse = stackIterator.next(); 2022 pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId 2023 + " -- duration: " +fse.mFocusChangeType); 2024 } 2025 } 2026 } 2027 2028 /** 2029 * Helper function: 2030 * Remove a focus listener from the focus stack. 2031 * @param focusListenerToRemove the focus listener 2032 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding 2033 * focus, notify the next item in the stack it gained focus. 2034 */ 2035 private void removeFocusStackEntry(String clientToRemove, boolean signal) { 2036 // is the current top of the focus stack abandoning focus? (because of death or request) 2037 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove)) 2038 { 2039 //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); 2040 mFocusStack.pop(); 2041 if (signal) { 2042 // notify the new top of the stack it gained focus 2043 notifyTopOfAudioFocusStack(); 2044 } 2045 } else { 2046 // focus is abandoned by a client that's not at the top of the stack, 2047 // no need to update focus. 2048 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2049 while(stackIterator.hasNext()) { 2050 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 2051 if(fse.mClientId.equals(clientToRemove)) { 2052 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 2053 + fse.mClientId); 2054 mFocusStack.remove(fse); 2055 } 2056 } 2057 } 2058 } 2059 2060 /** 2061 * Helper function: 2062 * Remove focus listeners from the focus stack for a particular client. 2063 */ 2064 private void removeFocusStackEntryForClient(IBinder cb) { 2065 // is the owner of the audio focus part of the client to remove? 2066 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && 2067 mFocusStack.peek().mSourceRef.equals(cb); 2068 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2069 while(stackIterator.hasNext()) { 2070 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 2071 if(fse.mSourceRef.equals(cb)) { 2072 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 2073 + fse.mClientId); 2074 mFocusStack.remove(fse); 2075 } 2076 } 2077 if (isTopOfStackForClientToRemove) { 2078 // we removed an entry at the top of the stack: 2079 // notify the new top of the stack it gained focus. 2080 notifyTopOfAudioFocusStack(); 2081 } 2082 } 2083 2084 /** 2085 * Helper function: 2086 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. 2087 */ 2088 private boolean canReassignAudioFocus() { 2089 // focus requests are rejected during a phone call or when the phone is ringing 2090 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus 2091 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) { 2092 return false; 2093 } 2094 return true; 2095 } 2096 2097 /** 2098 * Inner class to monitor audio focus client deaths, and remove them from the audio focus 2099 * stack if necessary. 2100 */ 2101 private class AudioFocusDeathHandler implements IBinder.DeathRecipient { 2102 private IBinder mCb; // To be notified of client's death 2103 2104 AudioFocusDeathHandler(IBinder cb) { 2105 mCb = cb; 2106 } 2107 2108 public void binderDied() { 2109 synchronized(mAudioFocusLock) { 2110 Log.w(TAG, " AudioFocus audio focus client died"); 2111 removeFocusStackEntryForClient(mCb); 2112 } 2113 } 2114 2115 public IBinder getBinder() { 2116 return mCb; 2117 } 2118 } 2119 2120 2121 /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */ 2122 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb, 2123 IAudioFocusDispatcher fd, String clientId) { 2124 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId); 2125 // the main stream type for the audio focus request is currently not used. It may 2126 // potentially be used to handle multiple stream type-dependent audio focuses. 2127 2128 // we need a valid binder callback for clients other than the AudioService's phone 2129 // state listener 2130 if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId) && ((cb == null) || !cb.pingBinder())) { 2131 Log.i(TAG, " AudioFocus DOA client for requestAudioFocus(), exiting"); 2132 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 2133 } 2134 2135 synchronized(mAudioFocusLock) { 2136 if (!canReassignAudioFocus()) { 2137 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 2138 } 2139 2140 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) { 2141 // if focus is already owned by this client and the reason for acquiring the focus 2142 // hasn't changed, don't do anything 2143 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) { 2144 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2145 } 2146 // the reason for the audio focus request has changed: remove the current top of 2147 // stack and respond as if we had a new focus owner 2148 mFocusStack.pop(); 2149 } 2150 2151 // notify current top of stack it is losing focus 2152 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 2153 try { 2154 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 2155 -1 * focusChangeHint, // loss and gain codes are inverse of each other 2156 mFocusStack.peek().mClientId); 2157 } catch (RemoteException e) { 2158 Log.e(TAG, " Failure to signal loss of focus due to "+ e); 2159 e.printStackTrace(); 2160 } 2161 } 2162 2163 // focus requester might already be somewhere below in the stack, remove it 2164 removeFocusStackEntry(clientId, false); 2165 2166 // push focus requester at the top of the audio focus stack 2167 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb, 2168 clientId)); 2169 }//synchronized(mAudioFocusLock) 2170 2171 // handle the potential premature death of the new holder of the focus 2172 // (premature death == death before abandoning focus) for a client which is not the 2173 // AudioService's phone state listener 2174 if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId)) { 2175 // Register for client death notification 2176 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); 2177 try { 2178 cb.linkToDeath(afdh, 0); 2179 } catch (RemoteException e) { 2180 // client has already died! 2181 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); 2182 } 2183 } 2184 2185 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2186 } 2187 2188 /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */ 2189 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) { 2190 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId); 2191 try { 2192 // this will take care of notifying the new focus owner if needed 2193 synchronized(mAudioFocusLock) { 2194 removeFocusStackEntry(clientId, true); 2195 } 2196 } catch (java.util.ConcurrentModificationException cme) { 2197 // Catching this exception here is temporary. It is here just to prevent 2198 // a crash seen when the "Silent" notification is played. This is believed to be fixed 2199 // but this try catch block is left just to be safe. 2200 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme); 2201 cme.printStackTrace(); 2202 } 2203 2204 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2205 } 2206 2207 2208 public void unregisterAudioFocusClient(String clientId) { 2209 synchronized(mAudioFocusLock) { 2210 removeFocusStackEntry(clientId, false); 2211 } 2212 } 2213 2214 2215 //========================================================================================== 2216 // RemoteControl 2217 //========================================================================================== 2218 /** 2219 * Receiver for media button intents. Handles the dispatching of the media button event 2220 * to one of the registered listeners, or if there was none, resumes the intent broadcast 2221 * to the rest of the system. 2222 */ 2223 private class MediaButtonBroadcastReceiver extends BroadcastReceiver { 2224 @Override 2225 public void onReceive(Context context, Intent intent) { 2226 String action = intent.getAction(); 2227 if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) { 2228 return; 2229 } 2230 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 2231 if (event != null) { 2232 // if in a call or ringing, do not break the current phone app behavior 2233 // TODO modify this to let the phone app specifically get the RC focus 2234 // add modify the phone app to take advantage of the new API 2235 if ((getMode() == AudioSystem.MODE_IN_CALL) || 2236 (getMode() == AudioSystem.MODE_RINGTONE)) { 2237 return; 2238 } 2239 synchronized(mRCStack) { 2240 if (!mRCStack.empty()) { 2241 // create a new intent specifically aimed at the current registered listener 2242 Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 2243 targetedIntent.putExtras(intent.getExtras()); 2244 targetedIntent.setComponent(mRCStack.peek().mReceiverComponent); 2245 // trap the current broadcast 2246 abortBroadcast(); 2247 //Log.v(TAG, " Sending intent" + targetedIntent); 2248 context.sendBroadcast(targetedIntent, null); 2249 } 2250 } 2251 } 2252 } 2253 } 2254 2255 private static class RemoteControlStackEntry { 2256 public ComponentName mReceiverComponent;// always non null 2257 // TODO implement registration expiration? 2258 //public int mRegistrationTime; 2259 2260 public RemoteControlStackEntry() { 2261 } 2262 2263 public RemoteControlStackEntry(ComponentName r) { 2264 mReceiverComponent = r; 2265 } 2266 } 2267 2268 private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>(); 2269 2270 /** 2271 * Helper function: 2272 * Display in the log the current entries in the remote control focus stack 2273 */ 2274 private void dumpRCStack(PrintWriter pw) { 2275 pw.println("\nRemote Control stack entries:"); 2276 synchronized(mRCStack) { 2277 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2278 while(stackIterator.hasNext()) { 2279 RemoteControlStackEntry fse = stackIterator.next(); 2280 pw.println(" receiver:" + fse.mReceiverComponent); 2281 } 2282 } 2283 } 2284 2285 /** 2286 * Helper function: 2287 * Set the new remote control receiver at the top of the RC focus stack 2288 */ 2289 private void pushMediaButtonReceiver(ComponentName newReceiver) { 2290 // already at top of stack? 2291 if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) { 2292 return; 2293 } 2294 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2295 while(stackIterator.hasNext()) { 2296 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 2297 if(rcse.mReceiverComponent.equals(newReceiver)) { 2298 mRCStack.remove(rcse); 2299 break; 2300 } 2301 } 2302 mRCStack.push(new RemoteControlStackEntry(newReceiver)); 2303 } 2304 2305 /** 2306 * Helper function: 2307 * Remove the remote control receiver from the RC focus stack 2308 */ 2309 private void removeMediaButtonReceiver(ComponentName newReceiver) { 2310 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2311 while(stackIterator.hasNext()) { 2312 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 2313 if(rcse.mReceiverComponent.equals(newReceiver)) { 2314 mRCStack.remove(rcse); 2315 break; 2316 } 2317 } 2318 } 2319 2320 2321 /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */ 2322 public void registerMediaButtonEventReceiver(ComponentName eventReceiver) { 2323 Log.i(TAG, " Remote Control registerMediaButtonEventReceiver() for " + eventReceiver); 2324 2325 synchronized(mRCStack) { 2326 pushMediaButtonReceiver(eventReceiver); 2327 } 2328 } 2329 2330 /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */ 2331 public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) { 2332 Log.i(TAG, " Remote Control unregisterMediaButtonEventReceiver() for " + eventReceiver); 2333 2334 synchronized(mRCStack) { 2335 removeMediaButtonReceiver(eventReceiver); 2336 } 2337 } 2338 2339 2340 @Override 2341 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2342 // TODO probably a lot more to do here than just the audio focus and remote control stacks 2343 dumpFocusStack(pw); 2344 dumpRCStack(pw); 2345 } 2346 2347 2348} 2349