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