AI 144453: am: CL 144452 More Location Manager cleanup:
  Remove 1 Hz "heartbeat" polling of location providers from LocationManagerService.
  Now location providers report their location to LocationManagerService via
  LocationManager.setLocation() rather than waiting to be polled.
  This reduces GPS fix latency by up to one second.
  Remove LocationProvderImpl.getLocation().
  Since we are no longer polling, this method is no longer necessary.
  BUG=1729031
  Original author: lockwood

Automated import of CL 144453
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index d0f9877..a7fb04d 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -53,6 +53,9 @@
     boolean isProviderEnabled(String provider);
 
     Location getLastKnownLocation(String provider);
+    
+    /* used by location providers to tell the location manager when it has a new location */
+    void setLocation(in Location location);
 
     String getFromLocation(double latitude, double longitude, int maxResults,
         String language, String country, String variant, String appName, out List<Address> addrs);
diff --git a/location/java/android/location/LocationProviderImpl.java b/location/java/android/location/LocationProviderImpl.java
index 0962992..bb225e2 100644
--- a/location/java/android/location/LocationProviderImpl.java
+++ b/location/java/android/location/LocationProviderImpl.java
@@ -27,6 +27,7 @@
 import java.util.List;
 
 import android.os.Bundle;
+import android.os.RemoteException;
 import android.util.Config;
 import android.util.Log;
 
@@ -46,11 +47,13 @@
     private static HashMap<String, LocationProviderImpl> sProvidersByName
         = new HashMap<String, LocationProviderImpl>();
 
+    private final ILocationManager mLocationManager;
     private boolean mLocationTracking = false;
     private long mMinTime = 0;
 
-    protected LocationProviderImpl(String name) {
+    protected LocationProviderImpl(String name, ILocationManager locationManager) {
         super(name);
+        mLocationManager = locationManager;
     }
 
     public static void addProvider(LocationProviderImpl provider) {
@@ -114,16 +117,24 @@
         return null;
     }
 
+    public void reportLocationChanged(Location location) {
+        try {
+            mLocationManager.setLocation(location);
+        } catch (RemoteException e) {
+            Log.e(TAG, "RemoteException calling ILocationManager.onLocationChanged");
+        }
+    }
+    
     /**
      * Enables this provider.  When enabled, calls to {@link #getStatus()}
-     * and {@link #getLocation} must be handled.  Hardware may be started up
+     * must be handled.  Hardware may be started up
      * when the provider is enabled.
      */
     public abstract void enable();
 
     /**
      * Disables this provider.  When disabled, calls to {@link #getStatus()}
-     * and {@link #getLocation} need not be handled.  Hardware may be shut
+     * need not be handled.  Hardware may be shut
      * down while the provider is disabled.
      */
     public abstract void disable();
@@ -175,15 +186,6 @@
     }
 
     /**
-     * Sets a Location object with the information gathered
-     * during the most recent fix.
-     *
-     * @param l location object to set
-     * @return true if a location fix is available
-     */
-    public abstract boolean getLocation(Location l);
-
-    /**
      * Notifies the location provider that clients are listening for locations.
      * Called with enable set to true when the first client is added and
      * called with enable set to false when the last client is removed.
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index f595050..d09318a 100644
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -22,6 +22,7 @@
 import android.content.IntentFilter;
 import android.location.Criteria;
 import android.location.IGpsStatusListener;
+import android.location.ILocationManager;
 import android.location.Location;
 import android.location.LocationManager;
 import android.location.LocationProvider;
@@ -209,8 +210,8 @@
         return native_is_supported();
     }
 
-    public GpsLocationProvider(Context context) {
-        super(LocationManager.GPS_PROVIDER);
+    public GpsLocationProvider(Context context, ILocationManager locationManager) {
+        super(LocationManager.GPS_PROVIDER, locationManager);
         mContext = context;
 
         TelephonyBroadcastReceiver receiver = new TelephonyBroadcastReceiver();
@@ -355,7 +356,7 @@
 
     /**
      * Enables this provider.  When enabled, calls to getStatus()
-     * and getLocation() must be handled.  Hardware may be started up
+     * must be handled.  Hardware may be started up
      * when the provider is enabled.
      */
     @Override
@@ -385,7 +386,7 @@
 
     /**
      * Disables this provider.  When disabled, calls to getStatus()
-     * and getLocation() need not be handled.  Hardware may be shut
+     * need not be handled.  Hardware may be shut
      * down while the provider is disabled.
      */
     @Override
@@ -449,19 +450,6 @@
     }
 
     @Override
-    public boolean getLocation(Location l) {
-        synchronized (mLocation) {
-            // don't report locations without latitude and longitude
-            if ((mLocationFlags & LOCATION_HAS_LAT_LONG) == 0) {
-                return false;
-            }
-            l.set(mLocation);
-            l.setExtras(mLocationExtras);
-            return true;
-        }
-    }
-
-    @Override
     public void enableLocationTracking(boolean enable) {
         if (mLocationTracking == enable) {
             return;
@@ -685,6 +673,8 @@
                 mLocation.removeAccuracy();
             }
 
+            reportLocationChanged(mLocation);
+
             // Send to collector
             if ((flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG
                     && mCollector != null) {
diff --git a/location/java/com/android/internal/location/INetworkLocationProvider.java b/location/java/com/android/internal/location/INetworkLocationProvider.java
index 730cb48..f8947a6 100644
--- a/location/java/com/android/internal/location/INetworkLocationProvider.java
+++ b/location/java/com/android/internal/location/INetworkLocationProvider.java
@@ -20,12 +20,7 @@
 import android.location.Location;
 import android.net.wifi.ScanResult;
 
-import com.google.common.io.protocol.ProtoBuf;
-
-import java.io.IOException;
-import java.util.Collection;
 import java.util.List;
-import java.util.Locale;
 
 /**
  * Interface for network location provider
diff --git a/location/java/com/android/internal/location/MockProvider.java b/location/java/com/android/internal/location/MockProvider.java
index bd5cdd8..c8d5b4f 100644
--- a/location/java/com/android/internal/location/MockProvider.java
+++ b/location/java/com/android/internal/location/MockProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.internal.location;
 
+import android.location.ILocationManager;
 import android.location.Location;
 import android.location.LocationProviderImpl;
 import android.os.Bundle;
@@ -46,10 +47,11 @@
     private boolean mHasStatus;
     private boolean mEnabled;
 
-    public MockProvider(String name,  boolean requiresNetwork, boolean requiresSatellite,
+    public MockProvider(String name,  ILocationManager locationManager,
+        boolean requiresNetwork, boolean requiresSatellite,
         boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
         boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
-        super(name);
+        super(name, locationManager);
 
         mRequiresNetwork = requiresNetwork;
         mRequiresSatellite = requiresSatellite;
@@ -74,15 +76,6 @@
     }
 
     @Override
-    public boolean getLocation(Location l) {
-        if (mHasLocation) {
-            l.set(mLocation);
-            return true;
-        }
-        return false;
-    }
-
-    @Override
     public int getStatus(Bundle extras) {
         if (mHasStatus) {
             extras.clear();
@@ -94,6 +87,11 @@
     }
 
     @Override
+    public long getStatusUpdateTime() {
+        return mStatusUpdateTime;
+    }
+
+    @Override
     public boolean isEnabled() {
         return mEnabled;
     }
@@ -146,6 +144,7 @@
     public void setLocation(Location l) {
         mLocation.set(l);
         mHasLocation = true;
+        reportLocationChanged(mLocation);
     }
 
     public void clearLocation() {
@@ -164,29 +163,7 @@
 
     public void clearStatus() {
         mHasStatus = false;
-    }
-
-    public int overrideStatus(int status) {
-        if (mHasStatus) {
-            return mStatus;
-        } else {
-            return status;
-        }
-    }
-
-    public long overrideStatusUpdateTime(long statusUpdateTime) {
-        if (mHasStatus) {
-            return mStatusUpdateTime;
-        } else {
-            return statusUpdateTime;
-        }
-    }
-
-    public void overrideExtras(Bundle extras) {
-        if (mHasStatus) {
-            extras.clear();
-            extras.putAll(mExtras);
-        }
+        mStatusUpdateTime = 0;
     }
 
     public void dump(PrintWriter pw, String prefix) {
diff --git a/location/java/com/android/internal/location/TrackProvider.java b/location/java/com/android/internal/location/TrackProvider.java
index 545d7dc..1686260 100644
--- a/location/java/com/android/internal/location/TrackProvider.java
+++ b/location/java/com/android/internal/location/TrackProvider.java
@@ -16,6 +16,7 @@
 import java.util.StringTokenizer;
 
 import android.location.Criteria;
+import android.location.ILocationManager;
 import android.location.Location;
 import android.location.LocationProviderImpl;
 import android.os.Bundle;
@@ -50,6 +51,7 @@
     private static final long INTERVAL = 1000L;
 
     private boolean mEnabled = true;
+    private TrackProviderThread mThread;
 
     private double mLatitude;
     private double mLongitude;
@@ -86,6 +88,36 @@
 
     private Location mInitialLocation;
 
+    private class TrackProviderThread extends Thread {
+
+        private boolean mDone = false;
+
+        public TrackProviderThread() {
+            super("TrackProviderThread");
+        }
+
+        public void run() {            
+            // thread exits after disable() is called
+            synchronized (this) {
+                while (!mDone) {
+                    try {
+                        wait(INTERVAL);
+                    } catch (InterruptedException e) {
+                    }
+                    
+                    if (!mDone) {
+                        TrackProvider.this.update();
+                    }
+                }
+            }
+        }
+        
+        synchronized void setDone() {
+            mDone = true;
+            notify();
+        }
+    }
+
     private void close(Reader rdr) {
         try {
             if (rdr != null) {
@@ -392,13 +424,13 @@
         }
     }
 
-    public TrackProvider(String name) {
-        super(name);
+    public TrackProvider(String name, ILocationManager locationManager) {
+        super(name, locationManager);
         setTimes();
     }
 
-    public TrackProvider(String name, File file) {
-        this(name);
+    public TrackProvider(String name, ILocationManager locationManager, File file) {
+        this(name, locationManager);
 
         String filename = file.getName();
         if (filename.endsWith("kml")) {
@@ -429,12 +461,7 @@
     }
 
     private void update() {
-        // Don't update the position at all unless INTERVAL milliseconds
-        // have passed since the last request
         long time = System.currentTimeMillis() - mBaseTime;
-        if (time - mLastTime < INTERVAL) {
-            return;
-        }
 
         List<Waypoint> waypoints = mWaypoints;
         if (waypoints == null) {
@@ -594,12 +621,22 @@
         mTrackSpeed = trackSpeed;
     }
 
-    @Override public void enable() {
-        mEnabled = true;
+    @Override public synchronized void enable() {
+       mEnabled = true;
+        mThread = new TrackProviderThread();
+        mThread.start();
     }
 
-    @Override public void disable() {
+    @Override public synchronized void disable() {
         mEnabled = false;
+        if (mThread != null) {
+            mThread.setDone();
+            try {
+                mThread.join();
+            } catch (InterruptedException e) {
+            }
+            mThread = null;
+        }
     }
 
     @Override public boolean isEnabled() {
@@ -610,31 +647,6 @@
         return AVAILABLE;
     }
 
-    @Override public boolean getLocation(Location l) {
-        if (mEnabled) {
-            update();
-            l.setProvider(getName());
-            l.setTime(mTime + mBaseTime);
-            l.setLatitude(mLatitude);
-            l.setLongitude(mLongitude);
-            if (mSupportsAltitude && mHasAltitude) {
-                l.setAltitude(mAltitude);
-            }
-            if (mSupportsBearing && mHasBearing) {
-                l.setBearing(mBearing);
-            }
-            if (mSupportsSpeed && mHasSpeed) {
-                l.setSpeed(mSpeed);
-            }
-            if (mExtras != null) {
-                l.setExtras(mExtras);
-            }
-            return true;
-        } else {
-            return false;
-        }
-    }
-
     public Location getInitialLocation() {
         return mInitialLocation;
     }
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index f3187d7..952fdc5 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -136,7 +136,7 @@
     private LocationWorkerHandler mLocationHandler;
 
     // Handler messages
-    private static final int MESSAGE_HEARTBEAT = 1;
+    private static final int MESSAGE_LOCATION_CHANGED = 1;
     private static final int MESSAGE_ACQUIRE_WAKE_LOCK = 2;
     private static final int MESSAGE_RELEASE_WAKE_LOCK = 3;
     private static final int MESSAGE_INSTALL_NETWORK_LOCATION_PROVIDER = 4;
@@ -191,13 +191,6 @@
     private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
         new HashMap<String,ArrayList<UpdateRecord>>();
 
-    /**
-     * Mappings from provider name to object to use for current location. Locations
-     * contained in this list may not always be valid.
-     */
-    private final HashMap<String,Location> mLocationsByProvider =
-        new HashMap<String,Location>();
-
     // Proximity listeners
     private Receiver mProximityListener = null;
     private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
@@ -484,7 +477,7 @@
         // Attempt to load "real" providers first
         if (GpsLocationProvider.isSupported()) {
             // Create a gps location provider
-            mGpsLocationProvider = new GpsLocationProvider(mContext);
+            mGpsLocationProvider = new GpsLocationProvider(mContext, this);
             LocationProviderImpl.addProvider(mGpsLocationProvider);
         }
 
@@ -518,7 +511,7 @@
                             File propertiesFile = new File(subdirs[i], "properties");
 
                             if (propertiesFile.exists()) {
-                                provider = new TrackProvider(name);
+                                provider = new TrackProvider(name, this);
                                 ((TrackProvider)provider).readProperties(propertiesFile);
 
                                 File kmlFile = new File(subdirs[i], "kml");
@@ -885,14 +878,6 @@
             p.disable();
             updateWakelockStatusLocked(mScreenOn);
         }
-
-        if (enabled && listeners > 0) {
-            mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
-            Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
-            mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
-        } else {
-            mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
-        }
     }
 
     private long getMinTimeLocked(String provider) {
@@ -1059,11 +1044,6 @@
                         updateReportedGpsLocked();
                     }
                 }
-                
-                // Clear heartbeats if any before starting a new one
-                mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
-                Message m = Message.obtain(mLocationHandler, MESSAGE_HEARTBEAT, provider);
-                mLocationHandler.sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
             } else {
                 try {
                     // Notify the listener that updates are currently disabled
@@ -1162,7 +1142,6 @@
                     if (hasOtherListener) {
                         p.setMinTime(getMinTimeLocked(provider));
                     } else {
-                        mLocationHandler.removeMessages(MESSAGE_HEARTBEAT, provider);
                         p.enableLocationTracking(false);
                     }
                     
@@ -1522,6 +1501,12 @@
         }
     }
 
+    public void setLocation(Location location) {
+        mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
+        Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
+        mLocationHandler.sendMessageAtFrontOfQueue(m);
+    }
+
     private boolean _isProviderEnabledLocked(String provider) {
         checkPermissionsSafe(provider);
 
@@ -1592,7 +1577,8 @@
         return true;
     }
 
-    private void handleLocationChangedLocked(String provider) {
+    private void handleLocationChangedLocked(Location location) {
+        String provider = location.getProvider();
         ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
         if (records == null || records.size() == 0) {
             return;
@@ -1603,40 +1589,19 @@
             return;
         }
 
-        // Get location object
-        Location loc = mLocationsByProvider.get(provider);
-        if (loc == null) {
-            loc = new Location(provider);
-            mLocationsByProvider.put(provider, loc);
-        } else {
-            loc.reset();
-        }
-
-         boolean locationValid;
-
-       // Use the mock location if available
-        MockProvider mockProvider = mMockProviders.get(provider);
-        if (mockProvider != null && mockProvider.getLocation(loc)) {
-            locationValid = true;
-        } else {
-            locationValid = p.getLocation(loc);
-        }
-
         // Update last known location for provider
-        if (locationValid) {
-            Location location = mLastKnownLocation.get(provider);
-            if (location == null) {
-                mLastKnownLocation.put(provider, new Location(loc));
-            } else {
-                location.set(loc);
-            }
-            writeLastKnownLocationLocked(provider, loc);
+        Location lastLocation = mLastKnownLocation.get(provider);
+        if (lastLocation == null) {
+            mLastKnownLocation.put(provider, new Location(location));
+        } else {
+            lastLocation.set(location);
+        }
+        writeLastKnownLocationLocked(provider, location);
 
-            if (p instanceof INetworkLocationProvider) {
-                mWakeLockNetworkReceived = true;
-            } else if (p instanceof GpsLocationProvider) {
-                // Gps location received signal is in NetworkStateBroadcastReceiver
-            }
+        if (p instanceof INetworkLocationProvider) {
+            mWakeLockNetworkReceived = true;
+        } else if (p instanceof GpsLocationProvider) {
+            // Gps location received signal is in NetworkStateBroadcastReceiver
         }
 
         // Fetch latest status update time
@@ -1646,13 +1611,6 @@
         Bundle extras = new Bundle();
         int status = p.getStatus(extras);
 
-        // Override with mock values if mock provider is present
-        if (mockProvider != null) {
-            status = mockProvider.overrideStatus(status);
-            newStatusUpdateTime = mockProvider.overrideStatusUpdateTime(newStatusUpdateTime);
-            mockProvider.overrideExtras(extras);
-        }
-
         ArrayList<Receiver> deadReceivers = null;
         
         // Broadcast location or status to all listeners
@@ -1661,28 +1619,25 @@
             UpdateRecord r = records.get(i);
             Receiver receiver = r.mReceiver;
 
-            // Broadcast location only if it is valid
-            if (locationValid) {
-                HashMap<String,Location> map = mLastFixBroadcast.get(receiver);
-                if (map == null) {
-                    map = new HashMap<String,Location>();
-                    mLastFixBroadcast.put(receiver, map);
+            HashMap<String,Location> map = mLastFixBroadcast.get(receiver);
+            if (map == null) {
+                map = new HashMap<String,Location>();
+                mLastFixBroadcast.put(receiver, map);
+            }
+            Location lastLoc = map.get(provider);
+            if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
+                if (lastLoc == null) {
+                    lastLoc = new Location(location);
+                    map.put(provider, lastLoc);
+                } else {
+                    lastLoc.set(location);
                 }
-                Location lastLoc = map.get(provider);
-                if ((lastLoc == null) || shouldBroadcastSafe(loc, lastLoc, r)) {
-                    if (lastLoc == null) {
-                        lastLoc = new Location(loc);
-                        map.put(provider, lastLoc);
-                    } else {
-                        lastLoc.set(loc);
+                if (!receiver.callLocationChangedLocked(location)) {
+                    Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
+                    if (deadReceivers == null) {
+                        deadReceivers = new ArrayList<Receiver>();
                     }
-                    if (!receiver.callLocationChangedLocked(loc)) {
-                        Log.w(TAG, "RemoteException calling onLocationChanged on " + receiver);
-                        if (deadReceivers == null) {
-                            deadReceivers = new ArrayList<Receiver>();
-                        }
-                        deadReceivers.add(receiver);
-                    }
+                    deadReceivers.add(receiver);
                 }
             }
 
@@ -1723,25 +1678,19 @@
         @Override
         public void handleMessage(Message msg) {
             try {
-                if (msg.what == MESSAGE_HEARTBEAT) {
-                    // log("LocationWorkerHandler: Heartbeat!");
+                if (msg.what == MESSAGE_LOCATION_CHANGED) {
+                    // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
 
                     synchronized (mLocationListeners) {
-                        String provider = (String) msg.obj;
+                        Location location = (Location) msg.obj;
+                        String provider = location.getProvider();
                         if (!isAllowedBySettingsLocked(provider)) {
                             return;
                         }
 
                         // Process the location fix if the screen is on or we're holding a wakelock
                         if (mScreenOn || (mWakeLockAcquireTime != 0)) {
-                            handleLocationChangedLocked(provider);
-                        }
-
-                        // If it continues to have listeners
-                        ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
-                        if (records != null && records.size() > 0) {
-                            Message m = Message.obtain(this, MESSAGE_HEARTBEAT, provider);
-                            sendMessageAtTime(m, SystemClock.uptimeMillis() + 1000);
+                            handleLocationChangedLocked(location);
                         }
 
                         if ((mWakeLockAcquireTime != 0) &&
@@ -2386,7 +2335,8 @@
         checkMockPermissionsSafe();
 
         synchronized (mLocationListeners) {
-            MockProvider provider = new MockProvider(name, requiresNetwork, requiresSatellite,
+            MockProvider provider = new MockProvider(name, this,
+                requiresNetwork, requiresSatellite,
                 requiresCell, hasMonetaryCost, supportsAltitude,
                 supportsSpeed, supportsBearing, powerRequirement, accuracy);
             if (LocationProviderImpl.getProvider(name) != null) {
@@ -2563,12 +2513,6 @@
                     j.dump(pw, "        ");
                 }
             }
-            pw.println("  Locations by Provider:");
-            for (Map.Entry<String, Location> i
-                    : mLocationsByProvider.entrySet()) {
-                pw.println("    " + i.getKey() + ":");
-                i.getValue().dump(new PrintWriterPrinter(pw), "      ");
-            }
             pw.println("  Last Known Locations:");
             for (Map.Entry<String, Location> i
                     : mLastKnownLocation.entrySet()) {
diff --git a/test-runner/android/test/TestLocationProvider.java b/test-runner/android/test/TestLocationProvider.java
index 00c1ce8..69747d2 100644
--- a/test-runner/android/test/TestLocationProvider.java
+++ b/test-runner/android/test/TestLocationProvider.java
@@ -18,6 +18,7 @@
 
 
 import android.location.Criteria;
+import android.location.ILocationManager;
 import android.location.Location;
 import android.location.LocationProviderImpl;
 import android.os.Bundle;
@@ -36,14 +37,45 @@
     public static final float SPEED = 10;
     public static final float BEARING = 1;
     public static final int STATUS = AVAILABLE;
+    private static final long LOCATION_INTERVAL = 1000;
 
     private Location mLocation;
     private boolean mEnabled;
+    private TestLocationProviderThread mThread;
 
-    public TestLocationProvider() {
-        super(PROVIDER_NAME);
+    private class TestLocationProviderThread extends Thread {
+
+        private boolean mDone = false;
+
+        public TestLocationProviderThread() {
+            super("TestLocationProviderThread");
+        }
+
+        public void run() {            
+            // thread exits after disable() is called
+            synchronized (this) {
+                while (!mDone) {
+                    try {
+                        wait(LOCATION_INTERVAL);
+                    } catch (InterruptedException e) {
+                    }
+                    
+                    if (!mDone) {
+                        TestLocationProvider.this.updateLocation();
+                    }
+                }
+            }
+        }
+        
+        synchronized void setDone() {
+            mDone = true;
+            notify();
+        }
+    }
+
+    public TestLocationProvider(ILocationManager locationManager) {
+        super(PROVIDER_NAME, locationManager);
         mLocation = new Location(PROVIDER_NAME);
-        updateLocation();
     }
 
     //LocationProvider methods
@@ -95,13 +127,23 @@
 
     //LocationProviderImpl methods
     @Override
-    public void disable() {
+    public synchronized void disable() {
         mEnabled = false;
+        if (mThread != null) {
+            mThread.setDone();
+            try {
+                mThread.join();
+            } catch (InterruptedException e) {
+            }
+            mThread = null;
+        }
     }
 
     @Override
-    public void enable() {
-        mEnabled = true;
+    public synchronized void enable() {
+       mEnabled = true;
+        mThread = new TestLocationProviderThread();
+        mThread.start();
     }
 
     @Override
@@ -110,13 +152,6 @@
     }
 
     @Override
-    public boolean getLocation(Location l) {
-        updateLocation();
-        l.set(mLocation);
-        return true;
-    }
-
-    @Override
     public int getStatus(Bundle extras) {
         return STATUS;
     }
@@ -134,6 +169,7 @@
         extras.putInt("extraTest", 24);
         mLocation.setExtras(extras);
         mLocation.setTime(time);
+        reportLocationChanged(mLocation);
     }
 
 }