Merge change 263 into donut
* changes:
location: Generalize support for location provider usage tracking.
diff --git a/media/java/android/media/AudioTrack.java b/media/java/android/media/AudioTrack.java
index 4196ef3..3cd841d 100644
--- a/media/java/android/media/AudioTrack.java
+++ b/media/java/android/media/AudioTrack.java
@@ -32,22 +32,24 @@
* It allows to stream PCM audio buffers to the audio hardware for playback. This is
* achieved by "pushing" the data to the AudioTrack object using one of the
* {@link #write(byte[], int, int)} and {@link #write(short[], int, int)} methods.
- * <p>An AudioTrack instance can operate under two modes: static of streaming.<br>
- * The Streaming mode consists in continuously writing data to the AudioTrack, using one
- * of the write() methods. These are blocking and return when the data has been transferred
- * from the Java layer to the native layer, and is queued for playback. The streaming mode
+ *
+ * <p>An AudioTrack instance can operate under two modes: static or streaming.<br>
+ * In Streaming mode, the application writes a continuous stream of data to the AudioTrack, using
+ * one of the write() methods. These are blocking and return when the data has been transferred
+ * from the Java layer to the native layer and queued for playback. The streaming mode
* is most useful when playing blocks of audio data that for instance are:
* <ul>
* <li>too big to fit in memory because of the duration of the sound to play,</li>
* <li>too big to fit in memory because of the characteristics of the audio data
* (high sampling rate, bits per sample ...)</li>
- * <li>chosen, received or generated as the audio keeps playing.</li>
+ * <li>received or generated while previously queued audio is playing.</li>
* </ul>
* The static mode is to be chosen when dealing with short sounds that fit in memory and
- * that need to be played with the smallest latency possible. Static mode AudioTrack instances can
- * play the sound without the need to transfer the audio data from Java to the audio hardware
+ * that need to be played with the smallest latency possible. AudioTrack instances in static mode
+ * can play the sound without the need to transfer the audio data from Java to native layer
* each time the sound is to be played. The static mode will therefore be preferred for UI and
* game sounds that are played often, and with the smallest overhead possible.
+ *
* <p>Upon creation, an AudioTrack object initializes its associated audio buffer.
* The size of this buffer, specified during the construction, determines how long an AudioTrack
* can play before running out of data.<br>
@@ -66,11 +68,11 @@
/** Maximum value for a channel volume */
private static final float VOLUME_MAX = 1.0f;
- /** state of an AudioTrack this is stopped */
+ /** indicates AudioTrack state is stopped */
public static final int PLAYSTATE_STOPPED = 1; // matches SL_PLAYSTATE_STOPPED
- /** state of an AudioTrack this is paused */
+ /** indicates AudioTrack state is paused */
public static final int PLAYSTATE_PAUSED = 2; // matches SL_PLAYSTATE_PAUSED
- /** state of an AudioTrack this is playing */
+ /** indicates AudioTrack state is playing */
public static final int PLAYSTATE_PLAYING = 3; // matches SL_PLAYSTATE_PLAYING
/**
@@ -85,7 +87,7 @@
public static final int MODE_STREAM = 1;
/**
- * State of an AudioTrack that was not successfully initialized upon creation
+ * State of an AudioTrack that was not successfully initialized upon creation.
*/
public static final int STATE_UNINITIALIZED = 0;
/**
@@ -126,11 +128,11 @@
// Events:
// to keep in sync with frameworks/base/include/media/AudioTrack.h
/**
- * Event id for when the playback head has reached a previously set marker.
+ * Event id denotes when playback head has reached a previously set marker.
*/
private static final int NATIVE_EVENT_MARKER = 3;
/**
- * Event id for when the previously set update period has passed during playback.
+ * Event id denotes when previously set update period has elapsed during playback.
*/
private static final int NATIVE_EVENT_NEW_POS = 4;
@@ -141,11 +143,11 @@
// Member variables
//--------------------
/**
- * Indicates the state of the AudioTrack instance
+ * Indicates the state of the AudioTrack instance.
*/
private int mState = STATE_UNINITIALIZED;
/**
- * Indicates the play state of the AudioTrack instance
+ * Indicates the play state of the AudioTrack instance.
*/
private int mPlayState = PLAYSTATE_STOPPED;
/**
@@ -159,7 +161,7 @@
*/
private OnPlaybackPositionUpdateListener mPositionListener = null;
/**
- * Lock to protect event listener updates against event notifications
+ * Lock to protect event listener updates against event notifications.
*/
private final Object mPositionListenerLock = new Object();
/**
@@ -167,11 +169,11 @@
*/
private int mNativeBufferSizeInBytes = 0;
/**
- * Handler for marker events coming from the native code
+ * Handler for marker events coming from the native code.
*/
private NativeEventHandlerDelegate mEventHandlerDelegate = null;
/**
- * Looper associated with the thread that creates the AudioTrack instance
+ * Looper associated with the thread that creates the AudioTrack instance.
*/
private Looper mInitializationLooper = null;
/**
@@ -179,7 +181,7 @@
*/
private int mSampleRate = 22050;
/**
- * The number of input audio channels (1 is mono, 2 is stereo)
+ * The number of input audio channels (1 is mono, 2 is stereo).
*/
private int mChannelCount = 1;
/**
@@ -194,7 +196,7 @@
*/
private int mDataLoadMode = MODE_STREAM;
/**
- * The current audio channel configuration
+ * The current audio channel configuration.
*/
private int mChannelConfiguration = AudioFormat.CHANNEL_CONFIGURATION_MONO;
/**
@@ -209,7 +211,7 @@
// Used exclusively by native code
//--------------------
/**
- * Accessed by native methods: provides access to C++ AudioTrack object
+ * Accessed by native methods: provides access to C++ AudioTrack object.
*/
@SuppressWarnings("unused")
private int mNativeTrackInJavaObj;
@@ -227,17 +229,14 @@
/**
* Class constructor.
* @param streamType the type of the audio stream. See
-
* {@link AudioManager#STREAM_VOICE_CALL}, {@link AudioManager#STREAM_SYSTEM},
* {@link AudioManager#STREAM_RING}, {@link AudioManager#STREAM_MUSIC} and
* {@link AudioManager#STREAM_ALARM}
* @param sampleRateInHz the sample rate expressed in Hertz. Examples of rates are (but
* not limited to) 44100, 22050 and 11025.
* @param channelConfig describes the configuration of the audio channels.
-
* See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and
* {@link AudioFormat#CHANNEL_CONFIGURATION_STEREO}
-
* @param audioFormat the format in which the audio data is represented.
* See {@link AudioFormat#ENCODING_PCM_16BIT} and
* {@link AudioFormat#ENCODING_PCM_8BIT}
@@ -245,6 +244,9 @@
* from for playback. If using the AudioTrack in streaming mode, you can write data into
* this buffer in smaller chunks than this size. If using the AudioTrack in static mode,
* this is the maximum size of the sound that will be played for this instance.
+ * See {@link #getMinBufferSize(int, int, int)} to determine the minimum required buffer size
+ * for the successful creation of an AudioTrack instance in streaming mode. Using values
+ * smaller than getMinBufferSize() will result in an initialization failure.
* @param mode streaming or static buffer. See {@link #MODE_STATIC} and {@link #MODE_STREAM}
* @throws java.lang.IllegalArgumentException
*/
@@ -423,8 +425,8 @@
}
/**
- * Returns the current playback rate in Hz. Note that this rate may differ from one set using
- * {@link #setPlaybackRate(int)} as the value effectively set is implementation-dependent.
+ * Returns the current playback rate in Hz. Note that this rate may differ from the one set
+ * with {@link #setPlaybackRate(int)} as the value effectively used is implementation-dependent.
*/
public int getPlaybackRate() {
return native_get_playback_rate();
@@ -470,6 +472,9 @@
* AudioTrack instance has been created to check if it was initialized
* properly. This ensures that the appropriate hardware resources have been
* acquired.
+ * @see #STATE_INITIALIZED
+ * @see #STATE_NO_STATIC_DATA
+ * @see #STATE_UNINITIALIZED
*/
public int getState() {
return mState;
@@ -486,28 +491,28 @@
}
/**
- * Returns the native frame count used by the hardware
+ * Returns the native frame count used by the hardware.
*/
protected int getNativeFrameCount() {
return native_get_native_frame_count();
}
/**
- * @return marker position in frames
+ * Returns marker position expressed in frames.
*/
public int getNotificationMarkerPosition() {
return native_get_marker_pos();
}
/**
- * @return update period in frames
+ * Returns the notification update period expressed in frames.
*/
public int getPositionNotificationPeriod() {
return native_get_pos_update_period();
}
/**
- * @return playback head position in frames
+ * Returns the playback head position expressed in frames
*/
public int getPlaybackHeadPosition() {
return native_get_position();
@@ -522,7 +527,9 @@
/**
* Returns the minimum buffer size required for the successful creation of an AudioTrack
- * object to be created in the {@link #MODE_STREAM} mode.
+ * object to be created in the {@link #MODE_STREAM} mode. Note that this size doesn't
+ * guarantee a smooth playback under load, and higher values should be chosen according to
+ * the expected frequency at which the buffer will be refilled with additional data to play.
* @param sampleRateInHz the sample rate expressed in Hertz.
* @param channelConfig describes the configuration of the audio channels.
* See {@link AudioFormat#CHANNEL_CONFIGURATION_MONO} and
@@ -533,7 +540,7 @@
* @return {@link #ERROR_BAD_VALUE} if an invalid parameter was passed,
* or {@link #ERROR} if the implementation was unable to query the hardware for its output
* properties,
- * or the minimum buffer size expressed in number of bytes.
+ * or the minimum buffer size expressed in bytes.
*/
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
int channelCount = 0;
@@ -577,13 +584,22 @@
/**
* Sets the listener the AudioTrack notifies when a previously set marker is reached or
* for each periodic playback head position update.
+ * Notifications will be received in the same thread as the one in which the AudioTrack
+ * instance was created.
* @param listener
*/
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener listener) {
setPlaybackPositionUpdateListener(listener, null);
}
-
+ /**
+ * Sets the listener the AudioTrack notifies when a previously set marker is reached or
+ * for each periodic playback head position update.
+ * Use this method to receive AudioTrack events in the Handler associated with another
+ * thread than the one in which you created the AudioTrack instance.
+ * @param listener
+ * @param handler the Handler that will receive the event notification messages.
+ */
public void setPlaybackPositionUpdateListener(OnPlaybackPositionUpdateListener listener,
Handler handler) {
synchronized (mPositionListenerLock) {
@@ -636,13 +652,17 @@
* the audio data will be consumed and played back, not the original sampling rate of the
* content. Setting it to half the sample rate of the content will cause the playback to
* last twice as long, but will also result result in a negative pitch shift.
- * The current implementation supports a maximum sample rate of twice the hardware output
- * sample rate (see {@link #getNativeOutputSampleRate(int)}). Use {@link #getSampleRate()} to
- * check the rate actually used in hardware after potential clamping.
- * @param sampleRateInHz
+ * The current implementation supports a maximum sample rate of 64kHz.
+ * Use {@link #getSampleRate()} to check the rate actually used in hardware after
+ * potential clamping.
+ * @param sampleRateInHz the sample rate expressed in Hz
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
+ // FIXME: the implementation should support twice the hardware output sample rate
+ // (see {@link #getNativeOutputSampleRate(int)}), but currently
+ // due to the representation of the sample rate in the native layer, the sample rate
+ // is limited to 65535Hz
public int setPlaybackRate(int sampleRateInHz) {
if (mState != STATE_INITIALIZED) {
return ERROR_INVALID_OPERATION;
@@ -656,7 +676,7 @@
/**
- *
+ * Sets the position of the notification marker.
* @param markerInFrames marker in frames
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
@@ -670,7 +690,8 @@
/**
- * @param periodInFrames update period in frames
+ * Sets the period for the periodic notification event.
+ * @param periodInFrames update period expressed in frames
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_INVALID_OPERATION}
*/
public int setPositionNotificationPeriod(int periodInFrames) {
@@ -683,7 +704,7 @@
/**
* Sets the playback head position. The track must be stopped for the position to be changed.
- * @param positionInFrames playback head position in frames
+ * @param positionInFrames playback head position expressed in frames
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
* {@link #ERROR_INVALID_OPERATION}
*/
@@ -699,8 +720,8 @@
/**
* Sets the loop points and the loop count. The loop can be infinite.
- * @param startInFrames loop start marker in frames
- * @param endInFrames loop end marker in frames
+ * @param startInFrames loop start marker expressed in frames
+ * @param endInFrames loop end marker expressed in frames
* @param loopCount the number of times the loop is looped.
* A value of -1 means infinite looping.
* @return error code or success, see {@link #SUCCESS}, {@link #ERROR_BAD_VALUE},
@@ -797,7 +818,8 @@
/**
* Writes the audio data to the audio hardware for playback.
* @param audioData the array that holds the data to play.
- * @param offsetInBytes the offset in audioData where the data to play starts.
+ * @param offsetInBytes the offset expressed in bytes in audioData where the data to play
+ * starts.
* @param sizeInBytes the number of bytes to read in audioData after the offset.
* @return the number of bytes that were written or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
@@ -827,7 +849,8 @@
/**
* Writes the audio data to the audio hardware for playback.
* @param audioData the array that holds the data to play.
- * @param offsetInShorts the offset in audioData where the data to play starts.
+ * @param offsetInShorts the offset expressed in shorts in audioData where the data to play
+ * starts.
* @param sizeInShorts the number of bytes to read in audioData after the offset.
* @return the number of shorts that were written or {@link #ERROR_INVALID_OPERATION}
* if the object wasn't properly initialized, or {@link #ERROR_BAD_VALUE} if
diff --git a/media/java/android/media/SoundPool.java b/media/java/android/media/SoundPool.java
index ab3274b..b13c2e6 100644
--- a/media/java/android/media/SoundPool.java
+++ b/media/java/android/media/SoundPool.java
@@ -46,6 +46,19 @@
* number of streams helps to cap CPU loading and reducing the likelihood that
* audio mixing will impact visuals or UI performance.</p>
*
+ * <p>Sounds can be looped by setting a non-zero loop value. A value of -1
+ * causes the sound to loop forever. In this case, the application must
+ * explicitly call the stop() function to stop the sound. Any other non-zero
+ * value will cause the sound to repeat the specified number of times, e.g.
+ * a value of 3 causes the sound to play a total of 4 times.</p>
+ *
+ * <p>The playback rate can also be changed. A playback rate of 1.0 causes
+ * the sound to play at its original frequency (resampled, if necessary,
+ * to the hardware output frequency). A playback rate of 2.0 causes the
+ * sound to play at twice its original frequency, and a playback rate of
+ * 0.5 causes it to play at half its original frequency. The playback
+ * rate range is 0.5 to 2.0.</p>
+ *
* <p>Priority runs low to high, i.e. higher numbers are higher priority.
* Priority is used when a call to play() would cause the number of active
* streams to exceed the value established by the maxStreams parameter when
@@ -72,6 +85,13 @@
* adjusting the playback rate in real-time for doppler or synthesis
* effects.</p>
*
+ * <p>Note that since streams can be stopped due to resource constraints, the
+ * streamID is a reference to a particular instance of a stream. If the stream
+ * is stopped to allow a higher priority stream to play, the stream is no
+ * longer be valid. However, the application is allowed to call methods on
+ * the streamID without error. This may help simplify program logic since
+ * the application need not concern itself with the stream lifecycle.</p>
+ *
* <p>In our example, when the player has completed the level, the game
* logic should call SoundPool.release() to release all the native resources
* in use and then set the SoundPool reference to null. If the player starts
@@ -104,10 +124,11 @@
}
/**
- * Load the sound from the specified path
- *
+ * Load the sound from the specified path.
+ *
* @param path the path to the audio file
- * @param priority the priority of the sound. Currently has no effect.
+ * @param priority the priority of the sound. Currently has no effect. Use
+ * a value of 1 for future compatibility.
* @return a sound ID. This value can be used to play or unload the sound.
*/
public int load(String path, int priority)
@@ -133,17 +154,18 @@
}
/**
- * Load the sound from the specified APK resource
+ * Load the sound from the specified APK resource.
*
- * <p>Note that the extension is dropped. For example, if you want to load
+ * Note that the extension is dropped. For example, if you want to load
* a sound from the raw resource file "explosion.mp3", you would specify
* "R.raw.explosion" as the resource ID. Note that this means you cannot
* have both an "explosion.wav" and an "explosion.mp3" in the res/raw
- * directory.</p>
+ * directory.
*
* @param context the application context
* @param resId the resource ID
- * @param priority the priority of the sound. Currently has no effect.
+ * @param priority the priority of the sound. Currently has no effect. Use
+ * a value of 1 for future compatibility.
* @return a sound ID. This value can be used to play or unload the sound.
*/
public int load(Context context, int resId, int priority) {
@@ -162,10 +184,11 @@
}
/**
- * Load the sound from an asset file descriptor
+ * Load the sound from an asset file descriptor.
*
* @param afd an asset file descriptor
- * @param priority the priority of the sound. Currently has no effect.
+ * @param priority the priority of the sound. Currently has no effect. Use
+ * a value of 1 for future compatibility.
* @return a sound ID. This value can be used to play or unload the sound.
*/
public int load(AssetFileDescriptor afd, int priority) {
@@ -181,16 +204,17 @@
}
/**
- * Load the sound from a FileDescriptor
+ * Load the sound from a FileDescriptor.
*
- * <p>This version is useful if you store multiple sounds in a single
+ * This version is useful if you store multiple sounds in a single
* binary. The offset specifies the offset from the start of the file
- * and the length specifies the length of the sound within the file.</p>
+ * and the length specifies the length of the sound within the file.
*
* @param fd a FileDescriptor object
* @param offset offset to the start of the sound
* @param length length of the sound
- * @param priority the priority of the sound. Currently has no effect.
+ * @param priority the priority of the sound. Currently has no effect. Use
+ * a value of 1 for future compatibility.
* @return a sound ID. This value can be used to play or unload the sound.
*/
public int load(FileDescriptor fd, long offset, long length, int priority) {
@@ -202,11 +226,11 @@
private native final int _load(FileDescriptor fd, long offset, long length, int priority);
/**
- * Unload a sound from a sound ID
+ * Unload a sound from a sound ID.
*
- * <p>Unloads the sound specified by the soundID. This is the value
+ * Unloads the sound specified by the soundID. This is the value
* returned by the load() function. Returns true if the sound is
- * successfully unloaded, false if the sound was already unloaded.</p>
+ * successfully unloaded, false if the sound was already unloaded.
*
* @param soundID a soundID returned by the load() function
* @return true if just unloaded, false if previously unloaded
@@ -214,66 +238,77 @@
public native final boolean unload(int soundID);
/**
- * Play a sound from a sound ID
+ * Play a sound from a sound ID.
*
- * <p>Play the sound specified by the soundID. This is the value
+ * Play the sound specified by the soundID. This is the value
* returned by the load() function. Returns a non-zero streamID
* if successful, zero if it fails. The streamID can be used to
* further control playback. Note that calling play() may cause
* another sound to stop playing if the maximum number of active
- * streams is exceeded.</p>
+ * streams is exceeded. A loop value of -1 means loop forever,
+ * a value of 0 means don't loop, other values indicate the
+ * number of repeats, e.g. a value of 1 plays the audio twice.
+ * The playback rate allows the application to vary the playback
+ * rate (pitch) of the sound. A value of 1.0 means play back at
+ * the original frequency. A value of 2.0 means play back twice
+ * as fast, and a value of 0.5 means playback at half speed.
*
* @param soundID a soundID returned by the load() function
+ * @param leftVolume left volume value (range = 0.0 to 1.0)
+ * @param rightVolume right volume value (range = 0.0 to 1.0)
+ * @param priority stream priority (0 = lowest priority)
+ * @param loop loop mode (0 = no loop, -1 = loop forever)
+ * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
* @return non-zero streamID if successful, zero if failed
*/
public native final int play(int soundID, float leftVolume, float rightVolume,
int priority, int loop, float rate);
/**
- * Pause a playback stream
+ * Pause a playback stream.
*
- * <p>Pause the stream specified by the streamID. This is the
+ * Pause the stream specified by the streamID. This is the
* value returned by the play() function. If the stream is
* playing, it will be paused. If the stream is not playing
* (e.g. is stopped or was previously paused), calling this
- * function will have no effect.</p>
+ * function will have no effect.
*
* @param streamID a streamID returned by the play() function
*/
public native final void pause(int streamID);
/**
- * Resume a playback stream
+ * Resume a playback stream.
*
- * <p>Resume the stream specified by the streamID. This
+ * Resume the stream specified by the streamID. This
* is the value returned by the play() function. If the stream
* is paused, this will resume playback. If the stream was not
- * previously paused, calling this function will have no effect.</p>
+ * previously paused, calling this function will have no effect.
*
* @param streamID a streamID returned by the play() function
*/
public native final void resume(int streamID);
/**
- * Stop a playback stream
+ * Stop a playback stream.
*
- * <p>Stop the stream specified by the streamID. This
+ * Stop the stream specified by the streamID. This
* is the value returned by the play() function. If the stream
* is playing, it will be stopped. It also releases any native
* resources associated with this stream. If the stream is not
- * playing, it will have no effect.</p>
+ * playing, it will have no effect.
*
* @param streamID a streamID returned by the play() function
*/
public native final void stop(int streamID);
/**
- * Set stream volume
+ * Set stream volume.
*
- * <p>Sets the volume on the stream specified by the streamID.
+ * Sets the volume on the stream specified by the streamID.
* This is the value returned by the play() function. The
* value must be in the range of 0.0 to 1.0. If the stream does
- * not exist, it will have no effect.</p>
+ * not exist, it will have no effect.
*
* @param streamID a streamID returned by the play() function
* @param leftVolume left volume value (range = 0.0 to 1.0)
@@ -283,29 +318,51 @@
float leftVolume, float rightVolume);
/**
- * Change stream priority
+ * Change stream priority.
*
- * <p>Change the priority of the stream specified by the streamID.
+ * Change the priority of the stream specified by the streamID.
* This is the value returned by the play() function. Affects the
- * order in which streams are re-used to play new sounds.
+ * order in which streams are re-used to play new sounds. If the
+ * stream does not exist, it will have no effect.
*
* @param streamID a streamID returned by the play() function
*/
public native final void setPriority(int streamID, int priority);
/**
- * Change stream priority
+ * Set loop mode.
*
- * <p>Change the priority of the stream specified by the streamID.
- * This is the value returned by the play() function. Affects the
- * order in which streams are re-used to play new sounds.
+ * Change the loop mode. A loop value of -1 means loop forever,
+ * a value of 0 means don't loop, other values indicate the
+ * number of repeats, e.g. a value of 1 plays the audio twice.
+ * If the stream does not exist, it will have no effect.
*
* @param streamID a streamID returned by the play() function
+ * @param loop loop mode (0 = no loop, -1 = loop forever)
*/
public native final void setLoop(int streamID, int loop);
+ /**
+ * Change playback rate.
+ *
+ * The playback rate allows the application to vary the playback
+ * rate (pitch) of the sound. A value of 1.0 means playback at
+ * the original frequency. A value of 2.0 means playback twice
+ * as fast, and a value of 0.5 means playback at half speed.
+ * If the stream does not exist, it will have no effect.
+ *
+ * @param streamID a streamID returned by the play() function
+ * @param rate playback rate (1.0 = normal playback, range 0.5 to 2.0)
+ */
public native final void setRate(int streamID, float rate);
+ /**
+ * Release the SoundPool resources.
+ *
+ * Release all memory and native resources used by the SoundPool
+ * object. The SoundPool can no longer be used and the reference
+ * should be set to null.
+ */
public native final void release();
private native final void native_setup(Object mediaplayer_this,
diff --git a/test-runner/android/test/InstrumentationCoreTestRunner.java b/test-runner/android/test/InstrumentationCoreTestRunner.java
index 6b1a4e4..3f77a60 100644
--- a/test-runner/android/test/InstrumentationCoreTestRunner.java
+++ b/test-runner/android/test/InstrumentationCoreTestRunner.java
@@ -17,27 +17,158 @@
package android.test;
import java.io.File;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.List;
+import com.android.internal.util.Predicate;
+import com.android.internal.util.Predicates;
+
+import dalvik.annotation.BrokenTest;
+import dalvik.annotation.SideEffect;
+
+import junit.framework.AssertionFailedError;
+import junit.framework.Test;
+import junit.framework.TestCase;
+import junit.framework.TestListener;
import android.os.Bundle;
+import android.test.suitebuilder.TestMethod;
+import android.test.suitebuilder.annotation.HasAnnotation;
+import android.util.Log;
/**
* This test runner extends the default InstrumentationTestRunner. It overrides
* the {@code onCreate(Bundle)} method and sets the system properties necessary
* for many core tests to run. This is needed because there are some core tests
- * that need writing access to the filesystem.
+ * that need writing access to the file system. We also need to set the harness
+ * Thread's context ClassLoader. Otherwise some classes and resources will not
+ * be found. Finally, we add a means to free memory allocated by a TestCase
+ * after its execution.
*
* @hide
*/
public class InstrumentationCoreTestRunner extends InstrumentationTestRunner {
+ private static final String TAG = "InstrumentationCoreTestRunner";
+ private boolean singleTest = false;
+
@Override
public void onCreate(Bundle arguments) {
- super.onCreate(arguments);
-
+ // We might want to move this to /sdcard, if is is mounted/writable.
File cacheDir = getTargetContext().getCacheDir();
System.setProperty("user.language", "en");
System.setProperty("user.region", "US");
+
+ System.setProperty("java.home", cacheDir.getAbsolutePath());
+ System.setProperty("user.home", cacheDir.getAbsolutePath());
System.setProperty("java.io.tmpdir", cacheDir.getAbsolutePath());
+ System.setProperty("javax.net.ssl.trustStore",
+ "/etc/security/cacerts.bks");
+
+ if (arguments != null) {
+ String classArg = arguments.getString(ARGUMENT_TEST_CLASS);
+ singleTest = classArg != null && classArg.contains("#");
+ }
+
+ super.onCreate(arguments);
+ }
+
+ protected AndroidTestRunner getAndroidTestRunner() {
+ AndroidTestRunner runner = super.getAndroidTestRunner();
+
+ runner.addTestListener(new TestListener() {
+ private Class<?> lastClass;
+
+ public void startTest(Test test) {
+ if (test.getClass() != lastClass) {
+ printMemory(test.getClass());
+ }
+
+ Thread.currentThread().setContextClassLoader(
+ test.getClass().getClassLoader());
+ }
+
+ public void endTest(Test test) {
+ if (test instanceof TestCase) {
+ if (lastClass == null) {
+ lastClass = test.getClass();
+ } else {
+ if (test.getClass() != lastClass) {
+ cleanup(lastClass);
+ lastClass = test.getClass();
+ }
+ }
+ }
+ }
+
+ public void addError(Test test, Throwable t) {
+ }
+
+ public void addFailure(Test test, AssertionFailedError t) {
+ }
+
+ /**
+ * Dumps some memory info.
+ */
+ private void printMemory(Class<? extends Test> testClass) {
+ Runtime runtime = Runtime.getRuntime();
+
+ long total = runtime.totalMemory();
+ long free = runtime.freeMemory();
+ long used = total - free;
+
+ Log.d(TAG, "Total memory : " + total);
+ Log.d(TAG, "Used memory : " + used);
+ Log.d(TAG, "Free memory : " + free);
+ Log.d(TAG, "Now executing : " + testClass.getName());
+ }
+
+ /**
+ * Nulls all static reference fields in the given test class. This
+ * method helps us with those test classes that don't have an
+ * explicit tearDown() method. Normally the garbage collector should
+ * take care of everything, but since JUnit keeps references to all
+ * test cases, a little help might be a good idea.
+ */
+ private void cleanup(Class<?> clazz) {
+ if (clazz != TestCase.class) {
+ Field[] fields = clazz.getDeclaredFields();
+ for (int i = 0; i < fields.length; i++) {
+ Field f = fields[i];
+ if (!f.getType().isPrimitive() &&
+ Modifier.isStatic(f.getModifiers())) {
+ try {
+ f.setAccessible(true);
+ f.set(null, null);
+ } catch (Exception ignored) {
+ // Nothing we can do about it.
+ }
+ }
+ }
+
+ // don't cleanup the superclass for now
+ //cleanup(clazz.getSuperclass());
+ }
+ }
+
+ });
+
+ return runner;
+ }
+
+ @Override
+ List<Predicate<TestMethod>> getBuilderRequirements() {
+ List<Predicate<TestMethod>> builderRequirements =
+ super.getBuilderRequirements();
+ Predicate<TestMethod> brokenTestPredicate =
+ Predicates.not(new HasAnnotation(BrokenTest.class));
+ builderRequirements.add(brokenTestPredicate);
+ if (!singleTest) {
+ Predicate<TestMethod> sideEffectPredicate =
+ Predicates.not(new HasAnnotation(SideEffect.class));
+ builderRequirements.add(sideEffectPredicate);
+ }
+ return builderRequirements;
}
}
diff --git a/test-runner/android/test/InstrumentationTestRunner.java b/test-runner/android/test/InstrumentationTestRunner.java
index 044f555..d5e64596 100644
--- a/test-runner/android/test/InstrumentationTestRunner.java
+++ b/test-runner/android/test/InstrumentationTestRunner.java
@@ -43,6 +43,8 @@
import java.io.PrintStream;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
/**
@@ -315,6 +317,8 @@
} else {
parseTestClasses(testClassesArg, testSuiteBuilder);
}
+
+ testSuiteBuilder.addRequirements(getBuilderRequirements());
mTestRunner = getAndroidTestRunner();
mTestRunner.setContext(getTargetContext());
@@ -331,6 +335,10 @@
start();
}
+ List<Predicate<TestMethod>> getBuilderRequirements() {
+ return new ArrayList<Predicate<TestMethod>>();
+ }
+
/**
* Parses and loads the specified set of test classes
* @param testClassArg - comma-separated list of test classes and methods