summaryrefslogtreecommitdiffstats
path: root/services/java
diff options
context:
space:
mode:
Diffstat (limited to 'services/java')
-rw-r--r--services/java/com/android/server/LocationManagerService.java449
-rw-r--r--services/java/com/android/server/location/Geofence.java100
-rw-r--r--services/java/com/android/server/location/GeofenceManager.java242
3 files changed, 435 insertions, 356 deletions
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 2918dbc..06b056d 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -19,18 +19,13 @@ package com.android.server;
import android.app.Activity;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
-import android.content.ComponentName;
import android.content.ContentQueryMap;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.pm.PackageManager.NameNotFoundException;
-import android.content.pm.Signature;
import android.content.res.Resources;
import android.database.Cursor;
import android.location.Address;
@@ -46,7 +41,6 @@ import android.location.LocationManager;
import android.location.LocationProvider;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
-import android.net.Uri;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
@@ -58,14 +52,15 @@ import android.os.Process;
import android.os.RemoteException;
import android.os.WorkSource;
import android.provider.Settings;
+import android.provider.Settings.NameValueTable;
import android.util.Log;
import android.util.Slog;
import android.util.PrintWriterPrinter;
import com.android.internal.content.PackageMonitor;
-import com.android.internal.location.GpsNetInitiatedHandler;
import com.android.server.location.GeocoderProxy;
+import com.android.server.location.GeofenceManager;
import com.android.server.location.GpsLocationProvider;
import com.android.server.location.LocationProviderInterface;
import com.android.server.location.LocationProviderProxy;
@@ -95,9 +90,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
private static final String TAG = "LocationManagerService";
private static final boolean LOCAL_LOGV = false;
- // The last time a location was written, by provider name.
- private HashMap<String,Long> mLastWriteTime = new HashMap<String,Long>();
-
private static final String ACCESS_FINE_LOCATION =
android.Manifest.permission.ACCESS_FINE_LOCATION;
private static final String ACCESS_COARSE_LOCATION =
@@ -147,7 +139,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
private final static String WAKELOCK_KEY = "LocationManagerService";
private PowerManager.WakeLock mWakeLock = null;
private int mPendingBroadcasts;
-
+
/**
* List of all receivers.
*/
@@ -179,13 +171,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
*/
private final WorkSource mTmpWorkSource = new WorkSource();
- // Proximity listeners
- private Receiver mProximityReceiver = null;
- private ILocationListener mProximityListener = null;
- private HashMap<PendingIntent,ProximityAlert> mProximityAlerts =
- new HashMap<PendingIntent,ProximityAlert>();
- private HashSet<ProximityAlert> mProximitiesEntered =
- new HashSet<ProximityAlert>();
+ GeofenceManager mGeofenceManager;
// Last known location for each provider
private HashMap<String,Location> mLastKnownLocation =
@@ -266,13 +252,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
throw new IllegalStateException("Request for non-existent listener");
}
- public PendingIntent getPendingIntent() {
- if (mPendingIntent != null) {
- return mPendingIntent;
- }
- throw new IllegalStateException("Request for non-existent intent");
- }
-
public boolean callStatusChangedLocked(String provider, int status, Bundle extras) {
if (mListener != null) {
try {
@@ -280,11 +259,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mListener.onStatusChanged(provider, status, extras);
- if (mListener != mProximityListener) {
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
+ // call this after broadcasting so we do not increment
+ // if we throw an exeption.
+ incrementPendingBroadcastsLocked();
}
} catch (RemoteException e) {
return false;
@@ -317,11 +294,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mListener.onLocationChanged(location);
- if (mListener != mProximityListener) {
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
+ // call this after broadcasting so we do not increment
+ // if we throw an exeption.
+ incrementPendingBroadcastsLocked();
}
} catch (RemoteException e) {
return false;
@@ -357,11 +332,9 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
} else {
mListener.onProviderDisabled(provider);
}
- if (mListener != mProximityListener) {
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcastsLocked();
- }
+ // call this after broadcasting so we do not increment
+ // if we throw an exeption.
+ incrementPendingBroadcastsLocked();
}
} catch (RemoteException e) {
return false;
@@ -402,6 +375,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void onSendFinished(PendingIntent pendingIntent, Intent intent,
int resultCode, String resultData, Bundle resultExtras) {
synchronized (this) {
@@ -424,6 +398,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void locationCallbackFinished(ILocationListener listener) {
//Do not use getReceiver here as that will add the ILocationListener to
//the receiver list if it is not found. If it is not found then the
@@ -442,6 +417,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
private final class SettingsObserver implements Observer {
+ @Override
public void update(Observable o, Object arg) {
synchronized (mLock) {
updateProvidersLocked();
@@ -588,7 +564,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
void systemReady() {
- // we defer starting up the service until the system is ready
+ // we defer starting up the service until the system is ready
Thread thread = new Thread(null, this, "LocationManagerService");
thread.start();
}
@@ -616,20 +592,22 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// listen for settings changes
ContentResolver resolver = mContext.getContentResolver();
Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
- "(" + Settings.System.NAME + "=?)",
+ "(" + NameValueTable.NAME + "=?)",
new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
null);
- mSettings = new ContentQueryMap(settingsCursor, Settings.System.NAME, true, mLocationHandler);
+ mSettings = new ContentQueryMap(settingsCursor, NameValueTable.NAME, true, mLocationHandler);
SettingsObserver settingsObserver = new SettingsObserver();
mSettings.addObserver(settingsObserver);
}
+ @Override
public void run()
{
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
Looper.prepare();
mLocationHandler = new LocationWorkerHandler();
initialize();
+ mGeofenceManager = new GeofenceManager(mContext);
Looper.loop();
}
@@ -690,6 +668,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return true;
}
+ @Override
public List<String> getAllProviders() {
try {
synchronized (mLock) {
@@ -715,6 +694,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return out;
}
+ @Override
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
try {
synchronized (mLock) {
@@ -782,6 +762,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
private class LpPowerComparator implements Comparator<LocationProviderInterface> {
+ @Override
public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
// Smaller is better
return (l1.getPowerRequirement() - l2.getPowerRequirement());
@@ -793,6 +774,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
private class LpAccuracyComparator implements Comparator<LocationProviderInterface> {
+ @Override
public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
// Smaller is better
return (l1.getAccuracy() - l2.getAccuracy());
@@ -815,6 +797,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
(p.supportsSpeed() ? SPEED_SCORE : 0);
}
+ @Override
public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
return (score(l2) - score(l1)); // Bigger is better
}
@@ -896,6 +879,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
* @param enabledOnly if true then only a provider that is currently enabled is returned
* @return name of the provider that best matches the requirements
*/
+ @Override
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
List<String> goodProviders = getProviders(criteria, enabledOnly);
if (!goodProviders.isEmpty()) {
@@ -951,6 +935,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return null;
}
+ @Override
public boolean providerMeetsCriteria(String provider, Criteria criteria) {
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
@@ -988,7 +973,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
ArrayList<Receiver> deadReceivers = null;
-
+
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records != null) {
final int N = records.size();
@@ -1010,7 +995,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
removeUpdatesLocked(deadReceivers.get(i));
}
}
-
+
if (enabled) {
p.enable();
if (listeners > 0) {
@@ -1148,14 +1133,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
}
- for (ProximityAlert alert : mProximityAlerts.values()) {
- if (alert.mUid == uid) {
- return true;
- }
- }
return false;
}
+ @Override
public void requestLocationUpdates(String provider, Criteria criteria,
long minTime, float minDistance, boolean singleShot, ILocationListener listener) {
if (criteria != null) {
@@ -1181,6 +1162,20 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ void validatePackageName(int uid, String packageName) {
+ if (packageName == null) {
+ throw new SecurityException("packageName cannot be null");
+ }
+ String[] packages = mPackageManager.getPackagesForUid(uid);
+ if (packages == null) {
+ throw new SecurityException("invalid UID " + uid);
+ }
+ for (String pkg : packages) {
+ if (packageName.equals(pkg)) return;
+ }
+ throw new SecurityException("invalid package name");
+ }
+
void validatePendingIntent(PendingIntent intent) {
if (intent.isTargetedToPackage()) {
return;
@@ -1193,6 +1188,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// + intent);
}
+ @Override
public void requestLocationUpdatesPI(String provider, Criteria criteria,
long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
validatePendingIntent(intent);
@@ -1270,6 +1266,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void removeUpdates(ILocationListener listener) {
try {
synchronized (mLock) {
@@ -1284,6 +1281,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void removeUpdatesPI(PendingIntent intent) {
try {
synchronized (mLock) {
@@ -1370,6 +1368,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public boolean addGpsStatusListener(IGpsStatusListener listener) {
if (mGpsStatusProvider == null) {
return false;
@@ -1388,6 +1387,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return true;
}
+ @Override
public void removeGpsStatusListener(IGpsStatusListener listener) {
synchronized (mLock) {
try {
@@ -1398,6 +1398,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public boolean sendExtraCommand(String provider, String command, Bundle extras) {
if (provider == null) {
// throw NullPointerException to remain compatible with previous implementation
@@ -1417,11 +1418,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (p == null) {
return false;
}
-
+
return p.sendExtraCommand(command, extras);
}
}
+ @Override
public boolean sendNiResponse(int notifId, int userResponse)
{
if (Binder.getCallingUid() != Process.myUid()) {
@@ -1438,223 +1440,11 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- class ProximityAlert {
- final int mUid;
- final double mLatitude;
- final double mLongitude;
- final float mRadius;
- final long mExpiration;
- final PendingIntent mIntent;
- final Location mLocation;
-
- public ProximityAlert(int uid, double latitude, double longitude,
- float radius, long expiration, PendingIntent intent) {
- mUid = uid;
- mLatitude = latitude;
- mLongitude = longitude;
- mRadius = radius;
- mExpiration = expiration;
- mIntent = intent;
-
- mLocation = new Location("");
- mLocation.setLatitude(latitude);
- mLocation.setLongitude(longitude);
- }
-
- long getExpiration() {
- return mExpiration;
- }
-
- PendingIntent getIntent() {
- return mIntent;
- }
-
- boolean isInProximity(double latitude, double longitude, float accuracy) {
- Location loc = new Location("");
- loc.setLatitude(latitude);
- loc.setLongitude(longitude);
-
- double radius = loc.distanceTo(mLocation);
- return radius <= Math.max(mRadius,accuracy);
- }
-
- @Override
- public String toString() {
- return "ProximityAlert{"
- + Integer.toHexString(System.identityHashCode(this))
- + " uid " + mUid + mIntent + "}";
- }
-
- void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + this);
- pw.println(prefix + "mLatitude=" + mLatitude + " mLongitude=" + mLongitude);
- pw.println(prefix + "mRadius=" + mRadius + " mExpiration=" + mExpiration);
- pw.println(prefix + "mIntent=" + mIntent);
- pw.println(prefix + "mLocation:");
- mLocation.dump(new PrintWriterPrinter(pw), prefix + " ");
- }
- }
-
- // Listener for receiving locations to trigger proximity alerts
- class ProximityListener extends ILocationListener.Stub implements PendingIntent.OnFinished {
-
- boolean isGpsAvailable = false;
-
- // Note: this is called with the lock held.
- public void onLocationChanged(Location loc) {
-
- // If Gps is available, then ignore updates from NetworkLocationProvider
- if (loc.getProvider().equals(LocationManager.GPS_PROVIDER)) {
- isGpsAvailable = true;
- }
- if (isGpsAvailable && loc.getProvider().equals(LocationManager.NETWORK_PROVIDER)) {
- return;
- }
-
- // Process proximity alerts
- long now = System.currentTimeMillis();
- double latitude = loc.getLatitude();
- double longitude = loc.getLongitude();
- float accuracy = loc.getAccuracy();
- ArrayList<PendingIntent> intentsToRemove = null;
-
- for (ProximityAlert alert : mProximityAlerts.values()) {
- PendingIntent intent = alert.getIntent();
- long expiration = alert.getExpiration();
-
- if ((expiration == -1) || (now <= expiration)) {
- boolean entered = mProximitiesEntered.contains(alert);
- boolean inProximity =
- alert.isInProximity(latitude, longitude, accuracy);
- if (!entered && inProximity) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Entered alert");
- }
- mProximitiesEntered.add(alert);
- Intent enteredIntent = new Intent();
- enteredIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
- try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcasts()
- // is called before decrementPendingBroadcasts()
- intent.send(mContext, 0, enteredIntent, this, mLocationHandler,
- ACCESS_FINE_LOCATION);
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcasts();
- }
- } catch (PendingIntent.CanceledException e) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Canceled proximity alert: " + alert, e);
- }
- if (intentsToRemove == null) {
- intentsToRemove = new ArrayList<PendingIntent>();
- }
- intentsToRemove.add(intent);
- }
- } else if (entered && !inProximity) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Exited alert");
- }
- mProximitiesEntered.remove(alert);
- Intent exitedIntent = new Intent();
- exitedIntent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
- try {
- synchronized (this) {
- // synchronize to ensure incrementPendingBroadcasts()
- // is called before decrementPendingBroadcasts()
- intent.send(mContext, 0, exitedIntent, this, mLocationHandler,
- ACCESS_FINE_LOCATION);
- // call this after broadcasting so we do not increment
- // if we throw an exeption.
- incrementPendingBroadcasts();
- }
- } catch (PendingIntent.CanceledException e) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Canceled proximity alert: " + alert, e);
- }
- if (intentsToRemove == null) {
- intentsToRemove = new ArrayList<PendingIntent>();
- }
- intentsToRemove.add(intent);
- }
- }
- } else {
- // Mark alert for expiration
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Expiring proximity alert: " + alert);
- }
- if (intentsToRemove == null) {
- intentsToRemove = new ArrayList<PendingIntent>();
- }
- intentsToRemove.add(alert.getIntent());
- }
- }
-
- // Remove expired alerts
- if (intentsToRemove != null) {
- for (PendingIntent i : intentsToRemove) {
- ProximityAlert alert = mProximityAlerts.get(i);
- mProximitiesEntered.remove(alert);
- removeProximityAlertLocked(i);
- }
- }
- }
-
- // Note: this is called with the lock held.
- public void onProviderDisabled(String provider) {
- if (provider.equals(LocationManager.GPS_PROVIDER)) {
- isGpsAvailable = false;
- }
- }
-
- // Note: this is called with the lock held.
- public void onProviderEnabled(String provider) {
- // ignore
- }
-
- // Note: this is called with the lock held.
- public void onStatusChanged(String provider, int status, Bundle extras) {
- if ((provider.equals(LocationManager.GPS_PROVIDER)) &&
- (status != LocationProvider.AVAILABLE)) {
- isGpsAvailable = false;
- }
- }
-
- public void onSendFinished(PendingIntent pendingIntent, Intent intent,
- int resultCode, String resultData, Bundle resultExtras) {
- // synchronize to ensure incrementPendingBroadcasts()
- // is called before decrementPendingBroadcasts()
- synchronized (this) {
- decrementPendingBroadcasts();
- }
- }
- }
-
+ @Override
public void addProximityAlert(double latitude, double longitude,
- float radius, long expiration, PendingIntent intent) {
+ float radius, long expiration, PendingIntent intent, String packageName) {
validatePendingIntent(intent);
- try {
- synchronized (mLock) {
- addProximityAlertLocked(latitude, longitude, radius, expiration, intent);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "addProximityAlert got exception:", e);
- }
- }
-
- private void addProximityAlertLocked(double latitude, double longitude,
- float radius, long expiration, PendingIntent intent) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "addProximityAlert: latitude = " + latitude +
- ", longitude = " + longitude +
- ", expiration = " + expiration +
- ", intent = " + intent);
- }
+ validatePackageName(Binder.getCallingUid(), packageName);
// Require ability to access all providers for now
if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
@@ -1662,59 +1452,28 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
}
- if (expiration != -1) {
- expiration += System.currentTimeMillis();
- }
- ProximityAlert alert = new ProximityAlert(Binder.getCallingUid(),
- latitude, longitude, radius, expiration, intent);
- mProximityAlerts.put(intent, alert);
+ if (LOCAL_LOGV) Slog.v(TAG, "addProximityAlert: lat=" + latitude + ", long=" + longitude +
+ ", radius=" + radius + ", exp=" + expiration + ", intent = " + intent);
- if (mProximityReceiver == null) {
- mProximityListener = new ProximityListener();
- mProximityReceiver = new Receiver(mProximityListener);
-
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface provider = mProviders.get(i);
- requestLocationUpdatesLocked(provider.getName(), 1000L, 1.0f,
- false, mProximityReceiver);
- }
- }
+ mGeofenceManager.addFence(latitude, longitude, radius, expiration, intent,
+ Binder.getCallingUid(), packageName);
}
+ @Override
public void removeProximityAlert(PendingIntent intent) {
- try {
- synchronized (mLock) {
- removeProximityAlertLocked(intent);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "removeProximityAlert got exception:", e);
- }
- }
+ if (intent == null) throw new NullPointerException("pending intent is null");
- private void removeProximityAlertLocked(PendingIntent intent) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "removeProximityAlert: intent = " + intent);
- }
+ if (LOCAL_LOGV) Slog.v(TAG, "removeProximityAlert: intent = " + intent);
- mProximityAlerts.remove(intent);
- if (mProximityAlerts.size() == 0) {
- if (mProximityReceiver != null) {
- removeUpdatesLocked(mProximityReceiver);
- }
- mProximityReceiver = null;
- mProximityListener = null;
- }
- }
+ mGeofenceManager.removeFence(intent);
+ }
/**
* @return null if the provider does not exist
* @throws SecurityException if the provider is not allowed to be
* accessed by the caller
*/
+ @Override
public Bundle getProviderInfo(String provider) {
try {
synchronized (mLock) {
@@ -1752,6 +1511,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return b;
}
+ @Override
public boolean isProviderEnabled(String provider) {
try {
synchronized (mLock) {
@@ -1765,6 +1525,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void reportLocation(Location location, boolean passive) {
if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
!= PackageManager.PERMISSION_GRANTED) {
@@ -1787,6 +1548,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return isAllowedBySettingsLocked(provider);
}
+ @Override
public Location getLastKnownLocation(String provider) {
if (LOCAL_LOGV) {
Slog.v(TAG, "getLastKnownLocation: " + provider);
@@ -1869,7 +1631,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
int status = p.getStatus(extras);
ArrayList<Receiver> deadReceivers = null;
-
+
// Broadcast location or status to all listeners
final int N = records.size();
for (int i=0; i<N; i++) {
@@ -1912,7 +1674,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
}
-
+
if (deadReceivers != null) {
for (int i=deadReceivers.size()-1; i>=0; i--) {
removeUpdatesLocked(deadReceivers.get(i));
@@ -2024,31 +1786,6 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
}
- ArrayList<ProximityAlert> removedAlerts = null;
- for (ProximityAlert i : mProximityAlerts.values()) {
- if (i.mUid == uid) {
- if (queryRestart) {
- setResultCode(Activity.RESULT_OK);
- return;
- }
- if (removedAlerts == null) {
- removedAlerts = new ArrayList<ProximityAlert>();
- }
- if (!removedAlerts.contains(i)) {
- removedAlerts.add(i);
- }
- }
- }
- if (removedRecs != null) {
- for (int i=removedRecs.size()-1; i>=0; i--) {
- removeUpdatesLocked(removedRecs.get(i));
- }
- }
- if (removedAlerts != null) {
- for (int i=removedAlerts.size()-1; i>=0; i--) {
- removeProximityAlertLocked(removedAlerts.get(i).mIntent);
- }
- }
}
}
}
@@ -2089,6 +1826,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// Called by main thread; divert work to LocationWorker.
Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
}
+ @Override
+ public void onPackageDisappeared(String packageName, int uid) {
+ mGeofenceManager.removeFence(packageName);
+ }
};
// Wake locks
@@ -2130,10 +1871,12 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// Geocoder
+ @Override
public boolean geocoderIsPresent() {
return mGeocodeProvider != null;
}
+ @Override
public String getFromLocation(double latitude, double longitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
if (mGeocodeProvider != null) {
@@ -2144,6 +1887,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
+ @Override
public String getFromLocationName(String locationName,
double lowerLeftLatitude, double lowerLeftLongitude,
double upperRightLatitude, double upperRightLongitude, int maxResults,
@@ -2169,9 +1913,10 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (mContext.checkCallingPermission(ACCESS_MOCK_LOCATION) !=
PackageManager.PERMISSION_GRANTED) {
throw new SecurityException("Requires ACCESS_MOCK_LOCATION permission");
- }
+ }
}
+ @Override
public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
@@ -2207,6 +1952,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
Binder.restoreCallingIdentity(identity);
}
+ @Override
public void removeTestProvider(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2231,6 +1977,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void setTestProviderLocation(String provider, Location loc) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2245,6 +1992,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void clearTestProviderLocation(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2256,6 +2004,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void setTestProviderEnabled(String provider, boolean enabled) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2278,6 +2027,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void clearTestProviderEnabled(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2293,6 +2043,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2304,6 +2055,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ @Override
public void clearTestProviderStatus(String provider) {
checkMockPermissionsSafe();
synchronized (mLock) {
@@ -2320,7 +2072,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
Slog.d(TAG, log);
}
}
-
+
+ @Override
protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
!= PackageManager.PERMISSION_GRANTED) {
@@ -2329,7 +2082,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
+ ", uid=" + Binder.getCallingUid());
return;
}
-
+
synchronized (mLock) {
pw.println("Current Location Manager state:");
pw.println(" sProvidersLoaded=" + sProvidersLoaded);
@@ -2361,36 +2114,20 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
pw.println(" " + i.getKey() + ":");
i.getValue().dump(new PrintWriterPrinter(pw), " ");
}
- if (mProximityAlerts.size() > 0) {
- pw.println(" Proximity Alerts:");
- for (Map.Entry<PendingIntent, ProximityAlert> i
- : mProximityAlerts.entrySet()) {
- pw.println(" " + i.getKey() + ":");
- i.getValue().dump(pw, " ");
- }
- }
- if (mProximitiesEntered.size() > 0) {
- pw.println(" Proximities Entered:");
- for (ProximityAlert i : mProximitiesEntered) {
- pw.println(" " + i + ":");
- i.dump(pw, " ");
- }
- }
- pw.println(" mProximityReceiver=" + mProximityReceiver);
- pw.println(" mProximityListener=" + mProximityListener);
+ mGeofenceManager.dump(pw);
if (mEnabledProviders.size() > 0) {
pw.println(" Enabled Providers:");
for (String i : mEnabledProviders) {
pw.println(" " + i);
}
-
+
}
if (mDisabledProviders.size() > 0) {
pw.println(" Disabled Providers:");
for (String i : mDisabledProviders) {
pw.println(" " + i);
}
-
+
}
if (mMockProviders.size() > 0) {
pw.println(" Mock Providers:");
diff --git a/services/java/com/android/server/location/Geofence.java b/services/java/com/android/server/location/Geofence.java
new file mode 100644
index 0000000..f63607a
--- /dev/null
+++ b/services/java/com/android/server/location/Geofence.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package com.android.server.location;
+
+import android.location.Location;
+
+/**
+ * Represents a simple circular GeoFence.
+ */
+public class Geofence {
+ public final static int FLAG_ENTER = 0x01;
+ public final static int FLAG_EXIT = 0x02;
+
+ static final int STATE_UNKNOWN = 0;
+ static final int STATE_INSIDE = 1;
+ static final int STATE_OUTSIDE = 2;
+
+ final double mLatitude;
+ final double mLongitude;
+ final float mRadius;
+ final Location mLocation;
+
+ int mState; // current state
+ double mDistance; // current distance to center of fence
+
+ public Geofence(double latitude, double longitude, float radius, Location prevLocation) {
+ mState = STATE_UNKNOWN;
+
+ mLatitude = latitude;
+ mLongitude = longitude;
+ mRadius = radius;
+
+ mLocation = new Location("");
+ mLocation.setLatitude(latitude);
+ mLocation.setLongitude(longitude);
+
+ if (prevLocation != null) {
+ processLocation(prevLocation);
+ }
+ }
+
+ /**
+ * Process a new location.
+ * @return FLAG_ENTER or FLAG_EXIT if the fence was crossed, 0 otherwise
+ */
+ public int processLocation(Location location) {
+ mDistance = mLocation.distanceTo(location);
+
+ int prevState = mState;
+ //TODO: inside/outside detection could be made more rigorous
+ boolean inside = mDistance <= Math.max(mRadius, location.getAccuracy());
+ if (inside) {
+ mState = STATE_INSIDE;
+ } else {
+ mState = STATE_OUTSIDE;
+ }
+
+ if (prevState != 0 && mState != prevState) {
+ if (mState == STATE_INSIDE) return FLAG_ENTER;
+ if (mState == STATE_OUTSIDE) return FLAG_EXIT;
+ }
+ return 0;
+ }
+
+ public double getDistance() {
+ return mDistance;
+ }
+
+ @Override
+ public String toString() {
+ String state;
+ switch (mState) {
+ case STATE_INSIDE:
+ state = "IN";
+ break;
+ case STATE_OUTSIDE:
+ state = "OUT";
+ break;
+ default:
+ state = "?";
+ }
+ return String.format("(%.4f, %.4f r=%.0f d=%.0f %s)", mLatitude, mLongitude, mRadius,
+ mDistance, state);
+ }
+}
diff --git a/services/java/com/android/server/location/GeofenceManager.java b/services/java/com/android/server/location/GeofenceManager.java
new file mode 100644
index 0000000..b3f53ea
--- /dev/null
+++ b/services/java/com/android/server/location/GeofenceManager.java
@@ -0,0 +1,242 @@
+/*
+ * Copyright (C) 20012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.location;
+
+import java.io.PrintWriter;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+
+import android.Manifest.permission;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.location.Location;
+import android.location.LocationListener;
+import android.location.LocationManager;
+import android.os.Bundle;
+import android.os.Looper;
+import android.os.PowerManager;
+import android.os.SystemClock;
+
+public class GeofenceManager implements LocationListener, PendingIntent.OnFinished {
+ static final String TAG = "GeofenceManager";
+
+ /**
+ * Assume a maximum land speed, as a heuristic to throttle location updates.
+ * (Air travel should result in an airplane mode toggle which will
+ * force a new location update anyway).
+ */
+ static final int MAX_SPEED_M_S = 100; // 360 km/hr (high speed train)
+
+ class GeofenceWrapper {
+ final Geofence fence;
+ final long expiry;
+ final String packageName;
+ final PendingIntent intent;
+
+ public GeofenceWrapper(Geofence fence, long expiry, String packageName,
+ PendingIntent intent) {
+ this.fence = fence;
+ this.expiry = expiry;
+ this.packageName = packageName;
+ this.intent = intent;
+ }
+ }
+
+ final Context mContext;
+ final LocationManager mLocationManager;
+ final PowerManager.WakeLock mWakeLock;
+ final Looper mLooper; // looper thread to take location updates on
+
+ // access to members below is synchronized on this
+ Location mLastLocation;
+ List<GeofenceWrapper> mFences = new LinkedList<GeofenceWrapper>();
+
+ public GeofenceManager(Context context) {
+ mContext = context;
+ mLocationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+ mLooper = Looper.myLooper();
+ mLocationManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER, 0, 0, this);
+ }
+
+ public void addFence(double latitude, double longitude, float radius, long expiration,
+ PendingIntent intent, int uid, String packageName) {
+ long expiry = SystemClock.elapsedRealtime() + expiration;
+ if (expiration < 0) {
+ expiry = Long.MAX_VALUE;
+ }
+ Geofence fence = new Geofence(latitude, longitude, radius, mLastLocation);
+ GeofenceWrapper fenceWrapper = new GeofenceWrapper(fence, expiry, packageName, intent);
+
+ synchronized (this) {
+ mFences.add(fenceWrapper);
+ updateProviderRequirements();
+ }
+ }
+
+ public void removeFence(PendingIntent intent) {
+ synchronized (this) {
+ Iterator<GeofenceWrapper> iter = mFences.iterator();
+ while (iter.hasNext()) {
+ GeofenceWrapper fenceWrapper = iter.next();
+ if (fenceWrapper.intent.equals(intent)) {
+ iter.remove();
+ }
+ }
+ updateProviderRequirements();
+ }
+ }
+
+ public void removeFence(String packageName) {
+ synchronized (this) {
+ Iterator<GeofenceWrapper> iter = mFences.iterator();
+ while (iter.hasNext()) {
+ GeofenceWrapper fenceWrapper = iter.next();
+ if (fenceWrapper.packageName.equals(packageName)) {
+ iter.remove();
+ }
+ }
+ updateProviderRequirements();
+ }
+ }
+
+ void removeExpiredFences() {
+ synchronized (this) {
+ long time = SystemClock.elapsedRealtime();
+ Iterator<GeofenceWrapper> iter = mFences.iterator();
+ while (iter.hasNext()) {
+ GeofenceWrapper fenceWrapper = iter.next();
+ if (fenceWrapper.expiry < time) {
+ iter.remove();
+ }
+ }
+ }
+ }
+
+ void processLocation(Location location) {
+ List<PendingIntent> enterIntents = new LinkedList<PendingIntent>();
+ List<PendingIntent> exitIntents = new LinkedList<PendingIntent>();
+
+ synchronized (this) {
+ mLastLocation = location;
+
+ removeExpiredFences();
+
+ for (GeofenceWrapper fenceWrapper : mFences) {
+ int event = fenceWrapper.fence.processLocation(location);
+ if ((event & Geofence.FLAG_ENTER) != 0) {
+ enterIntents.add(fenceWrapper.intent);
+ }
+ if ((event & Geofence.FLAG_EXIT) != 0) {
+ exitIntents.add(fenceWrapper.intent);
+ }
+ }
+ updateProviderRequirements();
+ }
+
+ // release lock before sending intents
+ for (PendingIntent intent : exitIntents) {
+ sendIntentExit(intent);
+ }
+ for (PendingIntent intent : enterIntents) {
+ sendIntentEnter(intent);
+ }
+ }
+
+ void sendIntentEnter(PendingIntent pendingIntent) {
+ Intent intent = new Intent();
+ intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
+ sendIntent(pendingIntent, intent);
+ }
+
+ void sendIntentExit(PendingIntent pendingIntent) {
+ Intent intent = new Intent();
+ intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
+ sendIntent(pendingIntent, intent);
+ }
+
+ void sendIntent(PendingIntent pendingIntent, Intent intent) {
+ try {
+ mWakeLock.acquire();
+ pendingIntent.send(mContext, 0, intent, this, null, permission.ACCESS_FINE_LOCATION);
+ } catch (PendingIntent.CanceledException e) {
+ removeFence(pendingIntent);
+ mWakeLock.release();
+ }
+ }
+
+ void updateProviderRequirements() {
+ synchronized (this) {
+ double minDistance = Double.MAX_VALUE;
+ for (GeofenceWrapper alert : mFences) {
+ if (alert.fence.getDistance() < minDistance) {
+ minDistance = alert.fence.getDistance();
+ }
+ }
+
+ if (minDistance == Double.MAX_VALUE) {
+ disableLocation();
+ } else {
+ int intervalMs = (int)(minDistance * 1000) / MAX_SPEED_M_S;
+ setLocationInterval(intervalMs);
+ }
+ }
+ }
+
+ void setLocationInterval(int intervalMs) {
+ mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, intervalMs, 0, this,
+ mLooper);
+ }
+
+ void disableLocation() {
+ mLocationManager.removeUpdates(this);
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ processLocation(location);
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) { }
+
+ @Override
+ public void onProviderEnabled(String provider) { }
+
+ @Override
+ public void onProviderDisabled(String provider) { }
+
+ @Override
+ public void onSendFinished(PendingIntent pendingIntent, Intent intent, int resultCode,
+ String resultData, Bundle resultExtras) {
+ mWakeLock.release();
+ }
+
+ public void dump(PrintWriter pw) {
+ pw.println(" Geofences:");
+ for (GeofenceWrapper fenceWrapper : mFences) {
+ pw.append(" ");
+ pw.append(fenceWrapper.packageName);
+ pw.append(" ");
+ pw.append(fenceWrapper.fence.toString());
+ pw.append("\n");
+ }
+ }
+}