summaryrefslogtreecommitdiffstats
path: root/location/java
diff options
context:
space:
mode:
authorThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:43 -0800
committerThe Android Open Source Project <initial-contribution@android.com>2008-12-17 18:05:43 -0800
commitf013e1afd1e68af5e3b868c26a653bbfb39538f8 (patch)
tree7ad6c8fd9c7b55f4b4017171dec1cb760bbd26bf /location/java
parente70cfafe580c6f2994c4827cd8a534aabf3eb05c (diff)
downloadframeworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.zip
frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.gz
frameworks_base-f013e1afd1e68af5e3b868c26a653bbfb39538f8.tar.bz2
Code drop from //branches/cupcake/...@124589
Diffstat (limited to 'location/java')
-rw-r--r--location/java/android/location/Geocoder.java2
-rw-r--r--location/java/android/location/GpsSatellite.java117
-rw-r--r--location/java/android/location/GpsStatus.java200
-rw-r--r--location/java/android/location/GpsStatusListener.java58
-rw-r--r--location/java/android/location/ILocationManager.aidl3
-rw-r--r--location/java/android/location/LocationManager.java265
-rw-r--r--location/java/com/android/internal/location/CellState.java187
-rw-r--r--location/java/com/android/internal/location/GpsLocationProvider.java18
-rw-r--r--location/java/com/android/internal/location/LocationCache.java53
-rw-r--r--location/java/com/android/internal/location/LocationCollector.java6
-rw-r--r--location/java/com/android/internal/location/LocationMasfClient.java149
-rw-r--r--location/java/com/android/internal/location/NetworkLocationProvider.java4
-rw-r--r--location/java/com/android/internal/location/protocol/GDebugProfile.java1
-rw-r--r--location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java4
14 files changed, 834 insertions, 233 deletions
diff --git a/location/java/android/location/Geocoder.java b/location/java/android/location/Geocoder.java
index 92a19e4..709ad23 100644
--- a/location/java/android/location/Geocoder.java
+++ b/location/java/android/location/Geocoder.java
@@ -228,7 +228,7 @@ public final class Geocoder {
ArrayList<Address> result = new ArrayList<Address>();
String ex = mService.getFromLocationName(locationName,
lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
- maxResults, mLanguage, mCountry, mVariant, mAppName, new ArrayList<Address>());
+ maxResults, mLanguage, mCountry, mVariant, mAppName, result);
if (ex != null) {
throw new IOException(ex);
} else {
diff --git a/location/java/android/location/GpsSatellite.java b/location/java/android/location/GpsSatellite.java
new file mode 100644
index 0000000..17af4a6
--- /dev/null
+++ b/location/java/android/location/GpsSatellite.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2008 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 android.location;
+
+/**
+ * This class represents the current state of a GPS satellite.
+ * This class is used in conjunction with the {@link GpsStatus} class.
+ */
+public final class GpsSatellite {
+ /* These package private values are modified by the GpsStatus class */
+ boolean mValid;
+ boolean mHasEphemeris;
+ boolean mHasAlmanac;
+ boolean mUsedInFix;
+ int mPrn;
+ float mSnr;
+ float mElevation;
+ float mAzimuth;
+
+ GpsSatellite(int prn) {
+ mPrn = prn;
+ }
+
+ /**
+ * Used by {@link LocationManager#getGpsStatus} to copy LocationManager's
+ * cached GpsStatus instance to the client's copy.
+ */
+ void setStatus(GpsSatellite satellite) {
+ mValid = satellite.mValid;
+ mHasEphemeris = satellite.mHasEphemeris;
+ mHasAlmanac = satellite.mHasAlmanac;
+ mUsedInFix = satellite.mUsedInFix;
+ mSnr = satellite.mSnr;
+ mElevation = satellite.mElevation;
+ mAzimuth = satellite.mAzimuth;
+ }
+
+ /**
+ * Returns the PRN (pseudo-random number) for the satellite.
+ *
+ * @return PRN number
+ */
+ public int getPrn() {
+ return mPrn;
+ }
+
+ /**
+ * Returns the signal to noise ratio for the satellite.
+ *
+ * @return the signal to noise ratio
+ */
+ public float getSnr() {
+ return mSnr;
+ }
+
+ /**
+ * Returns the elevation of the satellite in degrees.
+ * The elevation can vary between 0 and 90.
+ *
+ * @return the elevation in degrees
+ */
+ public float getElevation() {
+ return mElevation;
+ }
+
+ /**
+ * Returns the azimuth of the satellite in degrees.
+ * The azimuth can vary between 0 and 360.
+ *
+ * @return the azimuth in degrees
+ */
+ public float getAzimuth() {
+ return mAzimuth;
+ }
+
+ /**
+ * Returns true if the GPS engine has ephemeris data for the satellite.
+ *
+ * @return true if the satellite has ephemeris data
+ */
+ public boolean hasEphemeris() {
+ return mHasEphemeris;
+ }
+
+ /**
+ * Returns true if the GPS engine has almanac data for the satellite.
+ *
+ * @return true if the satellite has almanac data
+ */
+ public boolean hasAlmanac() {
+ return mHasAlmanac;
+ }
+
+ /**
+ * Returns true if the satellite was used by the GPS engine when
+ * calculating the most recent GPS fix.
+ *
+ * @return true if the satellite was used to compute the most recent fix.
+ */
+ public boolean usedInFix() {
+ return mUsedInFix;
+ }
+}
diff --git a/location/java/android/location/GpsStatus.java b/location/java/android/location/GpsStatus.java
new file mode 100644
index 0000000..a40b1fb
--- /dev/null
+++ b/location/java/android/location/GpsStatus.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2008 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 android.location;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+
+/**
+ * This class represents the current state of the GPS engine.
+ * This class is used in conjunction with the {@link Listener} interface.
+ */
+public final class GpsStatus {
+ private static final int NUM_SATELLITES = 32;
+
+ /* These package private values are modified by the LocationManager class */
+ private int mTimeToFirstFix;
+ private GpsSatellite mSatellites[] = new GpsSatellite[NUM_SATELLITES];
+
+ private final class SatelliteIterator implements Iterator<GpsSatellite> {
+
+ private GpsSatellite[] mSatellites;
+ int mIndex = 0;
+
+ SatelliteIterator(GpsSatellite[] satellites) {
+ mSatellites = satellites;
+ }
+
+ public boolean hasNext() {
+ for (int i = mIndex; i < mSatellites.length; i++) {
+ if (mSatellites[i].mValid) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public GpsSatellite next() {
+ while (mIndex < mSatellites.length) {
+ GpsSatellite satellite = mSatellites[mIndex++];
+ if (satellite.mValid) {
+ return satellite;
+ }
+ }
+ throw new NoSuchElementException();
+ }
+
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private Iterable<GpsSatellite> mSatelliteList = new Iterable<GpsSatellite>() {
+ public Iterator<GpsSatellite> iterator() {
+ return new SatelliteIterator(mSatellites);
+ }
+ };
+
+ /**
+ * Event sent when the GPS system has started.
+ */
+ public static final int GPS_EVENT_STARTED = 1;
+
+ /**
+ * Event sent when the GPS system has stopped.
+ */
+ public static final int GPS_EVENT_STOPPED = 2;
+
+ /**
+ * Event sent when the GPS system has received its first fix since starting.
+ * Call {@link #getTimeToFirstFix()} to find the time from start to first fix.
+ */
+ public static final int GPS_EVENT_FIRST_FIX = 3;
+
+ /**
+ * Event sent periodically to report GPS satellite status.
+ * Call {@link #getSatellites()} to retrieve the status for each satellite.
+ */
+ public static final int GPS_EVENT_SATELLITE_STATUS = 4;
+
+ /**
+ * Used for receiving notifications when GPS status has changed.
+ */
+ public interface Listener {
+ /**
+ * Called to report changes in the GPS status.
+ * The event number is one of:
+ * <ul>
+ * <li> {@link GpsStatus#GPS_EVENT_STARTED}
+ * <li> {@link GpsStatus#GPS_EVENT_STOPPED}
+ * <li> {@link GpsStatus#GPS_EVENT_FIRST_FIX}
+ * <li> {@link GpsStatus#GPS_EVENT_SATELLITE_STATUS}
+ * </ul>
+ *
+ * When this method is called, the client should call
+ * {@link LocationManager#getGpsStatus} to get additional
+ * status information.
+ *
+ * @param event event number for this notification
+ */
+ void onGpsStatusChanged(int event);
+ }
+
+ GpsStatus() {
+ for (int i = 0; i < mSatellites.length; i++) {
+ mSatellites[i] = new GpsSatellite(i + 1);
+ }
+ }
+
+ /**
+ * Used internally within {@link LocationManager} to copy GPS status
+ * data from the Location Manager Service to its cached GpsStatus instance.
+ * Is synchronized to ensure that GPS status updates are atomic.
+ */
+ synchronized void setStatus(int svCount, int[] prns, float[] snrs,
+ float[] elevations, float[] azimuths, int ephemerisMask,
+ int almanacMask, int usedInFixMask) {
+ int i;
+
+ for (i = 0; i < mSatellites.length; i++) {
+ mSatellites[i].mValid = false;
+ }
+
+ for (i = 0; i < svCount; i++) {
+ int prn = prns[i] - 1;
+ int prnShift = (1 << prn);
+ GpsSatellite satellite = mSatellites[prn];
+
+ satellite.mValid = true;
+ satellite.mSnr = snrs[i];
+ satellite.mElevation = elevations[i];
+ satellite.mAzimuth = azimuths[i];
+ satellite.mHasEphemeris = ((ephemerisMask & prnShift) != 0);
+ satellite.mHasAlmanac = ((almanacMask & prnShift) != 0);
+ satellite.mUsedInFix = ((usedInFixMask & prnShift) != 0);
+ }
+ }
+
+ /**
+ * Used by {@link LocationManager#getGpsStatus} to copy LocationManager's
+ * cached GpsStatus instance to the client's copy.
+ * Since this method is only used within {@link LocationManager#getGpsStatus},
+ * it does not need to be synchronized.
+ */
+ void setStatus(GpsStatus status) {
+ mTimeToFirstFix = status.getTimeToFirstFix();
+
+ for (int i = 0; i < mSatellites.length; i++) {
+ mSatellites[i].setStatus(status.mSatellites[i]);
+ }
+ }
+
+ void setTimeToFirstFix(int ttff) {
+ mTimeToFirstFix = ttff;
+ }
+
+ /**
+ * Returns the time required to receive the first fix since the most recent
+ * restart of the GPS engine.
+ *
+ * @return time to first fix in milliseconds
+ */
+ public int getTimeToFirstFix() {
+ return mTimeToFirstFix;
+ }
+
+ /**
+ * Returns an array of {@link GpsSatellite} objects, which represent the
+ * current state of the GPS engine.
+ *
+ * @return the list of satellites
+ */
+ public Iterable<GpsSatellite> getSatellites() {
+ return mSatelliteList;
+ }
+
+ /**
+ * Returns the maximum number of satellites that can be in the satellite
+ * list that can be returned by {@link #getSatellites()}.
+ *
+ * @return the maximum number of satellites
+ */
+ public int getMaxSatellites() {
+ return NUM_SATELLITES;
+ }
+}
diff --git a/location/java/android/location/GpsStatusListener.java b/location/java/android/location/GpsStatusListener.java
deleted file mode 100644
index e494887..0000000
--- a/location/java/android/location/GpsStatusListener.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2008 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 android.location;
-
-/**
- * Used for receiving notifications from the SensorManager when
- * sensor values have changed.
- *
- * @hide
- */
-public interface GpsStatusListener {
-
- /**
- * Called when the GPS has started.
- */
- void onGpsStarted();
-
- /**
- * Called when the GPS has stopped.
- */
- void onGpsStopped();
-
- /**
- * Called when the GPS status has received its first fix since starting.
- *
- * @param ttff Time to first fix in milliseconds.
- */
- void onFirstFix(int ttff);
-
- /**
- * Called when the GPS SV status has changed.
- *
- * @param svCount The number of visible SVs
- * @param prns Array of SV prns. Length of array is svCount.
- * @param snrs Array of signal to noise ratios for SVs, in 1/10 dB units. Length of array is svCount.
- * @param elevations Array of SV elevations in degrees. Length of array is svCount.
- * @param azimuths Array of SV azimuths in degrees. Length of array is svCount.
- * @param ephemerisMask Bit mask indicating which SVs the GPS has ephemeris data for.
- * @param almanacMask Bit mask indicating which SVs the GPS has almanac data for.
- * @param usedInFixMask Bit mask indicating which SVs were used in the most recent GPS fix.
- */
- public void onSvStatusChanged(int svCount, int[] prns, float[] snrs, float[] elevations,
- float[] azimuths, int ephemerisMask, int almanacMask, int usedInFixMask);
-}
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index d3eefeb..69c404a 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -37,7 +37,10 @@ interface ILocationManager
void requestLocationUpdates(String provider, long minTime, float minDistance,
in ILocationListener listener);
+ void requestLocationUpdatesPI(String provider, long minTime, float minDistance,
+ in PendingIntent intent);
void removeUpdates(in ILocationListener listener);
+ void removeUpdatesPI(in PendingIntent intent);
boolean addGpsStatusListener(IGpsStatusListener listener);
void removeGpsStatusListener(IGpsStatusListener listener);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index b5adb33..022ee25 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -47,8 +47,9 @@ import java.util.List;
public class LocationManager {
private static final String TAG = "LocationManager";
private ILocationManager mService;
- private HashMap<GpsStatusListener, GpsStatusListenerTransport> mGpsStatusListeners =
- new HashMap<GpsStatusListener, GpsStatusListenerTransport>();
+ private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners =
+ new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>();
+ private final GpsStatus mGpsStatus = new GpsStatus();
/**
* Name of the network location provider. This provider determines location based on
@@ -82,6 +83,24 @@ public class LocationManager {
*/
public static final String KEY_PROXIMITY_ENTERING = "entering";
+ /**
+ * Key used for a Bundle extra holding an Integer status value
+ * when a status change is broadcast using a PendingIntent.
+ */
+ public static final String KEY_STATUS_CHANGED = "status";
+
+ /**
+ * Key used for a Bundle extra holding an Boolean status value
+ * when a provider enabled/disabled event is broadcast using a PendingIntent.
+ */
+ public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
+
+ /**
+ * Key used for a Bundle extra holding a Location value
+ * when a location change is broadcast using a PendingIntent.
+ */
+ public static final String KEY_LOCATION_CHANGED = "location";
+
/** @hide -- does this belong here? */
public static final String PROVIDER_DIR = "/data/location";
@@ -180,7 +199,7 @@ public class LocationManager {
/**
* @hide - hide this constructor because it has a parameter
* of type ILocationManager, which is a system private class. The
- * right way to create an instance of this class is using the
+ * right way to create an instance of this class is using the
* factory Context.getSystemService.
*/
public LocationManager(ILocationManager service) {
@@ -686,11 +705,9 @@ public class LocationManager {
ListenerTransport transport = mListeners.get(listener);
if (transport == null) {
transport = new ListenerTransport(listener, looper);
- mListeners.put(listener, transport);
}
mListeners.put(listener, transport);
- mService.requestLocationUpdates(provider, minTime, minDistance,
- transport);
+ mService.requestLocationUpdates(provider, minTime, minDistance, transport);
}
} catch (RemoteException ex) {
Log.e(TAG, "requestLocationUpdates: DeadObjectException", ex);
@@ -698,6 +715,78 @@ public class LocationManager {
}
/**
+ * Registers the current activity to be notified periodically by
+ * the named provider. Periodically, the supplied PendingIntent will
+ * be broadcast with the current Location or with status updates.
+ *
+ * <p> Location updates are sent with a key of KEY_LOCATION_CHANGED and a Location value.
+ *
+ * <p> It may take a while to receive the most recent location. If
+ * an immediate location is required, applications may use the
+ * {@link #getLastKnownLocation(String)} method.
+ *
+ * <p> The frequency of notification or new locations may be controlled using the
+ * minTime and minDistance parameters. If minTime is greater than 0,
+ * the LocationManager could potentially rest for minTime milliseconds
+ * between location updates to conserve power. If minDistance is greater than 0,
+ * a location will only be broadcast if the device moves by minDistance meters.
+ * To obtain notifications as frequently as possible, set both parameters to 0.
+ *
+ * <p> Background services should be careful about setting a sufficiently high
+ * minTime so that the device doesn't consume too much power by keeping the
+ * GPS or wireless radios on all the time. In particular, values under 60000ms
+ * are not recommended.
+ *
+ * <p> In case the provider is disabled by the user, updates will stop,
+ * and an intent will be sent with an extra with key KEY_PROVIDER_ENABLED and a boolean value
+ * of false. If the provider is re-enabled, an intent will be sent with an
+ * extra with key KEY_PROVIDER_ENABLED and a boolean value of true and location updates will
+ * start again.
+ *
+ * <p> If the provider's status changes, an intent will be sent with an extra with key
+ * KEY_STATUS_CHANGED and an integer value indicating the new status. Any extras associated
+ * with the status update will be sent as well.
+ *
+ * @param provider the name of the provider with which to register
+ * @param minTime the minimum time interval for notifications, in
+ * milliseconds. This field is only used as a hint to conserve power, and actual
+ * time between location updates may be greater or lesser than this value.
+ * @param minDistance the minimum distance interval for notifications,
+ * in meters
+ * @param intent a {#link PendingIntet} to be sent for each location update
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present for the provider.
+ */
+ public void requestLocationUpdates(String provider,
+ long minTime, float minDistance, PendingIntent intent) {
+ if (provider == null) {
+ throw new IllegalArgumentException("provider==null");
+ }
+ if (intent == null) {
+ throw new IllegalArgumentException("intent==null");
+ }
+ _requestLocationUpdates(provider, minTime, minDistance, intent);
+ }
+
+ private void _requestLocationUpdates(String provider,
+ long minTime, float minDistance, PendingIntent intent) {
+ if (minTime < 0L) {
+ minTime = 0L;
+ }
+ if (minDistance < 0.0f) {
+ minDistance = 0.0f;
+ }
+
+ try {
+ mService.requestLocationUpdatesPI(provider, minTime, minDistance, intent);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "requestLocationUpdates: RemoteException", ex);
+ }
+ }
+
+ /**
* Removes any current registration for location updates of the current activity
* with the given LocationListener. Following this call, updates will no longer
* occur for this listener.
@@ -723,6 +812,28 @@ public class LocationManager {
}
/**
+ * Removes any current registration for location updates of the current activity
+ * with the given PendingIntent. Following this call, updates will no longer
+ * occur for this intent.
+ *
+ * @param intent {#link PendingIntent} object that no longer needs location updates
+ * @throws IllegalArgumentException if intent is null
+ */
+ public void removeUpdates(PendingIntent intent) {
+ if (intent == null) {
+ throw new IllegalArgumentException("intent==null");
+ }
+ if (Config.LOGD) {
+ Log.d(TAG, "removeUpdates: intent = " + intent);
+ }
+ try {
+ mService.removeUpdatesPI(intent);
+ } catch (RemoteException ex) {
+ Log.e(TAG, "removeUpdates: RemoteException", ex);
+ }
+ }
+
+ /**
* Sets a proximity alert for the location given by the position
* (latitude, longitude) and the given radius. When the device
* detects that it has entered or exited the area surrounding the
@@ -865,9 +976,9 @@ public class LocationManager {
* @param accuracy
*
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
+ * Settings.Secure.ALLOW_MOCK_LOCATION} system setting is not enabled
* @throws IllegalArgumentException if a provider with the given name already exists
- *
- * {@hide}
*/
public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
@@ -887,9 +998,9 @@ public class LocationManager {
* @param provider the provider name
*
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
+ * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- *
- * {@hide}
*/
public void removeTestProvider(String provider) {
try {
@@ -907,9 +1018,9 @@ public class LocationManager {
* @param loc the mock location
*
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
+ * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- *
- * {@hide}
*/
public void setTestProviderLocation(String provider, Location loc) {
try {
@@ -925,9 +1036,9 @@ public class LocationManager {
* @param provider the provider name
*
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
+ * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- *
- * {@hide}
*/
public void clearTestProviderLocation(String provider) {
try {
@@ -945,9 +1056,9 @@ public class LocationManager {
* @param enabled the mock enabled value
*
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
+ * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- *
- * {@hide}
*/
public void setTestProviderEnabled(String provider, boolean enabled) {
try {
@@ -963,9 +1074,9 @@ public class LocationManager {
* @param provider the provider name
*
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
+ * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- *
- * {@hide}
*/
public void clearTestProviderEnabled(String provider) {
try {
@@ -986,9 +1097,9 @@ public class LocationManager {
* @param updateTime the mock update time
*
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
+ * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- *
- * {@hide}
*/
public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
try {
@@ -1004,9 +1115,9 @@ public class LocationManager {
* @param provider the provider name
*
* @throws SecurityException if the ACCESS_MOCK_LOCATION permission is not present
+ * or the {@link android.provider.Settings.Secure#ALLOW_MOCK_LOCATION
+ * Settings.Secure.ALLOW_MOCK_LOCATION}} system setting is not enabled
* @throws IllegalArgumentException if no provider with the given name exists
- *
- * {@hide}
*/
public void clearTestProviderStatus(String provider) {
try {
@@ -1021,102 +1132,65 @@ public class LocationManager {
// This class is used to send GPS status events to the client's main thread.
private class GpsStatusListenerTransport extends IGpsStatusListener.Stub {
- private GpsStatusListener mListener;
- private int mTTFF;
- private int mSvCount;
- private int[] mPrns;
- private float[] mSnrs;
- private float[] mElevations;
- private float[] mAzimuths;
- private int mEphemerisMask;
- private int mAlmanacMask;
- private int mUsedInFixMask;
-
- private static final int GPS_STARTED = 0;
- private static final int GPS_STOPPED = 1;
- private static final int GPS_FIRST_FIX = 2;
- private static final int GPS_SV_STATUS = 3;
-
- GpsStatusListenerTransport(GpsStatusListener listener) {
+ private final GpsStatus.Listener mListener;
+
+ GpsStatusListenerTransport(GpsStatus.Listener listener) {
mListener = listener;
}
public void onGpsStarted() {
Message msg = Message.obtain();
- msg.what = GPS_STARTED;
+ msg.what = GpsStatus.GPS_EVENT_STARTED;
mGpsHandler.sendMessage(msg);
}
public void onGpsStopped() {
Message msg = Message.obtain();
- msg.what = GPS_STOPPED;
+ msg.what = GpsStatus.GPS_EVENT_STOPPED;
mGpsHandler.sendMessage(msg);
}
public void onFirstFix(int ttff) {
- mTTFF = ttff;
+ mGpsStatus.setTimeToFirstFix(ttff);
Message msg = Message.obtain();
- msg.what = GPS_FIRST_FIX;
+ msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
mGpsHandler.sendMessage(msg);
}
public void onSvStatusChanged(int svCount, int[] prns, float[] snrs,
float[] elevations, float[] azimuths, int ephemerisMask,
int almanacMask, int usedInFixMask) {
- // synchronize here to ensure SV count matches data in the arrays
- synchronized(this) {
- mSvCount = svCount;
- mPrns = prns;
- mSnrs = snrs;
- mElevations = elevations;
- mAzimuths = azimuths;
- mEphemerisMask = ephemerisMask;
- mAlmanacMask = almanacMask;
- mUsedInFixMask = usedInFixMask;
- }
+ mGpsStatus.setStatus(svCount, prns, snrs, elevations, azimuths,
+ ephemerisMask, almanacMask, usedInFixMask);
Message msg = Message.obtain();
- msg.what = GPS_SV_STATUS;
+ msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
// remove any SV status messages already in the queue
- mGpsHandler.removeMessages(GPS_SV_STATUS);
+ mGpsHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
mGpsHandler.sendMessage(msg);
}
private final Handler mGpsHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
- switch (msg.what) {
- case GPS_STARTED:
- mListener.onGpsStarted();
- break;
- case GPS_STOPPED:
- mListener.onGpsStopped();
- break;
- case GPS_FIRST_FIX:
- mListener.onFirstFix(mTTFF);
- break;
- case GPS_SV_STATUS:
- // synchronize here to ensure SV count matches data in the arrays
- synchronized(this) {
- mListener.onSvStatusChanged(mSvCount, mPrns, mSnrs, mElevations,
- mAzimuths, mEphemerisMask, mAlmanacMask, mUsedInFixMask);
- break;
- }
+ // synchronize on mGpsStatus to ensure the data is copied atomically.
+ synchronized(mGpsStatus) {
+ mListener.onGpsStatusChanged(msg.what);
}
}
};
}
/**
- * Registers a GPS status listener.
+ * Adds a GPS status listener.
*
- * @param listener GPS status listener object to register.
+ * @param listener GPS status listener object to register
*
- * @return true if the listener was successfully registered.
- *
- * {@hide}
+ * @return true if the listener was successfully added
+ *
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
*/
- public boolean registerGpsStatusListener(GpsStatusListener listener) {
+ public boolean addGpsStatusListener(GpsStatus.Listener listener) {
boolean result;
if (mGpsStatusListeners.get(listener) != null) {
@@ -1138,13 +1212,11 @@ public class LocationManager {
}
/**
- * Unegisters a GPS status listener.
- *
- * @param listener GPS status listener object to unregister.
+ * Removes a GPS status listener.
*
- * {@hide}
+ * @param listener GPS status listener object to remove
*/
- public void unregisterGpsStatusListener(GpsStatusListener listener) {
+ public void removeGpsStatusListener(GpsStatus.Listener listener) {
try {
GpsStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
if (transport != null) {
@@ -1154,7 +1226,26 @@ public class LocationManager {
Log.e(TAG, "RemoteException in unregisterGpsStatusListener: ", e);
}
}
-
+
+ /**
+ * Retrieves information about the current status of the GPS engine.
+ * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged}
+ * callback to ensure that the data is copied atomically.
+ *
+ * The caller may either pass in a {@link GpsStatus} object to set with the latest
+ * status information, or pass null to create a new {@link GpsStatus} object.
+ *
+ * @param status object containing GPS status details, or null.
+ * @return status object containing updated GPS status.
+ */
+ public GpsStatus getGpsStatus(GpsStatus status) {
+ if (status == null) {
+ status = new GpsStatus();
+ }
+ status.setStatus(mGpsStatus);
+ return status;
+ }
+
/**
* Sends additional commands to a location provider.
* Can be used to support provider specific extensions to the Location Manager API
@@ -1164,9 +1255,7 @@ public class LocationManager {
* @param extras optional arguments for the command (or null).
* The provider may optionally fill the extras Bundle with results from the command.
*
- * @return true if the command succeeds.
- *
- * {@hide}
+ * @return true if the command succeeds.
*/
public boolean sendExtraCommand(String provider, String command, Bundle extras) {
try {
diff --git a/location/java/com/android/internal/location/CellState.java b/location/java/com/android/internal/location/CellState.java
index 697fa92..8f6ca1f 100644
--- a/location/java/com/android/internal/location/CellState.java
+++ b/location/java/com/android/internal/location/CellState.java
@@ -17,10 +17,16 @@
package com.android.internal.location;
import android.telephony.CellLocation;
-import android.telephony.ServiceState;
+import android.telephony.TelephonyManager;
+import android.telephony.NeighboringCellInfo;
import android.telephony.gsm.GsmCellLocation;
+import android.util.Log;
+
import com.android.internal.telephony.TelephonyProperties;
+import java.util.List;
+import java.util.ArrayList;
+
import android.os.SystemProperties;
/**
@@ -45,53 +51,148 @@ public class CellState {
private String mCarrier = null;
private int mRadioType = -1;
private long mTime = 0;
+ private int mSignalStrength = -1;
+
+ private List<NeighborCell> mNeighbors;
public CellState() {
// constructor for invalid cell location
}
- public CellState(ServiceState service, CellLocation location) {
+ public CellState(TelephonyManager telephonyManager, CellLocation location, int signalStrength) {
GsmCellLocation loc = (GsmCellLocation)location;
mLac = loc.getLac(); // example: 6032
mCid = loc.getCid(); // example: 31792
mTime = System.currentTimeMillis();
// Get radio type
- String radioType = SystemProperties.get(TelephonyProperties.PROPERTY_DATA_NETWORK_TYPE);
- if (radioType != null && (radioType.equals("GPRS") || radioType.equals("EDGE"))) {
+ int radioType = telephonyManager.getNetworkType();
+ if (radioType == TelephonyManager.NETWORK_TYPE_GPRS ||
+ radioType == TelephonyManager.NETWORK_TYPE_EDGE) {
mRadioType = RADIO_TYPE_GPRS;
- } else if (radioType != null && radioType.equals("UMTS")) {
+ } else if (radioType == TelephonyManager.NETWORK_TYPE_UMTS) {
mRadioType = RADIO_TYPE_WCDMA;
}
+ // Get neighboring cells
+ mNeighbors = new ArrayList<NeighborCell>();
+ List<NeighboringCellInfo> neighboringCells = telephonyManager.getNeighboringCellInfo();
+ if (neighboringCells != null) {
+ for (NeighboringCellInfo n : neighboringCells) {
+ if (n.getCid() == NeighboringCellInfo.UNKNOWN_CID) {
+ continue;
+ }
+
+ if (mRadioType == RADIO_TYPE_WCDMA) {
+ mNeighbors.add(new NeighborCell(-1, -1, n.getCid(), n.getRssi()));
+ } else if (mRadioType == RADIO_TYPE_GPRS) {
+ try {
+ String hexCidLac = Integer.toHexString(n.getCid());
+ int l = hexCidLac.length();
+ if (l > 8) {
+ Log.w(TAG, "Unable to parse 2G Cell \"" + hexCidLac + "\"");
+ continue;
+ }
+ if (l < 8) {
+ for (int i = 0; i < (8-l); i++) {
+ hexCidLac = "0" + hexCidLac;
+ }
+ }
+ int lac = Integer.valueOf(hexCidLac.substring(0, 4), 16);
+ int cid = Integer.valueOf(hexCidLac.substring(4), 16);
+ mNeighbors.add(new NeighborCell(cid, lac, -1, n.getRssi()));
+
+ } catch (Exception e) {
+ Log.e(TAG, "Error parsing 2G Cell \"" + n.getCid() + "\"", e);
+ }
+ }
+ }
+ }
+
// Get MCC/MNC
- String operator = service.getOperatorNumeric();
+ String operator = telephonyManager.getNetworkOperator();
if (operator != null && !operator.equals("")) {
- String mcc = operator.substring(0, 3);
- String mnc = operator.substring(3);
-
- mMcc = Integer.parseInt(mcc);
- mMnc = Integer.parseInt(mnc);
+ // Use a try/catch block to detect index out of bounds or number format exceptions.
+ // If an error occurs, both mMcc and mMnc will be equal to -1.
+ try {
+ String mcc = operator.substring(0, 3);
+ String mnc = operator.substring(3);
+ int mccTmp = Integer.parseInt(mcc);
+ int mncTmp = Integer.parseInt(mnc);
+
+ // Parsing succeeded, update the instance variables together
+ mMcc = mccTmp;
+ mMnc = mncTmp;
+ } catch (Exception e) {
+ Log.e(TAG, "Error parsing MCC/MNC from operator \"" + operator + "\"", e);
+ }
}
// Get Home MCC/MNC
- String homeOperator = SystemProperties.get(TelephonyProperties.PROPERTY_SIM_OPERATOR_NUMERIC);
+ String homeOperator = telephonyManager.getSimOperator();
if (homeOperator != null && !homeOperator.equals("")) {
- String mcc = homeOperator.substring(0, 3);
- String mnc = homeOperator.substring(3);
-
- mHomeMcc = Integer.parseInt(mcc);
- mHomeMnc = Integer.parseInt(mnc);
+ // Use a try/catch block to detect index out of bounds or number format exceptions.
+ // If an error occurs, both mHomeMcc and mHomeMnc will be equal to -1.
+ try {
+ String mcc = homeOperator.substring(0, 3);
+ String mnc = homeOperator.substring(3);
+ int mccTmp = Integer.parseInt(mcc);
+ int mncTmp = Integer.parseInt(mnc);
+
+ // Parsing succeeded, update the instance variables together
+ mHomeMcc = mccTmp;
+ mHomeMnc = mncTmp;
+ } catch (Exception e) {
+ Log.e(TAG, "Error parsing MCC/MNC from home operator \"" + homeOperator + "\"", e);
+ }
}
// Get Carrier
- String carrier = service.getOperatorAlphaLong();
+ String carrier = telephonyManager.getNetworkOperatorName();
if (carrier != null && !carrier.equals("")) {
mCarrier = carrier;
}
- //Log.d(TAG, mLac +"," + mCid + "," + mMnc +"," + mMcc + "," + mHomeMcc + "," +
- // mHomeMnc + "," + mCarrier + "," + mRadioType);
+ // Initial signal strength
+ mSignalStrength = signalStrength;
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ String neighbors = "[";
+ for (NeighborCell n : mNeighbors) {
+ neighbors += n.toString() + ",";
+ }
+ neighbors += "]";
+ Log.d(TAG, "CellState(): " + mLac +"," + mCid + "," + mMnc +"," + mMcc + "," +
+ mHomeMcc + "," + mHomeMnc + "," + mCarrier + "," + mRadioType + "," +
+ mSignalStrength + "," + neighbors);
+ }
+ }
+
+ public void updateRadioType(TelephonyManager telephonyManager) {
+ // Get radio type
+ int radioType = telephonyManager.getNetworkType();
+ if (radioType == TelephonyManager.NETWORK_TYPE_GPRS ||
+ radioType == TelephonyManager.NETWORK_TYPE_EDGE) {
+ mRadioType = RADIO_TYPE_GPRS;
+ } else if (radioType == TelephonyManager.NETWORK_TYPE_UMTS) {
+ mRadioType = RADIO_TYPE_WCDMA;
+ }
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "updateRadioType(): " + mLac +"," + mCid + "," + mMnc +"," + mMcc + "," +
+ mHomeMcc + "," + mHomeMnc + "," + mCarrier + "," + mRadioType + "," +
+ mSignalStrength);
+ }
+ }
+
+ public void updateSignalStrength(int signalStrength) {
+ mSignalStrength = signalStrength;
+
+ if (Log.isLoggable(TAG, Log.VERBOSE)) {
+ Log.d(TAG, "updateSignal(): " + mLac +"," + mCid + "," + mMnc +"," + mMcc + "," +
+ mHomeMcc + "," + mHomeMnc + "," + mCarrier + "," + mRadioType + "," +
+ mSignalStrength);
+ }
}
public int getCid() {
@@ -142,6 +243,14 @@ public class CellState {
return mTime;
}
+ public int getSignalStrength() {
+ return mSignalStrength;
+ }
+
+ public List<NeighborCell> getNeighbors() {
+ return mNeighbors;
+ }
+
public boolean equals(CellState other) {
return (mCid == other.mCid && mLac == other.mLac);
}
@@ -149,4 +258,42 @@ public class CellState {
public boolean isValid() {
return (mCid != -1 && mLac != -1);
}
+
+ public static class NeighborCell {
+ private int mCid = -1;
+ private int mLac = -1;
+ private int mPsc = -1;
+ private int mRssi = -1;
+
+ NeighborCell(int cid, int lac, int psc, int rssi) {
+ mCid = cid;
+ mLac = lac;
+ mPsc = psc;
+ mRssi = rssi;
+ }
+
+ public int getCid() {
+ return mCid;
+ }
+
+ public int getLac() {
+ return mLac;
+ }
+
+ public int getPsc() {
+ return mPsc;
+ }
+
+ public int getRssi() {
+ return mRssi;
+ }
+
+ public String toString() {
+ if (mPsc != -1) {
+ return String.valueOf(mPsc) + "@" + mRssi;
+ } else {
+ return mCid + ":" + mLac + "@" + mRssi;
+ }
+ }
+ }
}
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index d0e4f49..7c47081 100644
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -16,12 +16,6 @@
package com.android.internal.location;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.IOException;
-import java.util.ArrayList;
-import java.util.Properties;
-
import android.content.Context;
import android.content.Intent;
import android.location.Criteria;
@@ -38,6 +32,12 @@ import android.os.SystemClock;
import android.util.Config;
import android.util.Log;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Properties;
+
/**
* A GPS implementation of LocationProvider used by LocationManager.
*
@@ -470,8 +470,7 @@ public class GpsLocationProvider extends LocationProviderImpl {
for (int i = 0; i < size && l == null; i++) {
Listener test = mListeners.get(i);
if (binder.equals(test.mListener.asBinder())) {
- // listener already added
- return;
+ l = test;
}
}
@@ -872,7 +871,8 @@ public class GpsLocationProvider extends LocationProviderImpl {
// mask[0] is ephemeris mask and mask[1] is almanac mask
private native int native_read_sv_status(int[] svs, float[] snrs,
float[] elevations, float[] azimuths, int[] masks);
-
+
+ // XTRA Support
private native void native_inject_time(long time, long timeReference, int uncertainty);
private native boolean native_supports_xtra();
private native void native_inject_xtra_data(byte[] data, int length);
diff --git a/location/java/com/android/internal/location/LocationCache.java b/location/java/com/android/internal/location/LocationCache.java
index f0928f9..545d8c9 100644
--- a/location/java/com/android/internal/location/LocationCache.java
+++ b/location/java/com/android/internal/location/LocationCache.java
@@ -98,7 +98,7 @@ public class LocationCache {
mCellCentroid.reset();
mWifiCentroid.reset();
- if (cellState != null) {
+ if (cellState != null && cellState.isValid()) {
String primaryCellKey = getCellCacheKey(cellState.getMcc(), cellState.getMnc(),
cellState.getLac(), cellState.getCid());
Record record = mCellCache.lookup(primaryCellKey);
@@ -173,20 +173,44 @@ public class LocationCache {
Log.d(TAG, "cellAccuracy:" + cellAccuracy+ ", wifiAccuracy:" + wifiAccuracy);
}
- if (mCellCentroid.getNumber() != 0 && cellAccuracy <= MAX_ACCURACY_ALLOWED &&
- (mWifiCentroid.getNumber() == 0 || cellConfidence >= wifiConfidence ||
- cellAccuracy < wifiAccuracy)) {
+ if ((mCellCentroid.getNumber() == 0 || cellAccuracy > MAX_ACCURACY_ALLOWED) &&
+ (mWifiCentroid.getNumber() == 0 || wifiAccuracy > MAX_ACCURACY_ALLOWED)) {
+ // Return invalid location if neither location is valid
+ result.setAccuracy(-1);
+
+ // don't make server request
+ return true;
+ }
+
+ float[] distance = new float[1];
+ Location.distanceBetween(mCellCentroid.getCentroidLat(), mCellCentroid.getCentroidLng(),
+ mWifiCentroid.getCentroidLat(), mWifiCentroid.getCentroidLng(),
+ distance);
+
+ boolean consistent = distance[0] <= (cellAccuracy + wifiAccuracy);
+
+ boolean useCell = true;
+
+ if (consistent) {
+ // If consistent locations, use one with greater accuracy
+ useCell = (cellAccuracy <= wifiAccuracy);
+ } else {
+ // If inconsistent locations, use one with greater confidence
+ useCell = (cellConfidence >= wifiConfidence);
+ }
+
+ if (useCell) {
// Use cell results
result.setAccuracy(cellAccuracy);
result.setLatitude(mCellCentroid.getCentroidLat());
result.setLongitude(mCellCentroid.getCentroidLng());
result.setTime(now);
-
+
Bundle extras = result.getExtras() == null ? new Bundle() : result.getExtras();
extras.putString(EXTRA_KEY_LOCATION_TYPE, EXTRA_VALUE_LOCATION_TYPE_CELL);
result.setExtras(extras);
- } else if (mWifiCentroid.getNumber() != 0 && wifiAccuracy <= MAX_ACCURACY_ALLOWED) {
+ } else {
// Use wifi results
result.setAccuracy(wifiAccuracy);
result.setLatitude(mWifiCentroid.getCentroidLat());
@@ -197,9 +221,6 @@ public class LocationCache {
extras.putString(EXTRA_KEY_LOCATION_TYPE, EXTRA_VALUE_LOCATION_TYPE_WIFI);
result.setExtras(extras);
- } else {
- // Return invalid location
- result.setAccuracy(-1);
}
// don't make a server request
@@ -459,7 +480,7 @@ public class LocationCache {
double mLatSum = 0;
double mLngSum = 0;
int mNumber = 0;
- int mConfidenceSum = 0;
+ int mConfidence = 0;
double mCentroidLat = 0;
double mCentroidLng = 0;
@@ -478,7 +499,7 @@ public class LocationCache {
mLatSum = 0;
mLngSum = 0;
mNumber = 0;
- mConfidenceSum = 0;
+ mConfidence = 0;
mCentroidLat = 0;
mCentroidLng = 0;
@@ -494,7 +515,9 @@ public class LocationCache {
if (mNumber < MAX_SIZE && accuracy <= MAX_ACCURACY_ALLOWED) {
mLatSum += lat;
mLngSum += lng;
- mConfidenceSum += confidence;
+ if (confidence > mConfidence) {
+ mConfidence = confidence;
+ }
mLats[mNumber] = lat;
mLngs[mNumber] = lng;
@@ -522,11 +545,7 @@ public class LocationCache {
}
public int getConfidence() {
- if (mNumber != 0) {
- return mConfidenceSum/mNumber;
- } else {
- return 0;
- }
+ return mConfidence;
}
public int getAccuracy() {
diff --git a/location/java/com/android/internal/location/LocationCollector.java b/location/java/com/android/internal/location/LocationCollector.java
index 3d2f6bf..39f432f 100644
--- a/location/java/com/android/internal/location/LocationCollector.java
+++ b/location/java/com/android/internal/location/LocationCollector.java
@@ -350,14 +350,14 @@ public class LocationCollector {
* is healthy.
*
* Additionally, data collection will *never* happen if the system
- * property ro.com.google.enable_google_location_features is not set.
+ * property ro.com.google.locationfeatures is not set.
*
* @return true if anonymous location collection is enabled
*/
private boolean isCollectionEnabled() {
// This class provides a Google-specific location feature, so it's enabled only
- // when the system property ro.com.google.enable_google_location_features is set.
- if (!SystemProperties.get("ro.com.google.enable_google_location_features").equals("1")) {
+ // when the system property ro.com.google.locationfeatures is set.
+ if (!SystemProperties.get("ro.com.google.locationfeatures").equals("1")) {
return false;
}
return mBatteryLevelIsHealthy && mNetworkProviderIsEnabled;
diff --git a/location/java/com/android/internal/location/LocationMasfClient.java b/location/java/com/android/internal/location/LocationMasfClient.java
index ed9f3d6..4efcdd5 100644
--- a/location/java/com/android/internal/location/LocationMasfClient.java
+++ b/location/java/com/android/internal/location/LocationMasfClient.java
@@ -64,6 +64,7 @@ import android.os.SystemClock;
import android.text.TextUtils;
import android.util.EventLog;
import android.util.Log;
+import android.telephony.NeighboringCellInfo;
/**
* Service to communicate to the Google Location Server (GLS) via MASF server
@@ -128,6 +129,12 @@ public class LocationMasfClient {
private static final String EXTRA_VALUE_LOCATION_SOURCE_CACHED = "cached";
private static final String EXTRA_VALUE_LOCATION_SOURCE_SERVER = "server";
+ // Maximum accuracy tolerated for a valid location
+ private static final int MAX_ACCURACY_ALLOWED = 5000; // 5km
+
+ // Indicates whether this is the first message after a device restart
+ private boolean mDeviceRestart = true;
+
/**
* Initializes the MobileServiceMux. Must be called before using any other function in the
* class.
@@ -204,10 +211,16 @@ public class LocationMasfClient {
ProtoBuf requestElement = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST_ELEMENT);
// Debug profile
+ ProtoBuf debugProfile = new ProtoBuf(GdebugprofileMessageTypes.GDEBUG_PROFILE);
+ requestElement.setProtoBuf(GLocRequestElement.DEBUG_PROFILE, debugProfile);
+
+ if (mDeviceRestart) {
+ debugProfile.setBool(GDebugProfile.DEVICE_RESTART, true);
+ mDeviceRestart = false;
+ }
+
if (trigger != -1) {
- ProtoBuf debugProfile = new ProtoBuf(GdebugprofileMessageTypes.GDEBUG_PROFILE);
debugProfile.setInt(GDebugProfile.TRIGGER, trigger);
- requestElement.setProtoBuf(GLocRequestElement.DEBUG_PROFILE, debugProfile);
}
// Cellular profile
@@ -226,6 +239,11 @@ public class LocationMasfClient {
primaryCell.setInt(GCell.MCC, mCellState.getMcc());
primaryCell.setInt(GCell.MNC, mCellState.getMnc());
}
+
+ if (mCellState.getSignalStrength() != -1) {
+ primaryCell.setInt(GCell.RSSI, mCellState.getSignalStrength());
+ }
+
cellularProfile.setProtoBuf(GCellularProfile.PRIMARY_CELL, primaryCell);
// History of cells
@@ -237,10 +255,18 @@ public class LocationMasfClient {
pastCell.setInt(GCell.MCC, c.getMcc());
pastCell.setInt(GCell.MNC, c.getMnc());
}
+
+ if (c.getSignalStrength() != -1) {
+ pastCell.setInt(GCell.RSSI, c.getSignalStrength());
+ }
+
pastCell.setInt(GCell.AGE, (int)(mCellState.getTime() - c.getTime()));
cellularProfile.addProtoBuf(GCellularProfile.HISTORICAL_CELLS, pastCell);
}
+ // Neighboring Cells
+ addNeighborsToCellProfile(mCellState, cellularProfile);
+
requestElement.setProtoBuf(GLocRequestElement.CELLULAR_PROFILE, cellularProfile);
}
@@ -344,13 +370,6 @@ public class LocationMasfClient {
}
ProtoBuf replyElement = response.getProtoBuf(GLocReply.REPLY_ELEMENTS);
- int status2 = replyElement.getInt(GLocReplyElement.STATUS);
- if (status2 != ResponseCodes.STATUS_STATUS_SUCCESS &&
- status2 != ResponseCodes.STATUS_STATUS_FAILED) {
- Log.e(TAG, "getNetworkLocation(): GLS failed with status " + status2);
- return false;
- }
-
// Get prefetched data to add to the device cache
Log.d(TAG, "getNetworkLocation(): Number of prefetched entries " +
replyElement.getCount(GLocReplyElement.DEVICE_LOCATION));
@@ -404,6 +423,13 @@ public class LocationMasfClient {
mLocationCache.save();
+ int status2 = replyElement.getInt(GLocReplyElement.STATUS);
+ if (status2 != ResponseCodes.STATUS_STATUS_SUCCESS &&
+ status2 != ResponseCodes.STATUS_STATUS_FAILED) {
+ Log.e(TAG, "getNetworkLocation(): GLS failed with status " + status2);
+ return false;
+ }
+
// For consistent results for user, always return cache computed location
boolean foundInCache =
mLocationCache.lookup(mCellState, mCellHistory, mWifiScanResults, mLocation);
@@ -447,6 +473,11 @@ public class LocationMasfClient {
accuracy = location.getInt(GLocation.ACCURACY);
}
+ if (accuracy > MAX_ACCURACY_ALLOWED) {
+ Log.e(TAG, "getNetworkLocation(): accuracy is too high " + accuracy);
+ return false;
+ }
+
mLocation.setLatitude(lat);
mLocation.setLongitude(lng);
mLocation.setTime(System.currentTimeMillis());
@@ -738,11 +769,16 @@ public class LocationMasfClient {
ProtoBuf requestElement = new ProtoBuf(LocserverMessageTypes.GLOC_REQUEST_ELEMENT);
// Include debug profile
+ ProtoBuf debugProfile = new ProtoBuf(GdebugprofileMessageTypes.GDEBUG_PROFILE);
+ requestElement.setProtoBuf(GLocRequestElement.DEBUG_PROFILE, debugProfile);
+
+ if (mDeviceRestart) {
+ debugProfile.setBool(GDebugProfile.DEVICE_RESTART, true);
+ mDeviceRestart = false;
+ }
+
if (trigger != -1) {
- ProtoBuf debugProfile = new ProtoBuf(GdebugprofileMessageTypes.GDEBUG_PROFILE);
debugProfile.setInt(GDebugProfile.TRIGGER, trigger);
- requestElement.setProtoBuf(GLocRequestElement.DEBUG_PROFILE, debugProfile);
-
EventLog.writeEvent(COLLECTION_EVENT_LOG_TAG, trigger);
}
@@ -760,7 +796,15 @@ public class LocationMasfClient {
primaryCell.setInt(GCell.MNC, cellState.getMnc());
}
+ if (cellState.getSignalStrength() != -1) {
+ primaryCell.setInt(GCell.RSSI, cellState.getSignalStrength());
+ }
+
cellularProfile.setProtoBuf(GCellularProfile.PRIMARY_CELL, primaryCell);
+
+ // Cell neighbors
+ addNeighborsToCellProfile(cellState, cellularProfile);
+
requestElement.setProtoBuf(GLocRequestElement.CELLULAR_PROFILE, cellularProfile);
}
@@ -818,8 +862,28 @@ public class LocationMasfClient {
platformProfile.setProtoBuf(GPlatformProfile.CELLULAR_PLATFORM_PROFILE,
cellularPlatform);
}
+
mCurrentCollectionRequest.setProtoBuf(GLocRequest.PLATFORM_PROFILE, platformProfile);
+
+ } else {
+
+ ProtoBuf platformProfile =
+ mCurrentCollectionRequest.getProtoBuf(GLocRequest.PLATFORM_PROFILE);
+ if (platformProfile == null) {
+ platformProfile = createPlatformProfile();
+ mCurrentCollectionRequest.setProtoBuf(
+ GLocRequest.PLATFORM_PROFILE, platformProfile);
+ }
+
+ // Add cellular platform profile is not already included
+ if (platformProfile.getProtoBuf(GPlatformProfile.CELLULAR_PLATFORM_PROFILE) == null &&
+ cellState != null && cellState.isValid()) {
+ ProtoBuf cellularPlatform = createCellularPlatformProfile(cellState);
+ platformProfile.setProtoBuf(GPlatformProfile.CELLULAR_PLATFORM_PROFILE,
+ cellularPlatform);
+ }
}
+
mCurrentCollectionRequest.addProtoBuf(GLocRequest.REQUEST_ELEMENTS, requestElement);
// Immediately upload collection events if buffer exceeds certain size
@@ -990,32 +1054,33 @@ public class LocationMasfClient {
}
private ProtoBuf createCellularPlatformProfile(CellState cellState) {
- if (mCellularPlatformProfile == null) {
- // Radio type
- int radioType = -1;
- if (cellState.getRadioType() == CellState.RADIO_TYPE_GPRS) {
- radioType = GCellularPlatformProfile.RADIO_TYPE_GPRS;
- } else if (cellState.getRadioType() == CellState.RADIO_TYPE_CDMA) {
- radioType = GCellularPlatformProfile.RADIO_TYPE_CDMA;
- } else if (cellState.getRadioType() == CellState.RADIO_TYPE_WCDMA) {
- radioType = GCellularPlatformProfile.RADIO_TYPE_WCDMA;
- }
+ // Radio type
+ int radioType = -1;
+ if (cellState.getRadioType() == CellState.RADIO_TYPE_GPRS) {
+ radioType = GCellularPlatformProfile.RADIO_TYPE_GPRS;
+ } else if (cellState.getRadioType() == CellState.RADIO_TYPE_CDMA) {
+ radioType = GCellularPlatformProfile.RADIO_TYPE_CDMA;
+ } else if (cellState.getRadioType() == CellState.RADIO_TYPE_WCDMA) {
+ radioType = GCellularPlatformProfile.RADIO_TYPE_WCDMA;
+ }
- // Cellular platform profile
- ProtoBuf cellularPlatform =
+ if (mCellularPlatformProfile == null) {
+ mCellularPlatformProfile =
new ProtoBuf(GlocationMessageTypes.GCELLULAR_PLATFORM_PROFILE);
- cellularPlatform.setInt(GCellularPlatformProfile.RADIO_TYPE, radioType);
- if ((cellState.getHomeMcc() != -1) && (cellState.getHomeMnc() != -1)) {
- cellularPlatform.setInt(GCellularPlatformProfile.HOME_MCC, cellState.getHomeMcc());
- cellularPlatform.setInt(GCellularPlatformProfile.HOME_MNC, cellState.getHomeMnc());
- }
- if (cellState.getCarrier() != null) {
- cellularPlatform.setString(GCellularPlatformProfile.CARRIER,
- cellState.getCarrier());
- }
- mCellularPlatformProfile = cellularPlatform;
}
+ mCellularPlatformProfile.setInt(GCellularPlatformProfile.RADIO_TYPE, radioType);
+ if ((cellState.getHomeMcc() != -1) && (cellState.getHomeMnc() != -1)) {
+ mCellularPlatformProfile.setInt(GCellularPlatformProfile.HOME_MCC,
+ cellState.getHomeMcc());
+ mCellularPlatformProfile.setInt(GCellularPlatformProfile.HOME_MNC,
+ cellState.getHomeMnc());
+ }
+ if (cellState.getCarrier() != null) {
+ mCellularPlatformProfile.setString(GCellularPlatformProfile.CARRIER,
+ cellState.getCarrier());
+ }
+
return mCellularPlatformProfile;
}
@@ -1100,4 +1165,20 @@ public class LocationMasfClient {
}
}
+ private void addNeighborsToCellProfile(CellState cellState, ProtoBuf cellularProfile) {
+ List<CellState.NeighborCell> neighbors = cellState.getNeighbors();
+ if (neighbors != null) {
+ for (CellState.NeighborCell neighbor : neighbors) {
+ ProtoBuf nCell = new ProtoBuf(GcellularMessageTypes.GCELL);
+ nCell.setInt(GCell.CELLID, neighbor.getCid());
+ nCell.setInt(GCell.LAC, neighbor.getLac());
+ nCell.setInt(GCell.RSSI, neighbor.getRssi());
+ if (neighbor.getPsc() != -1) {
+ nCell.setInt(GCell.PRIMARY_SCRAMBLING_CODE, neighbor.getPsc());
+ }
+ cellularProfile.addProtoBuf(GCellularProfile.NEIGHBORS, nCell);
+ }
+ }
+ }
+
}
diff --git a/location/java/com/android/internal/location/NetworkLocationProvider.java b/location/java/com/android/internal/location/NetworkLocationProvider.java
index 7d3fda1..d0a59b9 100644
--- a/location/java/com/android/internal/location/NetworkLocationProvider.java
+++ b/location/java/com/android/internal/location/NetworkLocationProvider.java
@@ -107,8 +107,8 @@ public class NetworkLocationProvider extends LocationProviderImpl {
public static boolean isSupported() {
// This class provides a Google-specific location feature, so it's enabled only
- // when the system property ro.com.google.enable_google_location_features is set.
- if (!SystemProperties.get("ro.com.google.enable_google_location_features").equals("1")) {
+ // when the system property ro.com.google.locationfeatures is set.
+ if (!SystemProperties.get("ro.com.google.locationfeatures").equals("1")) {
return false;
}
diff --git a/location/java/com/android/internal/location/protocol/GDebugProfile.java b/location/java/com/android/internal/location/protocol/GDebugProfile.java
index e5c9fe6..b964387 100644
--- a/location/java/com/android/internal/location/protocol/GDebugProfile.java
+++ b/location/java/com/android/internal/location/protocol/GDebugProfile.java
@@ -32,5 +32,6 @@ public interface GDebugProfile {
static final int TRIGGER = 1;
static final int ACTUAL_REQUEST = 2;
static final int CACHE_LOCATION = 3;
+ static final int DEVICE_RESTART = 4;
}
diff --git a/location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java b/location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java
index faf5b89..9a51efe 100644
--- a/location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java
+++ b/location/java/com/android/internal/location/protocol/GdebugprofileMessageTypes.java
@@ -29,7 +29,9 @@ public class GdebugprofileMessageTypes {
.addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_BOOL,
GDebugProfile.ACTUAL_REQUEST, ProtoBuf.TRUE)
.addElement(ProtoBufType.REPEATED | ProtoBufType.TYPE_MESSAGE,
- GDebugProfile.CACHE_LOCATION, GlocationMessageTypes.GDEVICE_LOCATION);
+ GDebugProfile.CACHE_LOCATION, GlocationMessageTypes.GDEVICE_LOCATION)
+ .addElement(ProtoBufType.OPTIONAL | ProtoBufType.TYPE_BOOL,
+ GDebugProfile.DEVICE_RESTART, ProtoBuf.FALSE);
}
}