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