summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/java/android/media/AudioManager.java89
-rw-r--r--media/java/android/media/AudioService.java1053
-rw-r--r--media/java/android/media/AudioSystem.java74
-rw-r--r--media/java/android/media/DataSource.java43
-rw-r--r--media/java/android/media/IAudioService.aidl4
-rw-r--r--media/java/android/media/IRingtonePlayer.aidl3
-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/MediaRouter.java508
-rw-r--r--media/java/android/media/MediaScanner.java64
-rw-r--r--media/java/android/media/RemoteDisplay.java153
-rw-r--r--media/java/android/media/Ringtone.java3
-rw-r--r--media/java/android/media/videoeditor/MediaArtistNativeHelper.java1
-rwxr-xr-xmedia/java/android/mtp/MtpDatabase.java29
-rw-r--r--media/jni/android_media_MediaCodec.cpp2
-rw-r--r--media/jni/android_media_MediaExtractor.cpp100
-rw-r--r--media/jni/android_media_MediaExtractor.h2
-rw-r--r--media/jni/android_media_MediaPlayer.cpp7
-rw-r--r--media/jni/android_mtp_MtpDatabase.cpp56
-rw-r--r--media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java3
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java6
-rw-r--r--media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java13
-rwxr-xr-xmedia/tests/EffectsTest/Android.mk10
-rwxr-xr-xmedia/tests/EffectsTest/AndroidManifest.xml57
-rwxr-xr-xmedia/tests/EffectsTest/res/drawable/icon.pngbin0 -> 6094 bytes
-rwxr-xr-xmedia/tests/EffectsTest/res/drawable/stop.pngbin0 -> 369 bytes
-rwxr-xr-xmedia/tests/EffectsTest/res/layout/bassboosttest.xml194
-rwxr-xr-xmedia/tests/EffectsTest/res/layout/effectstest.xml58
-rwxr-xr-xmedia/tests/EffectsTest/res/layout/envreverbtest.xml553
-rwxr-xr-xmedia/tests/EffectsTest/res/layout/equalizertest.xml470
-rwxr-xr-xmedia/tests/EffectsTest/res/layout/presetreverbtest.xml179
-rwxr-xr-xmedia/tests/EffectsTest/res/layout/virtualizertest.xml194
-rwxr-xr-xmedia/tests/EffectsTest/res/layout/visualizertest.xml263
-rw-r--r--media/tests/EffectsTest/res/raw/mp3_sample.mp3bin0 -> 1436942 bytes
-rw-r--r--media/tests/EffectsTest/res/raw/sine440_mo_16b_16k.wavbin0 -> 320044 bytes
-rwxr-xr-xmedia/tests/EffectsTest/res/values/strings.xml38
-rwxr-xr-xmedia/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java276
-rwxr-xr-xmedia/tests/EffectsTest/src/com/android/effectstest/EffectParameter.java89
-rwxr-xr-xmedia/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java208
-rwxr-xr-xmedia/tests/EffectsTest/src/com/android/effectstest/EnvReverbTest.java568
-rwxr-xr-xmedia/tests/EffectsTest/src/com/android/effectstest/EqualizerTest.java407
-rwxr-xr-xmedia/tests/EffectsTest/src/com/android/effectstest/PresetReverbTest.java298
-rw-r--r--media/tests/EffectsTest/src/com/android/effectstest/SimplePlayer.java197
-rwxr-xr-xmedia/tests/EffectsTest/src/com/android/effectstest/VirtualizerTest.java271
-rwxr-xr-xmedia/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java296
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java8
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java4
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java58
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java67
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java189
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java341
-rw-r--r--[-rwxr-xr-x]media/tests/contents/media_api/music/sine_200+1000Hz_44K_mo.wavbin265214 -> 794414 bytes
55 files changed, 6950 insertions, 619 deletions
diff --git a/media/java/android/media/AudioManager.java b/media/java/android/media/AudioManager.java
index b6e4659..035b282 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
@@ -1503,6 +1526,16 @@ public class AudioManager {
/**
* @hide
+ * Checks whether speech recognition is active
+ * @return true if a recording with source {@link MediaRecorder.AudioSource#VOICE_RECOGNITION}
+ * is underway.
+ */
+ public boolean isSpeechRecognitionActive() {
+ return AudioSystem.isSourceActive(MediaRecorder.AudioSource.VOICE_RECOGNITION);
+ }
+
+ /**
+ * @hide
* If the stream is active locally or remotely, adjust its volume according to the enforced
* priority rules.
* Note: only AudioManager.STREAM_MUSIC is supported at the moment
@@ -2427,4 +2460,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 a993381..f0fdfc1 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -59,11 +59,13 @@ 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;
import android.speech.RecognizerIntent;
import android.telephony.PhoneStateListener;
+import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
@@ -149,12 +151,18 @@ 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_CHECK_MUSIC_ACTIVE = 24;
+ private static final int MSG_BROADCAST_AUDIO_BECOMING_NOISY = 25;
+ private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME = 26;
+ private static final int MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED = 27;
// flags for MSG_PERSIST_VOLUME indicating if current and/or last audible volume should be
// persisted
@@ -387,10 +395,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
@@ -422,6 +432,16 @@ 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_ANLG_DOCK_HEADSET |
+ AudioSystem.DEVICE_OUT_ALL_USB;
+
+ private final boolean mMonitorOrientation;
+
+ private boolean mDockAudioMediaEnabled = true;
+
///////////////////////////////////////////////////////////////////////////
// Construction
///////////////////////////////////////////////////////////////////////////
@@ -444,13 +464,27 @@ 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);
mMode = AudioSystem.MODE_NORMAL;
mForcedUseForComm = AudioSystem.FORCE_NONE;
+
createAudioSystemThread();
+
+ boolean cameraSoundForced = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_camera_sound_forced);
+ mCameraSoundForced = new Boolean(cameraSoundForced);
+ sendMsg(mAudioHandler,
+ MSG_SET_FORCE_USE,
+ SENDMSG_QUEUE,
+ AudioSystem.FOR_SYSTEM,
+ cameraSoundForced ?
+ AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE,
+ null,
+ 0);
+
readPersistedSettings();
mSettingsObserver = new SettingsObserver();
updateStreamVolumeAlias(false /*updateVolumes*/);
@@ -458,6 +492,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mMediaServerOk = true;
+ mSafeMediaVolumeState = new Integer(SAFE_MEDIA_VOLUME_NOT_CONFIGURED);
+
// Call setRingerModeInt() to apply correct mute
// state on streams affected by ringer mode.
mRingerModeMutedStreams = 0;
@@ -475,12 +511,14 @@ 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);
+ intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
// Register a configuration change listener only if requested by system properties
// to monitor orientation changes (off by default)
- if (SystemProperties.getBoolean("ro.audio.monitorOrientation", false)) {
+ mMonitorOrientation = SystemProperties.getBoolean("ro.audio.monitorOrientation", false);
+ if (mMonitorOrientation) {
Log.v(TAG, "monitoring device orientation");
- intentFilter.addAction(Intent.ACTION_CONFIGURATION_CHANGED);
// initialize orientation in AudioSystem
setOrientationForAudioSystem();
}
@@ -569,6 +607,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mStreamStates[i].dump(pw);
pw.println("");
}
+ pw.print("\n- mute affected streams = 0x");
+ pw.println(Integer.toHexString(mMuteAffectedStreams));
}
@@ -599,11 +639,33 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
+ private void readDockAudioSettings(ContentResolver cr)
+ {
+ mDockAudioMediaEnabled = Settings.Global.getInt(
+ cr, Settings.Global.DOCK_AUDIO_MEDIA_ENABLED, 0) == 1;
+
+ if (mDockAudioMediaEnabled) {
+ mBecomingNoisyIntentDevices |= AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
+ } else {
+ mBecomingNoisyIntentDevices &= ~AudioSystem.DEVICE_OUT_ANLG_DOCK_HEADSET;
+ }
+
+ sendMsg(mAudioHandler,
+ MSG_SET_FORCE_USE,
+ SENDMSG_QUEUE,
+ AudioSystem.FOR_DOCK,
+ mDockAudioMediaEnabled ?
+ AudioSystem.FORCE_ANALOG_DOCK : AudioSystem.FORCE_NONE,
+ null,
+ 0);
+ }
+
private void readPersistedSettings() {
final ContentResolver cr = mContentResolver;
int ringerModeFromSettings =
- System.getInt(cr, System.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
+ Settings.Global.getInt(
+ cr, Settings.Global.MODE_RINGER, AudioManager.RINGER_MODE_NORMAL);
int ringerMode = ringerModeFromSettings;
// sanity check in case the settings are restored from a device with incompatible
// ringer modes
@@ -614,42 +676,66 @@ 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, Settings.Global.MODE_RINGER, ringerMode);
}
synchronized(mSettingsLock) {
mRingerMode = ringerMode;
- }
- // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting
- // are still needed while setVibrateSetting() and getVibrateSetting() are being deprecated.
- mVibrateSetting = getValueForVibrateSetting(0,
- AudioManager.VIBRATE_TYPE_NOTIFICATION,
- mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
- : AudioManager.VIBRATE_SETTING_OFF);
- mVibrateSetting = getValueForVibrateSetting(mVibrateSetting,
- AudioManager.VIBRATE_TYPE_RINGER,
- mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
- : AudioManager.VIBRATE_SETTING_OFF);
-
- // 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,
- Settings.System.MODE_RINGER_STREAMS_AFFECTED,
- ((1 << AudioSystem.STREAM_RING)|(1 << AudioSystem.STREAM_NOTIFICATION)|
- (1 << AudioSystem.STREAM_SYSTEM)|(1 << AudioSystem.STREAM_SYSTEM_ENFORCED)));
- if (mVoiceCapable) {
- mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
- } else {
- mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
+ // System.VIBRATE_ON is not used any more but defaults for mVibrateSetting
+ // are still needed while setVibrateSetting() and getVibrateSetting() are being
+ // deprecated.
+ mVibrateSetting = getValueForVibrateSetting(0,
+ AudioManager.VIBRATE_TYPE_NOTIFICATION,
+ mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
+ : AudioManager.VIBRATE_SETTING_OFF);
+ mVibrateSetting = getValueForVibrateSetting(mVibrateSetting,
+ AudioManager.VIBRATE_TYPE_RINGER,
+ mHasVibrator ? AudioManager.VIBRATE_SETTING_ONLY_SILENT
+ : AudioManager.VIBRATE_SETTING_OFF);
+
+ // 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.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)),
+ UserHandle.USER_CURRENT);
+
+ // ringtone, notification and system streams are always affected by ringer mode
+ mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_RING)|
+ (1 << AudioSystem.STREAM_NOTIFICATION)|
+ (1 << AudioSystem.STREAM_SYSTEM);
+
+ if (mVoiceCapable) {
+ mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_MUSIC);
+ } else {
+ mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
+ }
+ synchronized (mCameraSoundForced) {
+ if (mCameraSoundForced) {
+ mRingerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ } else {
+ mRingerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ }
+ }
+
+ Settings.System.putIntForUser(cr,
+ Settings.System.MODE_RINGER_STREAMS_AFFECTED,
+ mRingerModeAffectedStreams,
+ UserHandle.USER_CURRENT);
+
+ readDockAudioSettings(cr);
}
- Settings.System.putInt(cr,
- Settings.System.MODE_RINGER_STREAMS_AFFECTED, mRingerModeAffectedStreams);
- 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);
@@ -710,7 +796,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
if (streamType == STREAM_REMOTE_MUSIC) {
// don't play sounds for remote
- flags &= ~AudioManager.FLAG_PLAY_SOUND;
+ flags &= ~(AudioManager.FLAG_PLAY_SOUND|AudioManager.FLAG_FIXED_VOLUME);
//if (DEBUG_VOL) Log.i(TAG, "Need to adjust remote volume: calling adjustRemoteVolume()");
adjustRemoteVolume(AudioSystem.STREAM_MUSIC, direction, flags);
} else {
@@ -740,58 +826,72 @@ 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 */);
-
- 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 */);
+ int oldIndex;
+
+ flags &= ~AudioManager.FLAG_FIXED_VOLUME;
+ 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);
}
@@ -817,35 +917,48 @@ 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;
+
+ flags &= ~AudioManager.FLAG_FIXED_VOLUME;
+ 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]);
-
- // 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;
+ 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);
}
@@ -935,6 +1048,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)) {
@@ -943,13 +1074,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
@@ -959,7 +1092,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
@@ -973,9 +1106,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);
}
/**
@@ -1066,7 +1197,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() {
@@ -1206,8 +1345,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);
}
@@ -1643,6 +1782,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();
@@ -1651,11 +1794,16 @@ 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();
// unmute stream that was muted but is not affect by mute anymore
- if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType)) {
+ if (streamState.muteCount() != 0 && !isStreamAffectedByMute(streamType) &&
+ !isStreamMutedByRingerMode(streamType)) {
int size = streamState.mDeathHandlers.size();
for (int i = 0; i < size; i++) {
streamState.mDeathHandlers.get(i).mMuteCount = 1;
@@ -1665,10 +1813,17 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
}
+ // apply new ringer mode before checking volume for alias streams so that streams
+ // muted by ringer mode have the correct volume
+ setRingerModeInt(getRingerMode(), false);
+
checkAllAliasStreamVolumes();
- // apply new ringer mode
- setRingerModeInt(getRingerMode(), false);
+ synchronized (mSafeMediaVolumeState) {
+ if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) {
+ enforceSafeMediaVolume();
+ }
+ }
}
/** @see AudioManager#setSpeakerphoneOn() */
@@ -1707,7 +1862,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() */
@@ -1991,7 +2152,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;
}
}
@@ -2091,6 +2252,75 @@ 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*/);
+ }
+
+ 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 (mSafeMediaVolumeState) {
+ if (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE) {
+ 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();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ private void onConfigureSafeVolume(boolean force) {
+ synchronized (mSafeMediaVolumeState) {
+ int mcc = mContext.getResources().getConfiguration().mcc;
+ if ((mMcc != mcc) || ((mMcc == 0) && force)) {
+ mSafeMediaVolumeIndex = mContext.getResources().getInteger(
+ com.android.internal.R.integer.config_safe_media_volume_index) * 10;
+ boolean safeMediaVolumeEnabled = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_safe_media_volume_enabled);
+ if (safeMediaVolumeEnabled) {
+ mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
+ enforceSafeMediaVolume();
+ } else {
+ mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_DISABLED;
+ }
+ mMcc = mcc;
+ }
+ }
+ }
+
///////////////////////////////////////////////////////////////////////////
// Internal methods
///////////////////////////////////////////////////////////////////////////
@@ -2223,7 +2453,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;
@@ -2252,9 +2483,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) {
@@ -2282,9 +2513,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) {
@@ -2293,7 +2522,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);
}
}
@@ -2403,9 +2632,10 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
AudioSystem.initStreamVolume(streamType, 0, mIndexMax);
mIndexMax *= 10;
- readSettings();
-
+ // mDeathHandlers must be created before calling readSettings()
mDeathHandlers = new ArrayList<VolumeDeathHandler>();
+
+ readSettings();
}
public String getSettingNameForDevice(boolean lastAudible, int device) {
@@ -2422,6 +2652,26 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
public synchronized void readSettings() {
int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
+ // do not read system stream volume from settings: this stream is always aliased
+ // to another stream type and its volume is never persisted. Values in settings can
+ // only be stale values
+ // on first call to readSettings() at init time, muteCount() is always 0 so we will
+ // always create entries for default device
+ if ((mStreamType == AudioSystem.STREAM_SYSTEM) ||
+ (mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED)) {
+ int index = 10 * AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
+ synchronized (mCameraSoundForced) {
+ if (mCameraSoundForced) {
+ index = mIndexMax;
+ }
+ }
+ if (muteCount() == 0) {
+ mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
+ }
+ mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
+ return;
+ }
+
for (int i = 0; remainingDevices != 0; i++) {
int device = (1 << i);
if ((device & remainingDevices) == 0) {
@@ -2429,13 +2679,23 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
remainingDevices &= ~device;
+ // ignore settings for fixed volume devices: volume should always be at max
+ if ((mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_MUSIC) &&
+ ((device & mFixedVolumeDevices) != 0)) {
+ if (muteCount() == 0) {
+ mIndex.put(device, mIndexMax);
+ }
+ mLastAudibleIndex.put(device, mIndexMax);
+ continue;
+ }
// retrieve current volume for device
String name = getSettingNameForDevice(false /* lastAudible */, device);
// if no volume stored for current stream and device, use default volume if default
// 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;
}
@@ -2446,15 +2706,13 @@ 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).
- // same for system stream on phones and tablets
- if ((lastAudibleIndex == 0) &&
- ((mVoiceCapable &&
- (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
- (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
+ if ((lastAudibleIndex == 0) && mVoiceCapable &&
+ (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) {
lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
// Correct the data base
sendMsg(mAudioHandler,
@@ -2468,11 +2726,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex));
// the initial index should never be 0 for ring and notification streams on phones
// (voice capable devices) if not in silent or vibrate mode.
- // same for system stream on phones and tablets
if ((index == 0) && (mRingerMode == AudioManager.RINGER_MODE_NORMAL) &&
- ((mVoiceCapable &&
- (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) ||
- (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_SYSTEM))) {
+ mVoiceCapable &&
+ (mStreamVolumeAlias[mStreamType] == AudioSystem.STREAM_RING)) {
index = lastAudibleIndex;
// Correct the data base
sendMsg(mAudioHandler,
@@ -2483,7 +2739,9 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
this,
PERSIST_DELAY);
}
- mIndex.put(device, getValidIndex(10 * index));
+ if (muteCount() == 0) {
+ mIndex.put(device, getValidIndex(10 * index));
+ }
}
}
@@ -2523,6 +2781,11 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
public synchronized boolean setIndex(int index, int device, boolean lastAudible) {
int oldIndex = getIndex(device, false /* lastAudible */);
index = getValidIndex(index);
+ synchronized (mCameraSoundForced) {
+ if ((mStreamType == AudioSystem.STREAM_SYSTEM_ENFORCED) && mCameraSoundForced) {
+ index = mIndexMax;
+ }
+ }
mIndex.put(device, getValidIndex(index));
if (oldIndex != index) {
@@ -2617,7 +2880,27 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
int device = ((Integer)entry.getKey()).intValue();
int index = ((Integer)entry.getValue()).intValue();
index = rescaleIndex(index, srcStream.getStreamType(), mStreamType);
- setIndex(index, device, lastAudible);
+
+ if (lastAudible) {
+ setLastAudibleIndex(index, device);
+ } else {
+ setIndex(index, device, false /* lastAudible */);
+ }
+ }
+ }
+
+ public synchronized void setAllIndexesToMax() {
+ Set set = mIndex.entrySet();
+ Iterator i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ entry.setValue(mIndexMax);
+ }
+ set = mLastAudibleIndex.entrySet();
+ i = set.iterator();
+ while (i.hasNext()) {
+ Map.Entry entry = (Map.Entry)i.next();
+ entry.setValue(mIndexMax);
}
}
@@ -2769,6 +3052,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
private void dump(PrintWriter pw) {
+ pw.print(" Mute count: ");
+ pw.println(muteCount());
pw.print(" Current: ");
Set set = mIndex.entrySet();
Iterator i = set.iterator();
@@ -2858,19 +3143,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, Settings.Global.MODE_RINGER, ringerMode);
}
private void playSoundEffect(int effectType, int volume) {
@@ -2881,7 +3168,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;
}
@@ -2920,8 +3207,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) {
@@ -2957,13 +3246,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:
@@ -3009,6 +3302,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
// Restore forced usage for communcations and record
AudioSystem.setForceUse(AudioSystem.FOR_COMMUNICATION, mForcedUseForComm);
AudioSystem.setForceUse(AudioSystem.FOR_RECORD, mForcedUseForComm);
+ AudioSystem.setForceUse(AudioSystem.FOR_SYSTEM, mCameraSoundForced ?
+ AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE);
// Restore stream volumes
int numStreamTypes = AudioSystem.getNumStreamTypes();
@@ -3026,7 +3321,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
restoreMasterVolume();
// Reset device orientation (if monitored for this device)
- if (SystemProperties.getBoolean("ro.audio.monitorOrientation", false)) {
+ if (mMonitorOrientation) {
setOrientationForAudioSystem();
}
@@ -3055,6 +3350,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
break;
case MSG_SET_FORCE_USE:
+ case MSG_SET_FORCE_BT_A2DP_USE:
setForceUse(msg.arg1, msg.arg2);
break;
@@ -3117,6 +3413,23 @@ 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;
+
+ case MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED:
+ case MSG_CONFIGURE_SAFE_MEDIA_VOLUME:
+ onConfigureSafeVolume((msg.what == MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED));
+ break;
}
}
}
@@ -3127,6 +3440,8 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
super(new Handler());
mContentResolver.registerContentObserver(Settings.System.getUriFor(
Settings.System.MODE_RINGER_STREAMS_AFFECTED), false, this);
+ mContentResolver.registerContentObserver(Settings.Global.getUriFor(
+ Settings.Global.DOCK_AUDIO_MEDIA_ENABLED), false, this);
}
@Override
@@ -3137,15 +3452,23 @@ 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 {
ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_MUSIC);
}
+ synchronized (mCameraSoundForced) {
+ if (mCameraSoundForced) {
+ ringerModeAffectedStreams &= ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ } else {
+ ringerModeAffectedStreams |= (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ }
+ }
if (ringerModeAffectedStreams != mRingerModeAffectedStreams) {
/*
* Ensure all stream types that should be affected by ringer mode
@@ -3154,6 +3477,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
mRingerModeAffectedStreams = ringerModeAffectedStreams;
setRingerModeInt(getRingerMode(), false);
}
+ readDockAudioSettings(mContentResolver);
}
}
}
@@ -3172,8 +3496,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
@@ -3292,7 +3616,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) {
@@ -3305,8 +3631,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();
}
}
@@ -3362,7 +3694,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)
@@ -3372,12 +3709,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);
}
}
@@ -3406,7 +3757,13 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
config = AudioSystem.FORCE_BT_CAR_DOCK;
break;
case Intent.EXTRA_DOCK_STATE_LE_DESK:
- config = AudioSystem.FORCE_ANALOG_DOCK;
+ synchronized (mSettingsLock) {
+ if (mDockAudioMediaEnabled) {
+ config = AudioSystem.FORCE_ANALOG_DOCK;
+ } else {
+ config = AudioSystem.FORCE_NONE;
+ }
+ }
break;
case Intent.EXTRA_DOCK_STATE_HE_DESK:
config = AudioSystem.FORCE_DIGITAL_DOCK;
@@ -3415,6 +3772,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
default:
config = AudioSystem.FORCE_NONE;
}
+
AudioSystem.setForceUse(AudioSystem.FOR_DOCK, config);
} else if (action.equals(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED)) {
state = intent.getIntExtra(BluetoothProfile.EXTRA_STATE,
@@ -3469,7 +3827,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;
@@ -3514,7 +3872,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;
@@ -3531,13 +3889,21 @@ 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) {
adapter.getProfileProxy(mContext, mBluetoothProfileServiceListener,
BluetoothProfile.A2DP);
}
+
+ sendMsg(mAudioHandler,
+ MSG_CONFIGURE_SAFE_MEDIA_VOLUME_FORCED,
+ SENDMSG_REPLACE,
+ 0,
+ 0,
+ null,
+ SAFE_VOLUME_CONFIGURE_TIMEOUT_MS);
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)) {
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
// a package is being removed, not replaced
@@ -3552,6 +3918,27 @@ 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 playback for background user
+ sendMsg(mAudioHandler,
+ MSG_BROADCAST_AUDIO_BECOMING_NOISY,
+ SENDMSG_REPLACE,
+ 0,
+ 0,
+ null,
+ 0);
+ // the current audio focus owner is no longer valid
+ discardAudioFocusOwner();
+
+ // 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);
}
}
}
@@ -3587,6 +3974,32 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
}
};
+ /**
+ * Discard the current audio focus owner.
+ * Notify top of audio focus stack that it lost focus (regardless of possibility to reassign
+ * focus), remove it from the stack, and clear the remote control display.
+ */
+ private void discardAudioFocusOwner() {
+ synchronized(mAudioFocusLock) {
+ if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
+ // notify the current focus owner it lost focus after removing it from stack
+ FocusStackEntry focusOwner = mFocusStack.pop();
+ try {
+ focusOwner.mFocusDispatcher.dispatchAudioFocusChange(
+ AudioManager.AUDIOFOCUS_LOSS, focusOwner.mClientId);
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failure to signal loss of audio focus due to "+ e);
+ e.printStackTrace();
+ }
+ focusOwner.unlinkToDeath();
+ // clear RCD
+ synchronized(mRCStack) {
+ clearRemoteControlDisplay_syncAfRcs();
+ }
+ }
+ }
+ }
+
private void notifyTopOfAudioFocusStack() {
// notify the top of the stack it gained focus
if (!mFocusStack.empty() && (mFocusStack.peek().mFocusDispatcher != null)) {
@@ -3658,9 +4071,12 @@ 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
+ + " -- pack: " + fse.mPackageName
+ + " -- client: " + fse.mClientId
+ " -- duration: " + fse.mFocusChangeType
- + " -- uid: " + fse.mCallingUid);
+ + " -- uid: " + fse.mCallingUid
+ + " -- stream: " + fse.mStreamType);
}
}
}
@@ -3921,8 +4337,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);
+ }
}
/**
@@ -3955,8 +4376,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);
+ }
}
}
}
@@ -4020,7 +4447,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;
}
@@ -4328,6 +4755,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
while(stackIterator.hasNext()) {
RemoteControlStackEntry rcse = stackIterator.next();
pw.println(" pi: " + rcse.mMediaIntent +
+ " -- pack: " + rcse.mCallingPackageName +
" -- ercvr: " + rcse.mReceiverComponent +
" -- client: " + rcse.mRcClient +
" -- uid: " + rcse.mCallingUid +
@@ -4356,7 +4784,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
" -- vol: " + rcse.mPlaybackVolume +
" -- volMax: " + rcse.mPlaybackVolumeMax +
" -- volObs: " + rcse.mRemoteVolumeObs);
-
+
}
}
synchronized (mMainRemote) {
@@ -4414,8 +4842,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
@@ -4652,17 +5080,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;
}
@@ -5258,10 +5709,62 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
try {
// reading new orientation "safely" (i.e. under try catch) in case anything
// goes wrong when obtaining resources and configuration
- int newOrientation = context.getResources().getConfiguration().orientation;
- if (newOrientation != mDeviceOrientation) {
- mDeviceOrientation = newOrientation;
- setOrientationForAudioSystem();
+ Configuration config = context.getResources().getConfiguration();
+ if (mMonitorOrientation) {
+ int newOrientation = config.orientation;
+ if (newOrientation != mDeviceOrientation) {
+ mDeviceOrientation = newOrientation;
+ setOrientationForAudioSystem();
+ }
+ }
+ sendMsg(mAudioHandler,
+ MSG_CONFIGURE_SAFE_MEDIA_VOLUME,
+ SENDMSG_REPLACE,
+ 0,
+ 0,
+ null,
+ 0);
+
+ boolean cameraSoundForced = mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_camera_sound_forced);
+ synchronized (mSettingsLock) {
+ synchronized (mCameraSoundForced) {
+ if (cameraSoundForced != mCameraSoundForced) {
+ mCameraSoundForced = cameraSoundForced;
+
+ VolumeStreamState s = mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED];
+ if (cameraSoundForced) {
+ s.setAllIndexesToMax();
+ mRingerModeAffectedStreams &=
+ ~(1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ } else {
+ s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM],
+ false /*lastAudible*/);
+ s.setAllIndexes(mStreamStates[AudioSystem.STREAM_SYSTEM],
+ true /*lastAudible*/);
+ mRingerModeAffectedStreams |=
+ (1 << AudioSystem.STREAM_SYSTEM_ENFORCED);
+ }
+ // take new state into account for streams muted by ringer mode
+ setRingerModeInt(getRingerMode(), false);
+
+ sendMsg(mAudioHandler,
+ MSG_SET_FORCE_USE,
+ SENDMSG_QUEUE,
+ AudioSystem.FOR_SYSTEM,
+ cameraSoundForced ?
+ AudioSystem.FORCE_SYSTEM_ENFORCED : AudioSystem.FORCE_NONE,
+ null,
+ 0);
+
+ sendMsg(mAudioHandler,
+ MSG_SET_ALL_VOLUMES,
+ SENDMSG_QUEUE,
+ 0,
+ 0,
+ mStreamStates[AudioSystem.STREAM_SYSTEM_ENFORCED], 0);
+ }
+ }
}
} catch (Exception e) {
Log.e(TAG, "Error retrieving device orientation: " + e);
@@ -5296,10 +5799,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);
}
}
@@ -5323,6 +5825,154 @@ 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.
+ //==========================================================================================
+
+ // mSafeMediaVolumeState indicates whether the media volume is limited over headphones.
+ // It is SAFE_MEDIA_VOLUME_NOT_CONFIGURED at boot time until a network service is connected
+ // or the configure time is elapsed. It is then set to SAFE_MEDIA_VOLUME_ACTIVE or
+ // SAFE_MEDIA_VOLUME_DISABLED according to country option. If not SAFE_MEDIA_VOLUME_DISABLED, it
+ // can be set to SAFE_MEDIA_VOLUME_INACTIVE by calling AudioService.disableSafeMediaVolume()
+ // (when user opts out).
+ private final int SAFE_MEDIA_VOLUME_NOT_CONFIGURED = 0;
+ private final int SAFE_MEDIA_VOLUME_DISABLED = 1;
+ private final int SAFE_MEDIA_VOLUME_INACTIVE = 2;
+ private final int SAFE_MEDIA_VOLUME_ACTIVE = 3;
+ private Integer mSafeMediaVolumeState;
+
+ private int mMcc = 0;
+ // mSafeMediaVolumeIndex is the cached value of config_safe_media_volume_index property
+ private 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 static final int SAFE_VOLUME_CONFIGURE_TIMEOUT_MS = 30000; // 30s after boot completed
+
+ private void setSafeMediaVolumeEnabled(boolean on) {
+ synchronized (mSafeMediaVolumeState) {
+ if ((mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_NOT_CONFIGURED) &&
+ (mSafeMediaVolumeState != SAFE_MEDIA_VOLUME_DISABLED)) {
+ if (on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_INACTIVE)) {
+ mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_ACTIVE;
+ enforceSafeMediaVolume();
+ } else if (!on && (mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE)) {
+ mSafeMediaVolumeState = SAFE_MEDIA_VOLUME_INACTIVE;
+ mMusicActiveMs = 0;
+ sendMsg(mAudioHandler,
+ MSG_CHECK_MUSIC_ACTIVE,
+ SENDMSG_REPLACE,
+ 0,
+ 0,
+ null,
+ MUSIC_ACTIVE_POLL_PERIOD_MS);
+ }
+ }
+ }
+ }
+
+ 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 (mSafeMediaVolumeState) {
+ if ((mSafeMediaVolumeState == SAFE_MEDIA_VOLUME_ACTIVE) &&
+ (mStreamVolumeAlias[streamType] == AudioSystem.STREAM_MUSIC) &&
+ ((device & mSafeMediaVolumeDevices) != 0) &&
+ (index > mSafeMediaVolumeIndex)) {
+ mVolumePanel.postDisplaySafeVolumeWarning();
+ return false;
+ }
+ return true;
+ }
+ }
+
+ public void disableSafeMediaVolume() {
+ synchronized (mSafeMediaVolumeState) {
+ setSafeMediaVolumeEnabled(false);
+ }
+ }
+
+
+ //==========================================================================================
+ // Camera shutter sound policy.
+ // config_camera_sound_forced configuration option in config.xml defines if the camera shutter
+ // sound is forced (sound even if the device is in silent mode) or not. This option is false by
+ // default and can be overridden by country specific overlay in values-mccXXX/config.xml.
+ //==========================================================================================
+
+ // cached value of com.android.internal.R.bool.config_camera_sound_forced
+ private Boolean mCameraSoundForced;
+
+ // called by android.hardware.Camera to populate CameraInfo.canDisableShutterSound
+ public boolean isCameraSoundForced() {
+ synchronized (mCameraSoundForced) {
+ return mCameraSoundForced;
+ }
+ }
+
+ private static final String[] RINGER_MODE_NAMES = new String[] {
+ "SILENT",
+ "VIBRATE",
+ "NORMAL"
+ };
+
+ private void dumpRingerMode(PrintWriter pw) {
+ pw.println("\nRinger mode: ");
+ pw.println("- mode: "+RINGER_MODE_NAMES[mRingerMode]);
+ pw.print("- ringer mode affected streams = 0x");
+ pw.println(Integer.toHexString(mRingerModeAffectedStreams));
+ pw.print("- ringer mode muted streams = 0x");
+ pw.println(Integer.toHexString(mRingerModeMutedStreams));
+ }
+
public int verifyX509CertChain(int numcerts, byte [] chain, String domain, String authType) {
if (DEBUG_CERTS) {
@@ -5368,6 +6018,7 @@ public class AudioService extends IAudioService.Stub implements OnFinished {
dumpRCStack(pw);
dumpRCCStack(pw);
dumpStreamStates(pw);
+ dumpRingerMode(pw);
pw.println("\nAudio routes:");
pw.print(" mMainType=0x"); pw.println(Integer.toHexString(mCurAudioRoutes.mMainType));
pw.print(" mBluetoothName="); pw.println(mCurAudioRoutes.mBluetoothName);
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index 1ca0df4..dde2979 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -111,6 +111,13 @@ public class AudioSystem
public static native boolean isStreamActive(int stream, int inPastMs);
/*
+ * Checks whether the specified audio source is active.
+ *
+ * return true if any recorder using this source is currently recording
+ */
+ public static native boolean isSourceActive(int source);
+
+ /*
* Sets a group generic audio configuration parameters. The use of these parameters
* are platform dependent, see libaudio
*
@@ -188,6 +195,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 +218,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 +237,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 +249,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 +300,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 +335,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 +360,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_SYSTEM_ENFORCED = 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
@@ -327,7 +369,8 @@ public class AudioSystem
public static final int FOR_MEDIA = 1;
public static final int FOR_RECORD = 2;
public static final int FOR_DOCK = 3;
- private static final int NUM_FORCE_USE = 4;
+ public static final int FOR_SYSTEM = 4;
+ private static final int NUM_FORCE_USE = 5;
// usage for AudioRecord.startRecordingSync(), must match AudioSystem::sync_event_t
public static final int SYNC_EVENT_NONE = 0;
@@ -346,4 +389,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 7310232..d723cd4 100644
--- a/media/java/android/media/IAudioService.aidl
+++ b/media/java/android/media/IAudioService.aidl
@@ -110,6 +110,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);
@@ -153,4 +155,6 @@ interface IAudioService {
int setBluetoothA2dpDeviceConnectionState(in BluetoothDevice device, int state);
AudioRoutesInfo startWatchingRoutes(in IAudioRoutesObserver observer);
+
+ boolean isCameraSoundForced();
}
diff --git a/media/java/android/media/IRingtonePlayer.aidl b/media/java/android/media/IRingtonePlayer.aidl
index 44a0333..0872f1d 100644
--- a/media/java/android/media/IRingtonePlayer.aidl
+++ b/media/java/android/media/IRingtonePlayer.aidl
@@ -17,6 +17,7 @@
package android.media;
import android.net.Uri;
+import android.os.UserHandle;
/**
* @hide
@@ -28,6 +29,6 @@ interface IRingtonePlayer {
boolean isPlaying(IBinder token);
/** Used for Notification sound playback. */
- void playAsync(in Uri uri, boolean looping, int streamType);
+ void playAsync(in Uri uri, in UserHandle user, boolean looping, int streamType);
void stopAsync();
}
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/MediaRouter.java b/media/java/android/media/MediaRouter.java
index a256079..2a5a16e 100644
--- a/media/java/android/media/MediaRouter.java
+++ b/media/java/android/media/MediaRouter.java
@@ -22,12 +22,16 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.drawable.Drawable;
+import android.hardware.display.DisplayManager;
+import android.hardware.display.WifiDisplay;
+import android.hardware.display.WifiDisplayStatus;
import android.os.Handler;
import android.os.IBinder;
import android.os.RemoteException;
import android.os.ServiceManager;
import android.text.TextUtils;
import android.util.Log;
+import android.view.Display;
import java.util.ArrayList;
import java.util.HashMap;
@@ -48,9 +52,10 @@ import java.util.concurrent.CopyOnWriteArrayList;
public class MediaRouter {
private static final String TAG = "MediaRouter";
- static class Static {
+ static class Static implements DisplayManager.DisplayListener {
final Resources mResources;
final IAudioService mAudioService;
+ final DisplayManager mDisplayService;
final Handler mHandler;
final CopyOnWriteArrayList<CallbackInfo> mCallbacks =
new CopyOnWriteArrayList<CallbackInfo>();
@@ -60,18 +65,20 @@ public class MediaRouter {
final RouteCategory mSystemCategory;
- final AudioRoutesInfo mCurRoutesInfo = new AudioRoutesInfo();
+ final AudioRoutesInfo mCurAudioRoutesInfo = new AudioRoutesInfo();
- RouteInfo mDefaultAudio;
+ RouteInfo mDefaultAudioVideo;
RouteInfo mBluetoothA2dpRoute;
RouteInfo mSelectedRoute;
- final IAudioRoutesObserver.Stub mRoutesObserver = new IAudioRoutesObserver.Stub() {
+ WifiDisplayStatus mLastKnownWifiDisplayStatus;
+
+ final IAudioRoutesObserver.Stub mAudioRoutesObserver = new IAudioRoutesObserver.Stub() {
public void dispatchAudioRoutesChanged(final AudioRoutesInfo newRoutes) {
mHandler.post(new Runnable() {
@Override public void run() {
- updateRoutes(newRoutes);
+ updateAudioRoutes(newRoutes);
}
});
}
@@ -84,34 +91,55 @@ public class MediaRouter {
IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
mAudioService = IAudioService.Stub.asInterface(b);
+ mDisplayService = (DisplayManager) appContext.getSystemService(Context.DISPLAY_SERVICE);
+
mSystemCategory = new RouteCategory(
com.android.internal.R.string.default_audio_route_category_name,
- ROUTE_TYPE_LIVE_AUDIO, false);
+ ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO, false);
+ mSystemCategory.mIsSystem = true;
}
// Called after sStatic is initialized
void startMonitoringRoutes(Context appContext) {
- mDefaultAudio = new RouteInfo(mSystemCategory);
- mDefaultAudio.mNameResId = com.android.internal.R.string.default_audio_route_name;
- mDefaultAudio.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
- addRoute(mDefaultAudio);
-
+ mDefaultAudioVideo = new RouteInfo(mSystemCategory);
+ mDefaultAudioVideo.mNameResId = com.android.internal.R.string.default_audio_route_name;
+ mDefaultAudioVideo.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
+ mDefaultAudioVideo.mPresentationDisplay = choosePresentationDisplayForRoute(
+ mDefaultAudioVideo, getAllPresentationDisplays());
+ addRouteStatic(mDefaultAudioVideo);
+
+ // This will select the active wifi display route if there is one.
+ updateWifiDisplayStatus(mDisplayService.getWifiDisplayStatus());
+
+ appContext.registerReceiver(new WifiDisplayStatusChangedReceiver(),
+ new IntentFilter(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED));
appContext.registerReceiver(new VolumeChangeReceiver(),
new IntentFilter(AudioManager.VOLUME_CHANGED_ACTION));
- AudioRoutesInfo newRoutes = null;
+ mDisplayService.registerDisplayListener(this, mHandler);
+
+ AudioRoutesInfo newAudioRoutes = null;
try {
- newRoutes = mAudioService.startWatchingRoutes(mRoutesObserver);
+ newAudioRoutes = mAudioService.startWatchingRoutes(mAudioRoutesObserver);
} catch (RemoteException e) {
}
- if (newRoutes != null) {
- updateRoutes(newRoutes);
+ if (newAudioRoutes != null) {
+ // This will select the active BT route if there is one and the current
+ // selected route is the default system route, or if there is no selected
+ // route yet.
+ updateAudioRoutes(newAudioRoutes);
+ }
+
+ // Select the default route if the above didn't sync us up
+ // appropriately with relevant system state.
+ if (mSelectedRoute == null) {
+ selectRouteStatic(mDefaultAudioVideo.getSupportedTypes(), mDefaultAudioVideo);
}
}
- void updateRoutes(AudioRoutesInfo newRoutes) {
- if (newRoutes.mMainType != mCurRoutesInfo.mMainType) {
- mCurRoutesInfo.mMainType = newRoutes.mMainType;
+ void updateAudioRoutes(AudioRoutesInfo newRoutes) {
+ if (newRoutes.mMainType != mCurAudioRoutesInfo.mMainType) {
+ mCurAudioRoutesInfo.mMainType = newRoutes.mMainType;
int name;
if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADPHONES) != 0
|| (newRoutes.mMainType&AudioRoutesInfo.MAIN_HEADSET) != 0) {
@@ -119,14 +147,16 @@ public class MediaRouter {
} else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_DOCK_SPEAKERS) != 0) {
name = com.android.internal.R.string.default_audio_route_name_dock_speakers;
} else if ((newRoutes.mMainType&AudioRoutesInfo.MAIN_HDMI) != 0) {
- name = com.android.internal.R.string.default_audio_route_name_hdmi;
+ name = com.android.internal.R.string.default_media_route_name_hdmi;
} else {
name = com.android.internal.R.string.default_audio_route_name;
}
- sStatic.mDefaultAudio.mNameResId = name;
- dispatchRouteChanged(sStatic.mDefaultAudio);
+ sStatic.mDefaultAudioVideo.mNameResId = name;
+ dispatchRouteChanged(sStatic.mDefaultAudioVideo);
}
+ final int mainType = mCurAudioRoutesInfo.mMainType;
+
boolean a2dpEnabled;
try {
a2dpEnabled = mAudioService.isBluetoothA2dpOn();
@@ -135,17 +165,17 @@ public class MediaRouter {
a2dpEnabled = false;
}
- if (!TextUtils.equals(newRoutes.mBluetoothName, mCurRoutesInfo.mBluetoothName)) {
- mCurRoutesInfo.mBluetoothName = newRoutes.mBluetoothName;
- if (mCurRoutesInfo.mBluetoothName != null) {
+ if (!TextUtils.equals(newRoutes.mBluetoothName, mCurAudioRoutesInfo.mBluetoothName)) {
+ mCurAudioRoutesInfo.mBluetoothName = newRoutes.mBluetoothName;
+ if (mCurAudioRoutesInfo.mBluetoothName != null) {
if (sStatic.mBluetoothA2dpRoute == null) {
final RouteInfo info = new RouteInfo(sStatic.mSystemCategory);
- info.mName = mCurRoutesInfo.mBluetoothName;
+ info.mName = mCurAudioRoutesInfo.mBluetoothName;
info.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO;
sStatic.mBluetoothA2dpRoute = info;
- addRoute(sStatic.mBluetoothA2dpRoute);
+ addRouteStatic(sStatic.mBluetoothA2dpRoute);
} else {
- sStatic.mBluetoothA2dpRoute.mName = mCurRoutesInfo.mBluetoothName;
+ sStatic.mBluetoothA2dpRoute.mName = mCurAudioRoutesInfo.mBluetoothName;
dispatchRouteChanged(sStatic.mBluetoothA2dpRoute);
}
} else if (sStatic.mBluetoothA2dpRoute != null) {
@@ -155,15 +185,48 @@ public class MediaRouter {
}
if (mBluetoothA2dpRoute != null) {
- if (mCurRoutesInfo.mMainType != AudioRoutesInfo.MAIN_SPEAKER &&
- mSelectedRoute == mBluetoothA2dpRoute) {
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudio);
- } else if (mCurRoutesInfo.mMainType == AudioRoutesInfo.MAIN_SPEAKER &&
- mSelectedRoute == mDefaultAudio && a2dpEnabled) {
+ if (mainType != AudioRoutesInfo.MAIN_SPEAKER &&
+ mSelectedRoute == mBluetoothA2dpRoute && !a2dpEnabled) {
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mDefaultAudioVideo);
+ } else if ((mSelectedRoute == mDefaultAudioVideo || mSelectedRoute == null) &&
+ a2dpEnabled) {
selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO, mBluetoothA2dpRoute);
}
}
}
+
+ @Override
+ public void onDisplayAdded(int displayId) {
+ updatePresentationDisplays(displayId);
+ }
+
+ @Override
+ public void onDisplayChanged(int displayId) {
+ updatePresentationDisplays(displayId);
+ }
+
+ @Override
+ public void onDisplayRemoved(int displayId) {
+ updatePresentationDisplays(displayId);
+ }
+
+ public Display[] getAllPresentationDisplays() {
+ return mDisplayService.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION);
+ }
+
+ private void updatePresentationDisplays(int changedDisplayId) {
+ final Display[] displays = getAllPresentationDisplays();
+ final int count = mRoutes.size();
+ for (int i = 0; i < count; i++) {
+ final RouteInfo info = mRoutes.get(i);
+ Display display = choosePresentationDisplayForRoute(info, displays);
+ if (display != info.mPresentationDisplay
+ || (display != null && display.getDisplayId() == changedDisplayId)) {
+ info.mPresentationDisplay = display;
+ dispatchRoutePresentationDisplayChanged(info);
+ }
+ }
+ }
}
static Static sStatic;
@@ -181,6 +244,23 @@ public class MediaRouter {
public static final int ROUTE_TYPE_LIVE_AUDIO = 0x1;
/**
+ * Route type flag for live video.
+ *
+ * <p>A device that supports live video routing will allow a mirrored version
+ * of the device's primary display or a customized
+ * {@link android.app.Presentation Presentation} to be routed to supported destinations.</p>
+ *
+ * <p>Once initiated, display mirroring is transparent to the application.
+ * While remote routing is active the application may use a
+ * {@link android.app.Presentation Presentation} to replace the mirrored view
+ * on the external display with different content.</p>
+ *
+ * @see RouteInfo#getPresentationDisplay()
+ * @see android.app.Presentation
+ */
+ public static final int ROUTE_TYPE_LIVE_VIDEO = 0x2;
+
+ /**
* Route type flag for application-specific usage.
*
* <p>Unlike other media route types, user routes are managed by the application.
@@ -198,6 +278,9 @@ public class MediaRouter {
if ((types & ROUTE_TYPE_LIVE_AUDIO) != 0) {
result.append("ROUTE_TYPE_LIVE_AUDIO ");
}
+ if ((types & ROUTE_TYPE_LIVE_VIDEO) != 0) {
+ result.append("ROUTE_TYPE_LIVE_VIDEO ");
+ }
if ((types & ROUTE_TYPE_USER) != 0) {
result.append("ROUTE_TYPE_USER ");
}
@@ -219,7 +302,7 @@ public class MediaRouter {
* @hide for use by framework routing UI
*/
public RouteInfo getSystemAudioRoute() {
- return sStatic.mDefaultAudio;
+ return sStatic.mDefaultAudioVideo;
}
/**
@@ -296,7 +379,8 @@ public class MediaRouter {
}
static void selectRouteStatic(int types, RouteInfo route) {
- if (sStatic.mSelectedRoute == route) return;
+ final RouteInfo oldRoute = sStatic.mSelectedRoute;
+ if (oldRoute == route) return;
if ((route.getSupportedTypes() & types) == 0) {
Log.w(TAG, "selectRoute ignored; cannot select route with supported types " +
typesToString(route.getSupportedTypes()) + " into route types " +
@@ -306,7 +390,7 @@ public class MediaRouter {
final RouteInfo btRoute = sStatic.mBluetoothA2dpRoute;
if (btRoute != null && (types & ROUTE_TYPE_LIVE_AUDIO) != 0 &&
- (route == btRoute || route == sStatic.mDefaultAudio)) {
+ (route == btRoute || route == sStatic.mDefaultAudioVideo)) {
try {
sStatic.mAudioService.setBluetoothA2dpOn(route == btRoute);
} catch (RemoteException e) {
@@ -314,10 +398,21 @@ public class MediaRouter {
}
}
- if (sStatic.mSelectedRoute != null) {
+ final WifiDisplay activeDisplay =
+ sStatic.mDisplayService.getWifiDisplayStatus().getActiveDisplay();
+ final boolean oldRouteHasAddress = oldRoute != null && oldRoute.mDeviceAddress != null;
+ final boolean newRouteHasAddress = route != null && route.mDeviceAddress != null;
+ if (activeDisplay != null || oldRouteHasAddress || newRouteHasAddress) {
+ if (newRouteHasAddress && !matchesDeviceAddress(activeDisplay, route)) {
+ sStatic.mDisplayService.connectWifiDisplay(route.mDeviceAddress);
+ } else if (activeDisplay != null && !newRouteHasAddress) {
+ sStatic.mDisplayService.disconnectWifiDisplay();
+ }
+ }
+
+ if (oldRoute != null) {
// TODO filter types properly
- dispatchRouteUnselected(types & sStatic.mSelectedRoute.getSupportedTypes(),
- sStatic.mSelectedRoute);
+ dispatchRouteUnselected(types & oldRoute.getSupportedTypes(), oldRoute);
}
sStatic.mSelectedRoute = route;
if (route != null) {
@@ -327,6 +422,22 @@ public class MediaRouter {
}
/**
+ * Compare the device address of a display and a route.
+ * Nulls/no device address will match another null/no address.
+ */
+ static boolean matchesDeviceAddress(WifiDisplay display, RouteInfo info) {
+ final boolean routeHasAddress = info != null && info.mDeviceAddress != null;
+ if (display == null && !routeHasAddress) {
+ return true;
+ }
+
+ if (display != null && routeHasAddress) {
+ return display.getDeviceAddress().equals(info.mDeviceAddress);
+ }
+ return false;
+ }
+
+ /**
* Add an app-specified route for media to the MediaRouter.
* App-specified route definitions are created using {@link #createUserRoute(RouteCategory)}
*
@@ -335,22 +446,21 @@ public class MediaRouter {
* @see #removeUserRoute(UserRouteInfo)
*/
public void addUserRoute(UserRouteInfo info) {
- addRoute(info);
+ addRouteStatic(info);
}
/**
* @hide Framework use only
*/
public void addRouteInt(RouteInfo info) {
- addRoute(info);
+ addRouteStatic(info);
}
- static void addRoute(RouteInfo info) {
+ static void addRouteStatic(RouteInfo info) {
final RouteCategory cat = info.getCategory();
if (!sStatic.mCategories.contains(cat)) {
sStatic.mCategories.add(cat);
}
- final boolean onlyRoute = sStatic.mRoutes.isEmpty();
if (cat.isGroupable() && !(info instanceof RouteGroup)) {
// Enforce that any added route in a groupable category must be in a group.
final RouteGroup group = new RouteGroup(info.getCategory());
@@ -364,10 +474,6 @@ public class MediaRouter {
sStatic.mRoutes.add(info);
dispatchRouteAdded(info);
}
-
- if (onlyRoute) {
- selectRouteStatic(info.getSupportedTypes(), info);
- }
}
/**
@@ -419,7 +525,7 @@ public class MediaRouter {
if (info == sStatic.mSelectedRoute) {
// Removing the currently selected route? Select the default before we remove it.
// TODO: Be smarter about the route types here; this selects for all valid.
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER, sStatic.mDefaultAudio);
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER, sStatic.mDefaultAudioVideo);
}
if (!found) {
sStatic.mCategories.remove(removingCat);
@@ -444,7 +550,8 @@ public class MediaRouter {
if (info == sStatic.mSelectedRoute) {
// Removing the currently selected route? Select the default before we remove it.
// TODO: Be smarter about the route types here; this selects for all valid.
- selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_USER, sStatic.mDefaultAudio);
+ selectRouteStatic(ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO | ROUTE_TYPE_USER,
+ sStatic.mDefaultAudioVideo);
}
if (!found) {
sStatic.mCategories.remove(removingCat);
@@ -606,23 +713,192 @@ public class MediaRouter {
}
}
+ static void dispatchRoutePresentationDisplayChanged(RouteInfo info) {
+ for (CallbackInfo cbi : sStatic.mCallbacks) {
+ if ((cbi.type & info.mSupportedTypes) != 0) {
+ cbi.cb.onRoutePresentationDisplayChanged(cbi.router, info);
+ }
+ }
+ }
+
static void systemVolumeChanged(int newValue) {
final RouteInfo selectedRoute = sStatic.mSelectedRoute;
if (selectedRoute == null) return;
if (selectedRoute == sStatic.mBluetoothA2dpRoute ||
- selectedRoute == sStatic.mDefaultAudio) {
+ selectedRoute == sStatic.mDefaultAudioVideo) {
dispatchRouteVolumeChanged(selectedRoute);
} else if (sStatic.mBluetoothA2dpRoute != null) {
try {
dispatchRouteVolumeChanged(sStatic.mAudioService.isBluetoothA2dpOn() ?
- sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudio);
+ sStatic.mBluetoothA2dpRoute : sStatic.mDefaultAudioVideo);
} catch (RemoteException e) {
Log.e(TAG, "Error checking Bluetooth A2DP state to report volume change", e);
}
} else {
- dispatchRouteVolumeChanged(sStatic.mDefaultAudio);
+ dispatchRouteVolumeChanged(sStatic.mDefaultAudioVideo);
+ }
+ }
+
+ static void updateWifiDisplayStatus(WifiDisplayStatus newStatus) {
+ final WifiDisplayStatus oldStatus = sStatic.mLastKnownWifiDisplayStatus;
+
+ // TODO Naive implementation. Make this smarter later.
+ boolean wantScan = false;
+ boolean blockScan = false;
+ WifiDisplay[] oldDisplays = oldStatus != null ?
+ oldStatus.getRememberedDisplays() : new WifiDisplay[0];
+ WifiDisplay[] newDisplays = newStatus.getRememberedDisplays();
+ WifiDisplay[] availableDisplays = newStatus.getAvailableDisplays();
+ WifiDisplay activeDisplay = newStatus.getActiveDisplay();
+
+ for (int i = 0; i < newDisplays.length; i++) {
+ final WifiDisplay d = newDisplays[i];
+ final WifiDisplay oldRemembered = findMatchingDisplay(d, oldDisplays);
+ if (oldRemembered == null) {
+ addRouteStatic(makeWifiDisplayRoute(d,
+ findMatchingDisplay(d, availableDisplays) != null));
+ wantScan = true;
+ } else {
+ final boolean available = findMatchingDisplay(d, availableDisplays) != null;
+ final RouteInfo route = findWifiDisplayRoute(d);
+ updateWifiDisplayRoute(route, d, available, newStatus);
+ }
+ if (d.equals(activeDisplay)) {
+ final RouteInfo activeRoute = findWifiDisplayRoute(d);
+ if (activeRoute != null) {
+ selectRouteStatic(activeRoute.getSupportedTypes(), activeRoute);
+
+ // Don't scan if we're already connected to a wifi display,
+ // the scanning process can cause a hiccup with some configurations.
+ blockScan = true;
+ }
+ }
+ }
+ for (int i = 0; i < oldDisplays.length; i++) {
+ final WifiDisplay d = oldDisplays[i];
+ final WifiDisplay newDisplay = findMatchingDisplay(d, newDisplays);
+ if (newDisplay == null) {
+ removeRoute(findWifiDisplayRoute(d));
+ }
+ }
+
+ if (wantScan && !blockScan) {
+ sStatic.mDisplayService.scanWifiDisplays();
+ }
+
+ sStatic.mLastKnownWifiDisplayStatus = newStatus;
+ }
+
+ static RouteInfo makeWifiDisplayRoute(WifiDisplay display, boolean available) {
+ final RouteInfo newRoute = new RouteInfo(sStatic.mSystemCategory);
+ newRoute.mDeviceAddress = display.getDeviceAddress();
+ newRoute.mSupportedTypes = ROUTE_TYPE_LIVE_AUDIO | ROUTE_TYPE_LIVE_VIDEO;
+ newRoute.mVolumeHandling = RouteInfo.PLAYBACK_VOLUME_FIXED;
+ newRoute.mPlaybackType = RouteInfo.PLAYBACK_TYPE_REMOTE;
+
+ newRoute.setStatusCode(available ?
+ RouteInfo.STATUS_AVAILABLE : RouteInfo.STATUS_CONNECTING);
+ newRoute.mEnabled = available;
+
+ newRoute.mName = display.getFriendlyDisplayName();
+
+ newRoute.mPresentationDisplay = choosePresentationDisplayForRoute(newRoute,
+ sStatic.getAllPresentationDisplays());
+ return newRoute;
+ }
+
+ private static void updateWifiDisplayRoute(RouteInfo route, WifiDisplay display,
+ boolean available, WifiDisplayStatus wifiDisplayStatus) {
+ final boolean isScanning =
+ wifiDisplayStatus.getScanState() == WifiDisplayStatus.SCAN_STATE_SCANNING;
+
+ boolean changed = false;
+ int newStatus = RouteInfo.STATUS_NONE;
+
+ if (available) {
+ newStatus = isScanning ? RouteInfo.STATUS_SCANNING : RouteInfo.STATUS_AVAILABLE;
+ } else {
+ newStatus = RouteInfo.STATUS_NOT_AVAILABLE;
}
+
+ if (display.equals(wifiDisplayStatus.getActiveDisplay())) {
+ final int activeState = wifiDisplayStatus.getActiveDisplayState();
+ switch (activeState) {
+ case WifiDisplayStatus.DISPLAY_STATE_CONNECTED:
+ newStatus = RouteInfo.STATUS_NONE;
+ break;
+ case WifiDisplayStatus.DISPLAY_STATE_CONNECTING:
+ newStatus = RouteInfo.STATUS_CONNECTING;
+ break;
+ case WifiDisplayStatus.DISPLAY_STATE_NOT_CONNECTED:
+ Log.e(TAG, "Active display is not connected!");
+ break;
+ }
+ }
+
+ final String newName = display.getFriendlyDisplayName();
+ if (!route.getName().equals(newName)) {
+ route.mName = newName;
+ changed = true;
+ }
+
+ changed |= route.mEnabled != available;
+ route.mEnabled = available;
+
+ changed |= route.setStatusCode(newStatus);
+
+ if (changed) {
+ dispatchRouteChanged(route);
+ }
+
+ if (!available && route == sStatic.mSelectedRoute) {
+ // Oops, no longer available. Reselect the default.
+ final RouteInfo defaultRoute = sStatic.mDefaultAudioVideo;
+ selectRouteStatic(defaultRoute.getSupportedTypes(), defaultRoute);
+ }
+ }
+
+ private static WifiDisplay findMatchingDisplay(WifiDisplay d, WifiDisplay[] displays) {
+ for (int i = 0; i < displays.length; i++) {
+ final WifiDisplay other = displays[i];
+ if (d.getDeviceAddress().equals(other.getDeviceAddress())) {
+ return other;
+ }
+ }
+ return null;
+ }
+
+ private static RouteInfo findWifiDisplayRoute(WifiDisplay d) {
+ final int count = sStatic.mRoutes.size();
+ for (int i = 0; i < count; i++) {
+ final RouteInfo info = sStatic.mRoutes.get(i);
+ if (d.getDeviceAddress().equals(info.mDeviceAddress)) {
+ return info;
+ }
+ }
+ return null;
+ }
+
+ private static Display choosePresentationDisplayForRoute(RouteInfo route, Display[] displays) {
+ if ((route.mSupportedTypes & ROUTE_TYPE_LIVE_VIDEO) != 0) {
+ if (route.mDeviceAddress != null) {
+ // Find the indicated Wifi display by its address.
+ for (Display display : displays) {
+ if (display.getType() == Display.TYPE_WIFI
+ && route.mDeviceAddress.equals(display.getAddress())) {
+ return display;
+ }
+ }
+ return null;
+ }
+
+ if (route == sStatic.mDefaultAudioVideo && displays.length > 0) {
+ // Choose the first presentation display from the list.
+ return displays[0];
+ }
+ }
+ return null;
}
/**
@@ -643,6 +919,19 @@ public class MediaRouter {
int mVolumeHandling = RemoteControlClient.DEFAULT_PLAYBACK_VOLUME_HANDLING;
int mPlaybackStream = AudioManager.STREAM_MUSIC;
VolumeCallbackInfo mVcb;
+ Display mPresentationDisplay;
+
+ String mDeviceAddress;
+ boolean mEnabled = true;
+
+ // A predetermined connection status that can override mStatus
+ private int mStatusCode;
+
+ /** @hide */ public static final int STATUS_NONE = 0;
+ /** @hide */ public static final int STATUS_SCANNING = 1;
+ /** @hide */ public static final int STATUS_CONNECTING = 2;
+ /** @hide */ public static final int STATUS_AVAILABLE = 3;
+ /** @hide */ public static final int STATUS_NOT_AVAILABLE = 4;
private Object mTag;
@@ -711,6 +1000,41 @@ public class MediaRouter {
}
/**
+ * Set this route's status by predetermined status code. If the caller
+ * should dispatch a route changed event this call will return true;
+ */
+ boolean setStatusCode(int statusCode) {
+ if (statusCode != mStatusCode) {
+ mStatusCode = statusCode;
+ int resId = 0;
+ switch (statusCode) {
+ case STATUS_SCANNING:
+ resId = com.android.internal.R.string.media_route_status_scanning;
+ break;
+ case STATUS_CONNECTING:
+ resId = com.android.internal.R.string.media_route_status_connecting;
+ break;
+ case STATUS_AVAILABLE:
+ resId = com.android.internal.R.string.media_route_status_available;
+ break;
+ case STATUS_NOT_AVAILABLE:
+ resId = com.android.internal.R.string.media_route_status_not_available;
+ break;
+ }
+ mStatus = resId != 0 ? sStatic.mResources.getText(resId) : null;
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * @hide
+ */
+ public int getStatusCode() {
+ return mStatusCode;
+ }
+
+ /**
* @return A media type flag set describing which types this route supports.
*/
public int getSupportedTypes() {
@@ -866,6 +1190,45 @@ public class MediaRouter {
return mVolumeHandling;
}
+ /**
+ * Gets the {@link Display} that should be used by the application to show
+ * a {@link android.app.Presentation} on an external display when this route is selected.
+ * Depending on the route, this may only be valid if the route is currently
+ * selected.
+ * <p>
+ * The preferred presentation display may change independently of the route
+ * being selected or unselected. For example, the presentation display
+ * of the default system route may change when an external HDMI display is connected
+ * or disconnected even though the route itself has not changed.
+ * </p><p>
+ * This method may return null if there is no external display associated with
+ * the route or if the display is not ready to show UI yet.
+ * </p><p>
+ * The application should listen for changes to the presentation display
+ * using the {@link Callback#onRoutePresentationDisplayChanged} callback and
+ * show or dismiss its {@link android.app.Presentation} accordingly when the display
+ * becomes available or is removed.
+ * </p><p>
+ * This method only makes sense for {@link #ROUTE_TYPE_LIVE_VIDEO live video} routes.
+ * </p>
+ *
+ * @return The preferred presentation display to use when this route is
+ * selected or null if none.
+ *
+ * @see #ROUTE_TYPE_LIVE_VIDEO
+ * @see android.app.Presentation
+ */
+ public Display getPresentationDisplay() {
+ return mPresentationDisplay;
+ }
+
+ /**
+ * @return true if this route is enabled and may be selected
+ */
+ public boolean isEnabled() {
+ return mEnabled;
+ }
+
void setStatusInt(CharSequence status) {
if (!status.equals(mStatus)) {
mStatus = status;
@@ -881,7 +1244,6 @@ public class MediaRouter {
sStatic.mHandler.post(new Runnable() {
@Override
public void run() {
- //Log.d(TAG, "dispatchRemoteVolumeUpdate dir=" + direction + " val=" + value);
if (mVcb != null) {
if (direction != 0) {
mVcb.vcb.onVolumeUpdateRequest(mVcb.route, direction);
@@ -901,9 +1263,11 @@ public class MediaRouter {
@Override
public String toString() {
String supportedTypes = typesToString(getSupportedTypes());
- return getClass().getSimpleName() + "{ name=" + getName() + ", status=" + getStatus() +
- " category=" + getCategory() +
- " supportedTypes=" + supportedTypes + "}";
+ return getClass().getSimpleName() + "{ name=" + getName() +
+ ", status=" + getStatus() +
+ ", category=" + getCategory() +
+ ", supportedTypes=" + supportedTypes +
+ ", presentationDisplay=" + mPresentationDisplay + "}";
}
}
@@ -1400,6 +1764,7 @@ public class MediaRouter {
int mNameResId;
int mTypes;
final boolean mGroupable;
+ boolean mIsSystem;
RouteCategory(CharSequence name, int types, boolean groupable) {
mName = name;
@@ -1486,6 +1851,14 @@ public class MediaRouter {
return mGroupable;
}
+ /**
+ * @return true if this is the category reserved for system routes.
+ * @hide
+ */
+ public boolean isSystem() {
+ return mIsSystem;
+ }
+
public String toString() {
return "RouteCategory{ name=" + mName + " types=" + typesToString(mTypes) +
" groupable=" + mGroupable + " }";
@@ -1589,6 +1962,21 @@ public class MediaRouter {
* @param info The route with altered volume
*/
public abstract void onRouteVolumeChanged(MediaRouter router, RouteInfo info);
+
+ /**
+ * Called when a route's presentation display changes.
+ * <p>
+ * This method is called whenever the route's presentation display becomes
+ * available, is removes or has changes to some of its properties (such as its size).
+ * </p>
+ *
+ * @param router the MediaRouter reporting the event
+ * @param info The route whose presentation display changed
+ *
+ * @see RouteInfo#getPresentationDisplay()
+ */
+ public void onRoutePresentationDisplayChanged(MediaRouter router, RouteInfo info) {
+ }
}
/**
@@ -1671,7 +2059,6 @@ public class MediaRouter {
}
static class VolumeChangeReceiver extends BroadcastReceiver {
-
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(AudioManager.VOLUME_CHANGED_ACTION)) {
@@ -1689,6 +2076,15 @@ public class MediaRouter {
}
}
}
+ }
+ static class WifiDisplayStatusChangedReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(DisplayManager.ACTION_WIFI_DISPLAY_STATUS_CHANGED)) {
+ updateWifiDisplayStatus((WifiDisplayStatus) intent.getParcelableExtra(
+ DisplayManager.EXTRA_WIFI_DISPLAY_STATUS));
+ }
+ }
}
}
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/media/Ringtone.java b/media/java/android/media/Ringtone.java
index 23f7b55..f190eb9 100644
--- a/media/java/android/media/Ringtone.java
+++ b/media/java/android/media/Ringtone.java
@@ -225,8 +225,9 @@ public class Ringtone {
mLocalPlayer.start();
}
} else if (mAllowRemote) {
+ final Uri canonicalUri = mUri.getCanonicalUri();
try {
- mRemotePlayer.play(mRemoteToken, mUri, mStreamType);
+ mRemotePlayer.play(mRemoteToken, canonicalUri, mStreamType);
} catch (RemoteException e) {
Log.w(TAG, "Problem playing ringtone: " + e);
}
diff --git a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
index 4756078..f4fccbe 100644
--- a/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
+++ b/media/java/android/media/videoeditor/MediaArtistNativeHelper.java
@@ -3831,6 +3831,7 @@ class MediaArtistNativeHelper {
outWidth, outHeight, Bitmap.Config.ARGB_8888);
// Copy int[] to IntBuffer
+ decBuffer.rewind();
decBuffer.put(decArray, 0, thumbnailSize);
decBuffer.rewind();
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;
}
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
index 4941ae5..f91c9a0 100644
--- a/media/jni/android_media_MediaCodec.cpp
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -370,7 +370,7 @@ static void android_media_MediaCodec_native_configure(
sp<ISurfaceTexture> surfaceTexture;
if (jsurface != NULL) {
- sp<Surface> surface(Surface_getSurface(env, jsurface));
+ sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
if (surface != NULL) {
surfaceTexture = surface->getSurfaceTexture();
} else {
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
index 351ff04..23949fa 100644
--- a/media/jni/android_media_MediaExtractor.cpp
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -44,6 +44,72 @@ struct fields_t {
static fields_t gFields;
+class JavaDataSourceBridge : public DataSource {
+ jmethodID mReadMethod;
+ jmethodID mGetSizeMethod;
+ jmethodID mCloseMethod;
+ jobject mDataSource;
+ public:
+ JavaDataSourceBridge(JNIEnv *env, jobject source) {
+ mDataSource = env->NewGlobalRef(source);
+
+ jclass datasourceclass = env->GetObjectClass(mDataSource);
+ CHECK(datasourceclass != NULL);
+
+ mReadMethod = env->GetMethodID(datasourceclass, "readAt", "(J[BI)I");
+ CHECK(mReadMethod != NULL);
+
+ mGetSizeMethod = env->GetMethodID(datasourceclass, "getSize", "()J");
+ CHECK(mGetSizeMethod != NULL);
+
+ mCloseMethod = env->GetMethodID(datasourceclass, "close", "()V");
+ CHECK(mCloseMethod != NULL);
+ }
+
+ ~JavaDataSourceBridge() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+ env->CallVoidMethod(mDataSource, mCloseMethod);
+ env->DeleteGlobalRef(mDataSource);
+ }
+
+ virtual status_t initCheck() const {
+ return OK;
+ }
+
+ virtual ssize_t readAt(off64_t offset, void* buffer, size_t size) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ // XXX could optimize this by reusing the same array
+ jbyteArray byteArrayObj = env->NewByteArray(size);
+ env->DeleteLocalRef(env->GetObjectClass(mDataSource));
+ env->DeleteLocalRef(env->GetObjectClass(byteArrayObj));
+ ssize_t numread = env->CallIntMethod(mDataSource, mReadMethod, offset, byteArrayObj, size);
+ env->GetByteArrayRegion(byteArrayObj, 0, size, (jbyte*) buffer);
+ env->DeleteLocalRef(byteArrayObj);
+ if (env->ExceptionCheck()) {
+ ALOGW("Exception occurred while reading %d at %lld", size, offset);
+ LOGW_EX(env);
+ env->ExceptionClear();
+ return -1;
+ }
+ return numread;
+ }
+
+ virtual status_t getSize(off64_t *size) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ CHECK(size != NULL);
+
+ int64_t len = env->CallLongMethod(mDataSource, mGetSizeMethod);
+ if (len < 0) {
+ *size = ERROR_UNSUPPORTED;
+ } else {
+ *size = len;
+ }
+ return OK;
+ }
+};
+
////////////////////////////////////////////////////////////////////////////////
JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
@@ -76,6 +142,10 @@ status_t JMediaExtractor::setDataSource(int fd, off64_t offset, off64_t size) {
return mImpl->setDataSource(fd, offset, size);
}
+status_t JMediaExtractor::setDataSource(const sp<DataSource> &datasource) {
+ return mImpl->setDataSource(datasource);
+}
+
size_t JMediaExtractor::countTracks() const {
return mImpl->countTracks();
}
@@ -625,6 +695,33 @@ static void android_media_MediaExtractor_setDataSourceFd(
}
}
+static void android_media_MediaExtractor_setDataSourceCallback(
+ JNIEnv *env, jobject thiz,
+ jobject callbackObj) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ if (callbackObj == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
+
+ sp<JavaDataSourceBridge> bridge = new JavaDataSourceBridge(env, callbackObj);
+ status_t err = extractor->setDataSource(bridge);
+
+ if (err != OK) {
+ jniThrowException(
+ env,
+ "java/io/IOException",
+ "Failed to instantiate extractor.");
+ return;
+ }
+}
+
static jlong android_media_MediaExtractor_getCachedDurationUs(
JNIEnv *env, jobject thiz) {
sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
@@ -713,6 +810,9 @@ static JNINativeMethod gMethods[] = {
{ "setDataSource", "(Ljava/io/FileDescriptor;JJ)V",
(void *)android_media_MediaExtractor_setDataSourceFd },
+ { "setDataSource", "(Landroid/media/DataSource;)V",
+ (void *)android_media_MediaExtractor_setDataSourceCallback },
+
{ "getCachedDuration", "()J",
(void *)android_media_MediaExtractor_getCachedDurationUs },
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
index 2d4627e..03900db 100644
--- a/media/jni/android_media_MediaExtractor.h
+++ b/media/jni/android_media_MediaExtractor.h
@@ -19,6 +19,7 @@
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/DataSource.h>
#include <utils/Errors.h>
#include <utils/KeyedVector.h>
#include <utils/RefBase.h>
@@ -39,6 +40,7 @@ struct JMediaExtractor : public RefBase {
const KeyedVector<String8, String8> *headers);
status_t setDataSource(int fd, off64_t offset, off64_t size);
+ status_t setDataSource(const sp<DataSource> &source);
size_t countTracks() const;
status_t getTrackFormat(size_t index, jobject *format) const;
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index c2a6889..ad536f2 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -271,9 +271,14 @@ setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlaye
sp<ISurfaceTexture> new_st;
if (jsurface) {
- sp<Surface> surface(Surface_getSurface(env, jsurface));
+ sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
if (surface != NULL) {
new_st = surface->getSurfaceTexture();
+ if (new_st == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException",
+ "The surface does not have a binding SurfaceTexture!");
+ return;
+ }
new_st->incStrong(thiz);
} else {
jniThrowException(env, "java/lang/IllegalArgumentException",
diff --git a/media/jni/android_mtp_MtpDatabase.cpp b/media/jni/android_mtp_MtpDatabase.cpp
index 99e543b..bc65de5 100644
--- a/media/jni/android_mtp_MtpDatabase.cpp
+++ b/media/jni/android_mtp_MtpDatabase.cpp
@@ -751,13 +751,22 @@ MtpResponseCode MyMtpDatabase::getObjectPropertyList(MtpObjectHandle handle,
MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
MtpObjectInfo& info) {
- char date[20];
+ char date[20];
+ MtpString path;
+ int64_t length;
+ MtpObjectFormat format;
+
+ MtpResponseCode result = getObjectFilePath(handle, path, length, format);
+ if (result != MTP_RESPONSE_OK) {
+ return result;
+ }
+ info.mCompressedSize = (length > 0xFFFFFFFFLL ? 0xFFFFFFFF : (uint32_t)length);
JNIEnv* env = AndroidRuntime::getJNIEnv();
- jboolean result = env->CallBooleanMethod(mDatabase, method_getObjectInfo,
- (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer);
- if (!result)
+ if (!env->CallBooleanMethod(mDatabase, method_getObjectInfo,
+ (jint)handle, mIntBuffer, mStringBuffer, mLongBuffer)) {
return MTP_RESPONSE_INVALID_OBJECT_HANDLE;
+ }
jint* intValues = env->GetIntArrayElements(mIntBuffer, 0);
info.mStorageID = intValues[0];
@@ -766,9 +775,7 @@ MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
env->ReleaseIntArrayElements(mIntBuffer, intValues, 0);
jlong* longValues = env->GetLongArrayElements(mLongBuffer, 0);
- uint64_t size = longValues[0];
- info.mCompressedSize = (size > 0xFFFFFFFFLL ? 0xFFFFFFFF : size);
- info.mDateModified = longValues[1];
+ info.mDateModified = longValues[0];
env->ReleaseLongArrayElements(mLongBuffer, longValues, 0);
// info.mAssociationType = (format == MTP_FORMAT_ASSOCIATION ?
@@ -783,28 +790,23 @@ MtpResponseCode MyMtpDatabase::getObjectInfo(MtpObjectHandle handle,
// read EXIF data for thumbnail information
if (info.mFormat == MTP_FORMAT_EXIF_JPEG || info.mFormat == MTP_FORMAT_JFIF) {
- MtpString path;
- int64_t length;
- MtpObjectFormat format;
- if (getObjectFilePath(handle, path, length, format) == MTP_RESPONSE_OK) {
- ResetJpgfile();
- // Start with an empty image information structure.
- memset(&ImageInfo, 0, sizeof(ImageInfo));
- ImageInfo.FlashUsed = -1;
- ImageInfo.MeteringMode = -1;
- ImageInfo.Whitebalance = -1;
- strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX);
- if (ReadJpegFile((const char*)path, READ_METADATA)) {
- Section_t* section = FindSection(M_EXIF);
- if (section) {
- info.mThumbCompressedSize = ImageInfo.ThumbnailSize;
- info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
- info.mImagePixWidth = ImageInfo.Width;
- info.mImagePixHeight = ImageInfo.Height;
- }
+ ResetJpgfile();
+ // Start with an empty image information structure.
+ memset(&ImageInfo, 0, sizeof(ImageInfo));
+ ImageInfo.FlashUsed = -1;
+ ImageInfo.MeteringMode = -1;
+ ImageInfo.Whitebalance = -1;
+ strncpy(ImageInfo.FileName, (const char *)path, PATH_MAX);
+ if (ReadJpegFile((const char*)path, READ_METADATA)) {
+ Section_t* section = FindSection(M_EXIF);
+ if (section) {
+ info.mThumbCompressedSize = ImageInfo.ThumbnailSize;
+ info.mThumbFormat = MTP_FORMAT_EXIF_JPEG;
+ info.mImagePixWidth = ImageInfo.Width;
+ info.mImagePixHeight = ImageInfo.Height;
}
- DiscardData();
}
+ DiscardData();
}
checkAndClearExceptionFromCallback(env, __FUNCTION__);
diff --git a/media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java b/media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java
index d5c7aaa..f977e60 100644
--- a/media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java
+++ b/media/mca/effect/java/android/media/effect/effects/BackDropperEffect.java
@@ -91,6 +91,9 @@ public class BackDropperEffect extends FilterGraphEffect {
if (parameterKey.equals("source")) {
Filter background = mGraph.getFilter("background");
background.setInputValue("sourceUrl", value);
+ } else if (parameterKey.equals("context")) {
+ Filter background = mGraph.getFilter("background");
+ background.setInputValue("context", value);
}
}
diff --git a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
index 3450ef1..8618804 100644
--- a/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
+++ b/media/mca/filterpacks/java/android/filterpacks/imageproc/RedEyeFilter.java
@@ -72,9 +72,7 @@ public class RedEyeFilter extends Filter {
"void main() {\n" +
" vec4 color = texture2D(tex_sampler_0, v_texcoord);\n" +
" vec4 mask = texture2D(tex_sampler_1, v_texcoord);\n" +
- " gl_FragColor = vec4(mask.a, mask.a, mask.a, 1.0) * intensity + color * (1.0 - intensity);\n" +
" if (mask.a > 0.0) {\n" +
- " gl_FragColor.r = 0.0;\n" +
" float green_blue = color.g + color.b;\n" +
" float red_intensity = color.r / green_blue;\n" +
" if (red_intensity > intensity) {\n" +
@@ -105,8 +103,8 @@ public class RedEyeFilter extends Filter {
ShaderProgram shaderProgram = new ShaderProgram(context, mRedEyeShader);
shaderProgram.setMaximumTileSize(mTileSize);
mProgram = shaderProgram;
+ mProgram.setHostValue("intensity", DEFAULT_RED_INTENSITY);
break;
-
default:
throw new RuntimeException("Filter RedEye does not support frames of " +
"target " + target + "!");
@@ -180,8 +178,6 @@ public class RedEyeFilter extends Filter {
}
private void updateProgramParams() {
- mProgram.setHostValue("intensity", DEFAULT_RED_INTENSITY);
-
if ( mCenters.length % 2 == 1) {
throw new RuntimeException("The size of center array must be even.");
}
diff --git a/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java b/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java
index 9c40cec..0be6c62 100644
--- a/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java
+++ b/media/mca/filterpacks/java/android/filterpacks/videosrc/MediaSource.java
@@ -35,6 +35,7 @@ import android.filterfw.core.ShaderProgram;
import android.filterfw.format.ImageFormat;
import android.graphics.SurfaceTexture;
import android.media.MediaPlayer;
+import android.net.Uri;
import android.os.ConditionVariable;
import android.opengl.Matrix;
import android.view.Surface;
@@ -64,6 +65,12 @@ public class MediaSource extends Filter {
@GenerateFieldPort(name = "sourceAsset", hasDefault = true)
private AssetFileDescriptor mSourceAsset = null;
+ /** The context for the MediaPlayer to resolve the sourceUrl.
+ * Make sure this is set before the sourceUrl to avoid unexpected result.
+ * If the sourceUrl is not a content URI, it is OK to keep this as null. */
+ @GenerateFieldPort(name = "context", hasDefault = true)
+ private Context mContext = null;
+
/** Whether the media source is a URL or an asset file descriptor. Defaults
* to false.
*/
@@ -459,7 +466,11 @@ public class MediaSource extends Filter {
try {
if (useUrl) {
if (mLogVerbose) Log.v(TAG, "Setting MediaPlayer source to URI " + mSourceUrl);
- mMediaPlayer.setDataSource(mSourceUrl);
+ if (mContext == null) {
+ mMediaPlayer.setDataSource(mSourceUrl);
+ } else {
+ mMediaPlayer.setDataSource(mContext, Uri.parse(mSourceUrl.toString()));
+ }
} else {
if (mLogVerbose) Log.v(TAG, "Setting MediaPlayer source to asset " + mSourceAsset);
mMediaPlayer.setDataSource(mSourceAsset.getFileDescriptor(), mSourceAsset.getStartOffset(), mSourceAsset.getLength());
diff --git a/media/tests/EffectsTest/Android.mk b/media/tests/EffectsTest/Android.mk
new file mode 100755
index 0000000..25b4fe4
--- /dev/null
+++ b/media/tests/EffectsTest/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := EffectsTest
+
+include $(BUILD_PACKAGE)
diff --git a/media/tests/EffectsTest/AndroidManifest.xml b/media/tests/EffectsTest/AndroidManifest.xml
new file mode 100755
index 0000000..9b59891
--- /dev/null
+++ b/media/tests/EffectsTest/AndroidManifest.xml
@@ -0,0 +1,57 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.effectstest">
+
+ <uses-permission android:name="android.permission.RECORD_AUDIO" />
+ <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
+
+ <application>
+ <activity android:label="@string/app_name"
+ android:name="EffectsTest">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER"/>
+ </intent-filter>
+ </activity>
+
+ <activity android:label="@string/envreverb_test_name"
+ android:name="EnvReverbTest">
+ </activity>
+
+ <activity android:label="@string/presetrvb_test_name"
+ android:name="PresetReverbTest">
+ </activity>
+
+ <activity android:label="@string/equalizer_test_name"
+ android:name="EqualizerTest">
+ </activity>
+
+ <activity android:label="@string/virtualizer_test_name"
+ android:name="VirtualizerTest">
+ </activity>
+
+ <activity android:label="@string/bassboost_test_name"
+ android:name="BassBoostTest">
+ </activity>
+
+ <activity android:label="@string/visualizer_test_name"
+ android:name="VisualizerTest">
+ </activity>
+
+ </application>
+</manifest>
diff --git a/media/tests/EffectsTest/res/drawable/icon.png b/media/tests/EffectsTest/res/drawable/icon.png
new file mode 100755
index 0000000..64e3601
--- /dev/null
+++ b/media/tests/EffectsTest/res/drawable/icon.png
Binary files differ
diff --git a/media/tests/EffectsTest/res/drawable/stop.png b/media/tests/EffectsTest/res/drawable/stop.png
new file mode 100755
index 0000000..83f012c
--- /dev/null
+++ b/media/tests/EffectsTest/res/drawable/stop.png
Binary files differ
diff --git a/media/tests/EffectsTest/res/layout/bassboosttest.xml b/media/tests/EffectsTest/res/layout/bassboosttest.xml
new file mode 100755
index 0000000..ac912c8
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/bassboosttest.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/bbReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/bbReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/bbReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/bbControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/bbControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/bassboostOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/SessionFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/session"
+ style="@android:style/TextAppearance.Medium" />
+
+ <EditText android:id="@+id/sessionEdit"
+ android:singleLine="true"
+ android:numeric="integer"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|right" />
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/bbStrengthName"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/stength_name" />
+
+ <LinearLayout android:id="@+id/bbStrengthDesc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="30dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/bbStrengthMin"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_gravity="left"
+ android:gravity="left"/>
+ <TextView android:id="@+id/bbStrengthMax"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_gravity="right"
+ android:gravity="right"/>
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/bbStrengthSeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/bbStrengthValue"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/effectstest.xml b/media/tests/EffectsTest/res/layout/effectstest.xml
new file mode 100755
index 0000000..9af4eb6
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/effectstest.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <Button android:id="@+id/env_reverb_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/envreverb_test_name">
+ </Button>
+
+ <Button android:id="@+id/preset_reverb_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/presetrvb_test_name">
+ </Button>
+
+ <Button android:id="@+id/equalizer_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/equalizer_test_name">
+ </Button>
+
+ <Button android:id="@+id/virtualizer_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/virtualizer_test_name">
+ </Button>
+
+ <Button android:id="@+id/bassboost_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/bassboost_test_name">
+ </Button>
+
+ <Button android:id="@+id/visualizer_actvity"
+ android:layout_width="fill_parent" android:layout_height="wrap_content"
+ android:text="@string/visualizer_test_name">
+ </Button>
+
+ <ListView android:id="@+id/effect_list"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:drawSelectorOnTop="false"/>
+
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/envreverbtest.xml b/media/tests/EffectsTest/res/layout/envreverbtest.xml
new file mode 100755
index 0000000..01c3240
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/envreverbtest.xml
@@ -0,0 +1,553 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/rvbReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/rvbReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/rvbReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/rvbControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/rvbControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/rvbOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/auxFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="3dip"
+ android:layout_marginTop="3dip"
+ android:layout_marginRight="3dip"
+ android:layout_marginBottom="3dip" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_gravity="left"
+ android:layout_weight="1.0"
+ android:orientation="vertical"
+ android:layout_marginLeft="1dip"
+ android:layout_marginTop="1dip"
+ android:layout_marginRight="1dip"
+ android:layout_marginBottom="1dip"
+ >
+
+ <LinearLayout android:id="@+id/playPauseFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="1dip"
+ android:layout_marginTop="1dip"
+ android:layout_marginRight="3dip"
+ android:layout_marginBottom="1dip" >
+
+ <ImageButton android:id="@+id/stop1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|left"
+ android:layout_weight="0.0"
+ android:src="@drawable/stop"/>
+
+ <ImageButton android:id="@+id/playPause1"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|center"
+ android:layout_weight="0.0"
+ android:src="@android:drawable/ic_media_play"/>
+
+ <ToggleButton android:id="@+id/attachButton"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0"
+ android:textOff="@string/effect_attach_off"
+ android:textOn="@string/effect_attach_on" />
+ </LinearLayout>
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical"
+ android:layout_gravity="right"
+ android:layout_weight="1.0"
+ android:layout_marginLeft="3dip"
+ android:layout_marginTop="3dip"
+ android:layout_marginRight="1dip"
+ android:layout_marginBottom="3dip"
+ >
+
+ <TextView android:id="@+id/sendLevelText"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/send_level_name" />
+
+ <SeekBar android:id="@+id/sendLevelSeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/sendLevelValue"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam1Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_1_name" />
+
+ <SeekBar android:id="@+id/rvbParam1SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam1Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam2Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_2_name" />
+
+ <SeekBar android:id="@+id/rvbParam2SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam2Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam3Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_3_name" />
+
+ <SeekBar android:id="@+id/rvbParam3SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam3Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam4Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_4_name" />
+
+ <SeekBar android:id="@+id/rvbParam4SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam4Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam5Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_5_name" />
+
+ <SeekBar android:id="@+id/rvbParam5SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam5Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam6Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_6_name" />
+
+ <SeekBar android:id="@+id/rvbParam6SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam6Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam7Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_7_name" />
+
+ <SeekBar android:id="@+id/rvbParam7SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam7Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam8Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_8_name" />
+
+ <SeekBar android:id="@+id/rvbParam8SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam8Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam9Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_9_name" />
+
+ <SeekBar android:id="@+id/rvbParam9SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam9Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/rvbParam10Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/rvb_param_10_name" />
+
+ <SeekBar android:id="@+id/rvbParam10SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/rvbParam10Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+ </LinearLayout>
+
+ </ScrollView>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/equalizertest.xml b/media/tests/EffectsTest/res/layout/equalizertest.xml
new file mode 100755
index 0000000..5ef035d
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/equalizertest.xml
@@ -0,0 +1,470 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/eqReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/eqReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/eqControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/equalizerOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/SessionFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/session"
+ style="@android:style/TextAppearance.Medium" />
+
+ <EditText android:id="@+id/sessionEdit"
+ android:singleLine="true"
+ android:numeric="integer"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|right" />
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam1Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_1_name" />
+
+ <LinearLayout android:id="@+id/eqParam1Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqParam1Min"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam1Center"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam1Max"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/eqParam1SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam1Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam2Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_2_name" />
+
+ <LinearLayout android:id="@+id/eqParam2Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqParam2Min"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam2Center"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam2Max"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/eqParam2SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam2Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam3Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_3_name" />
+
+ <LinearLayout android:id="@+id/eqParam3Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqParam3Min"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam3Center"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam3Max"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/eqParam3SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam3Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam4Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_4_name" />
+
+ <LinearLayout android:id="@+id/eqParam4Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqParam4Min"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam4Center"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam4Max"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/eqParam4SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam4Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam5Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_5_name" />
+
+ <LinearLayout android:id="@+id/eqParam5Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/eqParam5Min"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam5Center"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/eqParam5Max"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/eqParam5SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam5Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/eqParam6Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/eq_param_6_name" />
+
+ <SeekBar android:id="@+id/eqParam6SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/eqParam6Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/presetreverbtest.xml b/media/tests/EffectsTest/res/layout/presetreverbtest.xml
new file mode 100755
index 0000000..cd7fbd3
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/presetreverbtest.xml
@@ -0,0 +1,179 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/presetrvbReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/presetrvbReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/presetrvbReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/presetrvbControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/presetrvbControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/presetrvbOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/SessionFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/session"
+ style="@android:style/TextAppearance.Medium" />
+
+ <EditText android:id="@+id/sessionEdit"
+ android:singleLine="true"
+ android:numeric="integer"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|right" />
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/presetrvbParam1Name"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/presetrvb_param_1_name" />
+
+ <SeekBar android:id="@+id/presetrvbParam1SeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/presetrvbParam1Value"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/virtualizertest.xml b/media/tests/EffectsTest/res/layout/virtualizertest.xml
new file mode 100755
index 0000000..1fafeab
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/virtualizertest.xml
@@ -0,0 +1,194 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/virtReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/virtReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/virtReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/virtControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/virtControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/virtualizerOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/SessionFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/session"
+ style="@android:style/TextAppearance.Medium" />
+
+ <EditText android:id="@+id/sessionEdit"
+ android:singleLine="true"
+ android:numeric="integer"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|right" />
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/virtStrengthName"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/stength_name" />
+
+ <LinearLayout android:id="@+id/virtStrengthDesc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="30dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/virtStrengthMin"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_gravity="left"
+ android:gravity="left"/>
+ <TextView android:id="@+id/virtStrengthMax"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1"
+ android:layout_gravity="right"
+ android:gravity="right"/>
+ </LinearLayout>
+
+ <SeekBar android:id="@+id/virtStrengthSeekBar"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:max="100"
+ android:progress="50"
+ android:layout_marginLeft="10dip"
+ android:layout_marginRight="30dip" />
+
+ <TextView android:id="@+id/virtStrengthValue"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/layout/visualizertest.xml b/media/tests/EffectsTest/res/layout/visualizertest.xml
new file mode 100755
index 0000000..50ac7bb
--- /dev/null
+++ b/media/tests/EffectsTest/res/layout/visualizertest.xml
@@ -0,0 +1,263 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:orientation="vertical">
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/visuReleaseLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/visuReleaseText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_release"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/visuReleaseButton"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/visuControlLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/visuControlText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/effect_control"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/visualizerOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/SessionFrame"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/sessionText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/session"
+ style="@android:style/TextAppearance.Medium" />
+
+ <EditText android:id="@+id/sessionEdit"
+ android:singleLine="true"
+ android:numeric="integer"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="0.5"
+ android:layout_gravity="center_vertical|right" />
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout android:id="@+id/visuCallbackLayout"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/visuCallbackText"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0"
+ android:layout_gravity="center_vertical|left"
+ android:text="@string/visu_callback"
+ style="@android:style/TextAppearance.Medium" />
+
+ <ToggleButton android:id="@+id/visuCallbackOnOff"
+ android:layout_width="wrap_content"
+ android:layout_height="fill_parent"
+ android:layout_gravity="center_vertical|right"
+ android:layout_weight="0.0" />
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <ScrollView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content" >
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/waveformName"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/waveform_name" />
+
+ <LinearLayout android:id="@+id/eqParam1Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/waveformMin"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/waveformCenter"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/waveformMax"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+ <LinearLayout
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:gravity="center_horizontal"
+ android:orientation="vertical"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip"
+ >
+
+ <TextView android:id="@+id/fftName"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:text="@string/fft_name" />
+
+ <LinearLayout android:id="@+id/eqParam1Desc"
+ android:orientation="horizontal"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="10dip"
+ android:layout_marginTop="10dip"
+ android:layout_marginRight="10dip"
+ android:layout_marginBottom="10dip" >
+
+ <TextView android:id="@+id/fftMin"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/fftCenter"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+ <TextView android:id="@+id/fftMax"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:layout_weight="1.0" />
+
+ </LinearLayout>
+
+ </LinearLayout>
+
+ <ImageView
+ android:src="@android:drawable/divider_horizontal_dark"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:scaleType="fitXY"/>
+
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
diff --git a/media/tests/EffectsTest/res/raw/mp3_sample.mp3 b/media/tests/EffectsTest/res/raw/mp3_sample.mp3
new file mode 100644
index 0000000..a9d8635
--- /dev/null
+++ b/media/tests/EffectsTest/res/raw/mp3_sample.mp3
Binary files differ
diff --git a/media/tests/EffectsTest/res/raw/sine440_mo_16b_16k.wav b/media/tests/EffectsTest/res/raw/sine440_mo_16b_16k.wav
new file mode 100644
index 0000000..2538b4d6
--- /dev/null
+++ b/media/tests/EffectsTest/res/raw/sine440_mo_16b_16k.wav
Binary files differ
diff --git a/media/tests/EffectsTest/res/values/strings.xml b/media/tests/EffectsTest/res/values/strings.xml
new file mode 100755
index 0000000..2a85184
--- /dev/null
+++ b/media/tests/EffectsTest/res/values/strings.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="app_name">Effects Test</string>
+ <string name="effect_control">Effect State</string>
+ <string name="effect_release">Effect Instantiated</string>
+ <string name="effect_bypass">Bypass</string>
+ <string name="envreverb_test_name">Environmental Reverb Test</string>
+ <string name="rvb_param_1_name">Room Level</string>
+ <string name="rvb_param_2_name">Room HF Level</string>
+ <string name="rvb_param_3_name">Decay Time</string>
+ <string name="rvb_param_4_name">Decay HF Ratio</string>
+ <string name="rvb_param_5_name">Reflections Level</string>
+ <string name="rvb_param_6_name">Reflections Delay</string>
+ <string name="rvb_param_7_name">Reverb Level</string>
+ <string name="rvb_param_8_name">Reverb Delay</string>
+ <string name="rvb_param_9_name">Diffusion</string>
+ <string name="rvb_param_10_name">Density</string>
+ <string name="presetrvb_test_name">Preset Reverb Test</string>
+ <string name="presetrvb_param_1_name">Presets</string>
+ <string name="equalizer_test_name">Equalizer Test</string>
+ <string name="session">Audio Session</string>
+ <string name="eq_param_1_name">Band 1 Level</string>
+ <string name="eq_param_2_name">Band 2 Level</string>
+ <string name="eq_param_3_name">Band 3 Level</string>
+ <string name="eq_param_4_name">Band 4 Level</string>
+ <string name="eq_param_5_name">Band 5 Level</string>
+ <string name="eq_param_6_name">Presets</string>
+ <string name="virtualizer_test_name">Virtualizer Test</string>
+ <string name="stength_name">Strength</string>
+ <string name="bassboost_test_name">Bass Boost Test</string>
+ <string name="visualizer_test_name">Visualizer Test</string>
+ <string name="visu_callback">Callback Mode</string>
+ <string name="waveform_name">PCM capture</string>
+ <string name="fft_name">FFT Capture</string>
+ <string name="effect_attach_off">Attach</string>
+ <string name="effect_attach_on">Detach</string>
+ <string name="send_level_name">Send Level</string>
+</resources>
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
new file mode 100755
index 0000000..1a10d64
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/BassBoostTest.java
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2009 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 com.android.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.media.audiofx.BassBoost;
+import android.media.audiofx.AudioEffect;
+
+public class BassBoostTest extends Activity implements OnCheckedChangeListener {
+
+ private final static String TAG = "BassBoostTest";
+
+ private static int NUM_PARAMS = 1;
+
+ private EffectParameter mStrength;
+ private BassBoost mBassBoost = null;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ EditText mSessionText;
+ static int sSession = 0;
+ EffectListner mEffectListener = new EffectListner();
+ private static HashMap<Integer, BassBoost> sInstances = new HashMap<Integer, BassBoost>(10);
+ String mSettings = "";
+
+ public BassBoostTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ SeekBar seekBar;
+ TextView textView;
+
+ setContentView(R.layout.bassboosttest);
+
+ mSessionText = (EditText) findViewById(R.id.sessionEdit);
+ mSessionText.setOnKeyListener(mSessionKeyListener);
+
+ mSessionText.setText(Integer.toString(sSession));
+
+ mReleaseButton = (ToggleButton)findViewById(R.id.bbReleaseButton);
+ mOnOffButton = (ToggleButton)findViewById(R.id.bassboostOnOff);
+
+ getEffect(sSession);
+
+ if (mBassBoost != null) {
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mOnOffButton.setOnCheckedChangeListener(this);
+
+ textView = (TextView)findViewById(R.id.bbStrengthMin);
+ textView.setText("0");
+ textView = (TextView)findViewById(R.id.bbStrengthMax);
+ textView.setText("1000");
+ seekBar = (SeekBar)findViewById(R.id.bbStrengthSeekBar);
+ textView = (TextView)findViewById(R.id.bbStrengthValue);
+ mStrength = new BassBoostParam(mBassBoost, 0, 1000, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mStrength);
+ mStrength.setEnabled(mBassBoost.getStrengthSupported());
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private View.OnKeyListener mSessionKeyListener
+ = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ Log.d(TAG, "onKey() keyCode: "+keyCode+" event.getAction(): "+event.getAction());
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ try {
+ sSession = Integer.parseInt(mSessionText.getText().toString());
+ getEffect(sSession);
+ if (mBassBoost != null) {
+ mStrength.setEffect(mBassBoost);
+ mStrength.setEnabled(mBassBoost.getStrengthSupported());
+ }
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString());
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.bassboostOnOff) {
+ if (mBassBoost != null) {
+ mBassBoost.setEnabled(isChecked);
+ mStrength.updateDisplay();
+ }
+ }
+ if (buttonView.getId() == R.id.bbReleaseButton) {
+ if (isChecked) {
+ if (mBassBoost == null) {
+ getEffect(sSession);
+ if (mBassBoost != null) {
+ mStrength.setEffect(mBassBoost);
+ mStrength.setEnabled(mBassBoost.getStrengthSupported());
+ }
+ }
+ } else {
+ if (mBassBoost != null) {
+ mStrength.setEnabled(false);
+ putEffect(sSession);
+ }
+ }
+ }
+ }
+
+ private class BassBoostParam extends EffectParameter {
+ private BassBoost mBassBoost;
+
+ public BassBoostParam(BassBoost bassboost, int min, int max, SeekBar seekBar, TextView textView) {
+ super (min, max, seekBar, textView, "o/oo");
+
+ mBassBoost = bassboost;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mBassBoost != null) {
+ mBassBoost.setStrength(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mBassBoost != null) {
+ return new Integer(mBassBoost.getRoundedStrength());
+ }
+ return new Integer(0);
+ }
+
+ @Override
+ public void setEffect(Object effect) {
+ mBassBoost = (BassBoost)effect;
+ }
+ }
+
+ public class EffectListner implements AudioEffect.OnEnableStatusChangeListener,
+ AudioEffect.OnControlStatusChangeListener, AudioEffect.OnParameterChangeListener
+ {
+ public EffectListner() {
+ }
+ public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+ Log.d(TAG,"onEnableStatusChange: "+ enabled);
+ }
+ public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
+ Log.d(TAG,"onControlStatusChange: "+ controlGranted);
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ int p = byteArrayToInt(param, 0);
+ short v = byteArrayToShort(value, 0);
+
+ Log.d(TAG,"onParameterChange, status: "+status+" p: "+p+" v: "+v);
+ }
+
+ private int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+ private short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+
+ }
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mBassBoost = sInstances.get(session);
+ } else {
+ try{
+ mBassBoost = new BassBoost(0, session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"BassBoost effect not supported");
+ } catch (IllegalStateException e) {
+ Log.e(TAG,"BassBoost cannot get strength supported");
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"BassBoost library not loaded");
+ } catch (RuntimeException e) {
+ Log.e(TAG,"BassBoost effect not found");
+ }
+ sInstances.put(session, mBassBoost);
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+
+ if (mBassBoost != null) {
+ if (mSettings != "") {
+ mBassBoost.setProperties(new BassBoost.Settings(mSettings));
+ }
+ mBassBoost.setEnableStatusListener(mEffectListener);
+ mBassBoost.setControlStatusListener(mEffectListener);
+ mBassBoost.setParameterListener(mEffectListener);
+
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+
+ mOnOffButton.setChecked(mBassBoost.getEnabled());
+ mOnOffButton.setEnabled(true);
+ }
+ }
+ }
+
+ private void putEffect(int session) {
+ mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mBassBoost != null) {
+ mSettings = mBassBoost.getProperties().toString();
+ mBassBoost.release();
+ Log.d(TAG,"BassBoost released");
+ mBassBoost = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EffectParameter.java b/media/tests/EffectsTest/src/com/android/effectstest/EffectParameter.java
new file mode 100755
index 0000000..95077e7
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EffectParameter.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2009 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 com.android.effectstest;
+
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+import android.widget.SeekBar;
+
+
+abstract class EffectParameter implements SeekBar.OnSeekBarChangeListener {
+
+ private final static String TAG = "EffectParameter";
+
+ protected int mMin;
+ protected int mMax;
+ protected String mUnit;
+ protected SeekBar mSeekBar;
+ protected TextView mValueText;
+
+ public EffectParameter (int min, int max, SeekBar seekBar, TextView textView, String unit) {
+ mMin = min;
+ mMax = max;
+ mSeekBar = seekBar;
+ mValueText = textView;
+ mUnit = unit;
+ byte[] paramBuf = new byte[4];
+
+ mSeekBar.setMax(max-min);
+ }
+
+ public void displayValue(int value, boolean fromTouch) {
+ String text = Integer.toString(value)+" "+mUnit;
+ mValueText.setText(text);
+ if (!fromTouch) {
+ mSeekBar.setProgress(value - mMin);
+ }
+ }
+
+ public void updateDisplay() {
+ displayValue(getParameter(), false);
+ }
+
+ public abstract void setParameter(Integer value);
+
+ public abstract Integer getParameter();
+
+ public abstract void setEffect(Object effect);
+
+ // SeekBar.OnSeekBarChangeListener
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
+
+ if (seekBar != mSeekBar) {
+ Log.e(TAG, "onProgressChanged called with wrong seekBar");
+ return;
+ }
+
+ int value = progress + mMin;
+ if (fromTouch) {
+ setParameter(value);
+ }
+
+ displayValue(getParameter(), fromTouch);
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ public void setEnabled(boolean e) {
+ mSeekBar.setEnabled(e);
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
new file mode 100755
index 0000000..7020246
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EffectsTest.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2009 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 com.android.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.ListView;
+import android.widget.BaseAdapter;
+import android.widget.LinearLayout;
+import android.media.audiofx.AudioEffect;
+
+import java.util.UUID;
+
+public class EffectsTest extends Activity {
+
+ private final static String TAG = "EffectsTest";
+
+
+ public EffectsTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setContentView(R.layout.effectstest);
+
+ Button button = (Button) findViewById(R.id.env_reverb_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, EnvReverbTest.class));
+ }
+ });
+
+ button = (Button) findViewById(R.id.preset_reverb_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, PresetReverbTest.class));
+ }
+ });
+
+ button = (Button) findViewById(R.id.equalizer_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, EqualizerTest.class));
+ }
+ });
+
+ button = (Button) findViewById(R.id.virtualizer_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, VirtualizerTest.class));
+ }
+ });
+
+ button = (Button) findViewById(R.id.bassboost_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, BassBoostTest.class));
+ }
+ });
+
+ button = (Button) findViewById(R.id.visualizer_actvity);
+ button.setOnClickListener(new OnClickListener() {
+ public void onClick(View v) {
+ startActivity(new Intent(EffectsTest.this, VisualizerTest.class));
+ }
+ });
+
+ AudioEffect.Descriptor[] descriptors = AudioEffect.queryEffects();
+
+ ListView list = (ListView) findViewById(R.id.effect_list);
+ list.setAdapter(new EffectListAdapter(this, descriptors));
+
+ }
+
+ private class EffectListAdapter extends BaseAdapter {
+
+ private Context mContext;
+
+ AudioEffect.Descriptor[] mDescriptors;
+
+ public EffectListAdapter(Context context, AudioEffect.Descriptor[] descriptors) {
+ Log.d(TAG, "EffectListAdapter contructor");
+ mContext = context;
+ mDescriptors = descriptors;
+ for (int i = 0; i < mDescriptors.length; i++) {
+ Log.d(TAG, "Effect: "+i+" name: "+ mDescriptors[i].name);
+ }
+ }
+
+ public int getCount() {
+ Log.d(TAG, "EffectListAdapter getCount(): "+mDescriptors.length);
+ return mDescriptors.length;
+ }
+
+ public Object getItem(int position) {
+ Log.d(TAG, "EffectListAdapter getItem() at: "+position+" name: "
+ +mDescriptors[position].name);
+ return mDescriptors[position];
+ }
+
+ public long getItemId(int position) {
+ return position;
+ }
+
+ public View getView(int position, View convertView, ViewGroup parent) {
+ EffectView ev;
+ if (convertView == null) {
+ Log.d(TAG, "getView() new EffectView position: " + position);
+ ev = new EffectView(mContext, mDescriptors);
+ } else {
+ Log.d(TAG, "getView() convertView position: " + position);
+ ev = new EffectView(mContext, mDescriptors);
+ //ev = (EffectView) convertView;
+ }
+ ev.set(position);
+ return ev;
+ }
+ }
+
+ private class EffectView extends LinearLayout {
+ private Context mContext;
+ AudioEffect.Descriptor[] mDescriptors;
+
+ public EffectView(Context context, AudioEffect.Descriptor[] descriptors) {
+ super(context);
+
+ mContext = context;
+ mDescriptors = descriptors;
+ this.setOrientation(VERTICAL);
+ }
+
+ public String effectUuidToString(UUID effectType) {
+ if (effectType.equals(AudioEffect.EFFECT_TYPE_VIRTUALIZER)) {
+ return "Virtualizer";
+ } else if (effectType.equals(AudioEffect.EFFECT_TYPE_ENV_REVERB)){
+ return "Reverb";
+ } else if (effectType.equals(AudioEffect.EFFECT_TYPE_PRESET_REVERB)){
+ return "Preset Reverb";
+ } else if (effectType.equals(AudioEffect.EFFECT_TYPE_EQUALIZER)){
+ return "Equalizer";
+ } else if (effectType.equals(AudioEffect.EFFECT_TYPE_BASS_BOOST)){
+ return "Bass Boost";
+ } else if (effectType.equals(AudioEffect.EFFECT_TYPE_AGC)){
+ return "Automatic Gain Control";
+ } else if (effectType.equals(AudioEffect.EFFECT_TYPE_AEC)){
+ return "Acoustic Echo Canceler";
+ } else if (effectType.equals(AudioEffect.EFFECT_TYPE_NS)){
+ return "Noise Suppressor";
+ }
+
+ return effectType.toString();
+ }
+
+ public void set(int position) {
+ TextView tv = new TextView(mContext);
+ tv.setText("Effect "+ position);
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ tv = new TextView(mContext);
+ tv.setText(" type: "+ effectUuidToString(mDescriptors[position].type));
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ tv = new TextView(mContext);
+ tv.setText(" uuid: "+ mDescriptors[position].uuid.toString());
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ tv = new TextView(mContext);
+ tv.setText(" name: "+ mDescriptors[position].name);
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ tv = new TextView(mContext);
+ tv.setText(" vendor: "+ mDescriptors[position].implementor);
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ tv = new TextView(mContext);
+ tv.setText(" mode: "+ mDescriptors[position].connectMode);
+ addView(tv, new LinearLayout.LayoutParams(
+ LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
+ }
+ }
+
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EnvReverbTest.java b/media/tests/EffectsTest/src/com/android/effectstest/EnvReverbTest.java
new file mode 100755
index 0000000..594e844
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EnvReverbTest.java
@@ -0,0 +1,568 @@
+/*
+ * Copyright (C) 2009 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 com.android.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.media.audiofx.EnvironmentalReverb;
+import android.media.audiofx.AudioEffect;
+import android.media.AudioManager;
+
+public class EnvReverbTest extends Activity implements OnCheckedChangeListener, SeekBar.OnSeekBarChangeListener {
+
+ private final static String TAG = "EnvReverbTest";
+
+ private static int NUM_PARAMS = 10;
+
+ private EffectParameter[] mParameters = new EffectParameter[NUM_PARAMS];
+ private EnvironmentalReverb mReverb;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ ToggleButton mAttachButton;
+ private static HashMap<Integer, EnvironmentalReverb> sInstances = new HashMap<Integer, EnvironmentalReverb>(10);
+ static SimplePlayer sPlayerController = null;
+ SeekBar mSendLevelSeekBar;
+ TextView mSendLevelDisplay;
+ static float sSendLevel = linToExp(50,100);
+ static boolean sAttached = false;
+ String mSettings = "";
+
+ public EnvReverbTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ Log.d(TAG, "onCreate");
+ SeekBar seekBar;
+ TextView textView;
+ ToggleButton button;
+ setContentView(R.layout.envreverbtest);
+
+ ImageView playPause = (ImageView) findViewById(R.id.playPause1);
+ ImageView stop = (ImageView) findViewById(R.id.stop1);
+ textView = (TextView) findViewById(R.id.sessionText);
+ if (sPlayerController == null) {
+ sPlayerController = new SimplePlayer(this, R.id.playPause1, playPause,
+ R.id.stop1, stop, textView,
+ R.raw.mp3_sample, AudioManager.STREAM_MUSIC, 0);
+ } else {
+ sPlayerController.set(this, R.id.playPause1, playPause,
+ R.id.stop1, stop, textView,
+ AudioManager.STREAM_MUSIC, 0);
+ }
+
+ // send level
+ mSendLevelSeekBar = (SeekBar)findViewById(R.id.sendLevelSeekBar);
+ mSendLevelDisplay = (TextView)findViewById(R.id.sendLevelValue);
+ mSendLevelSeekBar.setMax(100);
+ mSendLevelSeekBar.setOnSeekBarChangeListener(this);
+ mSendLevelSeekBar.setProgress(expToLin(sSendLevel,100));
+ sPlayerController.setAuxEffectSendLevel(sSendLevel);
+
+ mOnOffButton = (ToggleButton)findViewById(R.id.rvbOnOff);
+ mReleaseButton = (ToggleButton)findViewById(R.id.rvbReleaseButton);
+ mAttachButton = (ToggleButton)findViewById(R.id.attachButton);
+
+ getEffect(0);
+
+ if (mReverb != null) {
+ mOnOffButton.setOnCheckedChangeListener(this);
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mAttachButton.setOnCheckedChangeListener(this);
+
+// button = (ToggleButton)findViewById(R.id.rvbBypass);
+// button.setChecked(false);
+// button.setOnCheckedChangeListener(this);
+
+ // Room level
+ seekBar = (SeekBar)findViewById(R.id.rvbParam1SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam1Value);
+ mParameters[0] = new RoomLevelParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[0]);
+
+ // Room HF level
+ seekBar = (SeekBar)findViewById(R.id.rvbParam2SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam2Value);
+ mParameters[1] = new RoomHFLevelParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[1]);
+
+ // Decay time
+ seekBar = (SeekBar)findViewById(R.id.rvbParam3SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam3Value);
+ mParameters[2] = new DecayTimeParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[2]);
+
+ // Decay HF ratio
+ seekBar = (SeekBar)findViewById(R.id.rvbParam4SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam4Value);
+ mParameters[3] = new DecayHFRatioParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[3]);
+
+ // Reflections level
+ seekBar = (SeekBar)findViewById(R.id.rvbParam5SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam5Value);
+ mParameters[4] = new ReflectionsLevelParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[4]);
+
+ // Reflections delay
+ seekBar = (SeekBar)findViewById(R.id.rvbParam6SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam6Value);
+ mParameters[5] = new ReflectionsDelayParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[5]);
+
+ // Reverb level
+ seekBar = (SeekBar)findViewById(R.id.rvbParam7SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam7Value);
+ mParameters[6] = new ReverbLevelParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[6]);
+
+ // Reverb delay
+ seekBar = (SeekBar)findViewById(R.id.rvbParam8SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam8Value);
+ mParameters[7] = new ReverbDelayParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[7]);
+
+ // Diffusion
+ seekBar = (SeekBar)findViewById(R.id.rvbParam9SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam9Value);
+ mParameters[8] = new DiffusionParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[8]);
+
+ // Density
+ seekBar = (SeekBar)findViewById(R.id.rvbParam10SeekBar);
+ textView = (TextView)findViewById(R.id.rvbParam10Value);
+ mParameters[9] = new DensityParam(mReverb, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[9]);
+ }
+ }
+ @Override
+ public void onResume() {
+ super.onResume();
+ Log.d(TAG, "onResume");
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.rvbOnOff) {
+ if (mReverb != null) {
+ mReverb.setEnabled(isChecked);
+ Log.d(TAG,"onCheckedChanged: rvbOnOff");
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].updateDisplay();
+ }
+ }
+ }
+ if (buttonView.getId() == R.id.rvbReleaseButton) {
+ if (isChecked) {
+ if (mReverb == null) {
+ getEffect(0);
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEffect(mReverb);
+ mParameters[i].setEnabled(true);
+ }
+ }
+ } else {
+ if (mReverb != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEnabled(false);
+ }
+ putEffect(0);
+ }
+ }
+ }
+// if (buttonView.getId() == R.id.rvbBypass) {
+// // REVERB_PARAM_BYPASS parametervalue is 11 in EffectEnvironmentalReverApi.h
+// if (mReverb != null) {
+// if (isChecked) {
+// mReverb.setParameter((int)11, (int)1);
+// } else {
+// mReverb.setParameter((int)11, (int)0);
+// }
+// }
+// }
+ if (buttonView.getId() == R.id.attachButton) {
+ if (mReverb != null) {
+ if (isChecked) {
+ sPlayerController.attachAuxEffect(mReverb.getId());
+ sAttached = true;
+ } else {
+ sPlayerController.attachAuxEffect(0);
+ sAttached = false;
+ }
+ }
+ }
+ }
+
+ // SeekBar.OnSeekBarChangeListener
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromTouch) {
+
+ if (seekBar != mSendLevelSeekBar) {
+ Log.e(TAG, "onProgressChanged called with wrong seekBar");
+ return;
+ }
+
+ sSendLevel = linToExp(progress,100);
+ if (fromTouch) {
+ sPlayerController.setAuxEffectSendLevel(sSendLevel);
+ }
+ String text = Float.toString(sSendLevel);
+ mSendLevelDisplay.setText(text);
+ if (!fromTouch) {
+ seekBar.setProgress(progress);
+ }
+ }
+
+ static float linToExp(int lin, int range) {
+ if (lin == 0) return 0;
+ return (float)Math.pow((double)10,(double)72*(lin-range)/(20*range));
+ }
+
+ static int expToLin(float exp, int range) {
+ if (exp == 0) return 0;
+ return (int)(20*range*Math.log10((double)exp)/72 + range);
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+
+ private class EnvReverbParam extends EffectParameter {
+ private EnvironmentalReverb mReverb;
+
+ public EnvReverbParam(EnvironmentalReverb reverb, int min, int max, SeekBar seekBar, TextView textView, String unit) {
+ super (min, max, seekBar, textView, unit);
+ mReverb = reverb;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ }
+
+ @Override
+ public Integer getParameter() {
+ return new Integer(0);
+ }
+
+ @Override
+ public void setEffect(Object reverb) {
+ mReverb = (EnvironmentalReverb)reverb;
+ }
+ }
+
+ private class RoomLevelParam extends EnvReverbParam {
+
+ public RoomLevelParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, -9600, 0, seekBar, textView, "mB");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setRoomLevel(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getRoomLevel());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class RoomHFLevelParam extends EnvReverbParam {
+
+ public RoomHFLevelParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, -4000, 0, seekBar, textView, "mB");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setRoomHFLevel(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getRoomHFLevel());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class DecayTimeParam extends EnvReverbParam {
+
+ public DecayTimeParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 200, 4000, seekBar, textView, "ms");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setDecayTime(value.intValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return mReverb.getDecayTime();
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class DecayHFRatioParam extends EnvReverbParam {
+
+ public DecayHFRatioParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 100, 1000, seekBar, textView, "permilles");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setDecayHFRatio(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getDecayHFRatio());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class ReflectionsLevelParam extends EnvReverbParam {
+
+ public ReflectionsLevelParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, -9600, 0, seekBar, textView, "mB");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setReflectionsLevel(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getReflectionsLevel());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class ReflectionsDelayParam extends EnvReverbParam {
+
+ public ReflectionsDelayParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 0, 65, seekBar, textView, "ms");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setReflectionsDelay(value.intValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return mReverb.getReflectionsDelay();
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class ReverbLevelParam extends EnvReverbParam {
+
+ public ReverbLevelParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, -9600, 2000, seekBar, textView, "mB");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setReverbLevel(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getReverbLevel());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class ReverbDelayParam extends EnvReverbParam {
+
+ public ReverbDelayParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 0, 65, seekBar, textView, "ms");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setReverbDelay(value.intValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return mReverb.getReverbDelay();
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class DiffusionParam extends EnvReverbParam {
+
+ public DiffusionParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 0, 1000, seekBar, textView, "permilles");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setDiffusion(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getDiffusion());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class DensityParam extends EnvReverbParam {
+
+ public DensityParam(EnvironmentalReverb reverb, SeekBar seekBar, TextView textView) {
+ super (reverb, 0, 1000, seekBar, textView, "permilles");
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mReverb != null) {
+ mReverb.setDensity(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mReverb != null) {
+ return new Integer(mReverb.getDensity());
+ }
+ return new Integer(0);
+ }
+ }
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mReverb = sInstances.get(session);
+ } else {
+ try{
+ mReverb = new EnvironmentalReverb(0, session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"Reverb effect not supported");
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"Reverb library not loaded");
+ } catch (RuntimeException e) {
+ Log.e(TAG,"Reverb effect not found");
+ }
+ Log.d(TAG, "new reverb: "+mReverb);
+ sInstances.put(session, mReverb);
+ }
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+ mAttachButton.setEnabled(false);
+ if (mReverb != null) {
+ if (mSettings != "") {
+ mReverb.setProperties(new EnvironmentalReverb.Settings(mSettings));
+ }
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+ mOnOffButton.setChecked(mReverb.getEnabled());
+ mOnOffButton.setEnabled(true);
+ mAttachButton.setChecked(false);
+ mAttachButton.setEnabled(true);
+ if (sAttached) {
+ mAttachButton.setChecked(true);
+ sPlayerController.attachAuxEffect(mReverb.getId());
+ }
+ }
+ }
+
+ private void putEffect(int session) {
+ mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ mAttachButton.setChecked(false);
+ mAttachButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mReverb != null) {
+ mSettings = mReverb.getProperties().toString();
+ mReverb.release();
+ Log.d(TAG,"Reverb released, settings: "+mSettings);
+ mReverb = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/EqualizerTest.java b/media/tests/EffectsTest/src/com/android/effectstest/EqualizerTest.java
new file mode 100755
index 0000000..f30a26f
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/EqualizerTest.java
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2009 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 com.android.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+
+import android.media.audiofx.Equalizer;
+import android.media.audiofx.AudioEffect;
+
+public class EqualizerTest extends Activity implements OnCheckedChangeListener {
+
+ private final static String TAG = "EqualizerTest";
+
+ private static int NUM_BANDS = 5;
+ private static int NUM_PARAMS = NUM_BANDS + 1;
+
+ private EffectParameter[] mParameters = new EffectParameter[NUM_PARAMS];
+ private Equalizer mEqualizer;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ EditText mSessionText;
+ static int sSession = 0;
+ EffectListner mEffectListener = new EffectListner();
+ private static HashMap<Integer, Equalizer> sInstances = new HashMap<Integer, Equalizer>(10);
+ String mSettings = "";
+
+ public EqualizerTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ SeekBar seekBar;
+ TextView textView;
+
+ setContentView(R.layout.equalizertest);
+
+ mSessionText = (EditText) findViewById(R.id.sessionEdit);
+ mSessionText.setOnKeyListener(mSessionKeyListener);
+
+ mSessionText.setText(Integer.toString(sSession));
+
+ mReleaseButton = (ToggleButton)findViewById(R.id.eqReleaseButton);
+ mOnOffButton = (ToggleButton)findViewById(R.id.equalizerOnOff);
+
+ getEffect(sSession);
+
+ if (mEqualizer != null) {
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mOnOffButton.setOnCheckedChangeListener(this);
+
+ short[] bandLevelRange = mEqualizer.getBandLevelRange();
+ int centerFreq;
+ int []freqRange;
+
+ // Band 1 level
+ centerFreq = mEqualizer.getCenterFreq((short)0);
+ freqRange = mEqualizer.getBandFreqRange((short)0);
+ displayFreq(R.id.eqParam1Center, centerFreq);
+ displayFreq(R.id.eqParam1Min, freqRange[0]);
+ displayFreq(R.id.eqParam1Max, freqRange[1]);
+ seekBar = (SeekBar)findViewById(R.id.eqParam1SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam1Value);
+ mParameters[0] = new BandLevelParam(mEqualizer, 0, bandLevelRange[0], bandLevelRange[1], seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[0]);
+
+ // Band 2 level
+ centerFreq = mEqualizer.getCenterFreq((short)1);
+ freqRange = mEqualizer.getBandFreqRange((short)1);
+ displayFreq(R.id.eqParam2Center, centerFreq);
+ displayFreq(R.id.eqParam2Min, freqRange[0]);
+ displayFreq(R.id.eqParam2Max, freqRange[1]);
+ seekBar = (SeekBar)findViewById(R.id.eqParam2SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam2Value);
+ mParameters[1] = new BandLevelParam(mEqualizer, 1, bandLevelRange[0], bandLevelRange[1], seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[1]);
+
+ // Band 3 level
+ centerFreq = mEqualizer.getCenterFreq((short)2);
+ freqRange = mEqualizer.getBandFreqRange((short)2);
+ displayFreq(R.id.eqParam3Center, centerFreq);
+ displayFreq(R.id.eqParam3Min, freqRange[0]);
+ displayFreq(R.id.eqParam3Max, freqRange[1]);
+ seekBar = (SeekBar)findViewById(R.id.eqParam3SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam3Value);
+ mParameters[2] = new BandLevelParam(mEqualizer, 2, bandLevelRange[0], bandLevelRange[1], seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[2]);
+
+ // Band 4 level
+ centerFreq = mEqualizer.getCenterFreq((short)3);
+ freqRange = mEqualizer.getBandFreqRange((short)3);
+ displayFreq(R.id.eqParam4Center, centerFreq);
+ displayFreq(R.id.eqParam4Min, freqRange[0]);
+ displayFreq(R.id.eqParam4Max, freqRange[1]);
+ seekBar = (SeekBar)findViewById(R.id.eqParam4SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam4Value);
+ mParameters[3] = new BandLevelParam(mEqualizer, 3, bandLevelRange[0], bandLevelRange[1], seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[3]);
+
+ // Band 5 level
+ centerFreq = mEqualizer.getCenterFreq((short)4);
+ freqRange = mEqualizer.getBandFreqRange((short)4);
+ displayFreq(R.id.eqParam5Center, centerFreq);
+ displayFreq(R.id.eqParam5Min, freqRange[0]);
+ displayFreq(R.id.eqParam5Max, freqRange[1]);
+ seekBar = (SeekBar)findViewById(R.id.eqParam5SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam5Value);
+ mParameters[4] = new BandLevelParam(mEqualizer, 4, bandLevelRange[0], bandLevelRange[1], seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[4]);
+
+ // Presets
+ short numPresets = mEqualizer.getNumberOfPresets();
+ seekBar = (SeekBar)findViewById(R.id.eqParam6SeekBar);
+ textView = (TextView)findViewById(R.id.eqParam6Value);
+ mParameters[5] = new PresetParam(mEqualizer, (short)0, (short)(numPresets-1), seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[5]);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private View.OnKeyListener mSessionKeyListener
+ = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ try {
+ sSession = Integer.parseInt(mSessionText.getText().toString());
+ getEffect(sSession);
+ if (mEqualizer != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEffect(mEqualizer);
+ mParameters[i].setEnabled(true);
+ }
+ }
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString());
+ }
+
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.equalizerOnOff) {
+ if (mEqualizer != null) {
+ mEqualizer.setEnabled(isChecked);
+ updateBands();
+ }
+ }
+ if (buttonView.getId() == R.id.eqReleaseButton) {
+ if (isChecked) {
+ if (mEqualizer == null) {
+ getEffect(sSession);
+ if (mEqualizer != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEffect(mEqualizer);
+ mParameters[i].setEnabled(true);
+ }
+ }
+ }
+ } else {
+ if (mEqualizer != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEnabled(false);
+ }
+ putEffect(sSession);
+ }
+ }
+ }
+ }
+
+ protected void updateBands() {
+ for (int i = 0 ; i < NUM_BANDS; i++) {
+ mParameters[i].updateDisplay();
+ }
+ }
+
+ private void displayFreq(int viewId, int freq) {
+ TextView textView = (TextView)findViewById(viewId);
+ String text = Integer.toString(freq/1000)+" Hz";
+ textView.setText(text);
+ }
+
+ private class EqualizerParam extends EffectParameter {
+ private Equalizer mEqualizer;
+
+ public EqualizerParam(Equalizer equalizer, int min, int max, SeekBar seekBar, TextView textView, String unit) {
+ super (min, max, seekBar, textView, unit);
+
+ mEqualizer = equalizer;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ }
+
+ @Override
+ public Integer getParameter() {
+ return new Integer(0);
+ }
+
+ @Override
+ public void setEffect(Object eq) {
+ mEqualizer = (Equalizer)eq;
+ }
+ }
+
+ private class BandLevelParam extends EqualizerParam {
+ private int mBand;
+
+ public BandLevelParam(Equalizer equalizer, int band, short min, short max, SeekBar seekBar, TextView textView) {
+ super (equalizer, min, max, seekBar, textView, "mB");
+
+ mBand = band;
+ mEqualizer = equalizer;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mEqualizer != null) {
+ mEqualizer.setBandLevel((short)mBand, value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mEqualizer != null) {
+ return new Integer(mEqualizer.getBandLevel((short)mBand));
+ }
+ return new Integer(0);
+ }
+ }
+
+ private class PresetParam extends EqualizerParam {
+
+ public PresetParam(Equalizer equalizer, short min, short max, SeekBar seekBar, TextView textView) {
+ super (equalizer, min, max, seekBar, textView, "");
+
+ mEqualizer = equalizer;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mEqualizer != null) {
+ mEqualizer.usePreset(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mEqualizer != null) {
+ return new Integer(mEqualizer.getCurrentPreset());
+ }
+ return new Integer(0);
+ }
+
+ @Override
+ public void displayValue(int value, boolean fromTouch) {
+ String text = mEqualizer.getPresetName((short)value);
+ mValueText.setText(text);
+ if (!fromTouch) {
+ mSeekBar.setProgress(value - mMin);
+ } else {
+ updateBands();
+ }
+ }
+ }
+
+ public class EffectListner implements AudioEffect.OnEnableStatusChangeListener,
+ AudioEffect.OnControlStatusChangeListener,
+ Equalizer.OnParameterChangeListener
+ {
+ public EffectListner() {
+ }
+ public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+ Log.d(TAG,"onEnableStatusChange: "+ enabled);
+ }
+ public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
+ Log.d(TAG,"onControlStatusChange: "+ controlGranted);
+ }
+
+ public void onParameterChange(Equalizer effect, int status, int param1, int param2, int value) {
+ Log.d(TAG,"onParameterChange EQ, status: "+status+" p1: "+param1+" p2: "+param2+" v: "+value);
+ }
+
+ private int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+ private short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+ }
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mEqualizer = sInstances.get(session);
+ } else {
+ try{
+ mEqualizer = new Equalizer(0, session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"Equalizer effect not supported");
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"Equalizer library not loaded");
+ } catch (IllegalStateException e) {
+ Log.e(TAG,"Equalizer cannot get presets");
+ } catch (RuntimeException e) {
+ Log.e(TAG,"Equalizer effect not found");
+ }
+ sInstances.put(session, mEqualizer);
+ }
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+ if (mEqualizer != null) {
+ if (mSettings != "") {
+ Log.d(TAG,"Equalizer settings: "+mSettings);
+ mEqualizer.setProperties(new Equalizer.Settings(mSettings));
+ }
+
+ mEqualizer.setEnableStatusListener(mEffectListener);
+ mEqualizer.setControlStatusListener(mEffectListener);
+ mEqualizer.setParameterListener(mEffectListener);
+
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+
+ mOnOffButton.setChecked(mEqualizer.getEnabled());
+ mOnOffButton.setEnabled(true);
+ }
+ }
+
+ private void putEffect(int session) {
+// mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mEqualizer != null) {
+ mSettings = mEqualizer.getProperties().toString();
+ mEqualizer.release();
+ Log.d(TAG,"Equalizer released, settings: "+mSettings);
+ mEqualizer = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/PresetReverbTest.java b/media/tests/EffectsTest/src/com/android/effectstest/PresetReverbTest.java
new file mode 100755
index 0000000..91d7948
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/PresetReverbTest.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2009 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 com.android.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.media.audiofx.PresetReverb;
+import android.media.audiofx.AudioEffect;
+
+public class PresetReverbTest extends Activity implements OnCheckedChangeListener {
+
+ private final static String TAG = "PresetReverbTest";
+
+ private static int NUM_PARAMS = 1;
+
+ private EffectParameter[] mParameters = new EffectParameter[NUM_PARAMS];
+ private PresetReverb mPresetReverb;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ EditText mSessionText;
+ static int sSession = 0;
+ EffectListner mEffectListener = new EffectListner();
+ private static HashMap<Integer, PresetReverb> sInstances = new HashMap<Integer, PresetReverb>(10);
+ String mSettings = "";
+
+ public PresetReverbTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ private static String[] sPresetNames = {
+ "NONE", //PresetReverb.PRESET_NONE
+ "SMALLROOM", //PresetReverb.PRESET_SMALLROOM
+ "MEDIUMROOM", //PresetReverb.PRESET_MEDIUMROOM
+ "LARGEROOM", //PresetReverb.PRESET_LARGEROOM
+ "MEDIUMHALL", //PresetReverb.PRESET_MEDIUMHALL
+ "LARGEHALL", //PresetReverb.PRESET_LARGEHALL
+ "PLATE", //PresetReverb.PRESET_PLATE
+ };
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setContentView(R.layout.presetreverbtest);
+
+ mSessionText = (EditText) findViewById(R.id.sessionEdit);
+ mSessionText.setOnKeyListener(mSessionKeyListener);
+
+ mSessionText.setText(Integer.toString(sSession));
+
+ mReleaseButton = (ToggleButton)findViewById(R.id.presetrvbReleaseButton);
+ mOnOffButton = (ToggleButton)findViewById(R.id.presetrvbOnOff);
+
+ getEffect(sSession);
+
+ if (mPresetReverb != null) {
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mOnOffButton.setOnCheckedChangeListener(this);
+ // Presets
+ SeekBar seekBar = (SeekBar)findViewById(R.id.presetrvbParam1SeekBar);
+ TextView textView = (TextView)findViewById(R.id.presetrvbParam1Value);
+ mParameters[0] = new PresetParam(mPresetReverb, (short)0, (short)(sPresetNames.length - 1), seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mParameters[0]);
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private View.OnKeyListener mSessionKeyListener
+ = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ try {
+ sSession = Integer.parseInt(mSessionText.getText().toString());
+ getEffect(sSession);
+ if (mPresetReverb != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEffect(mPresetReverb);
+ mParameters[i].setEnabled(true);
+ mParameters[i].updateDisplay();
+ }
+ }
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString());
+ }
+
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.presetrvbOnOff) {
+ if (mPresetReverb != null) {
+ mPresetReverb.setEnabled(isChecked);
+ updateParams();
+ }
+ }
+ if (buttonView.getId() == R.id.presetrvbReleaseButton) {
+ if (isChecked) {
+ if (mPresetReverb == null) {
+ getEffect(sSession);
+ if (mPresetReverb != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEffect(mPresetReverb);
+ mParameters[i].setEnabled(true);
+ mParameters[i].updateDisplay();
+ }
+ }
+ }
+ } else {
+ if (mPresetReverb != null) {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].setEnabled(false);
+ }
+ putEffect(sSession);
+ }
+ }
+ }
+ }
+
+ private class PresetParam extends EffectParameter {
+ private PresetReverb mPresetReverb;
+
+ public PresetParam(PresetReverb presetrvb, short min, short max, SeekBar seekBar, TextView textView) {
+ super (min, max, seekBar, textView, "");
+
+ mPresetReverb = presetrvb;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mPresetReverb != null) {
+ mPresetReverb.setPreset(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mPresetReverb != null) {
+ return new Integer(mPresetReverb.getPreset());
+ }
+ return new Integer(0);
+ }
+
+ @Override
+ public void displayValue(int value, boolean fromTouch) {
+ mValueText.setText(sPresetNames[value]);
+ if (!fromTouch) {
+ mSeekBar.setProgress(value - mMin);
+ } else {
+ updateParams();
+ }
+ }
+
+ @Override
+ public void setEffect(Object presetrvb) {
+ mPresetReverb = (PresetReverb)presetrvb;
+ }
+
+ }
+
+ protected void updateParams() {
+ for (int i = 0 ; i < mParameters.length; i++) {
+ mParameters[i].updateDisplay();
+ }
+ }
+
+ public class EffectListner implements AudioEffect.OnEnableStatusChangeListener,
+ AudioEffect.OnControlStatusChangeListener,
+ PresetReverb.OnParameterChangeListener
+ {
+ public EffectListner() {
+ }
+ public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+ Log.d(TAG,"onEnableStatusChange: "+ enabled);
+ }
+ public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
+ Log.d(TAG,"onControlStatusChange: "+ controlGranted);
+ }
+
+ public void onParameterChange(PresetReverb effect, int status, int param, short value) {
+ Log.d(TAG,"onParameterChange, status: "+status+" p: "+param+" v: "+value);
+ }
+
+ private int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+ private short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+ }
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mPresetReverb = sInstances.get(session);
+ } else {
+ try{
+ mPresetReverb = new PresetReverb(0, session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"PresetReverb effect not supported");
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"PresetReverb library not loaded");
+ } catch (RuntimeException e) {
+ Log.e(TAG,"PresetReverb effect not found");
+ }
+ sInstances.put(session, mPresetReverb);
+ }
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+
+ if (mPresetReverb != null) {
+ if (mSettings != "") {
+ mPresetReverb.setProperties(new PresetReverb.Settings(mSettings));
+ }
+ mPresetReverb.setEnableStatusListener(mEffectListener);
+ mPresetReverb.setControlStatusListener(mEffectListener);
+ mPresetReverb.setParameterListener(mEffectListener);
+
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+
+ mOnOffButton.setChecked(mPresetReverb.getEnabled());
+ mOnOffButton.setEnabled(true);
+ }
+ }
+
+ private void putEffect(int session) {
+ mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mPresetReverb != null) {
+ mSettings = mPresetReverb.getProperties().toString();
+ mPresetReverb.release();
+ Log.d(TAG,"PresetReverb released");
+ mPresetReverb = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/SimplePlayer.java b/media/tests/EffectsTest/src/com/android/effectstest/SimplePlayer.java
new file mode 100644
index 0000000..119a604
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/SimplePlayer.java
@@ -0,0 +1,197 @@
+/*
+ * Copyright (C) 2009 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 com.android.effectstest;
+
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class SimplePlayer implements OnClickListener {
+
+ private final static String TAG = "SimplePlayer";
+
+ int mPlayPauseButtonId;
+ int mStopButtonId;
+ Context mContext;
+ ImageView mPlayPauseButton;
+ int mPlayImageResource;
+ int mPauseImageResource;
+ String mFileName;
+ int mFileResId;
+ MediaPlayer mMediaPlayer;
+ int mStreamType;
+ int mSession;
+ float mSendLevel = (float)0.5;
+ int mEffectId = 0;
+ TextView mSessionText;
+
+ SimplePlayer(Context context, int playPausebuttonId, ImageView playPausebutton,
+ int stopButtonId, ImageView stopButton, TextView sessionText, String fileName, int stream, int session)
+ {
+ set(context, playPausebuttonId, playPausebutton, stopButtonId, stopButton, sessionText, stream, session);
+ mFileName = fileName;
+ }
+
+ SimplePlayer(Context context, int playPausebuttonId, ImageView playPausebutton,
+ int stopButtonId, ImageView stopButton, TextView sessionText, int fileResId, int stream, int session) {
+ set(context, playPausebuttonId, playPausebutton, stopButtonId, stopButton, sessionText, stream, session);
+ mFileResId = fileResId;
+ mFileName = "";
+ }
+
+ public void set(Context context, int playPausebuttonId, ImageView playPausebutton,
+ int stopButtonId, ImageView stopButton, TextView sessionText, int stream, int session) {
+ mContext = context;
+ mPlayPauseButtonId = playPausebuttonId;
+ mStopButtonId = stopButtonId;
+ mPlayPauseButton = (ImageButton) playPausebutton;
+ ImageButton stop = (ImageButton) stopButton;
+
+ mPlayPauseButton.setOnClickListener(this);
+ mPlayPauseButton.requestFocus();
+ stop.setOnClickListener(this);
+
+ mPlayImageResource = android.R.drawable.ic_media_play;
+ mPauseImageResource = android.R.drawable.ic_media_pause;
+ mStreamType = stream;
+ mSession = session;
+ mSessionText = sessionText;
+ }
+
+
+ public void onClick(View v) {
+ if (v.getId() == mPlayPauseButtonId) {
+ playOrPause();
+ } else if (v.getId() == mStopButtonId) {
+ stop();
+ }
+ }
+
+ public void playOrPause() {
+ if (mMediaPlayer == null || !mMediaPlayer.isPlaying()){
+ if (mMediaPlayer == null) {
+ try {
+ mMediaPlayer = new MediaPlayer();
+ if (mSession != 0) {
+ mMediaPlayer.setAudioSessionId(mSession);
+ Log.d(TAG, "mMediaPlayer.setAudioSessionId(): "+ mSession);
+ }
+
+ if (mFileName.equals("")) {
+ Log.d(TAG, "Playing from resource");
+ AssetFileDescriptor afd = mContext.getResources().openRawResourceFd(mFileResId);
+ mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
+ afd.close();
+ } else {
+ Log.d(TAG, "Playing file: "+mFileName);
+ mMediaPlayer.setDataSource(mFileName);
+ }
+ mMediaPlayer.setAudioStreamType(mStreamType);
+ mMediaPlayer.prepare();
+ mMediaPlayer.setLooping(true);
+ } catch (IOException ex) {
+ Log.e(TAG, "mMediaPlayercreate failed:", ex);
+ mMediaPlayer = null;
+ } catch (IllegalArgumentException ex) {
+ Log.e(TAG, "mMediaPlayercreate failed:", ex);
+ mMediaPlayer = null;
+ } catch (SecurityException ex) {
+ Log.e(TAG, "mMediaPlayercreate failed:", ex);
+ mMediaPlayer = null;
+ }
+
+ if (mMediaPlayer != null) {
+ mMediaPlayer.setAuxEffectSendLevel(mSendLevel);
+ mMediaPlayer.attachAuxEffect(mEffectId);
+ mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ public void onCompletion(MediaPlayer mp) {
+ updatePlayPauseButton();
+ }
+ });
+ mSessionText.setText("Session: "+Integer.toString(mMediaPlayer.getAudioSessionId()));
+ }
+ }
+ if (mMediaPlayer != null) {
+ mMediaPlayer.start();
+ }
+ } else {
+ mMediaPlayer.pause();
+ }
+ updatePlayPauseButton();
+ }
+
+ public void stop() {
+ if (mMediaPlayer != null) {
+ mMediaPlayer.stop();
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ }
+ updatePlayPauseButton();
+ }
+
+ public boolean isPlaying() {
+ if (mMediaPlayer != null) {
+ return mMediaPlayer.isPlaying();
+ } else {
+ return false;
+ }
+ }
+
+ public void updatePlayPauseButton() {
+ mPlayPauseButton.setImageResource(isPlaying() ? mPauseImageResource : mPlayImageResource);
+ }
+
+ public void attachAuxEffect(int effectId) {
+ mEffectId = effectId;
+ if (mMediaPlayer != null) {
+ Log.d(TAG,"attach effect: "+effectId);
+ mMediaPlayer.attachAuxEffect(effectId);
+ }
+ }
+ public void setAuxEffectSendLevel(float level) {
+ mSendLevel = level;
+ if (mMediaPlayer != null) {
+ mMediaPlayer.setAuxEffectSendLevel(level);
+ }
+ }
+
+ public void setContext(Context context) {
+ mContext = context;
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/VirtualizerTest.java b/media/tests/EffectsTest/src/com/android/effectstest/VirtualizerTest.java
new file mode 100755
index 0000000..bb32e6f
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/VirtualizerTest.java
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2009 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 com.android.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View.OnClickListener;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.EditText;
+import android.widget.SeekBar;
+import android.widget.ToggleButton;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+import android.media.audiofx.Virtualizer;
+import android.media.audiofx.AudioEffect;
+
+public class VirtualizerTest extends Activity implements OnCheckedChangeListener {
+
+ private final static String TAG = "VirtualizerTest";
+
+ private static int NUM_PARAMS = 1;
+
+ private EffectParameter mStrength;
+ private Virtualizer mVirtualizer;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ EditText mSessionText;
+ static int sSession = 0;
+ EffectListner mEffectListener = new EffectListner();
+ private static HashMap<Integer, Virtualizer> sInstances = new HashMap<Integer, Virtualizer>(10);
+ String mSettings = "";
+
+ public VirtualizerTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ SeekBar seekBar;
+ TextView textView;
+
+ setContentView(R.layout.virtualizertest);
+
+ mSessionText = (EditText) findViewById(R.id.sessionEdit);
+ mSessionText.setOnKeyListener(mSessionKeyListener);
+ mSessionText.setText(Integer.toString(sSession));
+
+ mReleaseButton = (ToggleButton)findViewById(R.id.virtReleaseButton);
+ mOnOffButton = (ToggleButton)findViewById(R.id.virtualizerOnOff);
+
+ getEffect(sSession);
+
+ if (mVirtualizer != null) {
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mOnOffButton.setOnCheckedChangeListener(this);
+ textView = (TextView)findViewById(R.id.virtStrengthMin);
+ textView.setText("0");
+ textView = (TextView)findViewById(R.id.virtStrengthMax);
+ textView.setText("1000");
+ seekBar = (SeekBar)findViewById(R.id.virtStrengthSeekBar);
+ textView = (TextView)findViewById(R.id.virtStrengthValue);
+ mStrength = new VirtualizerParam(mVirtualizer, 0, 1000, seekBar, textView);
+ seekBar.setOnSeekBarChangeListener(mStrength);
+ mStrength.setEnabled(mVirtualizer.getStrengthSupported());
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private View.OnKeyListener mSessionKeyListener
+ = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ try {
+ sSession = Integer.parseInt(mSessionText.getText().toString());
+ getEffect(sSession);
+ if (mVirtualizer != null) {
+ mStrength.setEffect(mVirtualizer);
+ mStrength.setEnabled(mVirtualizer.getStrengthSupported());
+ }
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString());
+ }
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.virtualizerOnOff) {
+ if (mVirtualizer != null) {
+ mVirtualizer.setEnabled(isChecked);
+ mStrength.updateDisplay();
+ }
+ }
+ if (buttonView.getId() == R.id.virtReleaseButton) {
+ if (isChecked) {
+ if (mVirtualizer == null) {
+ getEffect(sSession);
+ if (mVirtualizer != null) {
+ mStrength.setEffect(mVirtualizer);
+ mStrength.setEnabled(mVirtualizer.getStrengthSupported());
+ }
+ }
+ } else {
+ if (mVirtualizer != null) {
+ mStrength.setEnabled(false);
+ putEffect(sSession);
+ }
+ }
+ }
+ }
+
+ private class VirtualizerParam extends EffectParameter {
+ private Virtualizer mVirtualizer;
+
+ public VirtualizerParam(Virtualizer virtualizer, int min, int max, SeekBar seekBar, TextView textView) {
+ super (min, max, seekBar, textView, "o/oo");
+
+ mVirtualizer = virtualizer;
+ updateDisplay();
+ }
+
+ @Override
+ public void setParameter(Integer value) {
+ if (mVirtualizer != null) {
+ mVirtualizer.setStrength(value.shortValue());
+ }
+ }
+
+ @Override
+ public Integer getParameter() {
+ if (mVirtualizer != null) {
+ return new Integer(mVirtualizer.getRoundedStrength());
+ }
+ return new Integer(0);
+ }
+
+ @Override
+ public void setEffect(Object effect) {
+ mVirtualizer = (Virtualizer)effect;
+ }
+ }
+
+ public class EffectListner implements AudioEffect.OnEnableStatusChangeListener,
+ AudioEffect.OnControlStatusChangeListener, AudioEffect.OnParameterChangeListener
+ {
+ public EffectListner() {
+ }
+ public void onEnableStatusChange(AudioEffect effect, boolean enabled) {
+ Log.d(TAG,"onEnableStatusChange: "+ enabled);
+ }
+ public void onControlStatusChange(AudioEffect effect, boolean controlGranted) {
+ Log.d(TAG,"onControlStatusChange: "+ controlGranted);
+ }
+ public void onParameterChange(AudioEffect effect, int status, byte[] param, byte[] value) {
+ int p = byteArrayToInt(param, 0);
+ short v = byteArrayToShort(value, 0);
+
+ Log.d(TAG,"onParameterChange, status: "+status+" p: "+p+" v: "+v);
+ }
+
+ private int byteArrayToInt(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getInt(offset);
+
+ }
+ private short byteArrayToShort(byte[] valueBuf, int offset) {
+ ByteBuffer converter = ByteBuffer.wrap(valueBuf);
+ converter.order(ByteOrder.nativeOrder());
+ return converter.getShort(offset);
+
+ }
+ }
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mVirtualizer = sInstances.get(session);
+ } else {
+ try{
+ mVirtualizer = new Virtualizer(0, session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG,"Virtualizer effect not supported");
+ } catch (IllegalStateException e) {
+ Log.e(TAG,"Virtualizer cannot get strength supported");
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"Virtualizer library not loaded");
+ } catch (RuntimeException e) {
+ Log.e(TAG,"Virtualizer effect not found");
+ }
+ sInstances.put(session, mVirtualizer);
+ }
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+
+ if (mVirtualizer != null) {
+ if (mSettings != "") {
+ mVirtualizer.setProperties(new Virtualizer.Settings(mSettings));
+ }
+ mVirtualizer.setEnableStatusListener(mEffectListener);
+ mVirtualizer.setControlStatusListener(mEffectListener);
+ mVirtualizer.setParameterListener(mEffectListener);
+
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+
+ mOnOffButton.setChecked(mVirtualizer.getEnabled());
+ mOnOffButton.setEnabled(true);
+ }
+ }
+
+ private void putEffect(int session) {
+ mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mVirtualizer != null) {
+ mSettings = mVirtualizer.getProperties().toString();
+ mVirtualizer.release();
+ Log.d(TAG,"Virtualizer released");
+ mVirtualizer = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+}
diff --git a/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
new file mode 100755
index 0000000..60583e0
--- /dev/null
+++ b/media/tests/EffectsTest/src/com/android/effectstest/VisualizerTest.java
@@ -0,0 +1,296 @@
+/*
+ * Copyright (C) 2009 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 com.android.effectstest;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.media.audiofx.Visualizer;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.CompoundButton;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+import android.widget.SeekBar;
+
+import java.nio.ByteOrder;
+import java.nio.ByteBuffer;
+import java.util.HashMap;
+import java.util.Map;
+
+public class VisualizerTest extends Activity implements OnCheckedChangeListener {
+
+ private final static String TAG = "Visualizer Test";
+
+ private Visualizer mVisualizer;
+ ToggleButton mOnOffButton;
+ ToggleButton mReleaseButton;
+ boolean mEnabled;
+ EditText mSessionText;
+ static int sSession = 0;
+ int mCaptureSize;
+ ToggleButton mCallbackButton;
+ boolean mCallbackOn;
+ VisualizerListener mVisualizerListener;
+ private static HashMap<Integer, Visualizer> sInstances = new HashMap<Integer, Visualizer>(10);
+ private VisualizerTestHandler mVisualizerTestHandler = null;
+
+ public VisualizerTest() {
+ Log.d(TAG, "contructor");
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ TextView textView;
+
+ setContentView(R.layout.visualizertest);
+
+ mSessionText = (EditText) findViewById(R.id.sessionEdit);
+ mSessionText.setOnKeyListener(mSessionKeyListener);
+ mSessionText.setText(Integer.toString(sSession));
+
+ mReleaseButton = (ToggleButton)findViewById(R.id.visuReleaseButton);
+ mOnOffButton = (ToggleButton)findViewById(R.id.visualizerOnOff);
+ mCallbackButton = (ToggleButton)findViewById(R.id.visuCallbackOnOff);
+ mCallbackOn = false;
+ mCallbackButton.setChecked(mCallbackOn);
+
+ mVisualizerTestHandler = new VisualizerTestHandler();
+ mVisualizerListener = new VisualizerListener();
+
+ getEffect(sSession);
+
+ if (mVisualizer != null) {
+ mReleaseButton.setOnCheckedChangeListener(this);
+ mOnOffButton.setOnCheckedChangeListener(this);
+ mCallbackButton.setOnCheckedChangeListener(this);
+ }
+ }
+
+ private static final int MSG_START_CAPTURE = 0;
+ private static final int MSG_STOP_CAPTURE = 1;
+ private static final int MSG_NEW_CAPTURE = 2;
+ private static final int CAPTURE_PERIOD_MS = 100;
+
+ private class VisualizerTestHandler extends Handler {
+ boolean mActive = false;
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case MSG_START_CAPTURE:
+ if (!mActive) {
+ Log.d(TAG, "Start capture");
+ mActive = true;
+ sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE, 0, 0, null), CAPTURE_PERIOD_MS);
+ }
+ break;
+ case MSG_STOP_CAPTURE:
+ if (mActive) {
+ Log.d(TAG, "Stop capture");
+ mActive = false;
+ }
+ break;
+ case MSG_NEW_CAPTURE:
+ if (mActive && mVisualizer != null) {
+ if (mCaptureSize > 0) {
+ byte[] data = new byte[mCaptureSize];
+ if (mVisualizer.getWaveForm(data) == Visualizer.SUCCESS) {
+ int len = data.length < mCaptureSize ? data.length : mCaptureSize;
+ displayVal(R.id.waveformMin, data[0]);
+ displayVal(R.id.waveformMax, data[len-1]);
+ displayVal(R.id.waveformCenter, data[len/2]);
+ };
+ if (mVisualizer.getFft(data) == Visualizer.SUCCESS) {
+ int len = data.length < mCaptureSize ? data.length : mCaptureSize;
+ displayVal(R.id.fftMin, data[0]);
+ displayVal(R.id.fftMax, data[len-1]);
+ displayVal(R.id.fftCenter, data[len/2]);
+ };
+ }
+ sendMessageDelayed(obtainMessage(MSG_NEW_CAPTURE, 0, 0, null), CAPTURE_PERIOD_MS);
+ }
+ break;
+ }
+ }
+ }
+
+ private class VisualizerListener implements Visualizer.OnDataCaptureListener {
+
+ public VisualizerListener() {
+ }
+ public void onWaveFormDataCapture(Visualizer visualizer, byte[] waveform, int samplingRate) {
+ if (visualizer == mVisualizer) {
+ if (waveform.length > 0) {
+ Log.d(TAG, "onWaveFormDataCapture(): "+waveform[0]+" smp rate: "+samplingRate/1000);
+ displayVal(R.id.waveformMin, waveform[0]);
+ displayVal(R.id.waveformMax, waveform[waveform.length - 1]);
+ displayVal(R.id.waveformCenter, waveform[waveform.length/2]);
+ }
+ }
+ }
+ public void onFftDataCapture(Visualizer visualizer, byte[] fft, int samplingRate) {
+ if (visualizer == mVisualizer) {
+ if (fft.length > 0) {
+ Log.d(TAG, "onFftDataCapture(): "+fft[0]);
+ displayVal(R.id.fftMin, fft[0]);
+ displayVal(R.id.fftMax, fft[fft.length - 1]);
+ displayVal(R.id.fftCenter, fft[fft.length/2]);
+ }
+ }
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ }
+
+ private View.OnKeyListener mSessionKeyListener
+ = new View.OnKeyListener() {
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ if (event.getAction() == KeyEvent.ACTION_DOWN) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_DPAD_CENTER:
+ case KeyEvent.KEYCODE_ENTER:
+ try {
+ sSession = Integer.parseInt(mSessionText.getText().toString());
+ getEffect(sSession);
+ } catch (NumberFormatException e) {
+ Log.d(TAG, "Invalid session #: "+mSessionText.getText().toString());
+ }
+
+ return true;
+ }
+ }
+ return false;
+ }
+ };
+
+ // OnCheckedChangeListener
+ public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+ if (buttonView.getId() == R.id.visualizerOnOff) {
+ if (mVisualizer != null) {
+ mEnabled = isChecked;
+ mCallbackButton.setEnabled(!mEnabled);
+ if (mCallbackOn && mEnabled) {
+ mVisualizer.setDataCaptureListener(mVisualizerListener,
+ 10000,
+ true,
+ true);
+ }
+ mVisualizer.setEnabled(mEnabled);
+ if (mCallbackOn) {
+ if (!mEnabled) {
+ mVisualizer.setDataCaptureListener(null,
+ 10000,
+ false,
+ false);
+ }
+ } else {
+ int msg = isChecked ? MSG_START_CAPTURE : MSG_STOP_CAPTURE;
+ mVisualizerTestHandler.sendMessage(
+ mVisualizerTestHandler.obtainMessage(msg, 0, 0, null));
+ }
+ }
+ }
+ if (buttonView.getId() == R.id.visuReleaseButton) {
+ if (isChecked) {
+ if (mVisualizer == null) {
+ getEffect(sSession);
+ }
+ } else {
+ if (mVisualizer != null) {
+ putEffect(sSession);
+ }
+ }
+ }
+ if (buttonView.getId() == R.id.visuCallbackOnOff) {
+ mCallbackOn = isChecked;
+ }
+ }
+
+ private void displayVal(int viewId, int val) {
+ TextView textView = (TextView)findViewById(viewId);
+ String text = Integer.toString(val);
+ textView.setText(text);
+ }
+
+
+ private void getEffect(int session) {
+ synchronized (sInstances) {
+ if (sInstances.containsKey(session)) {
+ mVisualizer = sInstances.get(session);
+ } else {
+ try{
+ mVisualizer = new Visualizer(session);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG,"Visualizer library not loaded");
+ throw (new RuntimeException("Cannot initialize effect"));
+ } catch (RuntimeException e) {
+ throw e;
+ }
+ sInstances.put(session, mVisualizer);
+ }
+ }
+ mReleaseButton.setEnabled(false);
+ mOnOffButton.setEnabled(false);
+ if (mVisualizer != null) {
+ mCaptureSize = mVisualizer.getCaptureSize();
+
+ mReleaseButton.setChecked(true);
+ mReleaseButton.setEnabled(true);
+
+ mEnabled = mVisualizer.getEnabled();
+ mOnOffButton.setChecked(mEnabled);
+ mOnOffButton.setEnabled(true);
+
+ mCallbackButton.setEnabled(!mEnabled);
+ }
+ }
+
+ private void putEffect(int session) {
+ mOnOffButton.setChecked(false);
+ mOnOffButton.setEnabled(false);
+ synchronized (sInstances) {
+ if (mVisualizer != null) {
+ mVisualizer.release();
+ Log.d(TAG,"Visualizer released");
+ mVisualizer = null;
+ sInstances.remove(session);
+ }
+ }
+ }
+
+}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
index a6cf355..3d5905d 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkPerfTestRunner.java
@@ -38,7 +38,7 @@ import android.util.Log;
public class MediaFrameworkPerfTestRunner extends InstrumentationTestRunner {
public static boolean mGetNativeHeapDump = false;
-
+ public static boolean mGetProcmem = false;
@Override
public TestSuite getAllTests() {
@@ -61,6 +61,12 @@ public class MediaFrameworkPerfTestRunner extends InstrumentationTestRunner {
if (get_heap_dump != null) {
mGetNativeHeapDump = true;
}
+
+ String get_procmem = (String) icicle.get("get_procmem");
+ if (get_procmem != null) {
+ mGetProcmem = true;
+ }
+
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
index e7f98de..ed9bb97 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaFrameworkTest.java
@@ -98,7 +98,9 @@ public class MediaFrameworkTest extends Activity implements SurfaceHolder.Callba
mWakeLock.acquire();
}
- public void onStop(Bundle icicle) {
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
mWakeLock.release();
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
index 95e7b5e..5c74552 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
@@ -33,18 +33,16 @@ public class MediaRecorderStressTestRunner extends InstrumentationTestRunner {
// the test must be supported by the corresponding camera.
public static int mCameraId = 0;
public static int mProfileQuality = CamcorderProfile.QUALITY_HIGH;
- public static CamcorderProfile profile =
- CamcorderProfile.get(mCameraId, mProfileQuality);
-
- public static int mIterations = 100;
+ public static CamcorderProfile profile = CamcorderProfile.get(mCameraId, mProfileQuality);
+ public static int mIterations = 15;
public static int mVideoEncoder = profile.videoCodec;
- public static int mAudioEncdoer = profile.audioCodec;
+ public static int mAudioEncoder = profile.audioCodec;
public static int mFrameRate = profile.videoFrameRate;
public static int mVideoWidth = profile.videoFrameWidth;
public static int mVideoHeight = profile.videoFrameHeight;
public static int mBitRate = profile.videoBitRate;
public static boolean mRemoveVideo = true;
- public static int mDuration = 10 * 1000; // 10 seconds
+ public static int mDuration = 60 * 1000; // 60 seconds
public static int mTimeLapseDuration = 180 * 1000; // 3 minutes
public static double mCaptureRate = 0.5; // 2 sec timelapse interval
@@ -64,41 +62,41 @@ public class MediaRecorderStressTestRunner extends InstrumentationTestRunner {
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
String iterations = (String) icicle.get("iterations");
- String video_encoder = (String) icicle.get("video_encoder");
- String audio_encoder = (String) icicle.get("audio_encoder");
- String frame_rate = (String) icicle.get("frame_rate");
- String video_width = (String) icicle.get("video_width");
- String video_height = (String) icicle.get("video_height");
- String bit_rate = (String) icicle.get("bit_rate");
- String record_duration = (String) icicle.get("record_duration");
- String remove_videos = (String) icicle.get("remove_videos");
+ String videoEncoder = (String) icicle.get("video_encoder");
+ String audioEncoder = (String) icicle.get("audio_encoder");
+ String frameRate = (String) icicle.get("frame_rate");
+ String videoWidth = (String) icicle.get("video_width");
+ String videoHeight = (String) icicle.get("video_height");
+ String bitRate = (String) icicle.get("bit_rate");
+ String recordDuration = (String) icicle.get("record_duration");
+ String removeVideos = (String) icicle.get("remove_videos");
if (iterations != null ) {
mIterations = Integer.parseInt(iterations);
}
- if ( video_encoder != null) {
- mVideoEncoder = Integer.parseInt(video_encoder);
+ if (videoEncoder != null) {
+ mVideoEncoder = Integer.parseInt(videoEncoder);
}
- if ( audio_encoder != null) {
- mAudioEncdoer = Integer.parseInt(audio_encoder);
+ if (audioEncoder != null) {
+ mAudioEncoder = Integer.parseInt(audioEncoder);
}
- if (frame_rate != null) {
- mFrameRate = Integer.parseInt(frame_rate);
+ if (frameRate != null) {
+ mFrameRate = Integer.parseInt(frameRate);
}
- if (video_width != null) {
- mVideoWidth = Integer.parseInt(video_width);
+ if (videoWidth != null) {
+ mVideoWidth = Integer.parseInt(videoWidth);
}
- if (video_height != null) {
- mVideoHeight = Integer.parseInt(video_height);
+ if (videoHeight != null) {
+ mVideoHeight = Integer.parseInt(videoHeight);
}
- if (bit_rate != null) {
- mBitRate = Integer.parseInt(bit_rate);
+ if (bitRate != null) {
+ mBitRate = Integer.parseInt(bitRate);
}
- if (record_duration != null) {
- mDuration = Integer.parseInt(record_duration);
+ if (recordDuration != null) {
+ mDuration = Integer.parseInt(recordDuration);
}
- if (remove_videos != null) {
- if (remove_videos.compareTo("true") == 0) {
+ if (removeVideos != null) {
+ if (removeVideos.compareTo("true") == 0) {
mRemoveVideo = true;
} else {
mRemoveVideo = false;
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
index ccb0638..1c60401 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/MediaPlayerPerformance.java
@@ -1,12 +1,12 @@
/*
* Copyright (C) 2008 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
@@ -53,7 +53,7 @@ import android.media.MediaMetadataRetriever;
import com.android.mediaframeworktest.MediaProfileReader;
/**
- * Junit / Instrumentation - performance measurement for media player and
+ * Junit / Instrumentation - performance measurement for media player and
* recorder
*
* FIXME:
@@ -100,6 +100,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
super("com.android.mediaframeworktest", MediaFrameworkTest.class);
}
+ @Override
protected void setUp() throws Exception {
super.setUp();
//Insert a 2 second before launching the test activity. This is
@@ -109,18 +110,25 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
MediaTestUtil.getNativeHeapDump(this.getName() + "_before");
- mProcMemWriter = new BufferedWriter(new FileWriter
- (new File(MEDIA_PROCMEM_OUTPUT), true));
- mProcMemWriter.write(this.getName() + "\n");
+ if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+ mProcMemWriter = new BufferedWriter(new FileWriter
+ (new File(MEDIA_PROCMEM_OUTPUT), true));
+ mProcMemWriter.write(this.getName() + "\n");
+ }
mMemWriter = new BufferedWriter(new FileWriter
(new File(MEDIA_MEMORY_OUTPUT), true));
-
+ mMemWriter.write(this.getName() + "\n");
}
+ @Override
protected void tearDown() throws Exception {
if (MediaFrameworkPerfTestRunner.mGetNativeHeapDump)
MediaTestUtil.getNativeHeapDump(this.getName() + "_after");
- mProcMemWriter.close();
+
+ if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+ mProcMemWriter.close();
+ }
+ mMemWriter.write("\n");
mMemWriter.close();
super.tearDown();
}
@@ -157,6 +165,7 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
}
private final class RawPreviewCallback implements PreviewCallback {
+ @Override
public void onPreviewFrame(byte[] rawData, Camera camera) {
mPreviewDone.open();
}
@@ -285,19 +294,21 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
}
}
- public void writeProcmemInfo() throws Exception{
- String cmd = "procmem " + getMediaserverPid();
- Process p = Runtime.getRuntime().exec(cmd);
-
- InputStream inStream = p.getInputStream();
- InputStreamReader inReader = new InputStreamReader(inStream);
- BufferedReader inBuffer = new BufferedReader(inReader);
- String s;
- while ((s = inBuffer.readLine()) != null) {
- mProcMemWriter.write(s);
- mProcMemWriter.write("\n");
+ public void writeProcmemInfo() throws Exception {
+ if (MediaFrameworkPerfTestRunner.mGetProcmem) {
+ String cmd = "procmem " + getMediaserverPid();
+ Process p = Runtime.getRuntime().exec(cmd);
+
+ InputStream inStream = p.getInputStream();
+ InputStreamReader inReader = new InputStreamReader(inStream);
+ BufferedReader inBuffer = new BufferedReader(inReader);
+ String s;
+ while ((s = inBuffer.readLine()) != null) {
+ mProcMemWriter.write(s);
+ mProcMemWriter.write("\n");
+ }
+ mProcMemWriter.write("\n\n");
}
- mProcMemWriter.write("\n\n");
}
public String captureMediaserverInfo() {
@@ -368,13 +379,11 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("H263 Video Playback Only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
mediaStressPlayback(MediaNames.VIDEO_HIGHRES_H263);
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
assertTrue("H263 playback memory test", memoryResult);
}
@@ -385,13 +394,11 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("H264 Video Playback only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
mediaStressPlayback(MediaNames.VIDEO_H264_AMR);
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, DECODER_LIMIT);
assertTrue("H264 playback memory test", memoryResult);
}
@@ -402,7 +409,6 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("H263 video record only\n");
int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
assertTrue("H263 video recording frame rate", frameRate != -1);
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
@@ -411,7 +417,6 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("H263 record only memory test", memoryResult);
}
@@ -422,7 +427,6 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("MPEG4 video record only\n");
int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.MPEG_4_SP);
assertTrue("MPEG4 video recording frame rate", frameRate != -1);
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
@@ -431,7 +435,6 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("mpeg4 record only memory test", memoryResult);
}
@@ -445,14 +448,12 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
mStartPid = getMediaserverPid();
int frameRate = MediaProfileReader.getMaxFrameRateForCodec(MediaRecorder.VideoEncoder.H263);
assertTrue("H263 video recording frame rate", frameRate != -1);
- mMemWriter.write("Audio and h263 video record\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
assertTrue(stressVideoRecord(frameRate, 352, 288, MediaRecorder.VideoEncoder.H263,
MediaRecorder.OutputFormat.MPEG_4, MediaNames.RECORDED_VIDEO_3GP, false));
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("H263 audio video record memory test", memoryResult);
}
@@ -463,13 +464,11 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("Audio record only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
stressAudioRecord(MediaNames.RECORDER_OUTPUT);
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, ENCODER_LIMIT);
assertTrue("audio record only memory test", memoryResult);
}
@@ -480,13 +479,11 @@ public class MediaPlayerPerformance extends ActivityInstrumentationTestCase2<Med
boolean memoryResult = false;
mStartPid = getMediaserverPid();
- mMemWriter.write("Camera Preview Only\n");
for (int i = 0; i < NUM_STRESS_LOOP; i++) {
stressCameraPreview();
getMemoryWriteToLog(i);
writeProcmemInfo();
}
- mMemWriter.write("\n");
memoryResult = validateMemoryResult(mStartPid, mStartMemory, CAMERA_LIMIT);
assertTrue("camera preview memory test", memoryResult);
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
index ab9e36c..ed1d8fc 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/CameraStressTest.java
@@ -28,6 +28,7 @@ import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
+import java.util.List;
import android.hardware.Camera;
import android.hardware.Camera.PictureCallback;
@@ -44,7 +45,7 @@ import com.android.mediaframeworktest.CameraStressTestRunner;
import junit.framework.Assert;
/**
- * Junit / Instrumentation test case for the camera zoom api
+ * Junit / Instrumentation test case for the camera zoom and scene mode APIs
*
* adb shell am instrument
* -e class com.android.mediaframeworktest.stress.CameraStressTest
@@ -54,18 +55,22 @@ public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFram
private String TAG = "CameraStressTest";
private Camera mCamera;
+ private static final int CAMERA_ID = 0;
private static final int NUMBER_OF_ZOOM_LOOPS = 100;
+ private static final int NUMBER_OF_SCENE_MODE_LOOPS = 10;
private static final long WAIT_GENERIC = 3 * 1000; // 3 seconds
private static final long WAIT_TIMEOUT = 10 * 1000; // 10 seconds
private static final long WAIT_ZOOM_ANIMATION = 5 * 1000; // 5 seconds
- private static final String CAMERA_STRESS_OUTPUT =
- "/sdcard/cameraStressOutput.txt";
- private static final int CAMERA_ID = 0;
+ private static final String CAMERA_STRESS_IMAGES_DIRECTORY = "cameraStressImages";
+ private static final String CAMERA_STRESS_IMAGES_PREFIX = "camera-stress-test";
+ private static final String CAMERA_STRESS_OUTPUT = "cameraStressOutput.txt";
private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
private Thread mLooperThread;
private Handler mHandler;
+ private Writer mOutput;
+
public CameraStressTest() {
super("com.android.mediaframeworktest", MediaFrameworkTest.class);
}
@@ -89,6 +94,20 @@ public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFram
}
getActivity();
super.setUp();
+
+ File sdcard = Environment.getExternalStorageDirectory();
+
+ // Create the test images directory if it doesn't exist
+ File stressImagesDirectory = new File(String.format("%s/%s", sdcard,
+ CAMERA_STRESS_IMAGES_DIRECTORY));
+ if (!stressImagesDirectory.exists()) {
+ stressImagesDirectory.mkdir();
+ }
+
+ // Start writing output file
+ File stressOutFile = new File(String.format("%s/%s",sdcard, CAMERA_STRESS_OUTPUT));
+ mOutput = new BufferedWriter(new FileWriter(stressOutFile, true));
+ mOutput.write(this.getName() + ":\n");
}
@Override
@@ -105,6 +124,9 @@ public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFram
mLooperThread = null;
}
+ mOutput.write("\n\n");
+ mOutput.close();
+
super.tearDown();
}
@@ -127,9 +149,7 @@ public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFram
private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback {
public void onError(int error, android.hardware.Camera camera) {
- if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
- assertTrue("Camera test mediaserver died", false);
- }
+ fail(String.format("Camera error, code: %d", error));
}
}
@@ -154,49 +174,76 @@ public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFram
try {
Log.v(TAG, "JPEG picture taken");
- fos = new FileOutputStream(String.format("%s/zoom-test-%d.jpg",
- Environment.getExternalStorageDirectory(), System.currentTimeMillis()));
+ fos = new FileOutputStream(String.format("%s/%s/%s-%d.jpg",
+ Environment.getExternalStorageDirectory(), CAMERA_STRESS_IMAGES_DIRECTORY,
+ CAMERA_STRESS_IMAGES_PREFIX, System.currentTimeMillis()));
fos.write(data);
- }
- catch (FileNotFoundException e) {
- Log.v(TAG, "File not found: " + e.toString());
- }
- catch (IOException e) {
- Log.v(TAG, "Error accessing file: " + e.toString());
- }
- finally {
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "File not found: " + e.toString());
+ } catch (IOException e) {
+ Log.e(TAG, "Error accessing file: " + e.toString());
+ } finally {
try {
if (fos != null) {
fos.close();
}
- }
- catch (IOException e) {
- Log.v(TAG, "Error closing file: " + e.toString());
+ } catch (IOException e) {
+ Log.e(TAG, "Error closing file: " + e.toString());
}
}
}
};
// Helper method for cleaning up pics taken during testStressCameraZoom
- private void cleanupZoomImages() {
+ private void cleanupStressTestImages() {
try {
- File sdcard = Environment.getExternalStorageDirectory();
+ File stressImagesDirectory = new File(String.format("%s/%s",
+ Environment.getExternalStorageDirectory(), CAMERA_STRESS_IMAGES_DIRECTORY));
File[] zoomImages = null;
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
- return name.startsWith("zoom-test-");
+ return name.startsWith(CAMERA_STRESS_IMAGES_PREFIX);
}
};
- zoomImages = sdcard.listFiles(filter);
+ zoomImages = stressImagesDirectory.listFiles(filter);
for (File f : zoomImages) {
f.delete();
}
+ } catch (SecurityException e) {
+ Log.e(TAG, "Security manager access violation: " + e.toString());
}
- catch (SecurityException e) {
- Log.v(TAG, "Security manager access violation: " + e.toString());
+ }
+
+ // Helper method for starting up the camera preview
+ private void startCameraPreview(SurfaceHolder surfaceHolder) {
+ try {
+ mCamera.setErrorCallback(mCameraErrorCallback);
+ mCamera.setPreviewDisplay(surfaceHolder);
+ mCamera.startPreview();
+ Thread.sleep(WAIT_GENERIC);
+ } catch (IOException e) {
+ Log.e(TAG, "Error setting preview display: " + e.toString());
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Error waiting for preview to come up: " + e.toString());
+ } catch (Exception e) {
+ Log.e(TAG, "Error starting up camera preview: " + e.toString());
+ }
+ }
+
+ // Helper method for taking a photo
+ private void capturePhoto() {
+ try {
+ mCamera.takePicture(shutterCallback, rawCallback, jpegCallback);
+ Thread.sleep(WAIT_GENERIC);
+ mCamera.stopPreview();
+ mCamera.release();
+ } catch (InterruptedException e) {
+ Log.e(TAG, "Error waiting for photo to be taken: " + e.toString());
+ } catch (Exception e) {
+ Log.e(TAG, "Error capturing photo: " + e.toString());
}
}
@@ -205,14 +252,11 @@ public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFram
public void testStressCameraZoom() throws Exception {
SurfaceHolder mSurfaceHolder;
mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
- File stressOutFile = new File(CAMERA_STRESS_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
- output.write("Camera zoom stress:\n");
- output.write("Total number of loops: " + NUMBER_OF_ZOOM_LOOPS + "\n");
+ mOutput.write("Total number of loops: " + NUMBER_OF_ZOOM_LOOPS + "\n");
try {
Log.v(TAG, "Start preview");
- output.write("No of loop: ");
+ mOutput.write("No of loop: ");
mCamera = Camera.open(CAMERA_ID);
Camera.Parameters params = mCamera.getParameters();
@@ -220,9 +264,8 @@ public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFram
if (!params.isSmoothZoomSupported() && !params.isZoomSupported()) {
Log.v(TAG, "Device camera does not support zoom");
- assertTrue("Camera zoom stress test", false);
- }
- else {
+ fail("Camera zoom stress test failed");
+ } else {
Log.v(TAG, "Device camera does support zoom");
int nextZoomLevel = 0;
@@ -235,11 +278,7 @@ public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFram
}
});
- mCamera.setErrorCallback(mCameraErrorCallback);
- mCamera.setPreviewDisplay(mSurfaceHolder);
- mCamera.startPreview();
- Thread.sleep(WAIT_GENERIC);
-
+ startCameraPreview(mSurfaceHolder);
params = mCamera.getParameters();
int currentZoomLevel = params.getZoom();
@@ -250,8 +289,7 @@ public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFram
if (params.isSmoothZoomSupported()) {
mCamera.startSmoothZoom(nextZoomLevel);
- }
- else {
+ } else {
params.setZoom(nextZoomLevel);
mCamera.setParameters(params);
}
@@ -259,23 +297,66 @@ public class CameraStressTest extends ActivityInstrumentationTestCase2<MediaFram
// sleep allows for zoom animation to finish
Thread.sleep(WAIT_ZOOM_ANIMATION);
+ capturePhoto();
- // take picture
- mCamera.takePicture(shutterCallback, rawCallback, jpegCallback);
- Thread.sleep(WAIT_GENERIC);
- mCamera.stopPreview();
- mCamera.release();
- output.write(" ," + i);
+ if (i == 0) {
+ mOutput.write(Integer.toString(i));
+ } else {
+ mOutput.write(", " + i);
+ }
}
}
-
- cleanupZoomImages();
+ cleanupStressTestImages();
+ } catch (Exception e) {
+ Log.e(TAG, e.toString());
+ fail("Camera zoom stress test Exception");
}
- catch (Exception e) {
- assertTrue("Camera zoom stress test Exception", false);
- Log.v(TAG, e.toString());
+ }
+
+ // Test case for stressing the camera scene mode feature
+ @LargeTest
+ public void testStressCameraSceneModes() throws Exception {
+ SurfaceHolder mSurfaceHolder;
+ mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+
+ try {
+ mCamera = Camera.open(CAMERA_ID);
+ Camera.Parameters params = mCamera.getParameters();
+ mCamera.release();
+ List<String> supportedSceneModes = params.getSupportedSceneModes();
+ assertNotNull("No scene modes supported", supportedSceneModes);
+
+ mOutput.write("Total number of loops: " +
+ (NUMBER_OF_SCENE_MODE_LOOPS * supportedSceneModes.size()) + "\n");
+ Log.v(TAG, "Start preview");
+ mOutput.write("No of loop: ");
+
+ for (int i = 0; i < supportedSceneModes.size(); i++) {
+ for (int j = 0; j < NUMBER_OF_SCENE_MODE_LOOPS; j++) {
+ runOnLooper(new Runnable() {
+ @Override
+ public void run() {
+ mCamera = Camera.open(CAMERA_ID);
+ }
+ });
+
+ startCameraPreview(mSurfaceHolder);
+ Log.v(TAG, "Setting mode to " + supportedSceneModes.get(i));
+ params.setSceneMode(supportedSceneModes.get(i));
+ mCamera.setParameters(params);
+ capturePhoto();
+
+ if ((i == 0) && (j == 0)) {
+ mOutput.write(Integer.toString(j + i * NUMBER_OF_SCENE_MODE_LOOPS));
+ } else {
+ mOutput.write(", " + (j + i * NUMBER_OF_SCENE_MODE_LOOPS));
+ }
+ }
+ }
+ cleanupStressTestImages();
+ } catch (Exception e) {
+ Log.e(TAG, e.toString());
+ fail("Camera scene mode test Exception");
}
- output.write("\n\n");
- output.close();
}
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
index 6995c60..6eb9891 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
@@ -31,6 +31,7 @@ import android.hardware.Camera;
import android.media.CamcorderProfile;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
+import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.test.ActivityInstrumentationTestCase2;
@@ -48,26 +49,26 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
private MediaRecorder mRecorder;
private Camera mCamera;
+ private static final int CAMERA_ID = 0;
private static final int NUMBER_OF_CAMERA_STRESS_LOOPS = 100;
private static final int NUMBER_OF_RECORDER_STRESS_LOOPS = 100;
private static final int NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS = 50;
private static final int NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER = 200;
private static final int NUMBER_OF_TIME_LAPSE_LOOPS = 25;
private static final int TIME_LAPSE_PLAYBACK_WAIT_TIME = 5* 1000; // 5 seconds
+ private static final int USE_TEST_RUNNER_PROFILE = -1;
+ private static final long WAIT_TIMEOUT = 10 * 1000; // 10 seconds
private static final long WAIT_TIME_CAMERA_TEST = 3 * 1000; // 3 seconds
private static final long WAIT_TIME_RECORDER_TEST = 6 * 1000; // 6 seconds
- private static final String OUTPUT_FILE = "/sdcard/temp";
private static final String OUTPUT_FILE_EXT = ".3gp";
- private static final String MEDIA_STRESS_OUTPUT =
- "/sdcard/mediaStressOutput.txt";
- private static final int CAMERA_ID = 0;
+ private static final String MEDIA_STRESS_OUTPUT = "mediaStressOutput.txt";
private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
private final RecorderErrorCallback mRecorderErrorCallback = new RecorderErrorCallback();
- private final static int WAIT_TIMEOUT = 10 * 1000; // 10 seconds
- private Thread mLooperThread;
private Handler mHandler;
+ private Thread mLooperThread;
+ private Writer mOutput;
public MediaRecorderStressTest() {
super("com.android.mediaframeworktest", MediaFrameworkTest.class);
@@ -95,6 +96,11 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
Thread.sleep(2000);
getActivity();
super.setUp();
+
+ File stressOutFile = new File(String.format("%s/%s",
+ Environment.getExternalStorageDirectory(), MEDIA_STRESS_OUTPUT));
+ mOutput = new BufferedWriter(new FileWriter(stressOutFile, true));
+ mOutput.write(this.getName() + "\n");
}
@Override
@@ -110,7 +116,8 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
}
mLooperThread = null;
}
-
+ mOutput.write("\n\n");
+ mOutput.close();
super.tearDown();
}
@@ -133,16 +140,13 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
private final class CameraErrorCallback implements android.hardware.Camera.ErrorCallback {
public void onError(int error, android.hardware.Camera camera) {
- if (error == android.hardware.Camera.CAMERA_ERROR_SERVER_DIED) {
- assertTrue("Camera test mediaserver died", false);
- }
+ fail(String.format("Camera error, code: %d", error));
}
}
private final class RecorderErrorCallback implements MediaRecorder.OnErrorListener {
public void onError(MediaRecorder mr, int what, int extra) {
- // fail the test case no matter what error come up
- assertTrue("mediaRecorder error", false);
+ fail(String.format("Media recorder error, code: %d\textra: %d", what, extra));
}
}
@@ -151,14 +155,11 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
public void testStressCamera() throws Exception {
SurfaceHolder mSurfaceHolder;
mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
- File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
- output.write("Camera start preview stress:\n");
- output.write("Total number of loops:" +
- NUMBER_OF_CAMERA_STRESS_LOOPS + "\n");
+ Log.v(TAG, "Camera start preview stress test");
+ mOutput.write("Total number of loops:" + NUMBER_OF_CAMERA_STRESS_LOOPS + "\n");
try {
Log.v(TAG, "Start preview");
- output.write("No of loop: ");
+ mOutput.write("No of loop: ");
for (int i = 0; i< NUMBER_OF_CAMERA_STRESS_LOOPS; i++) {
runOnLooper(new Runnable() {
@@ -173,29 +174,27 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
Thread.sleep(WAIT_TIME_CAMERA_TEST);
mCamera.stopPreview();
mCamera.release();
- output.write(" ," + i);
+ if (i == 0) {
+ mOutput.write(i + 1);
+ } else {
+ mOutput.write(String.format(", %d", (i + 1)));
+ }
}
} catch (Exception e) {
- assertTrue("CameraStressTest", false);
- Log.v(TAG, e.toString());
+ Log.e(TAG, e.toString());
+ fail("Camera startup preview stress test");
}
- output.write("\n\n");
- output.close();
}
//Test case for stressing the camera preview.
@LargeTest
public void testStressRecorder() throws Exception {
- String filename;
SurfaceHolder mSurfaceHolder;
mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
- File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
- output.write("H263 video record- reset after prepare Stress test\n");
- output.write("Total number of loops:" +
- NUMBER_OF_RECORDER_STRESS_LOOPS + "\n");
+ Log.v(TAG, "H263 video record: reset after prepare Stress test");
+ mOutput.write("Total number of loops:" + NUMBER_OF_RECORDER_STRESS_LOOPS + "\n");
try {
- output.write("No of loop: ");
+ mOutput.write("No of loop: ");
Log.v(TAG, "Start preview");
for (int i = 0; i < NUMBER_OF_RECORDER_STRESS_LOOPS; i++) {
runOnLooper(new Runnable() {
@@ -205,12 +204,15 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
}
});
Log.v(TAG, "counter = " + i);
- filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
- Log.v(TAG, filename);
+ String fileName = String.format("%s/temp%d%s",
+ Environment.getExternalStorageDirectory(),
+ i, OUTPUT_FILE_EXT);
+
+ Log.v(TAG, fileName);
mRecorder.setOnErrorListener(mRecorderErrorCallback);
mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
- mRecorder.setOutputFile(filename);
+ mRecorder.setOutputFile(fileName);
mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate);
mRecorder.setVideoSize(176,144);
Log.v(TAG, "setEncoder");
@@ -224,30 +226,29 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
Thread.sleep(WAIT_TIME_RECORDER_TEST);
mRecorder.reset();
mRecorder.release();
- output.write(", " + i);
+ if (i == 0) {
+ mOutput.write(i + 1);
+ } else {
+ mOutput.write(String.format(", %d", (i + 1)));
+ }
}
} catch (Exception e) {
- assertTrue("Recorder Stress test", false);
- Log.v(TAG, e.toString());
+ Log.e(TAG, e.toString());
+ fail("H263 video recording stress test");
}
- output.write("\n\n");
- output.close();
}
//Stress test case for switching camera and video recorder preview.
@LargeTest
public void testStressCameraSwitchRecorder() throws Exception {
- String filename;
SurfaceHolder mSurfaceHolder;
mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
- File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
- output.write("Camera and video recorder preview switching\n");
- output.write("Total number of loops:"
- + NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER + "\n");
+ Log.v(TAG, "Camera and video recorder preview switching");
+ mOutput.write("Total number of loops: " +
+ NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER + "\n");
try {
Log.v(TAG, "Start preview");
- output.write("No of loop: ");
+ mOutput.write("No of loop: ");
for (int i = 0; i < NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER; i++) {
runOnLooper(new Runnable() {
@Override
@@ -263,8 +264,10 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
mCamera.release();
mCamera = null;
Log.v(TAG, "release camera");
- filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
- Log.v(TAG, filename);
+ String fileName = String.format("%s/temp%d%s",
+ Environment.getExternalStorageDirectory(),
+ i, OUTPUT_FILE_EXT);
+ Log.v(TAG, fileName);
runOnLooper(new Runnable() {
@Override
public void run() {
@@ -274,7 +277,7 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
mRecorder.setOnErrorListener(mRecorderErrorCallback);
mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
- mRecorder.setOutputFile(filename);
+ mRecorder.setOutputFile(fileName);
mRecorder.setVideoFrameRate(MediaRecorderStressTestRunner.mFrameRate);
mRecorder.setVideoSize(176,144);
Log.v(TAG, "Media recorder setEncoder");
@@ -287,117 +290,167 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
Thread.sleep(WAIT_TIME_CAMERA_TEST);
mRecorder.release();
Log.v(TAG, "release video recorder");
- output.write(", " + i);
+ if (i == 0) {
+ mOutput.write(i + 1);
+ } else {
+ mOutput.write(String.format(", %d", (i + 1)));
+ }
}
} catch (Exception e) {
- assertTrue("Camer and recorder switch mode", false);
- Log.v(TAG, e.toString());
+ Log.e(TAG, e.toString());
+ fail("Camera and recorder switch mode");
}
- output.write("\n\n");
- output.close();
}
- public void validateRecordedVideo(String recorded_file) {
+ public void validateRecordedVideo(String recordedFile) {
try {
MediaPlayer mp = new MediaPlayer();
- mp.setDataSource(recorded_file);
+ mp.setDataSource(recordedFile);
mp.prepare();
int duration = mp.getDuration();
if (duration <= 0){
- assertTrue("stressRecordAndPlayback", false);
+ fail("stressRecordAndPlayback");
}
mp.release();
} catch (Exception e) {
- assertTrue("stressRecordAndPlayback", false);
+ fail("stressRecordAndPlayback");
}
}
- public void removeRecordedVideo(String filename){
- File video = new File(filename);
- Log.v(TAG, "remove recorded video " + filename);
+ public void removeRecordedVideo(String fileName){
+ File video = new File(fileName);
+ Log.v(TAG, "remove recorded video " + fileName);
video.delete();
}
- //Stress test case for record a video and play right away.
- @LargeTest
- public void testStressRecordVideoAndPlayback() throws Exception {
- int iterations = MediaRecorderStressTestRunner.mIterations;
- int video_encoder = MediaRecorderStressTestRunner.mVideoEncoder;
- int audio_encoder = MediaRecorderStressTestRunner.mAudioEncdoer;
- int frame_rate = MediaRecorderStressTestRunner.mFrameRate;
- int video_width = MediaRecorderStressTestRunner.mVideoWidth;
- int video_height = MediaRecorderStressTestRunner.mVideoHeight;
- int bit_rate = MediaRecorderStressTestRunner.mBitRate;
- boolean remove_video = MediaRecorderStressTestRunner.mRemoveVideo;
- int record_duration = MediaRecorderStressTestRunner.mDuration;
-
- String filename;
- SurfaceHolder mSurfaceHolder;
- mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
- File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
- Writer output = new BufferedWriter(
- new FileWriter(stressOutFile, true));
- output.write("Video record and play back stress test:\n");
- output.write("Total number of loops:"
- + NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS + "\n");
+ // Helper method for record & playback testing with different camcorder profiles
+ private void recordVideoAndPlayback(int profile) throws Exception {
+ int iterations;
+ int recordDuration;
+ boolean removeVideo;
+
+ int videoEncoder;
+ int audioEncoder;
+ int frameRate;
+ int videoWidth;
+ int videoHeight;
+ int bitRate;
+
+ if (profile != USE_TEST_RUNNER_PROFILE) {
+ assertTrue(String.format("Camera doesn't support profile %d", profile),
+ CamcorderProfile.hasProfile(CAMERA_ID, profile));
+ CamcorderProfile camcorderProfile = CamcorderProfile.get(CAMERA_ID, profile);
+ videoEncoder = camcorderProfile.videoCodec;
+ audioEncoder = camcorderProfile.audioCodec;
+ frameRate = camcorderProfile.videoFrameRate;
+ videoWidth = camcorderProfile.videoFrameWidth;
+ videoHeight = camcorderProfile.videoFrameHeight;
+ bitRate = camcorderProfile.videoBitRate;
+ } else {
+ videoEncoder = MediaRecorderStressTestRunner.mVideoEncoder;
+ audioEncoder = MediaRecorderStressTestRunner.mAudioEncoder;
+ frameRate = MediaRecorderStressTestRunner.mFrameRate;
+ videoWidth = MediaRecorderStressTestRunner.mVideoWidth;
+ videoHeight = MediaRecorderStressTestRunner.mVideoHeight;
+ bitRate = MediaRecorderStressTestRunner.mBitRate;
+ }
+ iterations = MediaRecorderStressTestRunner.mIterations;
+ recordDuration = MediaRecorderStressTestRunner.mDuration;
+ removeVideo = MediaRecorderStressTestRunner.mRemoveVideo;
+
+ SurfaceHolder surfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+ mOutput.write("Total number of loops: " + iterations + "\n");
+
try {
- output.write("No of loop: ");
- for (int i = 0; i < iterations; i++){
- filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
- Log.v(TAG, filename);
+ mOutput.write("No of loop: ");
+ for (int i = 0; i < iterations; i++) {
+ String fileName = String.format("%s/temp%d%s",
+ Environment.getExternalStorageDirectory(), i, OUTPUT_FILE_EXT);
+ Log.v(TAG, fileName);
+
runOnLooper(new Runnable() {
@Override
public void run() {
mRecorder = new MediaRecorder();
}
});
+
Log.v(TAG, "iterations : " + iterations);
- Log.v(TAG, "video_encoder : " + video_encoder);
- Log.v(TAG, "audio_encoder : " + audio_encoder);
- Log.v(TAG, "frame_rate : " + frame_rate);
- Log.v(TAG, "video_width : " + video_width);
- Log.v(TAG, "video_height : " + video_height);
- Log.v(TAG, "bit rate : " + bit_rate);
- Log.v(TAG, "record_duration : " + record_duration);
+ Log.v(TAG, "video encoder : " + videoEncoder);
+ Log.v(TAG, "audio encoder : " + audioEncoder);
+ Log.v(TAG, "frame rate : " + frameRate);
+ Log.v(TAG, "video width : " + videoWidth);
+ Log.v(TAG, "video height : " + videoHeight);
+ Log.v(TAG, "bit rate : " + bitRate);
+ Log.v(TAG, "record duration : " + recordDuration);
mRecorder.setOnErrorListener(mRecorderErrorCallback);
mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
mRecorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP);
- mRecorder.setOutputFile(filename);
- mRecorder.setVideoFrameRate(frame_rate);
- mRecorder.setVideoSize(video_width, video_height);
- mRecorder.setVideoEncoder(video_encoder);
- mRecorder.setAudioEncoder(audio_encoder);
- mRecorder.setVideoEncodingBitRate(bit_rate);
+ mRecorder.setOutputFile(fileName);
+ mRecorder.setVideoFrameRate(frameRate);
+ mRecorder.setVideoSize(videoWidth, videoHeight);
+ mRecorder.setVideoEncoder(videoEncoder);
+ mRecorder.setAudioEncoder(audioEncoder);
+ mRecorder.setVideoEncodingBitRate(bitRate);
+
Log.v(TAG, "mediaRecorder setPreview");
- mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
+ mRecorder.setPreviewDisplay(surfaceHolder.getSurface());
mRecorder.prepare();
mRecorder.start();
- Thread.sleep(record_duration);
+ Thread.sleep(recordDuration);
Log.v(TAG, "Before stop");
mRecorder.stop();
mRecorder.release();
+
//start the playback
MediaPlayer mp = new MediaPlayer();
- mp.setDataSource(filename);
+ mp.setDataSource(fileName);
mp.setDisplay(MediaFrameworkTest.mSurfaceView.getHolder());
mp.prepare();
mp.start();
- Thread.sleep(record_duration);
+ Thread.sleep(recordDuration);
mp.release();
- validateRecordedVideo(filename);
- if (remove_video) {
- removeRecordedVideo(filename);
+ validateRecordedVideo(fileName);
+ if (removeVideo) {
+ removeRecordedVideo(fileName);
+ }
+ if (i == 0) {
+ mOutput.write(i + 1);
+ } else {
+ mOutput.write(String.format(", %d", (i + 1)));
}
- output.write(", " + i);
}
} catch (Exception e) {
- Log.v(TAG, e.toString());
- assertTrue("record and playback", false);
+ Log.e(TAG, e.toString());
+ fail("Record and playback");
}
- output.write("\n\n");
- output.close();
+ }
+
+ // Record and playback stress test @ 1080P quality
+ @LargeTest
+ public void testStressRecordVideoAndPlayback1080P() throws Exception {
+ recordVideoAndPlayback(CamcorderProfile.QUALITY_1080P);
+ }
+
+ // Record and playback stress test @ 720P quality
+ @LargeTest
+ public void testStressRecordVideoAndPlayback720P() throws Exception {
+ recordVideoAndPlayback(CamcorderProfile.QUALITY_720P);
+ }
+
+ // Record and playback stress test @ 480P quality
+ @LargeTest
+ public void testStressRecordVideoAndPlayback480P() throws Exception {
+ recordVideoAndPlayback(CamcorderProfile.QUALITY_480P);
+ }
+
+ // This test method uses the codec info from the test runner. Use this
+ // for more granular control of video encoding.
+ @LargeTest
+ public void defaultStressRecordVideoAndPlayback() throws Exception {
+ recordVideoAndPlayback(USE_TEST_RUNNER_PROFILE);
}
// Test case for stressing time lapse
@@ -405,21 +458,19 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
public void testStressTimeLapse() throws Exception {
SurfaceHolder mSurfaceHolder;
mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
- int record_duration = MediaRecorderStressTestRunner.mTimeLapseDuration;
- boolean remove_video = MediaRecorderStressTestRunner.mRemoveVideo;
+ int recordDuration = MediaRecorderStressTestRunner.mTimeLapseDuration;
+ boolean removeVideo = MediaRecorderStressTestRunner.mRemoveVideo;
double captureRate = MediaRecorderStressTestRunner.mCaptureRate;
- String filename;
- File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
- Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
- output.write("Start camera time lapse stress:\n");
- output.write("Total number of loops: " + NUMBER_OF_TIME_LAPSE_LOOPS + "\n");
+ Log.v(TAG, "Start camera time lapse stress:");
+ mOutput.write("Total number of loops: " + NUMBER_OF_TIME_LAPSE_LOOPS + "\n");
try {
- for (int j = 0, n = Camera.getNumberOfCameras(); j < n; j++) {
- output.write("No of loop: camera " + j);
- for (int i = 0; i < NUMBER_OF_TIME_LAPSE_LOOPS; i++) {
- filename = OUTPUT_FILE + j + "_" + i + OUTPUT_FILE_EXT;
- Log.v(TAG, filename);
+ for (int i = 0, n = Camera.getNumberOfCameras(); i < n; i++) {
+ mOutput.write("No of loop: camera " + i);
+ for (int j = 0; j < NUMBER_OF_TIME_LAPSE_LOOPS; j++) {
+ String fileName = String.format("%s/temp%d_%d%s",
+ Environment.getExternalStorageDirectory(), i, j, OUTPUT_FILE_EXT);
+ Log.v(TAG, fileName);
runOnLooper(new Runnable() {
@Override
public void run() {
@@ -438,12 +489,12 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
CamcorderProfile.get(j, CamcorderProfile.QUALITY_TIME_LAPSE_HIGH);
mRecorder.setProfile(profile);
- // Set the timelapse setting; 0.1 = 10 sec timelapse, 0.5 = 2 sec timelapse, etc.
+ // Set the timelapse setting; 0.1 = 10 sec timelapse, 0.5 = 2 sec timelapse, etc
// http://developer.android.com/guide/topics/media/camera.html#time-lapse-video
mRecorder.setCaptureRate(captureRate);
// Set output file
- mRecorder.setOutputFile(filename);
+ mRecorder.setOutputFile(fileName);
// Set the preview display
Log.v(TAG, "mediaRecorder setPreviewDisplay");
@@ -451,40 +502,40 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
mRecorder.prepare();
mRecorder.start();
- Thread.sleep(record_duration);
+ Thread.sleep(recordDuration);
Log.v(TAG, "Before stop");
mRecorder.stop();
mRecorder.release();
// Start the playback
MediaPlayer mp = new MediaPlayer();
- mp.setDataSource(filename);
+ mp.setDataSource(fileName);
mp.setDisplay(mSurfaceHolder);
mp.prepare();
mp.start();
Thread.sleep(TIME_LAPSE_PLAYBACK_WAIT_TIME);
mp.release();
- validateRecordedVideo(filename);
- if (remove_video) {
- removeRecordedVideo(filename);
+ validateRecordedVideo(fileName);
+ if (removeVideo) {
+ removeRecordedVideo(fileName);
+ }
+
+ if (j == 0) {
+ mOutput.write(j + 1);
+ } else {
+ mOutput.write(String.format(", %d", (j + 1)));
}
- output.write(", " + i);
}
}
+ } catch (IllegalStateException e) {
+ Log.e(TAG, e.toString());
+ fail("Camera time lapse stress test IllegalStateException");
+ } catch (IOException e) {
+ Log.e(TAG, e.toString());
+ fail("Camera time lapse stress test IOException");
+ } catch (Exception e) {
+ Log.e(TAG, e.toString());
+ fail("Camera time lapse stress test Exception");
}
- catch (IllegalStateException e) {
- assertTrue("Camera time lapse stress test IllegalStateException", false);
- Log.v(TAG, e.toString());
- }
- catch (IOException e) {
- assertTrue("Camera time lapse stress test IOException", false);
- Log.v(TAG, e.toString());
- }
- catch (Exception e) {
- assertTrue("Camera time lapse stress test Exception", false);
- Log.v(TAG, e.toString());
- }
- output.write("\n\n");
- output.close();
}
}
diff --git a/media/tests/contents/media_api/music/sine_200+1000Hz_44K_mo.wav b/media/tests/contents/media_api/music/sine_200+1000Hz_44K_mo.wav
index 312b6fb..24c2a0d 100755..100644
--- a/media/tests/contents/media_api/music/sine_200+1000Hz_44K_mo.wav
+++ b/media/tests/contents/media_api/music/sine_200+1000Hz_44K_mo.wav
Binary files differ