AudioService.java revision 392a2bbb52688ebd25768a7784d9edca7f498110
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 mBluetoothHeadset.getState() == BluetoothHeadset.STATE_CONNECTED) { 1097 mBluetoothHeadsetConnected = true; 1098 } 1099 } 1100 public void onServiceDisconnected() { 1101 if (mBluetoothHeadset != null && 1102 mBluetoothHeadset.getState() == BluetoothHeadset.STATE_DISCONNECTED) { 1103 mBluetoothHeadsetConnected = false; 1104 clearAllScoClients(); 1105 } 1106 } 1107 }; 1108 1109 /////////////////////////////////////////////////////////////////////////// 1110 // Internal methods 1111 /////////////////////////////////////////////////////////////////////////// 1112 1113 /** 1114 * Checks if the adjustment should change ringer mode instead of just 1115 * adjusting volume. If so, this will set the proper ringer mode and volume 1116 * indices on the stream states. 1117 */ 1118 private boolean checkForRingerModeChange(int oldIndex, int direction) { 1119 boolean adjustVolumeIndex = true; 1120 int newRingerMode = mRingerMode; 1121 1122 if (mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 1123 // audible mode, at the bottom of the scale 1124 if (direction == AudioManager.ADJUST_LOWER 1125 && (oldIndex + 5) / 10 == 1) { 1126 // "silent mode", but which one? 1127 newRingerMode = System.getInt(mContentResolver, System.VIBRATE_IN_SILENT, 1) == 1 1128 ? AudioManager.RINGER_MODE_VIBRATE 1129 : AudioManager.RINGER_MODE_SILENT; 1130 } 1131 } else { 1132 if (direction == AudioManager.ADJUST_RAISE) { 1133 // exiting silent mode 1134 newRingerMode = AudioManager.RINGER_MODE_NORMAL; 1135 } else { 1136 // prevent last audible index to reach 0 1137 adjustVolumeIndex = false; 1138 } 1139 } 1140 1141 if (newRingerMode != mRingerMode) { 1142 setRingerMode(newRingerMode); 1143 1144 /* 1145 * If we are changing ringer modes, do not increment/decrement the 1146 * volume index. Instead, the handler for the message above will 1147 * take care of changing the index. 1148 */ 1149 adjustVolumeIndex = false; 1150 } 1151 1152 return adjustVolumeIndex; 1153 } 1154 1155 public boolean isStreamAffectedByRingerMode(int streamType) { 1156 return (mRingerModeAffectedStreams & (1 << streamType)) != 0; 1157 } 1158 1159 private boolean isStreamMutedByRingerMode(int streamType) { 1160 return (mRingerModeMutedStreams & (1 << streamType)) != 0; 1161 } 1162 1163 public boolean isStreamAffectedByMute(int streamType) { 1164 return (mMuteAffectedStreams & (1 << streamType)) != 0; 1165 } 1166 1167 private void ensureValidDirection(int direction) { 1168 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) { 1169 throw new IllegalArgumentException("Bad direction " + direction); 1170 } 1171 } 1172 1173 private void ensureValidStreamType(int streamType) { 1174 if (streamType < 0 || streamType >= mStreamStates.length) { 1175 throw new IllegalArgumentException("Bad stream type " + streamType); 1176 } 1177 } 1178 1179 private int getActiveStreamType(int suggestedStreamType) { 1180 boolean isOffhook = false; 1181 try { 1182 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 1183 if (phone != null) isOffhook = phone.isOffhook(); 1184 } catch (RemoteException e) { 1185 Log.w(TAG, "Couldn't connect to phone service", e); 1186 } 1187 1188 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) == AudioSystem.FORCE_BT_SCO) { 1189 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); 1190 return AudioSystem.STREAM_BLUETOOTH_SCO; 1191 } else if (isOffhook || AudioSystem.isStreamActive(AudioSystem.STREAM_VOICE_CALL)) { 1192 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); 1193 return AudioSystem.STREAM_VOICE_CALL; 1194 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC)) { 1195 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC..."); 1196 return AudioSystem.STREAM_MUSIC; 1197 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { 1198 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..."); 1199 return AudioSystem.STREAM_RING; 1200 } else { 1201 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType); 1202 return suggestedStreamType; 1203 } 1204 } 1205 1206 private void broadcastRingerMode() { 1207 // Send sticky broadcast 1208 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION); 1209 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, mRingerMode); 1210 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 1211 | Intent.FLAG_RECEIVER_REPLACE_PENDING); 1212 long origCallerIdentityToken = Binder.clearCallingIdentity(); 1213 mContext.sendStickyBroadcast(broadcast); 1214 Binder.restoreCallingIdentity(origCallerIdentityToken); 1215 } 1216 1217 private void broadcastVibrateSetting(int vibrateType) { 1218 // Send broadcast 1219 if (ActivityManagerNative.isSystemReady()) { 1220 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); 1221 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); 1222 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); 1223 mContext.sendBroadcast(broadcast); 1224 } 1225 } 1226 1227 // Message helper methods 1228 private static int getMsg(int baseMsg, int streamType) { 1229 return (baseMsg & 0xffff) | streamType << 16; 1230 } 1231 1232 private static int getMsgBase(int msg) { 1233 return msg & 0xffff; 1234 } 1235 1236 private static void sendMsg(Handler handler, int baseMsg, int streamType, 1237 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) { 1238 int msg = (streamType == SHARED_MSG) ? baseMsg : getMsg(baseMsg, streamType); 1239 1240 if (existingMsgPolicy == SENDMSG_REPLACE) { 1241 handler.removeMessages(msg); 1242 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) { 1243 return; 1244 } 1245 1246 handler 1247 .sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay); 1248 } 1249 1250 boolean checkAudioSettingsPermission(String method) { 1251 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS") 1252 == PackageManager.PERMISSION_GRANTED) { 1253 return true; 1254 } 1255 String msg = "Audio Settings Permission Denial: " + method + " from pid=" 1256 + Binder.getCallingPid() 1257 + ", uid=" + Binder.getCallingUid(); 1258 Log.w(TAG, msg); 1259 return false; 1260 } 1261 1262 1263 /////////////////////////////////////////////////////////////////////////// 1264 // Inner classes 1265 /////////////////////////////////////////////////////////////////////////// 1266 1267 public class VolumeStreamState { 1268 private final int mStreamType; 1269 1270 private String mVolumeIndexSettingName; 1271 private String mLastAudibleVolumeIndexSettingName; 1272 private int mIndexMax; 1273 private int mIndex; 1274 private int mLastAudibleIndex; 1275 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo requests client death 1276 1277 private VolumeStreamState(String settingName, int streamType) { 1278 1279 setVolumeIndexSettingName(settingName); 1280 1281 mStreamType = streamType; 1282 1283 final ContentResolver cr = mContentResolver; 1284 mIndexMax = MAX_STREAM_VOLUME[streamType]; 1285 mIndex = Settings.System.getInt(cr, 1286 mVolumeIndexSettingName, 1287 AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1288 mLastAudibleIndex = Settings.System.getInt(cr, 1289 mLastAudibleVolumeIndexSettingName, 1290 (mIndex > 0) ? mIndex : AudioManager.DEFAULT_STREAM_VOLUME[streamType]); 1291 AudioSystem.initStreamVolume(streamType, 0, mIndexMax); 1292 mIndexMax *= 10; 1293 mIndex = getValidIndex(10 * mIndex); 1294 mLastAudibleIndex = getValidIndex(10 * mLastAudibleIndex); 1295 setStreamVolumeIndex(streamType, mIndex); 1296 mDeathHandlers = new ArrayList<VolumeDeathHandler>(); 1297 } 1298 1299 public void setVolumeIndexSettingName(String settingName) { 1300 mVolumeIndexSettingName = settingName; 1301 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; 1302 } 1303 1304 public boolean adjustIndex(int deltaIndex) { 1305 return setIndex(mIndex + deltaIndex * 10, true); 1306 } 1307 1308 public boolean setIndex(int index, boolean lastAudible) { 1309 int oldIndex = mIndex; 1310 mIndex = getValidIndex(index); 1311 1312 if (oldIndex != mIndex) { 1313 if (lastAudible) { 1314 mLastAudibleIndex = mIndex; 1315 } 1316 // Apply change to all streams using this one as alias 1317 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1318 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1319 if (streamType != mStreamType && STREAM_VOLUME_ALIAS[streamType] == mStreamType) { 1320 mStreamStates[streamType].setIndex(rescaleIndex(mIndex, mStreamType, streamType), lastAudible); 1321 } 1322 } 1323 return true; 1324 } else { 1325 return false; 1326 } 1327 } 1328 1329 public void setLastAudibleIndex(int index) { 1330 mLastAudibleIndex = getValidIndex(index); 1331 } 1332 1333 public void adjustLastAudibleIndex(int deltaIndex) { 1334 setLastAudibleIndex(mLastAudibleIndex + deltaIndex * 10); 1335 } 1336 1337 public int getMaxIndex() { 1338 return mIndexMax; 1339 } 1340 1341 public void mute(IBinder cb, boolean state) { 1342 VolumeDeathHandler handler = getDeathHandler(cb, state); 1343 if (handler == null) { 1344 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType); 1345 return; 1346 } 1347 handler.mute(state); 1348 } 1349 1350 private int getValidIndex(int index) { 1351 if (index < 0) { 1352 return 0; 1353 } else if (index > mIndexMax) { 1354 return mIndexMax; 1355 } 1356 1357 return index; 1358 } 1359 1360 private class VolumeDeathHandler implements IBinder.DeathRecipient { 1361 private IBinder mICallback; // To be notified of client's death 1362 private int mMuteCount; // Number of active mutes for this client 1363 1364 VolumeDeathHandler(IBinder cb) { 1365 mICallback = cb; 1366 } 1367 1368 public void mute(boolean state) { 1369 synchronized(mDeathHandlers) { 1370 if (state) { 1371 if (mMuteCount == 0) { 1372 // Register for client death notification 1373 try { 1374 // mICallback can be 0 if muted by AudioService 1375 if (mICallback != null) { 1376 mICallback.linkToDeath(this, 0); 1377 } 1378 mDeathHandlers.add(this); 1379 // If the stream is not yet muted by any client, set lvel to 0 1380 if (muteCount() == 0) { 1381 setIndex(0, false); 1382 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, 1383 VolumeStreamState.this, 0); 1384 } 1385 } catch (RemoteException e) { 1386 // Client has died! 1387 binderDied(); 1388 mDeathHandlers.notify(); 1389 return; 1390 } 1391 } else { 1392 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client"); 1393 } 1394 mMuteCount++; 1395 } else { 1396 if (mMuteCount == 0) { 1397 Log.e(TAG, "unexpected unmute for stream: "+mStreamType); 1398 } else { 1399 mMuteCount--; 1400 if (mMuteCount == 0) { 1401 // Unregistr from client death notification 1402 mDeathHandlers.remove(this); 1403 // mICallback can be 0 if muted by AudioService 1404 if (mICallback != null) { 1405 mICallback.unlinkToDeath(this, 0); 1406 } 1407 if (muteCount() == 0) { 1408 // If the stream is not muted any more, restore it's volume if 1409 // ringer mode allows it 1410 if (!isStreamAffectedByRingerMode(mStreamType) || mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 1411 setIndex(mLastAudibleIndex, false); 1412 sendMsg(mAudioHandler, MSG_SET_SYSTEM_VOLUME, mStreamType, SENDMSG_NOOP, 0, 0, 1413 VolumeStreamState.this, 0); 1414 } 1415 } 1416 } 1417 } 1418 } 1419 mDeathHandlers.notify(); 1420 } 1421 } 1422 1423 public void binderDied() { 1424 Log.w(TAG, "Volume service client died for stream: "+mStreamType); 1425 if (mMuteCount != 0) { 1426 // Reset all active mute requests from this client. 1427 mMuteCount = 1; 1428 mute(false); 1429 } 1430 } 1431 } 1432 1433 private int muteCount() { 1434 int count = 0; 1435 int size = mDeathHandlers.size(); 1436 for (int i = 0; i < size; i++) { 1437 count += mDeathHandlers.get(i).mMuteCount; 1438 } 1439 return count; 1440 } 1441 1442 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) { 1443 synchronized(mDeathHandlers) { 1444 VolumeDeathHandler handler; 1445 int size = mDeathHandlers.size(); 1446 for (int i = 0; i < size; i++) { 1447 handler = mDeathHandlers.get(i); 1448 if (cb == handler.mICallback) { 1449 return handler; 1450 } 1451 } 1452 // If this is the first mute request for this client, create a new 1453 // client death handler. Otherwise, it is an out of sequence unmute request. 1454 if (state) { 1455 handler = new VolumeDeathHandler(cb); 1456 } else { 1457 Log.w(TAG, "stream was not muted by this client"); 1458 handler = null; 1459 } 1460 return handler; 1461 } 1462 } 1463 } 1464 1465 /** Thread that handles native AudioSystem control. */ 1466 private class AudioSystemThread extends Thread { 1467 AudioSystemThread() { 1468 super("AudioService"); 1469 } 1470 1471 @Override 1472 public void run() { 1473 // Set this thread up so the handler will work on it 1474 Looper.prepare(); 1475 1476 synchronized(AudioService.this) { 1477 mAudioHandler = new AudioHandler(); 1478 1479 // Notify that the handler has been created 1480 AudioService.this.notify(); 1481 } 1482 1483 // Listen for volume change requests that are set by VolumePanel 1484 Looper.loop(); 1485 } 1486 } 1487 1488 /** Handles internal volume messages in separate volume thread. */ 1489 private class AudioHandler extends Handler { 1490 1491 private void setSystemVolume(VolumeStreamState streamState) { 1492 1493 // Adjust volume 1494 setStreamVolumeIndex(streamState.mStreamType, streamState.mIndex); 1495 1496 // Apply change to all streams using this one as alias 1497 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1498 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1499 if (streamType != streamState.mStreamType && 1500 STREAM_VOLUME_ALIAS[streamType] == streamState.mStreamType) { 1501 setStreamVolumeIndex(streamType, mStreamStates[streamType].mIndex); 1502 } 1503 } 1504 1505 // Post a persist volume msg 1506 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, streamState.mStreamType, 1507 SENDMSG_REPLACE, 1, 1, streamState, PERSIST_DELAY); 1508 } 1509 1510 private void persistVolume(VolumeStreamState streamState, boolean current, boolean lastAudible) { 1511 if (current) { 1512 System.putInt(mContentResolver, streamState.mVolumeIndexSettingName, 1513 (streamState.mIndex + 5)/ 10); 1514 } 1515 if (lastAudible) { 1516 System.putInt(mContentResolver, streamState.mLastAudibleVolumeIndexSettingName, 1517 (streamState.mLastAudibleIndex + 5) / 10); 1518 } 1519 } 1520 1521 private void persistRingerMode() { 1522 System.putInt(mContentResolver, System.MODE_RINGER, mRingerMode); 1523 } 1524 1525 private void persistVibrateSetting() { 1526 System.putInt(mContentResolver, System.VIBRATE_ON, mVibrateSetting); 1527 } 1528 1529 private void playSoundEffect(int effectType, int volume) { 1530 synchronized (mSoundEffectsLock) { 1531 if (mSoundPool == null) { 1532 return; 1533 } 1534 float volFloat; 1535 // use STREAM_MUSIC volume attenuated by 3 dB if volume is not specified by caller 1536 if (volume < 0) { 1537 // Same linear to log conversion as in native AudioSystem::linearToLog() (AudioSystem.cpp) 1538 float dBPerStep = (float)((0.5 * 100) / MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]); 1539 int musicVolIndex = (mStreamStates[AudioSystem.STREAM_MUSIC].mIndex + 5) / 10; 1540 float musicVoldB = dBPerStep * (musicVolIndex - MAX_STREAM_VOLUME[AudioSystem.STREAM_MUSIC]); 1541 volFloat = (float)Math.pow(10, (musicVoldB - 3)/20); 1542 } else { 1543 volFloat = (float) volume / 1000.0f; 1544 } 1545 1546 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) { 1547 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f); 1548 } else { 1549 MediaPlayer mediaPlayer = new MediaPlayer(); 1550 if (mediaPlayer != null) { 1551 try { 1552 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]]; 1553 mediaPlayer.setDataSource(filePath); 1554 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM); 1555 mediaPlayer.prepare(); 1556 mediaPlayer.setVolume(volFloat, volFloat); 1557 mediaPlayer.setOnCompletionListener(new OnCompletionListener() { 1558 public void onCompletion(MediaPlayer mp) { 1559 cleanupPlayer(mp); 1560 } 1561 }); 1562 mediaPlayer.setOnErrorListener(new OnErrorListener() { 1563 public boolean onError(MediaPlayer mp, int what, int extra) { 1564 cleanupPlayer(mp); 1565 return true; 1566 } 1567 }); 1568 mediaPlayer.start(); 1569 } catch (IOException ex) { 1570 Log.w(TAG, "MediaPlayer IOException: "+ex); 1571 } catch (IllegalArgumentException ex) { 1572 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex); 1573 } catch (IllegalStateException ex) { 1574 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 1575 } 1576 } 1577 } 1578 } 1579 } 1580 1581 private void cleanupPlayer(MediaPlayer mp) { 1582 if (mp != null) { 1583 try { 1584 mp.stop(); 1585 mp.release(); 1586 } catch (IllegalStateException ex) { 1587 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 1588 } 1589 } 1590 } 1591 1592 @Override 1593 public void handleMessage(Message msg) { 1594 int baseMsgWhat = getMsgBase(msg.what); 1595 1596 switch (baseMsgWhat) { 1597 1598 case MSG_SET_SYSTEM_VOLUME: 1599 setSystemVolume((VolumeStreamState) msg.obj); 1600 break; 1601 1602 case MSG_PERSIST_VOLUME: 1603 persistVolume((VolumeStreamState) msg.obj, (msg.arg1 != 0), (msg.arg2 != 0)); 1604 break; 1605 1606 case MSG_PERSIST_RINGER_MODE: 1607 persistRingerMode(); 1608 break; 1609 1610 case MSG_PERSIST_VIBRATE_SETTING: 1611 persistVibrateSetting(); 1612 break; 1613 1614 case MSG_MEDIA_SERVER_DIED: 1615 // Force creation of new IAudioflinger interface 1616 if (!mMediaServerOk) { 1617 Log.e(TAG, "Media server died."); 1618 AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC); 1619 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SHARED_MSG, SENDMSG_NOOP, 0, 0, 1620 null, 500); 1621 } 1622 break; 1623 1624 case MSG_MEDIA_SERVER_STARTED: 1625 Log.e(TAG, "Media server started."); 1626 // Restore device connection states 1627 Set set = mConnectedDevices.entrySet(); 1628 Iterator i = set.iterator(); 1629 while(i.hasNext()){ 1630 Map.Entry device = (Map.Entry)i.next(); 1631 AudioSystem.setDeviceConnectionState(((Integer)device.getKey()).intValue(), 1632 AudioSystem.DEVICE_STATE_AVAILABLE, 1633 (String)device.getValue()); 1634 } 1635 1636 // Restore call state 1637 AudioSystem.setPhoneState(mMode); 1638 1639 // Restore forced usage for communcations and record 1640 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm); 1641 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm); 1642 1643 // Restore stream volumes 1644 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1645 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1646 int index; 1647 VolumeStreamState streamState = mStreamStates[streamType]; 1648 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10); 1649 if (streamState.muteCount() == 0) { 1650 index = streamState.mIndex; 1651 } else { 1652 index = 0; 1653 } 1654 setStreamVolumeIndex(streamType, index); 1655 } 1656 1657 // Restore ringer mode 1658 setRingerModeInt(getRingerMode(), false); 1659 break; 1660 1661 case MSG_PLAY_SOUND_EFFECT: 1662 playSoundEffect(msg.arg1, msg.arg2); 1663 break; 1664 1665 case MSG_BTA2DP_DOCK_TIMEOUT: 1666 // msg.obj == address of BTA2DP device 1667 makeA2dpDeviceUnavailableNow( (String) msg.obj ); 1668 break; 1669 } 1670 } 1671 } 1672 1673 private class SettingsObserver extends ContentObserver { 1674 1675 SettingsObserver() { 1676 super(new Handler()); 1677 mContentResolver.registerContentObserver(Settings.System.getUriFor( 1678 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this); 1679 mContentResolver.registerContentObserver(Settings.System.getUriFor( 1680 Settings.System.NOTIFICATIONS_USE_RING_VOLUME), false, this); 1681 } 1682 1683 @Override 1684 public void onChange(boolean selfChange) { 1685 super.onChange(selfChange); 1686 synchronized (mSettingsLock) { 1687 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver, 1688 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 1689 0); 1690 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) { 1691 /* 1692 * Ensure all stream types that should be affected by ringer mode 1693 * are in the proper state. 1694 */ 1695 mRingerModeAffectedStreams = ringerModeAffectedStreams; 1696 setRingerModeInt(getRingerMode(), false); 1697 } 1698 1699 int notificationsUseRingVolume = Settings.System.getInt(mContentResolver, 1700 Settings.System.NOTIFICATIONS_USE_RING_VOLUME, 1701 1); 1702 if (notificationsUseRingVolume != mNotificationsUseRingVolume) { 1703 mNotificationsUseRingVolume = notificationsUseRingVolume; 1704 if (mNotificationsUseRingVolume == 1) { 1705 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_RING; 1706 mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName( 1707 System.VOLUME_SETTINGS[AudioSystem.STREAM_RING]); 1708 } else { 1709 STREAM_VOLUME_ALIAS[AudioSystem.STREAM_NOTIFICATION] = AudioSystem.STREAM_NOTIFICATION; 1710 mStreamStates[AudioSystem.STREAM_NOTIFICATION].setVolumeIndexSettingName( 1711 System.VOLUME_SETTINGS[AudioSystem.STREAM_NOTIFICATION]); 1712 // Persist notification volume volume as it was not persisted while aliased to ring volume 1713 // and persist with no delay as there might be registered observers of the persisted 1714 // notification volume. 1715 sendMsg(mAudioHandler, MSG_PERSIST_VOLUME, AudioSystem.STREAM_NOTIFICATION, 1716 SENDMSG_REPLACE, 1, 1, mStreamStates[AudioSystem.STREAM_NOTIFICATION], 0); 1717 } 1718 } 1719 } 1720 } 1721 } 1722 1723 private void makeA2dpDeviceAvailable(String address) { 1724 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 1725 AudioSystem.DEVICE_STATE_AVAILABLE, 1726 address); 1727 // Reset A2DP suspend state each time a new sink is connected 1728 AudioSystem.setParameters("A2dpSuspended=false"); 1729 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), 1730 address); 1731 } 1732 1733 private void makeA2dpDeviceUnavailableNow(String address) { 1734 Intent noisyIntent = new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY); 1735 mContext.sendBroadcast(noisyIntent); 1736 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 1737 AudioSystem.DEVICE_STATE_UNAVAILABLE, 1738 address); 1739 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 1740 } 1741 1742 private void makeA2dpDeviceUnavailableLater(String address) { 1743 // prevent any activity on the A2DP audio output to avoid unwanted 1744 // reconnection of the sink. 1745 AudioSystem.setParameters("A2dpSuspended=true"); 1746 // the device will be made unavailable later, so consider it disconnected right away 1747 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 1748 // send the delayed message to make the device unavailable later 1749 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address); 1750 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS); 1751 1752 } 1753 1754 private void cancelA2dpDeviceTimeout() { 1755 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT); 1756 } 1757 1758 private boolean hasScheduledA2dpDockTimeout() { 1759 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT); 1760 } 1761 1762 /* cache of the address of the last dock the device was connected to */ 1763 private String mDockAddress; 1764 1765 /** 1766 * Receiver for misc intent broadcasts the Phone app cares about. 1767 */ 1768 private class AudioServiceBroadcastReceiver extends BroadcastReceiver { 1769 @Override 1770 public void onReceive(Context context, Intent intent) { 1771 String action = intent.getAction(); 1772 1773 if (action.equals(Intent.ACTION_DOCK_EVENT)) { 1774 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 1775 Intent.EXTRA_DOCK_STATE_UNDOCKED); 1776 int config; 1777 switch (dockState) { 1778 case Intent.EXTRA_DOCK_STATE_DESK: 1779 config = AudioSystem.FORCE_BT_DESK_DOCK; 1780 break; 1781 case Intent.EXTRA_DOCK_STATE_CAR: 1782 config = AudioSystem.FORCE_BT_CAR_DOCK; 1783 break; 1784 case Intent.EXTRA_DOCK_STATE_UNDOCKED: 1785 default: 1786 config = AudioSystem.FORCE_NONE; 1787 } 1788 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); 1789 } else if (action.equals(BluetoothA2dp.ACTION_SINK_STATE_CHANGED)) { 1790 int state = intent.getIntExtra(BluetoothA2dp.EXTRA_SINK_STATE, 1791 BluetoothA2dp.STATE_DISCONNECTED); 1792 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 1793 String address = btDevice.getAddress(); 1794 boolean isConnected = (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) && 1795 ((String)mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)).equals(address)); 1796 1797 if (isConnected && 1798 state != BluetoothA2dp.STATE_CONNECTED && state != BluetoothA2dp.STATE_PLAYING) { 1799 if (btDevice.isBluetoothDock()) { 1800 if (state == BluetoothA2dp.STATE_DISCONNECTED) { 1801 // introduction of a delay for transient disconnections of docks when 1802 // power is rapidly turned off/on, this message will be canceled if 1803 // we reconnect the dock under a preset delay 1804 makeA2dpDeviceUnavailableLater(address); 1805 // the next time isConnected is evaluated, it will be false for the dock 1806 } 1807 } else { 1808 makeA2dpDeviceUnavailableNow(address); 1809 } 1810 } else if (!isConnected && 1811 (state == BluetoothA2dp.STATE_CONNECTED || 1812 state == BluetoothA2dp.STATE_PLAYING)) { 1813 if (btDevice.isBluetoothDock()) { 1814 // this could be a reconnection after a transient disconnection 1815 cancelA2dpDeviceTimeout(); 1816 mDockAddress = address; 1817 } else { 1818 // this could be a connection of another A2DP device before the timeout of 1819 // a dock: cancel the dock timeout, and make the dock unavailable now 1820 if(hasScheduledA2dpDockTimeout()) { 1821 cancelA2dpDeviceTimeout(); 1822 makeA2dpDeviceUnavailableNow(mDockAddress); 1823 } 1824 } 1825 makeA2dpDeviceAvailable(address); 1826 } 1827 } else if (action.equals(BluetoothHeadset.ACTION_STATE_CHANGED)) { 1828 int state = intent.getIntExtra(BluetoothHeadset.EXTRA_STATE, 1829 BluetoothHeadset.STATE_ERROR); 1830 int device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO; 1831 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 1832 String address = null; 1833 if (btDevice != null) { 1834 address = btDevice.getAddress(); 1835 BluetoothClass btClass = btDevice.getBluetoothClass(); 1836 if (btClass != null) { 1837 switch (btClass.getDeviceClass()) { 1838 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 1839 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: 1840 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET; 1841 break; 1842 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 1843 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT; 1844 break; 1845 } 1846 } 1847 } 1848 1849 boolean isConnected = (mConnectedDevices.containsKey(device) && 1850 ((String)mConnectedDevices.get(device)).equals(address)); 1851 1852 if (isConnected && state != BluetoothHeadset.STATE_CONNECTED) { 1853 AudioSystem.setDeviceConnectionState(device, 1854 AudioSystem.DEVICE_STATE_UNAVAILABLE, 1855 address); 1856 mConnectedDevices.remove(device); 1857 mBluetoothHeadsetConnected = false; 1858 clearAllScoClients(); 1859 } else if (!isConnected && state == BluetoothHeadset.STATE_CONNECTED) { 1860 AudioSystem.setDeviceConnectionState(device, 1861 AudioSystem.DEVICE_STATE_AVAILABLE, 1862 address); 1863 mConnectedDevices.put(new Integer(device), address); 1864 mBluetoothHeadsetConnected = true; 1865 } 1866 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { 1867 int state = intent.getIntExtra("state", 0); 1868 int microphone = intent.getIntExtra("microphone", 0); 1869 1870 if (microphone != 0) { 1871 boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 1872 if (state == 0 && isConnected) { 1873 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, 1874 AudioSystem.DEVICE_STATE_UNAVAILABLE, 1875 ""); 1876 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADSET); 1877 } else if (state == 1 && !isConnected) { 1878 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADSET, 1879 AudioSystem.DEVICE_STATE_AVAILABLE, 1880 ""); 1881 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADSET), ""); 1882 } 1883 } else { 1884 boolean isConnected = mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 1885 if (state == 0 && isConnected) { 1886 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, 1887 AudioSystem.DEVICE_STATE_UNAVAILABLE, 1888 ""); 1889 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE); 1890 } else if (state == 1 && !isConnected) { 1891 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE, 1892 AudioSystem.DEVICE_STATE_AVAILABLE, 1893 ""); 1894 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_WIRED_HEADPHONE), ""); 1895 } 1896 } 1897 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { 1898 int state = intent.getIntExtra(BluetoothHeadset.EXTRA_AUDIO_STATE, 1899 BluetoothHeadset.STATE_ERROR); 1900 synchronized (mScoClients) { 1901 if (!mScoClients.isEmpty()) { 1902 switch (state) { 1903 case BluetoothHeadset.AUDIO_STATE_CONNECTED: 1904 state = AudioManager.SCO_AUDIO_STATE_CONNECTED; 1905 break; 1906 case BluetoothHeadset.AUDIO_STATE_DISCONNECTED: 1907 state = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; 1908 break; 1909 default: 1910 state = AudioManager.SCO_AUDIO_STATE_ERROR; 1911 break; 1912 } 1913 if (state != AudioManager.SCO_AUDIO_STATE_ERROR) { 1914 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 1915 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); 1916 mContext.sendStickyBroadcast(newIntent); 1917 } 1918 } 1919 } 1920 } 1921 } 1922 } 1923 1924 //========================================================================================== 1925 // AudioFocus 1926 //========================================================================================== 1927 1928 /* constant to identify focus stack entry that is used to hold the focus while the phone 1929 * is ringing or during a call 1930 */ 1931 private final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls"; 1932 1933 private final static Object mAudioFocusLock = new Object(); 1934 1935 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 1936 @Override 1937 public void onCallStateChanged(int state, String incomingNumber) { 1938 if (state == TelephonyManager.CALL_STATE_RINGING) { 1939 //Log.v(TAG, " CALL_STATE_RINGING"); 1940 int ringVolume = AudioService.this.getStreamVolume(AudioManager.STREAM_RING); 1941 if (ringVolume > 0) { 1942 requestAudioFocus(AudioManager.STREAM_RING, 1943 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, 1944 null, null /* both allowed to be null only for this clientId */, 1945 IN_VOICE_COMM_FOCUS_ID /*clientId*/); 1946 } 1947 } else if (state == TelephonyManager.CALL_STATE_OFFHOOK) { 1948 //Log.v(TAG, " CALL_STATE_OFFHOOK"); 1949 requestAudioFocus(AudioManager.STREAM_RING, 1950 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT, 1951 null, null /* both allowed to be null only for this clientId */, 1952 IN_VOICE_COMM_FOCUS_ID /*clientId*/); 1953 } else if (state == TelephonyManager.CALL_STATE_IDLE) { 1954 //Log.v(TAG, " CALL_STATE_IDLE"); 1955 abandonAudioFocus(null, IN_VOICE_COMM_FOCUS_ID); 1956 } 1957 } 1958 }; 1959 1960 private void notifyTopOfAudioFocusStack() { 1961 // notify the top of the stack it gained focus 1962 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 1963 if (canReassignAudioFocus()) { 1964 try { 1965 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 1966 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId); 1967 } catch (RemoteException e) { 1968 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e); 1969 e.printStackTrace(); 1970 } 1971 } 1972 } 1973 } 1974 1975 private static class FocusStackEntry { 1976 public int mStreamType = -1;// no stream type 1977 public boolean mIsTransportControlReceiver = false; 1978 public IAudioFocusDispatcher mFocusDispatcher = null; 1979 public IBinder mSourceRef = null; 1980 public String mClientId; 1981 public int mFocusChangeType; 1982 1983 public FocusStackEntry() { 1984 } 1985 1986 public FocusStackEntry(int streamType, int duration, boolean isTransportControlReceiver, 1987 IAudioFocusDispatcher afl, IBinder source, String id) { 1988 mStreamType = streamType; 1989 mIsTransportControlReceiver = isTransportControlReceiver; 1990 mFocusDispatcher = afl; 1991 mSourceRef = source; 1992 mClientId = id; 1993 mFocusChangeType = duration; 1994 } 1995 } 1996 1997 private Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>(); 1998 1999 /** 2000 * Helper function: 2001 * Display in the log the current entries in the audio focus stack 2002 */ 2003 private void dumpFocusStack(PrintWriter pw) { 2004 pw.println("\nAudio Focus stack entries:"); 2005 synchronized(mAudioFocusLock) { 2006 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2007 while(stackIterator.hasNext()) { 2008 FocusStackEntry fse = stackIterator.next(); 2009 pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId 2010 + " -- duration: " +fse.mFocusChangeType); 2011 } 2012 } 2013 } 2014 2015 /** 2016 * Helper function: 2017 * Remove a focus listener from the focus stack. 2018 * @param focusListenerToRemove the focus listener 2019 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding 2020 * focus, notify the next item in the stack it gained focus. 2021 */ 2022 private void removeFocusStackEntry(String clientToRemove, boolean signal) { 2023 // is the current top of the focus stack abandoning focus? (because of death or request) 2024 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove)) 2025 { 2026 //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); 2027 mFocusStack.pop(); 2028 if (signal) { 2029 // notify the new top of the stack it gained focus 2030 notifyTopOfAudioFocusStack(); 2031 } 2032 } else { 2033 // focus is abandoned by a client that's not at the top of the stack, 2034 // no need to update focus. 2035 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2036 while(stackIterator.hasNext()) { 2037 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 2038 if(fse.mClientId.equals(clientToRemove)) { 2039 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 2040 + fse.mClientId); 2041 mFocusStack.remove(fse); 2042 } 2043 } 2044 } 2045 } 2046 2047 /** 2048 * Helper function: 2049 * Remove focus listeners from the focus stack for a particular client. 2050 */ 2051 private void removeFocusStackEntryForClient(IBinder cb) { 2052 // is the owner of the audio focus part of the client to remove? 2053 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && 2054 mFocusStack.peek().mSourceRef.equals(cb); 2055 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 2056 while(stackIterator.hasNext()) { 2057 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 2058 if(fse.mSourceRef.equals(cb)) { 2059 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 2060 + fse.mClientId); 2061 mFocusStack.remove(fse); 2062 } 2063 } 2064 if (isTopOfStackForClientToRemove) { 2065 // we removed an entry at the top of the stack: 2066 // notify the new top of the stack it gained focus. 2067 notifyTopOfAudioFocusStack(); 2068 } 2069 } 2070 2071 /** 2072 * Helper function: 2073 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. 2074 */ 2075 private boolean canReassignAudioFocus() { 2076 // focus requests are rejected during a phone call or when the phone is ringing 2077 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus 2078 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) { 2079 return false; 2080 } 2081 return true; 2082 } 2083 2084 /** 2085 * Inner class to monitor audio focus client deaths, and remove them from the audio focus 2086 * stack if necessary. 2087 */ 2088 private class AudioFocusDeathHandler implements IBinder.DeathRecipient { 2089 private IBinder mCb; // To be notified of client's death 2090 2091 AudioFocusDeathHandler(IBinder cb) { 2092 mCb = cb; 2093 } 2094 2095 public void binderDied() { 2096 synchronized(mAudioFocusLock) { 2097 Log.w(TAG, " AudioFocus audio focus client died"); 2098 removeFocusStackEntryForClient(mCb); 2099 } 2100 } 2101 2102 public IBinder getBinder() { 2103 return mCb; 2104 } 2105 } 2106 2107 2108 /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */ 2109 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb, 2110 IAudioFocusDispatcher fd, String clientId) { 2111 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId); 2112 // the main stream type for the audio focus request is currently not used. It may 2113 // potentially be used to handle multiple stream type-dependent audio focuses. 2114 2115 // we need a valid binder callback for clients other than the AudioService's phone 2116 // state listener 2117 if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId) && ((cb == null) || !cb.pingBinder())) { 2118 Log.i(TAG, " AudioFocus DOA client for requestAudioFocus(), exiting"); 2119 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 2120 } 2121 2122 synchronized(mAudioFocusLock) { 2123 if (!canReassignAudioFocus()) { 2124 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 2125 } 2126 2127 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) { 2128 // if focus is already owned by this client and the reason for acquiring the focus 2129 // hasn't changed, don't do anything 2130 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) { 2131 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2132 } 2133 // the reason for the audio focus request has changed: remove the current top of 2134 // stack and respond as if we had a new focus owner 2135 mFocusStack.pop(); 2136 } 2137 2138 // notify current top of stack it is losing focus 2139 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 2140 try { 2141 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 2142 -1 * focusChangeHint, // loss and gain codes are inverse of each other 2143 mFocusStack.peek().mClientId); 2144 } catch (RemoteException e) { 2145 Log.e(TAG, " Failure to signal loss of focus due to "+ e); 2146 e.printStackTrace(); 2147 } 2148 } 2149 2150 // focus requester might already be somewhere below in the stack, remove it 2151 removeFocusStackEntry(clientId, false); 2152 2153 // push focus requester at the top of the audio focus stack 2154 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, false, fd, cb, 2155 clientId)); 2156 }//synchronized(mAudioFocusLock) 2157 2158 // handle the potential premature death of the new holder of the focus 2159 // (premature death == death before abandoning focus) for a client which is not the 2160 // AudioService's phone state listener 2161 if (!IN_VOICE_COMM_FOCUS_ID.equals(clientId)) { 2162 // Register for client death notification 2163 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); 2164 try { 2165 cb.linkToDeath(afdh, 0); 2166 } catch (RemoteException e) { 2167 // client has already died! 2168 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); 2169 } 2170 } 2171 2172 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2173 } 2174 2175 /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */ 2176 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) { 2177 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId); 2178 try { 2179 // this will take care of notifying the new focus owner if needed 2180 synchronized(mAudioFocusLock) { 2181 removeFocusStackEntry(clientId, true); 2182 } 2183 } catch (java.util.ConcurrentModificationException cme) { 2184 // Catching this exception here is temporary. It is here just to prevent 2185 // a crash seen when the "Silent" notification is played. This is believed to be fixed 2186 // but this try catch block is left just to be safe. 2187 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme); 2188 cme.printStackTrace(); 2189 } 2190 2191 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 2192 } 2193 2194 2195 public void unregisterAudioFocusClient(String clientId) { 2196 synchronized(mAudioFocusLock) { 2197 removeFocusStackEntry(clientId, false); 2198 } 2199 } 2200 2201 2202 //========================================================================================== 2203 // RemoteControl 2204 //========================================================================================== 2205 /** 2206 * Receiver for media button intents. Handles the dispatching of the media button event 2207 * to one of the registered listeners, or if there was none, resumes the intent broadcast 2208 * to the rest of the system. 2209 */ 2210 private class MediaButtonBroadcastReceiver extends BroadcastReceiver { 2211 @Override 2212 public void onReceive(Context context, Intent intent) { 2213 String action = intent.getAction(); 2214 if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) { 2215 return; 2216 } 2217 KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT); 2218 if (event != null) { 2219 // if in a call or ringing, do not break the current phone app behavior 2220 // TODO modify this to let the phone app specifically get the RC focus 2221 // add modify the phone app to take advantage of the new API 2222 if ((getMode() == AudioSystem.MODE_IN_CALL) || 2223 (getMode() == AudioSystem.MODE_RINGTONE)) { 2224 return; 2225 } 2226 synchronized(mRCStack) { 2227 if (!mRCStack.empty()) { 2228 // create a new intent specifically aimed at the current registered listener 2229 Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 2230 targetedIntent.putExtras(intent.getExtras()); 2231 targetedIntent.setComponent(mRCStack.peek().mReceiverComponent); 2232 // trap the current broadcast 2233 abortBroadcast(); 2234 //Log.v(TAG, " Sending intent" + targetedIntent); 2235 context.sendBroadcast(targetedIntent, null); 2236 } 2237 } 2238 } 2239 } 2240 } 2241 2242 private static class RemoteControlStackEntry { 2243 public ComponentName mReceiverComponent;// always non null 2244 // TODO implement registration expiration? 2245 //public int mRegistrationTime; 2246 2247 public RemoteControlStackEntry() { 2248 } 2249 2250 public RemoteControlStackEntry(ComponentName r) { 2251 mReceiverComponent = r; 2252 } 2253 } 2254 2255 private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>(); 2256 2257 /** 2258 * Helper function: 2259 * Display in the log the current entries in the remote control focus stack 2260 */ 2261 private void dumpRCStack(PrintWriter pw) { 2262 pw.println("\nRemote Control stack entries:"); 2263 synchronized(mRCStack) { 2264 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2265 while(stackIterator.hasNext()) { 2266 RemoteControlStackEntry fse = stackIterator.next(); 2267 pw.println(" receiver:" + fse.mReceiverComponent); 2268 } 2269 } 2270 } 2271 2272 /** 2273 * Helper function: 2274 * Set the new remote control receiver at the top of the RC focus stack 2275 */ 2276 private void pushMediaButtonReceiver(ComponentName newReceiver) { 2277 // already at top of stack? 2278 if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) { 2279 return; 2280 } 2281 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2282 while(stackIterator.hasNext()) { 2283 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 2284 if(rcse.mReceiverComponent.equals(newReceiver)) { 2285 mRCStack.remove(rcse); 2286 break; 2287 } 2288 } 2289 mRCStack.push(new RemoteControlStackEntry(newReceiver)); 2290 } 2291 2292 /** 2293 * Helper function: 2294 * Remove the remote control receiver from the RC focus stack 2295 */ 2296 private void removeMediaButtonReceiver(ComponentName newReceiver) { 2297 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 2298 while(stackIterator.hasNext()) { 2299 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 2300 if(rcse.mReceiverComponent.equals(newReceiver)) { 2301 mRCStack.remove(rcse); 2302 break; 2303 } 2304 } 2305 } 2306 2307 2308 /** see AudioManager.registerMediaButtonEventReceiver(ComponentName eventReceiver) */ 2309 public void registerMediaButtonEventReceiver(ComponentName eventReceiver) { 2310 Log.i(TAG, " Remote Control registerMediaButtonEventReceiver() for " + eventReceiver); 2311 2312 synchronized(mRCStack) { 2313 pushMediaButtonReceiver(eventReceiver); 2314 } 2315 } 2316 2317 /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */ 2318 public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) { 2319 Log.i(TAG, " Remote Control unregisterMediaButtonEventReceiver() for " + eventReceiver); 2320 2321 synchronized(mRCStack) { 2322 removeMediaButtonReceiver(eventReceiver); 2323 } 2324 } 2325 2326 2327 @Override 2328 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 2329 // TODO probably a lot more to do here than just the audio focus and remote control stacks 2330 dumpFocusStack(pw); 2331 dumpRCStack(pw); 2332 } 2333 2334 2335} 2336