summaryrefslogtreecommitdiffstats
path: root/services
diff options
context:
space:
mode:
Diffstat (limited to 'services')
-rw-r--r--services/java/com/android/server/LocationManagerService.java1926
-rw-r--r--services/java/com/android/server/ServiceWatcher.java274
-rw-r--r--services/java/com/android/server/location/GeocoderProxy.java92
-rw-r--r--services/java/com/android/server/location/GeofenceManager.java188
-rw-r--r--services/java/com/android/server/location/GeofenceState.java (renamed from services/java/com/android/server/location/Geofence.java)40
-rwxr-xr-xservices/java/com/android/server/location/GpsLocationProvider.java549
-rw-r--r--services/java/com/android/server/location/LocationProviderInterface.java53
-rw-r--r--services/java/com/android/server/location/LocationProviderProxy.java570
-rw-r--r--services/java/com/android/server/location/MockProvider.java156
-rw-r--r--services/java/com/android/server/location/PassiveProvider.java94
10 files changed, 1743 insertions, 2199 deletions
diff --git a/services/java/com/android/server/LocationManagerService.java b/services/java/com/android/server/LocationManagerService.java
index 198ba8b..a6c3860 100644
--- a/services/java/com/android/server/LocationManagerService.java
+++ b/services/java/com/android/server/LocationManagerService.java
@@ -16,21 +16,21 @@
package com.android.server;
-import android.app.Activity;
import android.app.PendingIntent;
-import android.content.BroadcastReceiver;
import android.content.ContentQueryMap;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
+import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
+import android.content.pm.PackageManager.NameNotFoundException;
import android.content.res.Resources;
import android.database.Cursor;
import android.location.Address;
import android.location.Criteria;
import android.location.GeocoderParams;
+import android.location.Geofence;
import android.location.IGpsStatusListener;
import android.location.IGpsStatusProvider;
import android.location.ILocationListener;
@@ -39,26 +39,28 @@ import android.location.INetInitiatedListener;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
+import android.location.LocationRequest;
import android.net.ConnectivityManager;
-import android.net.NetworkInfo;
import android.os.Binder;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.Message;
+import android.os.Parcelable;
import android.os.PowerManager;
import android.os.Process;
import android.os.RemoteException;
+import android.os.SystemClock;
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.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
import com.android.server.location.GeocoderProxy;
import com.android.server.location.GeofenceManager;
import com.android.server.location.GpsLocationProvider;
@@ -70,8 +72,7 @@ import com.android.server.location.PassiveProvider;
import java.io.FileDescriptor;
import java.io.PrintWriter;
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Comparator;
+import java.util.Arrays;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
@@ -83,135 +84,273 @@ import java.util.Set;
/**
* The service class that manages LocationProviders and issues location
* updates and alerts.
- *
- * {@hide}
*/
-public class LocationManagerService extends ILocationManager.Stub implements Runnable {
+public class LocationManagerService extends ILocationManager.Stub implements Observer, Runnable {
private static final String TAG = "LocationManagerService";
- private static final boolean LOCAL_LOGV = false;
+ public static final boolean D = false;
+
+ private static final String WAKELOCK_KEY = TAG;
+ private static final String THREAD_NAME = TAG;
private static final String ACCESS_FINE_LOCATION =
- android.Manifest.permission.ACCESS_FINE_LOCATION;
+ android.Manifest.permission.ACCESS_FINE_LOCATION;
private static final String ACCESS_COARSE_LOCATION =
- android.Manifest.permission.ACCESS_COARSE_LOCATION;
+ android.Manifest.permission.ACCESS_COARSE_LOCATION;
private static final String ACCESS_MOCK_LOCATION =
- android.Manifest.permission.ACCESS_MOCK_LOCATION;
+ android.Manifest.permission.ACCESS_MOCK_LOCATION;
private static final String ACCESS_LOCATION_EXTRA_COMMANDS =
- android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
+ android.Manifest.permission.ACCESS_LOCATION_EXTRA_COMMANDS;
private static final String INSTALL_LOCATION_PROVIDER =
- android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
+ android.Manifest.permission.INSTALL_LOCATION_PROVIDER;
+
+ private static final String NETWORK_LOCATION_SERVICE_ACTION =
+ "com.android.location.service.v2.NetworkLocationProvider";
+ private static final String FUSED_LOCATION_SERVICE_ACTION =
+ "com.android.location.service.FusedLocationProvider";
+
+ private static final int MSG_LOCATION_CHANGED = 1;
+
+ // Accuracy in meters above which a location is considered coarse
+ private static final double COARSE_ACCURACY_M = 100.0;
+ private static final String EXTRA_COARSE_LOCATION = "coarseLocation";
+
+ private static final int APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR = 111000;
+
+ /**
+ * Maximum latitude of 1 meter from the pole.
+ * This keeps cosine(MAX_LATITUDE) to a non-zero value;
+ */
+ private static final double MAX_LATITUDE =
+ 90.0 - (1.0 / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR);
// Location Providers may sometimes deliver location updates
// slightly faster that requested - provide grace period so
// we don't unnecessarily filter events that are otherwise on
// time
- private static final int MAX_PROVIDER_SCHEDULING_JITTER = 100;
+ private static final int MAX_PROVIDER_SCHEDULING_JITTER_MS = 100;
- // Set of providers that are explicitly enabled
- private final Set<String> mEnabledProviders = new HashSet<String>();
+ private static final LocationRequest DEFAULT_LOCATION_REQUEST = new LocationRequest();
- // Set of providers that are explicitly disabled
- private final Set<String> mDisabledProviders = new HashSet<String>();
-
- // Locations, status values, and extras for mock providers
- private final HashMap<String,MockProvider> mMockProviders = new HashMap<String,MockProvider>();
+ private final Context mContext;
- private static boolean sProvidersLoaded = false;
+ // used internally for synchronization
+ private final Object mLock = new Object();
- private final Context mContext;
- private PackageManager mPackageManager; // final after initialize()
- private String mNetworkLocationProviderPackageName; // only used on handler thread
- private String mGeocodeProviderPackageName; // only used on handler thread
+ // --- fields below are final after init() ---
+ private GeofenceManager mGeofenceManager;
+ private PowerManager.WakeLock mWakeLock;
+ private PackageManager mPackageManager;
private GeocoderProxy mGeocodeProvider;
private IGpsStatusProvider mGpsStatusProvider;
private INetInitiatedListener mNetInitiatedListener;
private LocationWorkerHandler mLocationHandler;
+ // track the passive provider for some special cases
+ private PassiveProvider mPassiveProvider;
- // Cache the real providers for use in addTestProvider() and removeTestProvider()
- LocationProviderProxy mNetworkLocationProvider;
- LocationProviderInterface mGpsLocationProvider;
+ // --- fields below are protected by mWakeLock ---
+ private int mPendingBroadcasts;
- // Handler messages
- private static final int MESSAGE_LOCATION_CHANGED = 1;
- private static final int MESSAGE_PACKAGE_UPDATED = 2;
+ // --- fields below are protected by mLock ---
+ // Set of providers that are explicitly enabled
+ private final Set<String> mEnabledProviders = new HashSet<String>();
- // wakelock variables
- private final static String WAKELOCK_KEY = "LocationManagerService";
- private PowerManager.WakeLock mWakeLock = null;
- private int mPendingBroadcasts;
+ // Set of providers that are explicitly disabled
+ private final Set<String> mDisabledProviders = new HashSet<String>();
- /**
- * List of all receivers.
- */
- private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
+ // Mock (test) providers
+ private final HashMap<String, MockProvider> mMockProviders =
+ new HashMap<String, MockProvider>();
+ // all receivers
+ private final HashMap<Object, Receiver> mReceivers = new HashMap<Object, Receiver>();
- /**
- * List of location providers.
- */
+ // currently installed providers (with mocks replacing real providers)
private final ArrayList<LocationProviderInterface> mProviders =
- new ArrayList<LocationProviderInterface>();
- private final HashMap<String, LocationProviderInterface> mProvidersByName
- = new HashMap<String, LocationProviderInterface>();
+ new ArrayList<LocationProviderInterface>();
- /**
- * Object used internally for synchronization
- */
- private final Object mLock = new Object();
+ // real providers, saved here when mocked out
+ private final HashMap<String, LocationProviderInterface> mRealProviders =
+ new HashMap<String, LocationProviderInterface>();
- /**
- * Mapping from provider name to all its UpdateRecords
- */
- private final HashMap<String,ArrayList<UpdateRecord>> mRecordsByProvider =
- new HashMap<String,ArrayList<UpdateRecord>>();
+ // mapping from provider name to provider
+ private final HashMap<String, LocationProviderInterface> mProvidersByName =
+ new HashMap<String, LocationProviderInterface>();
- /**
- * Temporary filled in when computing min time for a provider. Access is
- * protected by global lock mLock.
- */
- private final WorkSource mTmpWorkSource = new WorkSource();
+ // mapping from provider name to all its UpdateRecords
+ private final HashMap<String, ArrayList<UpdateRecord>> mRecordsByProvider =
+ new HashMap<String, ArrayList<UpdateRecord>>();
+
+ // mapping from provider name to last known location
+ private final HashMap<String, Location> mLastLocation = new HashMap<String, Location>();
+
+ // all providers that operate over proxy, for authorizing incoming location
+ private final ArrayList<LocationProviderProxy> mProxyProviders =
+ new ArrayList<LocationProviderProxy>();
+
+ public LocationManagerService(Context context) {
+ super();
+ mContext = context;
+
+ if (D) Log.d(TAG, "Constructed");
- GeofenceManager mGeofenceManager;
+ // most startup is deferred until systemReady()
+ }
- // Last known location for each provider
- private HashMap<String,Location> mLastKnownLocation =
- new HashMap<String,Location>();
+ public void systemReady() {
+ Thread thread = new Thread(null, this, THREAD_NAME);
+ thread.start();
+ }
- private int mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
+ @Override
+ public void run() {
+ Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
+ Looper.prepare();
+ mLocationHandler = new LocationWorkerHandler();
+ init();
+ Looper.loop();
+ }
- // for Settings change notification
- private ContentQueryMap mSettings;
+ private void init() {
+ if (D) Log.d(TAG, "init()");
+
+ PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
+ mPackageManager = mContext.getPackageManager();
+
+ synchronized (mLock) {
+ loadProvidersLocked();
+ }
+ mGeofenceManager = new GeofenceManager(mContext);
+
+ // Register for Network (Wifi or Mobile) updates
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
+
+ // listen for settings changes
+ ContentResolver resolver = mContext.getContentResolver();
+ Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
+ "(" + NameValueTable.NAME + "=?)",
+ new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED}, null);
+ ContentQueryMap query = new ContentQueryMap(settingsCursor, NameValueTable.NAME, true,
+ mLocationHandler);
+ settingsCursor.close();
+ query.addObserver(this);
+ mPackageMonitor.register(mContext, Looper.myLooper(), true);
+ }
+
+ private void loadProvidersLocked() {
+ if (GpsLocationProvider.isSupported()) {
+ // Create a gps location provider
+ GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
+ mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
+ mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
+ addProviderLocked(gpsProvider);
+ mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider);
+ }
+
+ // create a passive location provider, which is always enabled
+ PassiveProvider passiveProvider = new PassiveProvider(this);
+ addProviderLocked(passiveProvider);
+ mEnabledProviders.add(passiveProvider.getName());
+ mPassiveProvider = passiveProvider;
+
+ /*
+ Load package name(s) containing location provider support.
+ These packages can contain services implementing location providers:
+ Geocoder Provider, Network Location Provider, and
+ Fused Location Provider. They will each be searched for
+ service components implementing these providers.
+ The location framework also has support for installation
+ of new location providers at run-time. The new package does not
+ have to be explicitly listed here, however it must have a signature
+ that matches the signature of at least one package on this list.
+ */
+ Resources resources = mContext.getResources();
+ ArrayList<String> providerPackageNames = new ArrayList<String>();
+ String[] pkgs1 = resources.getStringArray(
+ com.android.internal.R.array.config_locationProviderPackageNames);
+ String[] pkgs2 = resources.getStringArray(
+ com.android.internal.R.array.config_overlay_locationProviderPackageNames);
+ if (D) Log.d(TAG, "built-in location providers: " + Arrays.toString(pkgs1));
+ if (D) Log.d(TAG, "overlay location providers: " + Arrays.toString(pkgs2));
+ if (pkgs1 != null) providerPackageNames.addAll(Arrays.asList(pkgs1));
+ if (pkgs2 != null) providerPackageNames.addAll(Arrays.asList(pkgs2));
+
+ // bind to network provider
+ LocationProviderProxy networkProvider = LocationProviderProxy.createAndBind(
+ mContext,
+ LocationManager.NETWORK_PROVIDER,
+ NETWORK_LOCATION_SERVICE_ACTION,
+ providerPackageNames, mLocationHandler);
+ if (networkProvider != null) {
+ mRealProviders.put(LocationManager.NETWORK_PROVIDER, networkProvider);
+ mProxyProviders.add(networkProvider);
+ addProviderLocked(networkProvider);
+ } else {
+ Slog.w(TAG, "no network location provider found");
+ }
+
+ // bind to fused provider
+ LocationProviderProxy fusedLocationProvider = LocationProviderProxy.createAndBind(
+ mContext,
+ LocationManager.FUSED_PROVIDER,
+ FUSED_LOCATION_SERVICE_ACTION,
+ providerPackageNames, mLocationHandler);
+ if (fusedLocationProvider != null) {
+ addProviderLocked(fusedLocationProvider);
+ mProxyProviders.add(fusedLocationProvider);
+ mEnabledProviders.add(fusedLocationProvider.getName());
+ } else {
+ Slog.e(TAG, "no fused location provider found",
+ new IllegalStateException("Location service needs a fused location provider"));
+ }
+
+ // bind to geocoder provider
+ mGeocodeProvider = GeocoderProxy.createAndBind(mContext, providerPackageNames);
+ if (mGeocodeProvider == null) {
+ Slog.e(TAG, "no geocoder provider found");
+ }
+
+ updateProvidersLocked();
+ }
/**
* A wrapper class holding either an ILocationListener or a PendingIntent to receive
* location updates.
*/
private final class Receiver implements IBinder.DeathRecipient, PendingIntent.OnFinished {
+ final int mUid; // uid of receiver
+ final int mPid; // pid of receiver
+ final String mPackageName; // package name of receiver
+ final String mPermission; // best permission that receiver has
+
final ILocationListener mListener;
final PendingIntent mPendingIntent;
final Object mKey;
+
final HashMap<String,UpdateRecord> mUpdateRecords = new HashMap<String,UpdateRecord>();
int mPendingBroadcasts;
- String mRequiredPermissions;
- Receiver(ILocationListener listener) {
+ Receiver(ILocationListener listener, PendingIntent intent, int pid, int uid,
+ String packageName) {
mListener = listener;
- mPendingIntent = null;
- mKey = listener.asBinder();
- }
-
- Receiver(PendingIntent intent) {
mPendingIntent = intent;
- mListener = null;
- mKey = intent;
+ if (listener != null) {
+ mKey = listener.asBinder();
+ } else {
+ mKey = intent;
+ }
+ mPermission = checkPermission();
+ mUid = uid;
+ mPid = pid;
+ mPackageName = packageName;
}
@Override
public boolean equals(Object otherObj) {
if (otherObj instanceof Receiver) {
- return mKey.equals(
- ((Receiver)otherObj).mKey);
+ return mKey.equals(((Receiver)otherObj).mKey);
}
return false;
}
@@ -223,18 +362,19 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public String toString() {
- String result;
+ StringBuilder s = new StringBuilder();
+ s.append("Reciever[");
+ s.append(Integer.toHexString(System.identityHashCode(this)));
if (mListener != null) {
- result = "Receiver{"
- + Integer.toHexString(System.identityHashCode(this))
- + " Listener " + mKey + "}";
+ s.append(" listener");
} else {
- result = "Receiver{"
- + Integer.toHexString(System.identityHashCode(this))
- + " Intent " + mKey + "}";
+ s.append(" intent");
}
- result += "mUpdateRecords: " + mUpdateRecords;
- return result;
+ for (String p : mUpdateRecords.keySet()) {
+ s.append(" ").append(mUpdateRecords.get(p).toString());
+ }
+ s.append("]");
+ return s.toString();
}
public boolean isListener() {
@@ -275,7 +415,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, statusChanged, this, mLocationHandler,
- mRequiredPermissions);
+ mPermission);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -309,7 +449,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, locationChanged, this, mLocationHandler,
- mRequiredPermissions);
+ mPermission);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -347,7 +487,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
// synchronize to ensure incrementPendingBroadcastsLocked()
// is called before decrementPendingBroadcasts()
mPendingIntent.send(mContext, 0, providerIntent, this, mLocationHandler,
- mRequiredPermissions);
+ mPermission);
// call this after broadcasting so we do not increment
// if we throw an exeption.
incrementPendingBroadcastsLocked();
@@ -361,9 +501,8 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
@Override
public void binderDied() {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Location listener died");
- }
+ if (D) Log.d(TAG, "Location listener died");
+
synchronized (mLock) {
removeUpdatesLocked(this);
}
@@ -416,200 +555,25 @@ 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();
- }
+ /** Settings Observer callback */
+ @Override
+ public void update(Observable o, Object arg) {
+ synchronized (mLock) {
+ updateProvidersLocked();
}
}
- private void addProvider(LocationProviderInterface provider) {
+ private void addProviderLocked(LocationProviderInterface provider) {
mProviders.add(provider);
mProvidersByName.put(provider.getName(), provider);
}
- private void removeProvider(LocationProviderInterface provider) {
+ private void removeProviderLocked(LocationProviderInterface provider) {
+ provider.disable();
mProviders.remove(provider);
mProvidersByName.remove(provider.getName());
}
- private void loadProviders() {
- synchronized (mLock) {
- if (sProvidersLoaded) {
- return;
- }
-
- // Load providers
- loadProvidersLocked();
- sProvidersLoaded = true;
- }
- }
-
- private void loadProvidersLocked() {
- try {
- _loadProvidersLocked();
- } catch (Exception e) {
- Slog.e(TAG, "Exception loading providers:", e);
- }
- }
-
- private void _loadProvidersLocked() {
- // Attempt to load "real" providers first
- if (GpsLocationProvider.isSupported()) {
- // Create a gps location provider
- GpsLocationProvider gpsProvider = new GpsLocationProvider(mContext, this);
- mGpsStatusProvider = gpsProvider.getGpsStatusProvider();
- mNetInitiatedListener = gpsProvider.getNetInitiatedListener();
- addProvider(gpsProvider);
- mGpsLocationProvider = gpsProvider;
- }
-
- // create a passive location provider, which is always enabled
- PassiveProvider passiveProvider = new PassiveProvider(this);
- addProvider(passiveProvider);
- mEnabledProviders.add(passiveProvider.getName());
-
- // initialize external network location and geocoder services.
- // The initial value of mNetworkLocationProviderPackageName and
- // mGeocodeProviderPackageName is just used to determine what
- // signatures future mNetworkLocationProviderPackageName and
- // mGeocodeProviderPackageName packages must have. So alternate
- // providers can be installed under a different package name
- // so long as they have the same signature as the original
- // provider packages.
- if (mNetworkLocationProviderPackageName != null) {
- String packageName = findBestPackage(LocationProviderProxy.SERVICE_ACTION,
- mNetworkLocationProviderPackageName);
- if (packageName != null) {
- mNetworkLocationProvider = new LocationProviderProxy(mContext,
- LocationManager.NETWORK_PROVIDER,
- packageName, mLocationHandler);
- mNetworkLocationProviderPackageName = packageName;
- addProvider(mNetworkLocationProvider);
- }
- }
- if (mGeocodeProviderPackageName != null) {
- String packageName = findBestPackage(GeocoderProxy.SERVICE_ACTION,
- mGeocodeProviderPackageName);
- if (packageName != null) {
- mGeocodeProvider = new GeocoderProxy(mContext, packageName);
- mGeocodeProviderPackageName = packageName;
- }
- }
-
- updateProvidersLocked();
- }
-
- /**
- * Pick the best (network location provider or geocode provider) package.
- * The best package:
- * - implements serviceIntentName
- * - has signatures that match that of sigPackageName
- * - has the highest version value in a meta-data field in the service component
- */
- String findBestPackage(String serviceIntentName, String sigPackageName) {
- Intent intent = new Intent(serviceIntentName);
- List<ResolveInfo> infos = mPackageManager.queryIntentServices(intent,
- PackageManager.GET_META_DATA);
- if (infos == null) return null;
-
- int bestVersion = Integer.MIN_VALUE;
- String bestPackage = null;
- for (ResolveInfo info : infos) {
- String packageName = info.serviceInfo.packageName;
- // check signature
- if (mPackageManager.checkSignatures(packageName, sigPackageName) !=
- PackageManager.SIGNATURE_MATCH) {
- Slog.w(TAG, packageName + " implements " + serviceIntentName +
- " but its signatures don't match those in " + sigPackageName +
- ", ignoring");
- continue;
- }
- // read version
- int version = 0;
- if (info.serviceInfo.metaData != null) {
- version = info.serviceInfo.metaData.getInt("version", 0);
- }
- if (LOCAL_LOGV) Slog.v(TAG, packageName + " implements " + serviceIntentName +
- " with version " + version);
- if (version > bestVersion) {
- bestVersion = version;
- bestPackage = packageName;
- }
- }
-
- return bestPackage;
- }
-
- /**
- * @param context the context that the LocationManagerService runs in
- */
- public LocationManagerService(Context context) {
- super();
- mContext = context;
- Resources resources = context.getResources();
-
- mNetworkLocationProviderPackageName = resources.getString(
- com.android.internal.R.string.config_networkLocationProviderPackageName);
- mGeocodeProviderPackageName = resources.getString(
- com.android.internal.R.string.config_geocodeProviderPackageName);
-
- mPackageMonitor.register(context, null, true);
-
- if (LOCAL_LOGV) {
- Slog.v(TAG, "Constructed LocationManager Service");
- }
- }
-
- void systemReady() {
- // we defer starting up the service until the system is ready
- Thread thread = new Thread(null, this, "LocationManagerService");
- thread.start();
- }
-
- private void initialize() {
- // Create a wake lock, needs to be done before calling loadProviders() below
- PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
- mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
- mPackageManager = mContext.getPackageManager();
-
- // Load providers
- loadProviders();
-
- // Register for Network (Wifi or Mobile) updates
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ConnectivityManager.CONNECTIVITY_ACTION);
- // Register for Package Manager updates
- intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
- intentFilter.addAction(Intent.ACTION_PACKAGE_RESTARTED);
- intentFilter.addAction(Intent.ACTION_QUERY_PACKAGE_RESTART);
- mContext.registerReceiver(mBroadcastReceiver, intentFilter);
- IntentFilter sdFilter = new IntentFilter(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE);
- mContext.registerReceiver(mBroadcastReceiver, sdFilter);
-
- // listen for settings changes
- ContentResolver resolver = mContext.getContentResolver();
- Cursor settingsCursor = resolver.query(Settings.Secure.CONTENT_URI, null,
- "(" + NameValueTable.NAME + "=?)",
- new String[]{Settings.Secure.LOCATION_PROVIDERS_ALLOWED},
- null);
- 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();
- }
private boolean isAllowedBySettingsLocked(String provider) {
if (mEnabledProviders.contains(provider)) {
@@ -624,324 +588,131 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return Settings.Secure.isLocationProviderEnabled(resolver, provider);
}
- private String checkPermissionsSafe(String provider, String lastPermission) {
- if (LocationManager.GPS_PROVIDER.equals(provider)
- || LocationManager.PASSIVE_PROVIDER.equals(provider)) {
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Provider " + provider
- + " requires ACCESS_FINE_LOCATION permission");
- }
- return ACCESS_FINE_LOCATION;
- }
-
- // Assume any other provider requires the coarse or fine permission.
- if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
- == PackageManager.PERMISSION_GRANTED) {
- return ACCESS_FINE_LOCATION.equals(lastPermission)
- ? lastPermission : ACCESS_COARSE_LOCATION;
- }
- if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- == PackageManager.PERMISSION_GRANTED) {
+ /**
+ * Throw SecurityException if caller has neither COARSE or FINE.
+ * Otherwise, return the best permission.
+ */
+ private String checkPermission() {
+ if (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION) ==
+ PackageManager.PERMISSION_GRANTED) {
return ACCESS_FINE_LOCATION;
+ } else if (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION) ==
+ PackageManager.PERMISSION_GRANTED) {
+ return ACCESS_COARSE_LOCATION;
}
- throw new SecurityException("Provider " + provider
- + " requires ACCESS_FINE_LOCATION or ACCESS_COARSE_LOCATION permission");
- }
-
- private boolean isAllowedProviderSafe(String provider) {
- if ((LocationManager.GPS_PROVIDER.equals(provider)
- || LocationManager.PASSIVE_PROVIDER.equals(provider))
- && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- return false;
- }
- if (LocationManager.NETWORK_PROVIDER.equals(provider)
- && (mContext.checkCallingOrSelfPermission(ACCESS_FINE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)
- && (mContext.checkCallingOrSelfPermission(ACCESS_COARSE_LOCATION)
- != PackageManager.PERMISSION_GRANTED)) {
- return false;
- }
-
- return true;
+ throw new SecurityException("Location requires either ACCESS_COARSE_LOCATION or" +
+ "ACCESS_FINE_LOCATION permission");
}
+ /**
+ * Returns all providers by name, including passive, but excluding
+ * fused.
+ */
@Override
public List<String> getAllProviders() {
- try {
- synchronized (mLock) {
- return _getAllProvidersLocked();
+ checkPermission();
+
+ ArrayList<String> out;
+ synchronized (mLock) {
+ out = new ArrayList<String>(mProviders.size());
+ for (LocationProviderInterface provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
+ }
+ out.add(name);
}
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "getAllProviders got exception:", e);
- return null;
}
- }
- private List<String> _getAllProvidersLocked() {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "getAllProviders");
- }
- ArrayList<String> out = new ArrayList<String>(mProviders.size());
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- out.add(p.getName());
- }
+ if (D) Log.d(TAG, "getAllProviders()=" + out);
return out;
}
+ /**
+ * Return all providers by name, that match criteria and are optionally
+ * enabled.
+ * Can return passive provider, but never returns fused provider.
+ */
@Override
public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
- try {
- synchronized (mLock) {
- return _getProvidersLocked(criteria, enabledOnly);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "getProviders got exception:", e);
- return null;
- }
- }
+ checkPermission();
- private List<String> _getProvidersLocked(Criteria criteria, boolean enabledOnly) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "getProviders");
- }
- ArrayList<String> out = new ArrayList<String>(mProviders.size());
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- String name = p.getName();
- if (isAllowedProviderSafe(name)) {
+ ArrayList<String> out;
+ synchronized (mLock) {
+ out = new ArrayList<String>(mProviders.size());
+ for (LocationProviderInterface provider : mProviders) {
+ String name = provider.getName();
+ if (LocationManager.FUSED_PROVIDER.equals(name)) {
+ continue;
+ }
if (enabledOnly && !isAllowedBySettingsLocked(name)) {
continue;
}
- if (criteria != null && !p.meetsCriteria(criteria)) {
+ if (criteria != null && !LocationProvider.propertiesMeetCriteria(
+ name, provider.getProperties(), criteria)) {
continue;
}
out.add(name);
}
}
- return out;
- }
- /**
- * Returns the next looser power requirement, in the sequence:
- *
- * POWER_LOW -> POWER_MEDIUM -> POWER_HIGH -> NO_REQUIREMENT
- */
- private int nextPower(int power) {
- switch (power) {
- case Criteria.POWER_LOW:
- return Criteria.POWER_MEDIUM;
- case Criteria.POWER_MEDIUM:
- return Criteria.POWER_HIGH;
- case Criteria.POWER_HIGH:
- return Criteria.NO_REQUIREMENT;
- case Criteria.NO_REQUIREMENT:
- default:
- return Criteria.NO_REQUIREMENT;
- }
- }
-
- /**
- * Returns the next looser accuracy requirement, in the sequence:
- *
- * ACCURACY_FINE -> ACCURACY_APPROXIMATE-> NO_REQUIREMENT
- */
- private int nextAccuracy(int accuracy) {
- if (accuracy == Criteria.ACCURACY_FINE) {
- return Criteria.ACCURACY_COARSE;
- } else {
- return Criteria.NO_REQUIREMENT;
- }
- }
-
- private class LpPowerComparator implements Comparator<LocationProviderInterface> {
- @Override
- public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
- // Smaller is better
- return (l1.getPowerRequirement() - l2.getPowerRequirement());
- }
-
- public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
- return (l1.getPowerRequirement() == l2.getPowerRequirement());
- }
- }
-
- private class LpAccuracyComparator implements Comparator<LocationProviderInterface> {
- @Override
- public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
- // Smaller is better
- return (l1.getAccuracy() - l2.getAccuracy());
- }
-
- public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
- return (l1.getAccuracy() == l2.getAccuracy());
- }
- }
-
- private class LpCapabilityComparator implements Comparator<LocationProviderInterface> {
-
- private static final int ALTITUDE_SCORE = 4;
- private static final int BEARING_SCORE = 4;
- private static final int SPEED_SCORE = 4;
-
- private int score(LocationProviderInterface p) {
- return (p.supportsAltitude() ? ALTITUDE_SCORE : 0) +
- (p.supportsBearing() ? BEARING_SCORE : 0) +
- (p.supportsSpeed() ? SPEED_SCORE : 0);
- }
-
- @Override
- public int compare(LocationProviderInterface l1, LocationProviderInterface l2) {
- return (score(l2) - score(l1)); // Bigger is better
- }
-
- public boolean equals(LocationProviderInterface l1, LocationProviderInterface l2) {
- return (score(l1) == score(l2));
- }
- }
-
- private LocationProviderInterface best(List<String> providerNames) {
- ArrayList<LocationProviderInterface> providers;
- synchronized (mLock) {
- providers = new ArrayList<LocationProviderInterface>(providerNames.size());
- for (String name : providerNames) {
- providers.add(mProvidersByName.get(name));
- }
- }
-
- if (providers.size() < 2) {
- return providers.get(0);
- }
-
- // First, sort by power requirement
- Collections.sort(providers, new LpPowerComparator());
- int power = providers.get(0).getPowerRequirement();
- if (power < providers.get(1).getPowerRequirement()) {
- return providers.get(0);
- }
-
- int idx, size;
-
- ArrayList<LocationProviderInterface> tmp = new ArrayList<LocationProviderInterface>();
- idx = 0;
- size = providers.size();
- while ((idx < size) && (providers.get(idx).getPowerRequirement() == power)) {
- tmp.add(providers.get(idx));
- idx++;
- }
-
- // Next, sort by accuracy
- Collections.sort(tmp, new LpAccuracyComparator());
- int acc = tmp.get(0).getAccuracy();
- if (acc < tmp.get(1).getAccuracy()) {
- return tmp.get(0);
- }
-
- ArrayList<LocationProviderInterface> tmp2 = new ArrayList<LocationProviderInterface>();
- idx = 0;
- size = tmp.size();
- while ((idx < size) && (tmp.get(idx).getAccuracy() == acc)) {
- tmp2.add(tmp.get(idx));
- idx++;
- }
-
- // Finally, sort by capability "score"
- Collections.sort(tmp2, new LpCapabilityComparator());
- return tmp2.get(0);
+ if (D) Log.d(TAG, "getProviders()=" + out);
+ return out;
}
/**
- * Returns the name of the provider that best meets the given criteria. Only providers
- * that are permitted to be accessed by the calling activity will be
- * returned. If several providers meet the criteria, the one with the best
- * accuracy is returned. If no provider meets the criteria,
- * the criteria are loosened in the following sequence:
- *
- * <ul>
- * <li> power requirement
- * <li> accuracy
- * <li> bearing
- * <li> speed
- * <li> altitude
- * </ul>
- *
- * <p> Note that the requirement on monetary cost is not removed
- * in this process.
- *
- * @param criteria the criteria that need to be matched
- * @param enabledOnly if true then only a provider that is currently enabled is returned
- * @return name of the provider that best matches the requirements
+ * Return the name of the best provider given a Criteria object.
+ * This method has been deprecated from the public API,
+ * and the whole LoactionProvider (including #meetsCriteria)
+ * has been deprecated as well. So this method now uses
+ * some simplified logic.
*/
@Override
public String getBestProvider(Criteria criteria, boolean enabledOnly) {
- List<String> goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
+ String result = null;
+ checkPermission();
- // Make a copy of the criteria that we can modify
- criteria = new Criteria(criteria);
-
- // Loosen power requirement
- int power = criteria.getPowerRequirement();
- while (goodProviders.isEmpty() && (power != Criteria.NO_REQUIREMENT)) {
- power = nextPower(power);
- criteria.setPowerRequirement(power);
- goodProviders = getProviders(criteria, enabledOnly);
- }
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
-
- // Loosen accuracy requirement
- int accuracy = criteria.getAccuracy();
- while (goodProviders.isEmpty() && (accuracy != Criteria.NO_REQUIREMENT)) {
- accuracy = nextAccuracy(accuracy);
- criteria.setAccuracy(accuracy);
- goodProviders = getProviders(criteria, enabledOnly);
- }
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
+ List<String> providers = getProviders(criteria, enabledOnly);
+ if (providers.size() < 1) {
+ result = pickBest(providers);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return result;
}
-
- // Remove bearing requirement
- criteria.setBearingRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
+ providers = getProviders(null, enabledOnly);
+ if (providers.size() >= 1) {
+ result = pickBest(providers);
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return result;
}
- // Remove speed requirement
- criteria.setSpeedRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
- }
+ if (D) Log.d(TAG, "getBestProvider(" + criteria + ", " + enabledOnly + ")=" + result);
+ return null;
+ }
- // Remove altitude requirement
- criteria.setAltitudeRequired(false);
- goodProviders = getProviders(criteria, enabledOnly);
- if (!goodProviders.isEmpty()) {
- return best(goodProviders).getName();
+ private String pickBest(List<String> providers) {
+ if (providers.contains(LocationManager.NETWORK_PROVIDER)) {
+ return LocationManager.NETWORK_PROVIDER;
+ } else if (providers.contains(LocationManager.GPS_PROVIDER)) {
+ return LocationManager.GPS_PROVIDER;
+ } else {
+ return providers.get(0);
}
-
- return null;
}
@Override
public boolean providerMeetsCriteria(String provider, Criteria criteria) {
+ checkPermission();
+
LocationProviderInterface p = mProvidersByName.get(provider);
if (p == null) {
throw new IllegalArgumentException("provider=" + provider);
}
- return p.meetsCriteria(criteria);
+
+ boolean result = LocationProvider.propertiesMeetCriteria(
+ p.getName(), p.getProperties(), criteria);
+ if (D) Log.d(TAG, "providerMeetsCriteria(" + provider + ", " + criteria + ")=" + result);
+ return result;
}
private void updateProvidersLocked() {
@@ -968,16 +739,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
int listeners = 0;
LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return;
- }
+ if (p == null) return;
ArrayList<Receiver> deadReceivers = null;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records != null) {
final int N = records.size();
- for (int i=0; i<N; i++) {
+ for (int i = 0; i < N; i++) {
UpdateRecord record = records.get(i);
// Sends a notification message to the receiver
if (!record.mReceiver.callProviderEnabledLocked(provider, enabled)) {
@@ -991,7 +760,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
if (deadReceivers != null) {
- for (int i=deadReceivers.size()-1; i>=0; i--) {
+ for (int i = deadReceivers.size() - 1; i >= 0; i--) {
removeUpdatesLocked(deadReceivers.get(i));
}
}
@@ -999,59 +768,70 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
if (enabled) {
p.enable();
if (listeners > 0) {
- p.setMinTime(getMinTimeLocked(provider), mTmpWorkSource);
- p.enableLocationTracking(true);
+ applyRequirementsLocked(provider);
}
} else {
- p.enableLocationTracking(false);
p.disable();
}
}
- private long getMinTimeLocked(String provider) {
- long minTime = Long.MAX_VALUE;
+ private void applyRequirementsLocked(String provider) {
+ LocationProviderInterface p = mProvidersByName.get(provider);
+ if (p == null) return;
+
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- mTmpWorkSource.clear();
+ WorkSource worksource = new WorkSource();
+ ProviderRequest providerRequest = new ProviderRequest();
+
if (records != null) {
- for (int i=records.size()-1; i>=0; i--) {
- UpdateRecord ur = records.get(i);
- long curTime = ur.mMinTime;
- if (curTime < minTime) {
- minTime = curTime;
+ for (UpdateRecord record : records) {
+ LocationRequest locationRequest = record.mRequest;
+
+ if (providerRequest.locationRequests == null) {
+ providerRequest.locationRequests = new ArrayList<LocationRequest>();
+ }
+
+ providerRequest.locationRequests.add(locationRequest);
+ if (locationRequest.getInterval() < providerRequest.interval) {
+ providerRequest.reportLocation = true;
+ providerRequest.interval = locationRequest.getInterval();
}
}
- long inclTime = (minTime*3)/2;
- for (int i=records.size()-1; i>=0; i--) {
- UpdateRecord ur = records.get(i);
- if (ur.mMinTime <= inclTime) {
- mTmpWorkSource.add(ur.mUid);
+
+ if (providerRequest.reportLocation) {
+ // calculate who to blame for power
+ // This is somewhat arbitrary. We pick a threshold interval
+ // that is slightly higher that the minimum interval, and
+ // spread the blame across all applications with a request
+ // under that threshold.
+ long thresholdInterval = (providerRequest.interval + 1000) * 3 / 2;
+ for (UpdateRecord record : records) {
+ LocationRequest locationRequest = record.mRequest;
+ if (locationRequest.getInterval() <= thresholdInterval) {
+ worksource.add(record.mReceiver.mUid);
+ }
}
}
}
- return minTime;
+
+ if (D) Log.d(TAG, "provider request: " + provider + " " + providerRequest);
+ p.setRequest(providerRequest, worksource);
}
private class UpdateRecord {
final String mProvider;
+ final LocationRequest mRequest;
final Receiver mReceiver;
- final long mMinTime;
- final float mMinDistance;
- final boolean mSingleShot;
- final int mUid;
Location mLastFixBroadcast;
long mLastStatusBroadcast;
/**
* Note: must be constructed with lock held.
*/
- UpdateRecord(String provider, long minTime, float minDistance, boolean singleShot,
- Receiver receiver, int uid) {
+ UpdateRecord(String provider, LocationRequest request, Receiver receiver) {
mProvider = provider;
+ mRequest = request;
mReceiver = receiver;
- mMinTime = minTime;
- mMinDistance = minDistance;
- mSingleShot = singleShot;
- mUid = uid;
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
if (records == null) {
@@ -1067,45 +847,49 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
* Method to be called when a record will no longer be used. Calling this multiple times
* must have the same effect as calling it once.
*/
- void disposeLocked() {
- ArrayList<UpdateRecord> records = mRecordsByProvider.get(this.mProvider);
- if (records != null) {
- records.remove(this);
+ void disposeLocked(boolean removeReceiver) {
+ // remove from mRecordsByProvider
+ ArrayList<UpdateRecord> globalRecords = mRecordsByProvider.get(this.mProvider);
+ if (globalRecords != null) {
+ globalRecords.remove(this);
+ }
+
+ if (!removeReceiver) return; // the caller will handle the rest
+
+ // remove from Receiver#mUpdateRecords
+ HashMap<String, UpdateRecord> receiverRecords = mReceiver.mUpdateRecords;
+ if (receiverRecords != null) {
+ receiverRecords.remove(this.mProvider);
+
+ // and also remove the Receiver if it has no more update records
+ if (removeReceiver && receiverRecords.size() == 0) {
+ removeUpdatesLocked(mReceiver);
+ }
}
}
@Override
public String toString() {
- return "UpdateRecord{"
- + Integer.toHexString(System.identityHashCode(this))
- + " mProvider: " + mProvider + " mUid: " + mUid + "}";
- }
-
- void dump(PrintWriter pw, String prefix) {
- pw.println(prefix + this);
- pw.println(prefix + "mProvider=" + mProvider + " mReceiver=" + mReceiver);
- pw.println(prefix + "mMinTime=" + mMinTime + " mMinDistance=" + mMinDistance);
- pw.println(prefix + "mSingleShot=" + mSingleShot);
- pw.println(prefix + "mUid=" + mUid);
- pw.println(prefix + "mLastFixBroadcast:");
- if (mLastFixBroadcast != null) {
- mLastFixBroadcast.dump(new PrintWriterPrinter(pw), prefix + " ");
- }
- pw.println(prefix + "mLastStatusBroadcast=" + mLastStatusBroadcast);
+ StringBuilder s = new StringBuilder();
+ s.append("UpdateRecord[");
+ s.append(mProvider);
+ s.append(' ').append(mReceiver.mPackageName).append('(');
+ s.append(mReceiver.mUid).append(')');
+ s.append(' ').append(mRequest);
+ s.append(']');
+ return s.toString();
}
}
- private Receiver getReceiver(ILocationListener listener) {
+ private Receiver getReceiver(ILocationListener listener, int pid, int uid, String packageName) {
IBinder binder = listener.asBinder();
Receiver receiver = mReceivers.get(binder);
if (receiver == null) {
- receiver = new Receiver(listener);
+ receiver = new Receiver(listener, null, pid, uid, packageName);
mReceivers.put(binder, receiver);
try {
- if (receiver.isListener()) {
- receiver.getListener().asBinder().linkToDeath(receiver, 0);
- }
+ receiver.getListener().asBinder().linkToDeath(receiver, 0);
} catch (RemoteException e) {
Slog.e(TAG, "linkToDeath failed:", e);
return null;
@@ -1114,58 +898,29 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
return receiver;
}
- private Receiver getReceiver(PendingIntent intent) {
+ private Receiver getReceiver(PendingIntent intent, int pid, int uid, String packageName) {
Receiver receiver = mReceivers.get(intent);
if (receiver == null) {
- receiver = new Receiver(intent);
+ receiver = new Receiver(null, intent, pid, uid, packageName);
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;
- }
- }
- }
- return false;
- }
+ private String checkPermissionAndRequest(LocationRequest request) {
+ String perm = checkPermission();
- @Override
- public void requestLocationUpdates(String provider, Criteria criteria,
- long minTime, float minDistance, boolean singleShot, ILocationListener listener) {
- if (criteria != null) {
- // FIXME - should we consider using multiple providers simultaneously
- // rather than only the best one?
- // Should we do anything different for single shot fixes?
- provider = getBestProvider(criteria, true);
- if (provider == null) {
- throw new IllegalArgumentException("no providers found for criteria");
- }
- }
- try {
- synchronized (mLock) {
- requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
- getReceiver(listener));
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "requestUpdates got exception:", e);
+ if (ACCESS_COARSE_LOCATION.equals(perm)) {
+ request.applyCoarsePermissionRestrictions();
}
+ return perm;
}
- void validatePackageName(int uid, String packageName) {
+ private void checkPackageName(String packageName) {
if (packageName == null) {
- throw new SecurityException("packageName cannot be null");
+ throw new SecurityException("invalid package name: " + packageName);
}
+ int uid = Binder.getCallingUid();
String[] packages = mPackageManager.getPackagesForUid(uid);
if (packages == null) {
throw new SecurityException("invalid UID " + uid);
@@ -1173,202 +928,188 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
for (String pkg : packages) {
if (packageName.equals(pkg)) return;
}
- throw new SecurityException("invalid package name");
+ throw new SecurityException("invalid package name: " + packageName);
}
- void validatePendingIntent(PendingIntent intent) {
- if (intent.isTargetedToPackage()) {
- return;
+ private void checkPendingIntent(PendingIntent intent) {
+ if (intent == null) {
+ throw new IllegalArgumentException("invalid pending intent: " + intent);
}
- Slog.i(TAG, "Given Intent does not require a specific package: "
- + intent);
- // XXX we should really throw a security exception, if the caller's
- // targetSdkVersion is high enough.
- //throw new SecurityException("Given Intent does not require a specific package: "
- // + intent);
}
- @Override
- public void requestLocationUpdatesPI(String provider, Criteria criteria,
- long minTime, float minDistance, boolean singleShot, PendingIntent intent) {
- validatePendingIntent(intent);
- if (criteria != null) {
- // FIXME - should we consider using multiple providers simultaneously
- // rather than only the best one?
- // Should we do anything different for single shot fixes?
- provider = getBestProvider(criteria, true);
- if (provider == null) {
- throw new IllegalArgumentException("no providers found for criteria");
- }
- }
- try {
- synchronized (mLock) {
- requestLocationUpdatesLocked(provider, minTime, minDistance, singleShot,
- getReceiver(intent));
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "requestUpdates got exception:", e);
+ private Receiver checkListenerOrIntent(ILocationListener listener, PendingIntent intent,
+ int pid, int uid, String packageName) {
+ if (intent == null && listener == null) {
+ throw new IllegalArgumentException("need eiter listener or intent");
+ } else if (intent != null && listener != null) {
+ throw new IllegalArgumentException("cannot register both listener and intent");
+ } else if (intent != null) {
+ checkPendingIntent(intent);
+ return getReceiver(intent, pid, uid, packageName);
+ } else {
+ return getReceiver(listener, pid, uid, packageName);
}
}
- private void requestLocationUpdatesLocked(String provider, long minTime, float minDistance,
- boolean singleShot, Receiver receiver) {
+ @Override
+ public void requestLocationUpdates(LocationRequest request, ILocationListener listener,
+ PendingIntent intent, String packageName) {
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ checkPackageName(packageName);
+ checkPermissionAndRequest(request);
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- throw new IllegalArgumentException("requested provider " + provider +
- " doesn't exisit");
- }
- receiver.mRequiredPermissions = checkPermissionsSafe(provider,
- receiver.mRequiredPermissions);
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ Receiver recevier = checkListenerOrIntent(listener, intent, pid, uid, packageName);
- // so wakelock calls will succeed
- final int callingPid = Binder.getCallingPid();
- final int callingUid = Binder.getCallingUid();
- boolean newUid = !providerHasListener(provider, callingUid, null);
+ // so wakelock calls will succeed (not totally sure this is still needed)
long identity = Binder.clearCallingIdentity();
try {
- UpdateRecord r = new UpdateRecord(provider, minTime, minDistance, singleShot,
- receiver, callingUid);
- UpdateRecord oldRecord = receiver.mUpdateRecords.put(provider, r);
- if (oldRecord != null) {
- oldRecord.disposeLocked();
- }
-
- if (newUid) {
- p.addListener(callingUid);
- }
-
- boolean isProviderEnabled = isAllowedBySettingsLocked(provider);
- if (isProviderEnabled) {
- long minTimeForProvider = getMinTimeLocked(provider);
- Slog.i(TAG, "request " + provider + " (pid " + callingPid + ") " + minTime +
- " " + minTimeForProvider + (singleShot ? " (singleshot)" : ""));
- p.setMinTime(minTimeForProvider, mTmpWorkSource);
- // try requesting single shot if singleShot is true, and fall back to
- // regular location tracking if requestSingleShotFix() is not supported
- if (!singleShot || !p.requestSingleShotFix()) {
- p.enableLocationTracking(true);
- }
- } else {
- // Notify the listener that updates are currently disabled
- receiver.callProviderEnabledLocked(provider, false);
- }
- if (LOCAL_LOGV) {
- Slog.v(TAG, "_requestLocationUpdates: provider = " + provider + " listener = " + receiver);
+ synchronized (mLock) {
+ requestLocationUpdatesLocked(request, recevier, pid, uid, packageName);
}
} finally {
Binder.restoreCallingIdentity(identity);
}
}
- @Override
- public void removeUpdates(ILocationListener listener) {
- try {
- synchronized (mLock) {
- removeUpdatesLocked(getReceiver(listener));
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "removeUpdates got exception:", e);
+ private void requestLocationUpdatesLocked(LocationRequest request, Receiver receiver,
+ int pid, int uid, String packageName) {
+ // Figure out the provider. Either its explicitly request (legacy use cases), or
+ // use the fused provider
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ String name = request.getProvider();
+ if (name == null) name = LocationManager.FUSED_PROVIDER;
+ LocationProviderInterface provider = mProvidersByName.get(name);
+ if (provider == null) {
+ throw new IllegalArgumentException("provider doesn't exisit: " + provider);
+ }
+
+ Log.i(TAG, "request " + Integer.toHexString(System.identityHashCode(receiver)) + " " +
+ name + " " + request + " from " + packageName + "(" + uid + ")");
+
+ UpdateRecord record = new UpdateRecord(name, request, receiver);
+ UpdateRecord oldRecord = receiver.mUpdateRecords.put(name, record);
+ if (oldRecord != null) {
+ oldRecord.disposeLocked(false);
+ }
+
+ boolean isProviderEnabled = isAllowedBySettingsLocked(name);
+ if (isProviderEnabled) {
+ applyRequirementsLocked(name);
+ } else {
+ // Notify the listener that updates are currently disabled
+ receiver.callProviderEnabledLocked(name, false);
}
}
@Override
- public void removeUpdatesPI(PendingIntent intent) {
+ public void removeUpdates(ILocationListener listener, PendingIntent intent,
+ String packageName) {
+ checkPackageName(packageName);
+ checkPermission();
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ Receiver receiver = checkListenerOrIntent(listener, intent, pid, uid, packageName);
+
+ // so wakelock calls will succeed (not totally sure this is still needed)
+ long identity = Binder.clearCallingIdentity();
try {
synchronized (mLock) {
- removeUpdatesLocked(getReceiver(intent));
+ removeUpdatesLocked(receiver);
}
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "removeUpdates got exception:", e);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
}
}
private void removeUpdatesLocked(Receiver receiver) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "_removeUpdates: listener = " + receiver);
- }
+ Log.i(TAG, "remove " + Integer.toHexString(System.identityHashCode(receiver)));
- // so wakelock calls will succeed
- final int callingPid = Binder.getCallingPid();
- final int callingUid = Binder.getCallingUid();
- long identity = Binder.clearCallingIdentity();
- try {
- if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
- receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
- synchronized(receiver) {
- if(receiver.mPendingBroadcasts > 0) {
- decrementPendingBroadcasts();
- receiver.mPendingBroadcasts = 0;
- }
+ if (mReceivers.remove(receiver.mKey) != null && receiver.isListener()) {
+ receiver.getListener().asBinder().unlinkToDeath(receiver, 0);
+ synchronized (receiver) {
+ if (receiver.mPendingBroadcasts > 0) {
+ decrementPendingBroadcasts();
+ receiver.mPendingBroadcasts = 0;
}
}
+ }
- // Record which providers were associated with this listener
- HashSet<String> providers = new HashSet<String>();
- HashMap<String,UpdateRecord> oldRecords = receiver.mUpdateRecords;
- if (oldRecords != null) {
- // Call dispose() on the obsolete update records.
- for (UpdateRecord record : oldRecords.values()) {
- if (!providerHasListener(record.mProvider, callingUid, receiver)) {
- LocationProviderInterface p = mProvidersByName.get(record.mProvider);
- if (p != null) {
- p.removeListener(callingUid);
- }
- }
- record.disposeLocked();
- }
- // Accumulate providers
- providers.addAll(oldRecords.keySet());
+ // Record which providers were associated with this listener
+ HashSet<String> providers = new HashSet<String>();
+ HashMap<String, UpdateRecord> oldRecords = receiver.mUpdateRecords;
+ if (oldRecords != null) {
+ // Call dispose() on the obsolete update records.
+ for (UpdateRecord record : oldRecords.values()) {
+ record.disposeLocked(false);
}
+ // Accumulate providers
+ providers.addAll(oldRecords.keySet());
+ }
- // See if the providers associated with this listener have any
- // other listeners; if one does, inform it of the new smallest minTime
- // value; if one does not, disable location tracking for it
- for (String provider : providers) {
- // If provider is already disabled, don't need to do anything
- if (!isAllowedBySettingsLocked(provider)) {
- continue;
- }
+ // update provider
+ for (String provider : providers) {
+ // If provider is already disabled, don't need to do anything
+ if (!isAllowedBySettingsLocked(provider)) {
+ continue;
+ }
- boolean hasOtherListener = false;
- ArrayList<UpdateRecord> recordsForProvider = mRecordsByProvider.get(provider);
- if (recordsForProvider != null && recordsForProvider.size() > 0) {
- hasOtherListener = true;
- }
+ applyRequirementsLocked(provider);
+ }
+ }
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p != null) {
- if (hasOtherListener) {
- long minTime = getMinTimeLocked(provider);
- Slog.i(TAG, "remove " + provider + " (pid " + callingPid +
- "), next minTime = " + minTime);
- p.setMinTime(minTime, mTmpWorkSource);
- } else {
- Slog.i(TAG, "remove " + provider + " (pid " + callingPid +
- "), disabled");
- p.enableLocationTracking(false);
- }
- }
+ @Override
+ public Location getLastLocation(LocationRequest request) {
+ if (D) Log.d(TAG, "getLastLocation: " + request);
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ String perm = checkPermissionAndRequest(request);
+
+ synchronized (mLock) {
+ // Figure out the provider. Either its explicitly request (deprecated API's),
+ // or use the fused provider
+ String name = request.getProvider();
+ if (name == null) name = LocationManager.FUSED_PROVIDER;
+ LocationProviderInterface provider = mProvidersByName.get(name);
+ if (provider == null) return null;
+
+ if (!isAllowedBySettingsLocked(name)) return null;
+
+ Location location = mLastLocation.get(name);
+ if (ACCESS_FINE_LOCATION.equals(perm)) {
+ return location;
+ } else {
+ return getCoarseLocationExtra(location);
}
- } finally {
- Binder.restoreCallingIdentity(identity);
}
}
@Override
+ public void requestGeofence(LocationRequest request, Geofence geofence, PendingIntent intent,
+ String packageName) {
+ if (request == null) request = DEFAULT_LOCATION_REQUEST;
+ checkPermissionAndRequest(request);
+ checkPendingIntent(intent);
+ checkPackageName(packageName);
+
+ if (D) Log.d(TAG, "requestGeofence: " + request + " " + geofence + " " + intent);
+
+ mGeofenceManager.addFence(request, geofence, intent, Binder.getCallingUid(), packageName);
+ }
+
+ @Override
+ public void removeGeofence(Geofence geofence, PendingIntent intent, String packageName) {
+ checkPermission();
+ checkPendingIntent(intent);
+ checkPackageName(packageName);
+
+ if (D) Log.d(TAG, "removeGeofence: " + geofence + " " + intent);
+
+ mGeofenceManager.removeFence(geofence, intent);
+ }
+
+
+ @Override
public boolean addGpsStatusListener(IGpsStatusListener listener) {
if (mGpsStatusProvider == null) {
return false;
@@ -1405,8 +1146,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
throw new NullPointerException();
}
- // first check for permission to the provider
- checkPermissionsSafe(provider, null);
+ checkPermission();
// and check for ACCESS_LOCATION_EXTRA_COMMANDS
if ((mContext.checkCallingOrSelfPermission(ACCESS_LOCATION_EXTRA_COMMANDS)
!= PackageManager.PERMISSION_GRANTED)) {
@@ -1415,176 +1155,113 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (mLock) {
LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return false;
- }
+ if (p == null) return false;
return p.sendExtraCommand(command, extras);
}
}
@Override
- public boolean sendNiResponse(int notifId, int userResponse)
- {
+ public boolean sendNiResponse(int notifId, int userResponse) {
if (Binder.getCallingUid() != Process.myUid()) {
throw new SecurityException(
"calling sendNiResponse from outside of the system is not allowed");
}
try {
return mNetInitiatedListener.sendNiResponse(notifId, userResponse);
- }
- catch (RemoteException e)
- {
+ } catch (RemoteException e) {
Slog.e(TAG, "RemoteException in LocationManagerService.sendNiResponse");
return false;
}
}
- @Override
- public void addProximityAlert(double latitude, double longitude,
- float radius, long expiration, PendingIntent intent, String packageName) {
- validatePendingIntent(intent);
- validatePackageName(Binder.getCallingUid(), packageName);
-
- // Require ability to access all providers for now
- if (!isAllowedProviderSafe(LocationManager.GPS_PROVIDER) ||
- !isAllowedProviderSafe(LocationManager.NETWORK_PROVIDER)) {
- throw new SecurityException("Requires ACCESS_FINE_LOCATION permission");
- }
-
- if (LOCAL_LOGV) Slog.v(TAG, "addProximityAlert: lat=" + latitude + ", long=" + longitude +
- ", radius=" + radius + ", exp=" + expiration + ", intent = " + intent);
-
- mGeofenceManager.addFence(latitude, longitude, radius, expiration, intent,
- Binder.getCallingUid(), packageName);
- }
-
- @Override
- public void removeProximityAlert(PendingIntent intent) {
- if (intent == null) throw new NullPointerException("pending intent is null");
-
- if (LOCAL_LOGV) Slog.v(TAG, "removeProximityAlert: intent = " + intent);
-
- 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) {
- return _getProviderInfoLocked(provider);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (IllegalArgumentException iae) {
- throw iae;
- } catch (Exception e) {
- Slog.e(TAG, "_getProviderInfo got exception:", e);
- return null;
- }
- }
+ public ProviderProperties getProviderProperties(String provider) {
+ checkPermission();
- private Bundle _getProviderInfoLocked(String provider) {
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return null;
+ LocationProviderInterface p;
+ synchronized (mLock) {
+ p = mProvidersByName.get(provider);
}
- checkPermissionsSafe(provider, null);
-
- Bundle b = new Bundle();
- b.putBoolean("network", p.requiresNetwork());
- b.putBoolean("satellite", p.requiresSatellite());
- b.putBoolean("cell", p.requiresCell());
- b.putBoolean("cost", p.hasMonetaryCost());
- b.putBoolean("altitude", p.supportsAltitude());
- b.putBoolean("speed", p.supportsSpeed());
- b.putBoolean("bearing", p.supportsBearing());
- b.putInt("power", p.getPowerRequirement());
- b.putInt("accuracy", p.getAccuracy());
-
- return b;
+ if (p == null) return null;
+ return p.getProperties();
}
@Override
public boolean isProviderEnabled(String provider) {
- try {
- synchronized (mLock) {
- return _isProviderEnabledLocked(provider);
- }
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "isProviderEnabled got exception:", e);
- return false;
+ checkPermission();
+ if (LocationManager.FUSED_PROVIDER.equals(provider)) return false;
+
+ synchronized (mLock) {
+ LocationProviderInterface p = mProvidersByName.get(provider);
+ if (p == null) return false;
+
+ return isAllowedBySettingsLocked(provider);
}
}
- @Override
- public void reportLocation(Location location, boolean passive) {
+ private void checkCallerIsProvider() {
if (mContext.checkCallingOrSelfPermission(INSTALL_LOCATION_PROVIDER)
- != PackageManager.PERMISSION_GRANTED) {
- throw new SecurityException("Requires INSTALL_LOCATION_PROVIDER permission");
- }
-
- if (!location.isComplete()) {
- Log.w(TAG, "Dropping incomplete location: " + location);
+ == PackageManager.PERMISSION_GRANTED) {
return;
}
- mLocationHandler.removeMessages(MESSAGE_LOCATION_CHANGED, location);
- Message m = Message.obtain(mLocationHandler, MESSAGE_LOCATION_CHANGED, location);
- m.arg1 = (passive ? 1 : 0);
- mLocationHandler.sendMessageAtFrontOfQueue(m);
- }
+ // Previously we only used the INSTALL_LOCATION_PROVIDER
+ // check. But that is system or signature
+ // protection level which is not flexible enough for
+ // providers installed oustide the system image. So
+ // also allow providers with a UID matching the
+ // currently bound package name
- private boolean _isProviderEnabledLocked(String provider) {
- checkPermissionsSafe(provider, null);
+ int uid = Binder.getCallingUid();
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return false;
+ if (mGeocodeProvider != null) {
+ if (doesPackageHaveUid(uid, mGeocodeProvider.getConnectedPackageName())) return;
+ }
+ for (LocationProviderProxy proxy : mProxyProviders) {
+ if (doesPackageHaveUid(uid, proxy.getConnectedPackageName())) return;
}
- return isAllowedBySettingsLocked(provider);
+ throw new SecurityException("need INSTALL_LOCATION_PROVIDER permission, " +
+ "or UID of a currently bound location provider");
}
- @Override
- public Location getLastKnownLocation(String provider) {
- if (LOCAL_LOGV) {
- Slog.v(TAG, "getLastKnownLocation: " + provider);
+ private boolean doesPackageHaveUid(int uid, String packageName) {
+ if (packageName == null) {
+ return false;
}
try {
- synchronized (mLock) {
- return _getLastKnownLocationLocked(provider);
+ ApplicationInfo appInfo = mPackageManager.getApplicationInfo(packageName, 0);
+ if (appInfo.uid != uid) {
+ return false;
}
- } catch (SecurityException se) {
- throw se;
- } catch (Exception e) {
- Slog.e(TAG, "getLastKnownLocation got exception:", e);
- return null;
+ } catch (NameNotFoundException e) {
+ return false;
}
+ return true;
}
- private Location _getLastKnownLocationLocked(String provider) {
- checkPermissionsSafe(provider, null);
-
- LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return null;
- }
+ @Override
+ public void reportLocation(Location location, boolean passive) {
+ checkCallerIsProvider();
- if (!isAllowedBySettingsLocked(provider)) {
- return null;
+ if (!location.isComplete()) {
+ Log.w(TAG, "Dropping incomplete location: " + location);
+ return;
}
- return mLastKnownLocation.get(provider);
+ mLocationHandler.removeMessages(MSG_LOCATION_CHANGED, location);
+ Message m = Message.obtain(mLocationHandler, MSG_LOCATION_CHANGED, location);
+ m.arg1 = (passive ? 1 : 0);
+ mLocationHandler.sendMessageAtFrontOfQueue(m);
}
+
private static boolean shouldBroadcastSafe(Location loc, Location lastLoc, UpdateRecord record) {
// Always broadcast the first update
if (lastLoc == null) {
@@ -1592,14 +1269,14 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
// Check whether sufficient time has passed
- long minTime = record.mMinTime;
+ long minTime = record.mRequest.getFastestInterval();
long delta = (loc.getElapsedRealtimeNano() - lastLoc.getElapsedRealtimeNano()) / 1000000L;
- if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER) {
+ if (delta < minTime - MAX_PROVIDER_SCHEDULING_JITTER_MS) {
return false;
}
// Check whether sufficient distance has been traveled
- double minDistance = record.mMinDistance;
+ double minDistance = record.mRequest.getSmallestDisplacement();
if (minDistance > 0.0) {
if (loc.distanceTo(lastLoc) <= minDistance) {
return false;
@@ -1610,24 +1287,27 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
private void handleLocationChangedLocked(Location location, boolean passive) {
+ long now = SystemClock.elapsedRealtime();
String provider = (passive ? LocationManager.PASSIVE_PROVIDER : location.getProvider());
ArrayList<UpdateRecord> records = mRecordsByProvider.get(provider);
- if (records == null || records.size() == 0) {
- return;
- }
+ if (records == null || records.size() == 0) return;
LocationProviderInterface p = mProvidersByName.get(provider);
- if (p == null) {
- return;
+ if (p == null) return;
+
+ // Add the coarse location as an extra, if not already present
+ Location coarse = getCoarseLocationExtra(location);
+ if (coarse == null) {
+ coarse = addCoarseLocationExtra(location);
}
- // Update last known location for provider
- Location lastLocation = mLastKnownLocation.get(provider);
+ // Update last known locations
+ Location lastLocation = mLastLocation.get(provider);
if (lastLocation == null) {
- mLastKnownLocation.put(provider, new Location(location));
- } else {
- lastLocation.set(location);
+ lastLocation = new Location(provider);
+ mLastLocation.put(provider, lastLocation);
}
+ lastLocation.set(location);
// Fetch latest status update time
long newStatusUpdateTime = p.getStatusUpdateTime();
@@ -1637,13 +1317,17 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
int status = p.getStatus(extras);
ArrayList<Receiver> deadReceivers = null;
+ ArrayList<UpdateRecord> deadUpdateRecords = null;
// Broadcast location or status to all listeners
- final int N = records.size();
- for (int i=0; i<N; i++) {
- UpdateRecord r = records.get(i);
+ for (UpdateRecord r : records) {
Receiver receiver = r.mReceiver;
boolean receiverDead = false;
+ if (ACCESS_FINE_LOCATION.equals(receiver.mPermission)) {
+ location = lastLocation; // use fine location
+ } else {
+ location = coarse; // use coarse location
+ }
Location lastLoc = r.mLastFixBroadcast;
if ((lastLoc == null) || shouldBroadcastSafe(location, lastLoc, r)) {
@@ -1670,8 +1354,15 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
- // remove receiver if it is dead or we just processed a single shot request
- if (receiverDead || r.mSingleShot) {
+ // track expired records
+ if (r.mRequest.getNumUpdates() == 0 || r.mRequest.getExpireAt() < now) {
+ if (deadUpdateRecords == null) {
+ deadUpdateRecords = new ArrayList<UpdateRecord>();
+ }
+ deadUpdateRecords.add(r);
+ }
+ // track dead receivers
+ if (receiverDead) {
if (deadReceivers == null) {
deadReceivers = new ArrayList<Receiver>();
}
@@ -1681,162 +1372,71 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ // remove dead records and receivers outside the loop
if (deadReceivers != null) {
- for (int i=deadReceivers.size()-1; i>=0; i--) {
- removeUpdatesLocked(deadReceivers.get(i));
+ for (Receiver receiver : deadReceivers) {
+ removeUpdatesLocked(receiver);
+ }
+ }
+ if (deadUpdateRecords != null) {
+ for (UpdateRecord r : deadUpdateRecords) {
+ r.disposeLocked(true);
}
}
}
private class LocationWorkerHandler extends Handler {
-
@Override
public void handleMessage(Message msg) {
- try {
- if (msg.what == MESSAGE_LOCATION_CHANGED) {
- // log("LocationWorkerHandler: MESSAGE_LOCATION_CHANGED!");
-
- synchronized (mLock) {
- Location location = (Location) msg.obj;
- String provider = location.getProvider();
- boolean passive = (msg.arg1 == 1);
-
- if (!passive) {
- // notify other providers of the new location
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface p = mProviders.get(i);
- if (!provider.equals(p.getName())) {
- p.updateLocation(location);
- }
- }
- }
+ switch (msg.what) {
+ case MSG_LOCATION_CHANGED:
+ handleLocationChanged((Location) msg.obj, msg.arg1 == 1);
+ break;
+ }
+ }
+ }
- if (isAllowedBySettingsLocked(provider)) {
- handleLocationChangedLocked(location, passive);
- }
- }
- } else if (msg.what == MESSAGE_PACKAGE_UPDATED) {
- String packageName = (String) msg.obj;
-
- // reconnect to external providers if there is a better package
- if (mNetworkLocationProviderPackageName != null &&
- mPackageManager.resolveService(
- new Intent(LocationProviderProxy.SERVICE_ACTION)
- .setPackage(packageName), 0) != null) {
- // package implements service, perform full check
- String bestPackage = findBestPackage(
- LocationProviderProxy.SERVICE_ACTION,
- mNetworkLocationProviderPackageName);
- if (packageName.equals(bestPackage)) {
- mNetworkLocationProvider.reconnect(bestPackage);
- mNetworkLocationProviderPackageName = packageName;
- }
- }
- if (mGeocodeProviderPackageName != null &&
- mPackageManager.resolveService(
- new Intent(GeocoderProxy.SERVICE_ACTION)
- .setPackage(packageName), 0) != null) {
- // package implements service, perform full check
- String bestPackage = findBestPackage(
- GeocoderProxy.SERVICE_ACTION,
- mGeocodeProviderPackageName);
- if (packageName.equals(bestPackage)) {
- mGeocodeProvider.reconnect(bestPackage);
- mGeocodeProviderPackageName = packageName;
- }
- }
- }
- } catch (Exception e) {
- // Log, don't crash!
- Slog.e(TAG, "Exception in LocationWorkerHandler.handleMessage:", e);
+ private void handleLocationChanged(Location location, boolean passive) {
+ String provider = location.getProvider();
+
+ if (!passive) {
+ // notify passive provider of the new location
+ mPassiveProvider.updateLocation(location);
+ }
+
+ synchronized (mLock) {
+ if (isAllowedBySettingsLocked(provider)) {
+ handleLocationChangedLocked(location, passive);
}
}
}
- private final BroadcastReceiver mBroadcastReceiver = new BroadcastReceiver() {
+ private final PackageMonitor mPackageMonitor = new PackageMonitor() {
@Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- boolean queryRestart = action.equals(Intent.ACTION_QUERY_PACKAGE_RESTART);
- if (queryRestart
- || action.equals(Intent.ACTION_PACKAGE_REMOVED)
- || action.equals(Intent.ACTION_PACKAGE_RESTARTED)
- || action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
- synchronized (mLock) {
- int uidList[] = null;
- if (action.equals(Intent.ACTION_EXTERNAL_APPLICATIONS_UNAVAILABLE)) {
- uidList = intent.getIntArrayExtra(Intent.EXTRA_CHANGED_UID_LIST);
- } else {
- uidList = new int[]{intent.getIntExtra(Intent.EXTRA_UID, -1)};
- }
- if (uidList == null || uidList.length == 0) {
- return;
- }
- for (int uid : uidList) {
- if (uid >= 0) {
- ArrayList<Receiver> removedRecs = null;
- for (ArrayList<UpdateRecord> i : mRecordsByProvider.values()) {
- for (int j=i.size()-1; j>=0; j--) {
- UpdateRecord ur = i.get(j);
- if (ur.mReceiver.isPendingIntent() && ur.mUid == uid) {
- if (queryRestart) {
- setResultCode(Activity.RESULT_OK);
- return;
- }
- if (removedRecs == null) {
- removedRecs = new ArrayList<Receiver>();
- }
- if (!removedRecs.contains(ur.mReceiver)) {
- removedRecs.add(ur.mReceiver);
- }
- }
- }
- }
+ public void onPackageDisappeared(String packageName, int reason) {
+ // remove all receivers associated with this package name
+ synchronized (mLock) {
+ ArrayList<Receiver> deadReceivers = null;
+
+ for (Receiver receiver : mReceivers.values()) {
+ if (receiver.mPackageName.equals(packageName)) {
+ if (deadReceivers == null) {
+ deadReceivers = new ArrayList<Receiver>();
}
+ deadReceivers.add(receiver);
}
}
- } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
- boolean noConnectivity =
- intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
- if (!noConnectivity) {
- mNetworkState = LocationProvider.AVAILABLE;
- } else {
- mNetworkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
- }
-
- final NetworkInfo info = intent.getParcelableExtra(
- ConnectivityManager.EXTRA_NETWORK_INFO);
- // Notify location providers of current network state
- synchronized (mLock) {
- for (int i = mProviders.size() - 1; i >= 0; i--) {
- LocationProviderInterface provider = mProviders.get(i);
- if (provider.requiresNetwork()) {
- provider.updateNetworkState(mNetworkState, info);
- }
+ // perform removal outside of mReceivers loop
+ if (deadReceivers != null) {
+ for (Receiver receiver : deadReceivers) {
+ removeUpdatesLocked(receiver);
}
}
}
}
};
- private final PackageMonitor mPackageMonitor = new PackageMonitor() {
- @Override
- public void onPackageUpdateFinished(String packageName, int uid) {
- // Called by main thread; divert work to LocationWorker.
- Message.obtain(mLocationHandler, MESSAGE_PACKAGE_UPDATED, packageName).sendToTarget();
- }
- @Override
- public void onPackageAdded(String packageName, int uid) {
- // 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
private void incrementPendingBroadcasts() {
@@ -1922,9 +1522,7 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
@Override
- public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
- boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
- boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ public void addTestProvider(String name, ProviderProperties properties) {
checkMockPermissionsSafe();
if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
@@ -1933,25 +1531,21 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
long identity = Binder.clearCallingIdentity();
synchronized (mLock) {
- MockProvider provider = new MockProvider(name, this,
- requiresNetwork, requiresSatellite,
- requiresCell, hasMonetaryCost, supportsAltitude,
- supportsSpeed, supportsBearing, powerRequirement, accuracy);
+ MockProvider provider = new MockProvider(name, this, properties);
// remove the real provider if we are replacing GPS or network provider
if (LocationManager.GPS_PROVIDER.equals(name)
|| LocationManager.NETWORK_PROVIDER.equals(name)) {
LocationProviderInterface p = mProvidersByName.get(name);
if (p != null) {
- p.enableLocationTracking(false);
- removeProvider(p);
+ removeProviderLocked(p);
}
}
if (mProvidersByName.get(name) != null) {
throw new IllegalArgumentException("Provider \"" + name + "\" already exists");
}
- addProvider(provider);
+ addProviderLocked(provider);
mMockProviders.put(name, provider);
- mLastKnownLocation.put(name, null);
+ mLastLocation.put(name, null);
updateProvidersLocked();
}
Binder.restoreCallingIdentity(identity);
@@ -1966,17 +1560,15 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
throw new IllegalArgumentException("Provider \"" + provider + "\" unknown");
}
long identity = Binder.clearCallingIdentity();
- removeProvider(mProvidersByName.get(provider));
+ removeProviderLocked(mProvidersByName.get(provider));
mMockProviders.remove(mockProvider);
- // reinstall real provider if we were mocking GPS or network provider
- if (LocationManager.GPS_PROVIDER.equals(provider) &&
- mGpsLocationProvider != null) {
- addProvider(mGpsLocationProvider);
- } else if (LocationManager.NETWORK_PROVIDER.equals(provider) &&
- mNetworkLocationProvider != null) {
- addProvider(mNetworkLocationProvider);
- }
- mLastKnownLocation.put(provider, null);
+
+ // reinstate real provider if available
+ LocationProviderInterface realProvider = mRealProviders.get(provider);
+ if (realProvider != null) {
+ addProviderLocked(realProvider);
+ }
+ mLastLocation.put(provider, null);
updateProvidersLocked();
Binder.restoreCallingIdentity(identity);
}
@@ -2072,6 +1664,106 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
}
}
+ private static double wrapLatitude(double lat) {
+ if (lat > MAX_LATITUDE) lat = MAX_LATITUDE;
+ if (lat < -MAX_LATITUDE) lat = -MAX_LATITUDE;
+ return lat;
+ }
+
+ private static double wrapLongitude(double lon) {
+ if (lon >= 180.0) lon -= 360.0;
+ if (lon < -180.0) lon += 360.0;
+ return lon;
+ }
+
+ private static double distanceToDegreesLatitude(double distance) {
+ return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR;
+ }
+
+ /**
+ * Requires latitude since longitudinal distances change with distance from equator.
+ */
+ private static double distanceToDegreesLongitude(double distance, double lat) {
+ return distance / APPROXIMATE_METERS_PER_DEGREE_AT_EQUATOR / Math.cos(lat);
+ }
+
+ /**
+ * Fudge a location into a coarse location.
+ * <p>Add a random offset, then quantize the result (snap-to-grid).
+ * Random offsets alone can be low-passed pretty easily.
+ * Snap-to-grid on its own is excellent unless you are sitting on a
+ * grid boundary and bouncing between quantizations.
+ * The combination is quite hard to reverse engineer.
+ * <p>The random offset used is smaller than the goal accuracy
+ * ({@link #COARSE_ACCURACY_M}), in order to give relatively stable
+ * results after quantization.
+ */
+ private static Location createCoarse(Location fine) {
+ Location coarse = new Location(fine);
+
+ coarse.removeBearing();
+ coarse.removeSpeed();
+ coarse.removeAltitude();
+
+ double lat = coarse.getLatitude();
+ double lon = coarse.getLongitude();
+
+ // wrap
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ if (coarse.getAccuracy() < COARSE_ACCURACY_M / 2) {
+ // apply a random offset
+ double fudgeDistance = COARSE_ACCURACY_M / 2.0 - coarse.getAccuracy();
+ lat += (Math.random() - 0.5) * distanceToDegreesLatitude(fudgeDistance);
+ lon += (Math.random() - 0.5) * distanceToDegreesLongitude(fudgeDistance, lat);
+ }
+
+ // wrap
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ // quantize (snap-to-grid)
+ double latGranularity = distanceToDegreesLatitude(COARSE_ACCURACY_M);
+ double lonGranularity = distanceToDegreesLongitude(COARSE_ACCURACY_M, lat);
+ long latQuantized = Math.round(lat / latGranularity);
+ long lonQuantized = Math.round(lon / lonGranularity);
+ lat = latQuantized * latGranularity;
+ lon = lonQuantized * lonGranularity;
+
+ // wrap again
+ lat = wrapLatitude(lat);
+ lon = wrapLongitude(lon);
+
+ // apply
+ coarse.setLatitude(lat);
+ coarse.setLongitude(lon);
+ coarse.setAccuracy((float)COARSE_ACCURACY_M);
+
+ return coarse;
+ }
+
+
+ private static Location getCoarseLocationExtra(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras == null) return null;
+ Parcelable parcel = extras.getParcelable(EXTRA_COARSE_LOCATION);
+ if (parcel == null) return null;
+ if (!(parcel instanceof Location)) return null;
+ Location coarse = (Location) parcel;
+ if (coarse.getAccuracy() < COARSE_ACCURACY_M) return null;
+ return coarse;
+ }
+
+ private static Location addCoarseLocationExtra(Location location) {
+ Bundle extras = location.getExtras();
+ if (extras == null) extras = new Bundle();
+ Location coarse = createCoarse(location);
+ extras.putParcelable(EXTRA_COARSE_LOCATION, coarse);
+ location.setExtras(extras);
+ return coarse;
+ }
+
private void log(String log) {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Slog.d(TAG, log);
@@ -2090,36 +1782,26 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
synchronized (mLock) {
pw.println("Current Location Manager state:");
- pw.println(" sProvidersLoaded=" + sProvidersLoaded);
- pw.println(" Listeners:");
- int N = mReceivers.size();
- for (int i=0; i<N; i++) {
- pw.println(" " + mReceivers.get(i));
- }
pw.println(" Location Listeners:");
- 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, " ");
- }
+ for (Receiver receiver : mReceivers.values()) {
+ pw.println(" " + receiver);
}
pw.println(" Records by Provider:");
- for (Map.Entry<String, ArrayList<UpdateRecord>> i
- : mRecordsByProvider.entrySet()) {
- pw.println(" " + i.getKey() + ":");
- for (UpdateRecord j : i.getValue()) {
- pw.println(" " + j + ":");
- j.dump(pw, " ");
+ for (Map.Entry<String, ArrayList<UpdateRecord>> entry : mRecordsByProvider.entrySet()) {
+ pw.println(" " + entry.getKey() + ":");
+ for (UpdateRecord record : entry.getValue()) {
+ pw.println(" " + record);
}
}
pw.println(" Last Known Locations:");
- for (Map.Entry<String, Location> i
- : mLastKnownLocation.entrySet()) {
- pw.println(" " + i.getKey() + ":");
- i.getValue().dump(new PrintWriterPrinter(pw), " ");
+ for (Map.Entry<String, Location> entry : mLastLocation.entrySet()) {
+ String provider = entry.getKey();
+ Location location = entry.getValue();
+ pw.println(" " + provider + ": " + location);
}
+
mGeofenceManager.dump(pw);
+
if (mEnabledProviders.size() > 0) {
pw.println(" Enabled Providers:");
for (String i : mEnabledProviders) {
@@ -2140,12 +1822,18 @@ public class LocationManagerService extends ILocationManager.Stub implements Run
i.getValue().dump(pw, " ");
}
}
+
+ if (args.length > 0 && "short".equals(args[0])) {
+ return;
+ }
for (LocationProviderInterface provider: mProviders) {
- String state = provider.getInternalState();
- if (state != null) {
- pw.println(provider.getName() + " Internal State:");
- pw.write(state);
+ pw.print(provider.getName() + " Internal State");
+ if (provider instanceof LocationProviderProxy) {
+ LocationProviderProxy proxy = (LocationProviderProxy) provider;
+ pw.print(" (" + proxy.getConnectedPackageName() + ")");
}
+ pw.println(":");
+ provider.dump(fd, pw, args);
}
}
}
diff --git a/services/java/com/android/server/ServiceWatcher.java b/services/java/com/android/server/ServiceWatcher.java
new file mode 100644
index 0000000..0dfaa05
--- /dev/null
+++ b/services/java/com/android/server/ServiceWatcher.java
@@ -0,0 +1,274 @@
+/*
+ * 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;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.ResolveInfo;
+import android.content.pm.Signature;
+import android.os.Handler;
+import android.os.IBinder;
+import android.util.Log;
+
+import com.android.internal.content.PackageMonitor;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+
+/**
+ * Find the best Service, and bind to it.
+ * Handles run-time package changes.
+ */
+public class ServiceWatcher implements ServiceConnection {
+ private static final boolean D = false;
+ private static final String EXTRA_VERSION = "version";
+
+ private final String mTag;
+ private final Context mContext;
+ private final PackageManager mPm;
+ private final List<HashSet<Signature>> mSignatureSets;
+ private final String mAction;
+ private final Runnable mNewServiceWork;
+ private final Handler mHandler;
+
+ private Object mLock = new Object();
+
+ // all fields below synchronized on mLock
+ private IBinder mBinder; // connected service
+ private String mPackageName; // current best package
+ private int mVersion; // current best version
+
+ public ServiceWatcher(Context context, String logTag, String action,
+ List<String> initialPackageNames, Runnable newServiceWork, Handler handler) {
+ mContext = context;
+ mTag = logTag;
+ mAction = action;
+ mPm = mContext.getPackageManager();
+ mNewServiceWork = newServiceWork;
+ mHandler = handler;
+
+ mSignatureSets = new ArrayList<HashSet<Signature>>();
+ for (int i=0; i < initialPackageNames.size(); i++) {
+ String pkg = initialPackageNames.get(i);
+ HashSet<Signature> set = new HashSet<Signature>();
+ try {
+ Signature[] sigs =
+ mPm.getPackageInfo(pkg, PackageManager.GET_SIGNATURES).signatures;
+ set.addAll(Arrays.asList(sigs));
+ mSignatureSets.add(set);
+ } catch (NameNotFoundException e) {
+ Log.w(logTag, pkg + " not found");
+ }
+ }
+
+ }
+
+ public boolean start() {
+ if (!bindBestPackage(null)) return false;
+
+ mPackageMonitor.register(mContext, null, true);
+ return true;
+ }
+
+ /**
+ * Searches and binds to the best package, or do nothing
+ * if the best package is already bound.
+ * Only checks the named package, or checks all packages if it
+ * is null.
+ * Return true if a new package was found to bind to.
+ */
+ private boolean bindBestPackage(String justCheckThisPackage) {
+ Intent intent = new Intent(mAction);
+ if (justCheckThisPackage != null) {
+ intent.setPackage(justCheckThisPackage);
+ }
+ List<ResolveInfo> rInfos = mPm.queryIntentServices(new Intent(mAction),
+ PackageManager.GET_META_DATA);
+ int bestVersion = Integer.MIN_VALUE;
+ String bestPackage = null;
+ for (ResolveInfo rInfo : rInfos) {
+ String packageName = rInfo.serviceInfo.packageName;
+
+ // check signature
+ try {
+ PackageInfo pInfo;
+ pInfo = mPm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
+ if (!isSignatureMatch(pInfo.signatures)) {
+ Log.w(mTag, packageName + " resolves service " + mAction +
+ ", but has wrong signature, ignoring");
+ continue;
+ }
+ } catch (NameNotFoundException e) {
+ Log.wtf(mTag, e);
+ continue;
+ }
+
+ // check version
+ int version = 0;
+ if (rInfo.serviceInfo.metaData != null) {
+ version = rInfo.serviceInfo.metaData.getInt(EXTRA_VERSION, 0);
+ }
+ if (version > mVersion) {
+ bestVersion = version;
+ bestPackage = packageName;
+ }
+ }
+
+ if (D) Log.d(mTag, String.format("bindBestPackage %s found %d, %s",
+ (justCheckThisPackage == null ? "" : "(" + justCheckThisPackage + ") "),
+ rInfos.size(),
+ (bestPackage == null ? "no new best package" : "new best packge: " + bestPackage)));
+
+ if (bestPackage != null) {
+ bindToPackage(bestPackage, bestVersion);
+ return true;
+ }
+ return false;
+ }
+
+ private void unbind() {
+ String pkg;
+ synchronized (mLock) {
+ pkg = mPackageName;
+ mPackageName = null;
+ mVersion = Integer.MIN_VALUE;
+ }
+ if (pkg != null) {
+ if (D) Log.d(mTag, "unbinding " + pkg);
+ mContext.unbindService(this);
+ }
+ }
+
+ private void bindToPackage(String packageName, int version) {
+ unbind();
+ Intent intent = new Intent(mAction);
+ intent.setPackage(packageName);
+ synchronized (mLock) {
+ mPackageName = packageName;
+ mVersion = version;
+ }
+ if (D) Log.d(mTag, "binding " + packageName + " (version " + version + ")");
+ mContext.bindService(intent, this, Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
+ | Context.BIND_ALLOW_OOM_MANAGEMENT);
+ }
+
+ private boolean isSignatureMatch(Signature[] signatures) {
+ if (signatures == null) return false;
+
+ // build hashset of input to test against
+ HashSet<Signature> inputSet = new HashSet<Signature>();
+ for (Signature s : signatures) {
+ inputSet.add(s);
+ }
+
+ // test input against each of the signature sets
+ for (HashSet<Signature> referenceSet : mSignatureSets) {
+ if (referenceSet.equals(inputSet)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private final PackageMonitor mPackageMonitor = new PackageMonitor() {
+ /**
+ * Called when package has been reinstalled
+ */
+ @Override
+ public void onPackageUpdateFinished(String packageName, int uid) {
+ if (packageName.equals(mPackageName)) {
+ // package updated, make sure to rebind
+ unbind();
+ }
+ // check the updated package in case it is better
+ bindBestPackage(packageName);
+ }
+
+ @Override
+ public void onPackageAdded(String packageName, int uid) {
+ if (packageName.equals(mPackageName)) {
+ // package updated, make sure to rebind
+ unbind();
+ }
+ // check the new package is case it is better
+ bindBestPackage(packageName);
+ }
+
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ if (packageName.equals(mPackageName)) {
+ unbind();
+ // the currently bound package was removed,
+ // need to search for a new package
+ bindBestPackage(null);
+ }
+ }
+ };
+
+ @Override
+ public void onServiceConnected(ComponentName name, IBinder binder) {
+ synchronized (mLock) {
+ String packageName = name.getPackageName();
+ if (packageName.equals(mPackageName)) {
+ if (D) Log.d(mTag, packageName + " connected");
+ mBinder = binder;
+ if (mHandler !=null && mNewServiceWork != null) {
+ mHandler.post(mNewServiceWork);
+ }
+ } else {
+ Log.w(mTag, "unexpected onServiceConnected: " + packageName);
+ }
+ }
+ }
+
+ @Override
+ public void onServiceDisconnected(ComponentName name) {
+ synchronized (mLock) {
+ String packageName = name.getPackageName();
+ if (D) Log.d(mTag, packageName + " disconnected");
+
+ if (packageName.equals(mPackageName)) {
+ mBinder = null;
+ }
+ }
+ }
+
+ public String getBestPackageName() {
+ synchronized (mLock) {
+ return mPackageName;
+ }
+ }
+
+ public int getBestVersion() {
+ synchronized (mLock) {
+ return mVersion;
+ }
+ }
+
+ public IBinder getBinder() {
+ synchronized (mLock) {
+ return mBinder;
+ }
+ }
+}
diff --git a/services/java/com/android/server/location/GeocoderProxy.java b/services/java/com/android/server/location/GeocoderProxy.java
index 07f3125..7d030e9 100644
--- a/services/java/com/android/server/location/GeocoderProxy.java
+++ b/services/java/com/android/server/location/GeocoderProxy.java
@@ -16,92 +16,64 @@
package com.android.server.location;
-import android.content.ComponentName;
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
import android.location.Address;
import android.location.GeocoderParams;
import android.location.IGeocodeProvider;
-import android.os.IBinder;
import android.os.RemoteException;
-import android.os.SystemClock;
import android.util.Log;
+import com.android.server.ServiceWatcher;
import java.util.List;
/**
- * A class for proxying IGeocodeProvider implementations.
- *
- * {@hide}
+ * Proxy for IGeocodeProvider implementations.
*/
public class GeocoderProxy {
-
private static final String TAG = "GeocoderProxy";
- public static final String SERVICE_ACTION =
- "com.android.location.service.GeocodeProvider";
+ private static final String SERVICE_ACTION = "com.android.location.service.GeocodeProvider";
private final Context mContext;
- private final Intent mIntent;
- private final Object mMutex = new Object(); // synchronizes access to mServiceConnection
- private Connection mServiceConnection; // never null after ctor
-
- public GeocoderProxy(Context context, String packageName) {
- mContext = context;
- mIntent = new Intent(SERVICE_ACTION);
- reconnect(packageName);
- }
-
- /** Bind to service. Will reconnect if already connected */
- public void reconnect(String packageName) {
- synchronized (mMutex) {
- if (mServiceConnection != null) {
- mContext.unbindService(mServiceConnection);
- }
- mServiceConnection = new Connection();
- mIntent.setPackage(packageName);
- mContext.bindService(mIntent, mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND
- | Context.BIND_ALLOW_OOM_MANAGEMENT);
+ private final ServiceWatcher mServiceWatcher;
+
+ public static GeocoderProxy createAndBind(Context context,
+ List<String> initialPackageNames) {
+ GeocoderProxy proxy = new GeocoderProxy(context, initialPackageNames);
+ if (proxy.bind()) {
+ return proxy;
+ } else {
+ return null;
}
}
- private class Connection implements ServiceConnection {
+ public GeocoderProxy(Context context, List<String> initialPackageNames) {
+ mContext = context;
- private IGeocodeProvider mProvider;
+ mServiceWatcher = new ServiceWatcher(mContext, TAG, SERVICE_ACTION, initialPackageNames,
+ null, null);
+ }
- public void onServiceConnected(ComponentName className, IBinder service) {
- synchronized (this) {
- mProvider = IGeocodeProvider.Stub.asInterface(service);
- }
- }
+ private boolean bind () {
+ return mServiceWatcher.start();
+ }
- public void onServiceDisconnected(ComponentName className) {
- synchronized (this) {
- mProvider = null;
- }
- }
+ private IGeocodeProvider getService() {
+ return IGeocodeProvider.Stub.asInterface(mServiceWatcher.getBinder());
+ }
- public IGeocodeProvider getProvider() {
- synchronized (this) {
- return mProvider;
- }
- }
+ public String getConnectedPackageName() {
+ return mServiceWatcher.getBestPackageName();
}
public String getFromLocation(double latitude, double longitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
- IGeocodeProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
+ IGeocodeProvider provider = getService();
if (provider != null) {
try {
- return provider.getFromLocation(latitude, longitude, maxResults,
- params, addrs);
+ return provider.getFromLocation(latitude, longitude, maxResults, params, addrs);
} catch (RemoteException e) {
- Log.e(TAG, "getFromLocation failed", e);
+ Log.w(TAG, e);
}
}
return "Service not Available";
@@ -111,19 +83,17 @@ public class GeocoderProxy {
double lowerLeftLatitude, double lowerLeftLongitude,
double upperRightLatitude, double upperRightLongitude, int maxResults,
GeocoderParams params, List<Address> addrs) {
- IGeocodeProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
+ IGeocodeProvider provider = getService();
if (provider != null) {
try {
return provider.getFromLocationName(locationName, lowerLeftLatitude,
lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
maxResults, params, addrs);
} catch (RemoteException e) {
- Log.e(TAG, "getFromLocationName failed", e);
+ Log.w(TAG, e);
}
}
return "Service not Available";
}
+
}
diff --git a/services/java/com/android/server/location/GeofenceManager.java b/services/java/com/android/server/location/GeofenceManager.java
index b3f53ea..338cd5d 100644
--- a/services/java/com/android/server/location/GeofenceManager.java
+++ b/services/java/com/android/server/location/GeofenceManager.java
@@ -25,47 +25,36 @@ import android.Manifest.permission;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
+import android.location.Geofence;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
+import android.location.LocationRequest;
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";
+ private 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;
- }
- }
+ private static final int MAX_SPEED_M_S = 100; // 360 km/hr (high speed train)
- final Context mContext;
- final LocationManager mLocationManager;
- final PowerManager.WakeLock mWakeLock;
- final Looper mLooper; // looper thread to take location updates on
+ private final Context mContext;
+ private final LocationManager mLocationManager;
+ private final PowerManager.WakeLock mWakeLock;
+ private 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>();
+ private Object mLock = new Object();
+
+ // access to members below is synchronized on mLock
+ private Location mLastLocation;
+ private List<GeofenceState> mFences = new LinkedList<GeofenceState>();
public GeofenceManager(Context context) {
mContext = context;
@@ -73,82 +62,98 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
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();
+ LocationRequest request = new LocationRequest()
+ .setQuality(LocationRequest.POWER_NONE)
+ .setFastestInterval(0);
+ mLocationManager.requestLocationUpdates(request, this, Looper.myLooper());
+ }
+
+ public void addFence(LocationRequest request, Geofence geofence, PendingIntent intent, int uid,
+ String packageName) {
+ GeofenceState state = new GeofenceState(geofence, mLastLocation,
+ request.getExpireAt(), packageName, intent);
+
+ synchronized (mLock) {
+ // first make sure it doesn't already exist
+ for (int i = mFences.size() - 1; i >= 0; i--) {
+ GeofenceState w = mFences.get(i);
+ if (geofence.equals(w.mFence) && intent.equals(w.mIntent)) {
+ // already exists, remove the old one
+ mFences.remove(i);
+ break;
+ }
+ }
+ mFences.add(state);
+ updateProviderRequirementsLocked();
}
}
- public void removeFence(PendingIntent intent) {
- synchronized (this) {
- Iterator<GeofenceWrapper> iter = mFences.iterator();
+ public void removeFence(Geofence fence, PendingIntent intent) {
+ synchronized (mLock) {
+ Iterator<GeofenceState> iter = mFences.iterator();
while (iter.hasNext()) {
- GeofenceWrapper fenceWrapper = iter.next();
- if (fenceWrapper.intent.equals(intent)) {
- iter.remove();
+ GeofenceState state = iter.next();
+ if (state.mIntent.equals(intent)) {
+
+ if (fence == null) {
+ // alwaus remove
+ iter.remove();
+ } else {
+ // just remove matching fences
+ if (fence.equals(state.mFence)) {
+ iter.remove();
+ }
+ }
}
}
- updateProviderRequirements();
+ updateProviderRequirementsLocked();
}
}
public void removeFence(String packageName) {
- synchronized (this) {
- Iterator<GeofenceWrapper> iter = mFences.iterator();
+ synchronized (mLock) {
+ Iterator<GeofenceState> iter = mFences.iterator();
while (iter.hasNext()) {
- GeofenceWrapper fenceWrapper = iter.next();
- if (fenceWrapper.packageName.equals(packageName)) {
+ GeofenceState state = iter.next();
+ if (state.mPackageName.equals(packageName)) {
iter.remove();
}
}
- updateProviderRequirements();
+ updateProviderRequirementsLocked();
}
}
- 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();
- }
+ private void removeExpiredFencesLocked() {
+ long time = SystemClock.elapsedRealtime();
+ Iterator<GeofenceState> iter = mFences.iterator();
+ while (iter.hasNext()) {
+ GeofenceState state = iter.next();
+ if (state.mExpireAt < time) {
+ iter.remove();
}
}
}
- void processLocation(Location location) {
+ private void processLocation(Location location) {
List<PendingIntent> enterIntents = new LinkedList<PendingIntent>();
List<PendingIntent> exitIntents = new LinkedList<PendingIntent>();
- synchronized (this) {
+ synchronized (mLock) {
mLastLocation = location;
- removeExpiredFences();
+ removeExpiredFencesLocked();
- for (GeofenceWrapper fenceWrapper : mFences) {
- int event = fenceWrapper.fence.processLocation(location);
- if ((event & Geofence.FLAG_ENTER) != 0) {
- enterIntents.add(fenceWrapper.intent);
+ for (GeofenceState state : mFences) {
+ int event = state.processLocation(location);
+ if ((event & GeofenceState.FLAG_ENTER) != 0) {
+ enterIntents.add(state.mIntent);
}
- if ((event & Geofence.FLAG_EXIT) != 0) {
- exitIntents.add(fenceWrapper.intent);
+ if ((event & GeofenceState.FLAG_EXIT) != 0) {
+ exitIntents.add(state.mIntent);
}
}
- updateProviderRequirements();
+ updateProviderRequirementsLocked();
}
// release lock before sending intents
@@ -160,52 +165,50 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
}
}
- void sendIntentEnter(PendingIntent pendingIntent) {
+ private void sendIntentEnter(PendingIntent pendingIntent) {
Intent intent = new Intent();
intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, true);
sendIntent(pendingIntent, intent);
}
- void sendIntentExit(PendingIntent pendingIntent) {
+ private void sendIntentExit(PendingIntent pendingIntent) {
Intent intent = new Intent();
intent.putExtra(LocationManager.KEY_PROXIMITY_ENTERING, false);
sendIntent(pendingIntent, intent);
}
- void sendIntent(PendingIntent pendingIntent, Intent intent) {
+ private 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);
+ removeFence(null, 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();
- }
+ private void updateProviderRequirementsLocked() {
+ double minDistance = Double.MAX_VALUE;
+ for (GeofenceState state : mFences) {
+ if (state.getDistance() < minDistance) {
+ minDistance = state.getDistance();
}
+ }
- if (minDistance == Double.MAX_VALUE) {
- disableLocation();
- } else {
- int intervalMs = (int)(minDistance * 1000) / MAX_SPEED_M_S;
- setLocationInterval(intervalMs);
- }
+ if (minDistance == Double.MAX_VALUE) {
+ disableLocationLocked();
+ } else {
+ int intervalMs = (int)(minDistance * 1000) / MAX_SPEED_M_S;
+ requestLocationLocked(intervalMs);
}
}
- void setLocationInterval(int intervalMs) {
- mLocationManager.requestLocationUpdates(LocationManager.GPS_PROVIDER, intervalMs, 0, this,
+ private void requestLocationLocked(int intervalMs) {
+ mLocationManager.requestLocationUpdates(new LocationRequest().setInterval(intervalMs), this,
mLooper);
}
- void disableLocation() {
+ private void disableLocationLocked() {
mLocationManager.removeUpdates(this);
}
@@ -231,11 +234,12 @@ public class GeofenceManager implements LocationListener, PendingIntent.OnFinish
public void dump(PrintWriter pw) {
pw.println(" Geofences:");
- for (GeofenceWrapper fenceWrapper : mFences) {
+
+ for (GeofenceState state : mFences) {
pw.append(" ");
- pw.append(fenceWrapper.packageName);
+ pw.append(state.mPackageName);
pw.append(" ");
- pw.append(fenceWrapper.fence.toString());
+ pw.append(state.mFence.toString());
pw.append("\n");
}
}
diff --git a/services/java/com/android/server/location/Geofence.java b/services/java/com/android/server/location/GeofenceState.java
index f63607a..1fd737f 100644
--- a/services/java/com/android/server/location/Geofence.java
+++ b/services/java/com/android/server/location/GeofenceState.java
@@ -17,37 +17,42 @@
package com.android.server.location;
+import android.app.PendingIntent;
+import android.location.Geofence;
import android.location.Location;
/**
- * Represents a simple circular GeoFence.
+ * Represents state associated with a geofence
*/
-public class Geofence {
+public class GeofenceState {
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;
+ private static final int STATE_UNKNOWN = 0;
+ private static final int STATE_INSIDE = 1;
+ private static final int STATE_OUTSIDE = 2;
- final double mLatitude;
- final double mLongitude;
- final float mRadius;
- final Location mLocation;
+ public final Geofence mFence;
+ private final Location mLocation;
+ public final long mExpireAt;
+ public final String mPackageName;
+ public final PendingIntent mIntent;
int mState; // current state
double mDistance; // current distance to center of fence
- public Geofence(double latitude, double longitude, float radius, Location prevLocation) {
+ public GeofenceState(Geofence fence, Location prevLocation, long expireAt,
+ String packageName, PendingIntent intent) {
mState = STATE_UNKNOWN;
- mLatitude = latitude;
- mLongitude = longitude;
- mRadius = radius;
+ mFence = fence;
+ mExpireAt = expireAt;
+ mPackageName = packageName;
+ mIntent = intent;
mLocation = new Location("");
- mLocation.setLatitude(latitude);
- mLocation.setLongitude(longitude);
+ mLocation.setLatitude(fence.getLatitude());
+ mLocation.setLongitude(fence.getLongitude());
if (prevLocation != null) {
processLocation(prevLocation);
@@ -63,7 +68,7 @@ public class Geofence {
int prevState = mState;
//TODO: inside/outside detection could be made more rigorous
- boolean inside = mDistance <= Math.max(mRadius, location.getAccuracy());
+ boolean inside = mDistance <= Math.max(mFence.getRadius(), location.getAccuracy());
if (inside) {
mState = STATE_INSIDE;
} else {
@@ -94,7 +99,6 @@ public class Geofence {
default:
state = "?";
}
- return String.format("(%.4f, %.4f r=%.0f d=%.0f %s)", mLatitude, mLongitude, mRadius,
- mDistance, state);
+ return String.format("%s d=%.0f %s", mFence.toString(), mDistance, state);
}
}
diff --git a/services/java/com/android/server/location/GpsLocationProvider.java b/services/java/com/android/server/location/GpsLocationProvider.java
index bd7668b..3cd767d 100755
--- a/services/java/com/android/server/location/GpsLocationProvider.java
+++ b/services/java/com/android/server/location/GpsLocationProvider.java
@@ -29,6 +29,7 @@ import android.location.IGpsStatusProvider;
import android.location.ILocationManager;
import android.location.INetInitiatedListener;
import android.location.Location;
+import android.location.LocationListener;
import android.location.LocationManager;
import android.location.LocationProvider;
import android.net.ConnectivityManager;
@@ -54,17 +55,19 @@ import android.telephony.TelephonyManager;
import android.telephony.gsm.GsmCellLocation;
import android.util.Log;
import android.util.NtpTrustedTime;
-import android.util.SparseIntArray;
-
import com.android.internal.app.IBatteryStats;
import com.android.internal.location.GpsNetInitiatedHandler;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.PhoneConstants;
import java.io.File;
+import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.IOException;
+import java.io.PrintWriter;
import java.io.StringReader;
import java.util.ArrayList;
import java.util.Date;
@@ -84,6 +87,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+ private static final ProviderProperties PROPERTIES = new ProviderProperties(
+ true, true, false, false, true, true, true,
+ Criteria.POWER_HIGH, Criteria.ACCURACY_FINE);
+
// these need to match GpsPositionMode enum in gps.h
private static final int GPS_POSITION_MODE_STANDALONE = 0;
private static final int GPS_POSITION_MODE_MS_BASED = 1;
@@ -150,14 +157,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Handler messages
private static final int CHECK_LOCATION = 1;
private static final int ENABLE = 2;
- private static final int ENABLE_TRACKING = 3;
+ private static final int SET_REQUEST = 3;
private static final int UPDATE_NETWORK_STATE = 4;
private static final int INJECT_NTP_TIME = 5;
private static final int DOWNLOAD_XTRA_DATA = 6;
private static final int UPDATE_LOCATION = 7;
private static final int ADD_LISTENER = 8;
private static final int REMOVE_LISTENER = 9;
- private static final int REQUEST_SINGLE_SHOT = 10;
// Request setid
private static final int AGPS_RIL_REQUEST_SETID_IMSI = 1;
@@ -179,6 +185,18 @@ public class GpsLocationProvider implements LocationProviderInterface {
private static final String PROPERTIES_FILE = "/etc/gps.conf";
+ /** simpler wrapper for ProviderRequest + Worksource */
+ private static class GpsRequest {
+ public ProviderRequest request;
+ public WorkSource source;
+ public GpsRequest(ProviderRequest request, WorkSource source) {
+ this.request = request;
+ this.source = source;
+ }
+ }
+
+ private Object mLock = new Object();
+
private int mLocationFlags = LOCATION_INVALID;
// current status
@@ -198,9 +216,16 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Typical hot TTTF is ~5 seconds, so 10 seconds seems sane.
private static final int GPS_POLLING_THRESHOLD_INTERVAL = 10 * 1000;
- // true if we are enabled
- private volatile boolean mEnabled;
-
+ // how often to request NTP time, in milliseconds
+ // current setting 24 hours
+ private static final long NTP_INTERVAL = 24*60*60*1000;
+ // how long to wait if we have a network error in NTP or XTRA downloading
+ // current setting - 5 minutes
+ private static final long RETRY_INTERVAL = 5*60*1000;
+
+ // true if we are enabled, protected by this
+ private boolean mEnabled;
+
// true if we have network connectivity
private boolean mNetworkAvailable;
@@ -217,16 +242,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
// true if GPS engine is on
private boolean mEngineOn;
-
+
// requested frequency of fixes, in milliseconds
private int mFixInterval = 1000;
// true if we started navigation
private boolean mStarted;
- // true if single shot request is in progress
- private boolean mSingleShot;
-
// capabilities of the GPS engine
private int mEngineCapabilities;
@@ -236,7 +258,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// for calculating time to first fix
private long mFixRequestTime = 0;
// time to first fix for most recent session
- private int mTTFF = 0;
+ private int mTimeToFirstFix = 0;
// time we received our last fix
private long mLastFixTime;
@@ -251,7 +273,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
private final Context mContext;
private final NtpTrustedTime mNtpTime;
- private final ILocationManager mLocationManager;
+ private final ILocationManager mILocationManager;
private Location mLocation = new Location(LocationManager.GPS_PROVIDER);
private Bundle mLocationExtras = new Bundle();
private ArrayList<Listener> mListeners = new ArrayList<Listener>();
@@ -267,17 +289,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
private int mAGpsDataConnectionState;
private int mAGpsDataConnectionIpAddr;
private final ConnectivityManager mConnMgr;
- private final GpsNetInitiatedHandler mNIHandler;
+ private final GpsNetInitiatedHandler mNIHandler;
// Wakelocks
private final static String WAKELOCK_KEY = "GpsLocationProvider";
private final PowerManager.WakeLock mWakeLock;
- // bitfield of pending messages to our Handler
- // used only for messages that cannot have multiple instances queued
- private int mPendingMessageBits;
- // separate counter for ADD_LISTENER and REMOVE_LISTENER messages,
- // which might have multiple instances queued
- private int mPendingListenerMessages;
// Alarms
private final static String ALARM_WAKEUP = "com.android.internal.location.ALARM_WAKEUP";
@@ -287,22 +303,18 @@ public class GpsLocationProvider implements LocationProviderInterface {
private final PendingIntent mTimeoutIntent;
private final IBatteryStats mBatteryStats;
- private final SparseIntArray mClientUids = new SparseIntArray();
- // how often to request NTP time, in milliseconds
- // current setting 24 hours
- private static final long NTP_INTERVAL = 24*60*60*1000;
- // how long to wait if we have a network error in NTP or XTRA downloading
- // current setting - 5 minutes
- private static final long RETRY_INTERVAL = 5*60*1000;
+ // only modified on handler thread
+ private int[] mClientUids = new int[0];
private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() {
+ @Override
public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException {
if (listener == null) {
throw new NullPointerException("listener is null in addGpsStatusListener");
}
- synchronized(mListeners) {
+ synchronized (mListeners) {
IBinder binder = listener.asBinder();
int size = mListeners.size();
for (int i = 0; i < size; i++) {
@@ -319,12 +331,13 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
+ @Override
public void removeGpsStatusListener(IGpsStatusListener listener) {
if (listener == null) {
throw new NullPointerException("listener is null in addGpsStatusListener");
}
- synchronized(mListeners) {
+ synchronized (mListeners) {
IBinder binder = listener.asBinder();
Listener l = null;
int size = mListeners.size();
@@ -353,7 +366,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
if (action.equals(ALARM_WAKEUP)) {
if (DEBUG) Log.d(TAG, "ALARM_WAKEUP");
- startNavigating(false);
+ startNavigating();
} else if (action.equals(ALARM_TIMEOUT)) {
if (DEBUG) Log.d(TAG, "ALARM_TIMEOUT");
hibernate();
@@ -361,6 +374,22 @@ public class GpsLocationProvider implements LocationProviderInterface {
checkSmsSuplInit(intent);
} else if (action.equals(Intents.WAP_PUSH_RECEIVED_ACTION)) {
checkWapSuplInit(intent);
+ } else if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
+ int networkState;
+ if (intent.getBooleanExtra(ConnectivityManager.EXTRA_NO_CONNECTIVITY, false)) {
+ networkState = LocationProvider.TEMPORARILY_UNAVAILABLE;
+ } else {
+ networkState = LocationProvider.AVAILABLE;
+ }
+
+ // retrieve NetworkInfo result for this UID
+ NetworkInfo info =
+ intent.getParcelableExtra(ConnectivityManager.EXTRA_NETWORK_INFO);
+ ConnectivityManager connManager = (ConnectivityManager)
+ mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
+ info = connManager.getNetworkInfo(info.getType());
+
+ updateNetworkState(networkState, info);
}
}
};
@@ -382,10 +411,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
return native_is_supported();
}
- public GpsLocationProvider(Context context, ILocationManager locationManager) {
+ public GpsLocationProvider(Context context, ILocationManager ilocationManager) {
mContext = context;
mNtpTime = NtpTrustedTime.getInstance(context);
- mLocationManager = locationManager;
+ mILocationManager = ilocationManager;
mNIHandler = new GpsNetInitiatedHandler(context);
mLocation.setExtras(mLocationExtras);
@@ -393,7 +422,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
// Create a wake lock
PowerManager powerManager = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, WAKELOCK_KEY);
- mWakeLock.setReferenceCounted(false);
+ mWakeLock.setReferenceCounted(true);
mAlarmManager = (AlarmManager)mContext.getSystemService(Context.ALARM_SERVICE);
mWakeupIntent = PendingIntent.getBroadcast(mContext, 0, new Intent(ALARM_WAKEUP), 0);
@@ -473,16 +502,14 @@ public class GpsLocationProvider implements LocationProviderInterface {
/**
* Returns the name of this provider.
*/
+ @Override
public String getName() {
return LocationManager.GPS_PROVIDER;
}
- /**
- * Returns true if the provider requires access to a
- * data network (e.g., the Internet), false otherwise.
- */
- public boolean requiresNetwork() {
- return true;
+ @Override
+ public ProviderProperties getProperties() {
+ return PROPERTIES;
}
public void updateNetworkState(int state, NetworkInfo info) {
@@ -516,7 +543,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
String apnName = info.getExtraInfo();
if (mNetworkAvailable) {
if (apnName == null) {
- /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
+ /* Assign a dummy value in the case of C2K as otherwise we will have a runtime
exception in the following call to native_agps_data_conn_open*/
apnName = "dummy-apn";
}
@@ -613,18 +640,11 @@ public class GpsLocationProvider implements LocationProviderInterface {
// try again later
// since this is delayed and not urgent we do not hold a wake lock here
mHandler.removeMessages(DOWNLOAD_XTRA_DATA);
- mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA), RETRY_INTERVAL);
+ mHandler.sendMessageDelayed(Message.obtain(mHandler, DOWNLOAD_XTRA_DATA),
+ RETRY_INTERVAL);
}
}
- /**
- * This is called to inform us when another location provider returns a location.
- * Someday we might use this for network location injection to aid the GPS
- */
- public void updateLocation(Location location) {
- sendMessage(UPDATE_LOCATION, 0, location);
- }
-
private void handleUpdateLocation(Location location) {
if (location.hasAccuracy()) {
native_inject_location(location.getLatitude(), location.getLongitude(),
@@ -633,107 +653,26 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
/**
- * Returns true if the provider requires access to a
- * satellite-based positioning system (e.g., GPS), false
- * otherwise.
- */
- public boolean requiresSatellite() {
- return true;
- }
-
- /**
- * Returns true if the provider requires access to an appropriate
- * cellular network (e.g., to make use of cell tower IDs), false
- * otherwise.
- */
- public boolean requiresCell() {
- return false;
- }
-
- /**
- * Returns true if the use of this provider may result in a
- * monetary charge to the user, false if use is free. It is up to
- * each provider to give accurate information.
- */
- public boolean hasMonetaryCost() {
- return false;
- }
-
- /**
- * Returns true if the provider is able to provide altitude
- * information, false otherwise. A provider that reports altitude
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public boolean supportsAltitude() {
- return true;
- }
-
- /**
- * Returns true if the provider is able to provide speed
- * information, false otherwise. A provider that reports speed
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public boolean supportsSpeed() {
- return true;
- }
-
- /**
- * Returns true if the provider is able to provide bearing
- * information, false otherwise. A provider that reports bearing
- * under most circumstances but may occassionally not report it
- * should return true.
- */
- public boolean supportsBearing() {
- return true;
- }
-
- /**
- * Returns the power requirement for this provider.
- *
- * @return the power requirement for this provider, as one of the
- * constants Criteria.POWER_REQUIREMENT_*.
- */
- public int getPowerRequirement() {
- return Criteria.POWER_HIGH;
- }
-
- /**
- * Returns true if this provider meets the given criteria,
- * false otherwise.
- */
- public boolean meetsCriteria(Criteria criteria) {
- return (criteria.getPowerRequirement() != Criteria.POWER_LOW);
- }
-
- /**
- * Returns the horizontal accuracy of this provider
- *
- * @return the accuracy of location from this provider, as one
- * of the constants Criteria.ACCURACY_*.
- */
- public int getAccuracy() {
- return Criteria.ACCURACY_FINE;
- }
-
- /**
* Enables this provider. When enabled, calls to getStatus()
* must be handled. Hardware may be started up
* when the provider is enabled.
*/
+ @Override
public void enable() {
- synchronized (mHandler) {
- sendMessage(ENABLE, 1, null);
- }
+ sendMessage(ENABLE, 1, null);
}
private void handleEnable() {
if (DEBUG) Log.d(TAG, "handleEnable");
- if (mEnabled) return;
- mEnabled = native_init();
- if (mEnabled) {
+ synchronized (mLock) {
+ if (mEnabled) return;
+ mEnabled = true;
+ }
+
+ boolean enabled = native_init();
+
+ if (enabled) {
mSupportsXtra = native_supports_xtra();
if (mSuplServerHost != null) {
native_set_agps_server(AGPS_TYPE_SUPL, mSuplServerHost, mSuplServerPort);
@@ -742,6 +681,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
native_set_agps_server(AGPS_TYPE_C2K, mC2KServerHost, mC2KServerPort);
}
} else {
+ synchronized (mLock) {
+ mEnabled = false;
+ }
Log.w(TAG, "Failed to enable location provider");
}
}
@@ -751,27 +693,35 @@ public class GpsLocationProvider implements LocationProviderInterface {
* need not be handled. Hardware may be shut
* down while the provider is disabled.
*/
+ @Override
public void disable() {
- synchronized (mHandler) {
- sendMessage(ENABLE, 0, null);
- }
+ sendMessage(ENABLE, 0, null);
}
private void handleDisable() {
if (DEBUG) Log.d(TAG, "handleDisable");
- if (!mEnabled) return;
- mEnabled = false;
+ synchronized (mLock) {
+ if (!mEnabled) return;
+ mEnabled = false;
+ }
+
stopNavigating();
+ mAlarmManager.cancel(mWakeupIntent);
+ mAlarmManager.cancel(mTimeoutIntent);
// do this before releasing wakelock
native_cleanup();
}
+ @Override
public boolean isEnabled() {
- return mEnabled;
+ synchronized (mLock) {
+ return mEnabled;
+ }
}
+ @Override
public int getStatus(Bundle extras) {
if (extras != null) {
extras.putInt("satellites", mSvCount);
@@ -788,93 +738,69 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
+ @Override
public long getStatusUpdateTime() {
return mStatusUpdateTime;
}
- public void enableLocationTracking(boolean enable) {
- // FIXME - should set a flag here to avoid race conditions with single shot request
- synchronized (mHandler) {
- sendMessage(ENABLE_TRACKING, (enable ? 1 : 0), null);
- }
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) {
+ sendMessage(SET_REQUEST, 0, new GpsRequest(request, source));
}
- private void handleEnableLocationTracking(boolean enable) {
- if (enable) {
- mTTFF = 0;
- mLastFixTime = 0;
- startNavigating(false);
- } else {
- if (!hasCapability(GPS_CAPABILITY_SCHEDULING)) {
- mAlarmManager.cancel(mWakeupIntent);
- mAlarmManager.cancel(mTimeoutIntent);
- }
- stopNavigating();
- }
- }
+ private void handleSetRequest(ProviderRequest request, WorkSource source) {
+ if (DEBUG) Log.d(TAG, "setRequest " + request);
- public boolean requestSingleShotFix() {
- if (mStarted) {
- // cannot do single shot if already navigating
- return false;
- }
- synchronized (mHandler) {
- mHandler.removeMessages(REQUEST_SINGLE_SHOT);
- Message m = Message.obtain(mHandler, REQUEST_SINGLE_SHOT);
- mHandler.sendMessage(m);
- }
- return true;
- }
- private void handleRequestSingleShot() {
- mTTFF = 0;
- mLastFixTime = 0;
- startNavigating(true);
- }
- public void setMinTime(long minTime, WorkSource ws) {
- if (DEBUG) Log.d(TAG, "setMinTime " + minTime);
-
- if (minTime >= 0) {
- mFixInterval = (int)minTime;
+ if (request.reportLocation) {
+ // update client uids
+ int[] uids = new int[source.size()];
+ for (int i=0; i < source.size(); i++) {
+ uids[i] = source.get(i);
+ }
+ updateClientUids(uids);
+ mFixInterval = (int) request.interval;
+
+ // check for overflow
+ if (mFixInterval != request.interval) {
+ Log.w(TAG, "interval overflow: " + request.interval);
+ mFixInterval = Integer.MAX_VALUE;
+ }
+
+ // apply request to GPS engine
if (mStarted && hasCapability(GPS_CAPABILITY_SCHEDULING)) {
+ // change period
if (!native_set_position_mode(mPositionMode, GPS_POSITION_RECURRENCE_PERIODIC,
mFixInterval, 0, 0)) {
Log.e(TAG, "set_position_mode failed in setMinTime()");
}
+ } else if (!mStarted) {
+ // start GPS
+ startNavigating();
}
- }
- }
-
- public String getInternalState() {
- StringBuilder s = new StringBuilder();
- s.append(" mFixInterval=").append(mFixInterval).append("\n");
- s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" (");
- if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED ");
- if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
- if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
- if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
- if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
- s.append(")\n");
+ } else {
+ updateClientUids(new int[0]);
- s.append(native_get_internal_state());
- return s.toString();
+ stopNavigating();
+ mAlarmManager.cancel(mWakeupIntent);
+ mAlarmManager.cancel(mTimeoutIntent);
+ }
}
private final class Listener implements IBinder.DeathRecipient {
final IGpsStatusListener mListener;
-
- int mSensors = 0;
-
+
Listener(IGpsStatusListener listener) {
mListener = listener;
}
-
+
+ @Override
public void binderDied() {
if (DEBUG) Log.d(TAG, "GPS status listener died");
- synchronized(mListeners) {
+ synchronized (mListeners) {
mListeners.remove(this);
}
if (mListener != null) {
@@ -883,64 +809,47 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
- public void addListener(int uid) {
- synchronized (mWakeLock) {
- mPendingListenerMessages++;
- mWakeLock.acquire();
- Message m = Message.obtain(mHandler, ADD_LISTENER);
- m.arg1 = uid;
- mHandler.sendMessage(m);
- }
- }
-
- private void handleAddListener(int uid) {
- synchronized(mListeners) {
- if (mClientUids.indexOfKey(uid) >= 0) {
- // Shouldn't be here -- already have this uid.
- Log.w(TAG, "Duplicate add listener for uid " + uid);
- return;
+ private void updateClientUids(int[] uids) {
+ // Find uid's that were not previously tracked
+ for (int uid1 : uids) {
+ boolean newUid = true;
+ for (int uid2 : mClientUids) {
+ if (uid1 == uid2) {
+ newUid = false;
+ break;
+ }
}
- mClientUids.put(uid, 0);
- if (mNavigating) {
+ if (newUid) {
try {
- mBatteryStats.noteStartGps(uid);
+ mBatteryStats.noteStartGps(uid1);
} catch (RemoteException e) {
- Log.w(TAG, "RemoteException in addListener");
+ Log.w(TAG, "RemoteException", e);
}
}
}
- }
-
- public void removeListener(int uid) {
- synchronized (mWakeLock) {
- mPendingListenerMessages++;
- mWakeLock.acquire();
- Message m = Message.obtain(mHandler, REMOVE_LISTENER);
- m.arg1 = uid;
- mHandler.sendMessage(m);
- }
- }
- private void handleRemoveListener(int uid) {
- synchronized(mListeners) {
- if (mClientUids.indexOfKey(uid) < 0) {
- // Shouldn't be here -- don't have this uid.
- Log.w(TAG, "Unneeded remove listener for uid " + uid);
- return;
+ // Find uid'd that were tracked but have now disappeared
+ for (int uid1 : mClientUids) {
+ boolean oldUid = true;
+ for (int uid2 : uids) {
+ if (uid1 == uid2) {
+ oldUid = false;
+ break;
+ }
}
- mClientUids.delete(uid);
- if (mNavigating) {
+ if (oldUid) {
try {
- mBatteryStats.noteStopGps(uid);
+ mBatteryStats.noteStopGps(uid1);
} catch (RemoteException e) {
- Log.w(TAG, "RemoteException in removeListener");
+ Log.w(TAG, "RemoteException", e);
}
}
}
}
+ @Override
public boolean sendExtraCommand(String command, Bundle extras) {
-
+
long identity = Binder.clearCallingIdentity();
boolean result = false;
@@ -957,7 +866,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
} else {
Log.w(TAG, "sendExtraCommand: unknown command " + command);
}
-
+
Binder.restoreCallingIdentity(identity);
return result;
}
@@ -992,18 +901,17 @@ public class GpsLocationProvider implements LocationProviderInterface {
return false;
}
- private void startNavigating(boolean singleShot) {
+ private void startNavigating() {
if (!mStarted) {
if (DEBUG) Log.d(TAG, "startNavigating");
+ mTimeToFirstFix = 0;
+ mLastFixTime = 0;
mStarted = true;
- mSingleShot = singleShot;
mPositionMode = GPS_POSITION_MODE_STANDALONE;
if (Settings.Secure.getInt(mContext.getContentResolver(),
Settings.Secure.ASSISTED_GPS_ENABLED, 1) != 0) {
- if (singleShot && hasCapability(GPS_CAPABILITY_MSA)) {
- mPositionMode = GPS_POSITION_MODE_MS_ASSISTED;
- } else if (hasCapability(GPS_CAPABILITY_MSB)) {
+ if (hasCapability(GPS_CAPABILITY_MSB)) {
mPositionMode = GPS_POSITION_MODE_MS_BASED;
}
}
@@ -1039,9 +947,8 @@ public class GpsLocationProvider implements LocationProviderInterface {
if (DEBUG) Log.d(TAG, "stopNavigating");
if (mStarted) {
mStarted = false;
- mSingleShot = false;
native_stop();
- mTTFF = 0;
+ mTimeToFirstFix = 0;
mLastFixTime = 0;
mLocationFlags = LOCATION_INVALID;
@@ -1056,8 +963,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
mAlarmManager.cancel(mTimeoutIntent);
mAlarmManager.cancel(mWakeupIntent);
long now = SystemClock.elapsedRealtime();
- mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP,
- SystemClock.elapsedRealtime() + mFixInterval, mWakeupIntent);
+ mAlarmManager.set(AlarmManager.ELAPSED_REALTIME_WAKEUP, now + mFixInterval, mWakeupIntent);
}
private boolean hasCapability(int capability) {
@@ -1105,7 +1011,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
mLocation.setExtras(mLocationExtras);
try {
- mLocationManager.reportLocation(mLocation, false);
+ mILocationManager.reportLocation(mLocation, false);
} catch (RemoteException e) {
Log.e(TAG, "RemoteException calling reportLocation");
}
@@ -1113,17 +1019,17 @@ public class GpsLocationProvider implements LocationProviderInterface {
mLastFixTime = System.currentTimeMillis();
// report time to first fix
- if (mTTFF == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
- mTTFF = (int)(mLastFixTime - mFixRequestTime);
- if (DEBUG) Log.d(TAG, "TTFF: " + mTTFF);
+ if (mTimeToFirstFix == 0 && (flags & LOCATION_HAS_LAT_LONG) == LOCATION_HAS_LAT_LONG) {
+ mTimeToFirstFix = (int)(mLastFixTime - mFixRequestTime);
+ if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix);
// notify status listeners
- synchronized(mListeners) {
+ synchronized (mListeners) {
int size = mListeners.size();
for (int i = 0; i < size; i++) {
Listener listener = mListeners.get(i);
try {
- listener.mListener.onFirstFix(mTTFF);
+ listener.mListener.onFirstFix(mTimeToFirstFix);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException in stopNavigating");
mListeners.remove(listener);
@@ -1134,9 +1040,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
- if (mSingleShot) {
- stopNavigating();
- }
if (mStarted && mStatus != LocationProvider.AVAILABLE) {
// we want to time out if we do not receive a fix
// within the time out and we are requesting infrequent fixes
@@ -1164,7 +1067,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
private void reportStatus(int status) {
if (DEBUG) Log.v(TAG, "reportStatus status: " + status);
- synchronized(mListeners) {
+ synchronized (mListeners) {
boolean wasNavigating = mNavigating;
switch (status) {
@@ -1202,20 +1105,6 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
}
- 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(LocationManager.GPS_ENABLED_CHANGE_ACTION);
intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating);
@@ -1230,15 +1119,15 @@ public class GpsLocationProvider implements LocationProviderInterface {
private void reportSvStatus() {
int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks);
-
- synchronized(mListeners) {
+
+ synchronized (mListeners) {
int size = mListeners.size();
for (int i = 0; i < size; i++) {
Listener listener = mListeners.get(i);
try {
- listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
- mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
- mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
+ listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs,
+ mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK],
+ mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]);
} catch (RemoteException e) {
Log.w(TAG, "RemoteException in reportSvInfo");
mListeners.remove(listener);
@@ -1254,7 +1143,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
" almanacMask: " + Integer.toHexString(mSvMasks[ALMANAC_MASK]));
for (int i = 0; i < svCount; i++) {
Log.v(TAG, "sv: " + mSvs[i] +
- " snr: " + (float)mSnrs[i]/10 +
+ " snr: " + mSnrs[i]/10 +
" elev: " + mSvElevations[i] +
" azimuth: " + mSvAzimuths[i] +
((mSvMasks[EPHEMERIS_MASK] & (1 << (mSvs[i] - 1))) == 0 ? " " : " E") +
@@ -1342,7 +1231,7 @@ public class GpsLocationProvider implements LocationProviderInterface {
* called from native code to report NMEA data received
*/
private void reportNmea(long timestamp) {
- synchronized(mListeners) {
+ synchronized (mListeners) {
int size = mListeners.size();
if (size > 0) {
// don't bother creating the String if we have no listeners
@@ -1389,19 +1278,18 @@ public class GpsLocationProvider implements LocationProviderInterface {
//=============================================================
private final INetInitiatedListener mNetInitiatedListener = new INetInitiatedListener.Stub() {
// Sends a response for an NI reqeust to HAL.
+ @Override
public boolean sendNiResponse(int notificationId, int userResponse)
{
// TODO Add Permission check
- StringBuilder extrasBuf = new StringBuilder();
-
if (DEBUG) Log.d(TAG, "sendNiResponse, notifId: " + notificationId +
", response: " + userResponse);
native_send_ni_response(notificationId, userResponse);
return true;
}
};
-
+
public INetInitiatedListener getNetInitiatedListener() {
return mNetInitiatedListener;
}
@@ -1550,16 +1438,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
}
private void sendMessage(int message, int arg, Object obj) {
- // hold a wake lock while messages are pending
- synchronized (mWakeLock) {
- mPendingMessageBits |= (1 << message);
- mWakeLock.acquire();
- mHandler.removeMessages(message);
- Message m = Message.obtain(mHandler, message);
- m.arg1 = arg;
- m.obj = obj;
- mHandler.sendMessage(m);
- }
+ // hold a wake lock until this message is delivered
+ mWakeLock.acquire();
+ mHandler.obtainMessage(message, arg, 1, obj).sendToTarget();
}
private final class ProviderHandler extends Handler {
@@ -1574,11 +1455,9 @@ public class GpsLocationProvider implements LocationProviderInterface {
handleDisable();
}
break;
- case ENABLE_TRACKING:
- handleEnableLocationTracking(msg.arg1 == 1);
- break;
- case REQUEST_SINGLE_SHOT:
- handleRequestSingleShot();
+ case SET_REQUEST:
+ GpsRequest gpsRequest = (GpsRequest) msg.obj;
+ handleSetRequest(gpsRequest.request, gpsRequest.source);
break;
case UPDATE_NETWORK_STATE:
handleUpdateNetworkState(msg.arg1, (NetworkInfo)msg.obj);
@@ -1594,22 +1473,10 @@ public class GpsLocationProvider implements LocationProviderInterface {
case UPDATE_LOCATION:
handleUpdateLocation((Location)msg.obj);
break;
- case ADD_LISTENER:
- handleAddListener(msg.arg1);
- break;
- case REMOVE_LISTENER:
- handleRemoveListener(msg.arg1);
- break;
}
- // release wake lock if no messages are pending
- synchronized (mWakeLock) {
- mPendingMessageBits &= ~(1 << message);
- if (message == ADD_LISTENER || message == REMOVE_LISTENER) {
- mPendingListenerMessages--;
- }
- if (mPendingMessageBits == 0 && mPendingListenerMessages == 0) {
- mWakeLock.release();
- }
+ if (msg.arg2 == 1) {
+ // wakelock was taken for this message, release it
+ mWakeLock.release();
}
}
};
@@ -1620,17 +1487,39 @@ public class GpsLocationProvider implements LocationProviderInterface {
super("GpsLocationProvider");
}
+ @Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
initialize();
Looper.prepare();
+
+ LocationManager locManager =
+ (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);
mHandler = new ProviderHandler();
// signal when we are initialized and ready to go
mInitializedLatch.countDown();
+ locManager.requestLocationUpdates(LocationManager.PASSIVE_PROVIDER,
+ 0, 0, new NetworkLocationListener(), Looper.myLooper());
Looper.loop();
}
}
+ private final class NetworkLocationListener implements LocationListener {
+ @Override
+ public void onLocationChanged(Location location) {
+ // this callback happens on mHandler looper
+ if (LocationManager.NETWORK_PROVIDER.equals(location.getProvider())) {
+ handleUpdateLocation(location);
+ }
+ }
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) { }
+ @Override
+ public void onProviderEnabled(String provider) { }
+ @Override
+ public void onProviderDisabled(String provider) { }
+ }
+
private String getSelectedApn() {
Uri uri = Uri.parse("content://telephony/carriers/preferapn");
String apn = null;
@@ -1650,6 +1539,22 @@ public class GpsLocationProvider implements LocationProviderInterface {
return apn;
}
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ StringBuilder s = new StringBuilder();
+ s.append(" mFixInterval=").append(mFixInterval).append("\n");
+ s.append(" mEngineCapabilities=0x").append(Integer.toHexString(mEngineCapabilities)).append(" (");
+ if (hasCapability(GPS_CAPABILITY_SCHEDULING)) s.append("SCHED ");
+ if (hasCapability(GPS_CAPABILITY_MSB)) s.append("MSB ");
+ if (hasCapability(GPS_CAPABILITY_MSA)) s.append("MSA ");
+ if (hasCapability(GPS_CAPABILITY_SINGLE_SHOT)) s.append("SINGLE_SHOT ");
+ if (hasCapability(GPS_CAPABILITY_ON_DEMAND_TIME)) s.append("ON_DEMAND_TIME ");
+ s.append(")\n");
+
+ s.append(native_get_internal_state());
+ pw.append(s);
+ }
+
// for GPS SV statistics
private static final int MAX_SVS = 32;
private static final int EPHEMERIS_MASK = 0;
diff --git a/services/java/com/android/server/location/LocationProviderInterface.java b/services/java/com/android/server/location/LocationProviderInterface.java
index 858a582..6f09232 100644
--- a/services/java/com/android/server/location/LocationProviderInterface.java
+++ b/services/java/com/android/server/location/LocationProviderInterface.java
@@ -16,42 +16,33 @@
package com.android.server.location;
-import android.location.Criteria;
-import android.location.Location;
-import android.net.NetworkInfo;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
+
import android.os.Bundle;
import android.os.WorkSource;
/**
* Location Manager's interface for location providers.
- *
- * {@hide}
+ * @hide
*/
public interface LocationProviderInterface {
- String getName();
- boolean requiresNetwork();
- boolean requiresSatellite();
- boolean requiresCell();
- boolean hasMonetaryCost();
- boolean supportsAltitude();
- boolean supportsSpeed();
- boolean supportsBearing();
- int getPowerRequirement();
- boolean meetsCriteria(Criteria criteria);
- int getAccuracy();
- boolean isEnabled();
- void enable();
- void disable();
- int getStatus(Bundle extras);
- long getStatusUpdateTime();
- void enableLocationTracking(boolean enable);
- /* returns false if single shot is not supported */
- boolean requestSingleShotFix();
- String getInternalState();
- void setMinTime(long minTime, WorkSource ws);
- void updateNetworkState(int state, NetworkInfo info);
- void updateLocation(Location location);
- boolean sendExtraCommand(String command, Bundle extras);
- void addListener(int uid);
- void removeListener(int uid);
+ public String getName();
+
+ public void enable();
+ public void disable();
+ public boolean isEnabled();
+ public void setRequest(ProviderRequest request, WorkSource source);
+
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args);
+
+ // --- deprecated (but still supported) ---
+ public ProviderProperties getProperties();
+ public int getStatus(Bundle extras);
+ public long getStatusUpdateTime();
+ public boolean sendExtraCommand(String command, Bundle extras);
}
diff --git a/services/java/com/android/server/location/LocationProviderProxy.java b/services/java/com/android/server/location/LocationProviderProxy.java
index a227ab6..7faf72c 100644
--- a/services/java/com/android/server/location/LocationProviderProxy.java
+++ b/services/java/com/android/server/location/LocationProviderProxy.java
@@ -16,458 +16,272 @@
package com.android.server.location;
-import android.content.ComponentName;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+
import android.content.Context;
-import android.content.Intent;
-import android.content.ServiceConnection;
-import android.location.Criteria;
-import android.location.ILocationProvider;
-import android.location.Location;
-import android.net.NetworkInfo;
+import android.location.LocationProvider;
import android.os.Bundle;
import android.os.Handler;
-import android.os.IBinder;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
-import com.android.internal.location.DummyLocationProvider;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ILocationProvider;
+import com.android.internal.location.ProviderRequest;
+import com.android.server.LocationManagerService;
+import com.android.server.ServiceWatcher;
/**
- * A class for proxying location providers implemented as services.
- *
- * {@hide}
+ * Proxy for ILocationProvider implementations.
*/
public class LocationProviderProxy implements LocationProviderInterface {
-
private static final String TAG = "LocationProviderProxy";
-
- public static final String SERVICE_ACTION =
- "com.android.location.service.NetworkLocationProvider";
+ private static final boolean D = LocationManagerService.D;
private final Context mContext;
private final String mName;
- private final Intent mIntent;
- private final Handler mHandler;
- private final Object mMutex = new Object(); // synchronizes access to non-final members
- private Connection mServiceConnection; // never null after ctor
+ private final ServiceWatcher mServiceWatcher;
+
+ private Object mLock = new Object();
- // cached values set by the location manager
- private boolean mLocationTracking = false;
+ // cached values set by the location manager, synchronized on mLock
+ private ProviderProperties mProperties;
private boolean mEnabled = false;
- private long mMinTime = -1;
- private WorkSource mMinTimeSource = new WorkSource();
- private int mNetworkState;
- private NetworkInfo mNetworkInfo;
-
- // constructor for proxying location providers implemented in a separate service
- public LocationProviderProxy(Context context, String name, String packageName,
- Handler handler) {
+ private ProviderRequest mRequest = null;
+ private WorkSource mWorksource = new WorkSource();
+
+ public static LocationProviderProxy createAndBind(Context context, String name, String action,
+ List<String> initialPackageNames, Handler handler) {
+ LocationProviderProxy proxy = new LocationProviderProxy(context, name, action,
+ initialPackageNames, handler);
+ if (proxy.bind()) {
+ return proxy;
+ } else {
+ return null;
+ }
+ }
+
+ private LocationProviderProxy(Context context, String name, String action,
+ List<String> initialPackageNames, Handler handler) {
mContext = context;
mName = name;
- mIntent = new Intent(SERVICE_ACTION);
- mHandler = handler;
- reconnect(packageName);
+ mServiceWatcher = new ServiceWatcher(mContext, TAG, action, initialPackageNames,
+ mNewServiceWork, handler);
}
- /** Bind to service. Will reconnect if already connected */
- public void reconnect(String packageName) {
- synchronized (mMutex) {
- if (mServiceConnection != null) {
- mContext.unbindService(mServiceConnection);
- }
- mServiceConnection = new Connection();
- mIntent.setPackage(packageName);
- mContext.bindService(mIntent, mServiceConnection,
- Context.BIND_AUTO_CREATE | Context.BIND_NOT_FOREGROUND |
- Context.BIND_ALLOW_OOM_MANAGEMENT);
- }
+ private boolean bind () {
+ return mServiceWatcher.start();
}
- private class Connection implements ServiceConnection, Runnable {
-
- private ILocationProvider mProvider;
-
- // for caching requiresNetwork, requiresSatellite, etc.
- private DummyLocationProvider mCachedAttributes; // synchronized by mMutex
+ private ILocationProvider getService() {
+ return ILocationProvider.Stub.asInterface(mServiceWatcher.getBinder());
+ }
- public void onServiceConnected(ComponentName className, IBinder service) {
- synchronized (this) {
- mProvider = ILocationProvider.Stub.asInterface(service);
- if (mProvider != null) {
- mHandler.post(this);
- }
- }
- }
+ public String getConnectedPackageName() {
+ return mServiceWatcher.getBestPackageName();
+ }
- public void onServiceDisconnected(ComponentName className) {
- synchronized (this) {
- mProvider = null;
+ /**
+ * Work to apply current state to a newly connected provider.
+ * Remember we can switch the service that implements a providers
+ * at run-time, so need to apply current state.
+ */
+ private Runnable mNewServiceWork = new Runnable() {
+ @Override
+ public void run() {
+ if (D) Log.d(TAG, "applying state to connected service");
+
+ boolean enabled;
+ ProviderProperties properties = null;
+ ProviderRequest request;
+ WorkSource source;
+ ILocationProvider service;
+ synchronized (mLock) {
+ enabled = mEnabled;
+ request = mRequest;
+ source = mWorksource;
+ service = getService();
}
- }
- public synchronized ILocationProvider getProvider() {
- return mProvider;
- }
-
- public synchronized DummyLocationProvider getCachedAttributes() {
- return mCachedAttributes;
- }
+ if (service == null) return;
- public void run() {
- synchronized (mMutex) {
- if (mServiceConnection != this) {
- // This ServiceConnection no longer the one we want to bind to.
- return;
- }
- ILocationProvider provider = getProvider();
- if (provider == null) {
- return;
+ try {
+ // load properties from provider
+ properties = service.getProperties();
+ if (properties == null) {
+ Log.e(TAG, mServiceWatcher.getBestPackageName() +
+ " has invalid locatino provider properties");
}
- // resend previous values from the location manager if the service has restarted
- try {
- if (mEnabled) {
- provider.enable();
- }
- if (mLocationTracking) {
- provider.enableLocationTracking(true);
+ // apply current state to new service
+ if (enabled) {
+ service.enable();
+ if (request != null) {
+ service.setRequest(request, source);
}
- if (mMinTime >= 0) {
- provider.setMinTime(mMinTime, mMinTimeSource);
- }
- if (mNetworkInfo != null) {
- provider.updateNetworkState(mNetworkState, mNetworkInfo);
- }
- } catch (RemoteException e) {
}
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
- // init cache of parameters
- if (mCachedAttributes == null) {
- try {
- mCachedAttributes = new DummyLocationProvider(mName, null);
- mCachedAttributes.setRequiresNetwork(provider.requiresNetwork());
- mCachedAttributes.setRequiresSatellite(provider.requiresSatellite());
- mCachedAttributes.setRequiresCell(provider.requiresCell());
- mCachedAttributes.setHasMonetaryCost(provider.hasMonetaryCost());
- mCachedAttributes.setSupportsAltitude(provider.supportsAltitude());
- mCachedAttributes.setSupportsSpeed(provider.supportsSpeed());
- mCachedAttributes.setSupportsBearing(provider.supportsBearing());
- mCachedAttributes.setPowerRequirement(provider.getPowerRequirement());
- mCachedAttributes.setAccuracy(provider.getAccuracy());
- } catch (RemoteException e) {
- mCachedAttributes = null;
- }
- }
+ synchronized (mLock) {
+ mProperties = properties;
}
}
};
+ @Override
public String getName() {
return mName;
}
- private DummyLocationProvider getCachedAttributes() {
- synchronized (mMutex) {
- return mServiceConnection.getCachedAttributes();
- }
- }
-
- public boolean requiresNetwork() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.requiresNetwork();
- } else {
- return false;
- }
- }
-
- public boolean requiresSatellite() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.requiresSatellite();
- } else {
- return false;
- }
- }
-
- public boolean requiresCell() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.requiresCell();
- } else {
- return false;
- }
- }
-
- public boolean hasMonetaryCost() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.hasMonetaryCost();
- } else {
- return false;
- }
- }
-
- public boolean supportsAltitude() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.supportsAltitude();
- } else {
- return false;
- }
- }
-
- public boolean supportsSpeed() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.supportsSpeed();
- } else {
- return false;
- }
- }
-
- public boolean supportsBearing() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.supportsBearing();
- } else {
- return false;
- }
- }
-
- public int getPowerRequirement() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.getPowerRequirement();
- } else {
- return -1;
+ @Override
+ public ProviderProperties getProperties() {
+ synchronized (mLock) {
+ return mProperties;
}
}
- public int getAccuracy() {
- DummyLocationProvider cachedAttributes = getCachedAttributes();
- if (cachedAttributes != null) {
- return cachedAttributes.getAccuracy();
- } else {
- return -1;
- }
- }
-
- public boolean meetsCriteria(Criteria criteria) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- return provider.meetsCriteria(criteria);
- } catch (RemoteException e) {
- }
- }
- }
- // default implementation if we lost connection to the provider
- if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
- (criteria.getAccuracy() < getAccuracy())) {
- return false;
- }
- int criteriaPower = criteria.getPowerRequirement();
- if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
- (criteriaPower < getPowerRequirement())) {
- return false;
- }
- if (criteria.isAltitudeRequired() && !supportsAltitude()) {
- return false;
- }
- if (criteria.isSpeedRequired() && !supportsSpeed()) {
- return false;
- }
- if (criteria.isBearingRequired() && !supportsBearing()) {
- return false;
- }
- return true;
- }
-
+ @Override
public void enable() {
- synchronized (mMutex) {
+ synchronized (mLock) {
mEnabled = true;
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.enable();
- } catch (RemoteException e) {
- }
- }
}
- }
+ ILocationProvider service = getService();
+ if (service == null) return;
- public void disable() {
- synchronized (mMutex) {
- mEnabled = false;
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.disable();
- } catch (RemoteException e) {
- }
- }
+ try {
+ service.enable();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
}
- public boolean isEnabled() {
- synchronized (mMutex) {
- return mEnabled;
+ @Override
+ public void disable() {
+ synchronized (mLock) {
+ mEnabled = false;
}
- }
+ ILocationProvider service = getService();
+ if (service == null) return;
- public int getStatus(Bundle extras) {
- ILocationProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
+ try {
+ service.disable();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
- if (provider != null) {
- try {
- return provider.getStatus(extras);
- } catch (RemoteException e) {
- }
- }
- return 0;
}
- public long getStatusUpdateTime() {
- ILocationProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
- if (provider != null) {
- try {
- return provider.getStatusUpdateTime();
- } catch (RemoteException e) {
- }
- }
- return 0;
- }
-
- public String getInternalState() {
- ILocationProvider provider;
- synchronized (mMutex) {
- provider = mServiceConnection.getProvider();
- }
- if (provider != null) {
- try {
- return provider.getInternalState();
- } catch (RemoteException e) {
- Log.e(TAG, "getInternalState failed", e);
- }
+ @Override
+ public boolean isEnabled() {
+ synchronized (mLock) {
+ return mEnabled;
}
- return null;
}
- public boolean isLocationTracking() {
- synchronized (mMutex) {
- return mLocationTracking;
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) {
+ synchronized (mLock) {
+ mRequest = request;
+ mWorksource = source;
}
- }
+ ILocationProvider service = getService();
+ if (service == null) return;
- public void enableLocationTracking(boolean enable) {
- synchronized (mMutex) {
- mLocationTracking = enable;
- if (!enable) {
- mMinTime = -1;
- mMinTimeSource.clear();
- }
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.enableLocationTracking(enable);
- } catch (RemoteException e) {
- }
- }
+ try {
+ service.setRequest(request, source);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
}
- public boolean requestSingleShotFix() {
- return false;
- }
-
- public long getMinTime() {
- synchronized (mMutex) {
- return mMinTime;
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.append("REMOTE SERVICE");
+ pw.append(" name=").append(mName);
+ pw.append(" pkg=").append(mServiceWatcher.getBestPackageName());
+ pw.append(" version=").append("" + mServiceWatcher.getBestVersion());
+ pw.append('\n');
+
+ ILocationProvider service = getService();
+ if (service == null) {
+ pw.println("service down (null)");
+ return;
+ }
+ pw.flush();
+
+ try {
+ service.asBinder().dump(fd, args);
+ } catch (RemoteException e) {
+ pw.println("service down (RemoteException)");
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ pw.println("service down (Exception)");
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
}
- public void setMinTime(long minTime, WorkSource ws) {
- synchronized (mMutex) {
- mMinTime = minTime;
- mMinTimeSource.set(ws);
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.setMinTime(minTime, ws);
- } catch (RemoteException e) {
- }
- }
- }
+ @Override
+ public int getStatus(Bundle extras) {
+ ILocationProvider service = getService();
+ if (service == null) return LocationProvider.TEMPORARILY_UNAVAILABLE;
+
+ try {
+ return service.getStatus(extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
+ }
+ return LocationProvider.TEMPORARILY_UNAVAILABLE;
}
- public void updateNetworkState(int state, NetworkInfo info) {
- synchronized (mMutex) {
- mNetworkState = state;
- mNetworkInfo = info;
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.updateNetworkState(state, info);
- } catch (RemoteException e) {
- }
- }
- }
- }
+ @Override
+ public long getStatusUpdateTime() {
+ ILocationProvider service = getService();
+ if (service == null) return 0;
- public void updateLocation(Location location) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.updateLocation(location);
- } catch (RemoteException e) {
- }
- }
+ try {
+ return service.getStatusUpdateTime();
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
+ return 0;
}
+ @Override
public boolean sendExtraCommand(String command, Bundle extras) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- return provider.sendExtraCommand(command, extras);
- } catch (RemoteException e) {
- }
- }
- }
- return false;
- }
+ ILocationProvider service = getService();
+ if (service == null) return false;
- public void addListener(int uid) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.addListener(uid);
- } catch (RemoteException e) {
- }
- }
- }
- }
-
- public void removeListener(int uid) {
- synchronized (mMutex) {
- ILocationProvider provider = mServiceConnection.getProvider();
- if (provider != null) {
- try {
- provider.removeListener(uid);
- } catch (RemoteException e) {
- }
- }
+ try {
+ return service.sendExtraCommand(command, extras);
+ } catch (RemoteException e) {
+ Log.w(TAG, e);
+ } catch (Exception e) {
+ // never let remote service crash system server
+ Log.e(TAG, "Exception from " + mServiceWatcher.getBestPackageName(), e);
}
+ return false;
}
-}
+ }
diff --git a/services/java/com/android/server/location/MockProvider.java b/services/java/com/android/server/location/MockProvider.java
index 09d799f..36c43ff 100644
--- a/services/java/com/android/server/location/MockProvider.java
+++ b/services/java/com/android/server/location/MockProvider.java
@@ -20,15 +20,19 @@ import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationProvider;
-import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
import android.util.PrintWriterPrinter;
+
+import java.io.FileDescriptor;
import java.io.PrintWriter;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
/**
* A mock location provider used by LocationManagerService to implement test providers.
*
@@ -36,60 +40,56 @@ import java.io.PrintWriter;
*/
public class MockProvider implements LocationProviderInterface {
private final String mName;
+ private final ProviderProperties mProperties;
private final ILocationManager mLocationManager;
- private final boolean mRequiresNetwork;
- private final boolean mRequiresSatellite;
- private final boolean mRequiresCell;
- private final boolean mHasMonetaryCost;
- private final boolean mSupportsAltitude;
- private final boolean mSupportsSpeed;
- private final boolean mSupportsBearing;
- private final int mPowerRequirement;
- private final int mAccuracy;
+
private final Location mLocation;
+ private final Bundle mExtras = new Bundle();
+
private int mStatus;
private long mStatusUpdateTime;
- private final Bundle mExtras = new Bundle();
private boolean mHasLocation;
private boolean mHasStatus;
private boolean mEnabled;
private static final String TAG = "MockProvider";
- public MockProvider(String name, ILocationManager locationManager,
- boolean requiresNetwork, boolean requiresSatellite,
- boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
- boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ public MockProvider(String name, ILocationManager locationManager,
+ ProviderProperties properties) {
+ if (properties == null) throw new NullPointerException("properties is null");
+
mName = name;
mLocationManager = locationManager;
- mRequiresNetwork = requiresNetwork;
- mRequiresSatellite = requiresSatellite;
- mRequiresCell = requiresCell;
- mHasMonetaryCost = hasMonetaryCost;
- mSupportsAltitude = supportsAltitude;
- mSupportsBearing = supportsBearing;
- mSupportsSpeed = supportsSpeed;
- mPowerRequirement = powerRequirement;
- mAccuracy = accuracy;
+ mProperties = properties;
mLocation = new Location(name);
}
+ @Override
public String getName() {
return mName;
}
+ @Override
+ public ProviderProperties getProperties() {
+ return mProperties;
+ }
+
+ @Override
public void disable() {
mEnabled = false;
}
+ @Override
public void enable() {
mEnabled = true;
}
+ @Override
public boolean isEnabled() {
return mEnabled;
}
+ @Override
public int getStatus(Bundle extras) {
if (mHasStatus) {
extras.clear();
@@ -100,75 +100,20 @@ public class MockProvider implements LocationProviderInterface {
}
}
+ @Override
public long getStatusUpdateTime() {
return mStatusUpdateTime;
}
- public int getAccuracy() {
- return mAccuracy;
- }
-
- public int getPowerRequirement() {
- return mPowerRequirement;
- }
-
- public boolean hasMonetaryCost() {
- return mHasMonetaryCost;
- }
-
- public boolean requiresCell() {
- return mRequiresCell;
- }
-
- public boolean requiresNetwork() {
- return mRequiresNetwork;
- }
-
- public boolean requiresSatellite() {
- return mRequiresSatellite;
- }
-
- public boolean supportsAltitude() {
- return mSupportsAltitude;
- }
-
- public boolean supportsBearing() {
- return mSupportsBearing;
- }
-
- public boolean supportsSpeed() {
- return mSupportsSpeed;
- }
-
- public boolean meetsCriteria(Criteria criteria) {
- if ((criteria.getAccuracy() != Criteria.NO_REQUIREMENT) &&
- (criteria.getAccuracy() < mAccuracy)) {
- return false;
- }
- int criteriaPower = criteria.getPowerRequirement();
- if ((criteriaPower != Criteria.NO_REQUIREMENT) &&
- (criteriaPower < mPowerRequirement)) {
- return false;
- }
- if (criteria.isAltitudeRequired() && !mSupportsAltitude) {
- return false;
- }
- if (criteria.isSpeedRequired() && !mSupportsSpeed) {
- return false;
- }
- if (criteria.isBearingRequired() && !mSupportsBearing) {
- return false;
- }
- return true;
- }
-
public void setLocation(Location l) {
mLocation.set(l);
mHasLocation = true;
- try {
- mLocationManager.reportLocation(mLocation, false);
- } catch (RemoteException e) {
- Log.e(TAG, "RemoteException calling reportLocation");
+ if (mEnabled) {
+ try {
+ mLocationManager.reportLocation(mLocation, false);
+ } catch (RemoteException e) {
+ Log.e(TAG, "RemoteException calling reportLocation");
+ }
}
}
@@ -191,34 +136,9 @@ public class MockProvider implements LocationProviderInterface {
mStatusUpdateTime = 0;
}
- public String getInternalState() {
- return null;
- }
-
- public void enableLocationTracking(boolean enable) {
- }
-
- public boolean requestSingleShotFix() {
- return false;
- }
-
- public void setMinTime(long minTime, WorkSource ws) {
- }
-
- public void updateNetworkState(int state, NetworkInfo info) {
- }
-
- public void updateLocation(Location location) {
- }
-
- public boolean sendExtraCommand(String command, Bundle extras) {
- return false;
- }
-
- public void addListener(int uid) {
- }
-
- public void removeListener(int uid) {
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ dump(pw, "");
}
public void dump(PrintWriter pw, String prefix) {
@@ -231,4 +151,12 @@ public class MockProvider implements LocationProviderInterface {
pw.println(prefix + "mStatusUpdateTime=" + mStatusUpdateTime);
pw.println(prefix + "mExtras=" + mExtras);
}
+
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) { }
+
+ @Override
+ public boolean sendExtraCommand(String command, Bundle extras) {
+ return false;
+ }
}
diff --git a/services/java/com/android/server/location/PassiveProvider.java b/services/java/com/android/server/location/PassiveProvider.java
index ea0d1b0..0ce21b7 100644
--- a/services/java/com/android/server/location/PassiveProvider.java
+++ b/services/java/com/android/server/location/PassiveProvider.java
@@ -16,17 +16,23 @@
package com.android.server.location;
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.location.ProviderRequest;
+
import android.location.Criteria;
import android.location.ILocationManager;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationProvider;
-import android.net.NetworkInfo;
import android.os.Bundle;
import android.os.RemoteException;
import android.os.WorkSource;
import android.util.Log;
+
/**
* A passive location provider reports locations received from other providers
* for clients that want to listen passively without actually triggering
@@ -35,103 +41,63 @@ import android.util.Log;
* {@hide}
*/
public class PassiveProvider implements LocationProviderInterface {
-
private static final String TAG = "PassiveProvider";
+ private static final ProviderProperties PROPERTIES = new ProviderProperties(
+ false, false, false, false, false, false, false,
+ Criteria.POWER_LOW, Criteria.ACCURACY_COARSE);
+
private final ILocationManager mLocationManager;
- private boolean mTracking;
+ private boolean mReportLocation;
public PassiveProvider(ILocationManager locationManager) {
mLocationManager = locationManager;
}
+ @Override
public String getName() {
return LocationManager.PASSIVE_PROVIDER;
}
- public boolean requiresNetwork() {
- return false;
- }
-
- public boolean requiresSatellite() {
- return false;
- }
-
- public boolean requiresCell() {
- return false;
- }
-
- public boolean hasMonetaryCost() {
- return false;
- }
-
- public boolean supportsAltitude() {
- return false;
- }
-
- public boolean supportsSpeed() {
- return false;
- }
-
- public boolean supportsBearing() {
- return false;
- }
-
- public int getPowerRequirement() {
- return -1;
- }
-
- public boolean meetsCriteria(Criteria criteria) {
- // We do not want to match the special passive provider based on criteria.
- return false;
- }
-
- public int getAccuracy() {
- return -1;
+ @Override
+ public ProviderProperties getProperties() {
+ return PROPERTIES;
}
+ @Override
public boolean isEnabled() {
return true;
}
+ @Override
public void enable() {
}
+ @Override
public void disable() {
}
+ @Override
public int getStatus(Bundle extras) {
- if (mTracking) {
+ if (mReportLocation) {
return LocationProvider.AVAILABLE;
} else {
return LocationProvider.TEMPORARILY_UNAVAILABLE;
}
}
+ @Override
public long getStatusUpdateTime() {
return -1;
}
- public String getInternalState() {
- return null;
- }
-
- public void enableLocationTracking(boolean enable) {
- mTracking = enable;
- }
-
- public boolean requestSingleShotFix() {
- return false;
- }
-
- public void setMinTime(long minTime, WorkSource ws) {
- }
-
- public void updateNetworkState(int state, NetworkInfo info) {
+ @Override
+ public void setRequest(ProviderRequest request, WorkSource source) {
+ mReportLocation = request.reportLocation;
}
public void updateLocation(Location location) {
- if (mTracking) {
+ if (mReportLocation) {
try {
// pass the location back to the location manager
mLocationManager.reportLocation(location, true);
@@ -141,13 +107,13 @@ public class PassiveProvider implements LocationProviderInterface {
}
}
+ @Override
public boolean sendExtraCommand(String command, Bundle extras) {
return false;
}
- public void addListener(int uid) {
- }
-
- public void removeListener(int uid) {
+ @Override
+ public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
+ pw.println("mReportLocaiton=" + mReportLocation);
}
}