Merge change 279 into donut
* changes:
Add sketch gesture demo application.
diff --git a/location/java/android/location/ILocationProvider.aidl b/location/java/android/location/ILocationProvider.aidl
index 08b9246..d43fd6e 100644
--- a/location/java/android/location/ILocationProvider.aidl
+++ b/location/java/android/location/ILocationProvider.aidl
@@ -46,9 +46,9 @@
void setMinTime(long minTime);
void updateNetworkState(int state);
boolean sendExtraCommand(String command, inout Bundle extras);
+ void addListener(int uid);
+ void removeListener(int uid);
- /* the following are only used for NetworkLocationProvider */
+ /* the following is used only for NetworkLocationProvider */
void updateCellLockStatus(boolean acquired);
- void addListener(in String[] applications);
- void removeListener(in String[] applications);
}
diff --git a/location/java/android/location/LocationProviderImpl.java b/location/java/android/location/LocationProviderImpl.java
index 2a9199e..fa0cd2d 100644
--- a/location/java/android/location/LocationProviderImpl.java
+++ b/location/java/android/location/LocationProviderImpl.java
@@ -249,4 +249,20 @@
public boolean sendExtraCommand(String command, Bundle extras) {
return false;
}
+
+ /**
+ * Informs the location provider when a new client is listening for location information
+ *
+ * @param uid the uid of the client proces
+ */
+ public void addListener(int uid) {
+ }
+
+ /**
+ * Informs the location provider when a client is no longerlistening for location information
+ *
+ * @param uid the uid of the client proces
+ */
+ public void removeListener(int uid) {
+ }
}
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index d56594e..f133b4f 100644
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -33,10 +33,13 @@
import android.os.Bundle;
import android.os.IBinder;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.SystemClock;
import android.util.Config;
import android.util.Log;
+import android.util.SparseIntArray;
+import com.android.internal.app.IBatteryStats;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.TelephonyIntents;
@@ -192,7 +195,10 @@
private boolean mSetSuplServer;
private String mSuplApn;
private int mSuplDataConnectionState;
- private ConnectivityManager mConnMgr;
+ private final ConnectivityManager mConnMgr;
+
+ private final IBatteryStats mBatteryStats;
+ private final SparseIntArray mClientUids = new SparseIntArray();
// how often to request NTP time, in milliseconds
// current setting 4 hours
@@ -241,6 +247,9 @@
mConnMgr = (ConnectivityManager)context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ // Battery statistics service to be notified when GPS turns on or off
+ mBatteryStats = IBatteryStats.Stub.asInterface(ServiceManager.getService("batteryinfo"));
+
mProperties = new Properties();
try {
File file = new File(PROPERTIES_FILE);
@@ -568,6 +577,30 @@
}
@Override
+ public void addListener(int uid) {
+ mClientUids.put(uid, 0);
+ if (mNavigating) {
+ try {
+ mBatteryStats.noteStartGps(uid);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in addListener");
+ }
+ }
+ }
+
+ @Override
+ public void removeListener(int uid) {
+ mClientUids.delete(uid);
+ if (mNavigating) {
+ try {
+ mBatteryStats.noteStopGps(uid);
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in removeListener");
+ }
+ }
+ }
+
+ @Override
public boolean sendExtraCommand(String command, Bundle extras) {
if ("delete_aiding_data".equals(command)) {
@@ -746,6 +779,20 @@
}
}
+ try {
+ // update battery stats
+ for (int i=mClientUids.size() - 1; i >= 0; i--) {
+ int uid = mClientUids.keyAt(i);
+ if (mNavigating) {
+ mBatteryStats.noteStartGps(uid);
+ } else {
+ mBatteryStats.noteStopGps(uid);
+ }
+ }
+ } catch (RemoteException e) {
+ Log.w(TAG, "RemoteException in reportStatus");
+ }
+
// send an intent to notify that the GPS has been enabled or disabled.
Intent intent = new Intent(GPS_ENABLED_CHANGE_ACTION);
intent.putExtra(EXTRA_ENABLED, mNavigating);
diff --git a/location/java/com/android/internal/location/LocationProviderProxy.java b/location/java/com/android/internal/location/LocationProviderProxy.java
index 5b5b27a..84462b2 100644
--- a/location/java/com/android/internal/location/LocationProviderProxy.java
+++ b/location/java/com/android/internal/location/LocationProviderProxy.java
@@ -230,17 +230,17 @@
}
}
- public void addListener(String[] applications) {
+ public void addListener(int uid) {
try {
- mProvider.addListener(applications);
+ mProvider.addListener(uid);
} catch (RemoteException e) {
Log.e(TAG, "addListener failed", e);
}
}
- public void removeListener(String[] applications) {
+ public void removeListener(int uid) {
try {
- mProvider.removeListener(applications);
+ mProvider.removeListener(uid);
} catch (RemoteException e) {
Log.e(TAG, "removeListener failed", e);
}
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/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 75b59ee..c91e21c 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -73,7 +73,6 @@
import android.util.PrintWriterPrinter;
import android.util.SparseIntArray;
-import com.android.internal.app.IBatteryStats;
import com.android.internal.location.GpsLocationProvider;
import com.android.internal.location.LocationProviderProxy;
import com.android.internal.location.MockProvider;
@@ -152,34 +151,15 @@
private boolean mWifiWakeLockAcquired = false;
private boolean mCellWakeLockAcquired = false;
- private final IBatteryStats mBatteryStats;
-
/**
- * Mapping from listener IBinder/PendingIntent to local Listener wrappers.
+ * List of all receivers.
*/
- private final ArrayList<Receiver> mListeners = new ArrayList<Receiver>();
+ private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
/**
- * Used for reporting which UIDs are causing the GPS to run.
+ * Object used internally for synchronization
*/
- private final SparseIntArray mReportedGpsUids = new SparseIntArray();
- private int mReportedGpsSeq = 0;
-
- /**
- * Mapping from listener IBinder/PendingIntent to a map from provider name to UpdateRecord.
- * This also serves as the lock for our state.
- */
- private final HashMap<Receiver,HashMap<String,UpdateRecord>> mLocationListeners =
- new HashMap<Receiver,HashMap<String,UpdateRecord>>();
-
- /**
- * Mapping from listener IBinder/PendingIntent to a map from provider name to last broadcast
- * location.
- */
- private final HashMap<Receiver,HashMap<String,Location>> mLastFixBroadcast =
- new HashMap<Receiver,HashMap<String,Location>>();
- private final HashMap<Receiver,HashMap<String,Long>> mLastStatusBroadcast =
- new HashMap<Receiver,HashMap<String,Long>>();
+ private final Object mLock = new Object();
/**
* Mapping from provider name to all its UpdateRecords
@@ -219,20 +199,18 @@
private final class Receiver implements IBinder.DeathRecipient {
final ILocationListener mListener;
final PendingIntent mPendingIntent;
- final int mUid;
final Object mKey;
+ final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
- Receiver(ILocationListener listener, int uid) {
+ Receiver(ILocationListener listener) {
mListener = listener;
mPendingIntent = null;
- mUid = uid;
mKey = listener.asBinder();
}
- Receiver(PendingIntent intent, int uid) {
+ Receiver(PendingIntent intent) {
mPendingIntent = intent;
mListener = null;
- mUid = uid;
mKey = intent;
}
@@ -256,11 +234,11 @@
if (mListener != null) {
return "Receiver{"
+ Integer.toHexString(System.identityHashCode(this))
- + " uid " + mUid + " Listener " + mKey + "}";
+ + " Listener " + mKey + "}";
} else {
return "Receiver{"
+ Integer.toHexString(System.identityHashCode(this))
- + " uid " + mUid + " Intent " + mKey + "}";
+ + " Intent " + mKey + "}";
}
}
@@ -329,7 +307,7 @@
if (LOCAL_LOGV) {
Log.v(TAG, "Location listener died");
}
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
removeUpdatesLocked(this);
}
}
@@ -337,7 +315,7 @@
private final class SettingsObserver implements Observer {
public void update(Observable o, Object arg) {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
updateProvidersLocked();
}
}
@@ -444,7 +422,7 @@
* properties
*/
private void loadProviders() {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
if (sProvidersLoaded) {
return;
}
@@ -558,9 +536,6 @@
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
- // Battery statistics service to be notified when GPS turns on or off
- mBatteryStats = BatteryStatsService.getService();
-
// Load providers
loadProviders();
@@ -607,10 +582,9 @@
"Installing location providers outside of the system is not supported");
}
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
mNetworkLocationProvider =
new LocationProviderProxy(LocationManager.NETWORK_PROVIDER, this, provider);
- mNetworkLocationProvider.addListener(getPackageNames());
LocationProviderImpl.addProvider(mNetworkLocationProvider);
updateProvidersLocked();
@@ -626,7 +600,7 @@
"Installing location collectors outside of the system is not supported");
}
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
mCollector = collector;
if (mGpsLocationProvider != null) {
mGpsLocationProvider.setLocationCollector(mCollector);
@@ -699,15 +673,9 @@
return true;
}
- private String[] getPackageNames() {
- // Since a single UID may correspond to multiple packages, this can only be used as an
- // approximation for tracking
- return mContext.getPackageManager().getPackagesForUid(Binder.getCallingUid());
- }
-
public List<String> getAllProviders() {
try {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
return _getAllProvidersLocked();
}
} catch (SecurityException se) {
@@ -733,7 +701,7 @@
public List<String> getProviders(boolean enabledOnly) {
try {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
return _getProvidersLocked(enabledOnly);
}
} catch (SecurityException se) {
@@ -839,7 +807,6 @@
p.enableLocationTracking(false);
if (p == mGpsLocationProvider) {
mGpsNavigating = false;
- reportStopGpsLocked();
}
p.disable();
updateWakelockStatusLocked(mScreenOn);
@@ -863,19 +830,19 @@
final long mMinTime;
final float mMinDistance;
final int mUid;
- final String[] mPackages;
+ Location mLastFixBroadcast;
+ long mLastStatusBroadcast;
/**
* Note: must be constructed with lock held.
*/
UpdateRecord(String provider, long minTime, float minDistance,
- Receiver receiver, int uid, String[] packages) {
+ Receiver receiver, int uid) {
mProvider = provider;
mReceiver = receiver;
mMinTime = minTime;
mMinDistance = minDistance;
mUid = uid;
- mPackages = packages;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null) {
@@ -907,33 +874,77 @@
pw.println(prefix + this);
pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
- StringBuilder sb = new StringBuilder();
- if (mPackages != null) {
- for (int i=0; i<mPackages.length; i++) {
- if (i > 0) sb.append(", ");
- sb.append(mPackages[i]);
- }
- }
- pw.println(prefix + "mUid=" + mUid + " mPackages=" + sb);
+ pw.println(prefix + "mUid=" + mUid);
+ pw.println(prefix + "mLastFixBroadcast:");
+ mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
+ pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
}
/**
* Calls dispose().
*/
@Override protected void finalize() {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
disposeLocked();
}
}
}
+ private Receiver getReceiver(ILocationListener listener) {
+ IBinder binder = listener.asBinder();
+ Receiver receiver = mReceivers.get(binder);
+ if (receiver == null) {
+ receiver = new Receiver(listener);
+ mReceivers.put(binder, receiver);
+
+ try {
+ if (receiver.isListener()) {
+ receiver.getListener().asBinder().linkToDeath(receiver, 0);
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "linkToDeath failed:", e);
+ return null;
+ }
+ }
+ return receiver;
+ }
+
+ private Receiver getReceiver(PendingIntent intent) {
+ Receiver receiver = mReceivers.get(intent);
+ if (receiver == null) {
+ receiver = new Receiver(intent);
+ mReceivers.put(intent, receiver);
+ }
+ return receiver;
+ }
+
+ private boolean providerHasListener(String provider, int uid, Receiver excludedReceiver) {
+ ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
+ if (records != null) {
+ for (int i = records.size() - 1; i >= 0; i--) {
+ UpdateRecord record = records.get(i);
+ if (record.mUid == uid && record.mReceiver != excludedReceiver) {
+ return true;
+ }
+ }
+ }
+ if (LocationManager.GPS_PROVIDER.equals(provider) ||
+ LocationManager.NETWORK_PROVIDER.equals(provider)) {
+ for (ProximityAlert alert : mProximityAlerts.values()) {
+ if (alert.mUid == uid) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
public void requestLocationUpdates(String provider,
long minTime, float minDistance, ILocationListener listener) {
try {
- synchronized (mLocationListeners) {
- requestLocationUpdatesLocked(provider, minTime, minDistance,
- new Receiver(listener, Binder.getCallingUid()));
+ synchronized (mLock) {
+ requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(listener));
}
} catch (SecurityException se) {
throw se;
@@ -945,9 +956,8 @@
public void requestLocationUpdatesPI(String provider,
long minTime, float minDistance, PendingIntent intent) {
try {
- synchronized (mLocationListeners) {
- requestLocationUpdatesLocked(provider, minTime, minDistance,
- new Receiver(intent, Binder.getCallingUid()));
+ synchronized (mLock) {
+ requestLocationUpdatesLocked(provider, minTime, minDistance, getReceiver(intent));
}
} catch (SecurityException se) {
throw se;
@@ -969,47 +979,27 @@
checkPermissionsSafe(provider);
- String[] packages = getPackageNames();
-
// so wakelock calls will succeed
final int callingUid = Binder.getCallingUid();
+ boolean newUid = !providerHasListener(provider, callingUid, null);
long identity = Binder.clearCallingIdentity();
try {
- UpdateRecord r = new UpdateRecord(provider, minTime, minDistance,
- receiver, callingUid, packages);
- if (!mListeners.contains(receiver)) {
- try {
- if (receiver.isListener()) {
- receiver.getListener().asBinder().linkToDeath(receiver, 0);
- }
- mListeners.add(receiver);
- } catch (RemoteException e) {
- return;
- }
- }
-
- HashMap<String,UpdateRecord> records = mLocationListeners.get(receiver);
- if (records == null) {
- records = new HashMap<String,UpdateRecord>();
- mLocationListeners.put(receiver, records);
- }
- UpdateRecord oldRecord = records.put(provider, r);
+ UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, receiver, callingUid);
+ UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
if (oldRecord != null) {
oldRecord.disposeLocked();
}
+ if (newUid) {
+ impl.addListener(callingUid);
+ }
+
boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
if (isProviderEnabled) {
long minTimeForProvider = getMinTimeLocked(provider);
impl.setMinTime(minTimeForProvider);
impl.enableLocationTracking(true);
updateWakelockStatusLocked(mScreenOn);
-
- if (provider.equals(LocationManager.GPS_PROVIDER)) {
- if (mGpsNavigating) {
- updateReportedGpsLocked();
- }
- }
} else {
try {
// Notify the listener that updates are currently disabled
@@ -1028,8 +1018,8 @@
public void removeUpdates(ILocationListener listener) {
try {
- synchronized (mLocationListeners) {
- removeUpdatesLocked(new Receiver(listener, Binder.getCallingUid()));
+ synchronized (mLock) {
+ removeUpdatesLocked(getReceiver(listener));
}
} catch (SecurityException se) {
throw se;
@@ -1040,8 +1030,8 @@
public void removeUpdatesPI(PendingIntent intent) {
try {
- synchronized (mLocationListeners) {
- removeUpdatesLocked(new Receiver(intent, Binder.getCallingUid()));
+ synchronized (mLock) {
+ removeUpdatesLocked(getReceiver(intent));
}
} catch (SecurityException se) {
throw se;
@@ -1059,23 +1049,21 @@
final int callingUid = Binder.getCallingUid();
long identity = Binder.clearCallingIdentity();
try {
- int idx = mListeners.indexOf(receiver);
- if (idx >= 0) {
- Receiver myReceiver = mListeners.remove(idx);
- if (myReceiver.isListener()) {
- myReceiver.getListener().asBinder().unlinkToDeath(myReceiver, 0);
- }
+ if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
+ receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
}
// Record which providers were associated with this listener
HashSet<String> providers = new HashSet<String>();
- HashMap<String,UpdateRecord> oldRecords = mLocationListeners.get(receiver);
+ HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
if (oldRecords != null) {
// Call dispose() on the obsolete update records.
for (UpdateRecord record : oldRecords.values()) {
- if (record.mProvider.equals(LocationManager.NETWORK_PROVIDER)) {
- if (mNetworkLocationProvider != null) {
- mNetworkLocationProvider.removeListener(record.mPackages);
+ if (!providerHasListener(record.mProvider, callingUid, receiver)) {
+ LocationProviderImpl impl =
+ LocationProviderImpl.getProvider(record.mProvider);
+ if (impl != null) {
+ impl.removeListener(callingUid);
}
}
record.disposeLocked();
@@ -1083,10 +1071,6 @@
// Accumulate providers
providers.addAll(oldRecords.keySet());
}
-
- mLocationListeners.remove(receiver);
- mLastFixBroadcast.remove(receiver);
- mLastStatusBroadcast.remove(receiver);
// See if the providers associated with this listener have any
// other listeners; if one does, inform it of the new smallest minTime
@@ -1110,10 +1094,6 @@
} else {
p.enableLocationTracking(false);
}
-
- if (p == mGpsLocationProvider && mGpsNavigating) {
- updateReportedGpsLocked();
- }
}
}
@@ -1142,7 +1122,7 @@
}
public void removeGpsStatusListener(IGpsStatusListener listener) {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
mGpsLocationProvider.removeGpsStatusListener(listener);
}
}
@@ -1156,7 +1136,7 @@
throw new SecurityException("Requires ACCESS_LOCATION_EXTRA_COMMANDS permission");
}
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
LocationProviderImpl impl = LocationProviderImpl.getProvider(provider);
if (provider == null) {
return false;
@@ -1337,7 +1317,7 @@
public void addProximityAlert(double latitude, double longitude,
float radius, long expiration, PendingIntent intent) {
try {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
}
} catch (SecurityException se) {
@@ -1370,7 +1350,7 @@
mProximityAlerts.put(intent, alert);
if (mProximityListener == null) {
- mProximityListener = new Receiver(new ProximityListener(), -1);
+ mProximityListener = new Receiver(new ProximityListener());
LocationProvider provider = LocationProviderImpl.getProvider(
LocationManager.GPS_PROVIDER);
@@ -1383,14 +1363,12 @@
if (provider != null) {
requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f, mProximityListener);
}
- } else if (mGpsNavigating) {
- updateReportedGpsLocked();
}
}
public void removeProximityAlert(PendingIntent intent) {
try {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
removeProximityAlertLocked(intent);
}
} catch (SecurityException se) {
@@ -1409,8 +1387,6 @@
if (mProximityAlerts.size() == 0) {
removeUpdatesLocked(mProximityListener);
mProximityListener = null;
- } else if (mGpsNavigating) {
- updateReportedGpsLocked();
}
}
@@ -1421,7 +1397,7 @@
*/
public Bundle getProviderInfo(String provider) {
try {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
return _getProviderInfoLocked(provider);
}
} catch (SecurityException se) {
@@ -1456,7 +1432,7 @@
public boolean isProviderEnabled(String provider) {
try {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
return _isProviderEnabledLocked(provider);
}
} catch (SecurityException se) {
@@ -1485,7 +1461,7 @@
public Location getLastKnownLocation(String provider) {
try {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
return _getLastKnownLocationLocked(provider);
}
} catch (SecurityException se) {
@@ -1585,16 +1561,11 @@
UpdateRecord r = records.get(i);
Receiver receiver = r.mReceiver;
- HashMap<String,Location> map = mLastFixBroadcast.get(receiver);
- if (map == null) {
- map = new HashMap<String,Location>();
- mLastFixBroadcast.put(receiver, map);
- }
- Location lastLoc = map.get(provider);
+ Location lastLoc = r.mLastFixBroadcast;
if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
if (lastLoc == null) {
lastLoc = new Location(location);
- map.put(provider, lastLoc);
+ r.mLastFixBroadcast = lastLoc;
} else {
lastLoc.set(location);
}
@@ -1607,19 +1578,11 @@
}
}
- // Broadcast status message
- HashMap<String,Long> statusMap = mLastStatusBroadcast.get(receiver);
- if (statusMap == null) {
- statusMap = new HashMap<String,Long>();
- mLastStatusBroadcast.put(receiver, statusMap);
- }
- long prevStatusUpdateTime =
- (statusMap.get(provider) != null) ? statusMap.get(provider) : 0;
-
+ long prevStatusUpdateTime = r.mLastStatusBroadcast;
if ((newStatusUpdateTime > prevStatusUpdateTime) &&
(prevStatusUpdateTime != 0 || status != LocationProvider.AVAILABLE)) {
- statusMap.put(provider, newStatusUpdateTime);
+ r.mLastStatusBroadcast = newStatusUpdateTime;
if (!receiver.callStatusChangedLocked(provider, status, extras)) {
Log.w(TAG, "RemoteException calling onStatusChanged on " + receiver);
if (deadReceivers == null) {
@@ -1647,7 +1610,7 @@
if (msg.what == MESSAGE_LOCATION_CHANGED) {
// log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
Location location = (Location) msg.obj;
String provider = location.getProvider();
if (!isAllowedBySettingsLocked(provider)) {
@@ -1685,14 +1648,14 @@
} else if (msg.what == MESSAGE_ACQUIRE_WAKE_LOCK) {
log("LocationWorkerHandler: Acquire");
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
acquireWakeLockLocked();
}
} else if (msg.what == MESSAGE_RELEASE_WAKE_LOCK) {
log("LocationWorkerHandler: Release");
// Update wakelock status so the next alarm is set before releasing wakelock
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
updateWakelockStatusLocked(mScreenOn);
releaseWakeLockLocked();
}
@@ -1709,7 +1672,7 @@
String action = intent.getAction();
if (action.equals(ALARM_INTENT)) {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
log("PowerStateBroadcastReceiver: Alarm received");
mLocationHandler.removeMessages(MESSAGE_ACQUIRE_WAKE_LOCK);
// Have to do this immediately, rather than posting a
@@ -1721,18 +1684,18 @@
} else if (action.equals(Intent.ACTION_SCREEN_OFF)) {
log("PowerStateBroadcastReceiver: Screen off");
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
updateWakelockStatusLocked(false);
}
} else if (action.equals(Intent.ACTION_SCREEN_ON)) {
log("PowerStateBroadcastReceiver: Screen on");
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
updateWakelockStatusLocked(true);
}
} else if (action.equals(Intent.ACTION_PACKAGE_REMOVED)
|| action.equals(Intent.ACTION_PACKAGE_RESTARTED)) {
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
int uid = intent.getIntExtra(Intent.EXTRA_UID, -1);
if (uid >= 0) {
ArrayList<Receiver> removedRecs = null;
@@ -1790,7 +1753,7 @@
}
// Notify location providers of current network state
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
List<LocationProviderImpl> providers = LocationProviderImpl.getProviders();
for (LocationProviderImpl provider : providers) {
if (provider.requiresNetwork()) {
@@ -1803,12 +1766,10 @@
final boolean enabled = intent.getBooleanExtra(GpsLocationProvider.EXTRA_ENABLED,
false);
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
if (enabled) {
- updateReportedGpsLocked();
mGpsNavigating = true;
} else {
- reportStopGpsLocked();
mGpsNavigating = false;
// When GPS is disabled, we are OK to release wake-lock
mWakeLockGpsReceived = true;
@@ -1941,85 +1902,6 @@
}
}
- private boolean reportGpsUidLocked(int curSeq, int nextSeq, int uid) {
- int seq = mReportedGpsUids.get(uid, -1);
- if (seq == curSeq) {
- // Already reported; propagate to next sequence.
- mReportedGpsUids.put(uid, nextSeq);
- return true;
- } else if (seq != nextSeq) {
- try {
- // New UID; report it.
- mBatteryStats.noteStartGps(uid);
- mReportedGpsUids.put(uid, nextSeq);
- return true;
- } catch (RemoteException e) {
- }
- }
- return false;
- }
-
- private void updateReportedGpsLocked() {
- if (mGpsLocationProvider == null) {
- return;
- }
-
- final String name = mGpsLocationProvider.getName();
- final int curSeq = mReportedGpsSeq;
- final int nextSeq = (curSeq+1) >= 0 ? (curSeq+1) : 0;
- mReportedGpsSeq = nextSeq;
-
- ArrayList<UpdateRecord> urs = mRecordsByProvider.get(name);
- int num = 0;
- final int N = urs.size();
- for (int i=0; i<N; i++) {
- UpdateRecord ur = urs.get(i);
- if (ur.mReceiver == mProximityListener) {
- // We don't want the system to take the blame for this one.
- continue;
- }
- if (reportGpsUidLocked(curSeq, nextSeq, ur.mUid)) {
- num++;
- }
- }
-
- for (ProximityAlert pe : mProximityAlerts.values()) {
- if (reportGpsUidLocked(curSeq, nextSeq, pe.mUid)) {
- num++;
- }
- }
-
- if (num != mReportedGpsUids.size()) {
- // The number of uids is processed is different than the
- // array; report any that are no longer active.
- for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
- if (mReportedGpsUids.valueAt(i) != nextSeq) {
- try {
- mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
- } catch (RemoteException e) {
- }
- mReportedGpsUids.removeAt(i);
- }
- }
- }
- }
-
- private void reportStopGpsLocked() {
- int curSeq = mReportedGpsSeq;
- for (int i=mReportedGpsUids.size()-1; i>=0; i--) {
- if (mReportedGpsUids.valueAt(i) == curSeq) {
- try {
- mBatteryStats.noteStopGps(mReportedGpsUids.keyAt(i));
- } catch (RemoteException e) {
- }
- }
- }
- curSeq++;
- if (curSeq < 0) curSeq = 0;
- mReportedGpsSeq = curSeq;
- mReportedGpsUids.clear();
- }
-
private void startGpsLocked() {
boolean gpsActive = (mGpsLocationProvider != null)
&& mGpsLocationProvider.isLocationTracking();
@@ -2135,7 +2017,7 @@
boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
checkMockPermissionsSafe();
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
MockProvider provider = new MockProvider(name, this,
requiresNetwork, requiresSatellite,
requiresCell, hasMonetaryCost, supportsAltitude,
@@ -2151,7 +2033,7 @@
public void removeTestProvider(String provider) {
checkMockPermissionsSafe();
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
@@ -2164,7 +2046,7 @@
public void setTestProviderLocation(String provider, Location loc) {
checkMockPermissionsSafe();
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
@@ -2175,7 +2057,7 @@
public void clearTestProviderLocation(String provider) {
checkMockPermissionsSafe();
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
@@ -2186,7 +2068,7 @@
public void setTestProviderEnabled(String provider, boolean enabled) {
checkMockPermissionsSafe();
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
@@ -2206,7 +2088,7 @@
public void clearTestProviderEnabled(String provider) {
checkMockPermissionsSafe();
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
@@ -2219,7 +2101,7 @@
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
checkMockPermissionsSafe();
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
@@ -2230,7 +2112,7 @@
public void clearTestProviderStatus(String provider) {
checkMockPermissionsSafe();
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
MockProvider mockProvider = mMockProviders.get(provider);
if (mockProvider == null) {
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
@@ -2254,7 +2136,7 @@
return;
}
- synchronized (mLocationListeners) {
+ synchronized (mLock) {
pw.println("Current Location Manager state:");
pw.println(" sProvidersLoaded=" + sProvidersLoaded);
pw.println(" mGpsLocationProvider=" + mGpsLocationProvider);
@@ -2269,37 +2151,18 @@
pw.println(" mWifiWakeLockAcquired=" + mWifiWakeLockAcquired
+ " mCellWakeLockAcquired=" + mCellWakeLockAcquired);
pw.println(" Listeners:");
- int N = mListeners.size();
+ int N = mReceivers.size();
for (int i=0; i<N; i++) {
- pw.println(" " + mListeners.get(i));
+ pw.println(" " + mReceivers.get(i));
}
pw.println(" Location Listeners:");
- for (Map.Entry<Receiver, HashMap<String,UpdateRecord>> i
- : mLocationListeners.entrySet()) {
- pw.println(" " + i.getKey() + ":");
- for (Map.Entry<String,UpdateRecord> j : i.getValue().entrySet()) {
+ for (Receiver i : mReceivers.values()) {
+ pw.println(" " + i + ":");
+ for (Map.Entry<String,UpdateRecord> j : i.mUpdateRecords.entrySet()) {
pw.println(" " + j.getKey() + ":");
j.getValue().dump(pw, " ");
}
}
- pw.println(" Last Fix Broadcasts:");
- for (Map.Entry<Receiver, HashMap<String,Location>> i
- : mLastFixBroadcast.entrySet()) {
- pw.println(" " + i.getKey() + ":");
- for (Map.Entry<String,Location> j : i.getValue().entrySet()) {
- pw.println(" " + j.getKey() + ":");
- j.getValue().dump(new PrintWriterPrinter(pw), " ");
- }
- }
- pw.println(" Last Status Broadcasts:");
- for (Map.Entry<Receiver, HashMap<String,Long>> i
- : mLastStatusBroadcast.entrySet()) {
- pw.println(" " + i.getKey() + ":");
- for (Map.Entry<String,Long> j : i.getValue().entrySet()) {
- pw.println(" " + j.getKey() + " -> 0x"
- + Long.toHexString(j.getValue()));
- }
- }
pw.println(" Records by Provider:");
for (Map.Entry<String, ArrayList<UpdateRecord>> i
: mRecordsByProvider.entrySet()) {
@@ -2351,12 +2214,6 @@
i.getValue().dump(pw, " ");
}
}
- pw.println(" Reported GPS UIDs @ seq " + mReportedGpsSeq + ":");
- N = mReportedGpsUids.size();
- for (int i=0; i<N; i++) {
- pw.println(" UID " + mReportedGpsUids.keyAt(i)
- + " seq=" + mReportedGpsUids.valueAt(i));
- }
}
}
}
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..d5e6459 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