summaryrefslogtreecommitdiffstats
path: root/media/java
diff options
context:
space:
mode:
Diffstat (limited to 'media/java')
-rw-r--r--media/java/android/media/AudioManager.java79
-rw-r--r--media/java/android/media/AudioService.java671
-rw-r--r--media/java/android/media/AudioSystem.java64
-rw-r--r--media/java/android/media/DataSource.java43
-rw-r--r--media/java/android/media/IAudioService.aidl2
-rw-r--r--media/java/android/media/MediaExtractor.java9
-rw-r--r--media/java/android/media/MediaFile.java2
-rw-r--r--media/java/android/media/MediaMetadataRetriever.java5
-rw-r--r--media/java/android/media/MediaPlayer.java26
-rw-r--r--media/java/android/media/MediaRecorder.java21
-rw-r--r--media/java/android/media/MediaScanner.java64
-rw-r--r--media/java/android/media/RemoteDisplay.java153
-rwxr-xr-xmedia/java/android/mtp/MtpDatabase.java29
13 files changed, 955 insertions, 213 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index b6e4659..ee17bd3 100644
--- a/media/java/android/media/AudioManager.java
+++ b/media/java/android/media/AudioManager.java
@@ -49,6 +49,7 @@ public class AudioManager {
private final Context mContext;
private long mVolumeKeyUpTime;
private final boolean mUseMasterVolume;
+ private final boolean mUseVolumeKeySounds;
private static String TAG = "AudioManager";
/**
@@ -313,6 +314,13 @@ public class AudioManager {
public static final int FLAG_VIBRATE = 1 << 4;
/**
+ * Indicates to VolumePanel that the volume slider should be disabled as user
+ * cannot change the stream volume
+ * @hide
+ */
+ public static final int FLAG_FIXED_VOLUME = 1 << 5;
+
+ /**
* Ringer mode that will be silent and will not vibrate. (This overrides the
* vibrate setting.)
*
@@ -412,6 +420,8 @@ public class AudioManager {
mContext = context;
mUseMasterVolume = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_useMasterVolume);
+ mUseVolumeKeySounds = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_useVolumeKeySounds);
}
private static IAudioService getService()
@@ -463,6 +473,7 @@ public class AudioManager {
* responsive to the user.
*/
int flags = FLAG_SHOW_UI | FLAG_VIBRATE;
+
if (mUseMasterVolume) {
adjustMasterVolume(
keyCode == KeyEvent.KEYCODE_VOLUME_UP
@@ -502,18 +513,17 @@ public class AudioManager {
* Play a sound. This is done on key up since we don't want the
* sound to play when a user holds down volume down to mute.
*/
- if (mUseMasterVolume) {
- if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
+ if (mUseVolumeKeySounds) {
+ if (mUseMasterVolume) {
adjustMasterVolume(ADJUST_SAME, FLAG_PLAY_SOUND);
+ } else {
+ int flags = FLAG_PLAY_SOUND;
+ adjustSuggestedStreamVolume(
+ ADJUST_SAME,
+ stream,
+ flags);
}
- } else {
- int flags = FLAG_PLAY_SOUND;
- adjustSuggestedStreamVolume(
- ADJUST_SAME,
- stream,
- flags);
}
-
mVolumeKeyUpTime = SystemClock.uptimeMillis();
break;
}
@@ -1161,7 +1171,7 @@ public class AudioManager {
/**
* Indicates if current platform supports use of SCO for off call use cases.
* Application wanted to use bluetooth SCO audio when the phone is not in call
- * must first call thsi method to make sure that the platform supports this
+ * must first call this method to make sure that the platform supports this
* feature.
* @return true if bluetooth SCO can be used for audio when not in call
* false otherwise
@@ -1297,6 +1307,19 @@ public class AudioManager {
}
/**
+ * @hide
+ * Signals whether remote submix audio rerouting is enabled.
+ */
+ public void setRemoteSubmixOn(boolean on, int address) {
+ IAudioService service = getService();
+ try {
+ service.setRemoteSubmixOn(on, address);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Dead object in setRemoteSubmixOn", e);
+ }
+ }
+
+ /**
* Sets audio routing to the wired headset on or off.
*
* @param on set <var>true</var> to route audio to/from wired
@@ -2427,4 +2450,40 @@ public class AudioManager {
return null;
}
}
+
+ /**
+ * Used as a key for {@link #getProperty} to request the native or optimal output sample rate
+ * for this device's primary output stream, in decimal Hz.
+ */
+ public static final String PROPERTY_OUTPUT_SAMPLE_RATE =
+ "android.media.property.OUTPUT_SAMPLE_RATE";
+
+ /**
+ * Used as a key for {@link #getProperty} to request the native or optimal output buffer size
+ * for this device's primary output stream, in decimal PCM frames.
+ */
+ public static final String PROPERTY_OUTPUT_FRAMES_PER_BUFFER =
+ "android.media.property.OUTPUT_FRAMES_PER_BUFFER";
+
+ /**
+ * Returns the value of the property with the specified key.
+ * @param key One of the strings corresponding to a property key: either
+ * {@link #PROPERTY_OUTPUT_SAMPLE_RATE} or
+ * {@link #PROPERTY_OUTPUT_FRAMES_PER_BUFFER}
+ * @return A string representing the associated value for that property key,
+ * or null if there is no value for that key.
+ */
+ public String getProperty(String key) {
+ if (PROPERTY_OUTPUT_SAMPLE_RATE.equals(key)) {
+ int outputSampleRate = AudioSystem.getPrimaryOutputSamplingRate();
+ return outputSampleRate > 0 ? Integer.toString(outputSampleRate) : null;
+ } else if (PROPERTY_OUTPUT_FRAMES_PER_BUFFER.equals(key)) {
+ int outputFramesPerBuffer = AudioSystem.getPrimaryOutputFrameCount();
+ return outputFramesPerBuffer > 0 ? Integer.toString(outputFramesPerBuffer) : null;
+ } else {
+ // null or unknown key
+ return null;
+ }
+ }
+
}
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index 2e153dd..a754ef3 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -57,6 +57,7 @@ import android.os.RemoteCallbackList;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemProperties;
+import android.os.UserHandle;
import android.os.Vibrator;
import android.provider.Settings;
import android.provider.Settings.System;
@@ -143,12 +144,17 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
private static final int MSG_REEVALUATE_REMOTE = 17;
private static final int MSG_RCC_NEW_PLAYBACK_INFO = 18;
private static final int MSG_RCC_NEW_VOLUME_OBS = 19;
+ private static final int MSG_SET_FORCE_BT_A2DP_USE = 20;
// start of messages handled under wakelock
// these messages can only be queued, i.e. sent with queueMsgUnderWakeLock(),
// and not with sendMsg(..., ..., SENDMSG_QUEUE, ...)
- private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 20;
- private static final int MSG_SET_A2DP_CONNECTION_STATE = 21;
+ private static final int MSG_SET_WIRED_DEVICE_CONNECTION_STATE = 21;
+ private static final int MSG_SET_A2DP_CONNECTION_STATE = 22;
// end of messages handled under wakelock
+ private static final int MSG_SET_RSX_CONNECTION_STATE = 23; // change remote submix connection
+ private static final int MSG_SET_FORCE_RSX_USE = 24; // force remote submix audio routing
+ private static final int MSG_CHECK_MUSIC_ACTIVE = 25;
+ private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 26;
// flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
// persisted
@@ -381,10 +387,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// message looper for SoundPool listener
private Looper mSoundPoolLooper = null;
// volume applied to sound played with playSoundEffect()
- private static int SOUND_EFFECT_VOLUME_DB;
- // getActiveStreamType() will return STREAM_NOTIFICATION during this period after a notification
+ private static int sSoundEffectVolumeDb;
+ // getActiveStreamType() will return:
+ // - STREAM_NOTIFICATION on tablets during this period after a notification stopped
+ // - STREAM_MUSIC on phones during this period after music or talkback/voice search prompt
// stopped
- private static final int NOTIFICATION_VOLUME_DELAY_MS = 5000;
+ private static final int DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS = 5000;
// previous volume adjustment direction received by checkForRingerModeChange()
private int mPrevVolDirection = AudioManager.ADJUST_SAME;
// Keyguard manager proxy
@@ -416,6 +424,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
*/
public final static int STREAM_REMOTE_MUSIC = -200;
+ // Devices for which the volume is fixed and VolumePanel slider should be disabled
+ final int mFixedVolumeDevices = AudioSystem.DEVICE_OUT_AUX_DIGITAL |
+ AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
+ AudioSystem.DEVICE_OUT_ALL_USB;
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
@@ -426,6 +439,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mContentResolver = context.getContentResolver();
mVoiceCapable = mContext.getResources().getBoolean(
com.android.internal.R.bool.config_voice_capable);
+ mSafeMediaVolumeIndex = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_safe_media_volume_index) * 10;
PowerManager pm = (PowerManager)context.getSystemService(Context.POWER_SERVICE);
mMediaEventWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "handleMediaEvent");
@@ -438,7 +453,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
"ro.config.vc_call_vol_steps",
MAX_STREAM_VOLUME[AudioSystem.STREAM_VOICE_CALL]);
- SOUND_EFFECT_VOLUME_DB = context.getResources().getInteger(
+ sSoundEffectVolumeDb = context.getResources().getInteger(
com.android.internal.R.integer.config_soundEffectVolumeDb);
mVolumePanel = new VolumePanel(context, this);
@@ -450,6 +465,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
updateStreamVolumeAlias(false /*updateVolumes*/);
createStreamStates();
+ mSafeMediaVolumeEnabled = new Boolean(true);
+ synchronized (mSafeMediaVolumeEnabled) {
+ enforceSafeMediaVolume();
+ }
+
mMediaServerOk = true;
// Call setRingerModeInt() to apply correct mute
@@ -469,6 +489,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
intentFilter.addAction(Intent.ACTION_BOOT_COMPLETED);
intentFilter.addAction(Intent.ACTION_SCREEN_ON);
intentFilter.addAction(Intent.ACTION_SCREEN_OFF);
+ intentFilter.addAction(Intent.ACTION_USER_SWITCHED);
// Register a configuration change listener only if requested by system properties
// to monitor orientation changes (off by default)
@@ -597,7 +618,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
final ContentResolver cr = mContentResolver;
int ringerModeFromSettings =
- System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
+ Settings.Global.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
int ringerMode = ringerModeFromSettings;
// sanity check in case the settings are restored from a device with incompatible
// ringer modes
@@ -608,7 +629,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
ringerMode = AudioManager.RINGER_MODE_SILENT;
}
if (ringerMode != ringerModeFromSettings) {
- System.putInt(cr, System.MODE_RINGER, ringerMode);
+ Settings.Global.putInt(cr, System.MODE_RINGER, ringerMode);
}
synchronized(mSettingsLock) {
mRingerMode = ringerMode;
@@ -627,23 +648,30 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// make sure settings for ringer mode are consistent with device type: non voice capable
// devices (tablets) include media stream in silent mode whereas phones don't.
- mRingerModeAffectedStreams = Settings.System.getInt(cr,
+ mRingerModeAffectedStreams = Settings.System.getIntForUser(cr,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
- (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
+ (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
+ UserHandle.USER_CURRENT);
if (mVoiceCapable) {
mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
} else {
mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
}
- Settings.System.putInt(cr,
- Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams);
+ Settings.System.putIntForUser(cr,
+ Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+ mRingerModeAffectedStreams,
+ UserHandle.USER_CURRENT);
- mMuteAffectedStreams = System.getInt(cr,
+ mMuteAffectedStreams = System.getIntForUser(cr,
System.MUTE_STREAMS_AFFECTED,
- ((1 << AudioSystem.STREAM_MUSIC)|(1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_SYSTEM)));
+ ((1 << AudioSystem.STREAM_MUSIC)|
+ (1 << AudioSystem.STREAM_RING)|
+ (1 << AudioSystem.STREAM_SYSTEM)),
+ UserHandle.USER_CURRENT);
- boolean masterMute = System.getInt(cr, System.VOLUME_MASTER_MUTE, 0) == 1;
+ boolean masterMute = System.getIntForUser(cr, System.VOLUME_MASTER_MUTE,
+ 0, UserHandle.USER_CURRENT) == 1;
AudioSystem.setMasterMute(masterMute);
broadcastMasterMuteStatus(masterMute);
@@ -734,58 +762,71 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// convert one UI step (+/-1) into a number of internal units on the stream alias
int step = rescaleIndex(10, streamType, streamTypeAlias);
- // If either the client forces allowing ringer modes for this adjustment,
- // or the stream type is one that is affected by ringer modes
- if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
- (streamTypeAlias == getMasterStreamType())) {
- int ringerMode = getRingerMode();
- // do not vibrate if already in vibrate mode
- if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
- flags &= ~AudioManager.FLAG_VIBRATE;
- }
- // Check if the ringer mode changes with this volume adjustment. If
- // it does, it will handle adjusting the volume, so we won't below
- adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);
- if ((streamTypeAlias == getMasterStreamType()) &&
- (mRingerMode == AudioManager.RINGER_MODE_SILENT)) {
- streamState.setLastAudibleIndex(0, device);
- }
+ if ((direction == AudioManager.ADJUST_RAISE) &&
+ !checkSafeMediaVolume(streamTypeAlias, aliasIndex + step, device)) {
+ return;
}
- // If stream is muted, adjust last audible index only
int index;
- final int oldIndex = mStreamStates[streamType].getIndex(device,
- (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
+ int oldIndex;
- if (streamState.muteCount() != 0) {
- if (adjustVolume) {
- // Post a persist volume msg
- // no need to persist volume on all streams sharing the same alias
- streamState.adjustLastAudibleIndex(direction * step, device);
- sendMsg(mAudioHandler,
- MSG_PERSIST_VOLUME,
- SENDMSG_QUEUE,
- PERSIST_LAST_AUDIBLE,
- device,
- streamState,
- PERSIST_DELAY);
- }
- index = mStreamStates[streamType].getIndex(device, true /* lastAudible */);
+ if ((streamTypeAlias == AudioSystem.STREAM_MUSIC) &&
+ ((device & mFixedVolumeDevices) != 0)) {
+ flags |= AudioManager.FLAG_FIXED_VOLUME;
+ index = mStreamStates[streamType].getMaxIndex();
+ oldIndex = index;
} else {
- if (adjustVolume && streamState.adjustIndex(direction * step, device)) {
- // Post message to set system volume (it in turn will post a message
- // to persist). Do not change volume if stream is muted.
- sendMsg(mAudioHandler,
- MSG_SET_DEVICE_VOLUME,
- SENDMSG_QUEUE,
- device,
- 0,
- streamState,
- 0);
+ // If either the client forces allowing ringer modes for this adjustment,
+ // or the stream type is one that is affected by ringer modes
+ if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
+ (streamTypeAlias == getMasterStreamType())) {
+ int ringerMode = getRingerMode();
+ // do not vibrate if already in vibrate mode
+ if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+ flags &= ~AudioManager.FLAG_VIBRATE;
+ }
+ // Check if the ringer mode changes with this volume adjustment. If
+ // it does, it will handle adjusting the volume, so we won't below
+ adjustVolume = checkForRingerModeChange(aliasIndex, direction, step);
+ if ((streamTypeAlias == getMasterStreamType()) &&
+ (mRingerMode == AudioManager.RINGER_MODE_SILENT)) {
+ streamState.setLastAudibleIndex(0, device);
+ }
}
- index = mStreamStates[streamType].getIndex(device, false /* lastAudible */);
- }
+ // If stream is muted, adjust last audible index only
+ oldIndex = mStreamStates[streamType].getIndex(device,
+ (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
+
+ if (streamState.muteCount() != 0) {
+ if (adjustVolume) {
+ // Post a persist volume msg
+ // no need to persist volume on all streams sharing the same alias
+ streamState.adjustLastAudibleIndex(direction * step, device);
+ sendMsg(mAudioHandler,
+ MSG_PERSIST_VOLUME,
+ SENDMSG_QUEUE,
+ PERSIST_LAST_AUDIBLE,
+ device,
+ streamState,
+ PERSIST_DELAY);
+ }
+ index = mStreamStates[streamType].getIndex(device, true /* lastAudible */);
+ } else {
+ if (adjustVolume && streamState.adjustIndex(direction * step, device)) {
+ // Post message to set system volume (it in turn will post a message
+ // to persist). Do not change volume if stream is muted.
+ sendMsg(mAudioHandler,
+ MSG_SET_DEVICE_VOLUME,
+ SENDMSG_QUEUE,
+ device,
+ 0,
+ streamState,
+ 0);
+ }
+ index = mStreamStates[streamType].getIndex(device, false /* lastAudible */);
+ }
+ }
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
@@ -811,35 +852,47 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
VolumeStreamState streamState = mStreamStates[mStreamVolumeAlias[streamType]];
final int device = getDeviceForStream(streamType);
- // get last audible index if stream is muted, current index otherwise
- final int oldIndex = streamState.getIndex(device,
- (streamState.muteCount() != 0) /* lastAudible */);
+ int oldIndex;
- index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]);
-
- // setting volume on master stream type also controls silent mode
- if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
- (mStreamVolumeAlias[streamType] == getMasterStreamType())) {
- int newRingerMode;
- if (index == 0) {
- newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
- : AudioManager.RINGER_MODE_SILENT;
- setStreamVolumeInt(mStreamVolumeAlias[streamType],
- index,
- device,
- false,
- true);
- } else {
- newRingerMode = AudioManager.RINGER_MODE_NORMAL;
+ if ((mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
+ ((device & mFixedVolumeDevices) != 0)) {
+ flags |= AudioManager.FLAG_FIXED_VOLUME;
+ index = mStreamStates[streamType].getMaxIndex();
+ oldIndex = index;
+ } else {
+ // get last audible index if stream is muted, current index otherwise
+ oldIndex = streamState.getIndex(device,
+ (streamState.muteCount() != 0) /* lastAudible */);
+
+ index = rescaleIndex(index * 10, streamType, mStreamVolumeAlias[streamType]);
+
+ if (!checkSafeMediaVolume(mStreamVolumeAlias[streamType], index, device)) {
+ return;
}
- setRingerMode(newRingerMode);
- }
- setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false, true);
- // get last audible index if stream is muted, current index otherwise
- index = mStreamStates[streamType].getIndex(device,
- (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
+ // setting volume on master stream type also controls silent mode
+ if (((flags & AudioManager.FLAG_ALLOW_RINGER_MODES) != 0) ||
+ (mStreamVolumeAlias[streamType] == getMasterStreamType())) {
+ int newRingerMode;
+ if (index == 0) {
+ newRingerMode = mHasVibrator ? AudioManager.RINGER_MODE_VIBRATE
+ : AudioManager.RINGER_MODE_SILENT;
+ setStreamVolumeInt(mStreamVolumeAlias[streamType],
+ index,
+ device,
+ false,
+ true);
+ } else {
+ newRingerMode = AudioManager.RINGER_MODE_NORMAL;
+ }
+ setRingerMode(newRingerMode);
+ }
+ setStreamVolumeInt(mStreamVolumeAlias[streamType], index, device, false, true);
+ // get last audible index if stream is muted, current index otherwise
+ index = mStreamStates[streamType].getIndex(device,
+ (mStreamStates[streamType].muteCount() != 0) /* lastAudible */);
+ }
sendVolumeUpdate(streamType, oldIndex, index, flags);
}
@@ -929,6 +982,24 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
return delta;
}
+ private void sendBroadcastToAll(Intent intent) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
+ private void sendStickyBroadcastToAll(Intent intent) {
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendStickyBroadcastAsUser(intent, UserHandle.ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
+ }
+
// UI update and Broadcast Intent
private void sendVolumeUpdate(int streamType, int oldIndex, int index, int flags) {
if (!mVoiceCapable && (streamType == AudioSystem.STREAM_RING)) {
@@ -937,13 +1008,15 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mVolumePanel.postVolumeChanged(streamType, flags);
- oldIndex = (oldIndex + 5) / 10;
- index = (index + 5) / 10;
- Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
- intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
- intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
- intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
- mContext.sendBroadcast(intent);
+ if ((flags & AudioManager.FLAG_FIXED_VOLUME) == 0) {
+ oldIndex = (oldIndex + 5) / 10;
+ index = (index + 5) / 10;
+ Intent intent = new Intent(AudioManager.VOLUME_CHANGED_ACTION);
+ intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_TYPE, streamType);
+ intent.putExtra(AudioManager.EXTRA_VOLUME_STREAM_VALUE, index);
+ intent.putExtra(AudioManager.EXTRA_PREV_VOLUME_STREAM_VALUE, oldIndex);
+ sendBroadcastToAll(intent);
+ }
}
// UI update and Broadcast Intent
@@ -953,7 +1026,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Intent intent = new Intent(AudioManager.MASTER_VOLUME_CHANGED_ACTION);
intent.putExtra(AudioManager.EXTRA_PREV_MASTER_VOLUME_VALUE, oldVolume);
intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_VALUE, newVolume);
- mContext.sendBroadcast(intent);
+ sendBroadcastToAll(intent);
}
// UI update and Broadcast Intent
@@ -967,9 +1040,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
intent.putExtra(AudioManager.EXTRA_MASTER_VOLUME_MUTED, muted);
intent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- long origCallerIdentityToken = Binder.clearCallingIdentity();
- mContext.sendStickyBroadcast(intent);
- Binder.restoreCallingIdentity(origCallerIdentityToken);
+ sendStickyBroadcastToAll(intent);
}
/**
@@ -1060,7 +1131,15 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
public int getStreamVolume(int streamType) {
ensureValidStreamType(streamType);
int device = getDeviceForStream(streamType);
- return (mStreamStates[streamType].getIndex(device, false /* lastAudible */) + 5) / 10;
+ int index;
+
+ if ((mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
+ (device & mFixedVolumeDevices) != 0) {
+ index = mStreamStates[streamType].getMaxIndex();
+ } else {
+ index = mStreamStates[streamType].getIndex(device, false /* lastAudible */);
+ }
+ return (index + 5) / 10;
}
public int getMasterVolume() {
@@ -1200,8 +1279,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
private void restoreMasterVolume() {
if (mUseMasterVolume) {
- float volume = Settings.System.getFloat(mContentResolver,
- Settings.System.VOLUME_MASTER, -1.0f);
+ float volume = Settings.System.getFloatForUser(mContentResolver,
+ Settings.System.VOLUME_MASTER, -1.0f, UserHandle.USER_CURRENT);
if (volume >= 0.0f) {
AudioSystem.setMasterVolume(volume);
}
@@ -1637,6 +1716,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
/** @see AudioManager#reloadAudioSettings() */
public void reloadAudioSettings() {
+ readAudioSettings(false /*userSwitch*/);
+ }
+
+ private void readAudioSettings(boolean userSwitch) {
// restore ringer mode, ringer mode affected streams, mute affected streams and vibrate settings
readPersistedSettings();
@@ -1645,6 +1728,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
for (int streamType = 0; streamType < numStreamTypes; streamType++) {
VolumeStreamState streamState = mStreamStates[streamType];
+ if (userSwitch && mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) {
+ continue;
+ }
+
synchronized (streamState) {
streamState.readSettings();
@@ -1661,6 +1748,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
checkAllAliasStreamVolumes();
+ synchronized (mSafeMediaVolumeEnabled) {
+ if (mSafeMediaVolumeEnabled) {
+ enforceSafeMediaVolume();
+ }
+ }
+
// apply new ringer mode
setRingerModeInt(getRingerMode(), false);
}
@@ -1701,7 +1794,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
/** @see AudioManager#setBluetoothA2dpOn() */
public void setBluetoothA2dpOn(boolean on) {
- setBluetoothA2dpOnInt(on);
+ synchronized (mBluetoothA2dpEnabledLock) {
+ mBluetoothA2dpEnabled = on;
+ sendMsg(mAudioHandler, MSG_SET_FORCE_BT_A2DP_USE, SENDMSG_QUEUE,
+ AudioSystem.FOR_MEDIA,
+ mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
+ null, 0);
+ }
}
/** @see AudioManager#isBluetoothA2dpOn() */
@@ -1985,7 +2084,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, state);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_PREVIOUS_STATE,
mScoConnectionState);
- mContext.sendStickyBroadcast(newIntent);
+ sendStickyBroadcastToAll(newIntent);
mScoConnectionState = state;
}
}
@@ -2085,6 +2184,63 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
};
+ /** see AudioManager.setRemoteSubmixOn(boolean on) */
+ public void setRemoteSubmixOn(boolean on, int address) {
+ sendMsg(mAudioHandler, MSG_SET_RSX_CONNECTION_STATE,
+ SENDMSG_REPLACE /* replace with QUEUE when multiple addresses are supported */,
+ on ? 1 : 0 /*arg1*/,
+ address /*arg2*/,
+ null/*obj*/, 0/*delay*/);
+
+ // Note that we are currently forcing use of remote submix as soon as corresponding device
+ // is made available
+ sendMsg(mAudioHandler, MSG_SET_FORCE_RSX_USE, SENDMSG_REPLACE,
+ AudioSystem.FOR_MEDIA,
+ on ? AudioSystem.FORCE_REMOTE_SUBMIX : AudioSystem.FORCE_NONE,
+ null/*obj*/, 0/*delay*/);
+ }
+
+ private void onSetRsxConnectionState(int available, int address) {
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_IN_REMOTE_SUBMIX,
+ available == 1 ?
+ AudioSystem.DEVICE_STATE_AVAILABLE : AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ String.valueOf(address) /*device_address*/);
+ AudioSystem.setDeviceConnectionState(AudioSystem.DEVICE_OUT_REMOTE_SUBMIX,
+ available == 1 ?
+ AudioSystem.DEVICE_STATE_AVAILABLE : AudioSystem.DEVICE_STATE_UNAVAILABLE,
+ String.valueOf(address) /*device_address*/);
+ }
+
+ private void onCheckMusicActive() {
+ synchronized (mSafeMediaVolumeEnabled) {
+ if (!mSafeMediaVolumeEnabled) {
+ int device = getDeviceForStream(AudioSystem.STREAM_MUSIC);
+
+ if ((device & mSafeMediaVolumeDevices) != 0) {
+ sendMsg(mAudioHandler,
+ MSG_CHECK_MUSIC_ACTIVE,
+ SENDMSG_REPLACE,
+ 0,
+ 0,
+ null,
+ MUSIC_ACTIVE_POLL_PERIOD_MS);
+ int index = mStreamStates[AudioSystem.STREAM_MUSIC].getIndex(device,
+ false /*lastAudible*/);
+ if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0) &&
+ (index > mSafeMediaVolumeIndex)) {
+ // Approximate cumulative active music time
+ mMusicActiveMs += MUSIC_ACTIVE_POLL_PERIOD_MS;
+ if (mMusicActiveMs > UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX) {
+ setSafeMediaVolumeEnabled(true);
+ mMusicActiveMs = 0;
+ mVolumePanel.postDisplaySafeVolumeWarning();
+ }
+ }
+ }
+ }
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////
// Internal methods
///////////////////////////////////////////////////////////////////////////
@@ -2217,7 +2373,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
if (DEBUG_VOL)
Log.v(TAG, "getActiveStreamType: Forcing STREAM_REMOTE_MUSIC");
return STREAM_REMOTE_MUSIC;
- } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC, 0)) {
+ } else if (AudioSystem.isStreamActive(AudioSystem.STREAM_MUSIC,
+ DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
if (DEBUG_VOL)
Log.v(TAG, "getActiveStreamType: Forcing STREAM_MUSIC stream active");
return AudioSystem.STREAM_MUSIC;
@@ -2246,9 +2403,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
return AudioSystem.STREAM_VOICE_CALL;
}
} else if (AudioSystem.isStreamActive(AudioSystem.STREAM_NOTIFICATION,
- NOTIFICATION_VOLUME_DELAY_MS) ||
+ DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS) ||
AudioSystem.isStreamActive(AudioSystem.STREAM_RING,
- NOTIFICATION_VOLUME_DELAY_MS)) {
+ DEFAULT_STREAM_TYPE_OVERRIDE_DELAY_MS)) {
if (DEBUG_VOL) Log.v(TAG, "getActiveStreamType: Forcing STREAM_NOTIFICATION");
return AudioSystem.STREAM_NOTIFICATION;
} else if (suggestedStreamType == AudioManager.USE_DEFAULT_STREAM_TYPE) {
@@ -2276,9 +2433,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
broadcast.putExtra(AudioManager.EXTRA_RINGER_MODE, ringerMode);
broadcast.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT
| Intent.FLAG_RECEIVER_REPLACE_PENDING);
- long origCallerIdentityToken = Binder.clearCallingIdentity();
- mContext.sendStickyBroadcast(broadcast);
- Binder.restoreCallingIdentity(origCallerIdentityToken);
+ sendStickyBroadcastToAll(broadcast);
}
private void broadcastVibrateSetting(int vibrateType) {
@@ -2287,7 +2442,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Intent broadcast = new Intent(AudioManager.VIBRATE_SETTING_CHANGED_ACTION);
broadcast.putExtra(AudioManager.EXTRA_VIBRATE_TYPE, vibrateType);
broadcast.putExtra(AudioManager.EXTRA_VIBRATE_SETTING, getVibrateSetting(vibrateType));
- mContext.sendBroadcast(broadcast);
+ sendBroadcastToAll(broadcast);
}
}
@@ -2429,7 +2584,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// device, continue otherwise
int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
- int index = Settings.System.getInt(mContentResolver, name, defaultIndex);
+ int index = Settings.System.getIntForUser(
+ mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
if (index == -1) {
continue;
}
@@ -2440,7 +2596,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// or default index
defaultIndex = (index > 0) ?
index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
- int lastAudibleIndex = Settings.System.getInt(mContentResolver, name, defaultIndex);
+ int lastAudibleIndex = Settings.System.getIntForUser(
+ mContentResolver, name, defaultIndex, UserHandle.USER_CURRENT);
// a last audible index of 0 should never be stored for ring and notification
// streams on phones (voice capable devices).
@@ -2852,19 +3009,21 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
int persistType,
int device) {
if ((persistType & PERSIST_CURRENT) != 0) {
- System.putInt(mContentResolver,
+ System.putIntForUser(mContentResolver,
streamState.getSettingNameForDevice(false /* lastAudible */, device),
- (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10);
+ (streamState.getIndex(device, false /* lastAudible */) + 5)/ 10,
+ UserHandle.USER_CURRENT);
}
if ((persistType & PERSIST_LAST_AUDIBLE) != 0) {
- System.putInt(mContentResolver,
+ System.putIntForUser(mContentResolver,
streamState.getSettingNameForDevice(true /* lastAudible */, device),
- (streamState.getIndex(device, true /* lastAudible */) + 5) / 10);
+ (streamState.getIndex(device, true /* lastAudible */) + 5) / 10,
+ UserHandle.USER_CURRENT);
}
}
private void persistRingerMode(int ringerMode) {
- System.putInt(mContentResolver, System.MODE_RINGER, ringerMode);
+ Settings.Global.putInt(mContentResolver, System.MODE_RINGER, ringerMode);
}
private void playSoundEffect(int effectType, int volume) {
@@ -2875,7 +3034,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
float volFloat;
// use default if volume is not specified by caller
if (volume < 0) {
- volFloat = (float)Math.pow(10, SOUND_EFFECT_VOLUME_DB/20);
+ volFloat = (float)Math.pow(10, (float)sSoundEffectVolumeDb/20);
} else {
volFloat = (float) volume / 1000.0f;
}
@@ -2914,8 +3073,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
private void onHandlePersistMediaButtonReceiver(ComponentName receiver) {
- Settings.System.putString(mContentResolver, Settings.System.MEDIA_BUTTON_RECEIVER,
- receiver == null ? "" : receiver.flattenToString());
+ Settings.System.putStringForUser(mContentResolver,
+ Settings.System.MEDIA_BUTTON_RECEIVER,
+ receiver == null ? "" : receiver.flattenToString(),
+ UserHandle.USER_CURRENT);
}
private void cleanupPlayer(MediaPlayer mp) {
@@ -2951,13 +3112,17 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
break;
case MSG_PERSIST_MASTER_VOLUME:
- Settings.System.putFloat(mContentResolver, Settings.System.VOLUME_MASTER,
- (float)msg.arg1 / (float)1000.0);
+ Settings.System.putFloatForUser(mContentResolver,
+ Settings.System.VOLUME_MASTER,
+ (float)msg.arg1 / (float)1000.0,
+ UserHandle.USER_CURRENT);
break;
case MSG_PERSIST_MASTER_VOLUME_MUTE:
- Settings.System.putInt(mContentResolver, Settings.System.VOLUME_MASTER_MUTE,
- msg.arg1);
+ Settings.System.putIntForUser(mContentResolver,
+ Settings.System.VOLUME_MASTER_MUTE,
+ msg.arg1,
+ UserHandle.USER_CURRENT);
break;
case MSG_PERSIST_RINGER_MODE:
@@ -3049,6 +3214,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
break;
case MSG_SET_FORCE_USE:
+ case MSG_SET_FORCE_BT_A2DP_USE:
+ case MSG_SET_FORCE_RSX_USE:
setForceUse(msg.arg1, msg.arg2);
break;
@@ -3111,6 +3278,18 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
onRegisterVolumeObserverForRcc(msg.arg1 /* rccId */,
(IRemoteVolumeObserver)msg.obj /* rvo */);
break;
+
+ case MSG_SET_RSX_CONNECTION_STATE:
+ onSetRsxConnectionState(msg.arg1/*available*/, msg.arg2/*address*/);
+ break;
+
+ case MSG_CHECK_MUSIC_ACTIVE:
+ onCheckMusicActive();
+ break;
+
+ case MSG_BROADCAST_AUDIO_BECOMING_NOISY:
+ onSendBecomingNoisyIntent();
+ break;
}
}
}
@@ -3131,10 +3310,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// and mRingerModeAffectedStreams, so will leave this synchronized for now.
// mRingerModeMutedStreams and mMuteAffectedStreams are safe (only accessed once).
synchronized (mSettingsLock) {
- int ringerModeAffectedStreams = Settings.System.getInt(mContentResolver,
+ int ringerModeAffectedStreams = Settings.System.getIntForUser(mContentResolver,
Settings.System.MODE_RINGER_STREAMS_AFFECTED,
((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
- (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
+ (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)),
+ UserHandle.USER_CURRENT);
if (mVoiceCapable) {
ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
} else {
@@ -3166,8 +3346,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
address);
}
- private void sendBecomingNoisyIntent() {
- mContext.sendBroadcast(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
+ private void onSendBecomingNoisyIntent() {
+ sendBroadcastToAll(new Intent(AudioManager.ACTION_AUDIO_BECOMING_NOISY));
}
// must be called synchronized on mConnectedDevices
@@ -3286,7 +3466,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// sent if none of these devices is connected.
int mBecomingNoisyIntentDevices =
AudioSystem.DEVICE_OUT_WIRED_HEADSET | AudioSystem.DEVICE_OUT_WIRED_HEADPHONE |
- AudioSystem.DEVICE_OUT_ALL_A2DP;
+ AudioSystem.DEVICE_OUT_ALL_A2DP | AudioSystem.DEVICE_OUT_AUX_DIGITAL |
+ AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET | AudioSystem.DEVICE_OUT_DGTL_DOCK_HEADSET |
+ AudioSystem.DEVICE_OUT_ALL_USB;
// must be called before removing the device from mConnectedDevices
private int checkSendBecomingNoisyIntent(int device, int state) {
@@ -3299,8 +3481,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
if (devices == device) {
+ sendMsg(mAudioHandler,
+ MSG_BROADCAST_AUDIO_BECOMING_NOISY,
+ SENDMSG_REPLACE,
+ 0,
+ 0,
+ null,
+ 0);
delay = 1000;
- sendBecomingNoisyIntent();
}
}
@@ -3356,7 +3544,12 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
- ActivityManagerNative.broadcastStickyIntent(intent, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ ActivityManagerNative.broadcastStickyIntent(intent, null, UserHandle.USER_ALL);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
private void onSetWiredDeviceConnectionState(int device, int state, String name)
@@ -3366,12 +3559,26 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
(device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
setBluetoothA2dpOnInt(true);
}
- handleDeviceConnection((state == 1), device, "");
- if ((state != 0) && ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
- (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE))) {
- setBluetoothA2dpOnInt(false);
+ boolean isUsb = ((device & AudioSystem.DEVICE_OUT_ALL_USB) != 0);
+ handleDeviceConnection((state == 1), device, (isUsb ? name : ""));
+ if (state != 0) {
+ if ((device == AudioSystem.DEVICE_OUT_WIRED_HEADSET) ||
+ (device == AudioSystem.DEVICE_OUT_WIRED_HEADPHONE)) {
+ setBluetoothA2dpOnInt(false);
+ }
+ if ((device & mSafeMediaVolumeDevices) != 0) {
+ sendMsg(mAudioHandler,
+ MSG_CHECK_MUSIC_ACTIVE,
+ SENDMSG_REPLACE,
+ 0,
+ 0,
+ null,
+ MUSIC_ACTIVE_POLL_PERIOD_MS);
+ }
+ }
+ if (!isUsb) {
+ sendDeviceConnectionIntent(device, state, name);
}
- sendDeviceConnectionIntent(device, state, name);
}
}
@@ -3463,7 +3670,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
+ (action.equals(Intent.ACTION_USB_AUDIO_ACCESSORY_PLUG) ?
"ACTION_USB_AUDIO_ACCESSORY_PLUG" : "ACTION_USB_AUDIO_DEVICE_PLUG")
+ ", state = " + state + ", card: " + alsaCard + ", device: " + alsaDevice);
- handleDeviceConnection((state == 1), device, params);
+ setWiredDeviceConnectionState(device, state, params);
} else if (action.equals(BluetoothHeadset.ACTION_AUDIO_STATE_CHANGED)) {
boolean broadcast = false;
int scoAudioState = AudioManager.SCO_AUDIO_STATE_ERROR;
@@ -3508,7 +3715,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED. Remove when appropriate.
Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE, scoAudioState);
- mContext.sendStickyBroadcast(newIntent);
+ sendStickyBroadcastToAll(newIntent);
}
} else if (action.equals(Intent.ACTION_BOOT_COMPLETED)) {
mBootCompleted = true;
@@ -3525,7 +3732,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Intent newIntent = new Intent(AudioManager.ACTION_SCO_AUDIO_STATE_CHANGED);
newIntent.putExtra(AudioManager.EXTRA_SCO_AUDIO_STATE,
AudioManager.SCO_AUDIO_STATE_DISCONNECTED);
- mContext.sendStickyBroadcast(newIntent);
+ sendStickyBroadcastToAll(newIntent);
BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
if (adapter != null) {
@@ -3546,6 +3753,24 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
AudioSystem.setParameters("screen_state=off");
} else if (action.equalsIgnoreCase(Intent.ACTION_CONFIGURATION_CHANGED)) {
handleConfigurationChanged(context);
+ } else if (action.equals(Intent.ACTION_USER_SWITCHED)) {
+ // attempt to stop music playabck for background user
+ sendMsg(mAudioHandler,
+ MSG_BROADCAST_AUDIO_BECOMING_NOISY,
+ SENDMSG_REPLACE,
+ 0,
+ 0,
+ null,
+ 0);
+ // load volume settings for new user
+ readAudioSettings(true /*userSwitch*/);
+ // preserve STREAM_MUSIC volume from one user to the next.
+ sendMsg(mAudioHandler,
+ MSG_SET_ALL_VOLUMES,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ mStreamStates[AudioSystem.STREAM_MUSIC], 0);
}
}
}
@@ -3652,9 +3877,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
while(stackIterator.hasNext()) {
FocusStackEntry fse = stackIterator.next();
- pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
+ pw.println(" source:" + fse.mSourceRef + " -- client: " + fse.mClientId
+ " -- duration: " + fse.mFocusChangeType
- + " -- uid: " + fse.mCallingUid);
+ + " -- uid: " + fse.mCallingUid
+ + " -- stream: " + fse.mStreamType);
}
}
}
@@ -3915,8 +4141,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mMediaEventWakeLock.acquire();
keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
}
- mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
- mAudioHandler, Activity.RESULT_OK, null, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+ null, mKeyEventDone, mAudioHandler, Activity.RESULT_OK, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
/**
@@ -3949,8 +4180,14 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
if (needWakeLock) {
keyIntent.putExtra(EXTRA_WAKELOCK_ACQUIRED, WAKELOCK_RELEASE_ON_FINISHED);
}
- mContext.sendOrderedBroadcast(keyIntent, null, mKeyEventDone,
- mAudioHandler, Activity.RESULT_OK, null, null);
+ final long ident = Binder.clearCallingIdentity();
+ try {
+ mContext.sendOrderedBroadcastAsUser(keyIntent, UserHandle.ALL,
+ null, mKeyEventDone,
+ mAudioHandler, Activity.RESULT_OK, null, null);
+ } finally {
+ Binder.restoreCallingIdentity(ident);
+ }
}
}
}
@@ -4014,7 +4251,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
startVoiceBasedInteractions(needWakeLock);
break;
case VOICEBUTTON_ACTION_SIMULATE_KEY_PRESS:
- if (DEBUG_RC) Log.v(TAG, " send simulated key event");
+ if (DEBUG_RC) Log.v(TAG, " send simulated key event, wakelock=" + needWakeLock);
sendSimulatedMediaButtonEvent(keyEvent, needWakeLock);
break;
}
@@ -4350,7 +4587,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
" -- vol: " + rcse.mPlaybackVolume +
" -- volMax: " + rcse.mPlaybackVolumeMax +
" -- volObs: " + rcse.mRemoteVolumeObs);
-
+
}
}
synchronized (mMainRemote) {
@@ -4408,8 +4645,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
* Restore remote control receiver from the system settings.
*/
private void restoreMediaButtonReceiver() {
- String receiverName = Settings.System.getString(mContentResolver,
- Settings.System.MEDIA_BUTTON_RECEIVER);
+ String receiverName = Settings.System.getStringForUser(mContentResolver,
+ Settings.System.MEDIA_BUTTON_RECEIVER, UserHandle.USER_CURRENT);
if ((null != receiverName) && !receiverName.isEmpty()) {
ComponentName eventReceiver = ComponentName.unflattenFromString(receiverName);
// construct a PendingIntent targeted to the restored component name
@@ -4646,17 +4883,40 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
clearRemoteControlDisplay_syncAfRcs();
return;
}
- // if the top of the two stacks belong to different packages, there is a mismatch, clear
+
+ // determine which entry in the AudioFocus stack to consider, and compare against the
+ // top of the stack for the media button event receivers : simply using the top of the
+ // stack would make the entry disappear from the RemoteControlDisplay in conditions such as
+ // notifications playing during music playback.
+ // crawl the AudioFocus stack until an entry is found with the following characteristics:
+ // - focus gain on STREAM_MUSIC stream
+ // - non-transient focus gain on a stream other than music
+ FocusStackEntry af = null;
+ Iterator<FocusStackEntry> stackIterator = mFocusStack.iterator();
+ while(stackIterator.hasNext()) {
+ FocusStackEntry fse = (FocusStackEntry)stackIterator.next();
+ if ((fse.mStreamType == AudioManager.STREAM_MUSIC)
+ || (fse.mFocusChangeType == AudioManager.AUDIOFOCUS_GAIN)) {
+ af = fse;
+ break;
+ }
+ }
+ if (af == null) {
+ clearRemoteControlDisplay_syncAfRcs();
+ return;
+ }
+
+ // if the audio focus and RC owners belong to different packages, there is a mismatch, clear
if ((mRCStack.peek().mCallingPackageName != null)
- && (mFocusStack.peek().mPackageName != null)
+ && (af.mPackageName != null)
&& !(mRCStack.peek().mCallingPackageName.compareTo(
- mFocusStack.peek().mPackageName) == 0)) {
+ af.mPackageName) == 0)) {
clearRemoteControlDisplay_syncAfRcs();
return;
}
// if the audio focus didn't originate from the same Uid as the one in which the remote
// control information will be retrieved, clear
- if (mRCStack.peek().mCallingUid != mFocusStack.peek().mCallingUid) {
+ if (mRCStack.peek().mCallingUid != af.mCallingUid) {
clearRemoteControlDisplay_syncAfRcs();
return;
}
@@ -5290,10 +5550,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
public void setBluetoothA2dpOnInt(boolean on) {
synchronized (mBluetoothA2dpEnabledLock) {
mBluetoothA2dpEnabled = on;
- sendMsg(mAudioHandler, MSG_SET_FORCE_USE, SENDMSG_QUEUE,
- AudioSystem.FOR_MEDIA,
- mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP,
- null, 0);
+ mAudioHandler.removeMessages(MSG_SET_FORCE_BT_A2DP_USE);
+ AudioSystem.setForceUse(AudioSystem.FOR_MEDIA,
+ mBluetoothA2dpEnabled ? AudioSystem.FORCE_NONE : AudioSystem.FORCE_NO_BT_A2DP);
}
}
@@ -5317,6 +5576,108 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
+
+ //==========================================================================================
+ // Safe media volume management.
+ // MUSIC stream volume level is limited when headphones are connected according to safety
+ // regulation. When the user attempts to raise the volume above the limit, a warning is
+ // displayed and the user has to acknowlegde before the volume is actually changed.
+ // The volume index corresponding to the limit is stored in config_safe_media_volume_index
+ // property. Platforms with a different limit must set this property accordingly in their
+ // overlay.
+ //==========================================================================================
+
+ // mSafeMediaVolumeEnabled indicates whether the media volume is limited over headphones.
+ // It is true by default when headphones or a headset are inserted and can be overriden by
+ // calling AudioService.disableSafeMediaVolume() (when user opts out).
+ private Boolean mSafeMediaVolumeEnabled;
+ // mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property
+ private final int mSafeMediaVolumeIndex;
+ // mSafeMediaVolumeDevices lists the devices for which safe media volume is enforced,
+ private final int mSafeMediaVolumeDevices = AudioSystem.DEVICE_OUT_WIRED_HEADSET |
+ AudioSystem.DEVICE_OUT_WIRED_HEADPHONE;
+ // mMusicActiveMs is the cumulative time of music activity since safe volume was disabled.
+ // When this time reaches UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX, the safe media volume is re-enabled
+ // automatically. mMusicActiveMs is rounded to a multiple of MUSIC_ACTIVE_POLL_PERIOD_MS.
+ private int mMusicActiveMs;
+ private static final int UNSAFE_VOLUME_MUSIC_ACTIVE_MS_MAX = (20 * 3600 * 1000); // 20 hours
+ private static final int MUSIC_ACTIVE_POLL_PERIOD_MS = 60000; // 1 minute polling interval
+
+ private void setSafeMediaVolumeEnabled(boolean on) {
+ synchronized (mSafeMediaVolumeEnabled) {
+ if (on && !mSafeMediaVolumeEnabled) {
+ enforceSafeMediaVolume();
+ } else if (!on && mSafeMediaVolumeEnabled) {
+ mMusicActiveMs = 0;
+ sendMsg(mAudioHandler,
+ MSG_CHECK_MUSIC_ACTIVE,
+ SENDMSG_REPLACE,
+ 0,
+ 0,
+ null,
+ MUSIC_ACTIVE_POLL_PERIOD_MS);
+ }
+ mSafeMediaVolumeEnabled = on;
+ }
+ }
+
+ private void enforceSafeMediaVolume() {
+ VolumeStreamState streamState = mStreamStates[AudioSystem.STREAM_MUSIC];
+ boolean lastAudible = (streamState.muteCount() != 0);
+ int devices = mSafeMediaVolumeDevices;
+ int i = 0;
+
+ while (devices != 0) {
+ int device = 1 << i++;
+ if ((device & devices) == 0) {
+ continue;
+ }
+ int index = streamState.getIndex(device, lastAudible);
+ if (index > mSafeMediaVolumeIndex) {
+ if (lastAudible) {
+ streamState.setLastAudibleIndex(mSafeMediaVolumeIndex, device);
+ sendMsg(mAudioHandler,
+ MSG_PERSIST_VOLUME,
+ SENDMSG_QUEUE,
+ PERSIST_LAST_AUDIBLE,
+ device,
+ streamState,
+ PERSIST_DELAY);
+ } else {
+ streamState.setIndex(mSafeMediaVolumeIndex, device, true);
+ sendMsg(mAudioHandler,
+ MSG_SET_DEVICE_VOLUME,
+ SENDMSG_QUEUE,
+ device,
+ 0,
+ streamState,
+ 0);
+ }
+ }
+ devices &= ~device;
+ }
+ }
+
+ private boolean checkSafeMediaVolume(int streamType, int index, int device) {
+ synchronized (mSafeMediaVolumeEnabled) {
+ if (mSafeMediaVolumeEnabled &&
+ (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
+ ((device & mSafeMediaVolumeDevices) != 0) &&
+ (index > mSafeMediaVolumeIndex)) {
+ mVolumePanel.postDisplaySafeVolumeWarning();
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public void disableSafeMediaVolume() {
+ synchronized (mSafeMediaVolumeEnabled) {
+ setSafeMediaVolumeEnabled(false);
+ }
+ }
+
+
@Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.DUMP, TAG);
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 1ca0df4..2cff4ff 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -188,6 +188,13 @@ public class AudioSystem
* AudioPolicyService methods
*/
+ //
+ // audio device definitions: must be kept in sync with values in system/core/audio.h
+ //
+
+ // reserved bits
+ public static final int DEVICE_BIT_IN = 0x80000000;
+ public static final int DEVICE_BIT_DEFAULT = 0x40000000;
// output devices, be sure to update AudioManager.java also
public static final int DEVICE_OUT_EARPIECE = 0x1;
public static final int DEVICE_OUT_SPEAKER = 0x2;
@@ -204,8 +211,10 @@ public class AudioSystem
public static final int DEVICE_OUT_DGTL_DOCK_HEADSET = 0x1000;
public static final int DEVICE_OUT_USB_ACCESSORY = 0x2000;
public static final int DEVICE_OUT_USB_DEVICE = 0x4000;
+ public static final int DEVICE_OUT_REMOTE_SUBMIX = 0x8000;
+
+ public static final int DEVICE_OUT_DEFAULT = DEVICE_BIT_DEFAULT;
- public static final int DEVICE_OUT_DEFAULT = 0x8000;
public static final int DEVICE_OUT_ALL = (DEVICE_OUT_EARPIECE |
DEVICE_OUT_SPEAKER |
DEVICE_OUT_WIRED_HEADSET |
@@ -221,6 +230,7 @@ public class AudioSystem
DEVICE_OUT_DGTL_DOCK_HEADSET |
DEVICE_OUT_USB_ACCESSORY |
DEVICE_OUT_USB_DEVICE |
+ DEVICE_OUT_REMOTE_SUBMIX |
DEVICE_OUT_DEFAULT);
public static final int DEVICE_OUT_ALL_A2DP = (DEVICE_OUT_BLUETOOTH_A2DP |
DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
@@ -232,15 +242,36 @@ public class AudioSystem
DEVICE_OUT_USB_DEVICE);
// input devices
- public static final int DEVICE_IN_COMMUNICATION = 0x10000;
- public static final int DEVICE_IN_AMBIENT = 0x20000;
- public static final int DEVICE_IN_BUILTIN_MIC1 = 0x40000;
- public static final int DEVICE_IN_BUILTIN_MIC2 = 0x80000;
- public static final int DEVICE_IN_MIC_ARRAY = 0x100000;
- public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET = 0x200000;
- public static final int DEVICE_IN_WIRED_HEADSET = 0x400000;
- public static final int DEVICE_IN_AUX_DIGITAL = 0x800000;
- public static final int DEVICE_IN_DEFAULT = 0x80000000;
+ public static final int DEVICE_IN_COMMUNICATION = DEVICE_BIT_IN | 0x1;
+ public static final int DEVICE_IN_AMBIENT = DEVICE_BIT_IN | 0x2;
+ public static final int DEVICE_IN_BUILTIN_MIC = DEVICE_BIT_IN | 0x4;
+ public static final int DEVICE_IN_BLUETOOTH_SCO_HEADSET = DEVICE_BIT_IN | 0x8;
+ public static final int DEVICE_IN_WIRED_HEADSET = DEVICE_BIT_IN | 0x10;
+ public static final int DEVICE_IN_AUX_DIGITAL = DEVICE_BIT_IN | 0x20;
+ public static final int DEVICE_IN_VOICE_CALL = DEVICE_BIT_IN | 0x40;
+ public static final int DEVICE_IN_BACK_MIC = DEVICE_BIT_IN | 0x80;
+ public static final int DEVICE_IN_REMOTE_SUBMIX = DEVICE_BIT_IN | 0x100;
+ public static final int DEVICE_IN_ANLG_DOCK_HEADSET = DEVICE_BIT_IN | 0x200;
+ public static final int DEVICE_IN_DGTL_DOCK_HEADSET = DEVICE_BIT_IN | 0x400;
+ public static final int DEVICE_IN_USB_ACCESSORY = DEVICE_BIT_IN | 0x800;
+ public static final int DEVICE_IN_USB_DEVICE = DEVICE_BIT_IN | 0x1000;
+ public static final int DEVICE_IN_DEFAULT = DEVICE_BIT_IN | DEVICE_BIT_DEFAULT;
+
+ public static final int DEVICE_IN_ALL = (DEVICE_IN_COMMUNICATION |
+ DEVICE_IN_AMBIENT |
+ DEVICE_IN_BUILTIN_MIC |
+ DEVICE_IN_BLUETOOTH_SCO_HEADSET |
+ DEVICE_IN_WIRED_HEADSET |
+ DEVICE_IN_AUX_DIGITAL |
+ DEVICE_IN_VOICE_CALL |
+ DEVICE_IN_BACK_MIC |
+ DEVICE_IN_REMOTE_SUBMIX |
+ DEVICE_IN_ANLG_DOCK_HEADSET |
+ DEVICE_IN_DGTL_DOCK_HEADSET |
+ DEVICE_IN_USB_ACCESSORY |
+ DEVICE_IN_USB_DEVICE |
+ DEVICE_IN_DEFAULT);
+ public static final int DEVICE_IN_ALL_SCO = DEVICE_IN_BLUETOOTH_SCO_HEADSET;
// device states, must match AudioSystem::device_connection_state
public static final int DEVICE_STATE_UNAVAILABLE = 0;
@@ -262,6 +293,7 @@ public class AudioSystem
public static final String DEVICE_OUT_DGTL_DOCK_HEADSET_NAME = "digital_dock";
public static final String DEVICE_OUT_USB_ACCESSORY_NAME = "usb_accessory";
public static final String DEVICE_OUT_USB_DEVICE_NAME = "usb_device";
+ public static final String DEVICE_OUT_REMOTE_SUBMIX_NAME = "remote_submix";
public static String getDeviceName(int device)
{
@@ -296,7 +328,9 @@ public class AudioSystem
return DEVICE_OUT_USB_ACCESSORY_NAME;
case DEVICE_OUT_USB_DEVICE:
return DEVICE_OUT_USB_DEVICE_NAME;
- case DEVICE_IN_DEFAULT:
+ case DEVICE_OUT_REMOTE_SUBMIX:
+ return DEVICE_OUT_REMOTE_SUBMIX_NAME;
+ case DEVICE_OUT_DEFAULT:
default:
return "";
}
@@ -319,7 +353,8 @@ public class AudioSystem
public static final int FORCE_ANALOG_DOCK = 8;
public static final int FORCE_DIGITAL_DOCK = 9;
public static final int FORCE_NO_BT_A2DP = 10;
- private static final int NUM_FORCE_CONFIG = 11;
+ public static final int FORCE_REMOTE_SUBMIX = 11;
+ private static final int NUM_FORCE_CONFIG = 12;
public static final int FORCE_DEFAULT = FORCE_NONE;
// usage for setForceUse, must match AudioSystem::force_use
@@ -346,4 +381,9 @@ public class AudioSystem
public static native int setMasterMute(boolean mute);
public static native boolean getMasterMute();
public static native int getDevicesForStream(int stream);
+
+ // helpers for android.media.AudioManager.getProperty(), see description there for meaning
+ public static native int getPrimaryOutputSamplingRate();
+ public static native int getPrimaryOutputFrameCount();
+
}
diff --git a/media/java/android/media/DataSource.java b/media/java/android/media/DataSource.java
new file mode 100644
index 0000000..347bd5f
--- /dev/null
+++ b/media/java/android/media/DataSource.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.media;
+
+import java.io.Closeable;
+
+/**
+ * An abstraction for a media data source, e.g. a file or an http stream
+ * {@hide}
+ */
+public interface DataSource extends Closeable {
+ /**
+ * Reads data from the data source at the requested position
+ *
+ * @param offset where in the source to read
+ * @param buffer the buffer to read the data into
+ * @param size how many bytes to read
+ * @return the number of bytes read, or -1 if there was an error
+ */
+ public int readAt(long offset, byte[] buffer, int size);
+
+ /**
+ * Gets the size of the data source.
+ *
+ * @return size of data source, or -1 if the length is unknown
+ */
+ public long getSize();
+}
diff --git a/media/java/android/media/IAudioService.aidl b/media/java/android/media/IAudioService.aidl
index 854eb3f..7ae61cd 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -108,6 +108,8 @@ interface IAudioService {
boolean isBluetoothA2dpOn();
+ oneway void setRemoteSubmixOn(boolean on, int address);
+
int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher l,
String clientId, String callingPackageName);
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
index 687d3a5..749ef12 100644
--- a/media/java/android/media/MediaExtractor.java
+++ b/media/java/android/media/MediaExtractor.java
@@ -22,6 +22,7 @@ import android.content.res.AssetFileDescriptor;
import android.media.MediaCodec;
import android.media.MediaFormat;
import android.net.Uri;
+
import java.io.FileDescriptor;
import java.io.IOException;
import java.nio.ByteBuffer;
@@ -44,7 +45,7 @@ import java.util.Map;
* }
* ByteBuffer inputBuffer = ByteBuffer.allocate(...)
* while (extractor.readSampleData(inputBuffer, ...) &gt;= 0) {
- * int trackIndex = extractor.getTrackIndex();
+ * int trackIndex = extractor.getSampleTrackIndex();
* long presentationTimeUs = extractor.getSampleTime();
* ...
* extractor.advance();
@@ -60,6 +61,12 @@ final public class MediaExtractor {
}
/**
+ * Sets the DataSource object to be used as the data source for this extractor
+ * {@hide}
+ */
+ public native final void setDataSource(DataSource source);
+
+ /**
* Sets the data source as a content Uri.
*
* @param context the Context to use when resolving the Uri
diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java
index d21ada4..06d43a2 100644
--- a/media/java/android/media/MediaFile.java
+++ b/media/java/android/media/MediaFile.java
@@ -325,7 +325,7 @@ public class MediaFile {
}
int lastDot = fileName.lastIndexOf('.');
if (lastDot > 0) {
- String extension = fileName.substring(lastDot + 1);
+ String extension = fileName.substring(lastDot + 1).toUpperCase();
Integer value = sFileTypeToFormatMap.get(extension);
if (value != null) {
return value.intValue();
diff --git a/media/java/android/media/MediaMetadataRetriever.java b/media/java/android/media/MediaMetadataRetriever.java
index aef631f..cc59d02 100644
--- a/media/java/android/media/MediaMetadataRetriever.java
+++ b/media/java/android/media/MediaMetadataRetriever.java
@@ -483,5 +483,10 @@ public class MediaMetadataRetriever
* of 180 degrees will be retrieved as "-90.0000+180.0000", for instance.
*/
public static final int METADATA_KEY_LOCATION = 23;
+ /**
+ * This key retrieves the video rotation angle in degrees, if available.
+ * The video rotation angle may be 0, 90, 180, or 270 degrees.
+ */
+ public static final int METADATA_KEY_VIDEO_ROTATION = 24;
// Add more here...
}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index cd25865..ef0da3a 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -2265,6 +2265,16 @@ public class MediaPlayer
*/
public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = 200;
+ /** File or network related operation errors. */
+ public static final int MEDIA_ERROR_IO = -1004;
+ /** Bitstream is not conforming to the related coding standard or file spec. */
+ public static final int MEDIA_ERROR_MALFORMED = -1007;
+ /** Bitstream is conforming to the related coding standard or file spec, but
+ * the media framework does not support the feature. */
+ public static final int MEDIA_ERROR_UNSUPPORTED = -1010;
+ /** Some operation takes too long to complete, usually more than 3-5 seconds. */
+ public static final int MEDIA_ERROR_TIMED_OUT = -110;
+
/**
* Interface definition of a callback to be invoked when there
* has been an error during an asynchronous operation (other errors
@@ -2282,7 +2292,13 @@ public class MediaPlayer
* <li>{@link #MEDIA_ERROR_SERVER_DIED}
* </ul>
* @param extra an extra code, specific to the error. Typically
- * implementation dependant.
+ * implementation dependent.
+ * <ul>
+ * <li>{@link #MEDIA_ERROR_IO}
+ * <li>{@link #MEDIA_ERROR_MALFORMED}
+ * <li>{@link #MEDIA_ERROR_UNSUPPORTED}
+ * <li>{@link #MEDIA_ERROR_TIMED_OUT}
+ * </ul>
* @return True if the method handled the error, false if it didn't.
* Returning false, or not having an OnErrorListener at all, will
* cause the OnCompletionListener to be called.
@@ -2319,6 +2335,11 @@ public class MediaPlayer
*/
public static final int MEDIA_INFO_STARTED_AS_NEXT = 2;
+ /** The player just pushed the very first video frame for rendering.
+ * @see android.media.MediaPlayer.OnInfoListener
+ */
+ public static final int MEDIA_INFO_VIDEO_RENDERING_START = 3;
+
/** The video is too complex for the decoder: it can't decode frames fast
* enough. Possibly only the audio plays fine at this stage.
* @see android.media.MediaPlayer.OnInfoListener
@@ -2374,6 +2395,7 @@ public class MediaPlayer
* <ul>
* <li>{@link #MEDIA_INFO_UNKNOWN}
* <li>{@link #MEDIA_INFO_VIDEO_TRACK_LAGGING}
+ * <li>{@link #MEDIA_INFO_VIDEO_RENDERING_START}
* <li>{@link #MEDIA_INFO_BUFFERING_START}
* <li>{@link #MEDIA_INFO_BUFFERING_END}
* <li>{@link #MEDIA_INFO_BAD_INTERLEAVING}
@@ -2381,7 +2403,7 @@ public class MediaPlayer
* <li>{@link #MEDIA_INFO_METADATA_UPDATE}
* </ul>
* @param extra an extra code, specific to the info. Typically
- * implementation dependant.
+ * implementation dependent.
* @return True if the method handled the info, false if it didn't.
* Returning false, or not having an OnErrorListener at all, will
* cause the info to be discarded.
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 613354f..48bea52 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -177,6 +177,12 @@ public class MediaRecorder
* is applied.
*/
public static final int VOICE_COMMUNICATION = 7;
+
+ /**
+ * @hide
+ * Audio source for remote submix.
+ */
+ public static final int REMOTE_SUBMIX_SOURCE = 8;
}
/**
@@ -291,7 +297,12 @@ public class MediaRecorder
* Gets the maximum value for audio sources.
* @see android.media.MediaRecorder.AudioSource
*/
- public static final int getAudioSourceMax() { return AudioSource.VOICE_COMMUNICATION; }
+ public static final int getAudioSourceMax() {
+ // FIXME disable selection of the remote submxi source selection once test code
+ // doesn't rely on it
+ return AudioSource.REMOTE_SUBMIX_SOURCE;
+ //return AudioSource.VOICE_COMMUNICATION;
+ }
/**
* Sets the video source to be used for recording. If this method is not
@@ -720,12 +731,17 @@ public class MediaRecorder
public native int getMaxAmplitude() throws IllegalStateException;
/* Do not change this value without updating its counterpart
- * in include/media/mediarecorder.h!
+ * in include/media/mediarecorder.h or mediaplayer.h!
*/
/** Unspecified media recorder error.
* @see android.media.MediaRecorder.OnErrorListener
*/
public static final int MEDIA_RECORDER_ERROR_UNKNOWN = 1;
+ /** Media server died. In this case, the application must release the
+ * MediaRecorder object and instantiate a new one.
+ * @see android.media.MediaRecorder.OnErrorListener
+ */
+ public static final int MEDIA_ERROR_SERVER_DIED = 100;
/**
* Interface definition for a callback to be invoked when an error
@@ -740,6 +756,7 @@ public class MediaRecorder
* @param what the type of error that has occurred:
* <ul>
* <li>{@link #MEDIA_RECORDER_ERROR_UNKNOWN}
+ * <li>{@link #MEDIA_ERROR_SERVER_DIED}
* </ul>
* @param extra an extra code, specific to the error type
*/
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index fd37bcf..88cf4ac 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -314,6 +314,7 @@ public class MediaScanner
private int mMtpObjectHandle;
private final String mExternalStoragePath;
+ private final boolean mExternalIsEmulated;
/** whether to use bulk inserts or individual inserts for each item */
private static final boolean ENABLE_BULK_INSERTS = true;
@@ -392,6 +393,7 @@ public class MediaScanner
setDefaultRingtoneFileNames();
mExternalStoragePath = Environment.getExternalStorageDirectory().getAbsolutePath();
+ mExternalIsEmulated = Environment.isExternalStorageEmulated();
//mClient.testGenreNameConverter();
}
@@ -543,13 +545,28 @@ public class MediaScanner
boolean music = (lowpath.indexOf(MUSIC_DIR) > 0) ||
(!ringtones && !notifications && !alarms && !podcasts);
+ boolean isaudio = MediaFile.isAudioFileType(mFileType);
+ boolean isvideo = MediaFile.isVideoFileType(mFileType);
+ boolean isimage = MediaFile.isImageFileType(mFileType);
+
+ if (isaudio || isvideo || isimage) {
+ if (mExternalIsEmulated && path.startsWith(mExternalStoragePath)) {
+ // try to rewrite the path to bypass the sd card fuse layer
+ String directPath = Environment.getMediaStorageDirectory() +
+ path.substring(mExternalStoragePath.length());
+ File f = new File(directPath);
+ if (f.exists()) {
+ path = directPath;
+ }
+ }
+ }
+
// we only extract metadata for audio and video files
- if (MediaFile.isAudioFileType(mFileType)
- || MediaFile.isVideoFileType(mFileType)) {
+ if (isaudio || isvideo) {
processFile(path, mimeType, this);
}
- if (MediaFile.isImageFileType(mFileType)) {
+ if (isimage) {
processImageFile(path);
}
@@ -972,7 +989,6 @@ public class MediaScanner
}
values.put(FileColumns.MEDIA_TYPE, mediaType);
}
-
mMediaProvider.update(result, values, null, null);
}
@@ -1448,24 +1464,42 @@ public class MediaScanner
}
FileEntry makeEntryFor(String path) {
- String key = path;
String where;
String[] selectionArgs;
- if (mCaseInsensitivePaths) {
- // the 'like' makes it use the index, the 'lower()' makes it correct
- // when the path contains sqlite wildcard characters
- where = "_data LIKE ?1 AND lower(_data)=lower(?1)";
- selectionArgs = new String[] { path };
- } else {
- where = Files.FileColumns.DATA + "=?";
- selectionArgs = new String[] { path };
- }
Cursor c = null;
try {
+ boolean hasWildCards = path.contains("_") || path.contains("%");
+
+ if (hasWildCards || !mCaseInsensitivePaths) {
+ // if there are wildcard characters in the path, the "like" match
+ // will be slow, and it's worth trying an "=" comparison
+ // first, since in most cases the case will match.
+ // Also, we shouldn't do a "like" match on case-sensitive filesystems
+ where = Files.FileColumns.DATA + "=?";
+ selectionArgs = new String[] { path };
+ } else {
+ // if there are no wildcard characters in the path, then the "like"
+ // match will be just as fast as the "=" case, because of the index
+ where = "_data LIKE ?1 AND lower(_data)=lower(?1)";
+ selectionArgs = new String[] { path };
+ }
c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
where, selectionArgs, null, null);
- if (c.moveToNext()) {
+ if (!c.moveToFirst() && hasWildCards && mCaseInsensitivePaths) {
+ // Try again with case-insensitive match. This will be slower, especially
+ // if the path contains wildcard characters.
+ // The 'like' makes it use the index, the 'lower()' makes it correct
+ // when the path contains sqlite wildcard characters,
+ where = "_data LIKE ?1 AND lower(_data)=lower(?1)";
+ selectionArgs = new String[] { path };
+ c.close();
+ c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
+ where, selectionArgs, null, null);
+ // TODO update the path in the db with the correct case so the fast
+ // path works next time?
+ }
+ if (c.moveToFirst()) {
long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
diff --git a/media/java/android/media/RemoteDisplay.java b/media/java/android/media/RemoteDisplay.java
new file mode 100644
index 0000000..b463d26
--- /dev/null
+++ b/media/java/android/media/RemoteDisplay.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import dalvik.system.CloseGuard;
+
+import android.os.Handler;
+import android.view.Surface;
+
+/**
+ * Listens for Wifi remote display connections managed by the media server.
+ *
+ * @hide
+ */
+public final class RemoteDisplay {
+ /* these constants must be kept in sync with IRemoteDisplayClient.h */
+
+ public static final int DISPLAY_FLAG_SECURE = 1 << 0;
+
+ public static final int DISPLAY_ERROR_UNKOWN = 1;
+ public static final int DISPLAY_ERROR_CONNECTION_DROPPED = 2;
+
+ private final CloseGuard mGuard = CloseGuard.get();
+ private final Listener mListener;
+ private final Handler mHandler;
+
+ private int mPtr;
+
+ private native int nativeListen(String iface);
+ private native void nativeDispose(int ptr);
+
+ private RemoteDisplay(Listener listener, Handler handler) {
+ mListener = listener;
+ mHandler = handler;
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ dispose(true);
+ } finally {
+ super.finalize();
+ }
+ }
+
+ /**
+ * Starts listening for displays to be connected on the specified interface.
+ *
+ * @param iface The interface address and port in the form "x.x.x.x:y".
+ * @param listener The listener to invoke when displays are connected or disconnected.
+ * @param handler The handler on which to invoke the listener.
+ */
+ public static RemoteDisplay listen(String iface, Listener listener, Handler handler) {
+ if (iface == null) {
+ throw new IllegalArgumentException("iface must not be null");
+ }
+ if (listener == null) {
+ throw new IllegalArgumentException("listener must not be null");
+ }
+ if (handler == null) {
+ throw new IllegalArgumentException("handler must not be null");
+ }
+
+ RemoteDisplay display = new RemoteDisplay(listener, handler);
+ display.startListening(iface);
+ return display;
+ }
+
+ /**
+ * Disconnects the remote display and stops listening for new connections.
+ */
+ public void dispose() {
+ dispose(false);
+ }
+
+ private void dispose(boolean finalized) {
+ if (mPtr != 0) {
+ if (mGuard != null) {
+ if (finalized) {
+ mGuard.warnIfOpen();
+ } else {
+ mGuard.close();
+ }
+ }
+
+ nativeDispose(mPtr);
+ mPtr = 0;
+ }
+ }
+
+ private void startListening(String iface) {
+ mPtr = nativeListen(iface);
+ if (mPtr == 0) {
+ throw new IllegalStateException("Could not start listening for "
+ + "remote display connection on \"" + iface + "\"");
+ }
+ mGuard.open("dispose");
+ }
+
+ // Called from native.
+ private void notifyDisplayConnected(final Surface surface,
+ final int width, final int height, final int flags) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayConnected(surface, width, height, flags);
+ }
+ });
+ }
+
+ // Called from native.
+ private void notifyDisplayDisconnected() {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayDisconnected();
+ }
+ });
+ }
+
+ // Called from native.
+ private void notifyDisplayError(final int error) {
+ mHandler.post(new Runnable() {
+ @Override
+ public void run() {
+ mListener.onDisplayError(error);
+ }
+ });
+ }
+
+ /**
+ * Listener invoked when the remote display connection changes state.
+ */
+ public interface Listener {
+ void onDisplayConnected(Surface surface, int width, int height, int flags);
+ void onDisplayDisconnected();
+ void onDisplayError(int error);
+ }
+}
diff --git a/media/java/android/mtp/MtpDatabase.java b/media/java/android/mtp/MtpDatabase.java
index a0325dd..487585e 100755
--- a/media/java/android/mtp/MtpDatabase.java
+++ b/media/java/android/mtp/MtpDatabase.java
@@ -84,11 +84,10 @@ public class MtpDatabase {
Files.FileColumns._ID, // 0
Files.FileColumns.DATA, // 1
};
- private static final String[] PATH_SIZE_FORMAT_PROJECTION = new String[] {
+ private static final String[] PATH_FORMAT_PROJECTION = new String[] {
Files.FileColumns._ID, // 0
Files.FileColumns.DATA, // 1
- Files.FileColumns.SIZE, // 2
- Files.FileColumns.FORMAT, // 3
+ Files.FileColumns.FORMAT, // 2
};
private static final String[] OBJECT_INFO_PROJECTION = new String[] {
Files.FileColumns._ID, // 0
@@ -96,15 +95,14 @@ public class MtpDatabase {
Files.FileColumns.FORMAT, // 2
Files.FileColumns.PARENT, // 3
Files.FileColumns.DATA, // 4
- Files.FileColumns.SIZE, // 5
- Files.FileColumns.DATE_MODIFIED, // 6
+ Files.FileColumns.DATE_MODIFIED, // 5
};
private static final String ID_WHERE = Files.FileColumns._ID + "=?";
private static final String PATH_WHERE = Files.FileColumns.DATA + "=?";
private static final String STORAGE_WHERE = Files.FileColumns.STORAGE_ID + "=?";
- private static final String FORMAT_WHERE = Files.FileColumns.PARENT + "=?";
- private static final String PARENT_WHERE = Files.FileColumns.FORMAT + "=?";
+ private static final String FORMAT_WHERE = Files.FileColumns.FORMAT + "=?";
+ private static final String PARENT_WHERE = Files.FileColumns.PARENT + "=?";
private static final String STORAGE_FORMAT_WHERE = STORAGE_WHERE + " AND "
+ Files.FileColumns.FORMAT + "=?";
private static final String STORAGE_PARENT_WHERE = STORAGE_WHERE + " AND "
@@ -835,7 +833,7 @@ public class MtpDatabase {
}
private boolean getObjectInfo(int handle, int[] outStorageFormatParent,
- char[] outName, long[] outSizeModified) {
+ char[] outName, long[] outModified) {
Cursor c = null;
try {
c = mMediaProvider.query(mObjectsUri, OBJECT_INFO_PROJECTION,
@@ -856,8 +854,7 @@ public class MtpDatabase {
path.getChars(start, end, outName, 0);
outName[end - start] = 0;
- outSizeModified[0] = c.getLong(5);
- outSizeModified[1] = c.getLong(6);
+ outModified[0] = c.getLong(5);
return true;
}
} catch (RemoteException e) {
@@ -881,14 +878,16 @@ public class MtpDatabase {
}
Cursor c = null;
try {
- c = mMediaProvider.query(mObjectsUri, PATH_SIZE_FORMAT_PROJECTION,
+ c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
String path = c.getString(1);
path.getChars(0, path.length(), outFilePath, 0);
outFilePath[path.length()] = 0;
- outFileLengthFormat[0] = c.getLong(2);
- outFileLengthFormat[1] = c.getLong(3);
+ // File transfers from device to host will likely fail if the size is incorrect.
+ // So to be safe, use the actual file size here.
+ outFileLengthFormat[0] = new File(path).length();
+ outFileLengthFormat[1] = c.getLong(2);
return MtpConstants.RESPONSE_OK;
} else {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
@@ -910,13 +909,13 @@ public class MtpDatabase {
Cursor c = null;
try {
- c = mMediaProvider.query(mObjectsUri, PATH_SIZE_FORMAT_PROJECTION,
+ c = mMediaProvider.query(mObjectsUri, PATH_FORMAT_PROJECTION,
ID_WHERE, new String[] { Integer.toString(handle) }, null, null);
if (c != null && c.moveToNext()) {
// don't convert to media path here, since we will be matching
// against paths in the database matching /data/media
path = c.getString(1);
- format = c.getInt(3);
+ format = c.getInt(2);
} else {
return MtpConstants.RESPONSE_INVALID_OBJECT_HANDLE;
}