AudioService.java revision ab624c2fd4f0fad6ba7f422a960942e5a7035cea
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 static android.Manifest.permission.REMOTE_AUDIO_PLAYBACK; 20import static android.media.AudioManager.RINGER_MODE_NORMAL; 21import static android.media.AudioManager.RINGER_MODE_SILENT; 22import static android.media.AudioManager.RINGER_MODE_VIBRATE; 23 24import android.app.Activity; 25import android.app.ActivityManagerNative; 26import android.app.KeyguardManager; 27import android.app.PendingIntent; 28import android.app.PendingIntent.CanceledException; 29import android.app.PendingIntent.OnFinished; 30import android.bluetooth.BluetoothA2dp; 31import android.bluetooth.BluetoothAdapter; 32import android.bluetooth.BluetoothClass; 33import android.bluetooth.BluetoothDevice; 34import android.bluetooth.BluetoothHeadset; 35import android.bluetooth.BluetoothProfile; 36import android.content.ActivityNotFoundException; 37import android.content.BroadcastReceiver; 38import android.content.ComponentName; 39import android.content.ContentResolver; 40import android.content.Context; 41import android.content.Intent; 42import android.content.IntentFilter; 43import android.content.pm.PackageManager; 44import android.content.res.Configuration; 45import android.database.ContentObserver; 46import android.media.MediaPlayer.OnCompletionListener; 47import android.media.MediaPlayer.OnErrorListener; 48import android.os.Binder; 49import android.os.Bundle; 50import android.os.Environment; 51import android.os.Handler; 52import android.os.IBinder; 53import android.os.Looper; 54import android.os.Message; 55import android.os.PowerManager; 56import android.os.RemoteException; 57import android.os.ServiceManager; 58import android.os.SystemProperties; 59import android.os.Vibrator; 60import android.provider.Settings; 61import android.provider.Settings.System; 62import android.telephony.PhoneStateListener; 63import android.telephony.TelephonyManager; 64import android.util.Log; 65import android.view.KeyEvent; 66import android.view.VolumePanel; 67 68import com.android.internal.telephony.ITelephony; 69 70import java.io.FileDescriptor; 71import java.io.IOException; 72import java.io.PrintWriter; 73import java.util.ArrayList; 74import java.util.concurrent.ConcurrentHashMap; 75import java.util.HashMap; 76import java.util.Iterator; 77import java.util.List; 78import java.util.Map; 79import java.util.NoSuchElementException; 80import java.util.Set; 81import java.util.Stack; 82 83/** 84 * The implementation of the volume manager service. 85 * <p> 86 * This implementation focuses on delivering a responsive UI. Most methods are 87 * asynchronous to external calls. For example, the task of setting a volume 88 * will update our internal state, but in a separate thread will set the system 89 * volume and later persist to the database. Similarly, setting the ringer mode 90 * will update the state and broadcast a change and in a separate thread later 91 * persist the ringer mode. 92 * 93 * @hide 94 */ 95public class AudioService extends IAudioService.Stub implements OnFinished { 96 97 private static final String TAG = "AudioService"; 98 99 /** Debug remote control client/display feature */ 100 protected static final boolean DEBUG_RC = false; 101 102 /** How long to delay before persisting a change in volume/ringer mode. */ 103 private static final int PERSIST_DELAY = 500; 104 105 private Context mContext; 106 private ContentResolver mContentResolver; 107 private boolean mVoiceCapable; 108 109 /** The UI */ 110 private VolumePanel mVolumePanel; 111 112 // sendMsg() flags 113 /** If the msg is already queued, replace it with this one. */ 114 private static final int SENDMSG_REPLACE = 0; 115 /** If the msg is already queued, ignore this one and leave the old. */ 116 private static final int SENDMSG_NOOP = 1; 117 /** If the msg is already queued, queue this one and leave the old. */ 118 private static final int SENDMSG_QUEUE = 2; 119 120 // AudioHandler message.whats 121 private static final int MSG_SET_DEVICE_VOLUME = 0; 122 private static final int MSG_PERSIST_VOLUME = 1; 123 private static final int MSG_PERSIST_MASTER_VOLUME = 2; 124 private static final int MSG_PERSIST_RINGER_MODE = 3; 125 private static final int MSG_MEDIA_SERVER_DIED = 4; 126 private static final int MSG_MEDIA_SERVER_STARTED = 5; 127 private static final int MSG_PLAY_SOUND_EFFECT = 6; 128 private static final int MSG_BTA2DP_DOCK_TIMEOUT = 7; 129 private static final int MSG_LOAD_SOUND_EFFECTS = 8; 130 private static final int MSG_SET_FORCE_USE = 9; 131 private static final int MSG_PERSIST_MEDIABUTTONRECEIVER = 10; 132 private static final int MSG_BT_HEADSET_CNCT_FAILED = 11; 133 private static final int MSG_RCDISPLAY_CLEAR = 12; 134 private static final int MSG_RCDISPLAY_UPDATE = 13; 135 private static final int MSG_SET_ALL_VOLUMES = 14; 136 private static final int MSG_PERSIST_MASTER_VOLUME_MUTE = 15; 137 138 139 // flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be 140 // persisted 141 private static final int PERSIST_CURRENT = 0x1; 142 private static final int PERSIST_LAST_AUDIBLE = 0x2; 143 144 private static final int BTA2DP_DOCK_TIMEOUT_MILLIS = 8000; 145 // Timeout for connection to bluetooth headset service 146 private static final int BT_HEADSET_CNCT_TIMEOUT_MS = 3000; 147 148 /** @see AudioSystemThread */ 149 private AudioSystemThread mAudioSystemThread; 150 /** @see AudioHandler */ 151 private AudioHandler mAudioHandler; 152 /** @see VolumeStreamState */ 153 private VolumeStreamState[] mStreamStates; 154 private SettingsObserver mSettingsObserver; 155 156 private int mMode; 157 // protects mRingerMode 158 private final Object mSettingsLock = new Object(); 159 160 private boolean mMediaServerOk; 161 162 private SoundPool mSoundPool; 163 private final Object mSoundEffectsLock = new Object(); 164 private static final int NUM_SOUNDPOOL_CHANNELS = 4; 165 private static final int SOUND_EFFECT_VOLUME = 1000; 166 167 // Internally master volume is a float in the 0.0 - 1.0 range, 168 // but to support integer based AudioManager API we translate it to 0 - 100 169 private static final int MAX_MASTER_VOLUME = 100; 170 171 // Maximum volume adjust steps allowed in a single batch call. 172 private static final int MAX_BATCH_VOLUME_ADJUST_STEPS = 4; 173 174 /* Sound effect file names */ 175 private static final String SOUND_EFFECTS_PATH = "/media/audio/ui/"; 176 private static final String[] SOUND_EFFECT_FILES = new String[] { 177 "Effect_Tick.ogg", 178 "KeypressStandard.ogg", 179 "KeypressSpacebar.ogg", 180 "KeypressDelete.ogg", 181 "KeypressReturn.ogg" 182 }; 183 184 /* Sound effect file name mapping sound effect id (AudioManager.FX_xxx) to 185 * file index in SOUND_EFFECT_FILES[] (first column) and indicating if effect 186 * uses soundpool (second column) */ 187 private final int[][] SOUND_EFFECT_FILES_MAP = new int[][] { 188 {0, -1}, // FX_KEY_CLICK 189 {0, -1}, // FX_FOCUS_NAVIGATION_UP 190 {0, -1}, // FX_FOCUS_NAVIGATION_DOWN 191 {0, -1}, // FX_FOCUS_NAVIGATION_LEFT 192 {0, -1}, // FX_FOCUS_NAVIGATION_RIGHT 193 {1, -1}, // FX_KEYPRESS_STANDARD 194 {2, -1}, // FX_KEYPRESS_SPACEBAR 195 {3, -1}, // FX_FOCUS_DELETE 196 {4, -1} // FX_FOCUS_RETURN 197 }; 198 199 /** @hide Maximum volume index values for audio streams */ 200 private final int[] MAX_STREAM_VOLUME = new int[] { 201 5, // STREAM_VOICE_CALL 202 7, // STREAM_SYSTEM 203 7, // STREAM_RING 204 15, // STREAM_MUSIC 205 7, // STREAM_ALARM 206 7, // STREAM_NOTIFICATION 207 15, // STREAM_BLUETOOTH_SCO 208 7, // STREAM_SYSTEM_ENFORCED 209 15, // STREAM_DTMF 210 15 // STREAM_TTS 211 }; 212 /* mStreamVolumeAlias[] indicates for each stream if it uses the volume settings 213 * of another stream: This avoids multiplying the volume settings for hidden 214 * stream types that follow other stream behavior for volume settings 215 * NOTE: do not create loops in aliases! 216 * Some streams alias to different streams according to device category (phone or tablet) or 217 * use case (in call s off call...).See updateStreamVolumeAlias() for more details 218 * mStreamVolumeAlias contains the default aliases for a voice capable device (phone) and 219 * STREAM_VOLUME_ALIAS_NON_VOICE for a non voice capable device (tablet).*/ 220 private final int[] STREAM_VOLUME_ALIAS = new int[] { 221 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL 222 AudioSystem.STREAM_RING, // STREAM_SYSTEM 223 AudioSystem.STREAM_RING, // STREAM_RING 224 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC 225 AudioSystem.STREAM_ALARM, // STREAM_ALARM 226 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION 227 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO 228 AudioSystem.STREAM_RING, // STREAM_SYSTEM_ENFORCED 229 AudioSystem.STREAM_RING, // STREAM_DTMF 230 AudioSystem.STREAM_MUSIC // STREAM_TTS 231 }; 232 private final int[] STREAM_VOLUME_ALIAS_NON_VOICE = new int[] { 233 AudioSystem.STREAM_VOICE_CALL, // STREAM_VOICE_CALL 234 AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM 235 AudioSystem.STREAM_RING, // STREAM_RING 236 AudioSystem.STREAM_MUSIC, // STREAM_MUSIC 237 AudioSystem.STREAM_ALARM, // STREAM_ALARM 238 AudioSystem.STREAM_RING, // STREAM_NOTIFICATION 239 AudioSystem.STREAM_BLUETOOTH_SCO, // STREAM_BLUETOOTH_SCO 240 AudioSystem.STREAM_MUSIC, // STREAM_SYSTEM_ENFORCED 241 AudioSystem.STREAM_MUSIC, // STREAM_DTMF 242 AudioSystem.STREAM_MUSIC // STREAM_TTS 243 }; 244 private int[] mStreamVolumeAlias; 245 246 // stream names used by dumpStreamStates() 247 private final String[] STREAM_NAMES = new String[] { 248 "STREAM_VOICE_CALL", 249 "STREAM_SYSTEM", 250 "STREAM_RING", 251 "STREAM_MUSIC", 252 "STREAM_ALARM", 253 "STREAM_NOTIFICATION", 254 "STREAM_BLUETOOTH_SCO", 255 "STREAM_SYSTEM_ENFORCED", 256 "STREAM_DTMF", 257 "STREAM_TTS" 258 }; 259 260 private final AudioSystem.ErrorCallback mAudioSystemCallback = new AudioSystem.ErrorCallback() { 261 public void onError(int error) { 262 switch (error) { 263 case AudioSystem.AUDIO_STATUS_SERVER_DIED: 264 if (mMediaServerOk) { 265 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0, 266 null, 1500); 267 mMediaServerOk = false; 268 } 269 break; 270 case AudioSystem.AUDIO_STATUS_OK: 271 if (!mMediaServerOk) { 272 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_STARTED, SENDMSG_NOOP, 0, 0, 273 null, 0); 274 mMediaServerOk = true; 275 } 276 break; 277 default: 278 break; 279 } 280 } 281 }; 282 283 /** 284 * Current ringer mode from one of {@link AudioManager#RINGER_MODE_NORMAL}, 285 * {@link AudioManager#RINGER_MODE_SILENT}, or 286 * {@link AudioManager#RINGER_MODE_VIBRATE}. 287 */ 288 // protected by mSettingsLock 289 private int mRingerMode; 290 291 /** @see System#MODE_RINGER_STREAMS_AFFECTED */ 292 private int mRingerModeAffectedStreams; 293 294 // Streams currently muted by ringer mode 295 private int mRingerModeMutedStreams; 296 297 /** @see System#MUTE_STREAMS_AFFECTED */ 298 private int mMuteAffectedStreams; 299 300 /** 301 * NOTE: setVibrateSetting(), getVibrateSetting(), shouldVibrate() are deprecated. 302 * mVibrateSetting is just maintained during deprecation period but vibration policy is 303 * now only controlled by mHasVibrator and mRingerMode 304 */ 305 private int mVibrateSetting; 306 307 // Is there a vibrator 308 private final boolean mHasVibrator; 309 310 // Broadcast receiver for device connections intent broadcasts 311 private final BroadcastReceiver mReceiver = new AudioServiceBroadcastReceiver(); 312 313 // Used to alter media button redirection when the phone is ringing. 314 private boolean mIsRinging = false; 315 316 // Devices currently connected 317 private final HashMap <Integer, String> mConnectedDevices = new HashMap <Integer, String>(); 318 319 // Forced device usage for communications 320 private int mForcedUseForComm; 321 322 // True if we have master volume support 323 private final boolean mUseMasterVolume; 324 325 private final int[] mMasterVolumeRamp; 326 327 // List of binder death handlers for setMode() client processes. 328 // The last process to have called setMode() is at the top of the list. 329 private final ArrayList <SetModeDeathHandler> mSetModeDeathHandlers = new ArrayList <SetModeDeathHandler>(); 330 331 // List of clients having issued a SCO start request 332 private final ArrayList <ScoClient> mScoClients = new ArrayList <ScoClient>(); 333 334 // BluetoothHeadset API to control SCO connection 335 private BluetoothHeadset mBluetoothHeadset; 336 337 // Bluetooth headset device 338 private BluetoothDevice mBluetoothHeadsetDevice; 339 340 // Indicate if SCO audio connection is currently active and if the initiator is 341 // audio service (internal) or bluetooth headset (external) 342 private int mScoAudioState; 343 // SCO audio state is not active 344 private static final int SCO_STATE_INACTIVE = 0; 345 // SCO audio activation request waiting for headset service to connect 346 private static final int SCO_STATE_ACTIVATE_REQ = 1; 347 // SCO audio state is active or starting due to a local request to start a virtual call 348 private static final int SCO_STATE_ACTIVE_INTERNAL = 3; 349 // SCO audio deactivation request waiting for headset service to connect 350 private static final int SCO_STATE_DEACTIVATE_REQ = 5; 351 352 // SCO audio state is active due to an action in BT handsfree (either voice recognition or 353 // in call audio) 354 private static final int SCO_STATE_ACTIVE_EXTERNAL = 2; 355 // Deactivation request for all SCO connections (initiated by audio mode change) 356 // waiting for headset service to connect 357 private static final int SCO_STATE_DEACTIVATE_EXT_REQ = 4; 358 359 // Current connection state indicated by bluetooth headset 360 private int mScoConnectionState; 361 362 // true if boot sequence has been completed 363 private boolean mBootCompleted; 364 // listener for SoundPool sample load completion indication 365 private SoundPoolCallback mSoundPoolCallBack; 366 // thread for SoundPool listener 367 private SoundPoolListenerThread mSoundPoolListenerThread; 368 // message looper for SoundPool listener 369 private Looper mSoundPoolLooper = null; 370 // default volume applied to sound played with playSoundEffect() 371 private static final int SOUND_EFFECT_DEFAULT_VOLUME_DB = 0; 372 // volume applied to sound played with playSoundEffect() read from ro.config.sound_fx_volume 373 private int SOUND_EFFECT_VOLUME_DB; 374 // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification 375 // stopped 376 private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000; 377 // previous volume adjustment direction received by checkForRingerModeChange() 378 private int mPrevVolDirection = AudioManager.ADJUST_SAME; 379 // Keyguard manager proxy 380 private KeyguardManager mKeyguardManager; 381 // mVolumeControlStream is set by VolumePanel to temporarily force the stream type which volume 382 // is controlled by Vol keys. 383 private int mVolumeControlStream = -1; 384 private final Object mForceControlStreamLock = new Object(); 385 // VolumePanel is currently the only client of forceVolumeControlStream() and runs in system 386 // server process so in theory it is not necessary to monitor the client death. 387 // However it is good to be ready for future evolutions. 388 private ForceControlStreamClient mForceControlStreamClient = null; 389 // Used to play ringtones outside system_server 390 private volatile IRingtonePlayer mRingtonePlayer; 391 392 private int mDeviceOrientation = Configuration.ORIENTATION_UNDEFINED; 393 394 /////////////////////////////////////////////////////////////////////////// 395 // Construction 396 /////////////////////////////////////////////////////////////////////////// 397 398 /** @hide */ 399 public AudioService(Context context) { 400 mContext = context; 401 mContentResolver = context.getContentResolver(); 402 mVoiceCapable = mContext.getResources().getBoolean( 403 com.android.internal.R.bool.config_voice_capable); 404 405 PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE); 406 mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "mediaKeyEvent"); 407 408 Vibrator vibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE); 409 mHasVibrator = vibrator == null ? false : vibrator.hasVibrator(); 410 411 // Intialized volume 412 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL] = SystemProperties.getInt( 413 "ro.config.vc_call_vol_steps", 414 MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]); 415 416 SOUND_EFFECT_VOLUME_DB = SystemProperties.getInt( 417 "ro.config.sound_fx_volume", 418 SOUND_EFFECT_DEFAULT_VOLUME_DB); 419 420 mVolumePanel = new VolumePanel(context, this); 421 mMode = AudioSystem.MODE_NORMAL; 422 mForcedUseForComm = AudioSystem.FORCE_NONE; 423 createAudioSystemThread(); 424 readPersistedSettings(); 425 mSettingsObserver = new SettingsObserver(); 426 updateStreamVolumeAlias(false /*updateVolumes*/); 427 createStreamStates(); 428 429 mMediaServerOk = true; 430 431 // Call setRingerModeInt() to apply correct mute 432 // state on streams affected by ringer mode. 433 mRingerModeMutedStreams = 0; 434 setRingerModeInt(getRingerMode(), false); 435 436 AudioSystem.setErrorCallback(mAudioSystemCallback); 437 438 // Register for device connection intent broadcasts. 439 IntentFilter intentFilter = 440 new IntentFilter(Intent.ACTION_HEADSET_PLUG); 441 442 intentFilter.addAction(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 443 intentFilter.addAction(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED); 444 intentFilter.addAction(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 445 intentFilter.addAction(Intent.ACTION_DOCK_EVENT); 446 intentFilter.addAction(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG); 447 intentFilter.addAction(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG); 448 intentFilter.addAction(Intent.ACTION_HDMI_AUDIO_PLUG); 449 intentFilter.addAction(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG); 450 intentFilter.addAction(Intent.ACTION_USB_AUDIO_DEVICE_PLUG); 451 intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED); 452 intentFilter.addAction(Intent.ACTION_SCREEN_ON); 453 intentFilter.addAction(Intent.ACTION_SCREEN_OFF); 454 455 // Register a configuration change listener only if requested by system properties 456 // to monitor orientation changes (off by default) 457 if (SystemProperties.getBoolean("ro.audio.monitorOrientation", false)) { 458 Log.v(TAG, "monitoring device orientation"); 459 intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED); 460 // initialize orientation in AudioSystem 461 setOrientationForAudioSystem(); 462 } 463 464 context.registerReceiver(mReceiver, intentFilter); 465 466 // Register for package removal intent broadcasts for media button receiver persistence 467 IntentFilter pkgFilter = new IntentFilter(); 468 pkgFilter.addAction(Intent.ACTION_PACKAGE_REMOVED); 469 pkgFilter.addDataScheme("package"); 470 context.registerReceiver(mReceiver, pkgFilter); 471 472 // Register for phone state monitoring 473 TelephonyManager tmgr = (TelephonyManager) 474 context.getSystemService(Context.TELEPHONY_SERVICE); 475 tmgr.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); 476 477 mUseMasterVolume = context.getResources().getBoolean( 478 com.android.internal.R.bool.config_useMasterVolume); 479 restoreMasterVolume(); 480 481 mMasterVolumeRamp = context.getResources().getIntArray( 482 com.android.internal.R.array.config_masterVolumeRamp); 483 } 484 485 private void createAudioSystemThread() { 486 mAudioSystemThread = new AudioSystemThread(); 487 mAudioSystemThread.start(); 488 waitForAudioHandlerCreation(); 489 } 490 491 /** Waits for the volume handler to be created by the other thread. */ 492 private void waitForAudioHandlerCreation() { 493 synchronized(this) { 494 while (mAudioHandler == null) { 495 try { 496 // Wait for mAudioHandler to be set by the other thread 497 wait(); 498 } catch (InterruptedException e) { 499 Log.e(TAG, "Interrupted while waiting on volume handler."); 500 } 501 } 502 } 503 } 504 505 private void checkAllAliasStreamVolumes() { 506 int numStreamTypes = AudioSystem.getNumStreamTypes(); 507 for (int streamType = 0; streamType < numStreamTypes; streamType++) { 508 if (streamType != mStreamVolumeAlias[streamType]) { 509 mStreamStates[streamType]. 510 setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], 511 false /*lastAudible*/); 512 mStreamStates[streamType]. 513 setAllIndexes(mStreamStates[mStreamVolumeAlias[streamType]], 514 true /*lastAudible*/); 515 } 516 // apply stream volume 517 if (mStreamStates[streamType].muteCount() == 0) { 518 mStreamStates[streamType].applyAllVolumes(); 519 } 520 } 521 } 522 523 private void createStreamStates() { 524 int numStreamTypes = AudioSystem.getNumStreamTypes(); 525 VolumeStreamState[] streams = mStreamStates = new VolumeStreamState[numStreamTypes]; 526 527 for (int i = 0; i < numStreamTypes; i++) { 528 streams[i] = new VolumeStreamState(System.VOLUME_SETTINGS[mStreamVolumeAlias[i]], i); 529 } 530 531 checkAllAliasStreamVolumes(); 532 } 533 534 private void dumpStreamStates(PrintWriter pw) { 535 pw.println("\nStream volumes (device: index)"); 536 int numStreamTypes = AudioSystem.getNumStreamTypes(); 537 for (int i = 0; i < numStreamTypes; i++) { 538 pw.println("- "+STREAM_NAMES[i]+":"); 539 mStreamStates[i].dump(pw); 540 pw.println(""); 541 } 542 } 543 544 545 private void updateStreamVolumeAlias(boolean updateVolumes) { 546 int dtmfStreamAlias; 547 if (mVoiceCapable) { 548 mStreamVolumeAlias = STREAM_VOLUME_ALIAS; 549 dtmfStreamAlias = AudioSystem.STREAM_RING; 550 } else { 551 mStreamVolumeAlias = STREAM_VOLUME_ALIAS_NON_VOICE; 552 dtmfStreamAlias = AudioSystem.STREAM_MUSIC; 553 } 554 if (isInCommunication()) { 555 dtmfStreamAlias = AudioSystem.STREAM_VOICE_CALL; 556 } 557 mStreamVolumeAlias[AudioSystem.STREAM_DTMF] = dtmfStreamAlias; 558 if (updateVolumes) { 559 mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias], 560 false /*lastAudible*/); 561 mStreamStates[AudioSystem.STREAM_DTMF].setAllIndexes(mStreamStates[dtmfStreamAlias], 562 true /*lastAudible*/); 563 sendMsg(mAudioHandler, 564 MSG_SET_ALL_VOLUMES, 565 SENDMSG_QUEUE, 566 0, 567 0, 568 mStreamStates[AudioSystem.STREAM_DTMF], 0); 569 } 570 } 571 572 private void readPersistedSettings() { 573 final ContentResolver cr = mContentResolver; 574 575 int ringerModeFromSettings = 576 System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL); 577 int ringerMode = ringerModeFromSettings; 578 // sanity check in case the settings are restored from a device with incompatible 579 // ringer modes 580 if (!AudioManager.isValidRingerMode(ringerMode)) { 581 ringerMode = AudioManager.RINGER_MODE_NORMAL; 582 } 583 if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) { 584 ringerMode = AudioManager.RINGER_MODE_SILENT; 585 } 586 if (ringerMode != ringerModeFromSettings) { 587 System.putInt(cr, System.MODE_RINGER, ringerMode); 588 } 589 synchronized(mSettingsLock) { 590 mRingerMode = ringerMode; 591 } 592 593 // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting 594 // are still needed while setVibrateSetting() and getVibrateSetting() are being deprecated. 595 mVibrateSetting = getValueForVibrateSetting(0, 596 AudioManager.VIBRATE_TYPE_NOTIFICATION, 597 mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT 598 : AudioManager.VIBRATE_SETTING_OFF); 599 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, 600 AudioManager.VIBRATE_TYPE_RINGER, 601 mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT 602 : AudioManager.VIBRATE_SETTING_OFF); 603 604 // make sure settings for ringer mode are consistent with device type: non voice capable 605 // devices (tablets) include media stream in silent mode whereas phones don't. 606 mRingerModeAffectedStreams = Settings.System.getInt(cr, 607 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 608 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)| 609 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED))); 610 if (mVoiceCapable) { 611 mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC); 612 } else { 613 mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC); 614 } 615 Settings.System.putInt(cr, 616 Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams); 617 618 mMuteAffectedStreams = System.getInt(cr, 619 System.MUTE_STREAMS_AFFECTED, 620 ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM))); 621 622 boolean masterMute = System.getInt(cr, System.VOLUME_MASTER_MUTE, 0) == 1; 623 AudioSystem.setMasterMute(masterMute); 624 broadcastMasterMuteStatus(masterMute); 625 626 // Each stream will read its own persisted settings 627 628 // Broadcast the sticky intent 629 broadcastRingerMode(ringerMode); 630 631 // Broadcast vibrate settings 632 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_RINGER); 633 broadcastVibrateSetting(AudioManager.VIBRATE_TYPE_NOTIFICATION); 634 635 // Restore the default media button receiver from the system settings 636 restoreMediaButtonReceiver(); 637 } 638 639 private int rescaleIndex(int index, int srcStream, int dstStream) { 640 return (index * mStreamStates[dstStream].getMaxIndex() + mStreamStates[srcStream].getMaxIndex() / 2) / mStreamStates[srcStream].getMaxIndex(); 641 } 642 643 /////////////////////////////////////////////////////////////////////////// 644 // IPC methods 645 /////////////////////////////////////////////////////////////////////////// 646 647 /** @see AudioManager#adjustVolume(int, int) */ 648 public void adjustVolume(int direction, int flags) { 649 adjustSuggestedStreamVolume(direction, AudioManager.USE_DEFAULT_STREAM_TYPE, flags); 650 } 651 652 /** @see AudioManager#adjustVolume(int, int, int) */ 653 public void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags) { 654 655 int streamType; 656 if (mVolumeControlStream != -1) { 657 streamType = mVolumeControlStream; 658 } else { 659 streamType = getActiveStreamType(suggestedStreamType); 660 } 661 662 // Play sounds on STREAM_RING only and if lock screen is not on. 663 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && 664 ((mStreamVolumeAlias[streamType] != AudioSystem.STREAM_RING) 665 || (mKeyguardManager != null && mKeyguardManager.isKeyguardLocked()))) { 666 flags &= ~AudioManager.FLAG_PLAY_SOUND; 667 } 668 669 adjustStreamVolume(streamType, direction, flags); 670 } 671 672 /** @see AudioManager#adjustStreamVolume(int, int, int) */ 673 public void adjustStreamVolume(int streamType, int direction, int flags) { 674 ensureValidDirection(direction); 675 ensureValidStreamType(streamType); 676 677 // use stream type alias here so that streams with same alias have the same behavior, 678 // including with regard to silent mode control (e.g the use of STREAM_RING below and in 679 // checkForRingerModeChange() in place of STREAM_RING or STREAM_NOTIFICATION) 680 int streamTypeAlias = mStreamVolumeAlias[streamType]; 681 VolumeStreamState streamState = mStreamStates[streamTypeAlias]; 682 683 final int device = getDeviceForStream(streamTypeAlias); 684 // get last audible index if stream is muted, current index otherwise 685 final int aliasIndex = streamState.getIndex(device, 686 (streamState.muteCount() != 0) /* lastAudible */); 687 boolean adjustVolume = true; 688 689 // convert one UI step (+/-1) into a number of internal units on the stream alias 690 int step = rescaleIndex(10, streamType, streamTypeAlias); 691 692 // If either the client forces allowing ringer modes for this adjustment, 693 // or the stream type is one that is affected by ringer modes 694 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || 695 (streamTypeAlias == getMasterStreamType())) { 696 int ringerMode = getRingerMode(); 697 // do not vibrate if already in vibrate mode 698 if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) { 699 flags &= ~AudioManager.FLAG_VIBRATE; 700 } 701 // Check if the ringer mode changes with this volume adjustment. If 702 // it does, it will handle adjusting the volume, so we won't below 703 adjustVolume = checkForRingerModeChange(aliasIndex, direction, step); 704 if ((streamTypeAlias == getMasterStreamType()) && 705 (mRingerMode == AudioManager.RINGER_MODE_SILENT)) { 706 streamState.setLastAudibleIndex(0, device); 707 } 708 } 709 710 // If stream is muted, adjust last audible index only 711 int index; 712 final int oldIndex = mStreamStates[streamType].getIndex(device, 713 (mStreamStates[streamType].muteCount() != 0) /* lastAudible */); 714 715 if (streamState.muteCount() != 0) { 716 if (adjustVolume) { 717 // Post a persist volume msg 718 // no need to persist volume on all streams sharing the same alias 719 streamState.adjustLastAudibleIndex(direction * step, device); 720 sendMsg(mAudioHandler, 721 MSG_PERSIST_VOLUME, 722 SENDMSG_QUEUE, 723 PERSIST_LAST_AUDIBLE, 724 device, 725 streamState, 726 PERSIST_DELAY); 727 } 728 index = mStreamStates[streamType].getIndex(device, true /* lastAudible */); 729 } else { 730 if (adjustVolume && streamState.adjustIndex(direction * step, device)) { 731 // Post message to set system volume (it in turn will post a message 732 // to persist). Do not change volume if stream is muted. 733 sendMsg(mAudioHandler, 734 MSG_SET_DEVICE_VOLUME, 735 SENDMSG_QUEUE, 736 device, 737 0, 738 streamState, 739 0); 740 } 741 index = mStreamStates[streamType].getIndex(device, false /* lastAudible */); 742 } 743 744 sendVolumeUpdate(streamType, oldIndex, index, flags); 745 } 746 747 /** @see AudioManager#adjustMasterVolume(int) */ 748 public void adjustMasterVolume(int steps, int flags) { 749 ensureValidSteps(steps); 750 int volume = Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME); 751 int delta = 0; 752 int numSteps = Math.abs(steps); 753 int direction = steps > 0 ? AudioManager.ADJUST_RAISE : AudioManager.ADJUST_LOWER; 754 for (int i = 0; i < numSteps; ++i) { 755 delta = findVolumeDelta(direction, volume); 756 volume += delta; 757 } 758 759 //Log.d(TAG, "adjustMasterVolume volume: " + volume + " steps: " + steps); 760 setMasterVolume(volume, flags); 761 } 762 763 /** @see AudioManager#setStreamVolume(int, int, int) */ 764 public void setStreamVolume(int streamType, int index, int flags) { 765 ensureValidStreamType(streamType); 766 VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]]; 767 768 final int device = getDeviceForStream(streamType); 769 // get last audible index if stream is muted, current index otherwise 770 final int oldIndex = streamState.getIndex(device, 771 (streamState.muteCount() != 0) /* lastAudible */); 772 773 index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]); 774 775 // setting volume on master stream type also controls silent mode 776 if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) || 777 (mStreamVolumeAlias[streamType] == getMasterStreamType())) { 778 int newRingerMode; 779 if (index == 0) { 780 newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE 781 : AudioManager.RINGER_MODE_SILENT; 782 setStreamVolumeInt(mStreamVolumeAlias[streamType], 783 index, 784 device, 785 false, 786 true); 787 } else { 788 newRingerMode = AudioManager.RINGER_MODE_NORMAL; 789 } 790 setRingerMode(newRingerMode); 791 } 792 793 setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false, true); 794 // get last audible index if stream is muted, current index otherwise 795 index = mStreamStates[streamType].getIndex(device, 796 (mStreamStates[streamType].muteCount() != 0) /* lastAudible */); 797 798 sendVolumeUpdate(streamType, oldIndex, index, flags); 799 } 800 801 /** @see AudioManager#forceVolumeControlStream(int) */ 802 public void forceVolumeControlStream(int streamType, IBinder cb) { 803 synchronized(mForceControlStreamLock) { 804 mVolumeControlStream = streamType; 805 if (mVolumeControlStream == -1) { 806 if (mForceControlStreamClient != null) { 807 mForceControlStreamClient.release(); 808 mForceControlStreamClient = null; 809 } 810 } else { 811 mForceControlStreamClient = new ForceControlStreamClient(cb); 812 } 813 } 814 } 815 816 private class ForceControlStreamClient implements IBinder.DeathRecipient { 817 private IBinder mCb; // To be notified of client's death 818 819 ForceControlStreamClient(IBinder cb) { 820 if (cb != null) { 821 try { 822 cb.linkToDeath(this, 0); 823 } catch (RemoteException e) { 824 // Client has died! 825 Log.w(TAG, "ForceControlStreamClient() could not link to "+cb+" binder death"); 826 cb = null; 827 } 828 } 829 mCb = cb; 830 } 831 832 public void binderDied() { 833 synchronized(mForceControlStreamLock) { 834 Log.w(TAG, "SCO client died"); 835 if (mForceControlStreamClient != this) { 836 Log.w(TAG, "unregistered control stream client died"); 837 } else { 838 mForceControlStreamClient = null; 839 mVolumeControlStream = -1; 840 } 841 } 842 } 843 844 public void release() { 845 if (mCb != null) { 846 mCb.unlinkToDeath(this, 0); 847 mCb = null; 848 } 849 } 850 } 851 852 private int findVolumeDelta(int direction, int volume) { 853 int delta = 0; 854 if (direction == AudioManager.ADJUST_RAISE) { 855 if (volume == MAX_MASTER_VOLUME) { 856 return 0; 857 } 858 // This is the default value if we make it to the end 859 delta = mMasterVolumeRamp[1]; 860 // If we're raising the volume move down the ramp array until we 861 // find the volume we're above and use that groups delta. 862 for (int i = mMasterVolumeRamp.length - 1; i > 1; i -= 2) { 863 if (volume >= mMasterVolumeRamp[i - 1]) { 864 delta = mMasterVolumeRamp[i]; 865 break; 866 } 867 } 868 } else if (direction == AudioManager.ADJUST_LOWER){ 869 if (volume == 0) { 870 return 0; 871 } 872 int length = mMasterVolumeRamp.length; 873 // This is the default value if we make it to the end 874 delta = -mMasterVolumeRamp[length - 1]; 875 // If we're lowering the volume move up the ramp array until we 876 // find the volume we're below and use the group below it's delta 877 for (int i = 2; i < length; i += 2) { 878 if (volume <= mMasterVolumeRamp[i]) { 879 delta = -mMasterVolumeRamp[i - 1]; 880 break; 881 } 882 } 883 } 884 return delta; 885 } 886 887 // UI update and Broadcast Intent 888 private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) { 889 if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) { 890 streamType = AudioSystem.STREAM_NOTIFICATION; 891 } 892 893 mVolumePanel.postVolumeChanged(streamType, flags); 894 895 oldIndex = (oldIndex + 5) / 10; 896 index = (index + 5) / 10; 897 Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION); 898 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType); 899 intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index); 900 intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex); 901 mContext.sendBroadcast(intent); 902 } 903 904 // UI update and Broadcast Intent 905 private void sendMasterVolumeUpdate(int flags, int oldVolume, int newVolume) { 906 mVolumePanel.postMasterVolumeChanged(flags); 907 908 Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION); 909 intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume); 910 intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume); 911 mContext.sendBroadcast(intent); 912 } 913 914 // UI update and Broadcast Intent 915 private void sendMasterMuteUpdate(boolean muted, int flags) { 916 mVolumePanel.postMasterMuteChanged(flags); 917 broadcastMasterMuteStatus(muted); 918 } 919 920 private void broadcastMasterMuteStatus(boolean muted) { 921 Intent intent = new Intent(AudioManager.MASTER_MUTE_CHANGED_ACTION); 922 intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted); 923 intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 924 | Intent.FLAG_RECEIVER_REPLACE_PENDING); 925 long origCallerIdentityToken = Binder.clearCallingIdentity(); 926 mContext.sendStickyBroadcast(intent); 927 Binder.restoreCallingIdentity(origCallerIdentityToken); 928 } 929 930 /** 931 * Sets the stream state's index, and posts a message to set system volume. 932 * This will not call out to the UI. Assumes a valid stream type. 933 * 934 * @param streamType Type of the stream 935 * @param index Desired volume index of the stream 936 * @param device the device whose volume must be changed 937 * @param force If true, set the volume even if the desired volume is same 938 * as the current volume. 939 * @param lastAudible If true, stores new index as last audible one 940 */ 941 private void setStreamVolumeInt(int streamType, 942 int index, 943 int device, 944 boolean force, 945 boolean lastAudible) { 946 VolumeStreamState streamState = mStreamStates[streamType]; 947 948 // If stream is muted, set last audible index only 949 if (streamState.muteCount() != 0) { 950 // Do not allow last audible index to be 0 951 if (index != 0) { 952 streamState.setLastAudibleIndex(index, device); 953 // Post a persist volume msg 954 sendMsg(mAudioHandler, 955 MSG_PERSIST_VOLUME, 956 SENDMSG_QUEUE, 957 PERSIST_LAST_AUDIBLE, 958 device, 959 streamState, 960 PERSIST_DELAY); 961 } 962 } else { 963 if (streamState.setIndex(index, device, lastAudible) || force) { 964 // Post message to set system volume (it in turn will post a message 965 // to persist). 966 sendMsg(mAudioHandler, 967 MSG_SET_DEVICE_VOLUME, 968 SENDMSG_QUEUE, 969 device, 970 0, 971 streamState, 972 0); 973 } 974 } 975 } 976 977 /** @see AudioManager#setStreamSolo(int, boolean) */ 978 public void setStreamSolo(int streamType, boolean state, IBinder cb) { 979 for (int stream = 0; stream < mStreamStates.length; stream++) { 980 if (!isStreamAffectedByMute(stream) || stream == streamType) continue; 981 // Bring back last audible volume 982 mStreamStates[stream].mute(cb, state); 983 } 984 } 985 986 /** @see AudioManager#setStreamMute(int, boolean) */ 987 public void setStreamMute(int streamType, boolean state, IBinder cb) { 988 if (isStreamAffectedByMute(streamType)) { 989 mStreamStates[streamType].mute(cb, state); 990 } 991 } 992 993 /** get stream mute state. */ 994 public boolean isStreamMute(int streamType) { 995 return (mStreamStates[streamType].muteCount() != 0); 996 } 997 998 /** @see AudioManager#setMasterMute(boolean, IBinder) */ 999 public void setMasterMute(boolean state, int flags, IBinder cb) { 1000 if (state != AudioSystem.getMasterMute()) { 1001 AudioSystem.setMasterMute(state); 1002 // Post a persist master volume msg 1003 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME_MUTE, SENDMSG_REPLACE, state ? 1 1004 : 0, 0, null, PERSIST_DELAY); 1005 sendMasterMuteUpdate(state, flags); 1006 } 1007 } 1008 1009 /** get master mute state. */ 1010 public boolean isMasterMute() { 1011 return AudioSystem.getMasterMute(); 1012 } 1013 1014 /** @see AudioManager#getStreamVolume(int) */ 1015 public int getStreamVolume(int streamType) { 1016 ensureValidStreamType(streamType); 1017 int device = getDeviceForStream(streamType); 1018 return (mStreamStates[streamType].getIndex(device, false /* lastAudible */) + 5) / 10; 1019 } 1020 1021 public int getMasterVolume() { 1022 if (isMasterMute()) return 0; 1023 return getLastAudibleMasterVolume(); 1024 } 1025 1026 public void setMasterVolume(int volume, int flags) { 1027 if (volume < 0) { 1028 volume = 0; 1029 } else if (volume > MAX_MASTER_VOLUME) { 1030 volume = MAX_MASTER_VOLUME; 1031 } 1032 doSetMasterVolume((float)volume / MAX_MASTER_VOLUME, flags); 1033 } 1034 1035 private void doSetMasterVolume(float volume, int flags) { 1036 // don't allow changing master volume when muted 1037 if (!AudioSystem.getMasterMute()) { 1038 int oldVolume = getMasterVolume(); 1039 AudioSystem.setMasterVolume(volume); 1040 1041 int newVolume = getMasterVolume(); 1042 if (newVolume != oldVolume) { 1043 // Post a persist master volume msg 1044 sendMsg(mAudioHandler, MSG_PERSIST_MASTER_VOLUME, SENDMSG_REPLACE, 1045 Math.round(volume * (float)1000.0), 0, null, PERSIST_DELAY); 1046 } 1047 // Send the volume update regardless whether there was a change. 1048 sendMasterVolumeUpdate(flags, oldVolume, newVolume); 1049 } 1050 } 1051 1052 /** @see AudioManager#getStreamMaxVolume(int) */ 1053 public int getStreamMaxVolume(int streamType) { 1054 ensureValidStreamType(streamType); 1055 return (mStreamStates[streamType].getMaxIndex() + 5) / 10; 1056 } 1057 1058 public int getMasterMaxVolume() { 1059 return MAX_MASTER_VOLUME; 1060 } 1061 1062 /** Get last audible volume before stream was muted. */ 1063 public int getLastAudibleStreamVolume(int streamType) { 1064 ensureValidStreamType(streamType); 1065 int device = getDeviceForStream(streamType); 1066 return (mStreamStates[streamType].getIndex(device, true /* lastAudible */) + 5) / 10; 1067 } 1068 1069 /** Get last audible master volume before it was muted. */ 1070 public int getLastAudibleMasterVolume() { 1071 return Math.round(AudioSystem.getMasterVolume() * MAX_MASTER_VOLUME); 1072 } 1073 1074 /** @see AudioManager#getMasterStreamType(int) */ 1075 public int getMasterStreamType() { 1076 if (mVoiceCapable) { 1077 return AudioSystem.STREAM_RING; 1078 } else { 1079 return AudioSystem.STREAM_MUSIC; 1080 } 1081 } 1082 1083 /** @see AudioManager#getRingerMode() */ 1084 public int getRingerMode() { 1085 synchronized(mSettingsLock) { 1086 return mRingerMode; 1087 } 1088 } 1089 1090 private void ensureValidRingerMode(int ringerMode) { 1091 if (!AudioManager.isValidRingerMode(ringerMode)) { 1092 throw new IllegalArgumentException("Bad ringer mode " + ringerMode); 1093 } 1094 } 1095 1096 /** @see AudioManager#setRingerMode(int) */ 1097 public void setRingerMode(int ringerMode) { 1098 if ((ringerMode == AudioManager.RINGER_MODE_VIBRATE) && !mHasVibrator) { 1099 ringerMode = AudioManager.RINGER_MODE_SILENT; 1100 } 1101 if (ringerMode != getRingerMode()) { 1102 setRingerModeInt(ringerMode, true); 1103 // Send sticky broadcast 1104 broadcastRingerMode(ringerMode); 1105 } 1106 } 1107 1108 private void setRingerModeInt(int ringerMode, boolean persist) { 1109 synchronized(mSettingsLock) { 1110 mRingerMode = ringerMode; 1111 } 1112 1113 // Mute stream if not previously muted by ringer mode and ringer mode 1114 // is not RINGER_MODE_NORMAL and stream is affected by ringer mode. 1115 // Unmute stream if previously muted by ringer mode and ringer mode 1116 // is RINGER_MODE_NORMAL or stream is not affected by ringer mode. 1117 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1118 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 1119 if (isStreamMutedByRingerMode(streamType)) { 1120 if (!isStreamAffectedByRingerMode(streamType) || 1121 ringerMode == AudioManager.RINGER_MODE_NORMAL) { 1122 // ring and notifications volume should never be 0 when not silenced 1123 // on voice capable devices 1124 if (mVoiceCapable && 1125 mStreamVolumeAlias[streamType] == AudioSystem.STREAM_RING) { 1126 synchronized (mStreamStates[streamType]) { 1127 Set set = mStreamStates[streamType].mLastAudibleIndex.entrySet(); 1128 Iterator i = set.iterator(); 1129 while (i.hasNext()) { 1130 Map.Entry entry = (Map.Entry)i.next(); 1131 if ((Integer)entry.getValue() == 0) { 1132 entry.setValue(10); 1133 } 1134 } 1135 } 1136 } 1137 mStreamStates[streamType].mute(null, false); 1138 mRingerModeMutedStreams &= ~(1 << streamType); 1139 } 1140 } else { 1141 if (isStreamAffectedByRingerMode(streamType) && 1142 ringerMode != AudioManager.RINGER_MODE_NORMAL) { 1143 mStreamStates[streamType].mute(null, true); 1144 mRingerModeMutedStreams |= (1 << streamType); 1145 } 1146 } 1147 } 1148 1149 // Post a persist ringer mode msg 1150 if (persist) { 1151 sendMsg(mAudioHandler, MSG_PERSIST_RINGER_MODE, 1152 SENDMSG_REPLACE, 0, 0, null, PERSIST_DELAY); 1153 } 1154 } 1155 1156 private void restoreMasterVolume() { 1157 if (mUseMasterVolume) { 1158 float volume = Settings.System.getFloat(mContentResolver, 1159 Settings.System.VOLUME_MASTER, -1.0f); 1160 if (volume >= 0.0f) { 1161 AudioSystem.setMasterVolume(volume); 1162 } 1163 } 1164 } 1165 1166 /** @see AudioManager#shouldVibrate(int) */ 1167 public boolean shouldVibrate(int vibrateType) { 1168 if (!mHasVibrator) return false; 1169 1170 switch (getVibrateSetting(vibrateType)) { 1171 1172 case AudioManager.VIBRATE_SETTING_ON: 1173 return getRingerMode() != AudioManager.RINGER_MODE_SILENT; 1174 1175 case AudioManager.VIBRATE_SETTING_ONLY_SILENT: 1176 return getRingerMode() == AudioManager.RINGER_MODE_VIBRATE; 1177 1178 case AudioManager.VIBRATE_SETTING_OFF: 1179 // return false, even for incoming calls 1180 return false; 1181 1182 default: 1183 return false; 1184 } 1185 } 1186 1187 /** @see AudioManager#getVibrateSetting(int) */ 1188 public int getVibrateSetting(int vibrateType) { 1189 if (!mHasVibrator) return AudioManager.VIBRATE_SETTING_OFF; 1190 return (mVibrateSetting >> (vibrateType * 2)) & 3; 1191 } 1192 1193 /** @see AudioManager#setVibrateSetting(int, int) */ 1194 public void setVibrateSetting(int vibrateType, int vibrateSetting) { 1195 1196 if (!mHasVibrator) return; 1197 1198 mVibrateSetting = getValueForVibrateSetting(mVibrateSetting, vibrateType, vibrateSetting); 1199 1200 // Broadcast change 1201 broadcastVibrateSetting(vibrateType); 1202 1203 } 1204 1205 /** 1206 * @see #setVibrateSetting(int, int) 1207 */ 1208 public static int getValueForVibrateSetting(int existingValue, int vibrateType, 1209 int vibrateSetting) { 1210 1211 // First clear the existing setting. Each vibrate type has two bits in 1212 // the value. Note '3' is '11' in binary. 1213 existingValue &= ~(3 << (vibrateType * 2)); 1214 1215 // Set into the old value 1216 existingValue |= (vibrateSetting & 3) << (vibrateType * 2); 1217 1218 return existingValue; 1219 } 1220 1221 private class SetModeDeathHandler implements IBinder.DeathRecipient { 1222 private IBinder mCb; // To be notified of client's death 1223 private int mPid; 1224 private int mMode = AudioSystem.MODE_NORMAL; // Current mode set by this client 1225 1226 SetModeDeathHandler(IBinder cb, int pid) { 1227 mCb = cb; 1228 mPid = pid; 1229 } 1230 1231 public void binderDied() { 1232 int newModeOwnerPid = 0; 1233 synchronized(mSetModeDeathHandlers) { 1234 Log.w(TAG, "setMode() client died"); 1235 int index = mSetModeDeathHandlers.indexOf(this); 1236 if (index < 0) { 1237 Log.w(TAG, "unregistered setMode() client died"); 1238 } else { 1239 newModeOwnerPid = setModeInt(AudioSystem.MODE_NORMAL, mCb, mPid); 1240 } 1241 } 1242 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all 1243 // SCO connections not started by the application changing the mode 1244 if (newModeOwnerPid != 0) { 1245 disconnectBluetoothSco(newModeOwnerPid); 1246 } 1247 } 1248 1249 public int getPid() { 1250 return mPid; 1251 } 1252 1253 public void setMode(int mode) { 1254 mMode = mode; 1255 } 1256 1257 public int getMode() { 1258 return mMode; 1259 } 1260 1261 public IBinder getBinder() { 1262 return mCb; 1263 } 1264 } 1265 1266 /** @see AudioManager#setMode(int) */ 1267 public void setMode(int mode, IBinder cb) { 1268 if (!checkAudioSettingsPermission("setMode()")) { 1269 return; 1270 } 1271 1272 if (mode < AudioSystem.MODE_CURRENT || mode >= AudioSystem.NUM_MODES) { 1273 return; 1274 } 1275 1276 int newModeOwnerPid = 0; 1277 synchronized(mSetModeDeathHandlers) { 1278 if (mode == AudioSystem.MODE_CURRENT) { 1279 mode = mMode; 1280 } 1281 newModeOwnerPid = setModeInt(mode, cb, Binder.getCallingPid()); 1282 } 1283 // when entering RINGTONE, IN_CALL or IN_COMMUNICATION mode, clear all 1284 // SCO connections not started by the application changing the mode 1285 if (newModeOwnerPid != 0) { 1286 disconnectBluetoothSco(newModeOwnerPid); 1287 } 1288 } 1289 1290 // must be called synchronized on mSetModeDeathHandlers 1291 // setModeInt() returns a valid PID if the audio mode was successfully set to 1292 // any mode other than NORMAL. 1293 int setModeInt(int mode, IBinder cb, int pid) { 1294 int newModeOwnerPid = 0; 1295 if (cb == null) { 1296 Log.e(TAG, "setModeInt() called with null binder"); 1297 return newModeOwnerPid; 1298 } 1299 1300 SetModeDeathHandler hdlr = null; 1301 Iterator iter = mSetModeDeathHandlers.iterator(); 1302 while (iter.hasNext()) { 1303 SetModeDeathHandler h = (SetModeDeathHandler)iter.next(); 1304 if (h.getPid() == pid) { 1305 hdlr = h; 1306 // Remove from client list so that it is re-inserted at top of list 1307 iter.remove(); 1308 hdlr.getBinder().unlinkToDeath(hdlr, 0); 1309 break; 1310 } 1311 } 1312 int status = AudioSystem.AUDIO_STATUS_OK; 1313 do { 1314 if (mode == AudioSystem.MODE_NORMAL) { 1315 // get new mode from client at top the list if any 1316 if (!mSetModeDeathHandlers.isEmpty()) { 1317 hdlr = mSetModeDeathHandlers.get(0); 1318 cb = hdlr.getBinder(); 1319 mode = hdlr.getMode(); 1320 } 1321 } else { 1322 if (hdlr == null) { 1323 hdlr = new SetModeDeathHandler(cb, pid); 1324 } 1325 // Register for client death notification 1326 try { 1327 cb.linkToDeath(hdlr, 0); 1328 } catch (RemoteException e) { 1329 // Client has died! 1330 Log.w(TAG, "setMode() could not link to "+cb+" binder death"); 1331 } 1332 1333 // Last client to call setMode() is always at top of client list 1334 // as required by SetModeDeathHandler.binderDied() 1335 mSetModeDeathHandlers.add(0, hdlr); 1336 hdlr.setMode(mode); 1337 } 1338 1339 if (mode != mMode) { 1340 status = AudioSystem.setPhoneState(mode); 1341 if (status == AudioSystem.AUDIO_STATUS_OK) { 1342 mMode = mode; 1343 } else { 1344 if (hdlr != null) { 1345 mSetModeDeathHandlers.remove(hdlr); 1346 cb.unlinkToDeath(hdlr, 0); 1347 } 1348 // force reading new top of mSetModeDeathHandlers stack 1349 mode = AudioSystem.MODE_NORMAL; 1350 } 1351 } else { 1352 status = AudioSystem.AUDIO_STATUS_OK; 1353 } 1354 } while (status != AudioSystem.AUDIO_STATUS_OK && !mSetModeDeathHandlers.isEmpty()); 1355 1356 if (status == AudioSystem.AUDIO_STATUS_OK) { 1357 if (mode != AudioSystem.MODE_NORMAL) { 1358 if (mSetModeDeathHandlers.isEmpty()) { 1359 Log.e(TAG, "setMode() different from MODE_NORMAL with empty mode client stack"); 1360 } else { 1361 newModeOwnerPid = mSetModeDeathHandlers.get(0).getPid(); 1362 } 1363 } 1364 int streamType = getActiveStreamType(AudioManager.USE_DEFAULT_STREAM_TYPE); 1365 int device = getDeviceForStream(streamType); 1366 int index = mStreamStates[mStreamVolumeAlias[streamType]].getIndex(device, false); 1367 setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, true, false); 1368 1369 updateStreamVolumeAlias(true /*updateVolumes*/); 1370 } 1371 return newModeOwnerPid; 1372 } 1373 1374 /** @see AudioManager#getMode() */ 1375 public int getMode() { 1376 return mMode; 1377 } 1378 1379 /** @see AudioManager#playSoundEffect(int) */ 1380 public void playSoundEffect(int effectType) { 1381 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP, 1382 effectType, -1, null, 0); 1383 } 1384 1385 /** @see AudioManager#playSoundEffect(int, float) */ 1386 public void playSoundEffectVolume(int effectType, float volume) { 1387 loadSoundEffects(); 1388 sendMsg(mAudioHandler, MSG_PLAY_SOUND_EFFECT, SENDMSG_NOOP, 1389 effectType, (int) (volume * 1000), null, 0); 1390 } 1391 1392 /** 1393 * Loads samples into the soundpool. 1394 * This method must be called at first when sound effects are enabled 1395 */ 1396 public boolean loadSoundEffects() { 1397 int status; 1398 1399 synchronized (mSoundEffectsLock) { 1400 if (!mBootCompleted) { 1401 Log.w(TAG, "loadSoundEffects() called before boot complete"); 1402 return false; 1403 } 1404 1405 if (mSoundPool != null) { 1406 return true; 1407 } 1408 mSoundPool = new SoundPool(NUM_SOUNDPOOL_CHANNELS, AudioSystem.STREAM_SYSTEM, 0); 1409 1410 try { 1411 mSoundPoolCallBack = null; 1412 mSoundPoolListenerThread = new SoundPoolListenerThread(); 1413 mSoundPoolListenerThread.start(); 1414 // Wait for mSoundPoolCallBack to be set by the other thread 1415 mSoundEffectsLock.wait(); 1416 } catch (InterruptedException e) { 1417 Log.w(TAG, "Interrupted while waiting sound pool listener thread."); 1418 } 1419 1420 if (mSoundPoolCallBack == null) { 1421 Log.w(TAG, "loadSoundEffects() could not create SoundPool listener or thread"); 1422 if (mSoundPoolLooper != null) { 1423 mSoundPoolLooper.quit(); 1424 mSoundPoolLooper = null; 1425 } 1426 mSoundPoolListenerThread = null; 1427 mSoundPool.release(); 1428 mSoundPool = null; 1429 return false; 1430 } 1431 /* 1432 * poolId table: The value -1 in this table indicates that corresponding 1433 * file (same index in SOUND_EFFECT_FILES[] has not been loaded. 1434 * Once loaded, the value in poolId is the sample ID and the same 1435 * sample can be reused for another effect using the same file. 1436 */ 1437 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 1438 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 1439 poolId[fileIdx] = -1; 1440 } 1441 /* 1442 * Effects whose value in SOUND_EFFECT_FILES_MAP[effect][1] is -1 must be loaded. 1443 * If load succeeds, value in SOUND_EFFECT_FILES_MAP[effect][1] is > 0: 1444 * this indicates we have a valid sample loaded for this effect. 1445 */ 1446 1447 int lastSample = 0; 1448 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 1449 // Do not load sample if this effect uses the MediaPlayer 1450 if (SOUND_EFFECT_FILES_MAP[effect][1] == 0) { 1451 continue; 1452 } 1453 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == -1) { 1454 String filePath = Environment.getRootDirectory() 1455 + SOUND_EFFECTS_PATH 1456 + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effect][0]]; 1457 int sampleId = mSoundPool.load(filePath, 0); 1458 if (sampleId <= 0) { 1459 Log.w(TAG, "Soundpool could not load file: "+filePath); 1460 } else { 1461 SOUND_EFFECT_FILES_MAP[effect][1] = sampleId; 1462 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = sampleId; 1463 lastSample = sampleId; 1464 } 1465 } else { 1466 SOUND_EFFECT_FILES_MAP[effect][1] = poolId[SOUND_EFFECT_FILES_MAP[effect][0]]; 1467 } 1468 } 1469 // wait for all samples to be loaded 1470 if (lastSample != 0) { 1471 mSoundPoolCallBack.setLastSample(lastSample); 1472 1473 try { 1474 mSoundEffectsLock.wait(); 1475 status = mSoundPoolCallBack.status(); 1476 } catch (java.lang.InterruptedException e) { 1477 Log.w(TAG, "Interrupted while waiting sound pool callback."); 1478 status = -1; 1479 } 1480 } else { 1481 status = -1; 1482 } 1483 1484 if (mSoundPoolLooper != null) { 1485 mSoundPoolLooper.quit(); 1486 mSoundPoolLooper = null; 1487 } 1488 mSoundPoolListenerThread = null; 1489 if (status != 0) { 1490 Log.w(TAG, 1491 "loadSoundEffects(), Error " 1492 + ((lastSample != 0) ? mSoundPoolCallBack.status() : -1) 1493 + " while loading samples"); 1494 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 1495 if (SOUND_EFFECT_FILES_MAP[effect][1] > 0) { 1496 SOUND_EFFECT_FILES_MAP[effect][1] = -1; 1497 } 1498 } 1499 1500 mSoundPool.release(); 1501 mSoundPool = null; 1502 } 1503 } 1504 return (status == 0); 1505 } 1506 1507 /** 1508 * Unloads samples from the sound pool. 1509 * This method can be called to free some memory when 1510 * sound effects are disabled. 1511 */ 1512 public void unloadSoundEffects() { 1513 synchronized (mSoundEffectsLock) { 1514 if (mSoundPool == null) { 1515 return; 1516 } 1517 1518 mAudioHandler.removeMessages(MSG_LOAD_SOUND_EFFECTS); 1519 mAudioHandler.removeMessages(MSG_PLAY_SOUND_EFFECT); 1520 1521 int[] poolId = new int[SOUND_EFFECT_FILES.length]; 1522 for (int fileIdx = 0; fileIdx < SOUND_EFFECT_FILES.length; fileIdx++) { 1523 poolId[fileIdx] = 0; 1524 } 1525 1526 for (int effect = 0; effect < AudioManager.NUM_SOUND_EFFECTS; effect++) { 1527 if (SOUND_EFFECT_FILES_MAP[effect][1] <= 0) { 1528 continue; 1529 } 1530 if (poolId[SOUND_EFFECT_FILES_MAP[effect][0]] == 0) { 1531 mSoundPool.unload(SOUND_EFFECT_FILES_MAP[effect][1]); 1532 SOUND_EFFECT_FILES_MAP[effect][1] = -1; 1533 poolId[SOUND_EFFECT_FILES_MAP[effect][0]] = -1; 1534 } 1535 } 1536 mSoundPool.release(); 1537 mSoundPool = null; 1538 } 1539 } 1540 1541 class SoundPoolListenerThread extends Thread { 1542 public SoundPoolListenerThread() { 1543 super("SoundPoolListenerThread"); 1544 } 1545 1546 @Override 1547 public void run() { 1548 1549 Looper.prepare(); 1550 mSoundPoolLooper = Looper.myLooper(); 1551 1552 synchronized (mSoundEffectsLock) { 1553 if (mSoundPool != null) { 1554 mSoundPoolCallBack = new SoundPoolCallback(); 1555 mSoundPool.setOnLoadCompleteListener(mSoundPoolCallBack); 1556 } 1557 mSoundEffectsLock.notify(); 1558 } 1559 Looper.loop(); 1560 } 1561 } 1562 1563 private final class SoundPoolCallback implements 1564 android.media.SoundPool.OnLoadCompleteListener { 1565 1566 int mStatus; 1567 int mLastSample; 1568 1569 public int status() { 1570 return mStatus; 1571 } 1572 1573 public void setLastSample(int sample) { 1574 mLastSample = sample; 1575 } 1576 1577 public void onLoadComplete(SoundPool soundPool, int sampleId, int status) { 1578 synchronized (mSoundEffectsLock) { 1579 if (status != 0) { 1580 mStatus = status; 1581 } 1582 if (sampleId == mLastSample) { 1583 mSoundEffectsLock.notify(); 1584 } 1585 } 1586 } 1587 } 1588 1589 /** @see AudioManager#reloadAudioSettings() */ 1590 public void reloadAudioSettings() { 1591 // restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings 1592 readPersistedSettings(); 1593 1594 // restore volume settings 1595 int numStreamTypes = AudioSystem.getNumStreamTypes(); 1596 for (int streamType = 0; streamType < numStreamTypes; streamType++) { 1597 VolumeStreamState streamState = mStreamStates[streamType]; 1598 1599 synchronized (streamState) { 1600 streamState.readSettings(); 1601 1602 // unmute stream that was muted but is not affect by mute anymore 1603 if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) { 1604 int size = streamState.mDeathHandlers.size(); 1605 for (int i = 0; i < size; i++) { 1606 streamState.mDeathHandlers.get(i).mMuteCount = 1; 1607 streamState.mDeathHandlers.get(i).mute(false); 1608 } 1609 } 1610 } 1611 } 1612 1613 checkAllAliasStreamVolumes(); 1614 1615 // apply new ringer mode 1616 setRingerModeInt(getRingerMode(), false); 1617 } 1618 1619 /** @see AudioManager#setSpeakerphoneOn() */ 1620 public void setSpeakerphoneOn(boolean on){ 1621 if (!checkAudioSettingsPermission("setSpeakerphoneOn()")) { 1622 return; 1623 } 1624 mForcedUseForComm = on ? AudioSystem.FORCE_SPEAKER : AudioSystem.FORCE_NONE; 1625 1626 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, 1627 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0); 1628 } 1629 1630 /** @see AudioManager#isSpeakerphoneOn() */ 1631 public boolean isSpeakerphoneOn() { 1632 return (mForcedUseForComm == AudioSystem.FORCE_SPEAKER); 1633 } 1634 1635 /** @see AudioManager#setBluetoothScoOn() */ 1636 public void setBluetoothScoOn(boolean on){ 1637 if (!checkAudioSettingsPermission("setBluetoothScoOn()")) { 1638 return; 1639 } 1640 mForcedUseForComm = on ? AudioSystem.FORCE_BT_SCO : AudioSystem.FORCE_NONE; 1641 1642 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, 1643 AudioSystem.FOR_COMMUNICATION, mForcedUseForComm, null, 0); 1644 sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE, 1645 AudioSystem.FOR_RECORD, mForcedUseForComm, null, 0); 1646 } 1647 1648 /** @see AudioManager#isBluetoothScoOn() */ 1649 public boolean isBluetoothScoOn() { 1650 return (mForcedUseForComm == AudioSystem.FORCE_BT_SCO); 1651 } 1652 1653 /** @see AudioManager#startBluetoothSco() */ 1654 public void startBluetoothSco(IBinder cb){ 1655 if (!checkAudioSettingsPermission("startBluetoothSco()") || 1656 !mBootCompleted) { 1657 return; 1658 } 1659 ScoClient client = getScoClient(cb, true); 1660 client.incCount(); 1661 } 1662 1663 /** @see AudioManager#stopBluetoothSco() */ 1664 public void stopBluetoothSco(IBinder cb){ 1665 if (!checkAudioSettingsPermission("stopBluetoothSco()") || 1666 !mBootCompleted) { 1667 return; 1668 } 1669 ScoClient client = getScoClient(cb, false); 1670 if (client != null) { 1671 client.decCount(); 1672 } 1673 } 1674 1675 private class ScoClient implements IBinder.DeathRecipient { 1676 private IBinder mCb; // To be notified of client's death 1677 private int mCreatorPid; 1678 private int mStartcount; // number of SCO connections started by this client 1679 1680 ScoClient(IBinder cb) { 1681 mCb = cb; 1682 mCreatorPid = Binder.getCallingPid(); 1683 mStartcount = 0; 1684 } 1685 1686 public void binderDied() { 1687 synchronized(mScoClients) { 1688 Log.w(TAG, "SCO client died"); 1689 int index = mScoClients.indexOf(this); 1690 if (index < 0) { 1691 Log.w(TAG, "unregistered SCO client died"); 1692 } else { 1693 clearCount(true); 1694 mScoClients.remove(this); 1695 } 1696 } 1697 } 1698 1699 public void incCount() { 1700 synchronized(mScoClients) { 1701 requestScoState(BluetoothHeadset.STATE_AUDIO_CONNECTED); 1702 if (mStartcount == 0) { 1703 try { 1704 mCb.linkToDeath(this, 0); 1705 } catch (RemoteException e) { 1706 // client has already died! 1707 Log.w(TAG, "ScoClient incCount() could not link to "+mCb+" binder death"); 1708 } 1709 } 1710 mStartcount++; 1711 } 1712 } 1713 1714 public void decCount() { 1715 synchronized(mScoClients) { 1716 if (mStartcount == 0) { 1717 Log.w(TAG, "ScoClient.decCount() already 0"); 1718 } else { 1719 mStartcount--; 1720 if (mStartcount == 0) { 1721 try { 1722 mCb.unlinkToDeath(this, 0); 1723 } catch (NoSuchElementException e) { 1724 Log.w(TAG, "decCount() going to 0 but not registered to binder"); 1725 } 1726 } 1727 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1728 } 1729 } 1730 } 1731 1732 public void clearCount(boolean stopSco) { 1733 synchronized(mScoClients) { 1734 if (mStartcount != 0) { 1735 try { 1736 mCb.unlinkToDeath(this, 0); 1737 } catch (NoSuchElementException e) { 1738 Log.w(TAG, "clearCount() mStartcount: "+mStartcount+" != 0 but not registered to binder"); 1739 } 1740 } 1741 mStartcount = 0; 1742 if (stopSco) { 1743 requestScoState(BluetoothHeadset.STATE_AUDIO_DISCONNECTED); 1744 } 1745 } 1746 } 1747 1748 public int getCount() { 1749 return mStartcount; 1750 } 1751 1752 public IBinder getBinder() { 1753 return mCb; 1754 } 1755 1756 public int getPid() { 1757 return mCreatorPid; 1758 } 1759 1760 public int totalCount() { 1761 synchronized(mScoClients) { 1762 int count = 0; 1763 int size = mScoClients.size(); 1764 for (int i = 0; i < size; i++) { 1765 count += mScoClients.get(i).getCount(); 1766 } 1767 return count; 1768 } 1769 } 1770 1771 private void requestScoState(int state) { 1772 checkScoAudioState(); 1773 if (totalCount() == 0) { 1774 if (state == BluetoothHeadset.STATE_AUDIO_CONNECTED) { 1775 // Make sure that the state transitions to CONNECTING even if we cannot initiate 1776 // the connection. 1777 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTING); 1778 // Accept SCO audio activation only in NORMAL audio mode or if the mode is 1779 // currently controlled by the same client process. 1780 synchronized(mSetModeDeathHandlers) { 1781 if ((mSetModeDeathHandlers.isEmpty() || 1782 mSetModeDeathHandlers.get(0).getPid() == mCreatorPid) && 1783 (mScoAudioState == SCO_STATE_INACTIVE || 1784 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) { 1785 if (mScoAudioState == SCO_STATE_INACTIVE) { 1786 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) { 1787 if (mBluetoothHeadset.startScoUsingVirtualVoiceCall( 1788 mBluetoothHeadsetDevice)) { 1789 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1790 } else { 1791 broadcastScoConnectionState( 1792 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1793 } 1794 } else if (getBluetoothHeadset()) { 1795 mScoAudioState = SCO_STATE_ACTIVATE_REQ; 1796 } 1797 } else { 1798 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1799 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_CONNECTED); 1800 } 1801 } else { 1802 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1803 } 1804 } 1805 } else if (state == BluetoothHeadset.STATE_AUDIO_DISCONNECTED && 1806 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL || 1807 mScoAudioState == SCO_STATE_ACTIVATE_REQ)) { 1808 if (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL) { 1809 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null) { 1810 if (!mBluetoothHeadset.stopScoUsingVirtualVoiceCall( 1811 mBluetoothHeadsetDevice)) { 1812 mScoAudioState = SCO_STATE_INACTIVE; 1813 broadcastScoConnectionState( 1814 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1815 } 1816 } else if (getBluetoothHeadset()) { 1817 mScoAudioState = SCO_STATE_DEACTIVATE_REQ; 1818 } 1819 } else { 1820 mScoAudioState = SCO_STATE_INACTIVE; 1821 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1822 } 1823 } 1824 } 1825 } 1826 } 1827 1828 private void checkScoAudioState() { 1829 if (mBluetoothHeadset != null && mBluetoothHeadsetDevice != null && 1830 mScoAudioState == SCO_STATE_INACTIVE && 1831 mBluetoothHeadset.getAudioState(mBluetoothHeadsetDevice) 1832 != BluetoothHeadset.STATE_AUDIO_DISCONNECTED) { 1833 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 1834 } 1835 } 1836 1837 private ScoClient getScoClient(IBinder cb, boolean create) { 1838 synchronized(mScoClients) { 1839 ScoClient client = null; 1840 int size = mScoClients.size(); 1841 for (int i = 0; i < size; i++) { 1842 client = mScoClients.get(i); 1843 if (client.getBinder() == cb) 1844 return client; 1845 } 1846 if (create) { 1847 client = new ScoClient(cb); 1848 mScoClients.add(client); 1849 } 1850 return client; 1851 } 1852 } 1853 1854 public void clearAllScoClients(int exceptPid, boolean stopSco) { 1855 synchronized(mScoClients) { 1856 ScoClient savedClient = null; 1857 int size = mScoClients.size(); 1858 for (int i = 0; i < size; i++) { 1859 ScoClient cl = mScoClients.get(i); 1860 if (cl.getPid() != exceptPid) { 1861 cl.clearCount(stopSco); 1862 } else { 1863 savedClient = cl; 1864 } 1865 } 1866 mScoClients.clear(); 1867 if (savedClient != null) { 1868 mScoClients.add(savedClient); 1869 } 1870 } 1871 } 1872 1873 private boolean getBluetoothHeadset() { 1874 boolean result = false; 1875 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 1876 if (adapter != null) { 1877 result = adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, 1878 BluetoothProfile.HEADSET); 1879 } 1880 // If we could not get a bluetooth headset proxy, send a failure message 1881 // without delay to reset the SCO audio state and clear SCO clients. 1882 // If we could get a proxy, send a delayed failure message that will reset our state 1883 // in case we don't receive onServiceConnected(). 1884 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 1885 SENDMSG_REPLACE, 0, 0, null, result ? BT_HEADSET_CNCT_TIMEOUT_MS : 0); 1886 return result; 1887 } 1888 1889 private void disconnectBluetoothSco(int exceptPid) { 1890 synchronized(mScoClients) { 1891 checkScoAudioState(); 1892 if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL || 1893 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) { 1894 if (mBluetoothHeadsetDevice != null) { 1895 if (mBluetoothHeadset != null) { 1896 if (!mBluetoothHeadset.stopVoiceRecognition( 1897 mBluetoothHeadsetDevice)) { 1898 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 1899 SENDMSG_REPLACE, 0, 0, null, 0); 1900 } 1901 } else if (mScoAudioState == SCO_STATE_ACTIVE_EXTERNAL && 1902 getBluetoothHeadset()) { 1903 mScoAudioState = SCO_STATE_DEACTIVATE_EXT_REQ; 1904 } 1905 } 1906 } else { 1907 clearAllScoClients(exceptPid, true); 1908 } 1909 } 1910 } 1911 1912 private void resetBluetoothSco() { 1913 synchronized(mScoClients) { 1914 clearAllScoClients(0, false); 1915 mScoAudioState = SCO_STATE_INACTIVE; 1916 broadcastScoConnectionState(AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 1917 } 1918 } 1919 1920 private void broadcastScoConnectionState(int state) { 1921 if (state != mScoConnectionState) { 1922 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_UPDATED); 1923 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state); 1924 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE, 1925 mScoConnectionState); 1926 mContext.sendStickyBroadcast(newIntent); 1927 mScoConnectionState = state; 1928 } 1929 } 1930 1931 private BluetoothProfile.ServiceListener mBluetoothProfileServiceListener = 1932 new BluetoothProfile.ServiceListener() { 1933 public void onServiceConnected(int profile, BluetoothProfile proxy) { 1934 BluetoothDevice btDevice; 1935 List<BluetoothDevice> deviceList; 1936 switch(profile) { 1937 case BluetoothProfile.A2DP: 1938 BluetoothA2dp a2dp = (BluetoothA2dp) proxy; 1939 deviceList = a2dp.getConnectedDevices(); 1940 if (deviceList.size() > 0) { 1941 btDevice = deviceList.get(0); 1942 handleA2dpConnectionStateChange(btDevice, a2dp.getConnectionState(btDevice)); 1943 } 1944 break; 1945 1946 case BluetoothProfile.HEADSET: 1947 synchronized (mScoClients) { 1948 // Discard timeout message 1949 mAudioHandler.removeMessages(MSG_BT_HEADSET_CNCT_FAILED); 1950 mBluetoothHeadset = (BluetoothHeadset) proxy; 1951 deviceList = mBluetoothHeadset.getConnectedDevices(); 1952 if (deviceList.size() > 0) { 1953 mBluetoothHeadsetDevice = deviceList.get(0); 1954 } else { 1955 mBluetoothHeadsetDevice = null; 1956 } 1957 // Refresh SCO audio state 1958 checkScoAudioState(); 1959 // Continue pending action if any 1960 if (mScoAudioState == SCO_STATE_ACTIVATE_REQ || 1961 mScoAudioState == SCO_STATE_DEACTIVATE_REQ || 1962 mScoAudioState == SCO_STATE_DEACTIVATE_EXT_REQ) { 1963 boolean status = false; 1964 if (mBluetoothHeadsetDevice != null) { 1965 switch (mScoAudioState) { 1966 case SCO_STATE_ACTIVATE_REQ: 1967 mScoAudioState = SCO_STATE_ACTIVE_INTERNAL; 1968 status = mBluetoothHeadset.startScoUsingVirtualVoiceCall( 1969 mBluetoothHeadsetDevice); 1970 break; 1971 case SCO_STATE_DEACTIVATE_REQ: 1972 status = mBluetoothHeadset.stopScoUsingVirtualVoiceCall( 1973 mBluetoothHeadsetDevice); 1974 break; 1975 case SCO_STATE_DEACTIVATE_EXT_REQ: 1976 status = mBluetoothHeadset.stopVoiceRecognition( 1977 mBluetoothHeadsetDevice); 1978 } 1979 } 1980 if (!status) { 1981 sendMsg(mAudioHandler, MSG_BT_HEADSET_CNCT_FAILED, 1982 SENDMSG_REPLACE, 0, 0, null, 0); 1983 } 1984 } 1985 } 1986 break; 1987 1988 default: 1989 break; 1990 } 1991 } 1992 public void onServiceDisconnected(int profile) { 1993 switch(profile) { 1994 case BluetoothProfile.A2DP: 1995 synchronized (mConnectedDevices) { 1996 if (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)) { 1997 makeA2dpDeviceUnavailableNow( 1998 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP)); 1999 } 2000 } 2001 break; 2002 2003 case BluetoothProfile.HEADSET: 2004 synchronized (mScoClients) { 2005 mBluetoothHeadset = null; 2006 } 2007 break; 2008 2009 default: 2010 break; 2011 } 2012 } 2013 }; 2014 2015 /////////////////////////////////////////////////////////////////////////// 2016 // Internal methods 2017 /////////////////////////////////////////////////////////////////////////// 2018 2019 /** 2020 * Checks if the adjustment should change ringer mode instead of just 2021 * adjusting volume. If so, this will set the proper ringer mode and volume 2022 * indices on the stream states. 2023 */ 2024 private boolean checkForRingerModeChange(int oldIndex, int direction, int step) { 2025 boolean adjustVolumeIndex = true; 2026 int ringerMode = getRingerMode(); 2027 2028 switch (ringerMode) { 2029 case RINGER_MODE_NORMAL: 2030 if (direction == AudioManager.ADJUST_LOWER) { 2031 if (mHasVibrator) { 2032 // "step" is the delta in internal index units corresponding to a 2033 // change of 1 in UI index units. 2034 // Because of rounding when rescaling from one stream index range to its alias 2035 // index range, we cannot simply test oldIndex == step: 2036 // (step <= oldIndex < 2 * step) is equivalent to: (old UI index == 1) 2037 if (step <= oldIndex && oldIndex < 2 * step) { 2038 ringerMode = RINGER_MODE_VIBRATE; 2039 } 2040 } else { 2041 // (oldIndex < step) is equivalent to (old UI index == 0) 2042 if ((oldIndex < step) && mPrevVolDirection != AudioManager.ADJUST_LOWER) { 2043 ringerMode = RINGER_MODE_SILENT; 2044 } 2045 } 2046 } 2047 break; 2048 case RINGER_MODE_VIBRATE: 2049 if (!mHasVibrator) { 2050 Log.e(TAG, "checkForRingerModeChange() current ringer mode is vibrate" + 2051 "but no vibrator is present"); 2052 break; 2053 } 2054 if ((direction == AudioManager.ADJUST_LOWER)) { 2055 if (mPrevVolDirection != AudioManager.ADJUST_LOWER) { 2056 ringerMode = RINGER_MODE_SILENT; 2057 } 2058 } else if (direction == AudioManager.ADJUST_RAISE) { 2059 ringerMode = RINGER_MODE_NORMAL; 2060 } 2061 adjustVolumeIndex = false; 2062 break; 2063 case RINGER_MODE_SILENT: 2064 if (direction == AudioManager.ADJUST_RAISE) { 2065 if (mHasVibrator) { 2066 ringerMode = RINGER_MODE_VIBRATE; 2067 } else { 2068 ringerMode = RINGER_MODE_NORMAL; 2069 } 2070 } 2071 adjustVolumeIndex = false; 2072 break; 2073 default: 2074 Log.e(TAG, "checkForRingerModeChange() wrong ringer mode: "+ringerMode); 2075 break; 2076 } 2077 2078 setRingerMode(ringerMode); 2079 2080 mPrevVolDirection = direction; 2081 2082 return adjustVolumeIndex; 2083 } 2084 2085 public boolean isStreamAffectedByRingerMode(int streamType) { 2086 return (mRingerModeAffectedStreams & (1 << streamType)) != 0; 2087 } 2088 2089 private boolean isStreamMutedByRingerMode(int streamType) { 2090 return (mRingerModeMutedStreams & (1 << streamType)) != 0; 2091 } 2092 2093 public boolean isStreamAffectedByMute(int streamType) { 2094 return (mMuteAffectedStreams & (1 << streamType)) != 0; 2095 } 2096 2097 private void ensureValidDirection(int direction) { 2098 if (direction < AudioManager.ADJUST_LOWER || direction > AudioManager.ADJUST_RAISE) { 2099 throw new IllegalArgumentException("Bad direction " + direction); 2100 } 2101 } 2102 2103 private void ensureValidSteps(int steps) { 2104 if (Math.abs(steps) > MAX_BATCH_VOLUME_ADJUST_STEPS) { 2105 throw new IllegalArgumentException("Bad volume adjust steps " + steps); 2106 } 2107 } 2108 2109 private void ensureValidStreamType(int streamType) { 2110 if (streamType < 0 || streamType >= mStreamStates.length) { 2111 throw new IllegalArgumentException("Bad stream type " + streamType); 2112 } 2113 } 2114 2115 private boolean isInCommunication() { 2116 boolean isOffhook = false; 2117 2118 if (mVoiceCapable) { 2119 try { 2120 ITelephony phone = ITelephony.Stub.asInterface(ServiceManager.checkService("phone")); 2121 if (phone != null) isOffhook = phone.isOffhook(); 2122 } catch (RemoteException e) { 2123 Log.w(TAG, "Couldn't connect to phone service", e); 2124 } 2125 } 2126 return (isOffhook || getMode() == AudioManager.MODE_IN_COMMUNICATION); 2127 } 2128 2129 private int getActiveStreamType(int suggestedStreamType) { 2130 if (mVoiceCapable) { 2131 if (isInCommunication()) { 2132 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) 2133 == AudioSystem.FORCE_BT_SCO) { 2134 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); 2135 return AudioSystem.STREAM_BLUETOOTH_SCO; 2136 } else { 2137 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); 2138 return AudioSystem.STREAM_VOICE_CALL; 2139 } 2140 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) { 2141 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC..."); 2142 return AudioSystem.STREAM_MUSIC; 2143 } else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) { 2144 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_RING..." 2145 // + " b/c USE_DEFAULT_STREAM_TYPE..."); 2146 return AudioSystem.STREAM_RING; 2147 } else { 2148 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType); 2149 return suggestedStreamType; 2150 } 2151 } else { 2152 if (isInCommunication()) { 2153 if (AudioSystem.getForceUse(AudioSystem.FOR_COMMUNICATION) 2154 == AudioSystem.FORCE_BT_SCO) { 2155 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_BLUETOOTH_SCO..."); 2156 return AudioSystem.STREAM_BLUETOOTH_SCO; 2157 } else { 2158 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_VOICE_CALL..."); 2159 return AudioSystem.STREAM_VOICE_CALL; 2160 } 2161 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION, 2162 NOTIFICATION_VOLUME_DELAY_MS) || 2163 AudioSystem.isStreamActive(AudioSystem.STREAM_RING, 2164 NOTIFICATION_VOLUME_DELAY_MS)) { 2165 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION..."); 2166 return AudioSystem.STREAM_NOTIFICATION; 2167 } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) || 2168 (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE)) { 2169 // Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC " 2170 // + " b/c USE_DEFAULT_STREAM_TYPE..."); 2171 return AudioSystem.STREAM_MUSIC; 2172 } else { 2173 // Log.v(TAG, "getActiveStreamType: Returning suggested type " + suggestedStreamType); 2174 return suggestedStreamType; 2175 } 2176 } 2177 } 2178 2179 private void broadcastRingerMode(int ringerMode) { 2180 // Send sticky broadcast 2181 Intent broadcast = new Intent(AudioManager.RINGER_MODE_CHANGED_ACTION); 2182 broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode); 2183 broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT 2184 | Intent.FLAG_RECEIVER_REPLACE_PENDING); 2185 long origCallerIdentityToken = Binder.clearCallingIdentity(); 2186 mContext.sendStickyBroadcast(broadcast); 2187 Binder.restoreCallingIdentity(origCallerIdentityToken); 2188 } 2189 2190 private void broadcastVibrateSetting(int vibrateType) { 2191 // Send broadcast 2192 if (ActivityManagerNative.isSystemReady()) { 2193 Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION); 2194 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType); 2195 broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType)); 2196 mContext.sendBroadcast(broadcast); 2197 } 2198 } 2199 2200 // Message helper methods 2201 2202 private static void sendMsg(Handler handler, int msg, 2203 int existingMsgPolicy, int arg1, int arg2, Object obj, int delay) { 2204 2205 if (existingMsgPolicy == SENDMSG_REPLACE) { 2206 handler.removeMessages(msg); 2207 } else if (existingMsgPolicy == SENDMSG_NOOP && handler.hasMessages(msg)) { 2208 return; 2209 } 2210 2211 handler.sendMessageDelayed(handler.obtainMessage(msg, arg1, arg2, obj), delay); 2212 } 2213 2214 boolean checkAudioSettingsPermission(String method) { 2215 if (mContext.checkCallingOrSelfPermission("android.permission.MODIFY_AUDIO_SETTINGS") 2216 == PackageManager.PERMISSION_GRANTED) { 2217 return true; 2218 } 2219 String msg = "Audio Settings Permission Denial: " + method + " from pid=" 2220 + Binder.getCallingPid() 2221 + ", uid=" + Binder.getCallingUid(); 2222 Log.w(TAG, msg); 2223 return false; 2224 } 2225 2226 private int getDeviceForStream(int stream) { 2227 int device = AudioSystem.getDevicesForStream(stream); 2228 if ((device & (device - 1)) != 0) { 2229 // Multiple device selection is either: 2230 // - speaker + one other device: give priority to speaker in this case. 2231 // - one A2DP device + another device: happens with duplicated output. In this case 2232 // retain the device on the A2DP output as the other must not correspond to an active 2233 // selection if not the speaker. 2234 if ((device & AudioSystem.DEVICE_OUT_SPEAKER) != 0) { 2235 device = AudioSystem.DEVICE_OUT_SPEAKER; 2236 } else { 2237 device &= AudioSystem.DEVICE_OUT_ALL_A2DP; 2238 } 2239 } 2240 return device; 2241 } 2242 2243 /////////////////////////////////////////////////////////////////////////// 2244 // Inner classes 2245 /////////////////////////////////////////////////////////////////////////// 2246 2247 public class VolumeStreamState { 2248 private final int mStreamType; 2249 2250 private String mVolumeIndexSettingName; 2251 private String mLastAudibleVolumeIndexSettingName; 2252 private int mIndexMax; 2253 private final ConcurrentHashMap<Integer, Integer> mIndex = 2254 new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4); 2255 private final ConcurrentHashMap<Integer, Integer> mLastAudibleIndex = 2256 new ConcurrentHashMap<Integer, Integer>(8, 0.75f, 4); 2257 private ArrayList<VolumeDeathHandler> mDeathHandlers; //handles mute/solo clients death 2258 2259 private VolumeStreamState(String settingName, int streamType) { 2260 2261 mVolumeIndexSettingName = settingName; 2262 mLastAudibleVolumeIndexSettingName = settingName + System.APPEND_FOR_LAST_AUDIBLE; 2263 2264 mStreamType = streamType; 2265 mIndexMax = MAX_STREAM_VOLUME[streamType]; 2266 AudioSystem.initStreamVolume(streamType, 0, mIndexMax); 2267 mIndexMax *= 10; 2268 2269 readSettings(); 2270 2271 mDeathHandlers = new ArrayList<VolumeDeathHandler>(); 2272 } 2273 2274 public String getSettingNameForDevice(boolean lastAudible, int device) { 2275 String name = lastAudible ? 2276 mLastAudibleVolumeIndexSettingName : 2277 mVolumeIndexSettingName; 2278 String suffix = AudioSystem.getDeviceName(device); 2279 if (suffix.isEmpty()) { 2280 return name; 2281 } 2282 return name + "_" + suffix; 2283 } 2284 2285 public synchronized void readSettings() { 2286 int remainingDevices = AudioSystem.DEVICE_OUT_ALL; 2287 2288 for (int i = 0; remainingDevices != 0; i++) { 2289 int device = (1 << i); 2290 if ((device & remainingDevices) == 0) { 2291 continue; 2292 } 2293 remainingDevices &= ~device; 2294 2295 // retrieve current volume for device 2296 String name = getSettingNameForDevice(false /* lastAudible */, device); 2297 // if no volume stored for current stream and device, use default volume if default 2298 // device, continue otherwise 2299 int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ? 2300 AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1; 2301 int index = Settings.System.getInt(mContentResolver, name, defaultIndex); 2302 if (index == -1) { 2303 continue; 2304 } 2305 2306 // retrieve last audible volume for device 2307 name = getSettingNameForDevice(true /* lastAudible */, device); 2308 // use stored last audible index if present, otherwise use current index if not 0 2309 // or default index 2310 defaultIndex = (index > 0) ? 2311 index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]; 2312 int lastAudibleIndex = Settings.System.getInt(mContentResolver, name, defaultIndex); 2313 2314 // a last audible index of 0 should never be stored for ring and notification 2315 // streams on phones (voice capable devices). 2316 // same for system stream on phones and tablets 2317 if ((lastAudibleIndex == 0) && 2318 ((mVoiceCapable && 2319 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) || 2320 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) { 2321 lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]; 2322 // Correct the data base 2323 sendMsg(mAudioHandler, 2324 MSG_PERSIST_VOLUME, 2325 SENDMSG_QUEUE, 2326 PERSIST_LAST_AUDIBLE, 2327 device, 2328 this, 2329 PERSIST_DELAY); 2330 } 2331 mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex)); 2332 // the initial index should never be 0 for ring and notification streams on phones 2333 // (voice capable devices) if not in silent or vibrate mode. 2334 // same for system stream on phones and tablets 2335 if ((index == 0) && (mRingerMode == AudioManager.RINGER_MODE_NORMAL) && 2336 ((mVoiceCapable && 2337 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) || 2338 (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) { 2339 index = lastAudibleIndex; 2340 // Correct the data base 2341 sendMsg(mAudioHandler, 2342 MSG_PERSIST_VOLUME, 2343 SENDMSG_QUEUE, 2344 PERSIST_CURRENT, 2345 device, 2346 this, 2347 PERSIST_DELAY); 2348 } 2349 mIndex.put(device, getValidIndex(10 * index)); 2350 } 2351 } 2352 2353 public void applyDeviceVolume(int device) { 2354 AudioSystem.setStreamVolumeIndex(mStreamType, 2355 (getIndex(device, false /* lastAudible */) + 5)/10, 2356 device); 2357 } 2358 2359 public synchronized void applyAllVolumes() { 2360 // apply default volume first: by convention this will reset all 2361 // devices volumes in audio policy manager to the supplied value 2362 AudioSystem.setStreamVolumeIndex(mStreamType, 2363 (getIndex(AudioSystem.DEVICE_OUT_DEFAULT, false /* lastAudible */) + 5)/10, 2364 AudioSystem.DEVICE_OUT_DEFAULT); 2365 // then apply device specific volumes 2366 Set set = mIndex.entrySet(); 2367 Iterator i = set.iterator(); 2368 while (i.hasNext()) { 2369 Map.Entry entry = (Map.Entry)i.next(); 2370 int device = ((Integer)entry.getKey()).intValue(); 2371 if (device != AudioSystem.DEVICE_OUT_DEFAULT) { 2372 AudioSystem.setStreamVolumeIndex(mStreamType, 2373 ((Integer)entry.getValue() + 5)/10, 2374 device); 2375 } 2376 } 2377 } 2378 2379 public boolean adjustIndex(int deltaIndex, int device) { 2380 return setIndex(getIndex(device, 2381 false /* lastAudible */) + deltaIndex, 2382 device, 2383 true /* lastAudible */); 2384 } 2385 2386 public synchronized boolean setIndex(int index, int device, boolean lastAudible) { 2387 int oldIndex = getIndex(device, false /* lastAudible */); 2388 index = getValidIndex(index); 2389 mIndex.put(device, getValidIndex(index)); 2390 2391 if (oldIndex != index) { 2392 if (lastAudible) { 2393 mLastAudibleIndex.put(device, index); 2394 } 2395 // Apply change to all streams using this one as alias 2396 // if changing volume of current device, also change volume of current 2397 // device on aliased stream 2398 boolean currentDevice = (device == getDeviceForStream(mStreamType)); 2399 int numStreamTypes = AudioSystem.getNumStreamTypes(); 2400 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 2401 if (streamType != mStreamType && 2402 mStreamVolumeAlias[streamType] == mStreamType) { 2403 int scaledIndex = rescaleIndex(index, mStreamType, streamType); 2404 mStreamStates[streamType].setIndex(scaledIndex, 2405 device, 2406 lastAudible); 2407 if (currentDevice) { 2408 mStreamStates[streamType].setIndex(scaledIndex, 2409 getDeviceForStream(streamType), 2410 lastAudible); 2411 } 2412 } 2413 } 2414 return true; 2415 } else { 2416 return false; 2417 } 2418 } 2419 2420 public synchronized int getIndex(int device, boolean lastAudible) { 2421 ConcurrentHashMap <Integer, Integer> indexes; 2422 if (lastAudible) { 2423 indexes = mLastAudibleIndex; 2424 } else { 2425 indexes = mIndex; 2426 } 2427 Integer index = indexes.get(device); 2428 if (index == null) { 2429 // there is always an entry for AudioSystem.DEVICE_OUT_DEFAULT 2430 index = indexes.get(AudioSystem.DEVICE_OUT_DEFAULT); 2431 } 2432 return index.intValue(); 2433 } 2434 2435 public synchronized void setLastAudibleIndex(int index, int device) { 2436 // Apply change to all streams using this one as alias 2437 // if changing volume of current device, also change volume of current 2438 // device on aliased stream 2439 boolean currentDevice = (device == getDeviceForStream(mStreamType)); 2440 int numStreamTypes = AudioSystem.getNumStreamTypes(); 2441 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 2442 if (streamType != mStreamType && 2443 mStreamVolumeAlias[streamType] == mStreamType) { 2444 int scaledIndex = rescaleIndex(index, mStreamType, streamType); 2445 mStreamStates[streamType].setLastAudibleIndex(scaledIndex, device); 2446 if (currentDevice) { 2447 mStreamStates[streamType].setLastAudibleIndex(scaledIndex, 2448 getDeviceForStream(streamType)); 2449 } 2450 } 2451 } 2452 mLastAudibleIndex.put(device, getValidIndex(index)); 2453 } 2454 2455 public synchronized void adjustLastAudibleIndex(int deltaIndex, int device) { 2456 setLastAudibleIndex(getIndex(device, 2457 true /* lastAudible */) + deltaIndex, 2458 device); 2459 } 2460 2461 public int getMaxIndex() { 2462 return mIndexMax; 2463 } 2464 2465 // only called by setAllIndexes() which is already synchronized 2466 public ConcurrentHashMap <Integer, Integer> getAllIndexes(boolean lastAudible) { 2467 if (lastAudible) { 2468 return mLastAudibleIndex; 2469 } else { 2470 return mIndex; 2471 } 2472 } 2473 2474 public synchronized void setAllIndexes(VolumeStreamState srcStream, boolean lastAudible) { 2475 ConcurrentHashMap <Integer, Integer> indexes = srcStream.getAllIndexes(lastAudible); 2476 Set set = indexes.entrySet(); 2477 Iterator i = set.iterator(); 2478 while (i.hasNext()) { 2479 Map.Entry entry = (Map.Entry)i.next(); 2480 int device = ((Integer)entry.getKey()).intValue(); 2481 int index = ((Integer)entry.getValue()).intValue(); 2482 index = rescaleIndex(index, srcStream.getStreamType(), mStreamType); 2483 setIndex(index, device, lastAudible); 2484 } 2485 } 2486 2487 public synchronized void mute(IBinder cb, boolean state) { 2488 VolumeDeathHandler handler = getDeathHandler(cb, state); 2489 if (handler == null) { 2490 Log.e(TAG, "Could not get client death handler for stream: "+mStreamType); 2491 return; 2492 } 2493 handler.mute(state); 2494 } 2495 2496 public int getStreamType() { 2497 return mStreamType; 2498 } 2499 2500 private int getValidIndex(int index) { 2501 if (index < 0) { 2502 return 0; 2503 } else if (index > mIndexMax) { 2504 return mIndexMax; 2505 } 2506 2507 return index; 2508 } 2509 2510 private class VolumeDeathHandler implements IBinder.DeathRecipient { 2511 private IBinder mICallback; // To be notified of client's death 2512 private int mMuteCount; // Number of active mutes for this client 2513 2514 VolumeDeathHandler(IBinder cb) { 2515 mICallback = cb; 2516 } 2517 2518 // must be called while synchronized on parent VolumeStreamState 2519 public void mute(boolean state) { 2520 if (state) { 2521 if (mMuteCount == 0) { 2522 // Register for client death notification 2523 try { 2524 // mICallback can be 0 if muted by AudioService 2525 if (mICallback != null) { 2526 mICallback.linkToDeath(this, 0); 2527 } 2528 mDeathHandlers.add(this); 2529 // If the stream is not yet muted by any client, set level to 0 2530 if (muteCount() == 0) { 2531 Set set = mIndex.entrySet(); 2532 Iterator i = set.iterator(); 2533 while (i.hasNext()) { 2534 Map.Entry entry = (Map.Entry)i.next(); 2535 int device = ((Integer)entry.getKey()).intValue(); 2536 setIndex(0, device, false /* lastAudible */); 2537 } 2538 sendMsg(mAudioHandler, 2539 MSG_SET_ALL_VOLUMES, 2540 SENDMSG_QUEUE, 2541 0, 2542 0, 2543 VolumeStreamState.this, 0); 2544 } 2545 } catch (RemoteException e) { 2546 // Client has died! 2547 binderDied(); 2548 return; 2549 } 2550 } else { 2551 Log.w(TAG, "stream: "+mStreamType+" was already muted by this client"); 2552 } 2553 mMuteCount++; 2554 } else { 2555 if (mMuteCount == 0) { 2556 Log.e(TAG, "unexpected unmute for stream: "+mStreamType); 2557 } else { 2558 mMuteCount--; 2559 if (mMuteCount == 0) { 2560 // Unregister from client death notification 2561 mDeathHandlers.remove(this); 2562 // mICallback can be 0 if muted by AudioService 2563 if (mICallback != null) { 2564 mICallback.unlinkToDeath(this, 0); 2565 } 2566 if (muteCount() == 0) { 2567 // If the stream is not muted any more, restore its volume if 2568 // ringer mode allows it 2569 if (!isStreamAffectedByRingerMode(mStreamType) || 2570 mRingerMode == AudioManager.RINGER_MODE_NORMAL) { 2571 Set set = mIndex.entrySet(); 2572 Iterator i = set.iterator(); 2573 while (i.hasNext()) { 2574 Map.Entry entry = (Map.Entry)i.next(); 2575 int device = ((Integer)entry.getKey()).intValue(); 2576 setIndex(getIndex(device, 2577 true /* lastAudible */), 2578 device, 2579 false /* lastAudible */); 2580 } 2581 sendMsg(mAudioHandler, 2582 MSG_SET_ALL_VOLUMES, 2583 SENDMSG_QUEUE, 2584 0, 2585 0, 2586 VolumeStreamState.this, 0); 2587 } 2588 } 2589 } 2590 } 2591 } 2592 } 2593 2594 public void binderDied() { 2595 Log.w(TAG, "Volume service client died for stream: "+mStreamType); 2596 if (mMuteCount != 0) { 2597 // Reset all active mute requests from this client. 2598 mMuteCount = 1; 2599 mute(false); 2600 } 2601 } 2602 } 2603 2604 private synchronized int muteCount() { 2605 int count = 0; 2606 int size = mDeathHandlers.size(); 2607 for (int i = 0; i < size; i++) { 2608 count += mDeathHandlers.get(i).mMuteCount; 2609 } 2610 return count; 2611 } 2612 2613 // only called by mute() which is already synchronized 2614 private VolumeDeathHandler getDeathHandler(IBinder cb, boolean state) { 2615 VolumeDeathHandler handler; 2616 int size = mDeathHandlers.size(); 2617 for (int i = 0; i < size; i++) { 2618 handler = mDeathHandlers.get(i); 2619 if (cb == handler.mICallback) { 2620 return handler; 2621 } 2622 } 2623 // If this is the first mute request for this client, create a new 2624 // client death handler. Otherwise, it is an out of sequence unmute request. 2625 if (state) { 2626 handler = new VolumeDeathHandler(cb); 2627 } else { 2628 Log.w(TAG, "stream was not muted by this client"); 2629 handler = null; 2630 } 2631 return handler; 2632 } 2633 2634 private void dump(PrintWriter pw) { 2635 pw.print(" Current: "); 2636 Set set = mIndex.entrySet(); 2637 Iterator i = set.iterator(); 2638 while (i.hasNext()) { 2639 Map.Entry entry = (Map.Entry)i.next(); 2640 pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue()) 2641 + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", "); 2642 } 2643 pw.print("\n Last audible: "); 2644 set = mLastAudibleIndex.entrySet(); 2645 i = set.iterator(); 2646 while (i.hasNext()) { 2647 Map.Entry entry = (Map.Entry)i.next(); 2648 pw.print(Integer.toHexString(((Integer)entry.getKey()).intValue()) 2649 + ": " + ((((Integer)entry.getValue()).intValue() + 5) / 10)+", "); 2650 } 2651 } 2652 } 2653 2654 /** Thread that handles native AudioSystem control. */ 2655 private class AudioSystemThread extends Thread { 2656 AudioSystemThread() { 2657 super("AudioService"); 2658 } 2659 2660 @Override 2661 public void run() { 2662 // Set this thread up so the handler will work on it 2663 Looper.prepare(); 2664 2665 synchronized(AudioService.this) { 2666 mAudioHandler = new AudioHandler(); 2667 2668 // Notify that the handler has been created 2669 AudioService.this.notify(); 2670 } 2671 2672 // Listen for volume change requests that are set by VolumePanel 2673 Looper.loop(); 2674 } 2675 } 2676 2677 /** Handles internal volume messages in separate volume thread. */ 2678 private class AudioHandler extends Handler { 2679 2680 private void setDeviceVolume(VolumeStreamState streamState, int device) { 2681 2682 // Apply volume 2683 streamState.applyDeviceVolume(device); 2684 2685 // Apply change to all streams using this one as alias 2686 int numStreamTypes = AudioSystem.getNumStreamTypes(); 2687 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 2688 if (streamType != streamState.mStreamType && 2689 mStreamVolumeAlias[streamType] == streamState.mStreamType) { 2690 mStreamStates[streamType].applyDeviceVolume(getDeviceForStream(streamType)); 2691 } 2692 } 2693 2694 // Post a persist volume msg 2695 sendMsg(mAudioHandler, 2696 MSG_PERSIST_VOLUME, 2697 SENDMSG_QUEUE, 2698 PERSIST_CURRENT|PERSIST_LAST_AUDIBLE, 2699 device, 2700 streamState, 2701 PERSIST_DELAY); 2702 2703 } 2704 2705 private void setAllVolumes(VolumeStreamState streamState) { 2706 2707 // Apply volume 2708 streamState.applyAllVolumes(); 2709 2710 // Apply change to all streams using this one as alias 2711 int numStreamTypes = AudioSystem.getNumStreamTypes(); 2712 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 2713 if (streamType != streamState.mStreamType && 2714 mStreamVolumeAlias[streamType] == streamState.mStreamType) { 2715 mStreamStates[streamType].applyAllVolumes(); 2716 } 2717 } 2718 } 2719 2720 private void persistVolume(VolumeStreamState streamState, 2721 int persistType, 2722 int device) { 2723 if ((persistType & PERSIST_CURRENT) != 0) { 2724 System.putInt(mContentResolver, 2725 streamState.getSettingNameForDevice(false /* lastAudible */, device), 2726 (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10); 2727 } 2728 if ((persistType & PERSIST_LAST_AUDIBLE) != 0) { 2729 System.putInt(mContentResolver, 2730 streamState.getSettingNameForDevice(true /* lastAudible */, device), 2731 (streamState.getIndex(device, true /* lastAudible */) + 5) / 10); 2732 } 2733 } 2734 2735 private void persistRingerMode(int ringerMode) { 2736 System.putInt(mContentResolver, System.MODE_RINGER, ringerMode); 2737 } 2738 2739 private void playSoundEffect(int effectType, int volume) { 2740 synchronized (mSoundEffectsLock) { 2741 if (mSoundPool == null) { 2742 return; 2743 } 2744 float volFloat; 2745 // use default if volume is not specified by caller 2746 if (volume < 0) { 2747 volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20); 2748 } else { 2749 volFloat = (float) volume / 1000.0f; 2750 } 2751 2752 if (SOUND_EFFECT_FILES_MAP[effectType][1] > 0) { 2753 mSoundPool.play(SOUND_EFFECT_FILES_MAP[effectType][1], volFloat, volFloat, 0, 0, 1.0f); 2754 } else { 2755 MediaPlayer mediaPlayer = new MediaPlayer(); 2756 try { 2757 String filePath = Environment.getRootDirectory() + SOUND_EFFECTS_PATH + SOUND_EFFECT_FILES[SOUND_EFFECT_FILES_MAP[effectType][0]]; 2758 mediaPlayer.setDataSource(filePath); 2759 mediaPlayer.setAudioStreamType(AudioSystem.STREAM_SYSTEM); 2760 mediaPlayer.prepare(); 2761 mediaPlayer.setVolume(volFloat, volFloat); 2762 mediaPlayer.setOnCompletionListener(new OnCompletionListener() { 2763 public void onCompletion(MediaPlayer mp) { 2764 cleanupPlayer(mp); 2765 } 2766 }); 2767 mediaPlayer.setOnErrorListener(new OnErrorListener() { 2768 public boolean onError(MediaPlayer mp, int what, int extra) { 2769 cleanupPlayer(mp); 2770 return true; 2771 } 2772 }); 2773 mediaPlayer.start(); 2774 } catch (IOException ex) { 2775 Log.w(TAG, "MediaPlayer IOException: "+ex); 2776 } catch (IllegalArgumentException ex) { 2777 Log.w(TAG, "MediaPlayer IllegalArgumentException: "+ex); 2778 } catch (IllegalStateException ex) { 2779 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 2780 } 2781 } 2782 } 2783 } 2784 2785 private void onHandlePersistMediaButtonReceiver(ComponentName receiver) { 2786 Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER, 2787 receiver == null ? "" : receiver.flattenToString()); 2788 } 2789 2790 private void cleanupPlayer(MediaPlayer mp) { 2791 if (mp != null) { 2792 try { 2793 mp.stop(); 2794 mp.release(); 2795 } catch (IllegalStateException ex) { 2796 Log.w(TAG, "MediaPlayer IllegalStateException: "+ex); 2797 } 2798 } 2799 } 2800 2801 private void setForceUse(int usage, int config) { 2802 AudioSystem.setForceUse(usage, config); 2803 } 2804 2805 @Override 2806 public void handleMessage(Message msg) { 2807 2808 switch (msg.what) { 2809 2810 case MSG_SET_DEVICE_VOLUME: 2811 setDeviceVolume((VolumeStreamState) msg.obj, msg.arg1); 2812 break; 2813 2814 case MSG_SET_ALL_VOLUMES: 2815 setAllVolumes((VolumeStreamState) msg.obj); 2816 break; 2817 2818 case MSG_PERSIST_VOLUME: 2819 persistVolume((VolumeStreamState) msg.obj, msg.arg1, msg.arg2); 2820 break; 2821 2822 case MSG_PERSIST_MASTER_VOLUME: 2823 Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER, 2824 (float)msg.arg1 / (float)1000.0); 2825 break; 2826 2827 case MSG_PERSIST_MASTER_VOLUME_MUTE: 2828 Settings.System.putInt(mContentResolver, Settings.System.VOLUME_MASTER_MUTE, 2829 msg.arg1); 2830 break; 2831 2832 case MSG_PERSIST_RINGER_MODE: 2833 // note that the value persisted is the current ringer mode, not the 2834 // value of ringer mode as of the time the request was made to persist 2835 persistRingerMode(getRingerMode()); 2836 break; 2837 2838 case MSG_MEDIA_SERVER_DIED: 2839 if (!mMediaServerOk) { 2840 Log.e(TAG, "Media server died."); 2841 // Force creation of new IAudioFlinger interface so that we are notified 2842 // when new media_server process is back to life. 2843 AudioSystem.setErrorCallback(mAudioSystemCallback); 2844 sendMsg(mAudioHandler, MSG_MEDIA_SERVER_DIED, SENDMSG_NOOP, 0, 0, 2845 null, 500); 2846 } 2847 break; 2848 2849 case MSG_MEDIA_SERVER_STARTED: 2850 Log.e(TAG, "Media server started."); 2851 // indicate to audio HAL that we start the reconfiguration phase after a media 2852 // server crash 2853 // Note that MSG_MEDIA_SERVER_STARTED message is only received when the media server 2854 // process restarts after a crash, not the first time it is started. 2855 AudioSystem.setParameters("restarting=true"); 2856 2857 // Restore device connection states 2858 synchronized (mConnectedDevices) { 2859 Set set = mConnectedDevices.entrySet(); 2860 Iterator i = set.iterator(); 2861 while (i.hasNext()) { 2862 Map.Entry device = (Map.Entry)i.next(); 2863 AudioSystem.setDeviceConnectionState( 2864 ((Integer)device.getKey()).intValue(), 2865 AudioSystem.DEVICE_STATE_AVAILABLE, 2866 (String)device.getValue()); 2867 } 2868 } 2869 // Restore call state 2870 AudioSystem.setPhoneState(mMode); 2871 2872 // Restore forced usage for communcations and record 2873 AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm); 2874 AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm); 2875 2876 // Restore stream volumes 2877 int numStreamTypes = AudioSystem.getNumStreamTypes(); 2878 for (int streamType = numStreamTypes - 1; streamType >= 0; streamType--) { 2879 VolumeStreamState streamState = mStreamStates[streamType]; 2880 AudioSystem.initStreamVolume(streamType, 0, (streamState.mIndexMax + 5) / 10); 2881 2882 streamState.applyAllVolumes(); 2883 } 2884 2885 // Restore ringer mode 2886 setRingerModeInt(getRingerMode(), false); 2887 2888 // Restore master volume 2889 restoreMasterVolume(); 2890 2891 // Reset device orientation (if monitored for this device) 2892 if (SystemProperties.getBoolean("ro.audio.monitorOrientation", false)) { 2893 setOrientationForAudioSystem(); 2894 } 2895 2896 // indicate the end of reconfiguration phase to audio HAL 2897 AudioSystem.setParameters("restarting=false"); 2898 break; 2899 2900 case MSG_LOAD_SOUND_EFFECTS: 2901 loadSoundEffects(); 2902 break; 2903 2904 case MSG_PLAY_SOUND_EFFECT: 2905 playSoundEffect(msg.arg1, msg.arg2); 2906 break; 2907 2908 case MSG_BTA2DP_DOCK_TIMEOUT: 2909 // msg.obj == address of BTA2DP device 2910 synchronized (mConnectedDevices) { 2911 makeA2dpDeviceUnavailableNow( (String) msg.obj ); 2912 } 2913 break; 2914 2915 case MSG_SET_FORCE_USE: 2916 setForceUse(msg.arg1, msg.arg2); 2917 break; 2918 2919 case MSG_PERSIST_MEDIABUTTONRECEIVER: 2920 onHandlePersistMediaButtonReceiver( (ComponentName) msg.obj ); 2921 break; 2922 2923 case MSG_RCDISPLAY_CLEAR: 2924 onRcDisplayClear(); 2925 break; 2926 2927 case MSG_RCDISPLAY_UPDATE: 2928 // msg.obj is guaranteed to be non null 2929 onRcDisplayUpdate( (RemoteControlStackEntry) msg.obj, msg.arg1); 2930 break; 2931 2932 case MSG_BT_HEADSET_CNCT_FAILED: 2933 resetBluetoothSco(); 2934 break; 2935 } 2936 } 2937 } 2938 2939 private class SettingsObserver extends ContentObserver { 2940 2941 SettingsObserver() { 2942 super(new Handler()); 2943 mContentResolver.registerContentObserver(Settings.System.getUriFor( 2944 Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this); 2945 } 2946 2947 @Override 2948 public void onChange(boolean selfChange) { 2949 super.onChange(selfChange); 2950 // FIXME This synchronized is not necessary if mSettingsLock only protects mRingerMode. 2951 // However there appear to be some missing locks around mRingerModeMutedStreams 2952 // and mRingerModeAffectedStreams, so will leave this synchronized for now. 2953 // mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once). 2954 synchronized (mSettingsLock) { 2955 int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver, 2956 Settings.System.MODE_RINGER_STREAMS_AFFECTED, 2957 ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)| 2958 (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED))); 2959 if (mVoiceCapable) { 2960 ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC); 2961 } else { 2962 ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC); 2963 } 2964 if (ringerModeAffectedStreams != mRingerModeAffectedStreams) { 2965 /* 2966 * Ensure all stream types that should be affected by ringer mode 2967 * are in the proper state. 2968 */ 2969 mRingerModeAffectedStreams = ringerModeAffectedStreams; 2970 setRingerModeInt(getRingerMode(), false); 2971 } 2972 } 2973 } 2974 } 2975 2976 // must be called synchronized on mConnectedDevices 2977 private void makeA2dpDeviceAvailable(String address) { 2978 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 2979 AudioSystem.DEVICE_STATE_AVAILABLE, 2980 address); 2981 // Reset A2DP suspend state each time a new sink is connected 2982 AudioSystem.setParameters("A2dpSuspended=false"); 2983 mConnectedDevices.put( new Integer(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP), 2984 address); 2985 } 2986 2987 private void sendBecomingNoisyIntent() { 2988 mContext.sendBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY)); 2989 } 2990 2991 // must be called synchronized on mConnectedDevices 2992 private void makeA2dpDeviceUnavailableNow(String address) { 2993 sendBecomingNoisyIntent(); 2994 AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP, 2995 AudioSystem.DEVICE_STATE_UNAVAILABLE, 2996 address); 2997 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 2998 } 2999 3000 // must be called synchronized on mConnectedDevices 3001 private void makeA2dpDeviceUnavailableLater(String address) { 3002 // prevent any activity on the A2DP audio output to avoid unwanted 3003 // reconnection of the sink. 3004 AudioSystem.setParameters("A2dpSuspended=true"); 3005 // the device will be made unavailable later, so consider it disconnected right away 3006 mConnectedDevices.remove(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP); 3007 // send the delayed message to make the device unavailable later 3008 Message msg = mAudioHandler.obtainMessage(MSG_BTA2DP_DOCK_TIMEOUT, address); 3009 mAudioHandler.sendMessageDelayed(msg, BTA2DP_DOCK_TIMEOUT_MILLIS); 3010 3011 } 3012 3013 // must be called synchronized on mConnectedDevices 3014 private void cancelA2dpDeviceTimeout() { 3015 mAudioHandler.removeMessages(MSG_BTA2DP_DOCK_TIMEOUT); 3016 } 3017 3018 // must be called synchronized on mConnectedDevices 3019 private boolean hasScheduledA2dpDockTimeout() { 3020 return mAudioHandler.hasMessages(MSG_BTA2DP_DOCK_TIMEOUT); 3021 } 3022 3023 private void handleA2dpConnectionStateChange(BluetoothDevice btDevice, int state) 3024 { 3025 if (btDevice == null) { 3026 return; 3027 } 3028 String address = btDevice.getAddress(); 3029 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 3030 address = ""; 3031 } 3032 synchronized (mConnectedDevices) { 3033 boolean isConnected = 3034 (mConnectedDevices.containsKey(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP) && 3035 mConnectedDevices.get(AudioSystem.DEVICE_OUT_BLUETOOTH_A2DP).equals(address)); 3036 3037 if (isConnected && state != BluetoothProfile.STATE_CONNECTED) { 3038 if (btDevice.isBluetoothDock()) { 3039 if (state == BluetoothProfile.STATE_DISCONNECTED) { 3040 // introduction of a delay for transient disconnections of docks when 3041 // power is rapidly turned off/on, this message will be canceled if 3042 // we reconnect the dock under a preset delay 3043 makeA2dpDeviceUnavailableLater(address); 3044 // the next time isConnected is evaluated, it will be false for the dock 3045 } 3046 } else { 3047 makeA2dpDeviceUnavailableNow(address); 3048 } 3049 } else if (!isConnected && state == BluetoothProfile.STATE_CONNECTED) { 3050 if (btDevice.isBluetoothDock()) { 3051 // this could be a reconnection after a transient disconnection 3052 cancelA2dpDeviceTimeout(); 3053 mDockAddress = address; 3054 } else { 3055 // this could be a connection of another A2DP device before the timeout of 3056 // a dock: cancel the dock timeout, and make the dock unavailable now 3057 if(hasScheduledA2dpDockTimeout()) { 3058 cancelA2dpDeviceTimeout(); 3059 makeA2dpDeviceUnavailableNow(mDockAddress); 3060 } 3061 } 3062 makeA2dpDeviceAvailable(address); 3063 } 3064 } 3065 } 3066 3067 private boolean handleDeviceConnection(boolean connected, int device, String params) { 3068 synchronized (mConnectedDevices) { 3069 boolean isConnected = (mConnectedDevices.containsKey(device) && 3070 (params.isEmpty() || mConnectedDevices.get(device).equals(params))); 3071 3072 if (isConnected && !connected) { 3073 AudioSystem.setDeviceConnectionState(device, 3074 AudioSystem.DEVICE_STATE_UNAVAILABLE, 3075 mConnectedDevices.get(device)); 3076 mConnectedDevices.remove(device); 3077 return true; 3078 } else if (!isConnected && connected) { 3079 AudioSystem.setDeviceConnectionState(device, 3080 AudioSystem.DEVICE_STATE_AVAILABLE, 3081 params); 3082 mConnectedDevices.put(new Integer(device), params); 3083 return true; 3084 } 3085 } 3086 return false; 3087 } 3088 3089 /* cache of the address of the last dock the device was connected to */ 3090 private String mDockAddress; 3091 3092 /** 3093 * Receiver for misc intent broadcasts the Phone app cares about. 3094 */ 3095 private class AudioServiceBroadcastReceiver extends BroadcastReceiver { 3096 @Override 3097 public void onReceive(Context context, Intent intent) { 3098 String action = intent.getAction(); 3099 int device; 3100 int state; 3101 3102 if (action.equals(Intent.ACTION_DOCK_EVENT)) { 3103 int dockState = intent.getIntExtra(Intent.EXTRA_DOCK_STATE, 3104 Intent.EXTRA_DOCK_STATE_UNDOCKED); 3105 int config; 3106 switch (dockState) { 3107 case Intent.EXTRA_DOCK_STATE_DESK: 3108 config = AudioSystem.FORCE_BT_DESK_DOCK; 3109 break; 3110 case Intent.EXTRA_DOCK_STATE_CAR: 3111 config = AudioSystem.FORCE_BT_CAR_DOCK; 3112 break; 3113 case Intent.EXTRA_DOCK_STATE_LE_DESK: 3114 config = AudioSystem.FORCE_ANALOG_DOCK; 3115 break; 3116 case Intent.EXTRA_DOCK_STATE_HE_DESK: 3117 config = AudioSystem.FORCE_DIGITAL_DOCK; 3118 break; 3119 case Intent.EXTRA_DOCK_STATE_UNDOCKED: 3120 default: 3121 config = AudioSystem.FORCE_NONE; 3122 } 3123 AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config); 3124 } else if (action.equals(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) { 3125 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 3126 BluetoothProfile.STATE_DISCONNECTED); 3127 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 3128 3129 handleA2dpConnectionStateChange(btDevice, state); 3130 } else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) { 3131 state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, 3132 BluetoothProfile.STATE_DISCONNECTED); 3133 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO; 3134 String address = null; 3135 3136 BluetoothDevice btDevice = intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE); 3137 if (btDevice == null) { 3138 return; 3139 } 3140 3141 address = btDevice.getAddress(); 3142 BluetoothClass btClass = btDevice.getBluetoothClass(); 3143 if (btClass != null) { 3144 switch (btClass.getDeviceClass()) { 3145 case BluetoothClass.Device.AUDIO_VIDEO_WEARABLE_HEADSET: 3146 case BluetoothClass.Device.AUDIO_VIDEO_HANDSFREE: 3147 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_HEADSET; 3148 break; 3149 case BluetoothClass.Device.AUDIO_VIDEO_CAR_AUDIO: 3150 device = AudioSystem.DEVICE_OUT_BLUETOOTH_SCO_CARKIT; 3151 break; 3152 } 3153 } 3154 3155 if (!BluetoothAdapter.checkBluetoothAddress(address)) { 3156 address = ""; 3157 } 3158 3159 boolean connected = (state == BluetoothProfile.STATE_CONNECTED); 3160 if (handleDeviceConnection(connected, device, address)) { 3161 synchronized (mScoClients) { 3162 if (connected) { 3163 mBluetoothHeadsetDevice = btDevice; 3164 } else { 3165 mBluetoothHeadsetDevice = null; 3166 resetBluetoothSco(); 3167 } 3168 } 3169 } 3170 } else if (action.equals(Intent.ACTION_HEADSET_PLUG)) { 3171 state = intent.getIntExtra("state", 0); 3172 int microphone = intent.getIntExtra("microphone", 0); 3173 3174 if (microphone != 0) { 3175 device = AudioSystem.DEVICE_OUT_WIRED_HEADSET; 3176 } else { 3177 device = AudioSystem.DEVICE_OUT_WIRED_HEADPHONE; 3178 } 3179 handleDeviceConnection((state == 1), device, ""); 3180 } else if (action.equals(Intent.ACTION_ANALOG_AUDIO_DOCK_PLUG)) { 3181 state = intent.getIntExtra("state", 0); 3182 Log.v(TAG, "Broadcast Receiver: Got ACTION_ANALOG_AUDIO_DOCK_PLUG, state = "+state); 3183 handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET, ""); 3184 } else if (action.equals(Intent.ACTION_HDMI_AUDIO_PLUG)) { 3185 state = intent.getIntExtra("state", 0); 3186 Log.v(TAG, "Broadcast Receiver: Got ACTION_HDMI_AUDIO_PLUG, state = "+state); 3187 handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_AUX_DIGITAL, ""); 3188 } else if (action.equals(Intent.ACTION_DIGITAL_AUDIO_DOCK_PLUG)) { 3189 state = intent.getIntExtra("state", 0); 3190 Log.v(TAG, 3191 "Broadcast Receiver Got ACTION_DIGITAL_AUDIO_DOCK_PLUG, state = " + state); 3192 handleDeviceConnection((state == 1), AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET, ""); 3193 } else if (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) || 3194 action.equals(Intent.ACTION_USB_AUDIO_DEVICE_PLUG)) { 3195 state = intent.getIntExtra("state", 0); 3196 if (state == 0) { 3197 sendBecomingNoisyIntent(); 3198 } 3199 int alsaCard = intent.getIntExtra("card", -1); 3200 int alsaDevice = intent.getIntExtra("device", -1); 3201 String params = (alsaCard == -1 && alsaDevice == -1 ? "" 3202 : "card=" + alsaCard + ";device=" + alsaDevice); 3203 device = action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ? 3204 AudioSystem.DEVICE_OUT_USB_ACCESSORY : AudioSystem.DEVICE_OUT_USB_DEVICE; 3205 Log.v(TAG, "Broadcast Receiver: Got " 3206 + (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ? 3207 "ACTION_USB_AUDIO_ACCESSORY_PLUG" : "ACTION_USB_AUDIO_DEVICE_PLUG") 3208 + ", state = " + state + ", card: " + alsaCard + ", device: " + alsaDevice); 3209 handleDeviceConnection((state == 1), device, params); 3210 } else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) { 3211 boolean broadcast = false; 3212 int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR; 3213 synchronized (mScoClients) { 3214 int btState = intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1); 3215 // broadcast intent if the connection was initated by AudioService 3216 if (!mScoClients.isEmpty() && 3217 (mScoAudioState == SCO_STATE_ACTIVE_INTERNAL || 3218 mScoAudioState == SCO_STATE_ACTIVATE_REQ || 3219 mScoAudioState == SCO_STATE_DEACTIVATE_REQ)) { 3220 broadcast = true; 3221 } 3222 switch (btState) { 3223 case BluetoothHeadset.STATE_AUDIO_CONNECTED: 3224 scoAudioState = AudioManager.SCO_AUDIO_STATE_CONNECTED; 3225 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && 3226 mScoAudioState != SCO_STATE_DEACTIVATE_REQ && 3227 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) { 3228 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 3229 } 3230 break; 3231 case BluetoothHeadset.STATE_AUDIO_DISCONNECTED: 3232 scoAudioState = AudioManager.SCO_AUDIO_STATE_DISCONNECTED; 3233 mScoAudioState = SCO_STATE_INACTIVE; 3234 clearAllScoClients(0, false); 3235 break; 3236 case BluetoothHeadset.STATE_AUDIO_CONNECTING: 3237 if (mScoAudioState != SCO_STATE_ACTIVE_INTERNAL && 3238 mScoAudioState != SCO_STATE_DEACTIVATE_REQ && 3239 mScoAudioState != SCO_STATE_DEACTIVATE_EXT_REQ) { 3240 mScoAudioState = SCO_STATE_ACTIVE_EXTERNAL; 3241 } 3242 default: 3243 // do not broadcast CONNECTING or invalid state 3244 broadcast = false; 3245 break; 3246 } 3247 } 3248 if (broadcast) { 3249 broadcastScoConnectionState(scoAudioState); 3250 //FIXME: this is to maintain compatibility with deprecated intent 3251 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. 3252 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 3253 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState); 3254 mContext.sendStickyBroadcast(newIntent); 3255 } 3256 } else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) { 3257 mBootCompleted = true; 3258 sendMsg(mAudioHandler, MSG_LOAD_SOUND_EFFECTS, SENDMSG_NOOP, 3259 0, 0, null, 0); 3260 3261 mKeyguardManager = 3262 (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); 3263 mScoConnectionState = AudioManager.SCO_AUDIO_STATE_ERROR; 3264 resetBluetoothSco(); 3265 getBluetoothHeadset(); 3266 //FIXME: this is to maintain compatibility with deprecated intent 3267 // AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate. 3268 Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED); 3269 newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, 3270 AudioManager.SCO_AUDIO_STATE_DISCONNECTED); 3271 mContext.sendStickyBroadcast(newIntent); 3272 3273 BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter(); 3274 if (adapter != null) { 3275 adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener, 3276 BluetoothProfile.A2DP); 3277 } 3278 } else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) { 3279 if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) { 3280 // a package is being removed, not replaced 3281 String packageName = intent.getData().getSchemeSpecificPart(); 3282 if (packageName != null) { 3283 removeMediaButtonReceiverForPackage(packageName); 3284 } 3285 } 3286 } else if (action.equals(Intent.ACTION_SCREEN_ON)) { 3287 AudioSystem.setParameters("screen_state=on"); 3288 } else if (action.equals(Intent.ACTION_SCREEN_OFF)) { 3289 AudioSystem.setParameters("screen_state=off"); 3290 } else if (action.equalsIgnoreCase(Intent.ACTION_CONFIGURATION_CHANGED)) { 3291 handleConfigurationChanged(context); 3292 } 3293 } 3294 } 3295 3296 //========================================================================================== 3297 // AudioFocus 3298 //========================================================================================== 3299 3300 /* constant to identify focus stack entry that is used to hold the focus while the phone 3301 * is ringing or during a call. Used by com.android.internal.telephony.CallManager when 3302 * entering and exiting calls. 3303 */ 3304 public final static String IN_VOICE_COMM_FOCUS_ID = "AudioFocus_For_Phone_Ring_And_Calls"; 3305 3306 private final static Object mAudioFocusLock = new Object(); 3307 3308 private final static Object mRingingLock = new Object(); 3309 3310 private PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 3311 @Override 3312 public void onCallStateChanged(int state, String incomingNumber) { 3313 if (state == TelephonyManager.CALL_STATE_RINGING) { 3314 //Log.v(TAG, " CALL_STATE_RINGING"); 3315 synchronized(mRingingLock) { 3316 mIsRinging = true; 3317 } 3318 } else if ((state == TelephonyManager.CALL_STATE_OFFHOOK) 3319 || (state == TelephonyManager.CALL_STATE_IDLE)) { 3320 synchronized(mRingingLock) { 3321 mIsRinging = false; 3322 } 3323 } 3324 } 3325 }; 3326 3327 private void notifyTopOfAudioFocusStack() { 3328 // notify the top of the stack it gained focus 3329 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 3330 if (canReassignAudioFocus()) { 3331 try { 3332 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 3333 AudioManager.AUDIOFOCUS_GAIN, mFocusStack.peek().mClientId); 3334 } catch (RemoteException e) { 3335 Log.e(TAG, "Failure to signal gain of audio control focus due to "+ e); 3336 e.printStackTrace(); 3337 } 3338 } 3339 } 3340 } 3341 3342 private static class FocusStackEntry { 3343 public int mStreamType = -1;// no stream type 3344 public IAudioFocusDispatcher mFocusDispatcher = null; 3345 public IBinder mSourceRef = null; 3346 public String mClientId; 3347 public int mFocusChangeType; 3348 public AudioFocusDeathHandler mHandler; 3349 public String mPackageName; 3350 public int mCallingUid; 3351 3352 public FocusStackEntry() { 3353 } 3354 3355 public FocusStackEntry(int streamType, int duration, 3356 IAudioFocusDispatcher afl, IBinder source, String id, AudioFocusDeathHandler hdlr, 3357 String pn, int uid) { 3358 mStreamType = streamType; 3359 mFocusDispatcher = afl; 3360 mSourceRef = source; 3361 mClientId = id; 3362 mFocusChangeType = duration; 3363 mHandler = hdlr; 3364 mPackageName = pn; 3365 mCallingUid = uid; 3366 } 3367 3368 public void unlinkToDeath() { 3369 try { 3370 if (mSourceRef != null && mHandler != null) { 3371 mSourceRef.unlinkToDeath(mHandler, 0); 3372 mHandler = null; 3373 } 3374 } catch (java.util.NoSuchElementException e) { 3375 Log.e(TAG, "Encountered " + e + " in FocusStackEntry.unlinkToDeath()"); 3376 } 3377 } 3378 3379 @Override 3380 protected void finalize() throws Throwable { 3381 unlinkToDeath(); // unlink exception handled inside method 3382 super.finalize(); 3383 } 3384 } 3385 3386 private final Stack<FocusStackEntry> mFocusStack = new Stack<FocusStackEntry>(); 3387 3388 /** 3389 * Helper function: 3390 * Display in the log the current entries in the audio focus stack 3391 */ 3392 private void dumpFocusStack(PrintWriter pw) { 3393 pw.println("\nAudio Focus stack entries:"); 3394 synchronized(mAudioFocusLock) { 3395 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 3396 while(stackIterator.hasNext()) { 3397 FocusStackEntry fse = stackIterator.next(); 3398 pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId 3399 + " -- duration: " + fse.mFocusChangeType 3400 + " -- uid: " + fse.mCallingUid); 3401 } 3402 } 3403 } 3404 3405 /** 3406 * Helper function: 3407 * Called synchronized on mAudioFocusLock 3408 * Remove a focus listener from the focus stack. 3409 * @param focusListenerToRemove the focus listener 3410 * @param signal if true and the listener was at the top of the focus stack, i.e. it was holding 3411 * focus, notify the next item in the stack it gained focus. 3412 */ 3413 private void removeFocusStackEntry(String clientToRemove, boolean signal) { 3414 // is the current top of the focus stack abandoning focus? (because of request, not death) 3415 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientToRemove)) 3416 { 3417 //Log.i(TAG, " removeFocusStackEntry() removing top of stack"); 3418 FocusStackEntry fse = mFocusStack.pop(); 3419 fse.unlinkToDeath(); 3420 if (signal) { 3421 // notify the new top of the stack it gained focus 3422 notifyTopOfAudioFocusStack(); 3423 // there's a new top of the stack, let the remote control know 3424 synchronized(mRCStack) { 3425 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 3426 } 3427 } 3428 } else { 3429 // focus is abandoned by a client that's not at the top of the stack, 3430 // no need to update focus. 3431 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 3432 while(stackIterator.hasNext()) { 3433 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 3434 if(fse.mClientId.equals(clientToRemove)) { 3435 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 3436 + fse.mClientId); 3437 stackIterator.remove(); 3438 fse.unlinkToDeath(); 3439 } 3440 } 3441 } 3442 } 3443 3444 /** 3445 * Helper function: 3446 * Called synchronized on mAudioFocusLock 3447 * Remove focus listeners from the focus stack for a particular client when it has died. 3448 */ 3449 private void removeFocusStackEntryForClient(IBinder cb) { 3450 // is the owner of the audio focus part of the client to remove? 3451 boolean isTopOfStackForClientToRemove = !mFocusStack.isEmpty() && 3452 mFocusStack.peek().mSourceRef.equals(cb); 3453 Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator(); 3454 while(stackIterator.hasNext()) { 3455 FocusStackEntry fse = (FocusStackEntry)stackIterator.next(); 3456 if(fse.mSourceRef.equals(cb)) { 3457 Log.i(TAG, " AudioFocus abandonAudioFocus(): removing entry for " 3458 + fse.mClientId); 3459 stackIterator.remove(); 3460 // the client just died, no need to unlink to its death 3461 } 3462 } 3463 if (isTopOfStackForClientToRemove) { 3464 // we removed an entry at the top of the stack: 3465 // notify the new top of the stack it gained focus. 3466 notifyTopOfAudioFocusStack(); 3467 // there's a new top of the stack, let the remote control know 3468 synchronized(mRCStack) { 3469 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 3470 } 3471 } 3472 } 3473 3474 /** 3475 * Helper function: 3476 * Returns true if the system is in a state where the focus can be reevaluated, false otherwise. 3477 */ 3478 private boolean canReassignAudioFocus() { 3479 // focus requests are rejected during a phone call or when the phone is ringing 3480 // this is equivalent to IN_VOICE_COMM_FOCUS_ID having the focus 3481 if (!mFocusStack.isEmpty() && IN_VOICE_COMM_FOCUS_ID.equals(mFocusStack.peek().mClientId)) { 3482 return false; 3483 } 3484 return true; 3485 } 3486 3487 /** 3488 * Inner class to monitor audio focus client deaths, and remove them from the audio focus 3489 * stack if necessary. 3490 */ 3491 private class AudioFocusDeathHandler implements IBinder.DeathRecipient { 3492 private IBinder mCb; // To be notified of client's death 3493 3494 AudioFocusDeathHandler(IBinder cb) { 3495 mCb = cb; 3496 } 3497 3498 public void binderDied() { 3499 synchronized(mAudioFocusLock) { 3500 Log.w(TAG, " AudioFocus audio focus client died"); 3501 removeFocusStackEntryForClient(mCb); 3502 } 3503 } 3504 3505 public IBinder getBinder() { 3506 return mCb; 3507 } 3508 } 3509 3510 3511 /** @see AudioManager#requestAudioFocus(IAudioFocusDispatcher, int, int) */ 3512 public int requestAudioFocus(int mainStreamType, int focusChangeHint, IBinder cb, 3513 IAudioFocusDispatcher fd, String clientId, String callingPackageName) { 3514 Log.i(TAG, " AudioFocus requestAudioFocus() from " + clientId); 3515 // the main stream type for the audio focus request is currently not used. It may 3516 // potentially be used to handle multiple stream type-dependent audio focuses. 3517 3518 // we need a valid binder callback for clients 3519 if (!cb.pingBinder()) { 3520 Log.e(TAG, " AudioFocus DOA client for requestAudioFocus(), aborting."); 3521 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 3522 } 3523 3524 synchronized(mAudioFocusLock) { 3525 if (!canReassignAudioFocus()) { 3526 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 3527 } 3528 3529 // handle the potential premature death of the new holder of the focus 3530 // (premature death == death before abandoning focus) 3531 // Register for client death notification 3532 AudioFocusDeathHandler afdh = new AudioFocusDeathHandler(cb); 3533 try { 3534 cb.linkToDeath(afdh, 0); 3535 } catch (RemoteException e) { 3536 // client has already died! 3537 Log.w(TAG, "AudioFocus requestAudioFocus() could not link to "+cb+" binder death"); 3538 return AudioManager.AUDIOFOCUS_REQUEST_FAILED; 3539 } 3540 3541 if (!mFocusStack.empty() && mFocusStack.peek().mClientId.equals(clientId)) { 3542 // if focus is already owned by this client and the reason for acquiring the focus 3543 // hasn't changed, don't do anything 3544 if (mFocusStack.peek().mFocusChangeType == focusChangeHint) { 3545 // unlink death handler so it can be gc'ed. 3546 // linkToDeath() creates a JNI global reference preventing collection. 3547 cb.unlinkToDeath(afdh, 0); 3548 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 3549 } 3550 // the reason for the audio focus request has changed: remove the current top of 3551 // stack and respond as if we had a new focus owner 3552 FocusStackEntry fse = mFocusStack.pop(); 3553 fse.unlinkToDeath(); 3554 } 3555 3556 // notify current top of stack it is losing focus 3557 if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) { 3558 try { 3559 mFocusStack.peek().mFocusDispatcher.dispatchAudioFocusChange( 3560 -1 * focusChangeHint, // loss and gain codes are inverse of each other 3561 mFocusStack.peek().mClientId); 3562 } catch (RemoteException e) { 3563 Log.e(TAG, " Failure to signal loss of focus due to "+ e); 3564 e.printStackTrace(); 3565 } 3566 } 3567 3568 // focus requester might already be somewhere below in the stack, remove it 3569 removeFocusStackEntry(clientId, false /* signal */); 3570 3571 // push focus requester at the top of the audio focus stack 3572 mFocusStack.push(new FocusStackEntry(mainStreamType, focusChangeHint, fd, cb, 3573 clientId, afdh, callingPackageName, Binder.getCallingUid())); 3574 3575 // there's a new top of the stack, let the remote control know 3576 synchronized(mRCStack) { 3577 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 3578 } 3579 }//synchronized(mAudioFocusLock) 3580 3581 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 3582 } 3583 3584 /** @see AudioManager#abandonAudioFocus(IAudioFocusDispatcher) */ 3585 public int abandonAudioFocus(IAudioFocusDispatcher fl, String clientId) { 3586 Log.i(TAG, " AudioFocus abandonAudioFocus() from " + clientId); 3587 try { 3588 // this will take care of notifying the new focus owner if needed 3589 synchronized(mAudioFocusLock) { 3590 removeFocusStackEntry(clientId, true); 3591 } 3592 } catch (java.util.ConcurrentModificationException cme) { 3593 // Catching this exception here is temporary. It is here just to prevent 3594 // a crash seen when the "Silent" notification is played. This is believed to be fixed 3595 // but this try catch block is left just to be safe. 3596 Log.e(TAG, "FATAL EXCEPTION AudioFocus abandonAudioFocus() caused " + cme); 3597 cme.printStackTrace(); 3598 } 3599 3600 return AudioManager.AUDIOFOCUS_REQUEST_GRANTED; 3601 } 3602 3603 3604 public void unregisterAudioFocusClient(String clientId) { 3605 synchronized(mAudioFocusLock) { 3606 removeFocusStackEntry(clientId, false); 3607 } 3608 } 3609 3610 3611 //========================================================================================== 3612 // RemoteControl 3613 //========================================================================================== 3614 public void dispatchMediaKeyEvent(KeyEvent keyEvent) { 3615 filterMediaKeyEvent(keyEvent, false /*needWakeLock*/); 3616 } 3617 3618 public void dispatchMediaKeyEventUnderWakelock(KeyEvent keyEvent) { 3619 filterMediaKeyEvent(keyEvent, true /*needWakeLock*/); 3620 } 3621 3622 private void filterMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { 3623 // sanity check on the incoming key event 3624 if (!isValidMediaKeyEvent(keyEvent)) { 3625 Log.e(TAG, "not dispatching invalid media key event " + keyEvent); 3626 return; 3627 } 3628 // event filtering for telephony 3629 synchronized(mRingingLock) { 3630 synchronized(mRCStack) { 3631 if ((mMediaReceiverForCalls != null) && 3632 (mIsRinging || (getMode() == AudioSystem.MODE_IN_CALL))) { 3633 dispatchMediaKeyEventForCalls(keyEvent, needWakeLock); 3634 return; 3635 } 3636 } 3637 } 3638 // event filtering based on voice-based interactions 3639 if (isValidVoiceInputKeyCode(keyEvent.getKeyCode())) { 3640 filterVoiceInputKeyEvent(keyEvent, needWakeLock); 3641 } else { 3642 dispatchMediaKeyEvent(keyEvent, needWakeLock); 3643 } 3644 } 3645 3646 /** 3647 * Handles the dispatching of the media button events to the telephony package. 3648 * Precondition: mMediaReceiverForCalls != null 3649 * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons 3650 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event 3651 * is dispatched. 3652 */ 3653 private void dispatchMediaKeyEventForCalls(KeyEvent keyEvent, boolean needWakeLock) { 3654 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); 3655 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 3656 keyIntent.setPackage(mMediaReceiverForCalls.getPackageName()); 3657 if (needWakeLock) { 3658 mMediaEventWakeLock.acquire(); 3659 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED); 3660 } 3661 mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone, 3662 mAudioHandler, Activity.RESULT_OK, null, null); 3663 } 3664 3665 /** 3666 * Handles the dispatching of the media button events to one of the registered listeners, 3667 * or if there was none, broadcast an ACTION_MEDIA_BUTTON intent to the rest of the system. 3668 * @param keyEvent a non-null KeyEvent whose key code is one of the supported media buttons 3669 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event 3670 * is dispatched. 3671 */ 3672 private void dispatchMediaKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { 3673 if (needWakeLock) { 3674 mMediaEventWakeLock.acquire(); 3675 } 3676 Intent keyIntent = new Intent(Intent.ACTION_MEDIA_BUTTON, null); 3677 keyIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent); 3678 synchronized(mRCStack) { 3679 if (!mRCStack.empty()) { 3680 // send the intent that was registered by the client 3681 try { 3682 mRCStack.peek().mMediaIntent.send(mContext, 3683 needWakeLock ? WAKELOCK_RELEASE_ON_FINISHED : 0 /*code*/, 3684 keyIntent, AudioService.this, mAudioHandler); 3685 } catch (CanceledException e) { 3686 Log.e(TAG, "Error sending pending intent " + mRCStack.peek()); 3687 e.printStackTrace(); 3688 } 3689 } else { 3690 // legacy behavior when nobody registered their media button event receiver 3691 // through AudioManager 3692 if (needWakeLock) { 3693 keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED); 3694 } 3695 mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone, 3696 mAudioHandler, Activity.RESULT_OK, null, null); 3697 } 3698 } 3699 } 3700 3701 /** 3702 * The different actions performed in response to a voice button key event. 3703 */ 3704 private final static int VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS = 1; 3705 private final static int VOICEBUTTON_ACTION_START_VOICE_INPUT = 2; 3706 private final static int VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS = 3; 3707 3708 private final Object mVoiceEventLock = new Object(); 3709 private boolean mVoiceButtonDown; 3710 private boolean mVoiceButtonHandled; 3711 3712 /** 3713 * Filter key events that may be used for voice-based interactions 3714 * @param keyEvent a non-null KeyEvent whose key code is that of one of the supported 3715 * media buttons that can be used to trigger voice-based interactions. 3716 * @param needWakeLock true if a PARTIAL_WAKE_LOCK needs to be held while this key event 3717 * is dispatched. 3718 */ 3719 private void filterVoiceInputKeyEvent(KeyEvent keyEvent, boolean needWakeLock) { 3720 if (DEBUG_RC) { 3721 Log.v(TAG, "voice input key event: " + keyEvent + ", needWakeLock=" + needWakeLock); 3722 } 3723 3724 int voiceButtonAction = VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS; 3725 int keyAction = keyEvent.getAction(); 3726 synchronized (mVoiceEventLock) { 3727 if (keyAction == KeyEvent.ACTION_DOWN) { 3728 if (keyEvent.getRepeatCount() == 0) { 3729 // initial down 3730 mVoiceButtonDown = true; 3731 mVoiceButtonHandled = false; 3732 } else if (mVoiceButtonDown && !mVoiceButtonHandled 3733 && (keyEvent.getFlags() & KeyEvent.FLAG_LONG_PRESS) != 0) { 3734 // long-press, start voice-based interactions 3735 mVoiceButtonHandled = true; 3736 voiceButtonAction = VOICEBUTTON_ACTION_START_VOICE_INPUT; 3737 } 3738 } else if (keyAction == KeyEvent.ACTION_UP) { 3739 if (mVoiceButtonDown) { 3740 // voice button up 3741 mVoiceButtonDown = false; 3742 if (!mVoiceButtonHandled && !keyEvent.isCanceled()) { 3743 voiceButtonAction = VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS; 3744 } 3745 } 3746 } 3747 }//synchronized (mVoiceEventLock) 3748 3749 // take action after media button event filtering for voice-based interactions 3750 switch (voiceButtonAction) { 3751 case VOICEBUTTON_ACTION_DISCARD_CURRENT_KEY_PRESS: 3752 if (DEBUG_RC) Log.v(TAG, " ignore key event"); 3753 break; 3754 case VOICEBUTTON_ACTION_START_VOICE_INPUT: 3755 if (DEBUG_RC) Log.v(TAG, " start voice-based interactions"); 3756 // then start the voice-based interactions 3757 startVoiceBasedInteractions(needWakeLock); 3758 break; 3759 case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS: 3760 if (DEBUG_RC) Log.v(TAG, " send simulated key event"); 3761 sendSimulatedMediaButtonEvent(keyEvent, needWakeLock); 3762 break; 3763 } 3764 } 3765 3766 private void sendSimulatedMediaButtonEvent(KeyEvent originalKeyEvent, boolean needWakeLock) { 3767 // send DOWN event 3768 KeyEvent keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_DOWN); 3769 dispatchMediaKeyEvent(keyEvent, needWakeLock); 3770 // send UP event 3771 keyEvent = KeyEvent.changeAction(originalKeyEvent, KeyEvent.ACTION_UP); 3772 dispatchMediaKeyEvent(keyEvent, needWakeLock); 3773 3774 } 3775 3776 3777 private static boolean isValidMediaKeyEvent(KeyEvent keyEvent) { 3778 if (keyEvent == null) { 3779 return false; 3780 } 3781 final int keyCode = keyEvent.getKeyCode(); 3782 switch (keyCode) { 3783 case KeyEvent.KEYCODE_MUTE: 3784 case KeyEvent.KEYCODE_HEADSETHOOK: 3785 case KeyEvent.KEYCODE_MEDIA_PLAY: 3786 case KeyEvent.KEYCODE_MEDIA_PAUSE: 3787 case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: 3788 case KeyEvent.KEYCODE_MEDIA_STOP: 3789 case KeyEvent.KEYCODE_MEDIA_NEXT: 3790 case KeyEvent.KEYCODE_MEDIA_PREVIOUS: 3791 case KeyEvent.KEYCODE_MEDIA_REWIND: 3792 case KeyEvent.KEYCODE_MEDIA_RECORD: 3793 case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD: 3794 case KeyEvent.KEYCODE_MEDIA_CLOSE: 3795 case KeyEvent.KEYCODE_MEDIA_EJECT: 3796 break; 3797 default: 3798 return false; 3799 } 3800 return true; 3801 } 3802 3803 /** 3804 * Checks whether the given key code is one that can trigger the launch of voice-based 3805 * interactions. 3806 * @param keyCode the key code associated with the key event 3807 * @return true if the key is one of the supported voice-based interaction triggers 3808 */ 3809 private static boolean isValidVoiceInputKeyCode(int keyCode) { 3810 if (keyCode == KeyEvent.KEYCODE_HEADSETHOOK) { 3811 return true; 3812 } else { 3813 return false; 3814 } 3815 } 3816 3817 /** 3818 * Tell the system to start voice-based interactions / voice commands 3819 */ 3820 private void startVoiceBasedInteractions(boolean needWakeLock) { 3821 Intent voiceIntent = new Intent(android.speech.RecognizerIntent.ACTION_WEB_SEARCH); 3822 if (needWakeLock) { 3823 mMediaEventWakeLock.acquire(); 3824 } 3825 voiceIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK 3826 | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS); 3827 try { 3828 if (mKeyguardManager != null) { 3829 // it's ok to start voice-based interactions when: 3830 // - the device is locked but doesn't require a password to be unlocked 3831 // - the device is not locked 3832 if ((mKeyguardManager.isKeyguardLocked() && !mKeyguardManager.isKeyguardSecure()) 3833 || !mKeyguardManager.isKeyguardLocked()) { 3834 mContext.startActivity(voiceIntent); 3835 } 3836 } 3837 } catch (ActivityNotFoundException e) { 3838 Log.e(TAG, "Error launching activity for ACTION_WEB_SEARCH: " + e); 3839 } finally { 3840 if (needWakeLock) { 3841 mMediaEventWakeLock.release(); 3842 } 3843 } 3844 } 3845 3846 /** 3847 * Verify whether it is safe to start voice-based interactions given the state of the system 3848 * @return false is the Keyguard is locked and secure, true otherwise 3849 */ 3850 private boolean safeToStartVoiceBasedInteractions() { 3851 KeyguardManager keyguard = 3852 (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE); 3853 if (keyguard == null) { 3854 return false; 3855 } 3856 3857 return true; 3858 } 3859 3860 private PowerManager.WakeLock mMediaEventWakeLock; 3861 3862 private static final int WAKELOCK_RELEASE_ON_FINISHED = 1980; //magic number 3863 3864 // only set when wakelock was acquired, no need to check value when received 3865 private static final String EXTRA_WAKELOCK_ACQUIRED = 3866 "android.media.AudioService.WAKELOCK_ACQUIRED"; 3867 3868 public void onSendFinished(PendingIntent pendingIntent, Intent intent, 3869 int resultCode, String resultData, Bundle resultExtras) { 3870 if (resultCode == WAKELOCK_RELEASE_ON_FINISHED) { 3871 mMediaEventWakeLock.release(); 3872 } 3873 } 3874 3875 BroadcastReceiver mKeyEventDone = new BroadcastReceiver() { 3876 public void onReceive(Context context, Intent intent) { 3877 if (intent == null) { 3878 return; 3879 } 3880 Bundle extras = intent.getExtras(); 3881 if (extras == null) { 3882 return; 3883 } 3884 if (extras.containsKey(EXTRA_WAKELOCK_ACQUIRED)) { 3885 mMediaEventWakeLock.release(); 3886 } 3887 } 3888 }; 3889 3890 private final Object mCurrentRcLock = new Object(); 3891 /** 3892 * The one remote control client which will receive a request for display information. 3893 * This object may be null. 3894 * Access protected by mCurrentRcLock. 3895 */ 3896 private IRemoteControlClient mCurrentRcClient = null; 3897 3898 private final static int RC_INFO_NONE = 0; 3899 private final static int RC_INFO_ALL = 3900 RemoteControlClient.FLAG_INFORMATION_REQUEST_ALBUM_ART | 3901 RemoteControlClient.FLAG_INFORMATION_REQUEST_KEY_MEDIA | 3902 RemoteControlClient.FLAG_INFORMATION_REQUEST_METADATA | 3903 RemoteControlClient.FLAG_INFORMATION_REQUEST_PLAYSTATE; 3904 3905 /** 3906 * A monotonically increasing generation counter for mCurrentRcClient. 3907 * Only accessed with a lock on mCurrentRcLock. 3908 * No value wrap-around issues as we only act on equal values. 3909 */ 3910 private int mCurrentRcClientGen = 0; 3911 3912 /** 3913 * Inner class to monitor remote control client deaths, and remove the client for the 3914 * remote control stack if necessary. 3915 */ 3916 private class RcClientDeathHandler implements IBinder.DeathRecipient { 3917 private IBinder mCb; // To be notified of client's death 3918 private PendingIntent mMediaIntent; 3919 3920 RcClientDeathHandler(IBinder cb, PendingIntent pi) { 3921 mCb = cb; 3922 mMediaIntent = pi; 3923 } 3924 3925 public void binderDied() { 3926 Log.w(TAG, " RemoteControlClient died"); 3927 // remote control client died, make sure the displays don't use it anymore 3928 // by setting its remote control client to null 3929 registerRemoteControlClient(mMediaIntent, null/*rcClient*/, null/*ignored*/); 3930 } 3931 3932 public IBinder getBinder() { 3933 return mCb; 3934 } 3935 } 3936 3937 private static class RemoteControlStackEntry { 3938 /** 3939 * The target for the ACTION_MEDIA_BUTTON events. 3940 * Always non null. 3941 */ 3942 public PendingIntent mMediaIntent; 3943 /** 3944 * The registered media button event receiver. 3945 * Always non null. 3946 */ 3947 public ComponentName mReceiverComponent; 3948 public String mCallingPackageName; 3949 public int mCallingUid; 3950 /** 3951 * Provides access to the information to display on the remote control. 3952 * May be null (when a media button event receiver is registered, 3953 * but no remote control client has been registered) */ 3954 public IRemoteControlClient mRcClient; 3955 public RcClientDeathHandler mRcClientDeathHandler; 3956 3957 /** precondition: mediaIntent != null, eventReceiver != null */ 3958 public RemoteControlStackEntry(PendingIntent mediaIntent, ComponentName eventReceiver) { 3959 mMediaIntent = mediaIntent; 3960 mReceiverComponent = eventReceiver; 3961 mCallingUid = -1; 3962 mRcClient = null; 3963 } 3964 3965 public void unlinkToRcClientDeath() { 3966 if ((mRcClientDeathHandler != null) && (mRcClientDeathHandler.mCb != null)) { 3967 try { 3968 mRcClientDeathHandler.mCb.unlinkToDeath(mRcClientDeathHandler, 0); 3969 mRcClientDeathHandler = null; 3970 } catch (java.util.NoSuchElementException e) { 3971 // not much we can do here 3972 Log.e(TAG, "Encountered " + e + " in unlinkToRcClientDeath()"); 3973 e.printStackTrace(); 3974 } 3975 } 3976 } 3977 3978 @Override 3979 protected void finalize() throws Throwable { 3980 unlinkToRcClientDeath();// unlink exception handled inside method 3981 super.finalize(); 3982 } 3983 } 3984 3985 /** 3986 * The stack of remote control event receivers. 3987 * Code sections and methods that modify the remote control event receiver stack are 3988 * synchronized on mRCStack, but also BEFORE on mFocusLock as any change in either 3989 * stack, audio focus or RC, can lead to a change in the remote control display 3990 */ 3991 private final Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>(); 3992 3993 /** 3994 * The component the telephony package can register so telephony calls have priority to 3995 * handle media button events 3996 */ 3997 private ComponentName mMediaReceiverForCalls = null; 3998 3999 /** 4000 * Helper function: 4001 * Display in the log the current entries in the remote control focus stack 4002 */ 4003 private void dumpRCStack(PrintWriter pw) { 4004 pw.println("\nRemote Control stack entries:"); 4005 synchronized(mRCStack) { 4006 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 4007 while(stackIterator.hasNext()) { 4008 RemoteControlStackEntry rcse = stackIterator.next(); 4009 pw.println(" pi: " + rcse.mMediaIntent + 4010 " -- ercvr: " + rcse.mReceiverComponent + 4011 " -- client: " + rcse.mRcClient + 4012 " -- uid: " + rcse.mCallingUid); 4013 } 4014 } 4015 } 4016 4017 /** 4018 * Helper function: 4019 * Remove any entry in the remote control stack that has the same package name as packageName 4020 * Pre-condition: packageName != null 4021 */ 4022 private void removeMediaButtonReceiverForPackage(String packageName) { 4023 synchronized(mRCStack) { 4024 if (mRCStack.empty()) { 4025 return; 4026 } else { 4027 RemoteControlStackEntry oldTop = mRCStack.peek(); 4028 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 4029 // iterate over the stack entries 4030 while(stackIterator.hasNext()) { 4031 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 4032 if (packageName.equalsIgnoreCase(rcse.mReceiverComponent.getPackageName())) { 4033 // a stack entry is from the package being removed, remove it from the stack 4034 stackIterator.remove(); 4035 rcse.unlinkToRcClientDeath(); 4036 } 4037 } 4038 if (mRCStack.empty()) { 4039 // no saved media button receiver 4040 mAudioHandler.sendMessage( 4041 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, 4042 null)); 4043 } else if (oldTop != mRCStack.peek()) { 4044 // the top of the stack has changed, save it in the system settings 4045 // by posting a message to persist it 4046 mAudioHandler.sendMessage( 4047 mAudioHandler.obtainMessage(MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, 4048 mRCStack.peek().mReceiverComponent)); 4049 } 4050 } 4051 } 4052 } 4053 4054 /** 4055 * Helper function: 4056 * Restore remote control receiver from the system settings. 4057 */ 4058 private void restoreMediaButtonReceiver() { 4059 String receiverName = Settings.System.getString(mContentResolver, 4060 Settings.System.MEDIA_BUTTON_RECEIVER); 4061 if ((null != receiverName) && !receiverName.isEmpty()) { 4062 ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName); 4063 // construct a PendingIntent targeted to the restored component name 4064 // for the media button and register it 4065 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON); 4066 // the associated intent will be handled by the component being registered 4067 mediaButtonIntent.setComponent(eventReceiver); 4068 PendingIntent pi = PendingIntent.getBroadcast(mContext, 4069 0/*requestCode, ignored*/, mediaButtonIntent, 0/*flags*/); 4070 registerMediaButtonIntent(pi, eventReceiver); 4071 } 4072 } 4073 4074 /** 4075 * Helper function: 4076 * Set the new remote control receiver at the top of the RC focus stack. 4077 * precondition: mediaIntent != null, target != null 4078 */ 4079 private void pushMediaButtonReceiver(PendingIntent mediaIntent, ComponentName target) { 4080 // already at top of stack? 4081 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(mediaIntent)) { 4082 return; 4083 } 4084 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 4085 RemoteControlStackEntry rcse = null; 4086 boolean wasInsideStack = false; 4087 while(stackIterator.hasNext()) { 4088 rcse = (RemoteControlStackEntry)stackIterator.next(); 4089 if(rcse.mMediaIntent.equals(mediaIntent)) { 4090 wasInsideStack = true; 4091 stackIterator.remove(); 4092 break; 4093 } 4094 } 4095 if (!wasInsideStack) { 4096 rcse = new RemoteControlStackEntry(mediaIntent, target); 4097 } 4098 mRCStack.push(rcse); 4099 4100 // post message to persist the default media button receiver 4101 mAudioHandler.sendMessage( mAudioHandler.obtainMessage( 4102 MSG_PERSIST_MEDIABUTTONRECEIVER, 0, 0, target/*obj*/) ); 4103 } 4104 4105 /** 4106 * Helper function: 4107 * Remove the remote control receiver from the RC focus stack. 4108 * precondition: pi != null 4109 */ 4110 private void removeMediaButtonReceiver(PendingIntent pi) { 4111 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 4112 while(stackIterator.hasNext()) { 4113 RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next(); 4114 if(rcse.mMediaIntent.equals(pi)) { 4115 stackIterator.remove(); 4116 rcse.unlinkToRcClientDeath(); 4117 break; 4118 } 4119 } 4120 } 4121 4122 /** 4123 * Helper function: 4124 * Called synchronized on mRCStack 4125 */ 4126 private boolean isCurrentRcController(PendingIntent pi) { 4127 if (!mRCStack.empty() && mRCStack.peek().mMediaIntent.equals(pi)) { 4128 return true; 4129 } 4130 return false; 4131 } 4132 4133 //========================================================================================== 4134 // Remote control display / client 4135 //========================================================================================== 4136 /** 4137 * Update the remote control displays with the new "focused" client generation 4138 */ 4139 private void setNewRcClientOnDisplays_syncRcsCurrc(int newClientGeneration, 4140 PendingIntent newMediaIntent, boolean clearing) { 4141 // NOTE: Only one IRemoteControlDisplay supported in this implementation 4142 if (mRcDisplay != null) { 4143 try { 4144 mRcDisplay.setCurrentClientId( 4145 newClientGeneration, newMediaIntent, clearing); 4146 } catch (RemoteException e) { 4147 Log.e(TAG, "Dead display in setNewRcClientOnDisplays_syncRcsCurrc() "+e); 4148 // if we had a display before, stop monitoring its death 4149 rcDisplay_stopDeathMonitor_syncRcStack(); 4150 mRcDisplay = null; 4151 } 4152 } 4153 } 4154 4155 /** 4156 * Update the remote control clients with the new "focused" client generation 4157 */ 4158 private void setNewRcClientGenerationOnClients_syncRcsCurrc(int newClientGeneration) { 4159 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 4160 while(stackIterator.hasNext()) { 4161 RemoteControlStackEntry se = stackIterator.next(); 4162 if ((se != null) && (se.mRcClient != null)) { 4163 try { 4164 se.mRcClient.setCurrentClientGenerationId(newClientGeneration); 4165 } catch (RemoteException e) { 4166 Log.w(TAG, "Dead client in setNewRcClientGenerationOnClients_syncRcsCurrc()"+e); 4167 stackIterator.remove(); 4168 se.unlinkToRcClientDeath(); 4169 } 4170 } 4171 } 4172 } 4173 4174 /** 4175 * Update the displays and clients with the new "focused" client generation and name 4176 * @param newClientGeneration the new generation value matching a client update 4177 * @param newClientEventReceiver the media button event receiver associated with the client. 4178 * May be null, which implies there is no registered media button event receiver. 4179 * @param clearing true if the new client generation value maps to a remote control update 4180 * where the display should be cleared. 4181 */ 4182 private void setNewRcClient_syncRcsCurrc(int newClientGeneration, 4183 PendingIntent newMediaIntent, boolean clearing) { 4184 // send the new valid client generation ID to all displays 4185 setNewRcClientOnDisplays_syncRcsCurrc(newClientGeneration, newMediaIntent, clearing); 4186 // send the new valid client generation ID to all clients 4187 setNewRcClientGenerationOnClients_syncRcsCurrc(newClientGeneration); 4188 } 4189 4190 /** 4191 * Called when processing MSG_RCDISPLAY_CLEAR event 4192 */ 4193 private void onRcDisplayClear() { 4194 if (DEBUG_RC) Log.i(TAG, "Clear remote control display"); 4195 4196 synchronized(mRCStack) { 4197 synchronized(mCurrentRcLock) { 4198 mCurrentRcClientGen++; 4199 // synchronously update the displays and clients with the new client generation 4200 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen, 4201 null /*newMediaIntent*/, true /*clearing*/); 4202 } 4203 } 4204 } 4205 4206 /** 4207 * Called when processing MSG_RCDISPLAY_UPDATE event 4208 */ 4209 private void onRcDisplayUpdate(RemoteControlStackEntry rcse, int flags /* USED ?*/) { 4210 synchronized(mRCStack) { 4211 synchronized(mCurrentRcLock) { 4212 if ((mCurrentRcClient != null) && (mCurrentRcClient.equals(rcse.mRcClient))) { 4213 if (DEBUG_RC) Log.i(TAG, "Display/update remote control "); 4214 4215 mCurrentRcClientGen++; 4216 // synchronously update the displays and clients with 4217 // the new client generation 4218 setNewRcClient_syncRcsCurrc(mCurrentRcClientGen, 4219 rcse.mMediaIntent /*newMediaIntent*/, 4220 false /*clearing*/); 4221 4222 // tell the current client that it needs to send info 4223 try { 4224 mCurrentRcClient.onInformationRequested(mCurrentRcClientGen, 4225 flags, mArtworkExpectedWidth, mArtworkExpectedHeight); 4226 } catch (RemoteException e) { 4227 Log.e(TAG, "Current valid remote client is dead: "+e); 4228 mCurrentRcClient = null; 4229 } 4230 } else { 4231 // the remote control display owner has changed between the 4232 // the message to update the display was sent, and the time it 4233 // gets to be processed (now) 4234 } 4235 } 4236 } 4237 } 4238 4239 4240 /** 4241 * Helper function: 4242 * Called synchronized on mRCStack 4243 */ 4244 private void clearRemoteControlDisplay_syncAfRcs() { 4245 synchronized(mCurrentRcLock) { 4246 mCurrentRcClient = null; 4247 } 4248 // will cause onRcDisplayClear() to be called in AudioService's handler thread 4249 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_CLEAR) ); 4250 } 4251 4252 /** 4253 * Helper function for code readability: only to be called from 4254 * checkUpdateRemoteControlDisplay_syncAfRcs() which checks the preconditions for 4255 * this method. 4256 * Preconditions: 4257 * - called synchronized mAudioFocusLock then on mRCStack 4258 * - mRCStack.isEmpty() is false 4259 */ 4260 private void updateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) { 4261 RemoteControlStackEntry rcse = mRCStack.peek(); 4262 int infoFlagsAboutToBeUsed = infoChangedFlags; 4263 // this is where we enforce opt-in for information display on the remote controls 4264 // with the new AudioManager.registerRemoteControlClient() API 4265 if (rcse.mRcClient == null) { 4266 //Log.w(TAG, "Can't update remote control display with null remote control client"); 4267 clearRemoteControlDisplay_syncAfRcs(); 4268 return; 4269 } 4270 synchronized(mCurrentRcLock) { 4271 if (!rcse.mRcClient.equals(mCurrentRcClient)) { 4272 // new RC client, assume every type of information shall be queried 4273 infoFlagsAboutToBeUsed = RC_INFO_ALL; 4274 } 4275 mCurrentRcClient = rcse.mRcClient; 4276 } 4277 // will cause onRcDisplayUpdate() to be called in AudioService's handler thread 4278 mAudioHandler.sendMessage( mAudioHandler.obtainMessage(MSG_RCDISPLAY_UPDATE, 4279 infoFlagsAboutToBeUsed /* arg1 */, 0, rcse /* obj, != null */) ); 4280 } 4281 4282 /** 4283 * Helper function: 4284 * Called synchronized on mAudioFocusLock, then mRCStack 4285 * Check whether the remote control display should be updated, triggers the update if required 4286 * @param infoChangedFlags the flags corresponding to the remote control client information 4287 * that has changed, if applicable (checking for the update conditions might trigger a 4288 * clear, rather than an update event). 4289 */ 4290 private void checkUpdateRemoteControlDisplay_syncAfRcs(int infoChangedFlags) { 4291 // determine whether the remote control display should be refreshed 4292 // if either stack is empty, there is a mismatch, so clear the RC display 4293 if (mRCStack.isEmpty() || mFocusStack.isEmpty()) { 4294 clearRemoteControlDisplay_syncAfRcs(); 4295 return; 4296 } 4297 // if the top of the two stacks belong to different packages, there is a mismatch, clear 4298 if ((mRCStack.peek().mCallingPackageName != null) 4299 && (mFocusStack.peek().mPackageName != null) 4300 && !(mRCStack.peek().mCallingPackageName.compareTo( 4301 mFocusStack.peek().mPackageName) == 0)) { 4302 clearRemoteControlDisplay_syncAfRcs(); 4303 return; 4304 } 4305 // if the audio focus didn't originate from the same Uid as the one in which the remote 4306 // control information will be retrieved, clear 4307 if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) { 4308 clearRemoteControlDisplay_syncAfRcs(); 4309 return; 4310 } 4311 // refresh conditions were verified: update the remote controls 4312 // ok to call: synchronized mAudioFocusLock then on mRCStack, mRCStack is not empty 4313 updateRemoteControlDisplay_syncAfRcs(infoChangedFlags); 4314 } 4315 4316 /** 4317 * see AudioManager.registerMediaButtonIntent(PendingIntent pi, ComponentName c) 4318 * precondition: mediaIntent != null, target != null 4319 */ 4320 public void registerMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) { 4321 Log.i(TAG, " Remote Control registerMediaButtonIntent() for " + mediaIntent); 4322 4323 synchronized(mAudioFocusLock) { 4324 synchronized(mRCStack) { 4325 pushMediaButtonReceiver(mediaIntent, eventReceiver); 4326 // new RC client, assume every type of information shall be queried 4327 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 4328 } 4329 } 4330 } 4331 4332 /** 4333 * see AudioManager.unregisterMediaButtonIntent(PendingIntent mediaIntent) 4334 * precondition: mediaIntent != null, eventReceiver != null 4335 */ 4336 public void unregisterMediaButtonIntent(PendingIntent mediaIntent, ComponentName eventReceiver) 4337 { 4338 Log.i(TAG, " Remote Control unregisterMediaButtonIntent() for " + mediaIntent); 4339 4340 synchronized(mAudioFocusLock) { 4341 synchronized(mRCStack) { 4342 boolean topOfStackWillChange = isCurrentRcController(mediaIntent); 4343 removeMediaButtonReceiver(mediaIntent); 4344 if (topOfStackWillChange) { 4345 // current RC client will change, assume every type of info needs to be queried 4346 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 4347 } 4348 } 4349 } 4350 } 4351 4352 /** 4353 * see AudioManager.registerMediaButtonEventReceiverForCalls(ComponentName c) 4354 * precondition: c != null 4355 */ 4356 public void registerMediaButtonEventReceiverForCalls(ComponentName c) { 4357 if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE") 4358 != PackageManager.PERMISSION_GRANTED) { 4359 Log.e(TAG, "Invalid permissions to register media button receiver for calls"); 4360 return; 4361 } 4362 synchronized(mRCStack) { 4363 mMediaReceiverForCalls = c; 4364 } 4365 } 4366 4367 /** 4368 * see AudioManager.unregisterMediaButtonEventReceiverForCalls() 4369 */ 4370 public void unregisterMediaButtonEventReceiverForCalls() { 4371 if (mContext.checkCallingPermission("android.permission.MODIFY_PHONE_STATE") 4372 != PackageManager.PERMISSION_GRANTED) { 4373 Log.e(TAG, "Invalid permissions to unregister media button receiver for calls"); 4374 return; 4375 } 4376 synchronized(mRCStack) { 4377 mMediaReceiverForCalls = null; 4378 } 4379 } 4380 4381 /** 4382 * see AudioManager.registerRemoteControlClient(ComponentName eventReceiver, ...) 4383 * Note: using this method with rcClient == null is a way to "disable" the IRemoteControlClient 4384 * without modifying the RC stack, but while still causing the display to refresh (will 4385 * become blank as a result of this) 4386 */ 4387 public void registerRemoteControlClient(PendingIntent mediaIntent, 4388 IRemoteControlClient rcClient, String callingPackageName) { 4389 if (DEBUG_RC) Log.i(TAG, "Register remote control client rcClient="+rcClient); 4390 synchronized(mAudioFocusLock) { 4391 synchronized(mRCStack) { 4392 // store the new display information 4393 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 4394 while(stackIterator.hasNext()) { 4395 RemoteControlStackEntry rcse = stackIterator.next(); 4396 if(rcse.mMediaIntent.equals(mediaIntent)) { 4397 // already had a remote control client? 4398 if (rcse.mRcClientDeathHandler != null) { 4399 // stop monitoring the old client's death 4400 rcse.unlinkToRcClientDeath(); 4401 } 4402 // save the new remote control client 4403 rcse.mRcClient = rcClient; 4404 rcse.mCallingPackageName = callingPackageName; 4405 rcse.mCallingUid = Binder.getCallingUid(); 4406 if (rcClient == null) { 4407 // here rcse.mRcClientDeathHandler is null; 4408 break; 4409 } 4410 4411 // there is a new (non-null) client: 4412 // 1/ give the new client the current display (if any) 4413 if (mRcDisplay != null) { 4414 try { 4415 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay); 4416 } catch (RemoteException e) { 4417 Log.e(TAG, "Error connecting remote control display to client: "+e); 4418 e.printStackTrace(); 4419 } 4420 } 4421 // 2/ monitor the new client's death 4422 IBinder b = rcse.mRcClient.asBinder(); 4423 RcClientDeathHandler rcdh = 4424 new RcClientDeathHandler(b, rcse.mMediaIntent); 4425 try { 4426 b.linkToDeath(rcdh, 0); 4427 } catch (RemoteException e) { 4428 // remote control client is DOA, disqualify it 4429 Log.w(TAG, "registerRemoteControlClient() has a dead client " + b); 4430 rcse.mRcClient = null; 4431 } 4432 rcse.mRcClientDeathHandler = rcdh; 4433 break; 4434 } 4435 } 4436 // if the eventReceiver is at the top of the stack 4437 // then check for potential refresh of the remote controls 4438 if (isCurrentRcController(mediaIntent)) { 4439 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 4440 } 4441 } 4442 } 4443 } 4444 4445 /** 4446 * see AudioManager.unregisterRemoteControlClient(PendingIntent pi, ...) 4447 * rcClient is guaranteed non-null 4448 */ 4449 public void unregisterRemoteControlClient(PendingIntent mediaIntent, 4450 IRemoteControlClient rcClient) { 4451 synchronized(mAudioFocusLock) { 4452 synchronized(mRCStack) { 4453 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 4454 while(stackIterator.hasNext()) { 4455 RemoteControlStackEntry rcse = stackIterator.next(); 4456 if ((rcse.mMediaIntent.equals(mediaIntent)) 4457 && rcClient.equals(rcse.mRcClient)) { 4458 // we found the IRemoteControlClient to unregister 4459 // stop monitoring its death 4460 rcse.unlinkToRcClientDeath(); 4461 // reset the client-related fields 4462 rcse.mRcClient = null; 4463 rcse.mCallingPackageName = null; 4464 } 4465 } 4466 } 4467 } 4468 } 4469 4470 /** 4471 * The remote control displays. 4472 * Access synchronized on mRCStack 4473 * NOTE: Only one IRemoteControlDisplay supported in this implementation 4474 */ 4475 private IRemoteControlDisplay mRcDisplay; 4476 private RcDisplayDeathHandler mRcDisplayDeathHandler; 4477 private int mArtworkExpectedWidth = -1; 4478 private int mArtworkExpectedHeight = -1; 4479 /** 4480 * Inner class to monitor remote control display deaths, and unregister them from the list 4481 * of displays if necessary. 4482 */ 4483 private class RcDisplayDeathHandler implements IBinder.DeathRecipient { 4484 private IBinder mCb; // To be notified of client's death 4485 4486 public RcDisplayDeathHandler(IBinder b) { 4487 if (DEBUG_RC) Log.i(TAG, "new RcDisplayDeathHandler for "+b); 4488 mCb = b; 4489 } 4490 4491 public void binderDied() { 4492 synchronized(mRCStack) { 4493 Log.w(TAG, "RemoteControl: display died"); 4494 mRcDisplay = null; 4495 } 4496 } 4497 4498 public void unlinkToRcDisplayDeath() { 4499 if (DEBUG_RC) Log.i(TAG, "unlinkToRcDisplayDeath for "+mCb); 4500 try { 4501 mCb.unlinkToDeath(this, 0); 4502 } catch (java.util.NoSuchElementException e) { 4503 // not much we can do here, the display was being unregistered anyway 4504 Log.e(TAG, "Encountered " + e + " in unlinkToRcDisplayDeath()"); 4505 e.printStackTrace(); 4506 } 4507 } 4508 4509 } 4510 4511 private void rcDisplay_stopDeathMonitor_syncRcStack() { 4512 if (mRcDisplay != null) { // implies (mRcDisplayDeathHandler != null) 4513 // we had a display before, stop monitoring its death 4514 mRcDisplayDeathHandler.unlinkToRcDisplayDeath(); 4515 } 4516 } 4517 4518 private void rcDisplay_startDeathMonitor_syncRcStack() { 4519 if (mRcDisplay != null) { 4520 // new non-null display, monitor its death 4521 IBinder b = mRcDisplay.asBinder(); 4522 mRcDisplayDeathHandler = new RcDisplayDeathHandler(b); 4523 try { 4524 b.linkToDeath(mRcDisplayDeathHandler, 0); 4525 } catch (RemoteException e) { 4526 // remote control display is DOA, disqualify it 4527 Log.w(TAG, "registerRemoteControlDisplay() has a dead client " + b); 4528 mRcDisplay = null; 4529 } 4530 } 4531 } 4532 4533 /** 4534 * Register an IRemoteControlDisplay. 4535 * Notify all IRemoteControlClient of the new display and cause the RemoteControlClient 4536 * at the top of the stack to update the new display with its information. 4537 * Since only one IRemoteControlDisplay is supported, this will unregister the previous display. 4538 * @param rcd the IRemoteControlDisplay to register. No effect if null. 4539 */ 4540 public void registerRemoteControlDisplay(IRemoteControlDisplay rcd) { 4541 if (DEBUG_RC) Log.d(TAG, ">>> registerRemoteControlDisplay("+rcd+")"); 4542 synchronized(mAudioFocusLock) { 4543 synchronized(mRCStack) { 4544 if ((mRcDisplay == rcd) || (rcd == null)) { 4545 return; 4546 } 4547 // if we had a display before, stop monitoring its death 4548 rcDisplay_stopDeathMonitor_syncRcStack(); 4549 mRcDisplay = rcd; 4550 // new display, start monitoring its death 4551 rcDisplay_startDeathMonitor_syncRcStack(); 4552 4553 // let all the remote control clients there is a new display 4554 // no need to unplug the previous because we only support one display 4555 // and the clients don't track the death of the display 4556 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 4557 while(stackIterator.hasNext()) { 4558 RemoteControlStackEntry rcse = stackIterator.next(); 4559 if(rcse.mRcClient != null) { 4560 try { 4561 rcse.mRcClient.plugRemoteControlDisplay(mRcDisplay); 4562 } catch (RemoteException e) { 4563 Log.e(TAG, "Error connecting remote control display to client: " + e); 4564 e.printStackTrace(); 4565 } 4566 } 4567 } 4568 4569 // we have a new display, of which all the clients are now aware: have it be updated 4570 checkUpdateRemoteControlDisplay_syncAfRcs(RC_INFO_ALL); 4571 } 4572 } 4573 } 4574 4575 /** 4576 * Unregister an IRemoteControlDisplay. 4577 * Since only one IRemoteControlDisplay is supported, this has no effect if the one to 4578 * unregister is not the current one. 4579 * @param rcd the IRemoteControlDisplay to unregister. No effect if null. 4580 */ 4581 public void unregisterRemoteControlDisplay(IRemoteControlDisplay rcd) { 4582 if (DEBUG_RC) Log.d(TAG, "<<< unregisterRemoteControlDisplay("+rcd+")"); 4583 synchronized(mRCStack) { 4584 // only one display here, so you can only unregister the current display 4585 if ((rcd == null) || (rcd != mRcDisplay)) { 4586 if (DEBUG_RC) Log.w(TAG, " trying to unregister unregistered RCD"); 4587 return; 4588 } 4589 // if we had a display before, stop monitoring its death 4590 rcDisplay_stopDeathMonitor_syncRcStack(); 4591 mRcDisplay = null; 4592 4593 // disconnect this remote control display from all the clients 4594 Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator(); 4595 while(stackIterator.hasNext()) { 4596 RemoteControlStackEntry rcse = stackIterator.next(); 4597 if(rcse.mRcClient != null) { 4598 try { 4599 rcse.mRcClient.unplugRemoteControlDisplay(rcd); 4600 } catch (RemoteException e) { 4601 Log.e(TAG, "Error disconnecting remote control display to client: " + e); 4602 e.printStackTrace(); 4603 } 4604 } 4605 } 4606 } 4607 } 4608 4609 public void remoteControlDisplayUsesBitmapSize(IRemoteControlDisplay rcd, int w, int h) { 4610 synchronized(mRCStack) { 4611 // NOTE: Only one IRemoteControlDisplay supported in this implementation 4612 mArtworkExpectedWidth = w; 4613 mArtworkExpectedHeight = h; 4614 } 4615 } 4616 4617 //========================================================================================== 4618 // Device orientation 4619 //========================================================================================== 4620 /** 4621 * Handles device configuration changes that may map to a change in the orientation. 4622 * This feature is optional, and is defined by the definition and value of the 4623 * "ro.audio.monitorOrientation" system property. 4624 */ 4625 private void handleConfigurationChanged(Context context) { 4626 try { 4627 // reading new orientation "safely" (i.e. under try catch) in case anything 4628 // goes wrong when obtaining resources and configuration 4629 int newOrientation = context.getResources().getConfiguration().orientation; 4630 if (newOrientation != mDeviceOrientation) { 4631 mDeviceOrientation = newOrientation; 4632 setOrientationForAudioSystem(); 4633 } 4634 } catch (Exception e) { 4635 Log.e(TAG, "Error retrieving device orientation: " + e); 4636 } 4637 } 4638 4639 private void setOrientationForAudioSystem() { 4640 switch (mDeviceOrientation) { 4641 case Configuration.ORIENTATION_LANDSCAPE: 4642 //Log.i(TAG, "orientation is landscape"); 4643 AudioSystem.setParameters("orientation=landscape"); 4644 break; 4645 case Configuration.ORIENTATION_PORTRAIT: 4646 //Log.i(TAG, "orientation is portrait"); 4647 AudioSystem.setParameters("orientation=portrait"); 4648 break; 4649 case Configuration.ORIENTATION_SQUARE: 4650 //Log.i(TAG, "orientation is square"); 4651 AudioSystem.setParameters("orientation=square"); 4652 break; 4653 case Configuration.ORIENTATION_UNDEFINED: 4654 //Log.i(TAG, "orientation is undefined"); 4655 AudioSystem.setParameters("orientation=undefined"); 4656 break; 4657 default: 4658 Log.e(TAG, "Unknown orientation"); 4659 } 4660 } 4661 4662 4663 @Override 4664 public void setRingtonePlayer(IRingtonePlayer player) { 4665 mContext.enforceCallingOrSelfPermission(REMOTE_AUDIO_PLAYBACK, null); 4666 mRingtonePlayer = player; 4667 } 4668 4669 @Override 4670 public IRingtonePlayer getRingtonePlayer() { 4671 return mRingtonePlayer; 4672 } 4673 4674 @Override 4675 protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 4676 mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG); 4677 4678 // TODO probably a lot more to do here than just the audio focus and remote control stacks 4679 dumpFocusStack(pw); 4680 dumpRCStack(pw); 4681 dumpStreamStates(pw); 4682 } 4683} 4684