summaryrefslogtreecommitdiff
path: root/android/location
diff options
context:
space:
mode:
authorJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
committerJustin Klaassen <justinklaassen@google.com>2017-09-15 17:58:39 -0400
commit10d07c88d69cc64f73a069163e7ea5ba2519a099 (patch)
tree8dbd149eb350320a29c3d10e7ad3201de1c5cbee /android/location
parent677516fb6b6f207d373984757d3d9450474b6b00 (diff)
downloadandroid-28-10d07c88d69cc64f73a069163e7ea5ba2519a099.tar.gz
Import Android SDK Platform PI [4335822]
/google/data/ro/projects/android/fetch_artifact \ --bid 4335822 \ --target sdk_phone_armv7-win_sdk \ sdk-repo-linux-sources-4335822.zip AndroidVersion.ApiLevel has been modified to appear as 28 Change-Id: Ic8f04be005a71c2b9abeaac754d8da8d6f9a2c32
Diffstat (limited to 'android/location')
-rw-r--r--android/location/Address.java569
-rw-r--r--android/location/BatchedLocationCallback.java44
-rw-r--r--android/location/BatchedLocationCallbackTransport.java66
-rw-r--r--android/location/Country.java202
-rw-r--r--android/location/CountryDetector.java149
-rw-r--r--android/location/CountryListener.java30
-rw-r--r--android/location/Criteria.java406
-rw-r--r--android/location/FusedBatchOptions.java148
-rw-r--r--android/location/Geocoder.java260
-rw-r--r--android/location/GeocoderParams.java94
-rw-r--r--android/location/Geofence.java182
-rw-r--r--android/location/GnssClock.java541
-rw-r--r--android/location/GnssMeasurement.java1107
-rw-r--r--android/location/GnssMeasurementCallbackTransport.java77
-rw-r--r--android/location/GnssMeasurementsEvent.java173
-rw-r--r--android/location/GnssNavigationMessage.java440
-rw-r--r--android/location/GnssNavigationMessageCallbackTransport.java79
-rw-r--r--android/location/GnssStatus.java250
-rw-r--r--android/location/GpsClock.java503
-rw-r--r--android/location/GpsMeasurement.java1413
-rw-r--r--android/location/GpsMeasurementsEvent.java159
-rw-r--r--android/location/GpsNavigationMessage.java324
-rw-r--r--android/location/GpsNavigationMessageEvent.java119
-rw-r--r--android/location/GpsSatellite.java125
-rw-r--r--android/location/GpsStatus.java250
-rw-r--r--android/location/LocalListenerHelper.java129
-rw-r--r--android/location/Location.java1192
-rw-r--r--android/location/LocationListener.java89
-rw-r--r--android/location/LocationManager.java2135
-rw-r--r--android/location/LocationProvider.java205
-rw-r--r--android/location/LocationRequest.java672
-rw-r--r--android/location/OnNmeaMessageListener.java34
-rw-r--r--android/location/SettingInjectorService.java220
33 files changed, 12386 insertions, 0 deletions
diff --git a/android/location/Address.java b/android/location/Address.java
new file mode 100644
index 00000000..335ad5eb
--- /dev/null
+++ b/android/location/Address.java
@@ -0,0 +1,569 @@
+/*
+ * Copyright (C) 2007 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.HashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class representing an Address, i.e, a set of Strings describing a location.
+ *
+ * The address format is a simplified version of xAL (eXtensible Address Language)
+ * http://www.oasis-open.org/committees/ciq/ciq.html#6
+ */
+public class Address implements Parcelable {
+
+ private Locale mLocale;
+
+ private String mFeatureName;
+ private HashMap<Integer, String> mAddressLines;
+ private int mMaxAddressLineIndex = -1;
+ private String mAdminArea;
+ private String mSubAdminArea;
+ private String mLocality;
+ private String mSubLocality;
+ private String mThoroughfare;
+ private String mSubThoroughfare;
+ private String mPremises;
+ private String mPostalCode;
+ private String mCountryCode;
+ private String mCountryName;
+ private double mLatitude;
+ private double mLongitude;
+ private boolean mHasLatitude = false;
+ private boolean mHasLongitude = false;
+ private String mPhone;
+ private String mUrl;
+ private Bundle mExtras = null;
+
+ /**
+ * Constructs a new Address object set to the given Locale and with all
+ * other fields initialized to null or false.
+ */
+ public Address(Locale locale) {
+ mLocale = locale;
+ }
+
+ /**
+ * Returns the Locale associated with this address.
+ */
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /**
+ * Returns the largest index currently in use to specify an address line.
+ * If no address lines are specified, -1 is returned.
+ */
+ public int getMaxAddressLineIndex() {
+ return mMaxAddressLineIndex;
+ }
+
+ /**
+ * Returns a line of the address numbered by the given index
+ * (starting at 0), or null if no such line is present.
+ *
+ * @throws IllegalArgumentException if index < 0
+ */
+ public String getAddressLine(int index) {
+ if (index < 0) {
+ throw new IllegalArgumentException("index = " + index + " < 0");
+ }
+ return mAddressLines == null? null : mAddressLines.get(index);
+ }
+
+ /**
+ * Sets the line of the address numbered by index (starting at 0) to the
+ * given String, which may be null.
+ *
+ * @throws IllegalArgumentException if index < 0
+ */
+ public void setAddressLine(int index, String line) {
+ if (index < 0) {
+ throw new IllegalArgumentException("index = " + index + " < 0");
+ }
+ if (mAddressLines == null) {
+ mAddressLines = new HashMap<Integer, String>();
+ }
+ mAddressLines.put(index, line);
+
+ if (line == null) {
+ // We've eliminated a line, recompute the max index
+ mMaxAddressLineIndex = -1;
+ for (Integer i : mAddressLines.keySet()) {
+ mMaxAddressLineIndex = Math.max(mMaxAddressLineIndex, i);
+ }
+ } else {
+ mMaxAddressLineIndex = Math.max(mMaxAddressLineIndex, index);
+ }
+ }
+
+ /**
+ * Returns the feature name of the address, for example, "Golden Gate Bridge", or null
+ * if it is unknown
+ */
+ public String getFeatureName() {
+ return mFeatureName;
+ }
+
+ /**
+ * Sets the feature name of the address to the given String, which may be null
+ */
+ public void setFeatureName(String featureName) {
+ mFeatureName = featureName;
+ }
+
+ /**
+ * Returns the administrative area name of the address, for example, "CA", or null if
+ * it is unknown
+ */
+ public String getAdminArea() {
+ return mAdminArea;
+ }
+
+ /**
+ * Sets the administrative area name of the address to the given String, which may be null
+ */
+ public void setAdminArea(String adminArea) {
+ this.mAdminArea = adminArea;
+ }
+
+ /**
+ * Returns the sub-administrative area name of the address, for example, "Santa Clara County",
+ * or null if it is unknown
+ */
+ public String getSubAdminArea() {
+ return mSubAdminArea;
+ }
+
+ /**
+ * Sets the sub-administrative area name of the address to the given String, which may be null
+ */
+ public void setSubAdminArea(String subAdminArea) {
+ this.mSubAdminArea = subAdminArea;
+ }
+
+ /**
+ * Returns the locality of the address, for example "Mountain View", or null if it is unknown.
+ */
+ public String getLocality() {
+ return mLocality;
+ }
+
+ /**
+ * Sets the locality of the address to the given String, which may be null.
+ */
+ public void setLocality(String locality) {
+ mLocality = locality;
+ }
+
+ /**
+ * Returns the sub-locality of the address, or null if it is unknown.
+ * For example, this may correspond to the neighborhood of the locality.
+ */
+ public String getSubLocality() {
+ return mSubLocality;
+ }
+
+ /**
+ * Sets the sub-locality of the address to the given String, which may be null.
+ */
+ public void setSubLocality(String sublocality) {
+ mSubLocality = sublocality;
+ }
+
+ /**
+ * Returns the thoroughfare name of the address, for example, "1600 Ampitheater Parkway",
+ * which may be null
+ */
+ public String getThoroughfare() {
+ return mThoroughfare;
+ }
+
+ /**
+ * Sets the thoroughfare name of the address, which may be null.
+ */
+ public void setThoroughfare(String thoroughfare) {
+ this.mThoroughfare = thoroughfare;
+ }
+
+ /**
+ * Returns the sub-thoroughfare name of the address, which may be null.
+ * This may correspond to the street number of the address.
+ */
+ public String getSubThoroughfare() {
+ return mSubThoroughfare;
+ }
+
+ /**
+ * Sets the sub-thoroughfare name of the address, which may be null.
+ */
+ public void setSubThoroughfare(String subthoroughfare) {
+ this.mSubThoroughfare = subthoroughfare;
+ }
+
+ /**
+ * Returns the premises of the address, or null if it is unknown.
+ */
+ public String getPremises() {
+ return mPremises;
+ }
+
+ /**
+ * Sets the premises of the address to the given String, which may be null.
+ */
+ public void setPremises(String premises) {
+ mPremises = premises;
+ }
+
+ /**
+ * Returns the postal code of the address, for example "94110",
+ * or null if it is unknown.
+ */
+ public String getPostalCode() {
+ return mPostalCode;
+ }
+
+ /**
+ * Sets the postal code of the address to the given String, which may
+ * be null.
+ */
+ public void setPostalCode(String postalCode) {
+ mPostalCode = postalCode;
+ }
+
+ /**
+ * Returns the country code of the address, for example "US",
+ * or null if it is unknown.
+ */
+ public String getCountryCode() {
+ return mCountryCode;
+ }
+
+ /**
+ * Sets the country code of the address to the given String, which may
+ * be null.
+ */
+ public void setCountryCode(String countryCode) {
+ mCountryCode = countryCode;
+ }
+
+ /**
+ * Returns the localized country name of the address, for example "Iceland",
+ * or null if it is unknown.
+ */
+ public String getCountryName() {
+ return mCountryName;
+ }
+
+ /**
+ * Sets the country name of the address to the given String, which may
+ * be null.
+ */
+ public void setCountryName(String countryName) {
+ mCountryName = countryName;
+ }
+
+ /**
+ * Returns true if a latitude has been assigned to this Address,
+ * false otherwise.
+ */
+ public boolean hasLatitude() {
+ return mHasLatitude;
+ }
+
+ /**
+ * Returns the latitude of the address if known.
+ *
+ * @throws IllegalStateException if this Address has not been assigned
+ * a latitude.
+ */
+ public double getLatitude() {
+ if (mHasLatitude) {
+ return mLatitude;
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Sets the latitude associated with this address.
+ */
+ public void setLatitude(double latitude) {
+ mLatitude = latitude;
+ mHasLatitude = true;
+ }
+
+ /**
+ * Removes any latitude associated with this address.
+ */
+ public void clearLatitude() {
+ mHasLatitude = false;
+ }
+
+ /**
+ * Returns true if a longitude has been assigned to this Address,
+ * false otherwise.
+ */
+ public boolean hasLongitude() {
+ return mHasLongitude;
+ }
+
+ /**
+ * Returns the longitude of the address if known.
+ *
+ * @throws IllegalStateException if this Address has not been assigned
+ * a longitude.
+ */
+ public double getLongitude() {
+ if (mHasLongitude) {
+ return mLongitude;
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ /**
+ * Sets the longitude associated with this address.
+ */
+ public void setLongitude(double longitude) {
+ mLongitude = longitude;
+ mHasLongitude = true;
+ }
+
+ /**
+ * Removes any longitude associated with this address.
+ */
+ public void clearLongitude() {
+ mHasLongitude = false;
+ }
+
+ /**
+ * Returns the phone number of the address if known,
+ * or null if it is unknown.
+ *
+ * @throws IllegalStateException if this Address has not been assigned
+ * a latitude.
+ */
+ public String getPhone() {
+ return mPhone;
+ }
+
+ /**
+ * Sets the phone number associated with this address.
+ */
+ public void setPhone(String phone) {
+ mPhone = phone;
+ }
+
+ /**
+ * Returns the public URL for the address if known,
+ * or null if it is unknown.
+ */
+ public String getUrl() {
+ return mUrl;
+ }
+
+ /**
+ * Sets the public URL associated with this address.
+ */
+ public void setUrl(String Url) {
+ mUrl = Url;
+ }
+
+ /**
+ * Returns additional provider-specific information about the
+ * address as a Bundle. The keys and values are determined
+ * by the provider. If no additional information is available,
+ * null is returned.
+ *
+ * <!--
+ * <p> A number of common key/value pairs are listed
+ * below. Providers that use any of the keys on this list must
+ * provide the corresponding value as described below.
+ *
+ * <ul>
+ * </ul>
+ * -->
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Sets the extra information associated with this fix to the
+ * given Bundle.
+ */
+ public void setExtras(Bundle extras) {
+ mExtras = (extras == null) ? null : new Bundle(extras);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Address[addressLines=[");
+ for (int i = 0; i <= mMaxAddressLineIndex; i++) {
+ if (i > 0) {
+ sb.append(',');
+ }
+ sb.append(i);
+ sb.append(':');
+ String line = mAddressLines.get(i);
+ if (line == null) {
+ sb.append("null");
+ } else {
+ sb.append('\"');
+ sb.append(line);
+ sb.append('\"');
+ }
+ }
+ sb.append(']');
+ sb.append(",feature=");
+ sb.append(mFeatureName);
+ sb.append(",admin=");
+ sb.append(mAdminArea);
+ sb.append(",sub-admin=");
+ sb.append(mSubAdminArea);
+ sb.append(",locality=");
+ sb.append(mLocality);
+ sb.append(",thoroughfare=");
+ sb.append(mThoroughfare);
+ sb.append(",postalCode=");
+ sb.append(mPostalCode);
+ sb.append(",countryCode=");
+ sb.append(mCountryCode);
+ sb.append(",countryName=");
+ sb.append(mCountryName);
+ sb.append(",hasLatitude=");
+ sb.append(mHasLatitude);
+ sb.append(",latitude=");
+ sb.append(mLatitude);
+ sb.append(",hasLongitude=");
+ sb.append(mHasLongitude);
+ sb.append(",longitude=");
+ sb.append(mLongitude);
+ sb.append(",phone=");
+ sb.append(mPhone);
+ sb.append(",url=");
+ sb.append(mUrl);
+ sb.append(",extras=");
+ sb.append(mExtras);
+ sb.append(']');
+ return sb.toString();
+ }
+
+ public static final Parcelable.Creator<Address> CREATOR =
+ new Parcelable.Creator<Address>() {
+ public Address createFromParcel(Parcel in) {
+ String language = in.readString();
+ String country = in.readString();
+ Locale locale = country.length() > 0 ?
+ new Locale(language, country) :
+ new Locale(language);
+ Address a = new Address(locale);
+
+ int N = in.readInt();
+ if (N > 0) {
+ a.mAddressLines = new HashMap<Integer, String>(N);
+ for (int i = 0; i < N; i++) {
+ int index = in.readInt();
+ String line = in.readString();
+ a.mAddressLines.put(index, line);
+ a.mMaxAddressLineIndex =
+ Math.max(a.mMaxAddressLineIndex, index);
+ }
+ } else {
+ a.mAddressLines = null;
+ a.mMaxAddressLineIndex = -1;
+ }
+ a.mFeatureName = in.readString();
+ a.mAdminArea = in.readString();
+ a.mSubAdminArea = in.readString();
+ a.mLocality = in.readString();
+ a.mSubLocality = in.readString();
+ a.mThoroughfare = in.readString();
+ a.mSubThoroughfare = in.readString();
+ a.mPremises = in.readString();
+ a.mPostalCode = in.readString();
+ a.mCountryCode = in.readString();
+ a.mCountryName = in.readString();
+ a.mHasLatitude = in.readInt() == 0 ? false : true;
+ if (a.mHasLatitude) {
+ a.mLatitude = in.readDouble();
+ }
+ a.mHasLongitude = in.readInt() == 0 ? false : true;
+ if (a.mHasLongitude) {
+ a.mLongitude = in.readDouble();
+ }
+ a.mPhone = in.readString();
+ a.mUrl = in.readString();
+ a.mExtras = in.readBundle();
+ return a;
+ }
+
+ public Address[] newArray(int size) {
+ return new Address[size];
+ }
+ };
+
+ public int describeContents() {
+ return (mExtras != null) ? mExtras.describeContents() : 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mLocale.getLanguage());
+ parcel.writeString(mLocale.getCountry());
+ if (mAddressLines == null) {
+ parcel.writeInt(0);
+ } else {
+ Set<Map.Entry<Integer, String>> entries = mAddressLines.entrySet();
+ parcel.writeInt(entries.size());
+ for (Map.Entry<Integer, String> e : entries) {
+ parcel.writeInt(e.getKey());
+ parcel.writeString(e.getValue());
+ }
+ }
+ parcel.writeString(mFeatureName);
+ parcel.writeString(mAdminArea);
+ parcel.writeString(mSubAdminArea);
+ parcel.writeString(mLocality);
+ parcel.writeString(mSubLocality);
+ parcel.writeString(mThoroughfare);
+ parcel.writeString(mSubThoroughfare);
+ parcel.writeString(mPremises);
+ parcel.writeString(mPostalCode);
+ parcel.writeString(mCountryCode);
+ parcel.writeString(mCountryName);
+ parcel.writeInt(mHasLatitude ? 1 : 0);
+ if (mHasLatitude) {
+ parcel.writeDouble(mLatitude);
+ }
+ parcel.writeInt(mHasLongitude ? 1 : 0);
+ if (mHasLongitude){
+ parcel.writeDouble(mLongitude);
+ }
+ parcel.writeString(mPhone);
+ parcel.writeString(mUrl);
+ parcel.writeBundle(mExtras);
+ }
+}
diff --git a/android/location/BatchedLocationCallback.java b/android/location/BatchedLocationCallback.java
new file mode 100644
index 00000000..f1c40ae2
--- /dev/null
+++ b/android/location/BatchedLocationCallback.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2017 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 android.annotation.IntDef;
+import android.annotation.SystemApi;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * Used for receiving notifications from the LocationManager when
+ * the a batch of location is ready. These methods are called if the
+ * BatchedLocationCallback has been registered with the location manager service
+ * using the
+ * {@link LocationManager#registerGnssBatchedLocationCallback#startGnssBatch(long,
+ * boolean, BatchedLocationCallback, android.os.Handler)} method.
+ * @hide
+ */
+@SystemApi
+public abstract class BatchedLocationCallback {
+
+ /**
+ * Called when a new batch of locations is ready
+ *
+ * @param locations A list of all new locations (possibly zero of them.)
+ */
+ public void onLocationBatch(List<Location> locations) {}
+}
diff --git a/android/location/BatchedLocationCallbackTransport.java b/android/location/BatchedLocationCallbackTransport.java
new file mode 100644
index 00000000..e00f855e
--- /dev/null
+++ b/android/location/BatchedLocationCallbackTransport.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2017 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 android.content.Context;
+import android.os.RemoteException;
+
+import java.util.List;
+
+/**
+ * A handler class to manage transport callbacks for {@link BatchedLocationCallback}.
+ *
+ * @hide
+ */
+class BatchedLocationCallbackTransport
+ extends LocalListenerHelper<BatchedLocationCallback> {
+ private final ILocationManager mLocationManager;
+
+ private final IBatchedLocationCallback mCallbackTransport = new CallbackTransport();
+
+ public BatchedLocationCallbackTransport(Context context, ILocationManager locationManager) {
+ super(context, "BatchedLocationCallbackTransport");
+ mLocationManager = locationManager;
+ }
+
+ @Override
+ protected boolean registerWithServer() throws RemoteException {
+ return mLocationManager.addGnssBatchingCallback(
+ mCallbackTransport,
+ getContext().getPackageName());
+ }
+
+ @Override
+ protected void unregisterFromServer() throws RemoteException {
+ mLocationManager.removeGnssBatchingCallback();
+ }
+
+ private class CallbackTransport extends IBatchedLocationCallback.Stub {
+ @Override
+ public void onLocationBatch(final List<Location> locations) {
+ ListenerOperation<BatchedLocationCallback> operation =
+ new ListenerOperation<BatchedLocationCallback>() {
+ @Override
+ public void execute(BatchedLocationCallback callback)
+ throws RemoteException {
+ callback.onLocationBatch(locations);
+ }
+ };
+ foreach(operation);
+ }
+ }
+}
diff --git a/android/location/Country.java b/android/location/Country.java
new file mode 100644
index 00000000..7c1485d8
--- /dev/null
+++ b/android/location/Country.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2010 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 android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+
+import java.util.Locale;
+
+/**
+ * This class wraps the country information.
+ *
+ * @hide
+ */
+public class Country implements Parcelable {
+ /**
+ * The country code came from the mobile network
+ */
+ public static final int COUNTRY_SOURCE_NETWORK = 0;
+
+ /**
+ * The country code came from the location service
+ */
+ public static final int COUNTRY_SOURCE_LOCATION = 1;
+
+ /**
+ * The country code was read from the SIM card
+ */
+ public static final int COUNTRY_SOURCE_SIM = 2;
+
+ /**
+ * The country code came from the system locale setting
+ */
+ public static final int COUNTRY_SOURCE_LOCALE = 3;
+
+ /**
+ * The ISO 3166-1 two letters country code.
+ */
+ private final String mCountryIso;
+
+ /**
+ * Where the country code came from.
+ */
+ private final int mSource;
+
+ private int mHashCode;
+
+ /**
+ * Time that this object was created (which we assume to be the time that the source was
+ * consulted). This time is in milliseconds since boot up.
+ */
+ private final long mTimestamp;
+
+ /**
+ * @param countryIso the ISO 3166-1 two letters country code.
+ * @param source where the countryIso came from, could be one of below
+ * values
+ * <p>
+ * <ul>
+ * <li>{@link #COUNTRY_SOURCE_NETWORK}</li>
+ * <li>{@link #COUNTRY_SOURCE_LOCATION}</li>
+ * <li>{@link #COUNTRY_SOURCE_SIM}</li>
+ * <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
+ * </ul>
+ */
+ public Country(final String countryIso, final int source) {
+ if (countryIso == null || source < COUNTRY_SOURCE_NETWORK
+ || source > COUNTRY_SOURCE_LOCALE) {
+ throw new IllegalArgumentException();
+ }
+ mCountryIso = countryIso.toUpperCase(Locale.US);
+ mSource = source;
+ mTimestamp = SystemClock.elapsedRealtime();
+ }
+
+ private Country(final String countryIso, final int source, long timestamp) {
+ if (countryIso == null || source < COUNTRY_SOURCE_NETWORK
+ || source > COUNTRY_SOURCE_LOCALE) {
+ throw new IllegalArgumentException();
+ }
+ mCountryIso = countryIso.toUpperCase(Locale.US);
+ mSource = source;
+ mTimestamp = timestamp;
+ }
+
+ public Country(Country country) {
+ mCountryIso = country.mCountryIso;
+ mSource = country.mSource;
+ mTimestamp = country.mTimestamp;
+ }
+
+ /**
+ * @return the ISO 3166-1 two letters country code
+ */
+ public final String getCountryIso() {
+ return mCountryIso;
+ }
+
+ /**
+ * @return where the country code came from, could be one of below values
+ * <p>
+ * <ul>
+ * <li>{@link #COUNTRY_SOURCE_NETWORK}</li>
+ * <li>{@link #COUNTRY_SOURCE_LOCATION}</li>
+ * <li>{@link #COUNTRY_SOURCE_SIM}</li>
+ * <li>{@link #COUNTRY_SOURCE_LOCALE}</li>
+ * </ul>
+ */
+ public final int getSource() {
+ return mSource;
+ }
+
+ /**
+ * Returns the time that this object was created (which we assume to be the time that the source
+ * was consulted).
+ */
+ public final long getTimestamp() {
+ return mTimestamp;
+ }
+
+ public static final Parcelable.Creator<Country> CREATOR = new Parcelable.Creator<Country>() {
+ public Country createFromParcel(Parcel in) {
+ return new Country(in.readString(), in.readInt(), in.readLong());
+ }
+
+ public Country[] newArray(int size) {
+ return new Country[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mCountryIso);
+ parcel.writeInt(mSource);
+ parcel.writeLong(mTimestamp);
+ }
+
+ /**
+ * Returns true if this {@link Country} is equivalent to the given object. This ignores
+ * the timestamp value and just checks for equivalence of countryIso and source values.
+ * Returns false otherwise.
+ */
+ @Override
+ public boolean equals(Object object) {
+ if (object == this) {
+ return true;
+ }
+ if (object instanceof Country) {
+ Country c = (Country) object;
+ // No need to check the equivalence of the timestamp
+ return mCountryIso.equals(c.getCountryIso()) && mSource == c.getSource();
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = mHashCode;
+ if (hash == 0) {
+ hash = 17;
+ hash = hash * 13 + mCountryIso.hashCode();
+ hash = hash * 13 + mSource;
+ mHashCode = hash;
+ }
+ return mHashCode;
+ }
+
+ /**
+ * Compare the specified country to this country object ignoring the source
+ * and timestamp fields, return true if the countryIso fields are equal
+ *
+ * @param country the country to compare
+ * @return true if the specified country's countryIso field is equal to this
+ * country's, false otherwise.
+ */
+ public boolean equalsIgnoreSource(Country country) {
+ return country != null && mCountryIso.equals(country.getCountryIso());
+ }
+
+ @Override
+ public String toString() {
+ return "Country {ISO=" + mCountryIso + ", source=" + mSource + ", time=" + mTimestamp + "}";
+ }
+}
diff --git a/android/location/CountryDetector.java b/android/location/CountryDetector.java
new file mode 100644
index 00000000..ec6dfb71
--- /dev/null
+++ b/android/location/CountryDetector.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2010 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.HashMap;
+
+import android.annotation.SystemService;
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * This class provides access to the system country detector service. This
+ * service allows applications to obtain the country that the user is in.
+ * <p>
+ * The country will be detected in order of reliability, like
+ * <ul>
+ * <li>Mobile network</li>
+ * <li>Location</li>
+ * <li>SIM's country</li>
+ * <li>Phone's locale</li>
+ * </ul>
+ * <p>
+ * Call the {@link #detectCountry()} to get the available country immediately.
+ * <p>
+ * To be notified of the future country change, use the
+ * {@link #addCountryListener}
+ * <p>
+ *
+ * @hide
+ */
+@SystemService(Context.COUNTRY_DETECTOR)
+public class CountryDetector {
+
+ /**
+ * The class to wrap the ICountryListener.Stub and CountryListener objects
+ * together. The CountryListener will be notified through the specific
+ * looper once the country changed and detected.
+ */
+ private final static class ListenerTransport extends ICountryListener.Stub {
+
+ private final CountryListener mListener;
+
+ private final Handler mHandler;
+
+ public ListenerTransport(CountryListener listener, Looper looper) {
+ mListener = listener;
+ if (looper != null) {
+ mHandler = new Handler(looper);
+ } else {
+ mHandler = new Handler();
+ }
+ }
+
+ public void onCountryDetected(final Country country) {
+ mHandler.post(new Runnable() {
+ public void run() {
+ mListener.onCountryDetected(country);
+ }
+ });
+ }
+ }
+
+ private final static String TAG = "CountryDetector";
+ private final ICountryDetector mService;
+ private final HashMap<CountryListener, ListenerTransport> mListeners;
+
+ /**
+ * @hide - hide this constructor because it has a parameter of type
+ * ICountryDetector, which is a system private class. The right way to
+ * create an instance of this class is using the factory
+ * Context.getSystemService.
+ */
+ public CountryDetector(ICountryDetector service) {
+ mService = service;
+ mListeners = new HashMap<CountryListener, ListenerTransport>();
+ }
+
+ /**
+ * Start detecting the country that the user is in.
+ *
+ * @return the country if it is available immediately, otherwise null will
+ * be returned.
+ */
+ public Country detectCountry() {
+ try {
+ return mService.detectCountry();
+ } catch (RemoteException e) {
+ Log.e(TAG, "detectCountry: RemoteException", e);
+ return null;
+ }
+ }
+
+ /**
+ * Add a listener to receive the notification when the country is detected
+ * or changed.
+ *
+ * @param listener will be called when the country is detected or changed.
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism. If looper is null then the
+ * callbacks will be called on the main thread.
+ */
+ public void addCountryListener(CountryListener listener, Looper looper) {
+ synchronized (mListeners) {
+ if (!mListeners.containsKey(listener)) {
+ ListenerTransport transport = new ListenerTransport(listener, looper);
+ try {
+ mService.addCountryListener(transport);
+ mListeners.put(listener, transport);
+ } catch (RemoteException e) {
+ Log.e(TAG, "addCountryListener: RemoteException", e);
+ }
+ }
+ }
+ }
+
+ /**
+ * Remove the listener
+ */
+ public void removeCountryListener(CountryListener listener) {
+ synchronized (mListeners) {
+ ListenerTransport transport = mListeners.get(listener);
+ if (transport != null) {
+ try {
+ mListeners.remove(listener);
+ mService.removeCountryListener(transport);
+ } catch (RemoteException e) {
+ Log.e(TAG, "removeCountryListener: RemoteException", e);
+ }
+ }
+ }
+ }
+}
diff --git a/android/location/CountryListener.java b/android/location/CountryListener.java
new file mode 100644
index 00000000..e36db412
--- /dev/null
+++ b/android/location/CountryListener.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 2010 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;
+
+/**
+ * The listener for receiving the notification when the country is detected or
+ * changed
+ *
+ * @hide
+ */
+public interface CountryListener {
+ /**
+ * @param country the changed or detected country.
+ */
+ void onCountryDetected(Country country);
+}
diff --git a/android/location/Criteria.java b/android/location/Criteria.java
new file mode 100644
index 00000000..a6099be6
--- /dev/null
+++ b/android/location/Criteria.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2007 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 android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class indicating the application criteria for selecting a
+ * location provider. Providers maybe ordered according to accuracy,
+ * power usage, ability to report altitude, speed,
+ * and bearing, and monetary cost.
+ */
+public class Criteria implements Parcelable {
+ /**
+ * A constant indicating that the application does not choose to
+ * place requirement on a particular feature.
+ */
+ public static final int NO_REQUIREMENT = 0;
+
+ /**
+ * A constant indicating a low power requirement.
+ */
+ public static final int POWER_LOW = 1;
+
+ /**
+ * A constant indicating a medium power requirement.
+ */
+ public static final int POWER_MEDIUM = 2;
+
+ /**
+ * A constant indicating a high power requirement.
+ */
+ public static final int POWER_HIGH = 3;
+
+ /**
+ * A constant indicating a finer location accuracy requirement
+ */
+ public static final int ACCURACY_FINE = 1;
+
+ /**
+ * A constant indicating an approximate accuracy requirement
+ */
+ public static final int ACCURACY_COARSE = 2;
+
+ /**
+ * A constant indicating a low location accuracy requirement
+ * - may be used for horizontal, altitude, speed or bearing accuracy.
+ * For horizontal and vertical position this corresponds roughly to
+ * an accuracy of greater than 500 meters.
+ */
+ public static final int ACCURACY_LOW = 1;
+
+ /**
+ * A constant indicating a medium accuracy requirement
+ * - currently used only for horizontal accuracy.
+ * For horizontal position this corresponds roughly to to an accuracy
+ * of between 100 and 500 meters.
+ */
+ public static final int ACCURACY_MEDIUM = 2;
+
+ /**
+ * a constant indicating a high accuracy requirement
+ * - may be used for horizontal, altitude, speed or bearing accuracy.
+ * For horizontal and vertical position this corresponds roughly to
+ * an accuracy of less than 100 meters.
+ */
+ public static final int ACCURACY_HIGH = 3;
+
+ private int mHorizontalAccuracy = NO_REQUIREMENT;
+ private int mVerticalAccuracy = NO_REQUIREMENT;
+ private int mSpeedAccuracy = NO_REQUIREMENT;
+ private int mBearingAccuracy = NO_REQUIREMENT;
+ private int mPowerRequirement = NO_REQUIREMENT;
+ private boolean mAltitudeRequired = false;
+ private boolean mBearingRequired = false;
+ private boolean mSpeedRequired = false;
+ private boolean mCostAllowed = false;
+
+ /**
+ * Constructs a new Criteria object. The new object will have no
+ * requirements on accuracy, power, or response time; will not
+ * require altitude, speed, or bearing; and will not allow monetary
+ * cost.
+ */
+ public Criteria() {}
+
+ /**
+ * Constructs a new Criteria object that is a copy of the given criteria.
+ */
+ public Criteria(Criteria criteria) {
+ mHorizontalAccuracy = criteria.mHorizontalAccuracy;
+ mVerticalAccuracy = criteria.mVerticalAccuracy;
+ mSpeedAccuracy = criteria.mSpeedAccuracy;
+ mBearingAccuracy = criteria.mBearingAccuracy;
+ mPowerRequirement = criteria.mPowerRequirement;
+ mAltitudeRequired = criteria.mAltitudeRequired;
+ mBearingRequired = criteria.mBearingRequired;
+ mSpeedRequired = criteria.mSpeedRequired;
+ mCostAllowed = criteria.mCostAllowed;
+ }
+
+ /**
+ * Indicates the desired horizontal accuracy (latitude and longitude).
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
+ * {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}.
+ * More accurate location may consume more power and may take longer.
+ *
+ * @throws IllegalArgumentException if accuracy is not one of the supported constants
+ */
+ public void setHorizontalAccuracy(int accuracy) {
+ if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
+ throw new IllegalArgumentException("accuracy=" + accuracy);
+ }
+ mHorizontalAccuracy = accuracy;
+ }
+
+ /**
+ * Returns a constant indicating the desired horizontal accuracy (latitude and longitude).
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
+ * {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}.
+ */
+ public int getHorizontalAccuracy() {
+ return mHorizontalAccuracy;
+ }
+
+ /**
+ * Indicates the desired vertical accuracy (altitude).
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_MEDIUM},
+ * {@link #ACCURACY_HIGH} or {@link #NO_REQUIREMENT}.
+ * More accurate location may consume more power and may take longer.
+ *
+ * @throws IllegalArgumentException if accuracy is not one of the supported constants
+ */
+ public void setVerticalAccuracy(int accuracy) {
+ if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
+ throw new IllegalArgumentException("accuracy=" + accuracy);
+ }
+ mVerticalAccuracy = accuracy;
+ }
+
+ /**
+ * Returns a constant indicating the desired vertical accuracy (altitude).
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
+ * or {@link #NO_REQUIREMENT}.
+ */
+ public int getVerticalAccuracy() {
+ return mVerticalAccuracy;
+ }
+
+ /**
+ * Indicates the desired speed accuracy.
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
+ * or {@link #NO_REQUIREMENT}.
+ * More accurate location may consume more power and may take longer.
+ *
+ * @throws IllegalArgumentException if accuracy is not one of the supported constants
+ */
+ public void setSpeedAccuracy(int accuracy) {
+ if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
+ throw new IllegalArgumentException("accuracy=" + accuracy);
+ }
+ mSpeedAccuracy = accuracy;
+ }
+
+ /**
+ * Returns a constant indicating the desired speed accuracy
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
+ * or {@link #NO_REQUIREMENT}.
+ */
+ public int getSpeedAccuracy() {
+ return mSpeedAccuracy;
+ }
+
+ /**
+ * Indicates the desired bearing accuracy.
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
+ * or {@link #NO_REQUIREMENT}.
+ * More accurate location may consume more power and may take longer.
+ *
+ * @throws IllegalArgumentException if accuracy is not one of the supported constants
+ */
+ public void setBearingAccuracy(int accuracy) {
+ if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_HIGH) {
+ throw new IllegalArgumentException("accuracy=" + accuracy);
+ }
+ mBearingAccuracy = accuracy;
+ }
+
+ /**
+ * Returns a constant indicating the desired bearing accuracy.
+ * Accuracy may be {@link #ACCURACY_LOW}, {@link #ACCURACY_HIGH},
+ * or {@link #NO_REQUIREMENT}.
+ */
+ public int getBearingAccuracy() {
+ return mBearingAccuracy;
+ }
+
+ /**
+ * Indicates the desired accuracy for latitude and longitude. Accuracy
+ * may be {@link #ACCURACY_FINE} if desired location
+ * is fine, else it can be {@link #ACCURACY_COARSE}.
+ * More accurate location may consume more power and may take longer.
+ *
+ * @throws IllegalArgumentException if accuracy is not one of the supported constants
+ */
+ public void setAccuracy(int accuracy) {
+ if (accuracy < NO_REQUIREMENT || accuracy > ACCURACY_COARSE) {
+ throw new IllegalArgumentException("accuracy=" + accuracy);
+ }
+ if (accuracy == ACCURACY_FINE) {
+ mHorizontalAccuracy = ACCURACY_HIGH;
+ } else {
+ mHorizontalAccuracy = ACCURACY_LOW;
+ }
+ }
+
+ /**
+ * Returns a constant indicating desired accuracy of location
+ * Accuracy may be {@link #ACCURACY_FINE} if desired location
+ * is fine, else it can be {@link #ACCURACY_COARSE}.
+ */
+ public int getAccuracy() {
+ if (mHorizontalAccuracy >= ACCURACY_HIGH) {
+ return ACCURACY_FINE;
+ } else {
+ return ACCURACY_COARSE;
+ }
+ }
+
+ /**
+ * Indicates the desired maximum power level. The level parameter
+ * must be one of NO_REQUIREMENT, POWER_LOW, POWER_MEDIUM, or
+ * POWER_HIGH.
+ */
+ public void setPowerRequirement(int level) {
+ if (level < NO_REQUIREMENT || level > POWER_HIGH) {
+ throw new IllegalArgumentException("level=" + level);
+ }
+ mPowerRequirement = level;
+ }
+
+ /**
+ * Returns a constant indicating the desired power requirement. The
+ * returned
+ */
+ public int getPowerRequirement() {
+ return mPowerRequirement;
+ }
+
+ /**
+ * Indicates whether the provider is allowed to incur monetary cost.
+ */
+ public void setCostAllowed(boolean costAllowed) {
+ mCostAllowed = costAllowed;
+ }
+
+ /**
+ * Returns whether the provider is allowed to incur monetary cost.
+ */
+ public boolean isCostAllowed() {
+ return mCostAllowed;
+ }
+
+ /**
+ * Indicates whether the provider must provide altitude information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public void setAltitudeRequired(boolean altitudeRequired) {
+ mAltitudeRequired = altitudeRequired;
+ }
+
+ /**
+ * Returns whether the provider must provide altitude information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public boolean isAltitudeRequired() {
+ return mAltitudeRequired;
+ }
+
+ /**
+ * Indicates whether the provider must provide speed information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public void setSpeedRequired(boolean speedRequired) {
+ mSpeedRequired = speedRequired;
+ }
+
+ /**
+ * Returns whether the provider must provide speed information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public boolean isSpeedRequired() {
+ return mSpeedRequired;
+ }
+
+ /**
+ * Indicates whether the provider must provide bearing information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public void setBearingRequired(boolean bearingRequired) {
+ mBearingRequired = bearingRequired;
+ }
+
+ /**
+ * Returns whether the provider must provide bearing information.
+ * Not all fixes are guaranteed to contain such information.
+ */
+ public boolean isBearingRequired() {
+ return mBearingRequired;
+ }
+
+ public static final Parcelable.Creator<Criteria> CREATOR =
+ new Parcelable.Creator<Criteria>() {
+ @Override
+ public Criteria createFromParcel(Parcel in) {
+ Criteria c = new Criteria();
+ c.mHorizontalAccuracy = in.readInt();
+ c.mVerticalAccuracy = in.readInt();
+ c.mSpeedAccuracy = in.readInt();
+ c.mBearingAccuracy = in.readInt();
+ c.mPowerRequirement = in.readInt();
+ c.mAltitudeRequired = in.readInt() != 0;
+ c.mBearingRequired = in.readInt() != 0;
+ c.mSpeedRequired = in.readInt() != 0;
+ c.mCostAllowed = in.readInt() != 0;
+ return c;
+ }
+
+ @Override
+ public Criteria[] newArray(int size) {
+ return new Criteria[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mHorizontalAccuracy);
+ parcel.writeInt(mVerticalAccuracy);
+ parcel.writeInt(mSpeedAccuracy);
+ parcel.writeInt(mBearingAccuracy);
+ parcel.writeInt(mPowerRequirement);
+ parcel.writeInt(mAltitudeRequired ? 1 : 0);
+ parcel.writeInt(mBearingRequired ? 1 : 0);
+ parcel.writeInt(mSpeedRequired ? 1 : 0);
+ parcel.writeInt(mCostAllowed ? 1 : 0);
+ }
+
+ private static String powerToString(int power) {
+ switch (power) {
+ case NO_REQUIREMENT:
+ return "NO_REQ";
+ case POWER_LOW:
+ return "LOW";
+ case POWER_MEDIUM:
+ return "MEDIUM";
+ case POWER_HIGH:
+ return "HIGH";
+ default:
+ return "???";
+ }
+ }
+
+ private static String accuracyToString(int accuracy) {
+ switch (accuracy) {
+ case NO_REQUIREMENT:
+ return "---";
+ case ACCURACY_HIGH:
+ return "HIGH";
+ case ACCURACY_MEDIUM:
+ return "MEDIUM";
+ case ACCURACY_LOW:
+ return "LOW";
+ default:
+ return "???";
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("Criteria[power=").append(powerToString(mPowerRequirement));
+ s.append(" acc=").append(accuracyToString(mHorizontalAccuracy));
+ s.append(']');
+ return s.toString();
+ }
+}
diff --git a/android/location/FusedBatchOptions.java b/android/location/FusedBatchOptions.java
new file mode 100644
index 00000000..aa4a8602
--- /dev/null
+++ b/android/location/FusedBatchOptions.java
@@ -0,0 +1,148 @@
+/*
+ * Copyright (C) 2013 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 android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A data class representing a set of options to configure batching sessions.
+ * @hide
+ */
+public class FusedBatchOptions implements Parcelable {
+ private volatile long mPeriodInNS = 0;
+ private volatile int mSourcesToUse = 0;
+ private volatile int mFlags = 0;
+
+ // the default value is set to request fixes at no cost
+ private volatile double mMaxPowerAllocationInMW = 0;
+ // If non-zero can be used for power savings by throttling location when device hasn't moved.
+ private volatile float mSmallestDisplacementMeters = 0;
+
+ /*
+ * Getters and setters for properties needed to hold the options.
+ */
+ public void setMaxPowerAllocationInMW(double value) {
+ mMaxPowerAllocationInMW = value;
+ }
+
+ public double getMaxPowerAllocationInMW() {
+ return mMaxPowerAllocationInMW;
+ }
+
+ public void setPeriodInNS(long value) {
+ mPeriodInNS = value;
+ }
+
+ public long getPeriodInNS() {
+ return mPeriodInNS;
+ }
+
+ public void setSmallestDisplacementMeters(float value) {
+ mSmallestDisplacementMeters = value;
+ }
+
+ public float getSmallestDisplacementMeters() {
+ return mSmallestDisplacementMeters;
+ }
+
+ public void setSourceToUse(int source) {
+ mSourcesToUse |= source;
+ }
+
+ public void resetSourceToUse(int source) {
+ mSourcesToUse &= ~source;
+ }
+
+ public boolean isSourceToUseSet(int source) {
+ return (mSourcesToUse & source) != 0;
+ }
+
+ public int getSourcesToUse() {
+ return mSourcesToUse;
+ }
+
+ public void setFlag(int flag) {
+ mFlags |= flag;
+ }
+
+ public void resetFlag(int flag) {
+ mFlags &= ~flag;
+ }
+
+ public boolean isFlagSet(int flag) {
+ return (mFlags & flag) != 0;
+ }
+
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Definition of enum flag sets needed by this class.
+ * Such values need to be kept in sync with the ones in fused_location.h
+ */
+ public static final class SourceTechnologies {
+ public static int GNSS = 1<<0;
+ public static int WIFI = 1<<1;
+ public static int SENSORS = 1<<2;
+ public static int CELL = 1<<3;
+ public static int BLUETOOTH = 1<<4;
+ }
+
+ public static final class BatchFlags {
+ // follow the definitions to the letter in fused_location.h
+ public static int WAKEUP_ON_FIFO_FULL = 0x0000001;
+ public static int CALLBACK_ON_LOCATION_FIX =0x0000002;
+ }
+
+ /*
+ * Method definitions to support Parcelable operations.
+ */
+ public static final Parcelable.Creator<FusedBatchOptions> CREATOR =
+ new Parcelable.Creator<FusedBatchOptions>() {
+ @Override
+ public FusedBatchOptions createFromParcel(Parcel parcel) {
+ FusedBatchOptions options = new FusedBatchOptions();
+ options.setMaxPowerAllocationInMW(parcel.readDouble());
+ options.setPeriodInNS(parcel.readLong());
+ options.setSourceToUse(parcel.readInt());
+ options.setFlag(parcel.readInt());
+ options.setSmallestDisplacementMeters(parcel.readFloat());
+ return options;
+ }
+
+ @Override
+ public FusedBatchOptions[] newArray(int size) {
+ return new FusedBatchOptions[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeDouble(mMaxPowerAllocationInMW);
+ parcel.writeLong(mPeriodInNS);
+ parcel.writeInt(mSourcesToUse);
+ parcel.writeInt(mFlags);
+ parcel.writeFloat(mSmallestDisplacementMeters);
+ }
+}
diff --git a/android/location/Geocoder.java b/android/location/Geocoder.java
new file mode 100644
index 00000000..ac7eb8b1
--- /dev/null
+++ b/android/location/Geocoder.java
@@ -0,0 +1,260 @@
+/*
+ * Copyright (C) 2007 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 android.content.Context;
+import android.location.Address;
+import android.os.RemoteException;
+import android.os.IBinder;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.Locale;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * A class for handling geocoding and reverse geocoding. Geocoding is
+ * the process of transforming a street address or other description
+ * of a location into a (latitude, longitude) coordinate. Reverse
+ * geocoding is the process of transforming a (latitude, longitude)
+ * coordinate into a (partial) address. The amount of detail in a
+ * reverse geocoded location description may vary, for example one
+ * might contain the full street address of the closest building, while
+ * another might contain only a city name and postal code.
+ *
+ * The Geocoder class requires a backend service that is not included in
+ * the core android framework. The Geocoder query methods will return an
+ * empty list if there no backend service in the platform. Use the
+ * isPresent() method to determine whether a Geocoder implementation
+ * exists.
+ */
+public final class Geocoder {
+ private static final String TAG = "Geocoder";
+
+ private GeocoderParams mParams;
+ private ILocationManager mService;
+
+ /**
+ * Returns true if the Geocoder methods getFromLocation and
+ * getFromLocationName are implemented. Lack of network
+ * connectivity may still cause these methods to return null or
+ * empty lists.
+ */
+ public static boolean isPresent() {
+ IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
+ ILocationManager lm = ILocationManager.Stub.asInterface(b);
+ try {
+ return lm.geocoderIsPresent();
+ } catch (RemoteException e) {
+ Log.e(TAG, "isPresent: got RemoteException", e);
+ return false;
+ }
+ }
+
+ /**
+ * Constructs a Geocoder whose responses will be localized for the
+ * given Locale.
+ *
+ * @param context the Context of the calling Activity
+ * @param locale the desired Locale for the query results
+ *
+ * @throws NullPointerException if Locale is null
+ */
+ public Geocoder(Context context, Locale locale) {
+ if (locale == null) {
+ throw new NullPointerException("locale == null");
+ }
+ mParams = new GeocoderParams(context, locale);
+ IBinder b = ServiceManager.getService(Context.LOCATION_SERVICE);
+ mService = ILocationManager.Stub.asInterface(b);
+ }
+
+ /**
+ * Constructs a Geocoder whose responses will be localized for the
+ * default system Locale.
+ *
+ * @param context the Context of the calling Activity
+ */
+ public Geocoder(Context context) {
+ this(context, Locale.getDefault());
+ }
+
+ /**
+ * Returns an array of Addresses that are known to describe the
+ * area immediately surrounding the given latitude and longitude.
+ * The returned addresses will be localized for the locale
+ * provided to this class's constructor.
+ *
+ * <p> The returned values may be obtained by means of a network lookup.
+ * The results are a best guess and are not guaranteed to be meaningful or
+ * correct. It may be useful to call this method from a thread separate from your
+ * primary UI thread.
+ *
+ * @param latitude the latitude a point for the search
+ * @param longitude the longitude a point for the search
+ * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
+ *
+ * @return a list of Address objects. Returns null or empty list if no matches were
+ * found or there is no backend service available.
+ *
+ * @throws IllegalArgumentException if latitude is
+ * less than -90 or greater than 90
+ * @throws IllegalArgumentException if longitude is
+ * less than -180 or greater than 180
+ * @throws IOException if the network is unavailable or any other
+ * I/O problem occurs
+ */
+ public List<Address> getFromLocation(double latitude, double longitude, int maxResults)
+ throws IOException {
+ if (latitude < -90.0 || latitude > 90.0) {
+ throw new IllegalArgumentException("latitude == " + latitude);
+ }
+ if (longitude < -180.0 || longitude > 180.0) {
+ throw new IllegalArgumentException("longitude == " + longitude);
+ }
+ try {
+ List<Address> results = new ArrayList<Address>();
+ String ex = mService.getFromLocation(latitude, longitude, maxResults,
+ mParams, results);
+ if (ex != null) {
+ throw new IOException(ex);
+ } else {
+ return results;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocation: got RemoteException", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns an array of Addresses that are known to describe the
+ * named location, which may be a place name such as "Dalvik,
+ * Iceland", an address such as "1600 Amphitheatre Parkway,
+ * Mountain View, CA", an airport code such as "SFO", etc.. The
+ * returned addresses will be localized for the locale provided to
+ * this class's constructor.
+ *
+ * <p> The query will block and returned values will be obtained by means of a network lookup.
+ * The results are a best guess and are not guaranteed to be meaningful or
+ * correct. It may be useful to call this method from a thread separate from your
+ * primary UI thread.
+ *
+ * @param locationName a user-supplied description of a location
+ * @param maxResults max number of results to return. Smaller numbers (1 to 5) are recommended
+ *
+ * @return a list of Address objects. Returns null or empty list if no matches were
+ * found or there is no backend service available.
+ *
+ * @throws IllegalArgumentException if locationName is null
+ * @throws IOException if the network is unavailable or any other
+ * I/O problem occurs
+ */
+ public List<Address> getFromLocationName(String locationName, int maxResults) throws IOException {
+ if (locationName == null) {
+ throw new IllegalArgumentException("locationName == null");
+ }
+ try {
+ List<Address> results = new ArrayList<Address>();
+ String ex = mService.getFromLocationName(locationName,
+ 0, 0, 0, 0, maxResults, mParams, results);
+ if (ex != null) {
+ throw new IOException(ex);
+ } else {
+ return results;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocationName: got RemoteException", e);
+ return null;
+ }
+ }
+
+ /**
+ * Returns an array of Addresses that are known to describe the
+ * named location, which may be a place name such as "Dalvik,
+ * Iceland", an address such as "1600 Amphitheatre Parkway,
+ * Mountain View, CA", an airport code such as "SFO", etc.. The
+ * returned addresses will be localized for the locale provided to
+ * this class's constructor.
+ *
+ * <p> You may specify a bounding box for the search results by including
+ * the Latitude and Longitude of the Lower Left point and Upper Right
+ * point of the box.
+ *
+ * <p> The query will block and returned values will be obtained by means of a network lookup.
+ * The results are a best guess and are not guaranteed to be meaningful or
+ * correct. It may be useful to call this method from a thread separate from your
+ * primary UI thread.
+ *
+ * @param locationName a user-supplied description of a location
+ * @param maxResults max number of addresses to return. Smaller numbers (1 to 5) are recommended
+ * @param lowerLeftLatitude the latitude of the lower left corner of the bounding box
+ * @param lowerLeftLongitude the longitude of the lower left corner of the bounding box
+ * @param upperRightLatitude the latitude of the upper right corner of the bounding box
+ * @param upperRightLongitude the longitude of the upper right corner of the bounding box
+ *
+ * @return a list of Address objects. Returns null or empty list if no matches were
+ * found or there is no backend service available.
+ *
+ * @throws IllegalArgumentException if locationName is null
+ * @throws IllegalArgumentException if any latitude is
+ * less than -90 or greater than 90
+ * @throws IllegalArgumentException if any longitude is
+ * less than -180 or greater than 180
+ * @throws IOException if the network is unavailable or any other
+ * I/O problem occurs
+ */
+ public List<Address> getFromLocationName(String locationName, int maxResults,
+ double lowerLeftLatitude, double lowerLeftLongitude,
+ double upperRightLatitude, double upperRightLongitude) throws IOException {
+ if (locationName == null) {
+ throw new IllegalArgumentException("locationName == null");
+ }
+ if (lowerLeftLatitude < -90.0 || lowerLeftLatitude > 90.0) {
+ throw new IllegalArgumentException("lowerLeftLatitude == "
+ + lowerLeftLatitude);
+ }
+ if (lowerLeftLongitude < -180.0 || lowerLeftLongitude > 180.0) {
+ throw new IllegalArgumentException("lowerLeftLongitude == "
+ + lowerLeftLongitude);
+ }
+ if (upperRightLatitude < -90.0 || upperRightLatitude > 90.0) {
+ throw new IllegalArgumentException("upperRightLatitude == "
+ + upperRightLatitude);
+ }
+ if (upperRightLongitude < -180.0 || upperRightLongitude > 180.0) {
+ throw new IllegalArgumentException("upperRightLongitude == "
+ + upperRightLongitude);
+ }
+ try {
+ ArrayList<Address> result = new ArrayList<Address>();
+ String ex = mService.getFromLocationName(locationName,
+ lowerLeftLatitude, lowerLeftLongitude, upperRightLatitude, upperRightLongitude,
+ maxResults, mParams, result);
+ if (ex != null) {
+ throw new IOException(ex);
+ } else {
+ return result;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "getFromLocationName: got RemoteException", e);
+ return null;
+ }
+ }
+}
diff --git a/android/location/GeocoderParams.java b/android/location/GeocoderParams.java
new file mode 100644
index 00000000..174fe3e0
--- /dev/null
+++ b/android/location/GeocoderParams.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright (C) 2010 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 android.content.Context;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Locale;
+
+/**
+ * This class contains extra parameters to pass to an IGeocodeProvider
+ * implementation from the Geocoder class. Currently this contains the
+ * language, country and variant information from the Geocoder's locale
+ * as well as the Geocoder client's package name for geocoder server
+ * logging. This information is kept in a separate class to allow for
+ * future expansion of the IGeocodeProvider interface.
+ *
+ * @hide
+ */
+public class GeocoderParams implements Parcelable {
+ private Locale mLocale;
+ private String mPackageName;
+
+ // used only for parcelling
+ private GeocoderParams() {
+ }
+
+ /**
+ * This object is only constructed by the Geocoder class
+ *
+ * @hide
+ */
+ public GeocoderParams(Context context, Locale locale) {
+ mLocale = locale;
+ mPackageName = context.getPackageName();
+ }
+
+ /**
+ * returns the Geocoder's locale
+ */
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /**
+ * returns the package name of the Geocoder's client
+ */
+ public String getClientPackage() {
+ return mPackageName;
+ }
+
+ public static final Parcelable.Creator<GeocoderParams> CREATOR =
+ new Parcelable.Creator<GeocoderParams>() {
+ public GeocoderParams createFromParcel(Parcel in) {
+ GeocoderParams gp = new GeocoderParams();
+ String language = in.readString();
+ String country = in.readString();
+ String variant = in.readString();
+ gp.mLocale = new Locale(language, country, variant);
+ gp.mPackageName = in.readString();
+ return gp;
+ }
+
+ public GeocoderParams[] newArray(int size) {
+ return new GeocoderParams[size];
+ }
+ };
+
+ public int describeContents() {
+ return 0;
+ }
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mLocale.getLanguage());
+ parcel.writeString(mLocale.getCountry());
+ parcel.writeString(mLocale.getVariant());
+ parcel.writeString(mPackageName);
+ }
+}
diff --git a/android/location/Geofence.java b/android/location/Geofence.java
new file mode 100644
index 00000000..5de779a2
--- /dev/null
+++ b/android/location/Geofence.java
@@ -0,0 +1,182 @@
+/*
+ * 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 android.location;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Represents a geographical boundary, also known as a geofence.
+ *
+ * <p>Currently only circular geofences are supported and they do not support altitude changes.
+ *
+ * @hide
+ */
+public final class Geofence implements Parcelable {
+ /** @hide */
+ public static final int TYPE_HORIZONTAL_CIRCLE = 1;
+
+ private final int mType;
+ private final double mLatitude;
+ private final double mLongitude;
+ private final float mRadius;
+
+ /**
+ * Create a circular geofence (on a flat, horizontal plane).
+ *
+ * @param latitude latitude in degrees, between -90 and +90 inclusive
+ * @param longitude longitude in degrees, between -180 and +180 inclusive
+ * @param radius radius in meters
+ * @return a new geofence
+ * @throws IllegalArgumentException if any parameters are out of range
+ */
+ public static Geofence createCircle(double latitude, double longitude, float radius) {
+ return new Geofence(latitude, longitude, radius);
+ }
+
+ private Geofence(double latitude, double longitude, float radius) {
+ checkRadius(radius);
+ checkLatLong(latitude, longitude);
+ mType = TYPE_HORIZONTAL_CIRCLE;
+ mLatitude = latitude;
+ mLongitude = longitude;
+ mRadius = radius;
+ }
+
+ /** @hide */
+ public int getType() {
+ return mType;
+ }
+
+ /** @hide */
+ public double getLatitude() {
+ return mLatitude;
+ }
+
+ /** @hide */
+ public double getLongitude() {
+ return mLongitude;
+ }
+
+ /** @hide */
+ public float getRadius() {
+ return mRadius;
+ }
+
+ private static void checkRadius(float radius) {
+ if (radius <= 0) {
+ throw new IllegalArgumentException("invalid radius: " + radius);
+ }
+ }
+
+ private static void checkLatLong(double latitude, double longitude) {
+ if (latitude > 90.0 || latitude < -90.0) {
+ throw new IllegalArgumentException("invalid latitude: " + latitude);
+ }
+ if (longitude > 180.0 || longitude < -180.0) {
+ throw new IllegalArgumentException("invalid longitude: " + longitude);
+ }
+ }
+
+ private static void checkType(int type) {
+ if (type != TYPE_HORIZONTAL_CIRCLE) {
+ throw new IllegalArgumentException("invalid type: " + type);
+ }
+ }
+
+ public static final Parcelable.Creator<Geofence> CREATOR = new Parcelable.Creator<Geofence>() {
+ @Override
+ public Geofence createFromParcel(Parcel in) {
+ int type = in.readInt();
+ double latitude = in.readDouble();
+ double longitude = in.readDouble();
+ float radius = in.readFloat();
+ checkType(type);
+ return Geofence.createCircle(latitude, longitude, radius);
+ }
+ @Override
+ public Geofence[] newArray(int size) {
+ return new Geofence[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mType);
+ parcel.writeDouble(mLatitude);
+ parcel.writeDouble(mLongitude);
+ parcel.writeFloat(mRadius);
+ }
+
+ private static String typeToString(int type) {
+ switch (type) {
+ case TYPE_HORIZONTAL_CIRCLE:
+ return "CIRCLE";
+ default:
+ checkType(type);
+ return null;
+ }
+ }
+
+ @Override
+ public String toString() {
+ return String.format("Geofence[%s %.6f, %.6f %.0fm]",
+ typeToString(mType), mLatitude, mLongitude, mRadius);
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ long temp;
+ temp = Double.doubleToLongBits(mLatitude);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ temp = Double.doubleToLongBits(mLongitude);
+ result = prime * result + (int) (temp ^ (temp >>> 32));
+ result = prime * result + Float.floatToIntBits(mRadius);
+ result = prime * result + mType;
+ return result;
+ }
+
+ /**
+ * Two geofences are equal if they have identical properties.
+ */
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (!(obj instanceof Geofence))
+ return false;
+ Geofence other = (Geofence) obj;
+ if (mRadius != other.mRadius)
+ return false;
+ if (mLatitude != other.mLatitude)
+ return false;
+ if (mLongitude != other.mLongitude)
+ return false;
+ if (mType != other.mType)
+ return false;
+ return true;
+ }
+}
diff --git a/android/location/GnssClock.java b/android/location/GnssClock.java
new file mode 100644
index 00000000..25d247de
--- /dev/null
+++ b/android/location/GnssClock.java
@@ -0,0 +1,541 @@
+/*
+ * Copyright (C) 2014 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 android.annotation.TestApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class containing a GPS clock timestamp.
+ *
+ * <p>It represents a measurement of the GPS receiver's clock.
+ */
+public final class GnssClock implements Parcelable {
+ // The following enumerations must be in sync with the values declared in gps.h
+
+ private static final int HAS_NO_FLAGS = 0;
+ private static final int HAS_LEAP_SECOND = (1<<0);
+ private static final int HAS_TIME_UNCERTAINTY = (1<<1);
+ private static final int HAS_FULL_BIAS = (1<<2);
+ private static final int HAS_BIAS = (1<<3);
+ private static final int HAS_BIAS_UNCERTAINTY = (1<<4);
+ private static final int HAS_DRIFT = (1<<5);
+ private static final int HAS_DRIFT_UNCERTAINTY = (1<<6);
+
+ // End enumerations in sync with gps.h
+
+ private int mFlags;
+ private int mLeapSecond;
+ private long mTimeNanos;
+ private double mTimeUncertaintyNanos;
+ private long mFullBiasNanos;
+ private double mBiasNanos;
+ private double mBiasUncertaintyNanos;
+ private double mDriftNanosPerSecond;
+ private double mDriftUncertaintyNanosPerSecond;
+ private int mHardwareClockDiscontinuityCount;
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public GnssClock() {
+ initialize();
+ }
+
+ /**
+ * Sets all contents to the values stored in the provided object.
+ * @hide
+ */
+ @TestApi
+ public void set(GnssClock clock) {
+ mFlags = clock.mFlags;
+ mLeapSecond = clock.mLeapSecond;
+ mTimeNanos = clock.mTimeNanos;
+ mTimeUncertaintyNanos = clock.mTimeUncertaintyNanos;
+ mFullBiasNanos = clock.mFullBiasNanos;
+ mBiasNanos = clock.mBiasNanos;
+ mBiasUncertaintyNanos = clock.mBiasUncertaintyNanos;
+ mDriftNanosPerSecond = clock.mDriftNanosPerSecond;
+ mDriftUncertaintyNanosPerSecond = clock.mDriftUncertaintyNanosPerSecond;
+ mHardwareClockDiscontinuityCount = clock.mHardwareClockDiscontinuityCount;
+ }
+
+ /**
+ * Resets all the contents to its original state.
+ * @hide
+ */
+ @TestApi
+ public void reset() {
+ initialize();
+ }
+
+ /**
+ * Returns {@code true} if {@link #getLeapSecond()} is available, {@code false} otherwise.
+ */
+ public boolean hasLeapSecond() {
+ return isFlagSet(HAS_LEAP_SECOND);
+ }
+
+ /**
+ * Gets the leap second associated with the clock's time.
+ *
+ * <p>The sign of the value is defined by the following equation:
+ * <pre>
+ * UtcTimeNanos = TimeNanos - (FullBiasNanos + BiasNanos) - LeapSecond * 1,000,000,000</pre>
+ *
+ * <p>The value is only available if {@link #hasLeapSecond()} is {@code true}.
+ */
+ public int getLeapSecond() {
+ return mLeapSecond;
+ }
+
+ /**
+ * Sets the leap second associated with the clock's time.
+ * @hide
+ */
+ @TestApi
+ public void setLeapSecond(int leapSecond) {
+ setFlag(HAS_LEAP_SECOND);
+ mLeapSecond = leapSecond;
+ }
+
+ /**
+ * Resets the leap second associated with the clock's time.
+ * @hide
+ */
+ @TestApi
+ public void resetLeapSecond() {
+ resetFlag(HAS_LEAP_SECOND);
+ mLeapSecond = Integer.MIN_VALUE;
+ }
+
+ /**
+ * Gets the GNSS receiver internal hardware clock value in nanoseconds.
+ *
+ * <p>This value is expected to be monotonically increasing while the hardware clock remains
+ * powered on. For the case of a hardware clock that is not continuously on, see the
+ * {@link #getHardwareClockDiscontinuityCount} field. The GPS time can be derived by subtracting
+ * the sum of {@link #getFullBiasNanos()} and {@link #getBiasNanos()} (when they are available)
+ * from this value. Sub-nanosecond accuracy can be provided by means of {@link #getBiasNanos()}.
+ *
+ * <p>The error estimate for this value (if applicable) is {@link #getTimeUncertaintyNanos()}.
+ */
+ public long getTimeNanos() {
+ return mTimeNanos;
+ }
+
+ /**
+ * Sets the GNSS receiver internal clock in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void setTimeNanos(long timeNanos) {
+ mTimeNanos = timeNanos;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getTimeUncertaintyNanos()} is available, {@code false}
+ * otherwise.
+ */
+ public boolean hasTimeUncertaintyNanos() {
+ return isFlagSet(HAS_TIME_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the clock's time Uncertainty (1-Sigma) in nanoseconds.
+ *
+ * <p>The uncertainty is represented as an absolute (single sided) value.
+ *
+ * <p>The value is only available if {@link #hasTimeUncertaintyNanos()} is {@code true}.
+ *
+ * <p>This value is often effectively zero (it is the reference clock by which all other times
+ * and time uncertainties are measured), and thus this field may often be 0, or not provided.
+ */
+ public double getTimeUncertaintyNanos() {
+ return mTimeUncertaintyNanos;
+ }
+
+ /**
+ * Sets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void setTimeUncertaintyNanos(double timeUncertaintyNanos) {
+ setFlag(HAS_TIME_UNCERTAINTY);
+ mTimeUncertaintyNanos = timeUncertaintyNanos;
+ }
+
+ /**
+ * Resets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void resetTimeUncertaintyNanos() {
+ resetFlag(HAS_TIME_UNCERTAINTY);
+ mTimeUncertaintyNanos = Double.NaN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getFullBiasNanos()} is available, {@code false} otherwise.
+ */
+ public boolean hasFullBiasNanos() {
+ return isFlagSet(HAS_FULL_BIAS);
+ }
+
+ /**
+ * Gets the difference between hardware clock ({@link #getTimeNanos()}) inside GPS receiver and
+ * the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
+ *
+ * <p>This value is available if the receiver has estimated GPS time. If the computed time is
+ * for a non-GPS constellation, the time offset of that constellation to GPS has to be applied
+ * to fill this value. The value is only available if {@link #hasFullBiasNanos()} is
+ * {@code true}.
+ *
+ * <p>The error estimate for the sum of this field and {@link #getBiasNanos} is
+ * {@link #getBiasUncertaintyNanos()}.
+ *
+ * <p>The sign of the value is defined by the following equation:
+ *
+ * <pre>
+ * local estimate of GPS time = TimeNanos - (FullBiasNanos + BiasNanos)</pre>
+ */
+ public long getFullBiasNanos() {
+ return mFullBiasNanos;
+ }
+
+ /**
+ * Sets the full bias in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void setFullBiasNanos(long value) {
+ setFlag(HAS_FULL_BIAS);
+ mFullBiasNanos = value;
+ }
+
+ /**
+ * Resets the full bias in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void resetFullBiasNanos() {
+ resetFlag(HAS_FULL_BIAS);
+ mFullBiasNanos = Long.MIN_VALUE;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getBiasNanos()} is available, {@code false} otherwise.
+ */
+ public boolean hasBiasNanos() {
+ return isFlagSet(HAS_BIAS);
+ }
+
+ /**
+ * Gets the clock's sub-nanosecond bias.
+ *
+ * <p>See the description of how this field is part of converting from hardware clock time, to
+ * GPS time, in {@link #getFullBiasNanos()}.
+ *
+ * <p>The error estimate for the sum of this field and {@link #getFullBiasNanos} is
+ * {@link #getBiasUncertaintyNanos()}.
+ *
+ * <p>The value is only available if {@link #hasBiasNanos()} is {@code true}.
+ */
+ public double getBiasNanos() {
+ return mBiasNanos;
+ }
+
+ /**
+ * Sets the sub-nanosecond bias.
+ * @hide
+ */
+ @TestApi
+ public void setBiasNanos(double biasNanos) {
+ setFlag(HAS_BIAS);
+ mBiasNanos = biasNanos;
+ }
+
+ /**
+ * Resets the clock's Bias in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void resetBiasNanos() {
+ resetFlag(HAS_BIAS);
+ mBiasNanos = Double.NaN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getBiasUncertaintyNanos()} is available, {@code false}
+ * otherwise.
+ */
+ public boolean hasBiasUncertaintyNanos() {
+ return isFlagSet(HAS_BIAS_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
+ *
+ * <p>See the description of how this field provides the error estimate in the conversion from
+ * hardware clock time, to GPS time, in {@link #getFullBiasNanos()}.
+ *
+ * <p>The value is only available if {@link #hasBiasUncertaintyNanos()} is {@code true}.
+ */
+ public double getBiasUncertaintyNanos() {
+ return mBiasUncertaintyNanos;
+ }
+
+ /**
+ * Sets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void setBiasUncertaintyNanos(double biasUncertaintyNanos) {
+ setFlag(HAS_BIAS_UNCERTAINTY);
+ mBiasUncertaintyNanos = biasUncertaintyNanos;
+ }
+
+ /**
+ * Resets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void resetBiasUncertaintyNanos() {
+ resetFlag(HAS_BIAS_UNCERTAINTY);
+ mBiasUncertaintyNanos = Double.NaN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getDriftNanosPerSecond()} is available, {@code false}
+ * otherwise.
+ */
+ public boolean hasDriftNanosPerSecond() {
+ return isFlagSet(HAS_DRIFT);
+ }
+
+ /**
+ * Gets the clock's Drift in nanoseconds per second.
+ *
+ * <p>A positive value indicates that the frequency is higher than the nominal (e.g. GPS master
+ * clock) frequency. The error estimate for this reported drift is
+ * {@link #getDriftUncertaintyNanosPerSecond()}.
+ *
+ * <p>The value is only available if {@link #hasDriftNanosPerSecond()} is {@code true}.
+ */
+ public double getDriftNanosPerSecond() {
+ return mDriftNanosPerSecond;
+ }
+
+ /**
+ * Sets the clock's Drift in nanoseconds per second.
+ * @hide
+ */
+ @TestApi
+ public void setDriftNanosPerSecond(double driftNanosPerSecond) {
+ setFlag(HAS_DRIFT);
+ mDriftNanosPerSecond = driftNanosPerSecond;
+ }
+
+ /**
+ * Resets the clock's Drift in nanoseconds per second.
+ * @hide
+ */
+ @TestApi
+ public void resetDriftNanosPerSecond() {
+ resetFlag(HAS_DRIFT);
+ mDriftNanosPerSecond = Double.NaN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getDriftUncertaintyNanosPerSecond()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasDriftUncertaintyNanosPerSecond() {
+ return isFlagSet(HAS_DRIFT_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
+ *
+ * <p>The value is only available if {@link #hasDriftUncertaintyNanosPerSecond()} is
+ * {@code true}.
+ */
+ public double getDriftUncertaintyNanosPerSecond() {
+ return mDriftUncertaintyNanosPerSecond;
+ }
+
+ /**
+ * Sets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
+ * @hide
+ */
+ @TestApi
+ public void setDriftUncertaintyNanosPerSecond(double driftUncertaintyNanosPerSecond) {
+ setFlag(HAS_DRIFT_UNCERTAINTY);
+ mDriftUncertaintyNanosPerSecond = driftUncertaintyNanosPerSecond;
+ }
+
+ /**
+ * Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
+ * @hide
+ */
+ @TestApi
+ public void resetDriftUncertaintyNanosPerSecond() {
+ resetFlag(HAS_DRIFT_UNCERTAINTY);
+ mDriftUncertaintyNanosPerSecond = Double.NaN;
+ }
+
+ /**
+ * Gets count of hardware clock discontinuities.
+ *
+ * <p>When this value stays the same, vs. a value in a previously reported {@link GnssClock}, it
+ * can be safely assumed that the {@code TimeNanos} value has been derived from a clock that has
+ * been running continuously - e.g. a single continuously powered crystal oscillator, and thus
+ * the {@code (FullBiasNanos + BiasNanos)} offset can be modelled with traditional clock bias
+ * &amp; drift models.
+ *
+ * <p>Each time this value changes, vs. the value in a previously reported {@link GnssClock},
+ * that suggests the hardware clock may have experienced a discontinuity (e.g. a power cycle or
+ * other anomaly), so that any assumptions about modelling a smoothly changing
+ * {@code (FullBiasNanos + BiasNanos)} offset, and a smoothly growing {@code (TimeNanos)}
+ * between this and the previously reported {@code GnssClock}, should be reset.
+ */
+ public int getHardwareClockDiscontinuityCount() {
+ return mHardwareClockDiscontinuityCount;
+ }
+
+ /**
+ * Sets count of last hardware clock discontinuity.
+ * @hide
+ */
+ @TestApi
+ public void setHardwareClockDiscontinuityCount(int value) {
+ mHardwareClockDiscontinuityCount = value;
+ }
+
+ public static final Creator<GnssClock> CREATOR = new Creator<GnssClock>() {
+ @Override
+ public GnssClock createFromParcel(Parcel parcel) {
+ GnssClock gpsClock = new GnssClock();
+
+ gpsClock.mFlags = parcel.readInt();
+ gpsClock.mLeapSecond = parcel.readInt();
+ gpsClock.mTimeNanos = parcel.readLong();
+ gpsClock.mTimeUncertaintyNanos = parcel.readDouble();
+ gpsClock.mFullBiasNanos = parcel.readLong();
+ gpsClock.mBiasNanos = parcel.readDouble();
+ gpsClock.mBiasUncertaintyNanos = parcel.readDouble();
+ gpsClock.mDriftNanosPerSecond = parcel.readDouble();
+ gpsClock.mDriftUncertaintyNanosPerSecond = parcel.readDouble();
+ gpsClock.mHardwareClockDiscontinuityCount = parcel.readInt();
+
+ return gpsClock;
+ }
+
+ @Override
+ public GnssClock[] newArray(int size) {
+ return new GnssClock[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mFlags);
+ parcel.writeInt(mLeapSecond);
+ parcel.writeLong(mTimeNanos);
+ parcel.writeDouble(mTimeUncertaintyNanos);
+ parcel.writeLong(mFullBiasNanos);
+ parcel.writeDouble(mBiasNanos);
+ parcel.writeDouble(mBiasUncertaintyNanos);
+ parcel.writeDouble(mDriftNanosPerSecond);
+ parcel.writeDouble(mDriftUncertaintyNanosPerSecond);
+ parcel.writeInt(mHardwareClockDiscontinuityCount);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ final String format = " %-15s = %s\n";
+ final String formatWithUncertainty = " %-15s = %-25s %-26s = %s\n";
+ StringBuilder builder = new StringBuilder("GnssClock:\n");
+
+ builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "TimeNanos",
+ mTimeNanos,
+ "TimeUncertaintyNanos",
+ hasTimeUncertaintyNanos() ? mTimeUncertaintyNanos : null));
+
+ builder.append(String.format(
+ format,
+ "FullBiasNanos",
+ hasFullBiasNanos() ? mFullBiasNanos : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "BiasNanos",
+ hasBiasNanos() ? mBiasNanos : null,
+ "BiasUncertaintyNanos",
+ hasBiasUncertaintyNanos() ? mBiasUncertaintyNanos : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "DriftNanosPerSecond",
+ hasDriftNanosPerSecond() ? mDriftNanosPerSecond : null,
+ "DriftUncertaintyNanosPerSecond",
+ hasDriftUncertaintyNanosPerSecond() ? mDriftUncertaintyNanosPerSecond : null));
+
+ builder.append(String.format(
+ format,
+ "HardwareClockDiscontinuityCount",
+ mHardwareClockDiscontinuityCount));
+
+ return builder.toString();
+ }
+
+ private void initialize() {
+ mFlags = HAS_NO_FLAGS;
+ resetLeapSecond();
+ setTimeNanos(Long.MIN_VALUE);
+ resetTimeUncertaintyNanos();
+ resetFullBiasNanos();
+ resetBiasNanos();
+ resetBiasUncertaintyNanos();
+ resetDriftNanosPerSecond();
+ resetDriftUncertaintyNanosPerSecond();
+ setHardwareClockDiscontinuityCount(Integer.MIN_VALUE);
+ }
+
+ private void setFlag(int flag) {
+ mFlags |= flag;
+ }
+
+ private void resetFlag(int flag) {
+ mFlags &= ~flag;
+ }
+
+ private boolean isFlagSet(int flag) {
+ return (mFlags & flag) == flag;
+ }
+}
diff --git a/android/location/GnssMeasurement.java b/android/location/GnssMeasurement.java
new file mode 100644
index 00000000..d24a4774
--- /dev/null
+++ b/android/location/GnssMeasurement.java
@@ -0,0 +1,1107 @@
+/*
+ * Copyright (C) 2014 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 android.annotation.TestApi;
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A class representing a GNSS satellite measurement, containing raw and computed information.
+ */
+public final class GnssMeasurement implements Parcelable {
+ private int mFlags;
+ private int mSvid;
+ private int mConstellationType;
+ private double mTimeOffsetNanos;
+ private int mState;
+ private long mReceivedSvTimeNanos;
+ private long mReceivedSvTimeUncertaintyNanos;
+ private double mCn0DbHz;
+ private double mPseudorangeRateMetersPerSecond;
+ private double mPseudorangeRateUncertaintyMetersPerSecond;
+ private int mAccumulatedDeltaRangeState;
+ private double mAccumulatedDeltaRangeMeters;
+ private double mAccumulatedDeltaRangeUncertaintyMeters;
+ private float mCarrierFrequencyHz;
+ private long mCarrierCycles;
+ private double mCarrierPhase;
+ private double mCarrierPhaseUncertainty;
+ private int mMultipathIndicator;
+ private double mSnrInDb;
+ private double mAutomaticGainControlLevelInDb;
+
+ // The following enumerations must be in sync with the values declared in gps.h
+
+ private static final int HAS_NO_FLAGS = 0;
+ private static final int HAS_SNR = (1<<0);
+ private static final int HAS_CARRIER_FREQUENCY = (1<<9);
+ private static final int HAS_CARRIER_CYCLES = (1<<10);
+ private static final int HAS_CARRIER_PHASE = (1<<11);
+ private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12);
+ private static final int HAS_AUTOMATIC_GAIN_CONTROL = (1<<13);
+
+ /**
+ * The status of the multipath indicator.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({MULTIPATH_INDICATOR_UNKNOWN, MULTIPATH_INDICATOR_DETECTED,
+ MULTIPATH_INDICATOR_NOT_DETECTED})
+ public @interface MultipathIndicator {}
+
+ /**
+ * The indicator is not available or the presence or absence of multipath is unknown.
+ */
+ public static final int MULTIPATH_INDICATOR_UNKNOWN = 0;
+
+ /**
+ * The measurement shows signs of multi-path.
+ */
+ public static final int MULTIPATH_INDICATOR_DETECTED = 1;
+
+ /**
+ * The measurement shows no signs of multi-path.
+ */
+ public static final int MULTIPATH_INDICATOR_NOT_DETECTED = 2;
+
+ /** This GNSS measurement's tracking state is invalid or unknown. */
+ public static final int STATE_UNKNOWN = 0;
+ /** This GNSS measurement's tracking state has code lock. */
+ public static final int STATE_CODE_LOCK = (1<<0);
+ /** This GNSS measurement's tracking state has bit sync. */
+ public static final int STATE_BIT_SYNC = (1<<1);
+ /** This GNSS measurement's tracking state has sub-frame sync. */
+ public static final int STATE_SUBFRAME_SYNC = (1<<2);
+ /** This GNSS measurement's tracking state has time-of-week decoded. */
+ public static final int STATE_TOW_DECODED = (1<<3);
+ /** This GNSS measurement's tracking state contains millisecond ambiguity. */
+ public static final int STATE_MSEC_AMBIGUOUS = (1<<4);
+ /** This GNSS measurement's tracking state has symbol sync. */
+ public static final int STATE_SYMBOL_SYNC = (1<<5);
+ /** This Glonass measurement's tracking state has string sync. */
+ public static final int STATE_GLO_STRING_SYNC = (1<<6);
+ /** This Glonass measurement's tracking state has time-of-day decoded. */
+ public static final int STATE_GLO_TOD_DECODED = (1<<7);
+ /** This Beidou measurement's tracking state has D2 bit sync. */
+ public static final int STATE_BDS_D2_BIT_SYNC = (1<<8);
+ /** This Beidou measurement's tracking state has D2 sub-frame sync. */
+ public static final int STATE_BDS_D2_SUBFRAME_SYNC = (1<<9);
+ /** This Galileo measurement's tracking state has E1B/C code lock. */
+ public static final int STATE_GAL_E1BC_CODE_LOCK = (1<<10);
+ /** This Galileo measurement's tracking state has E1C secondary code lock. */
+ public static final int STATE_GAL_E1C_2ND_CODE_LOCK = (1<<11);
+ /** This Galileo measurement's tracking state has E1B page sync. */
+ public static final int STATE_GAL_E1B_PAGE_SYNC = (1<<12);
+ /** This SBAS measurement's tracking state has whole second level sync. */
+ public static final int STATE_SBAS_SYNC = (1<<13);
+ /**
+ * This GNSS measurement's tracking state has time-of-week known, possibly not decoded
+ * over the air but has been determined from other sources. If TOW decoded is set then TOW Known
+ * will also be set.
+ */
+ public static final int STATE_TOW_KNOWN = (1<<14);
+ /**
+ * This Glonass measurement's tracking state has time-of-day known, possibly not decoded
+ * over the air but has been determined from other sources. If TOD decoded is set then TOD Known
+ * will also be set.
+ */
+ public static final int STATE_GLO_TOD_KNOWN = (1<<15);
+
+ /**
+ * All the GNSS receiver state flags, for bit masking purposes (not a sensible state for any
+ * individual measurement.)
+ */
+ private static final int STATE_ALL = 0x3fff; // 2 bits + 4 bits + 4 bits + 4 bits = 14 bits
+
+ /**
+ * The state of the 'Accumulated Delta Range' is invalid or unknown.
+ */
+ public static final int ADR_STATE_UNKNOWN = 0;
+
+ /**
+ * The state of the 'Accumulated Delta Range' is valid.
+ */
+ public static final int ADR_STATE_VALID = (1<<0);
+
+ /**
+ * The state of the 'Accumulated Delta Range' has detected a reset.
+ */
+ public static final int ADR_STATE_RESET = (1<<1);
+
+ /**
+ * The state of the 'Accumulated Delta Range' has a cycle slip detected.
+ */
+ public static final int ADR_STATE_CYCLE_SLIP = (1<<2);
+
+ /**
+ * All the 'Accumulated Delta Range' flags.
+ */
+ private static final int ADR_ALL = ADR_STATE_VALID | ADR_STATE_RESET | ADR_STATE_CYCLE_SLIP;
+
+ // End enumerations in sync with gps.h
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public GnssMeasurement() {
+ initialize();
+ }
+
+ /**
+ * Sets all contents to the values stored in the provided object.
+ * @hide
+ */
+ @TestApi
+ public void set(GnssMeasurement measurement) {
+ mFlags = measurement.mFlags;
+ mSvid = measurement.mSvid;
+ mConstellationType = measurement.mConstellationType;
+ mTimeOffsetNanos = measurement.mTimeOffsetNanos;
+ mState = measurement.mState;
+ mReceivedSvTimeNanos = measurement.mReceivedSvTimeNanos;
+ mReceivedSvTimeUncertaintyNanos = measurement.mReceivedSvTimeUncertaintyNanos;
+ mCn0DbHz = measurement.mCn0DbHz;
+ mPseudorangeRateMetersPerSecond = measurement.mPseudorangeRateMetersPerSecond;
+ mPseudorangeRateUncertaintyMetersPerSecond =
+ measurement.mPseudorangeRateUncertaintyMetersPerSecond;
+ mAccumulatedDeltaRangeState = measurement.mAccumulatedDeltaRangeState;
+ mAccumulatedDeltaRangeMeters = measurement.mAccumulatedDeltaRangeMeters;
+ mAccumulatedDeltaRangeUncertaintyMeters =
+ measurement.mAccumulatedDeltaRangeUncertaintyMeters;
+ mCarrierFrequencyHz = measurement.mCarrierFrequencyHz;
+ mCarrierCycles = measurement.mCarrierCycles;
+ mCarrierPhase = measurement.mCarrierPhase;
+ mCarrierPhaseUncertainty = measurement.mCarrierPhaseUncertainty;
+ mMultipathIndicator = measurement.mMultipathIndicator;
+ mSnrInDb = measurement.mSnrInDb;
+ mAutomaticGainControlLevelInDb = measurement.mAutomaticGainControlLevelInDb;
+ }
+
+ /**
+ * Resets all the contents to its original state.
+ * @hide
+ */
+ @TestApi
+ public void reset() {
+ initialize();
+ }
+
+ /**
+ * Gets the satellite ID.
+ *
+ * <p>Interpretation depends on {@link #getConstellationType()}.
+ * See {@link GnssStatus#getSvid(int)}.
+ */
+ public int getSvid() {
+ return mSvid;
+ }
+
+ /**
+ * Sets the Satellite ID.
+ * @hide
+ */
+ @TestApi
+ public void setSvid(int value) {
+ mSvid = value;
+ }
+
+ /**
+ * Gets the constellation type.
+ *
+ * <p>The return value is one of those constants with {@code CONSTELLATION_} prefix in
+ * {@link GnssStatus}.
+ */
+ @GnssStatus.ConstellationType
+ public int getConstellationType() {
+ return mConstellationType;
+ }
+
+ /**
+ * Sets the constellation type.
+ * @hide
+ */
+ @TestApi
+ public void setConstellationType(@GnssStatus.ConstellationType int value) {
+ mConstellationType = value;
+ }
+
+ /**
+ * Gets the time offset at which the measurement was taken in nanoseconds.
+ *
+ * <p>The reference receiver's time from which this is offset is specified by
+ * {@link GnssClock#getTimeNanos()}.
+ *
+ * <p>The sign of this value is given by the following equation:
+ * <pre>
+ * measurement time = TimeNanos + TimeOffsetNanos</pre>
+ *
+ * <p>The value provides an individual time-stamp for the measurement, and allows sub-nanosecond
+ * accuracy.
+ */
+ public double getTimeOffsetNanos() {
+ return mTimeOffsetNanos;
+ }
+
+ /**
+ * Sets the time offset at which the measurement was taken in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void setTimeOffsetNanos(double value) {
+ mTimeOffsetNanos = value;
+ }
+
+ /**
+ * Gets per-satellite sync state.
+ *
+ * <p>It represents the current sync state for the associated satellite.
+ *
+ * <p>This value helps interpret {@link #getReceivedSvTimeNanos()}.
+ */
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * Sets the sync state.
+ * @hide
+ */
+ @TestApi
+ public void setState(int value) {
+ mState = value;
+ }
+
+ /**
+ * Gets a string representation of the 'sync state'.
+ *
+ * <p>For internal and logging use only.
+ */
+ private String getStateString() {
+ if (mState == STATE_UNKNOWN) {
+ return "Unknown";
+ }
+
+ StringBuilder builder = new StringBuilder();
+ if ((mState & STATE_CODE_LOCK) != 0) {
+ builder.append("CodeLock|");
+ }
+ if ((mState & STATE_BIT_SYNC) != 0) {
+ builder.append("BitSync|");
+ }
+ if ((mState & STATE_SUBFRAME_SYNC) != 0) {
+ builder.append("SubframeSync|");
+ }
+ if ((mState & STATE_TOW_DECODED) != 0) {
+ builder.append("TowDecoded|");
+ }
+ if ((mState & STATE_TOW_KNOWN) != 0) {
+ builder.append("TowKnown|");
+ }
+ if ((mState & STATE_MSEC_AMBIGUOUS) != 0) {
+ builder.append("MsecAmbiguous|");
+ }
+ if ((mState & STATE_SYMBOL_SYNC) != 0) {
+ builder.append("SymbolSync|");
+ }
+ if ((mState & STATE_GLO_STRING_SYNC) != 0) {
+ builder.append("GloStringSync|");
+ }
+ if ((mState & STATE_GLO_TOD_DECODED) != 0) {
+ builder.append("GloTodDecoded|");
+ }
+ if ((mState & STATE_GLO_TOD_KNOWN) != 0) {
+ builder.append("GloTodKnown|");
+ }
+ if ((mState & STATE_BDS_D2_BIT_SYNC) != 0) {
+ builder.append("BdsD2BitSync|");
+ }
+ if ((mState & STATE_BDS_D2_SUBFRAME_SYNC) != 0) {
+ builder.append("BdsD2SubframeSync|");
+ }
+ if ((mState & STATE_GAL_E1BC_CODE_LOCK) != 0) {
+ builder.append("GalE1bcCodeLock|");
+ }
+ if ((mState & STATE_GAL_E1C_2ND_CODE_LOCK) != 0) {
+ builder.append("E1c2ndCodeLock|");
+ }
+ if ((mState & STATE_GAL_E1B_PAGE_SYNC) != 0) {
+ builder.append("GalE1bPageSync|");
+ }
+ if ((mState & STATE_SBAS_SYNC) != 0) {
+ builder.append("SbasSync|");
+ }
+
+ int remainingStates = mState & ~STATE_ALL;
+ if (remainingStates > 0) {
+ builder.append("Other(");
+ builder.append(Integer.toBinaryString(remainingStates));
+ builder.append(")|");
+ }
+ builder.setLength(builder.length() - 1);
+ return builder.toString();
+ }
+
+ /**
+ * Gets the received GNSS satellite time, at the measurement time, in nanoseconds.
+ *
+ * <p>For GPS &amp; QZSS, this is:
+ * <ul>
+ * <li>Received GPS Time-of-Week at the measurement time, in nanoseconds.</li>
+ * <li>The value is relative to the beginning of the current GPS week.</li>
+ * </ul>
+ *
+ * <p>Given the highest sync state that can be achieved, per each satellite, valid range
+ * for this field can be:
+ * <pre>
+ * Searching : [ 0 ] : STATE_UNKNOWN
+ * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set
+ * Bit sync : [ 0 20ms ] : STATE_BIT_SYNC is set
+ * Subframe sync : [ 0 6s ] : STATE_SUBFRAME_SYNC is set
+ * TOW decoded : [ 0 1week ] : STATE_TOW_DECODED is set
+ * TOW Known : [ 0 1week ] : STATE_TOW_KNOWN set</pre>
+ *
+ * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
+ * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
+ *
+ * <p>Note well: if there is any ambiguity in integer millisecond, {@code STATE_MSEC_AMBIGUOUS}
+ * must be set accordingly, in the 'state' field.
+ *
+ * <p>This value must be populated if 'state' != {@code STATE_UNKNOWN}.
+ *
+ * <p>For Glonass, this is:
+ * <ul>
+ * <li>Received Glonass time of day, at the measurement time in nanoseconds.</li>
+ * </ul>
+ *
+ * <p>Given the highest sync state that can be achieved, per each satellite, valid range for
+ * this field can be:
+ * <pre>
+ * Searching : [ 0 ] : STATE_UNKNOWN
+ * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set
+ * Symbol sync : [ 0 10ms ] : STATE_SYMBOL_SYNC is set
+ * Bit sync : [ 0 20ms ] : STATE_BIT_SYNC is set
+ * String sync : [ 0 2s ] : STATE_GLO_STRING_SYNC is set
+ * Time of day decoded : [ 0 1day ] : STATE_GLO_TOD_DECODED is set
+ * Time of day known : [ 0 1day ] : STATE_GLO_TOD_KNOWN set</pre>
+ *
+ * Note: Time of day known refers to the case where it is possibly not decoded over the air but
+ * has been determined from other sources. If Time of day decoded is set then Time of day known
+ * must also be set.
+ *
+ * <p>For Beidou, this is:
+ * <ul>
+ * <li>Received Beidou time of week, at the measurement time in nanoseconds.</li>
+ * </ul>
+ *
+ * <p>Given the highest sync state that can be achieved, per each satellite, valid range for
+ * this field can be:
+ * <pre>
+ * Searching : [ 0 ] : STATE_UNKNOWN
+ * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set
+ * Bit sync (D2) : [ 0 2ms ] : STATE_BDS_D2_BIT_SYNC is set
+ * Bit sync (D1) : [ 0 20ms ] : STATE_BIT_SYNC is set
+ * Subframe (D2) : [ 0 0.6s ] : STATE_BDS_D2_SUBFRAME_SYNC is set
+ * Subframe (D1) : [ 0 6s ] : STATE_SUBFRAME_SYNC is set
+ * Time of week decoded : [ 0 1week ] : STATE_TOW_DECODED is set
+ * Time of week known : [ 0 1week ] : STATE_TOW_KNOWN set</pre>
+ *
+ * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
+ * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
+ *
+ * <p>For Galileo, this is:
+ * <ul>
+ * <li>Received Galileo time of week, at the measurement time in nanoseconds.</li>
+ * </ul>
+ * <pre>
+ * E1BC code lock : [ 0 4ms ] : STATE_GAL_E1BC_CODE_LOCK is set
+ * E1C 2nd code lock : [ 0 100ms ] : STATE_GAL_E1C_2ND_CODE_LOCK is set
+ * E1B page : [ 0 2s ] : STATE_GAL_E1B_PAGE_SYNC is set
+ * Time of week decoded : [ 0 1week ] : STATE_TOW_DECODED is set
+ * Time of week known : [ 0 1week ] : STATE_TOW_KNOWN set</pre>
+ *
+ * Note: TOW Known refers to the case where TOW is possibly not decoded over the air but has
+ * been determined from other sources. If TOW decoded is set then TOW Known must also be set.
+ *
+ * <p>For SBAS, this is:
+ * <ul>
+ * <li>Received SBAS time, at the measurement time in nanoseconds.</li>
+ * </ul>
+ *
+ * <p>Given the highest sync state that can be achieved, per each satellite, valid range for
+ * this field can be:
+ * <pre>
+ * Searching : [ 0 ] : STATE_UNKNOWN
+ * C/A code lock : [ 0 1ms ] : STATE_CODE_LOCK is set
+ * Symbol sync : [ 0 2ms ] : STATE_SYMBOL_SYNC is set
+ * Message : [ 0 1s ] : STATE_SBAS_SYNC is set</pre>
+ */
+ public long getReceivedSvTimeNanos() {
+ return mReceivedSvTimeNanos;
+ }
+
+ /**
+ * Sets the received GNSS time in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void setReceivedSvTimeNanos(long value) {
+ mReceivedSvTimeNanos = value;
+ }
+
+ /**
+ * Gets the error estimate (1-sigma) for the received GNSS time, in nanoseconds.
+ */
+ public long getReceivedSvTimeUncertaintyNanos() {
+ return mReceivedSvTimeUncertaintyNanos;
+ }
+
+ /**
+ * Sets the received GNSS time uncertainty (1-Sigma) in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void setReceivedSvTimeUncertaintyNanos(long value) {
+ mReceivedSvTimeUncertaintyNanos = value;
+ }
+
+ /**
+ * Gets the Carrier-to-noise density in dB-Hz.
+ *
+ * <p>Typical range: 10-50 db-Hz.
+ *
+ * <p>The value contains the measured C/N0 for the signal at the antenna input.
+ */
+ public double getCn0DbHz() {
+ return mCn0DbHz;
+ }
+
+ /**
+ * Sets the carrier-to-noise density in dB-Hz.
+ * @hide
+ */
+ @TestApi
+ public void setCn0DbHz(double value) {
+ mCn0DbHz = value;
+ }
+
+ /**
+ * Gets the Pseudorange rate at the timestamp in m/s.
+ *
+ * <p>The error estimate for this value is
+ * {@link #getPseudorangeRateUncertaintyMetersPerSecond()}.
+ *
+ * <p>The value is uncorrected, i.e. corrections for receiver and satellite clock frequency
+ * errors are not included.
+ *
+ * <p>A positive 'uncorrected' value indicates that the SV is moving away from the receiver. The
+ * sign of the 'uncorrected' 'pseudorange rate' and its relation to the sign of 'doppler shift'
+ * is given by the equation:
+ *
+ * <pre>
+ * pseudorange rate = -k * doppler shift (where k is a constant)</pre>
+ */
+ public double getPseudorangeRateMetersPerSecond() {
+ return mPseudorangeRateMetersPerSecond;
+ }
+
+ /**
+ * Sets the pseudorange rate at the timestamp in m/s.
+ * @hide
+ */
+ @TestApi
+ public void setPseudorangeRateMetersPerSecond(double value) {
+ mPseudorangeRateMetersPerSecond = value;
+ }
+
+ /**
+ * Gets the pseudorange's rate uncertainty (1-Sigma) in m/s.
+ *
+ * <p>The uncertainty is represented as an absolute (single sided) value.
+ */
+ public double getPseudorangeRateUncertaintyMetersPerSecond() {
+ return mPseudorangeRateUncertaintyMetersPerSecond;
+ }
+
+ /**
+ * Sets the pseudorange's rate uncertainty (1-Sigma) in m/s.
+ * @hide
+ */
+ @TestApi
+ public void setPseudorangeRateUncertaintyMetersPerSecond(double value) {
+ mPseudorangeRateUncertaintyMetersPerSecond = value;
+ }
+
+ /**
+ * Gets 'Accumulated Delta Range' state.
+ *
+ * <p>It indicates whether {@link #getAccumulatedDeltaRangeMeters()} is reset or there is a
+ * cycle slip (indicating 'loss of lock').
+ */
+ public int getAccumulatedDeltaRangeState() {
+ return mAccumulatedDeltaRangeState;
+ }
+
+ /**
+ * Sets the 'Accumulated Delta Range' state.
+ * @hide
+ */
+ @TestApi
+ public void setAccumulatedDeltaRangeState(int value) {
+ mAccumulatedDeltaRangeState = value;
+ }
+
+ /**
+ * Gets a string representation of the 'Accumulated Delta Range state'.
+ *
+ * <p>For internal and logging use only.
+ */
+ private String getAccumulatedDeltaRangeStateString() {
+ if (mAccumulatedDeltaRangeState == ADR_STATE_UNKNOWN) {
+ return "Unknown";
+ }
+ StringBuilder builder = new StringBuilder();
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_VALID) == ADR_STATE_VALID) {
+ builder.append("Valid|");
+ }
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_RESET) == ADR_STATE_RESET) {
+ builder.append("Reset|");
+ }
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_CYCLE_SLIP) == ADR_STATE_CYCLE_SLIP) {
+ builder.append("CycleSlip|");
+ }
+ int remainingStates = mAccumulatedDeltaRangeState & ~ADR_ALL;
+ if (remainingStates > 0) {
+ builder.append("Other(");
+ builder.append(Integer.toBinaryString(remainingStates));
+ builder.append(")|");
+ }
+ builder.deleteCharAt(builder.length() - 1);
+ return builder.toString();
+ }
+
+ /**
+ * Gets the accumulated delta range since the last channel reset, in meters.
+ *
+ * <p>The error estimate for this value is {@link #getAccumulatedDeltaRangeUncertaintyMeters()}.
+ *
+ * <p>The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+ *
+ * <p>A positive value indicates that the SV is moving away from the receiver.
+ * The sign of {@link #getAccumulatedDeltaRangeMeters()} and its relation to the sign of
+ * {@link #getCarrierPhase()} is given by the equation:
+ *
+ * <pre>
+ * accumulated delta range = -k * carrier phase (where k is a constant)</pre>
+ */
+ public double getAccumulatedDeltaRangeMeters() {
+ return mAccumulatedDeltaRangeMeters;
+ }
+
+ /**
+ * Sets the accumulated delta range in meters.
+ * @hide
+ */
+ @TestApi
+ public void setAccumulatedDeltaRangeMeters(double value) {
+ mAccumulatedDeltaRangeMeters = value;
+ }
+
+ /**
+ * Gets the accumulated delta range's uncertainty (1-Sigma) in meters.
+ *
+ * <p>The uncertainty is represented as an absolute (single sided) value.
+ *
+ * <p>The status of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+ */
+ public double getAccumulatedDeltaRangeUncertaintyMeters() {
+ return mAccumulatedDeltaRangeUncertaintyMeters;
+ }
+
+ /**
+ * Sets the accumulated delta range's uncertainty (1-sigma) in meters.
+ *
+ * <p>The status of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setAccumulatedDeltaRangeUncertaintyMeters(double value) {
+ mAccumulatedDeltaRangeUncertaintyMeters = value;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getCarrierFrequencyHz()} is available, {@code false}
+ * otherwise.
+ */
+ public boolean hasCarrierFrequencyHz() {
+ return isFlagSet(HAS_CARRIER_FREQUENCY);
+ }
+
+ /**
+ * Gets the carrier frequency of the tracked signal.
+ *
+ * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz,
+ * L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
+ * common use central frequency, e.g. L1 = 1575.45 MHz for GPS.
+ *
+ * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two raw
+ * measurement objects will be reported for this same satellite, in one of the measurement
+ * objects, all the values related to L1 will be filled, and in the other all of the values
+ * related to L5 will be filled.
+ *
+ * <p>The value is only available if {@link #hasCarrierFrequencyHz()} is {@code true}.
+ *
+ * @return the carrier frequency of the signal tracked in Hz.
+ */
+ public float getCarrierFrequencyHz() {
+ return mCarrierFrequencyHz;
+ }
+
+ /**
+ * Sets the Carrier frequency in Hz.
+ * @hide
+ */
+ @TestApi
+ public void setCarrierFrequencyHz(float carrierFrequencyHz) {
+ setFlag(HAS_CARRIER_FREQUENCY);
+ mCarrierFrequencyHz = carrierFrequencyHz;
+ }
+
+ /**
+ * Resets the Carrier frequency in Hz.
+ * @hide
+ */
+ @TestApi
+ public void resetCarrierFrequencyHz() {
+ resetFlag(HAS_CARRIER_FREQUENCY);
+ mCarrierFrequencyHz = Float.NaN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getCarrierCycles()} is available, {@code false} otherwise.
+ */
+ public boolean hasCarrierCycles() {
+ return isFlagSet(HAS_CARRIER_CYCLES);
+ }
+
+ /**
+ * The number of full carrier cycles between the satellite and the receiver.
+ *
+ * <p>The reference frequency is given by the value of {@link #getCarrierFrequencyHz()}.
+ *
+ * <p>The value is only available if {@link #hasCarrierCycles()} is {@code true}.
+ */
+ public long getCarrierCycles() {
+ return mCarrierCycles;
+ }
+
+ /**
+ * Sets the number of full carrier cycles between the satellite and the receiver.
+ * @hide
+ */
+ @TestApi
+ public void setCarrierCycles(long value) {
+ setFlag(HAS_CARRIER_CYCLES);
+ mCarrierCycles = value;
+ }
+
+ /**
+ * Resets the number of full carrier cycles between the satellite and the receiver.
+ * @hide
+ */
+ @TestApi
+ public void resetCarrierCycles() {
+ resetFlag(HAS_CARRIER_CYCLES);
+ mCarrierCycles = Long.MIN_VALUE;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getCarrierPhase()} is available, {@code false} otherwise.
+ */
+ public boolean hasCarrierPhase() {
+ return isFlagSet(HAS_CARRIER_PHASE);
+ }
+
+ /**
+ * Gets the RF phase detected by the receiver.
+ *
+ * <p>Range: [0.0, 1.0].
+ *
+ * <p>This is the fractional part of the complete carrier phase measurement.
+ *
+ * <p>The reference frequency is given by the value of {@link #getCarrierFrequencyHz()}.
+ *
+ * <p>The error estimate for this value is {@link #getCarrierPhaseUncertainty()}.
+ *
+ * <p>The value is only available if {@link #hasCarrierPhase()} is {@code true}.
+ */
+ public double getCarrierPhase() {
+ return mCarrierPhase;
+ }
+
+ /**
+ * Sets the RF phase detected by the receiver.
+ * @hide
+ */
+ @TestApi
+ public void setCarrierPhase(double value) {
+ setFlag(HAS_CARRIER_PHASE);
+ mCarrierPhase = value;
+ }
+
+ /**
+ * Resets the RF phase detected by the receiver.
+ * @hide
+ */
+ @TestApi
+ public void resetCarrierPhase() {
+ resetFlag(HAS_CARRIER_PHASE);
+ mCarrierPhase = Double.NaN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getCarrierPhaseUncertainty()} is available, {@code false}
+ * otherwise.
+ */
+ public boolean hasCarrierPhaseUncertainty() {
+ return isFlagSet(HAS_CARRIER_PHASE_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the carrier-phase's uncertainty (1-Sigma).
+ *
+ * <p>The uncertainty is represented as an absolute (single sided) value.
+ *
+ * <p>The value is only available if {@link #hasCarrierPhaseUncertainty()} is {@code true}.
+ */
+ public double getCarrierPhaseUncertainty() {
+ return mCarrierPhaseUncertainty;
+ }
+
+ /**
+ * Sets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ * @hide
+ */
+ @TestApi
+ public void setCarrierPhaseUncertainty(double value) {
+ setFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
+ mCarrierPhaseUncertainty = value;
+ }
+
+ /**
+ * Resets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ * @hide
+ */
+ @TestApi
+ public void resetCarrierPhaseUncertainty() {
+ resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
+ mCarrierPhaseUncertainty = Double.NaN;
+ }
+
+ /**
+ * Gets a value indicating the 'multipath' state of the event.
+ */
+ @MultipathIndicator
+ public int getMultipathIndicator() {
+ return mMultipathIndicator;
+ }
+
+ /**
+ * Sets the 'multi-path' indicator.
+ * @hide
+ */
+ @TestApi
+ public void setMultipathIndicator(@MultipathIndicator int value) {
+ mMultipathIndicator = value;
+ }
+
+ /**
+ * Gets a string representation of the 'multi-path indicator'.
+ *
+ * <p>For internal and logging use only.
+ */
+ private String getMultipathIndicatorString() {
+ switch(mMultipathIndicator) {
+ case MULTIPATH_INDICATOR_UNKNOWN:
+ return "Unknown";
+ case MULTIPATH_INDICATOR_DETECTED:
+ return "Detected";
+ case MULTIPATH_INDICATOR_NOT_DETECTED:
+ return "NotDetected";
+ default:
+ return "<Invalid:" + mMultipathIndicator + ">";
+ }
+ }
+
+ /**
+ * Returns {@code true} if {@link #getSnrInDb()} is available, {@code false} otherwise.
+ */
+ public boolean hasSnrInDb() {
+ return isFlagSet(HAS_SNR);
+ }
+
+ /**
+ * Gets the Signal-to-Noise ratio (SNR) in dB.
+ *
+ * <p>The value is only available if {@link #hasSnrInDb()} is {@code true}.
+ */
+ public double getSnrInDb() {
+ return mSnrInDb;
+ }
+
+ /**
+ * Sets the Signal-to-noise ratio (SNR) in dB.
+ * @hide
+ */
+ @TestApi
+ public void setSnrInDb(double snrInDb) {
+ setFlag(HAS_SNR);
+ mSnrInDb = snrInDb;
+ }
+
+ /**
+ * Resets the Signal-to-noise ratio (SNR) in dB.
+ * @hide
+ */
+ @TestApi
+ public void resetSnrInDb() {
+ resetFlag(HAS_SNR);
+ mSnrInDb = Double.NaN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getAutomaticGainControlLevelDb()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasAutomaticGainControlLevelDb() {
+ return isFlagSet(HAS_AUTOMATIC_GAIN_CONTROL);
+ }
+
+ /**
+ * Gets the Automatic Gain Control level in dB.
+ *
+ * <p> AGC acts as a variable gain amplifier adjusting the power of the incoming signal. The AGC
+ * level may be used to indicate potential interference. When AGC is at a nominal level, this
+ * value must be set as 0. Higher gain (and/or lower input power) shall be output as a positive
+ * number. Hence in cases of strong jamming, in the band of this signal, this value will go more
+ * negative.
+ *
+ * <p>Note: Different hardware designs (e.g. antenna, pre-amplification, or other RF HW
+ * components) may also affect the typical output of of this value on any given hardware design
+ * in an open sky test - the important aspect of this output is that changes in this value are
+ * indicative of changes on input signal power in the frequency band for this measurement.
+ * <p>The value is only available if {@link #hasAutomaticGainControlLevelDb()} is {@code true}
+ */
+ public double getAutomaticGainControlLevelDb() {
+ return mAutomaticGainControlLevelInDb;
+ }
+
+ /**
+ * Sets the Automatic Gain Control level in dB.
+ * @hide
+ */
+ @TestApi
+ public void setAutomaticGainControlLevelInDb(double agcLevelDb) {
+ setFlag(HAS_AUTOMATIC_GAIN_CONTROL);
+ mAutomaticGainControlLevelInDb = agcLevelDb;
+ }
+
+ /**
+ * Resets the Automatic Gain Control level.
+ * @hide
+ */
+ @TestApi
+ public void resetAutomaticGainControlLevel() {
+ resetFlag(HAS_AUTOMATIC_GAIN_CONTROL);
+ mAutomaticGainControlLevelInDb = Double.NaN;
+ }
+
+ public static final Creator<GnssMeasurement> CREATOR = new Creator<GnssMeasurement>() {
+ @Override
+ public GnssMeasurement createFromParcel(Parcel parcel) {
+ GnssMeasurement gnssMeasurement = new GnssMeasurement();
+
+ gnssMeasurement.mFlags = parcel.readInt();
+ gnssMeasurement.mSvid = parcel.readInt();
+ gnssMeasurement.mConstellationType = parcel.readInt();
+ gnssMeasurement.mTimeOffsetNanos = parcel.readDouble();
+ gnssMeasurement.mState = parcel.readInt();
+ gnssMeasurement.mReceivedSvTimeNanos = parcel.readLong();
+ gnssMeasurement.mReceivedSvTimeUncertaintyNanos = parcel.readLong();
+ gnssMeasurement.mCn0DbHz = parcel.readDouble();
+ gnssMeasurement.mPseudorangeRateMetersPerSecond = parcel.readDouble();
+ gnssMeasurement.mPseudorangeRateUncertaintyMetersPerSecond = parcel.readDouble();
+ gnssMeasurement.mAccumulatedDeltaRangeState = parcel.readInt();
+ gnssMeasurement.mAccumulatedDeltaRangeMeters = parcel.readDouble();
+ gnssMeasurement.mAccumulatedDeltaRangeUncertaintyMeters = parcel.readDouble();
+ gnssMeasurement.mCarrierFrequencyHz = parcel.readFloat();
+ gnssMeasurement.mCarrierCycles = parcel.readLong();
+ gnssMeasurement.mCarrierPhase = parcel.readDouble();
+ gnssMeasurement.mCarrierPhaseUncertainty = parcel.readDouble();
+ gnssMeasurement.mMultipathIndicator = parcel.readInt();
+ gnssMeasurement.mSnrInDb = parcel.readDouble();
+ gnssMeasurement.mAutomaticGainControlLevelInDb = parcel.readDouble();
+
+ return gnssMeasurement;
+ }
+
+ @Override
+ public GnssMeasurement[] newArray(int i) {
+ return new GnssMeasurement[i];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mFlags);
+ parcel.writeInt(mSvid);
+ parcel.writeInt(mConstellationType);
+ parcel.writeDouble(mTimeOffsetNanos);
+ parcel.writeInt(mState);
+ parcel.writeLong(mReceivedSvTimeNanos);
+ parcel.writeLong(mReceivedSvTimeUncertaintyNanos);
+ parcel.writeDouble(mCn0DbHz);
+ parcel.writeDouble(mPseudorangeRateMetersPerSecond);
+ parcel.writeDouble(mPseudorangeRateUncertaintyMetersPerSecond);
+ parcel.writeInt(mAccumulatedDeltaRangeState);
+ parcel.writeDouble(mAccumulatedDeltaRangeMeters);
+ parcel.writeDouble(mAccumulatedDeltaRangeUncertaintyMeters);
+ parcel.writeFloat(mCarrierFrequencyHz);
+ parcel.writeLong(mCarrierCycles);
+ parcel.writeDouble(mCarrierPhase);
+ parcel.writeDouble(mCarrierPhaseUncertainty);
+ parcel.writeInt(mMultipathIndicator);
+ parcel.writeDouble(mSnrInDb);
+ parcel.writeDouble(mAutomaticGainControlLevelInDb);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ final String format = " %-29s = %s\n";
+ final String formatWithUncertainty = " %-29s = %-25s %-40s = %s\n";
+ StringBuilder builder = new StringBuilder("GnssMeasurement:\n");
+
+ builder.append(String.format(format, "Svid", mSvid));
+ builder.append(String.format(format, "ConstellationType", mConstellationType));
+ builder.append(String.format(format, "TimeOffsetNanos", mTimeOffsetNanos));
+
+ builder.append(String.format(format, "State", getStateString()));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "ReceivedSvTimeNanos",
+ mReceivedSvTimeNanos,
+ "ReceivedSvTimeUncertaintyNanos",
+ mReceivedSvTimeUncertaintyNanos));
+
+ builder.append(String.format(format, "Cn0DbHz", mCn0DbHz));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "PseudorangeRateMetersPerSecond",
+ mPseudorangeRateMetersPerSecond,
+ "PseudorangeRateUncertaintyMetersPerSecond",
+ mPseudorangeRateUncertaintyMetersPerSecond));
+
+ builder.append(String.format(
+ format,
+ "AccumulatedDeltaRangeState",
+ getAccumulatedDeltaRangeStateString()));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "AccumulatedDeltaRangeMeters",
+ mAccumulatedDeltaRangeMeters,
+ "AccumulatedDeltaRangeUncertaintyMeters",
+ mAccumulatedDeltaRangeUncertaintyMeters));
+
+ builder.append(String.format(
+ format,
+ "CarrierFrequencyHz",
+ hasCarrierFrequencyHz() ? mCarrierFrequencyHz : null));
+
+ builder.append(String.format(
+ format,
+ "CarrierCycles",
+ hasCarrierCycles() ? mCarrierCycles : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "CarrierPhase",
+ hasCarrierPhase() ? mCarrierPhase : null,
+ "CarrierPhaseUncertainty",
+ hasCarrierPhaseUncertainty() ? mCarrierPhaseUncertainty : null));
+
+ builder.append(String.format(format, "MultipathIndicator", getMultipathIndicatorString()));
+
+ builder.append(String.format(
+ format,
+ "SnrInDb",
+ hasSnrInDb() ? mSnrInDb : null));
+ builder.append(String.format(
+ format,
+ "AgcLevelDb",
+ hasAutomaticGainControlLevelDb() ? mAutomaticGainControlLevelInDb : null));
+
+ return builder.toString();
+ }
+
+ private void initialize() {
+ mFlags = HAS_NO_FLAGS;
+ setSvid(0);
+ setTimeOffsetNanos(Long.MIN_VALUE);
+ setState(STATE_UNKNOWN);
+ setReceivedSvTimeNanos(Long.MIN_VALUE);
+ setReceivedSvTimeUncertaintyNanos(Long.MAX_VALUE);
+ setCn0DbHz(Double.MIN_VALUE);
+ setPseudorangeRateMetersPerSecond(Double.MIN_VALUE);
+ setPseudorangeRateUncertaintyMetersPerSecond(Double.MIN_VALUE);
+ setAccumulatedDeltaRangeState(ADR_STATE_UNKNOWN);
+ setAccumulatedDeltaRangeMeters(Double.MIN_VALUE);
+ setAccumulatedDeltaRangeUncertaintyMeters(Double.MIN_VALUE);
+ resetCarrierFrequencyHz();
+ resetCarrierCycles();
+ resetCarrierPhase();
+ resetCarrierPhaseUncertainty();
+ setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
+ resetSnrInDb();
+ resetAutomaticGainControlLevel();
+ }
+
+ private void setFlag(int flag) {
+ mFlags |= flag;
+ }
+
+ private void resetFlag(int flag) {
+ mFlags &= ~flag;
+ }
+
+ private boolean isFlagSet(int flag) {
+ return (mFlags & flag) == flag;
+ }
+}
diff --git a/android/location/GnssMeasurementCallbackTransport.java b/android/location/GnssMeasurementCallbackTransport.java
new file mode 100644
index 00000000..21f63068
--- /dev/null
+++ b/android/location/GnssMeasurementCallbackTransport.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 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 android.content.Context;
+import android.os.RemoteException;
+
+/**
+ * A handler class to manage transport callbacks for {@link GnssMeasurementsEvent.Callback}.
+ *
+ * @hide
+ */
+class GnssMeasurementCallbackTransport
+ extends LocalListenerHelper<GnssMeasurementsEvent.Callback> {
+ private final ILocationManager mLocationManager;
+
+ private final IGnssMeasurementsListener mListenerTransport = new ListenerTransport();
+
+ public GnssMeasurementCallbackTransport(Context context, ILocationManager locationManager) {
+ super(context, "GnssMeasurementListenerTransport");
+ mLocationManager = locationManager;
+ }
+
+ @Override
+ protected boolean registerWithServer() throws RemoteException {
+ return mLocationManager.addGnssMeasurementsListener(
+ mListenerTransport,
+ getContext().getPackageName());
+ }
+
+ @Override
+ protected void unregisterFromServer() throws RemoteException {
+ mLocationManager.removeGnssMeasurementsListener(mListenerTransport);
+ }
+
+ private class ListenerTransport extends IGnssMeasurementsListener.Stub {
+ @Override
+ public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
+ ListenerOperation<GnssMeasurementsEvent.Callback> operation =
+ new ListenerOperation<GnssMeasurementsEvent.Callback>() {
+ @Override
+ public void execute(GnssMeasurementsEvent.Callback callback)
+ throws RemoteException {
+ callback.onGnssMeasurementsReceived(event);
+ }
+ };
+ foreach(operation);
+ }
+
+ @Override
+ public void onStatusChanged(final int status) {
+ ListenerOperation<GnssMeasurementsEvent.Callback> operation =
+ new ListenerOperation<GnssMeasurementsEvent.Callback>() {
+ @Override
+ public void execute(GnssMeasurementsEvent.Callback callback)
+ throws RemoteException {
+ callback.onStatusChanged(status);
+ }
+ };
+ foreach(operation);
+ }
+ }
+}
diff --git a/android/location/GnssMeasurementsEvent.java b/android/location/GnssMeasurementsEvent.java
new file mode 100644
index 00000000..d66fd9c4
--- /dev/null
+++ b/android/location/GnssMeasurementsEvent.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2014 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 android.annotation.TestApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * A class implementing a container for data associated with a measurement event.
+ * Events are delivered to registered instances of {@link Callback}.
+ */
+public final class GnssMeasurementsEvent implements Parcelable {
+ private final GnssClock mClock;
+ private final Collection<GnssMeasurement> mReadOnlyMeasurements;
+
+ /**
+ * Used for receiving GNSS satellite measurements from the GNSS engine.
+ * Each measurement contains raw and computed data identifying a satellite.
+ * You can implement this interface and call
+ * {@link LocationManager#registerGnssMeasurementsCallback}.
+ */
+ public static abstract class Callback {
+ /**
+ * The status of the GNSS measurements event.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_LOCATION_DISABLED})
+ public @interface GnssMeasurementsStatus {}
+
+ /**
+ * The system does not support tracking of GNSS Measurements.
+ *
+ * <p>This status will not change in the future.
+ */
+ public static final int STATUS_NOT_SUPPORTED = 0;
+
+ /**
+ * GNSS Measurements are successfully being tracked, it will receive updates once they are
+ * available.
+ */
+ public static final int STATUS_READY = 1;
+
+ /**
+ * GPS provider or Location is disabled, updates will not be received until they are
+ * enabled.
+ */
+ public static final int STATUS_LOCATION_DISABLED = 2;
+
+ /**
+ * Reports the latest collected GNSS Measurements.
+ */
+ public void onGnssMeasurementsReceived(GnssMeasurementsEvent eventArgs) {}
+
+ /**
+ * Reports the latest status of the GNSS Measurements sub-system.
+ */
+ public void onStatusChanged(@GnssMeasurementsStatus int status) {}
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public GnssMeasurementsEvent(GnssClock clock, GnssMeasurement[] measurements) {
+ if (clock == null) {
+ throw new InvalidParameterException("Parameter 'clock' must not be null.");
+ }
+ if (measurements == null || measurements.length == 0) {
+ mReadOnlyMeasurements = Collections.emptyList();
+ } else {
+ Collection<GnssMeasurement> measurementCollection = Arrays.asList(measurements);
+ mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
+ }
+
+ mClock = clock;
+ }
+
+ /**
+ * Gets the GNSS receiver clock information associated with the measurements for the current
+ * event.
+ */
+ @NonNull
+ public GnssClock getClock() {
+ return mClock;
+ }
+
+ /**
+ * Gets a read-only collection of measurements associated with the current event.
+ */
+ @NonNull
+ public Collection<GnssMeasurement> getMeasurements() {
+ return mReadOnlyMeasurements;
+ }
+
+ public static final Creator<GnssMeasurementsEvent> CREATOR =
+ new Creator<GnssMeasurementsEvent>() {
+ @Override
+ public GnssMeasurementsEvent createFromParcel(Parcel in) {
+ ClassLoader classLoader = getClass().getClassLoader();
+
+ GnssClock clock = in.readParcelable(classLoader);
+
+ int measurementsLength = in.readInt();
+ GnssMeasurement[] measurementsArray = new GnssMeasurement[measurementsLength];
+ in.readTypedArray(measurementsArray, GnssMeasurement.CREATOR);
+
+ return new GnssMeasurementsEvent(clock, measurementsArray);
+ }
+
+ @Override
+ public GnssMeasurementsEvent[] newArray(int size) {
+ return new GnssMeasurementsEvent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mClock, flags);
+
+ int measurementsCount = mReadOnlyMeasurements.size();
+ GnssMeasurement[] measurementsArray =
+ mReadOnlyMeasurements.toArray(new GnssMeasurement[measurementsCount]);
+ parcel.writeInt(measurementsArray.length);
+ parcel.writeTypedArray(measurementsArray, flags);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("[ GnssMeasurementsEvent:\n\n");
+
+ builder.append(mClock.toString());
+ builder.append("\n");
+
+ for (GnssMeasurement measurement : mReadOnlyMeasurements) {
+ builder.append(measurement.toString());
+ builder.append("\n");
+ }
+
+ builder.append("]");
+
+ return builder.toString();
+ }
+}
diff --git a/android/location/GnssNavigationMessage.java b/android/location/GnssNavigationMessage.java
new file mode 100644
index 00000000..c7188aa3
--- /dev/null
+++ b/android/location/GnssNavigationMessage.java
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2014 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 android.annotation.TestApi;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.security.InvalidParameterException;
+
+/**
+ * A class containing a GNSS satellite Navigation Message.
+ */
+public final class GnssNavigationMessage implements Parcelable {
+
+ private static final byte[] EMPTY_ARRAY = new byte[0];
+
+ /**
+ * The type of the GNSS Navigation Message
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({TYPE_UNKNOWN, TYPE_GPS_L1CA, TYPE_GPS_L2CNAV, TYPE_GPS_L5CNAV, TYPE_GPS_CNAV2,
+ TYPE_GLO_L1CA, TYPE_BDS_D1, TYPE_BDS_D2, TYPE_GAL_I, TYPE_GAL_F})
+ public @interface GnssNavigationMessageType {}
+
+ // The following enumerations must be in sync with the values declared in gps.h
+
+ /** Message type unknown */
+ public static final int TYPE_UNKNOWN = 0;
+ /** GPS L1 C/A message contained in the structure. */
+ public static final int TYPE_GPS_L1CA = 0x0101;
+ /** GPS L2-CNAV message contained in the structure. */
+ public static final int TYPE_GPS_L2CNAV = 0x0102;
+ /** GPS L5-CNAV message contained in the structure. */
+ public static final int TYPE_GPS_L5CNAV = 0x0103;
+ /** GPS CNAV-2 message contained in the structure. */
+ public static final int TYPE_GPS_CNAV2 = 0x0104;
+ /** Glonass L1 CA message contained in the structure. */
+ public static final int TYPE_GLO_L1CA = 0x0301;
+ /** Beidou D1 message contained in the structure. */
+ public static final int TYPE_BDS_D1 = 0x0501;
+ /** Beidou D2 message contained in the structure. */
+ public static final int TYPE_BDS_D2 = 0x0502;
+ /** Galileo I/NAV message contained in the structure. */
+ public static final int TYPE_GAL_I = 0x0601;
+ /** Galileo F/NAV message contained in the structure. */
+ public static final int TYPE_GAL_F = 0x0602;
+
+ /**
+ * The Navigation Message Status is 'unknown'.
+ */
+ public static final int STATUS_UNKNOWN = 0;
+
+ /**
+ * The Navigation Message was received without any parity error in its navigation words.
+ */
+ public static final int STATUS_PARITY_PASSED = (1<<0);
+
+ /**
+ * The Navigation Message was received with words that failed parity check, but the receiver was
+ * able to correct those words.
+ */
+ public static final int STATUS_PARITY_REBUILT = (1<<1);
+
+ /**
+ * Used for receiving GNSS satellite Navigation Messages from the GNSS engine.
+ *
+ * <p>You can implement this interface and call
+ * {@link LocationManager#registerGnssNavigationMessageCallback}.
+ */
+ public static abstract class Callback {
+ /**
+ * The status of GNSS Navigation Message event.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_LOCATION_DISABLED})
+ public @interface GnssNavigationMessageStatus {}
+
+ /**
+ * The system does not support tracking of GNSS Navigation Messages.
+ *
+ * This status will not change in the future.
+ */
+ public static final int STATUS_NOT_SUPPORTED = 0;
+
+ /**
+ * GNSS Navigation Messages are successfully being tracked, it will receive updates once
+ * they are available.
+ */
+ public static final int STATUS_READY = 1;
+
+ /**
+ * GNSS provider or Location is disabled, updated will not be received until they are
+ * enabled.
+ */
+ public static final int STATUS_LOCATION_DISABLED = 2;
+
+ /**
+ * Returns the latest collected GNSS Navigation Message.
+ */
+ public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {}
+
+ /**
+ * Returns the latest status of the GNSS Navigation Messages sub-system.
+ */
+ public void onStatusChanged(@GnssNavigationMessageStatus int status) {}
+ }
+
+ // End enumerations in sync with gps.h
+
+ private int mType;
+ private int mSvid;
+ private int mMessageId;
+ private int mSubmessageId;
+ private byte[] mData;
+ private int mStatus;
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public GnssNavigationMessage() {
+ initialize();
+ }
+
+ /**
+ * Sets all contents to the values stored in the provided object.
+ * @hide
+ */
+ @TestApi
+ public void set(GnssNavigationMessage navigationMessage) {
+ mType = navigationMessage.mType;
+ mSvid = navigationMessage.mSvid;
+ mMessageId = navigationMessage.mMessageId;
+ mSubmessageId = navigationMessage.mSubmessageId;
+ mData = navigationMessage.mData;
+ mStatus = navigationMessage.mStatus;
+ }
+
+ /**
+ * Resets all the contents to its original state.
+ * @hide
+ */
+ @TestApi
+ public void reset() {
+ initialize();
+ }
+
+ /**
+ * Gets the type of the navigation message contained in the object.
+ */
+ @GnssNavigationMessageType
+ public int getType() {
+ return mType;
+ }
+
+ /**
+ * Sets the type of the navigation message.
+ * @hide
+ */
+ @TestApi
+ public void setType(@GnssNavigationMessageType int value) {
+ mType = value;
+ }
+
+ /**
+ * Gets a string representation of the 'type'.
+ * For internal and logging use only.
+ */
+ private String getTypeString() {
+ switch (mType) {
+ case TYPE_UNKNOWN:
+ return "Unknown";
+ case TYPE_GPS_L1CA:
+ return "GPS L1 C/A";
+ case TYPE_GPS_L2CNAV:
+ return "GPS L2-CNAV";
+ case TYPE_GPS_L5CNAV:
+ return "GPS L5-CNAV";
+ case TYPE_GPS_CNAV2:
+ return "GPS CNAV2";
+ case TYPE_GLO_L1CA:
+ return "Glonass L1 C/A";
+ case TYPE_BDS_D1:
+ return "Beidou D1";
+ case TYPE_BDS_D2:
+ return "Beidou D2";
+ case TYPE_GAL_I:
+ return "Galileo I";
+ case TYPE_GAL_F:
+ return "Galileo F";
+ default:
+ return "<Invalid:" + mType + ">";
+ }
+ }
+
+ /**
+ * Gets the satellite ID.
+ *
+ * <p>Range varies by constellation. See definition at {@code GnssStatus#getSvid(int)}
+ */
+ public int getSvid() {
+ return mSvid;
+ }
+
+ /**
+ * Sets the satellite ID.
+ * @hide
+ */
+ @TestApi
+ public void setSvid(int value) {
+ mSvid = value;
+ }
+
+ /**
+ * Gets the Message identifier.
+ *
+ * <p>This provides an index to help with complete Navigation Message assembly. Similar
+ * identifiers within the data bits themselves often supplement this information, in ways even
+ * more specific to each message type; see the relevant satellite constellation ICDs for
+ * details.
+ *
+ * <ul>
+ * <li> For GPS L1 C/A subframe 4 and 5, this value corresponds to the 'frame id' of the
+ * navigation message, in the range of 1-25 (Subframe 1, 2, 3 does not contain a 'frame id' and
+ * this value can be set to -1.)</li>
+ * <li> For Glonass L1 C/A, this refers to the frame ID, in the range of 1-5.</li>
+ * <li> For BeiDou D1, this refers to the frame number in the range of 1-24</li>
+ * <li> For Beidou D2, this refers to the frame number, in the range of 1-120</li>
+ * <li> For Galileo F/NAV nominal frame structure, this refers to the subframe number, in the
+ * range of 1-12</li>
+ * <li> For Galileo I/NAV nominal frame structure, this refers to the subframe number in the
+ * range of 1-24</li>
+ * </ul>
+ */
+ public int getMessageId() {
+ return mMessageId;
+ }
+
+ /**
+ * Sets the Message Identifier.
+ * @hide
+ */
+ @TestApi
+ public void setMessageId(int value) {
+ mMessageId = value;
+ }
+
+ /**
+ * Gets the sub-message identifier, relevant to the {@link #getType()} of the message.
+ *
+ * <ul>
+ * <li> For GPS L1 C/A, BeiDou D1 &amp; BeiDou D2, the submessage id corresponds to the subframe
+ * number of the navigation message, in the range of 1-5.</li>
+ * <li>For Glonass L1 C/A, this refers to the String number, in the range from 1-15</li>
+ * <li>For Galileo F/NAV, this refers to the page type in the range 1-6</li>
+ * <li>For Galileo I/NAV, this refers to the word type in the range 1-10+</li>
+ * <li>For Galileo in particular, the type information embedded within the data bits may be even
+ * more useful in interpretation, than the nominal page and word types provided in this
+ * field.</li>
+ * </ul>
+ */
+ public int getSubmessageId() {
+ return mSubmessageId;
+ }
+
+ /**
+ * Sets the Sub-message identifier.
+ * @hide
+ */
+ @TestApi
+ public void setSubmessageId(int value) {
+ mSubmessageId = value;
+ }
+
+ /**
+ * Gets the data of the reported GPS message.
+ *
+ * <p>The bytes (or words) specified using big endian format (MSB first).
+ *
+ * <ul>
+ * <li>For GPS L1 C/A, Beidou D1 &amp; Beidou D2, each subframe contains 10 30-bit words. Each
+ * word (30 bits) should be fit into the last 30 bits in a 4-byte word (skip B31 and B32), with
+ * MSB first, for a total of 40 bytes, covering a time period of 6, 6, and 0.6 seconds,
+ * respectively.</li>
+ * <li>For Glonass L1 C/A, each string contains 85 data bits, including the checksum. These
+ * bits should be fit into 11 bytes, with MSB first (skip B86-B88), covering a time period of 2
+ * seconds.</li>
+ * <li>For Galileo F/NAV, each word consists of 238-bit (sync &amp; tail symbols excluded). Each
+ * word should be fit into 30-bytes, with MSB first (skip B239, B240), covering a time period of
+ * 10 seconds.</li>
+ * <li>For Galileo I/NAV, each page contains 2 page parts, even and odd, with a total of 2x114 =
+ * 228 bits, (sync &amp; tail excluded) that should be fit into 29 bytes, with MSB first (skip
+ * B229-B232).</li>
+ * </ul>
+ */
+ @NonNull
+ public byte[] getData() {
+ return mData;
+ }
+
+ /**
+ * Sets the data associated with the Navigation Message.
+ * @hide
+ */
+ @TestApi
+ public void setData(byte[] value) {
+ if (value == null) {
+ throw new InvalidParameterException("Data must be a non-null array");
+ }
+
+ mData = value;
+ }
+
+ /**
+ * Gets the Status of the navigation message contained in the object.
+ */
+ public int getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * Sets the status of the navigation message.
+ * @hide
+ */
+ @TestApi
+ public void setStatus(int value) {
+ mStatus = value;
+ }
+
+ /**
+ * Gets a string representation of the 'status'.
+ * For internal and logging use only.
+ */
+ private String getStatusString() {
+ switch (mStatus) {
+ case STATUS_UNKNOWN:
+ return "Unknown";
+ case STATUS_PARITY_PASSED:
+ return "ParityPassed";
+ case STATUS_PARITY_REBUILT:
+ return "ParityRebuilt";
+ default:
+ return "<Invalid:" + mStatus + ">";
+ }
+ }
+
+ public static final Creator<GnssNavigationMessage> CREATOR =
+ new Creator<GnssNavigationMessage>() {
+ @Override
+ public GnssNavigationMessage createFromParcel(Parcel parcel) {
+ GnssNavigationMessage navigationMessage = new GnssNavigationMessage();
+
+ navigationMessage.setType(parcel.readInt());
+ navigationMessage.setSvid(parcel.readInt());
+ navigationMessage.setMessageId(parcel.readInt());
+ navigationMessage.setSubmessageId(parcel.readInt());
+ int dataLength = parcel.readInt();
+ byte[] data = new byte[dataLength];
+ parcel.readByteArray(data);
+ navigationMessage.setData(data);
+ navigationMessage.setStatus(parcel.readInt());
+
+ return navigationMessage;
+ }
+
+ @Override
+ public GnssNavigationMessage[] newArray(int size) {
+ return new GnssNavigationMessage[size];
+ }
+ };
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mType);
+ parcel.writeInt(mSvid);
+ parcel.writeInt(mMessageId);
+ parcel.writeInt(mSubmessageId);
+ parcel.writeInt(mData.length);
+ parcel.writeByteArray(mData);
+ parcel.writeInt(mStatus);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ final String format = " %-15s = %s\n";
+ StringBuilder builder = new StringBuilder("GnssNavigationMessage:\n");
+
+ builder.append(String.format(format, "Type", getTypeString()));
+ builder.append(String.format(format, "Svid", mSvid));
+ builder.append(String.format(format, "Status", getStatusString()));
+ builder.append(String.format(format, "MessageId", mMessageId));
+ builder.append(String.format(format, "SubmessageId", mSubmessageId));
+
+ builder.append(String.format(format, "Data", "{"));
+ String prefix = " ";
+ for(byte value : mData) {
+ builder.append(prefix);
+ builder.append(value);
+ prefix = ", ";
+ }
+ builder.append(" }");
+
+ return builder.toString();
+ }
+
+ private void initialize() {
+ mType = TYPE_UNKNOWN;
+ mSvid = 0;
+ mMessageId = -1;
+ mSubmessageId = -1;
+ mData = EMPTY_ARRAY;
+ mStatus = STATUS_UNKNOWN;
+ }
+}
diff --git a/android/location/GnssNavigationMessageCallbackTransport.java b/android/location/GnssNavigationMessageCallbackTransport.java
new file mode 100644
index 00000000..1eafd02e
--- /dev/null
+++ b/android/location/GnssNavigationMessageCallbackTransport.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 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 android.content.Context;
+import android.os.RemoteException;
+
+/**
+ * A handler class to manage transport callback for {@link GnssNavigationMessage.Callback}.
+ *
+ * @hide
+ */
+class GnssNavigationMessageCallbackTransport
+ extends LocalListenerHelper<GnssNavigationMessage.Callback> {
+ private final ILocationManager mLocationManager;
+
+ private final IGnssNavigationMessageListener mListenerTransport = new ListenerTransport();
+
+ public GnssNavigationMessageCallbackTransport(
+ Context context,
+ ILocationManager locationManager) {
+ super(context, "GnssNavigationMessageCallbackTransport");
+ mLocationManager = locationManager;
+ }
+
+ @Override
+ protected boolean registerWithServer() throws RemoteException {
+ return mLocationManager.addGnssNavigationMessageListener(
+ mListenerTransport,
+ getContext().getPackageName());
+ }
+
+ @Override
+ protected void unregisterFromServer() throws RemoteException {
+ mLocationManager.removeGnssNavigationMessageListener(mListenerTransport);
+ }
+
+ private class ListenerTransport extends IGnssNavigationMessageListener.Stub {
+ @Override
+ public void onGnssNavigationMessageReceived(final GnssNavigationMessage event) {
+ ListenerOperation<GnssNavigationMessage.Callback> operation =
+ new ListenerOperation<GnssNavigationMessage.Callback>() {
+ @Override
+ public void execute(GnssNavigationMessage.Callback callback)
+ throws RemoteException {
+ callback.onGnssNavigationMessageReceived(event);
+ }
+ };
+ foreach(operation);
+ }
+
+ @Override
+ public void onStatusChanged(final int status) {
+ ListenerOperation<GnssNavigationMessage.Callback> operation =
+ new ListenerOperation<GnssNavigationMessage.Callback>() {
+ @Override
+ public void execute(GnssNavigationMessage.Callback callback)
+ throws RemoteException {
+ callback.onStatusChanged(status);
+ }
+ };
+ foreach(operation);
+ }
+ }
+}
diff --git a/android/location/GnssStatus.java b/android/location/GnssStatus.java
new file mode 100644
index 00000000..b2903c46
--- /dev/null
+++ b/android/location/GnssStatus.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright (C) 2016 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 android.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This class represents the current state of the GNSS engine.
+ * This class is used in conjunction with the {@link GnssStatus.Callback}.
+ */
+public final class GnssStatus {
+ // these must match the definitions in gps.h
+
+ /** Unknown constellation type. */
+ public static final int CONSTELLATION_UNKNOWN = 0;
+ /** Constellation type constant for GPS. */
+ public static final int CONSTELLATION_GPS = 1;
+ /** Constellation type constant for SBAS. */
+ public static final int CONSTELLATION_SBAS = 2;
+ /** Constellation type constant for Glonass. */
+ public static final int CONSTELLATION_GLONASS = 3;
+ /** Constellation type constant for QZSS. */
+ public static final int CONSTELLATION_QZSS = 4;
+ /** Constellation type constant for Beidou. */
+ public static final int CONSTELLATION_BEIDOU = 5;
+ /** Constellation type constant for Galileo. */
+ public static final int CONSTELLATION_GALILEO = 6;
+
+ /** @hide */
+ public static final int GNSS_SV_FLAGS_NONE = 0;
+ /** @hide */
+ public static final int GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA = (1 << 0);
+ /** @hide */
+ public static final int GNSS_SV_FLAGS_HAS_ALMANAC_DATA = (1 << 1);
+ /** @hide */
+ public static final int GNSS_SV_FLAGS_USED_IN_FIX = (1 << 2);
+ /** @hide */
+ public static final int GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY = (1 << 3);
+
+ /** @hide */
+ public static final int SVID_SHIFT_WIDTH = 8;
+ /** @hide */
+ public static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 4;
+ /** @hide */
+ public static final int CONSTELLATION_TYPE_MASK = 0xf;
+
+ /**
+ * Used for receiving notifications when GNSS events happen.
+ */
+ public static abstract class Callback {
+ /**
+ * Called when GNSS system has started.
+ */
+ public void onStarted() {}
+
+ /**
+ * Called when GNSS system has stopped.
+ */
+ public void onStopped() {}
+
+ /**
+ * Called when the GNSS system has received its first fix since starting.
+ * @param ttffMillis the time from start to first fix in milliseconds.
+ */
+ public void onFirstFix(int ttffMillis) {}
+
+ /**
+ * Called periodically to report GNSS satellite status.
+ * @param status the current status of all satellites.
+ */
+ public void onSatelliteStatusChanged(GnssStatus status) {}
+ }
+
+ /**
+ * Constellation type.
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({CONSTELLATION_UNKNOWN, CONSTELLATION_GPS, CONSTELLATION_SBAS, CONSTELLATION_GLONASS,
+ CONSTELLATION_QZSS, CONSTELLATION_BEIDOU, CONSTELLATION_GALILEO})
+ public @interface ConstellationType {}
+
+ final int[] mSvidWithFlags;
+ final float[] mCn0DbHz;
+ final float[] mElevations;
+ final float[] mAzimuths;
+ final int mSvCount;
+ final float[] mCarrierFrequencies;
+
+ GnssStatus(int svCount, int[] svidWithFlags, float[] cn0s, float[] elevations,
+ float[] azimuths, float[] carrierFrequencies) {
+ mSvCount = svCount;
+ mSvidWithFlags = svidWithFlags;
+ mCn0DbHz = cn0s;
+ mElevations = elevations;
+ mAzimuths = azimuths;
+ mCarrierFrequencies = carrierFrequencies;
+ }
+
+ /**
+ * Gets the total number of satellites in satellite list.
+ */
+ public int getSatelliteCount() {
+ return mSvCount;
+ }
+
+ /**
+ * Retrieves the constellation type of the satellite at the specified index.
+ *
+ * @param satIndex the index of the satellite in the list.
+ */
+ @ConstellationType
+ public int getConstellationType(int satIndex) {
+ return ((mSvidWithFlags[satIndex] >> CONSTELLATION_TYPE_SHIFT_WIDTH)
+ & CONSTELLATION_TYPE_MASK);
+ }
+
+ /**
+ * Gets the identification number for the satellite at the specific index.
+ *
+ * <p>This svid is pseudo-random number for most constellations. It is FCN &amp; OSN number for
+ * Glonass.
+ *
+ * <p>The distinction is made by looking at constellation field
+ * {@link #getConstellationType(int)} Expected values are in the range of:
+ *
+ * <ul>
+ * <li>GPS: 1-32</li>
+ * <li>SBAS: 120-151, 183-192</li>
+ * <li>GLONASS: One of: OSN, or FCN+100
+ * <ul>
+ * <li>1-24 as the orbital slot number (OSN) (preferred, if known)</li>
+ * <li>93-106 as the frequency channel number (FCN) (-7 to +6) plus 100.
+ * i.e. encode FCN of -7 as 93, 0 as 100, and +6 as 106</li>
+ * </ul></li>
+ * <li>QZSS: 193-200</li>
+ * <li>Galileo: 1-36</li>
+ * <li>Beidou: 1-37</li>
+ * </ul>
+ *
+ * @param satIndex the index of the satellite in the list.
+ */
+ public int getSvid(int satIndex) {
+ return mSvidWithFlags[satIndex] >> SVID_SHIFT_WIDTH;
+ }
+
+ /**
+ * Retrieves the carrier-to-noise density at the antenna of the satellite at the specified index
+ * in dB-Hz.
+ *
+ * @param satIndex the index of the satellite in the list.
+ */
+ public float getCn0DbHz(int satIndex) {
+ return mCn0DbHz[satIndex];
+ }
+
+ /**
+ * Retrieves the elevation of the satellite at the specified index.
+ *
+ * @param satIndex the index of the satellite in the list.
+ */
+ public float getElevationDegrees(int satIndex) {
+ return mElevations[satIndex];
+ }
+
+ /**
+ * Retrieves the azimuth the satellite at the specified index.
+ *
+ * @param satIndex the index of the satellite in the list.
+ */
+ public float getAzimuthDegrees(int satIndex) {
+ return mAzimuths[satIndex];
+ }
+
+ /**
+ * Reports whether the satellite at the specified index has ephemeris data.
+ *
+ * @param satIndex the index of the satellite in the list.
+ */
+ public boolean hasEphemerisData(int satIndex) {
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+ }
+
+ /**
+ * Reports whether the satellite at the specified index has almanac data.
+ *
+ * @param satIndex the index of the satellite in the list.
+ */
+ public boolean hasAlmanacData(int satIndex) {
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+ }
+
+ /**
+ * Reports whether the satellite at the specified index was used in the calculation of the most
+ * recent position fix.
+ *
+ * @param satIndex the index of the satellite in the list.
+ */
+ public boolean usedInFix(int satIndex) {
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+ }
+
+ /**
+ * Reports whether {@link #getCarrierFrequencyHz(int satIndex)} is available (i.e. carrier
+ * frequency is available for the satellite at the specified index).
+ *
+ * @param satIndex the index of the satellite in the list.
+ */
+ public boolean hasCarrierFrequencyHz(int satIndex) {
+ return (mSvidWithFlags[satIndex] & GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) != 0;
+ }
+
+ /**
+ * Gets the carrier frequency of the signal tracked.
+ *
+ * <p>For example it can be the GPS central frequency for L1 = 1575.45 MHz, or L2 = 1227.60 MHz,
+ * L5 = 1176.45 MHz, varying GLO channels, etc. If the field is not set, it is the primary
+ * common use central frequency, e.g. L1 = 1575.45 MHz for GPS.
+ *
+ * For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two measurements
+ * will be reported for this same satellite, in one all the values related to L1 will be filled,
+ * and in the other all of the values related to L5 will be filled.
+ *
+ * <p>The value is only available if {@link #hasCarrierFrequencyHz(int satIndex)} is {@code true}.
+ *
+ * @param satIndex the index of the satellite in the list.
+ *
+ * @return the carrier frequency of the signal tracked in Hz.
+ */
+ public float getCarrierFrequencyHz(int satIndex) {
+ return mCarrierFrequencies[satIndex];
+ }
+}
diff --git a/android/location/GpsClock.java b/android/location/GpsClock.java
new file mode 100644
index 00000000..4135a1c9
--- /dev/null
+++ b/android/location/GpsClock.java
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2014 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 android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class containing a GPS clock timestamp.
+ * It represents a measurement of the GPS receiver's clock.
+ *
+ * @hide
+ */
+@SystemApi
+public class GpsClock implements Parcelable {
+
+ // The following enumerations must be in sync with the values declared in gps.h
+
+ /**
+ * The type of the time stored is not available or it is unknown.
+ */
+ public static final byte TYPE_UNKNOWN = 0;
+
+ /**
+ * The source of the time value reported by this class is the 'Local Hardware Clock'.
+ */
+ public static final byte TYPE_LOCAL_HW_TIME = 1;
+
+ /**
+ * The source of the time value reported by this class is the 'GPS time' derived from
+ * satellites (epoch = Jan 6, 1980).
+ */
+ public static final byte TYPE_GPS_TIME = 2;
+
+ private static final short HAS_NO_FLAGS = 0;
+ private static final short HAS_LEAP_SECOND = (1<<0);
+ private static final short HAS_TIME_UNCERTAINTY = (1<<1);
+ private static final short HAS_FULL_BIAS = (1<<2);
+ private static final short HAS_BIAS = (1<<3);
+ private static final short HAS_BIAS_UNCERTAINTY = (1<<4);
+ private static final short HAS_DRIFT = (1<<5);
+ private static final short HAS_DRIFT_UNCERTAINTY = (1<<6);
+
+ // End enumerations in sync with gps.h
+
+ private short mFlags;
+ private short mLeapSecond;
+ private byte mType;
+ private long mTimeInNs;
+ private double mTimeUncertaintyInNs;
+ private long mFullBiasInNs;
+ private double mBiasInNs;
+ private double mBiasUncertaintyInNs;
+ private double mDriftInNsPerSec;
+ private double mDriftUncertaintyInNsPerSec;
+
+ GpsClock() {
+ initialize();
+ }
+
+ /**
+ * Sets all contents to the values stored in the provided object.
+ */
+ public void set(GpsClock clock) {
+ mFlags = clock.mFlags;
+ mLeapSecond = clock.mLeapSecond;
+ mType = clock.mType;
+ mTimeInNs = clock.mTimeInNs;
+ mTimeUncertaintyInNs = clock.mTimeUncertaintyInNs;
+ mFullBiasInNs = clock.mFullBiasInNs;
+ mBiasInNs = clock.mBiasInNs;
+ mBiasUncertaintyInNs = clock.mBiasUncertaintyInNs;
+ mDriftInNsPerSec = clock.mDriftInNsPerSec;
+ mDriftUncertaintyInNsPerSec = clock.mDriftUncertaintyInNsPerSec;
+ }
+
+ /**
+ * Resets all the contents to its original state.
+ */
+ public void reset() {
+ initialize();
+ }
+
+ /**
+ * Gets the type of time reported by {@link #getTimeInNs()}.
+ */
+ public byte getType() {
+ return mType;
+ }
+
+ /**
+ * Sets the type of time reported.
+ */
+ public void setType(byte value) {
+ mType = value;
+ }
+
+ /**
+ * Gets a string representation of the 'type'.
+ * For internal and logging use only.
+ */
+ private String getTypeString() {
+ switch (mType) {
+ case TYPE_UNKNOWN:
+ return "Unknown";
+ case TYPE_GPS_TIME:
+ return "GpsTime";
+ case TYPE_LOCAL_HW_TIME:
+ return "LocalHwClock";
+ default:
+ return "<Invalid:" + mType + ">";
+ }
+ }
+
+ /**
+ * Returns true if {@link #getLeapSecond()} is available, false otherwise.
+ */
+ public boolean hasLeapSecond() {
+ return isFlagSet(HAS_LEAP_SECOND);
+ }
+
+ /**
+ * Gets the leap second associated with the clock's time.
+ * The sign of the value is defined by the following equation:
+ * utc_time_ns = time_ns + (full_bias_ns + bias_ns) - leap_second * 1,000,000,000
+ *
+ * The value is only available if {@link #hasLeapSecond()} is true.
+ */
+ public short getLeapSecond() {
+ return mLeapSecond;
+ }
+
+ /**
+ * Sets the leap second associated with the clock's time.
+ */
+ public void setLeapSecond(short leapSecond) {
+ setFlag(HAS_LEAP_SECOND);
+ mLeapSecond = leapSecond;
+ }
+
+ /**
+ * Resets the leap second associated with the clock's time.
+ */
+ public void resetLeapSecond() {
+ resetFlag(HAS_LEAP_SECOND);
+ mLeapSecond = Short.MIN_VALUE;
+ }
+
+ /**
+ * Gets the GPS receiver internal clock value in nanoseconds.
+ * This can be either the 'local hardware clock' value ({@link #TYPE_LOCAL_HW_TIME}), or the
+ * current GPS time derived inside GPS receiver ({@link #TYPE_GPS_TIME}).
+ * {@link #getType()} defines the time reported.
+ *
+ * For 'local hardware clock' this value is expected to be monotonically increasing during the
+ * reporting session. The real GPS time can be derived by compensating
+ * {@link #getFullBiasInNs()} (when it is available) from this value.
+ *
+ * For 'GPS time' this value is expected to be the best estimation of current GPS time that GPS
+ * receiver can achieve. {@link #getTimeUncertaintyInNs()} should be available when GPS time is
+ * specified.
+ *
+ * Sub-nanosecond accuracy can be provided by means of {@link #getBiasInNs()}.
+ * The reported time includes {@link #getTimeUncertaintyInNs()}.
+ */
+ public long getTimeInNs() {
+ return mTimeInNs;
+ }
+
+ /**
+ * Sets the GPS receiver internal clock in nanoseconds.
+ */
+ public void setTimeInNs(long timeInNs) {
+ mTimeInNs = timeInNs;
+ }
+
+ /**
+ * Returns true if {@link #getTimeUncertaintyInNs()} is available, false otherwise.
+ */
+ public boolean hasTimeUncertaintyInNs() {
+ return isFlagSet(HAS_TIME_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the clock's time Uncertainty (1-Sigma) in nanoseconds.
+ * The uncertainty is represented as an absolute (single sided) value.
+ *
+ * The value is only available if {@link #hasTimeUncertaintyInNs()} is true.
+ */
+ public double getTimeUncertaintyInNs() {
+ return mTimeUncertaintyInNs;
+ }
+
+ /**
+ * Sets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
+ */
+ public void setTimeUncertaintyInNs(double timeUncertaintyInNs) {
+ setFlag(HAS_TIME_UNCERTAINTY);
+ mTimeUncertaintyInNs = timeUncertaintyInNs;
+ }
+
+ /**
+ * Resets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
+ */
+ public void resetTimeUncertaintyInNs() {
+ resetFlag(HAS_TIME_UNCERTAINTY);
+ mTimeUncertaintyInNs = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link @getFullBiasInNs()} is available, false otherwise.
+ */
+ public boolean hasFullBiasInNs() {
+ return isFlagSet(HAS_FULL_BIAS);
+ }
+
+ /**
+ * Gets the difference between hardware clock ({@link #getTimeInNs()}) inside GPS receiver and
+ * the true GPS time since 0000Z, January 6, 1980, in nanoseconds.
+ *
+ * This value is available if {@link #TYPE_LOCAL_HW_TIME} is set, and GPS receiver has solved
+ * the clock for GPS time.
+ * {@link #getBiasUncertaintyInNs()} should be used for quality check.
+ *
+ * The sign of the value is defined by the following equation:
+ * true time (GPS time) = time_ns + (full_bias_ns + bias_ns)
+ *
+ * The reported full bias includes {@link #getBiasUncertaintyInNs()}.
+ * The value is onl available if {@link #hasFullBiasInNs()} is true.
+ */
+ public long getFullBiasInNs() {
+ return mFullBiasInNs;
+ }
+
+ /**
+ * Sets the full bias in nanoseconds.
+ */
+ public void setFullBiasInNs(long value) {
+ setFlag(HAS_FULL_BIAS);
+ mFullBiasInNs = value;
+ }
+
+ /**
+ * Resets the full bias in nanoseconds.
+ */
+ public void resetFullBiasInNs() {
+ resetFlag(HAS_FULL_BIAS);
+ mFullBiasInNs = Long.MIN_VALUE;
+ }
+
+ /**
+ * Returns true if {@link #getBiasInNs()} is available, false otherwise.
+ */
+ public boolean hasBiasInNs() {
+ return isFlagSet(HAS_BIAS);
+ }
+
+ /**
+ * Gets the clock's sub-nanosecond bias.
+ * The reported bias includes {@link #getBiasUncertaintyInNs()}.
+ *
+ * The value is only available if {@link #hasBiasInNs()} is true.
+ */
+ public double getBiasInNs() {
+ return mBiasInNs;
+ }
+
+ /**
+ * Sets the sub-nanosecond bias.
+ */
+ public void setBiasInNs(double biasInNs) {
+ setFlag(HAS_BIAS);
+ mBiasInNs = biasInNs;
+ }
+
+ /**
+ * Resets the clock's Bias in nanoseconds.
+ */
+ public void resetBiasInNs() {
+ resetFlag(HAS_BIAS);
+ mBiasInNs = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getBiasUncertaintyInNs()} is available, false otherwise.
+ */
+ public boolean hasBiasUncertaintyInNs() {
+ return isFlagSet(HAS_BIAS_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
+ *
+ * The value is only available if {@link #hasBiasUncertaintyInNs()} is true.
+ */
+ public double getBiasUncertaintyInNs() {
+ return mBiasUncertaintyInNs;
+ }
+
+ /**
+ * Sets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
+ */
+ public void setBiasUncertaintyInNs(double biasUncertaintyInNs) {
+ setFlag(HAS_BIAS_UNCERTAINTY);
+ mBiasUncertaintyInNs = biasUncertaintyInNs;
+ }
+
+ /**
+ * Resets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
+ */
+ public void resetBiasUncertaintyInNs() {
+ resetFlag(HAS_BIAS_UNCERTAINTY);
+ mBiasUncertaintyInNs = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getDriftInNsPerSec()} is available, false otherwise.
+ */
+ public boolean hasDriftInNsPerSec() {
+ return isFlagSet(HAS_DRIFT);
+ }
+
+ /**
+ * Gets the clock's Drift in nanoseconds per second.
+ * A positive value indicates that the frequency is higher than the nominal frequency.
+ * The reported drift includes {@link #getDriftUncertaintyInNsPerSec()}.
+ *
+ * The value is only available if {@link #hasDriftInNsPerSec()} is true.
+ */
+ public double getDriftInNsPerSec() {
+ return mDriftInNsPerSec;
+ }
+
+ /**
+ * Sets the clock's Drift in nanoseconds per second.
+ */
+ public void setDriftInNsPerSec(double driftInNsPerSec) {
+ setFlag(HAS_DRIFT);
+ mDriftInNsPerSec = driftInNsPerSec;
+ }
+
+ /**
+ * Resets the clock's Drift in nanoseconds per second.
+ */
+ public void resetDriftInNsPerSec() {
+ resetFlag(HAS_DRIFT);
+ mDriftInNsPerSec = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getDriftUncertaintyInNsPerSec()} is available, false otherwise.
+ */
+ public boolean hasDriftUncertaintyInNsPerSec() {
+ return isFlagSet(HAS_DRIFT_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
+ *
+ * The value is only available if {@link #hasDriftUncertaintyInNsPerSec()} is true.
+ */
+ public double getDriftUncertaintyInNsPerSec() {
+ return mDriftUncertaintyInNsPerSec;
+ }
+
+ /**
+ * Sets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
+ */
+ public void setDriftUncertaintyInNsPerSec(double driftUncertaintyInNsPerSec) {
+ setFlag(HAS_DRIFT_UNCERTAINTY);
+ mDriftUncertaintyInNsPerSec = driftUncertaintyInNsPerSec;
+ }
+
+ /**
+ * Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
+ */
+ public void resetDriftUncertaintyInNsPerSec() {
+ resetFlag(HAS_DRIFT_UNCERTAINTY);
+ mDriftUncertaintyInNsPerSec = Double.NaN;
+ }
+
+ public static final Creator<GpsClock> CREATOR = new Creator<GpsClock>() {
+ @Override
+ public GpsClock createFromParcel(Parcel parcel) {
+ GpsClock gpsClock = new GpsClock();
+
+ gpsClock.mFlags = (short) parcel.readInt();
+ gpsClock.mLeapSecond = (short) parcel.readInt();
+ gpsClock.mType = parcel.readByte();
+ gpsClock.mTimeInNs = parcel.readLong();
+ gpsClock.mTimeUncertaintyInNs = parcel.readDouble();
+ gpsClock.mFullBiasInNs = parcel.readLong();
+ gpsClock.mBiasInNs = parcel.readDouble();
+ gpsClock.mBiasUncertaintyInNs = parcel.readDouble();
+ gpsClock.mDriftInNsPerSec = parcel.readDouble();
+ gpsClock.mDriftUncertaintyInNsPerSec = parcel.readDouble();
+
+ return gpsClock;
+ }
+
+ @Override
+ public GpsClock[] newArray(int size) {
+ return new GpsClock[size];
+ }
+ };
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mFlags);
+ parcel.writeInt(mLeapSecond);
+ parcel.writeByte(mType);
+ parcel.writeLong(mTimeInNs);
+ parcel.writeDouble(mTimeUncertaintyInNs);
+ parcel.writeLong(mFullBiasInNs);
+ parcel.writeDouble(mBiasInNs);
+ parcel.writeDouble(mBiasUncertaintyInNs);
+ parcel.writeDouble(mDriftInNsPerSec);
+ parcel.writeDouble(mDriftUncertaintyInNsPerSec);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ final String format = " %-15s = %s\n";
+ final String formatWithUncertainty = " %-15s = %-25s %-26s = %s\n";
+ StringBuilder builder = new StringBuilder("GpsClock:\n");
+
+ builder.append(String.format(format, "Type", getTypeString()));
+
+ builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "TimeInNs",
+ mTimeInNs,
+ "TimeUncertaintyInNs",
+ hasTimeUncertaintyInNs() ? mTimeUncertaintyInNs : null));
+
+ builder.append(String.format(
+ format,
+ "FullBiasInNs",
+ hasFullBiasInNs() ? mFullBiasInNs : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "BiasInNs",
+ hasBiasInNs() ? mBiasInNs : null,
+ "BiasUncertaintyInNs",
+ hasBiasUncertaintyInNs() ? mBiasUncertaintyInNs : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "DriftInNsPerSec",
+ hasDriftInNsPerSec() ? mDriftInNsPerSec : null,
+ "DriftUncertaintyInNsPerSec",
+ hasDriftUncertaintyInNsPerSec() ? mDriftUncertaintyInNsPerSec : null));
+
+ return builder.toString();
+ }
+
+ private void initialize() {
+ mFlags = HAS_NO_FLAGS;
+ resetLeapSecond();
+ setType(TYPE_UNKNOWN);
+ setTimeInNs(Long.MIN_VALUE);
+ resetTimeUncertaintyInNs();
+ resetFullBiasInNs();
+ resetBiasInNs();
+ resetBiasUncertaintyInNs();
+ resetDriftInNsPerSec();
+ resetDriftUncertaintyInNsPerSec();
+ }
+
+ private void setFlag(short flag) {
+ mFlags |= flag;
+ }
+
+ private void resetFlag(short flag) {
+ mFlags &= ~flag;
+ }
+
+ private boolean isFlagSet(short flag) {
+ return (mFlags & flag) == flag;
+ }
+}
diff --git a/android/location/GpsMeasurement.java b/android/location/GpsMeasurement.java
new file mode 100644
index 00000000..f13a440f
--- /dev/null
+++ b/android/location/GpsMeasurement.java
@@ -0,0 +1,1413 @@
+/*
+ * Copyright (C) 2014 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 android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A class representing a GPS satellite measurement, containing raw and computed information.
+ *
+ * @hide
+ */
+@SystemApi
+public class GpsMeasurement implements Parcelable {
+ private int mFlags;
+ private byte mPrn;
+ private double mTimeOffsetInNs;
+ private short mState;
+ private long mReceivedGpsTowInNs;
+ private long mReceivedGpsTowUncertaintyInNs;
+ private double mCn0InDbHz;
+ private double mPseudorangeRateInMetersPerSec;
+ private double mPseudorangeRateUncertaintyInMetersPerSec;
+ private short mAccumulatedDeltaRangeState;
+ private double mAccumulatedDeltaRangeInMeters;
+ private double mAccumulatedDeltaRangeUncertaintyInMeters;
+ private double mPseudorangeInMeters;
+ private double mPseudorangeUncertaintyInMeters;
+ private double mCodePhaseInChips;
+ private double mCodePhaseUncertaintyInChips;
+ private float mCarrierFrequencyInHz;
+ private long mCarrierCycles;
+ private double mCarrierPhase;
+ private double mCarrierPhaseUncertainty;
+ private byte mLossOfLock;
+ private int mBitNumber;
+ private short mTimeFromLastBitInMs;
+ private double mDopplerShiftInHz;
+ private double mDopplerShiftUncertaintyInHz;
+ private byte mMultipathIndicator;
+ private double mSnrInDb;
+ private double mElevationInDeg;
+ private double mElevationUncertaintyInDeg;
+ private double mAzimuthInDeg;
+ private double mAzimuthUncertaintyInDeg;
+ private boolean mUsedInFix;
+
+ // The following enumerations must be in sync with the values declared in gps.h
+
+ private static final int HAS_NO_FLAGS = 0;
+ private static final int HAS_SNR = (1<<0);
+ private static final int HAS_ELEVATION = (1<<1);
+ private static final int HAS_ELEVATION_UNCERTAINTY = (1<<2);
+ private static final int HAS_AZIMUTH = (1<<3);
+ private static final int HAS_AZIMUTH_UNCERTAINTY = (1<<4);
+ private static final int HAS_PSEUDORANGE = (1<<5);
+ private static final int HAS_PSEUDORANGE_UNCERTAINTY = (1<<6);
+ private static final int HAS_CODE_PHASE = (1<<7);
+ private static final int HAS_CODE_PHASE_UNCERTAINTY = (1<<8);
+ private static final int HAS_CARRIER_FREQUENCY = (1<<9);
+ private static final int HAS_CARRIER_CYCLES = (1<<10);
+ private static final int HAS_CARRIER_PHASE = (1<<11);
+ private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12);
+ private static final int HAS_BIT_NUMBER = (1<<13);
+ private static final int HAS_TIME_FROM_LAST_BIT = (1<<14);
+ private static final int HAS_DOPPLER_SHIFT = (1<<15);
+ private static final int HAS_DOPPLER_SHIFT_UNCERTAINTY = (1<<16);
+ private static final int HAS_USED_IN_FIX = (1<<17);
+ private static final int GPS_MEASUREMENT_HAS_UNCORRECTED_PSEUDORANGE_RATE = (1<<18);
+
+ /**
+ * The indicator is not available or it is unknown.
+ */
+ public static final byte LOSS_OF_LOCK_UNKNOWN = 0;
+
+ /**
+ * The measurement does not present any indication of 'loss of lock'.
+ */
+ public static final byte LOSS_OF_LOCK_OK = 1;
+
+ /**
+ * 'Loss of lock' detected between the previous and current observation: cycle slip possible.
+ */
+ public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2;
+
+ /**
+ * The indicator is not available or it is unknown.
+ */
+ public static final byte MULTIPATH_INDICATOR_UNKNOWN = 0;
+
+ /**
+ * The measurement has been indicated to use multi-path.
+ */
+ public static final byte MULTIPATH_INDICATOR_DETECTED = 1;
+
+ /**
+ * The measurement has been indicated not tu use multi-path.
+ */
+ public static final byte MULTIPATH_INDICATOR_NOT_USED = 2;
+
+ /**
+ * The state of GPS receiver the measurement is invalid or unknown.
+ */
+ public static final short STATE_UNKNOWN = 0;
+
+ /**
+ * The state of the GPS receiver is ranging code lock.
+ */
+ public static final short STATE_CODE_LOCK = (1<<0);
+
+ /**
+ * The state of the GPS receiver is in bit sync.
+ */
+ public static final short STATE_BIT_SYNC = (1<<1);
+
+ /**
+ *The state of the GPS receiver is in sub-frame sync.
+ */
+ public static final short STATE_SUBFRAME_SYNC = (1<<2);
+
+ /**
+ * The state of the GPS receiver has TOW decoded.
+ */
+ public static final short STATE_TOW_DECODED = (1<<3);
+
+ /**
+ * The state of the GPS receiver contains millisecond ambiguity.
+ */
+ public static final short STATE_MSEC_AMBIGUOUS = (1<<4);
+
+ /**
+ * All the GPS receiver state flags.
+ */
+ private static final short STATE_ALL = STATE_CODE_LOCK | STATE_BIT_SYNC | STATE_SUBFRAME_SYNC
+ | STATE_TOW_DECODED | STATE_MSEC_AMBIGUOUS;
+
+ /**
+ * The state of the 'Accumulated Delta Range' is invalid or unknown.
+ */
+ public static final short ADR_STATE_UNKNOWN = 0;
+
+ /**
+ * The state of the 'Accumulated Delta Range' is valid.
+ */
+ public static final short ADR_STATE_VALID = (1<<0);
+
+ /**
+ * The state of the 'Accumulated Delta Range' has detected a reset.
+ */
+ public static final short ADR_STATE_RESET = (1<<1);
+
+ /**
+ * The state of the 'Accumulated Delta Range' has a cycle slip detected.
+ */
+ public static final short ADR_STATE_CYCLE_SLIP = (1<<2);
+
+ /**
+ * All the 'Accumulated Delta Range' flags.
+ */
+ private static final short ADR_ALL = ADR_STATE_VALID | ADR_STATE_RESET | ADR_STATE_CYCLE_SLIP;
+
+ // End enumerations in sync with gps.h
+
+ GpsMeasurement() {
+ initialize();
+ }
+
+ /**
+ * Sets all contents to the values stored in the provided object.
+ */
+ public void set(GpsMeasurement measurement) {
+ mFlags = measurement.mFlags;
+ mPrn = measurement.mPrn;
+ mTimeOffsetInNs = measurement.mTimeOffsetInNs;
+ mState = measurement.mState;
+ mReceivedGpsTowInNs = measurement.mReceivedGpsTowInNs;
+ mReceivedGpsTowUncertaintyInNs = measurement.mReceivedGpsTowUncertaintyInNs;
+ mCn0InDbHz = measurement.mCn0InDbHz;
+ mPseudorangeRateInMetersPerSec = measurement.mPseudorangeRateInMetersPerSec;
+ mPseudorangeRateUncertaintyInMetersPerSec =
+ measurement.mPseudorangeRateUncertaintyInMetersPerSec;
+ mAccumulatedDeltaRangeState = measurement.mAccumulatedDeltaRangeState;
+ mAccumulatedDeltaRangeInMeters = measurement.mAccumulatedDeltaRangeInMeters;
+ mAccumulatedDeltaRangeUncertaintyInMeters =
+ measurement.mAccumulatedDeltaRangeUncertaintyInMeters;
+ mPseudorangeInMeters = measurement.mPseudorangeInMeters;
+ mPseudorangeUncertaintyInMeters = measurement.mPseudorangeUncertaintyInMeters;
+ mCodePhaseInChips = measurement.mCodePhaseInChips;
+ mCodePhaseUncertaintyInChips = measurement.mCodePhaseUncertaintyInChips;
+ mCarrierFrequencyInHz = measurement.mCarrierFrequencyInHz;
+ mCarrierCycles = measurement.mCarrierCycles;
+ mCarrierPhase = measurement.mCarrierPhase;
+ mCarrierPhaseUncertainty = measurement.mCarrierPhaseUncertainty;
+ mLossOfLock = measurement.mLossOfLock;
+ mBitNumber = measurement.mBitNumber;
+ mTimeFromLastBitInMs = measurement.mTimeFromLastBitInMs;
+ mDopplerShiftInHz = measurement.mDopplerShiftInHz;
+ mDopplerShiftUncertaintyInHz = measurement.mDopplerShiftUncertaintyInHz;
+ mMultipathIndicator = measurement.mMultipathIndicator;
+ mSnrInDb = measurement.mSnrInDb;
+ mElevationInDeg = measurement.mElevationInDeg;
+ mElevationUncertaintyInDeg = measurement.mElevationUncertaintyInDeg;
+ mAzimuthInDeg = measurement.mAzimuthInDeg;
+ mAzimuthUncertaintyInDeg = measurement.mAzimuthUncertaintyInDeg;
+ mUsedInFix = measurement.mUsedInFix;
+ }
+
+ /**
+ * Resets all the contents to its original state.
+ */
+ public void reset() {
+ initialize();
+ }
+
+ /**
+ * Gets the Pseudo-random number (PRN).
+ * Range: [1, 32]
+ */
+ public byte getPrn() {
+ return mPrn;
+ }
+
+ /**
+ * Sets the Pseud-random number (PRN).
+ */
+ public void setPrn(byte value) {
+ mPrn = value;
+ }
+
+ /**
+ * Gets the time offset at which the measurement was taken in nanoseconds.
+ * The reference receiver's time is specified by {@link GpsClock#getTimeInNs()} and should be
+ * interpreted in the same way as indicated by {@link GpsClock#getType()}.
+ *
+ * The sign of this value is given by the following equation:
+ * measurement time = time_ns + time_offset_ns
+ *
+ * The value provides an individual time-stamp for the measurement, and allows sub-nanosecond
+ * accuracy.
+ */
+ public double getTimeOffsetInNs() {
+ return mTimeOffsetInNs;
+ }
+
+ /**
+ * Sets the time offset at which the measurement was taken in nanoseconds.
+ */
+ public void setTimeOffsetInNs(double value) {
+ mTimeOffsetInNs = value;
+ }
+
+ /**
+ * Gets per-satellite sync state.
+ * It represents the current sync state for the associated satellite.
+ *
+ * This value helps interpret {@link #getReceivedGpsTowInNs()}.
+ */
+ public short getState() {
+ return mState;
+ }
+
+ /**
+ * Sets the sync state.
+ */
+ public void setState(short value) {
+ mState = value;
+ }
+
+ /**
+ * Gets a string representation of the 'sync state'.
+ * For internal and logging use only.
+ */
+ private String getStateString() {
+ if (mState == STATE_UNKNOWN) {
+ return "Unknown";
+ }
+ StringBuilder builder = new StringBuilder();
+ if ((mState & STATE_CODE_LOCK) == STATE_CODE_LOCK) {
+ builder.append("CodeLock|");
+ }
+ if ((mState & STATE_BIT_SYNC) == STATE_BIT_SYNC) {
+ builder.append("BitSync|");
+ }
+ if ((mState & STATE_SUBFRAME_SYNC) == STATE_SUBFRAME_SYNC) {
+ builder.append("SubframeSync|");
+ }
+ if ((mState & STATE_TOW_DECODED) == STATE_TOW_DECODED) {
+ builder.append("TowDecoded|");
+ }
+ if ((mState & STATE_MSEC_AMBIGUOUS) == STATE_MSEC_AMBIGUOUS) {
+ builder.append("MsecAmbiguous");
+ }
+ int remainingStates = mState & ~STATE_ALL;
+ if (remainingStates > 0) {
+ builder.append("Other(");
+ builder.append(Integer.toBinaryString(remainingStates));
+ builder.append(")|");
+ }
+ builder.deleteCharAt(builder.length() - 1);
+ return builder.toString();
+ }
+
+ /**
+ * Gets the received GPS Time-of-Week at the measurement time, in nanoseconds.
+ * The value is relative to the beginning of the current GPS week.
+ *
+ * Given {@link #getState()} of the GPS receiver, the range of this field can be:
+ * Searching : [ 0 ] : {@link #STATE_UNKNOWN} is set
+ * Ranging code lock : [ 0 1 ms ] : {@link #STATE_CODE_LOCK} is set
+ * Bit sync : [ 0 20 ms ] : {@link #STATE_BIT_SYNC} is set
+ * Subframe sync : [ 0 6 ms ] : {@link #STATE_SUBFRAME_SYNC} is set
+ * TOW decoded : [ 0 1 week ] : {@link #STATE_TOW_DECODED} is set
+ */
+ public long getReceivedGpsTowInNs() {
+ return mReceivedGpsTowInNs;
+ }
+
+ /**
+ * Sets the received GPS time-of-week in nanoseconds.
+ */
+ public void setReceivedGpsTowInNs(long value) {
+ mReceivedGpsTowInNs = value;
+ }
+
+ /**
+ * Gets the received GPS time-of-week's uncertainty (1-Sigma) in nanoseconds.
+ */
+ public long getReceivedGpsTowUncertaintyInNs() {
+ return mReceivedGpsTowUncertaintyInNs;
+ }
+
+ /**
+ * Sets the received GPS time-of-week's uncertainty (1-Sigma) in nanoseconds.
+ */
+ public void setReceivedGpsTowUncertaintyInNs(long value) {
+ mReceivedGpsTowUncertaintyInNs = value;
+ }
+
+ /**
+ * Gets the Carrier-to-noise density in dB-Hz.
+ * Range: [0, 63].
+ *
+ * The value contains the measured C/N0 for the signal at the antenna input.
+ */
+ public double getCn0InDbHz() {
+ return mCn0InDbHz;
+ }
+
+ /**
+ * Sets the carrier-to-noise density in dB-Hz.
+ */
+ public void setCn0InDbHz(double value) {
+ mCn0InDbHz = value;
+ }
+
+ /**
+ * Gets the Pseudorange rate at the timestamp in m/s.
+ * The reported value includes {@link #getPseudorangeRateUncertaintyInMetersPerSec()}.
+ *
+ * The correction of a given Pseudorange Rate value includes corrections from receiver and
+ * satellite clock frequency errors.
+ * {@link #isPseudorangeRateCorrected()} identifies the type of value reported.
+ *
+ * A positive 'uncorrected' value indicates that the SV is moving away from the receiver.
+ * The sign of the 'uncorrected' Pseudorange Rate and its relation to the sign of
+ * {@link #getDopplerShiftInHz()} is given by the equation:
+ * pseudorange rate = -k * doppler shift (where k is a constant)
+ */
+ public double getPseudorangeRateInMetersPerSec() {
+ return mPseudorangeRateInMetersPerSec;
+ }
+
+ /**
+ * Sets the pseudorange rate at the timestamp in m/s.
+ */
+ public void setPseudorangeRateInMetersPerSec(double value) {
+ mPseudorangeRateInMetersPerSec = value;
+ }
+
+ /**
+ * See {@link #getPseudorangeRateInMetersPerSec()} for more details.
+ *
+ * @return {@code true} if {@link #getPseudorangeRateInMetersPerSec()} contains a corrected
+ * value, {@code false} if it contains an uncorrected value.
+ */
+ public boolean isPseudorangeRateCorrected() {
+ return !isFlagSet(GPS_MEASUREMENT_HAS_UNCORRECTED_PSEUDORANGE_RATE);
+ }
+
+ /**
+ * Gets the pseudorange's rate uncertainty (1-Sigma) in m/s.
+ * The uncertainty is represented as an absolute (single sided) value.
+ */
+ public double getPseudorangeRateUncertaintyInMetersPerSec() {
+ return mPseudorangeRateUncertaintyInMetersPerSec;
+ }
+
+ /**
+ * Sets the pseudorange's rate uncertainty (1-Sigma) in m/s.
+ */
+ public void setPseudorangeRateUncertaintyInMetersPerSec(double value) {
+ mPseudorangeRateUncertaintyInMetersPerSec = value;
+ }
+
+ /**
+ * Gets 'Accumulated Delta Range' state.
+ * It indicates whether {@link #getAccumulatedDeltaRangeInMeters()} is reset or there is a
+ * cycle slip (indicating 'loss of lock').
+ */
+ public short getAccumulatedDeltaRangeState() {
+ return mAccumulatedDeltaRangeState;
+ }
+
+ /**
+ * Sets the 'Accumulated Delta Range' state.
+ */
+ public void setAccumulatedDeltaRangeState(short value) {
+ mAccumulatedDeltaRangeState = value;
+ }
+
+ /**
+ * Gets a string representation of the 'Accumulated Delta Range state'.
+ * For internal and logging use only.
+ */
+ private String getAccumulatedDeltaRangeStateString() {
+ if (mAccumulatedDeltaRangeState == ADR_STATE_UNKNOWN) {
+ return "Unknown";
+ }
+ StringBuilder builder = new StringBuilder();
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_VALID) == ADR_STATE_VALID) {
+ builder.append("Valid|");
+ }
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_RESET) == ADR_STATE_RESET) {
+ builder.append("Reset|");
+ }
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_CYCLE_SLIP) == ADR_STATE_CYCLE_SLIP) {
+ builder.append("CycleSlip|");
+ }
+ int remainingStates = mAccumulatedDeltaRangeState & ~ADR_ALL;
+ if (remainingStates > 0) {
+ builder.append("Other(");
+ builder.append(Integer.toBinaryString(remainingStates));
+ builder.append(")|");
+ }
+ builder.deleteCharAt(builder.length() - 1);
+ return builder.toString();
+ }
+
+ /**
+ * Gets the accumulated delta range since the last channel reset, in meters.
+ * The reported value includes {@link #getAccumulatedDeltaRangeUncertaintyInMeters()}.
+ *
+ * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+ *
+ * A positive value indicates that the SV is moving away from the receiver.
+ * The sign of {@link #getAccumulatedDeltaRangeInMeters()} and its relation to the sign of
+ * {@link #getCarrierPhase()} is given by the equation:
+ * accumulated delta range = -k * carrier phase (where k is a constant)
+ */
+ public double getAccumulatedDeltaRangeInMeters() {
+ return mAccumulatedDeltaRangeInMeters;
+ }
+
+ /**
+ * Sets the accumulated delta range in meters.
+ */
+ public void setAccumulatedDeltaRangeInMeters(double value) {
+ mAccumulatedDeltaRangeInMeters = value;
+ }
+
+ /**
+ * Gets the accumulated delta range's uncertainty (1-Sigma) in meters.
+ * The uncertainty is represented as an absolute (single sided) value.
+ *
+ * The status of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+ */
+ public double getAccumulatedDeltaRangeUncertaintyInMeters() {
+ return mAccumulatedDeltaRangeUncertaintyInMeters;
+ }
+
+ /**
+ * Sets the accumulated delta range's uncertainty (1-sigma) in meters.
+ *
+ * The status of the value is represented by {@link #getAccumulatedDeltaRangeState()}.
+ */
+ public void setAccumulatedDeltaRangeUncertaintyInMeters(double value) {
+ mAccumulatedDeltaRangeUncertaintyInMeters = value;
+ }
+
+ /**
+ * Returns true if {@link #getPseudorangeInMeters()} is available, false otherwise.
+ */
+ public boolean hasPseudorangeInMeters() {
+ return isFlagSet(HAS_PSEUDORANGE);
+ }
+
+ /**
+ * Gets the best derived pseudorange by the chipset, in meters.
+ * The reported pseudorange includes {@link #getPseudorangeUncertaintyInMeters()}.
+ *
+ * The value is only available if {@link #hasPseudorangeInMeters()} is true.
+ */
+ public double getPseudorangeInMeters() {
+ return mPseudorangeInMeters;
+ }
+
+ /**
+ * Sets the Pseudo-range in meters.
+ */
+ public void setPseudorangeInMeters(double value) {
+ setFlag(HAS_PSEUDORANGE);
+ mPseudorangeInMeters = value;
+ }
+
+ /**
+ * Resets the Pseudo-range in meters.
+ */
+ public void resetPseudorangeInMeters() {
+ resetFlag(HAS_PSEUDORANGE);
+ mPseudorangeInMeters = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getPseudorangeUncertaintyInMeters()} is available, false otherwise.
+ */
+ public boolean hasPseudorangeUncertaintyInMeters() {
+ return isFlagSet(HAS_PSEUDORANGE_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the pseudorange's uncertainty (1-Sigma) in meters.
+ * The value contains the 'pseudorange' and 'clock' uncertainty in it.
+ * The uncertainty is represented as an absolute (single sided) value.
+ *
+ * The value is only available if {@link #hasPseudorangeUncertaintyInMeters()} is true.
+ */
+ public double getPseudorangeUncertaintyInMeters() {
+ return mPseudorangeUncertaintyInMeters;
+ }
+
+ /**
+ * Sets the pseudo-range's uncertainty (1-Sigma) in meters.
+ */
+ public void setPseudorangeUncertaintyInMeters(double value) {
+ setFlag(HAS_PSEUDORANGE_UNCERTAINTY);
+ mPseudorangeUncertaintyInMeters = value;
+ }
+
+ /**
+ * Resets the pseudo-range's uncertainty (1-Sigma) in meters.
+ */
+ public void resetPseudorangeUncertaintyInMeters() {
+ resetFlag(HAS_PSEUDORANGE_UNCERTAINTY);
+ mPseudorangeUncertaintyInMeters = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getCodePhaseInChips()} is available, false otherwise.
+ */
+ public boolean hasCodePhaseInChips() {
+ return isFlagSet(HAS_CODE_PHASE);
+ }
+
+ /**
+ * Gets the fraction of the current C/A code cycle.
+ * Range: [0, 1023]
+ * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}.
+ * The reported code-phase includes {@link #getCodePhaseUncertaintyInChips()}.
+ *
+ * The value is only available if {@link #hasCodePhaseInChips()} is true.
+ */
+ public double getCodePhaseInChips() {
+ return mCodePhaseInChips;
+ }
+
+ /**
+ * Sets the Code-phase in chips.
+ */
+ public void setCodePhaseInChips(double value) {
+ setFlag(HAS_CODE_PHASE);
+ mCodePhaseInChips = value;
+ }
+
+ /**
+ * Resets the Code-phase in chips.
+ */
+ public void resetCodePhaseInChips() {
+ resetFlag(HAS_CODE_PHASE);
+ mCodePhaseInChips = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getCodePhaseUncertaintyInChips()} is available, false otherwise.
+ */
+ public boolean hasCodePhaseUncertaintyInChips() {
+ return isFlagSet(HAS_CODE_PHASE_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the code-phase's uncertainty (1-Sigma) as a fraction of chips.
+ * The uncertainty is represented as an absolute (single sided) value.
+ *
+ * The value is only available if {@link #hasCodePhaseUncertaintyInChips()} is true.
+ */
+ public double getCodePhaseUncertaintyInChips() {
+ return mCodePhaseUncertaintyInChips;
+ }
+
+ /**
+ * Sets the Code-phase's uncertainty (1-Sigma) in fractions of chips.
+ */
+ public void setCodePhaseUncertaintyInChips(double value) {
+ setFlag(HAS_CODE_PHASE_UNCERTAINTY);
+ mCodePhaseUncertaintyInChips = value;
+ }
+
+ /**
+ * Resets the Code-phase's uncertainty (1-Sigma) in fractions of chips.
+ */
+ public void resetCodePhaseUncertaintyInChips() {
+ resetFlag(HAS_CODE_PHASE_UNCERTAINTY);
+ mCodePhaseUncertaintyInChips = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getCarrierFrequencyInHz()} is available, false otherwise.
+ */
+ public boolean hasCarrierFrequencyInHz() {
+ return isFlagSet(HAS_CARRIER_FREQUENCY);
+ }
+
+ /**
+ * Gets the carrier frequency at which codes and messages are modulated, it can be L1 or L2.
+ * If the field is not set, the carrier frequency corresponds to L1.
+ *
+ * The value is only available if {@link #hasCarrierFrequencyInHz()} is true.
+ */
+ public float getCarrierFrequencyInHz() {
+ return mCarrierFrequencyInHz;
+ }
+
+ /**
+ * Sets the Carrier frequency (L1 or L2) in Hz.
+ */
+ public void setCarrierFrequencyInHz(float carrierFrequencyInHz) {
+ setFlag(HAS_CARRIER_FREQUENCY);
+ mCarrierFrequencyInHz = carrierFrequencyInHz;
+ }
+
+ /**
+ * Resets the Carrier frequency (L1 or L2) in Hz.
+ */
+ public void resetCarrierFrequencyInHz() {
+ resetFlag(HAS_CARRIER_FREQUENCY);
+ mCarrierFrequencyInHz = Float.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getCarrierCycles()} is available, false otherwise.
+ */
+ public boolean hasCarrierCycles() {
+ return isFlagSet(HAS_CARRIER_CYCLES);
+ }
+
+ /**
+ * The number of full carrier cycles between the satellite and the receiver.
+ * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}.
+ *
+ * The value is only available if {@link #hasCarrierCycles()} is true.
+ */
+ public long getCarrierCycles() {
+ return mCarrierCycles;
+ }
+
+ /**
+ * Sets the number of full carrier cycles between the satellite and the receiver.
+ */
+ public void setCarrierCycles(long value) {
+ setFlag(HAS_CARRIER_CYCLES);
+ mCarrierCycles = value;
+ }
+
+ /**
+ * Resets the number of full carrier cycles between the satellite and the receiver.
+ */
+ public void resetCarrierCycles() {
+ resetFlag(HAS_CARRIER_CYCLES);
+ mCarrierCycles = Long.MIN_VALUE;
+ }
+
+ /**
+ * Returns true if {@link #getCarrierPhase()} is available, false otherwise.
+ */
+ public boolean hasCarrierPhase() {
+ return isFlagSet(HAS_CARRIER_PHASE);
+ }
+
+ /**
+ * Gets the RF phase detected by the receiver.
+ * Range: [0.0, 1.0].
+ * This is usually the fractional part of the complete carrier phase measurement.
+ *
+ * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}.
+ * The reported carrier-phase includes {@link #getCarrierPhaseUncertainty()}.
+ *
+ * The value is only available if {@link #hasCarrierPhase()} is true.
+ */
+ public double getCarrierPhase() {
+ return mCarrierPhase;
+ }
+
+ /**
+ * Sets the RF phase detected by the receiver.
+ */
+ public void setCarrierPhase(double value) {
+ setFlag(HAS_CARRIER_PHASE);
+ mCarrierPhase = value;
+ }
+
+ /**
+ * Resets the RF phase detected by the receiver.
+ */
+ public void resetCarrierPhase() {
+ resetFlag(HAS_CARRIER_PHASE);
+ mCarrierPhase = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getCarrierPhaseUncertainty()} is available, false otherwise.
+ */
+ public boolean hasCarrierPhaseUncertainty() {
+ return isFlagSet(HAS_CARRIER_PHASE_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the carrier-phase's uncertainty (1-Sigma).
+ * The uncertainty is represented as an absolute (single sided) value.
+ *
+ * The value is only available if {@link #hasCarrierPhaseUncertainty()} is true.
+ */
+ public double getCarrierPhaseUncertainty() {
+ return mCarrierPhaseUncertainty;
+ }
+
+ /**
+ * Sets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ */
+ public void setCarrierPhaseUncertainty(double value) {
+ setFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
+ mCarrierPhaseUncertainty = value;
+ }
+
+ /**
+ * Resets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ */
+ public void resetCarrierPhaseUncertainty() {
+ resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
+ mCarrierPhaseUncertainty = Double.NaN;
+ }
+
+ /**
+ * Gets a value indicating the 'loss of lock' state of the event.
+ */
+ public byte getLossOfLock() {
+ return mLossOfLock;
+ }
+
+ /**
+ * Sets the 'loss of lock' status.
+ */
+ public void setLossOfLock(byte value) {
+ mLossOfLock = value;
+ }
+
+ /**
+ * Gets a string representation of the 'loss of lock'.
+ * For internal and logging use only.
+ */
+ private String getLossOfLockString() {
+ switch (mLossOfLock) {
+ case LOSS_OF_LOCK_UNKNOWN:
+ return "Unknown";
+ case LOSS_OF_LOCK_OK:
+ return "Ok";
+ case LOSS_OF_LOCK_CYCLE_SLIP:
+ return "CycleSlip";
+ default:
+ return "<Invalid:" + mLossOfLock + ">";
+ }
+ }
+
+ /**
+ * Returns true if {@link #getBitNumber()} is available, false otherwise.
+ */
+ public boolean hasBitNumber() {
+ return isFlagSet(HAS_BIT_NUMBER);
+ }
+
+ /**
+ * Gets the number of GPS bits transmitted since Sat-Sun midnight (GPS week).
+ *
+ * The value is only available if {@link #hasBitNumber()} is true.
+ */
+ public int getBitNumber() {
+ return mBitNumber;
+ }
+
+ /**
+ * Sets the bit number within the broadcast frame.
+ */
+ public void setBitNumber(int bitNumber) {
+ setFlag(HAS_BIT_NUMBER);
+ mBitNumber = bitNumber;
+ }
+
+ /**
+ * Resets the bit number within the broadcast frame.
+ */
+ public void resetBitNumber() {
+ resetFlag(HAS_BIT_NUMBER);
+ mBitNumber = Integer.MIN_VALUE;
+ }
+
+ /**
+ * Returns true if {@link #getTimeFromLastBitInMs()} is available, false otherwise.
+ */
+ public boolean hasTimeFromLastBitInMs() {
+ return isFlagSet(HAS_TIME_FROM_LAST_BIT);
+ }
+
+ /**
+ * Gets the elapsed time since the last received bit in milliseconds.
+ * Range: [0, 20].
+ *
+ * The value is only available if {@link #hasTimeFromLastBitInMs()} is true.
+ */
+ public short getTimeFromLastBitInMs() {
+ return mTimeFromLastBitInMs;
+ }
+
+ /**
+ * Sets the elapsed time since the last received bit in milliseconds.
+ */
+ public void setTimeFromLastBitInMs(short value) {
+ setFlag(HAS_TIME_FROM_LAST_BIT);
+ mTimeFromLastBitInMs = value;
+ }
+
+ /**
+ * Resets the elapsed time since the last received bit in milliseconds.
+ */
+ public void resetTimeFromLastBitInMs() {
+ resetFlag(HAS_TIME_FROM_LAST_BIT);
+ mTimeFromLastBitInMs = Short.MIN_VALUE;
+ }
+
+ /**
+ * Returns true if {@link #getDopplerShiftInHz()} is available, false otherwise.
+ */
+ public boolean hasDopplerShiftInHz() {
+ return isFlagSet(HAS_DOPPLER_SHIFT);
+ }
+
+ /**
+ * Gets the Doppler Shift in Hz.
+ * A positive value indicates that the SV is moving toward the receiver.
+ *
+ * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}.
+ * The reported doppler shift includes {@link #getDopplerShiftUncertaintyInHz()}.
+ *
+ * The value is only available if {@link #hasDopplerShiftInHz()} is true.
+ */
+ public double getDopplerShiftInHz() {
+ return mDopplerShiftInHz;
+ }
+
+ /**
+ * Sets the Doppler shift in Hz.
+ */
+ public void setDopplerShiftInHz(double value) {
+ setFlag(HAS_DOPPLER_SHIFT);
+ mDopplerShiftInHz = value;
+ }
+
+ /**
+ * Resets the Doppler shift in Hz.
+ */
+ public void resetDopplerShiftInHz() {
+ resetFlag(HAS_DOPPLER_SHIFT);
+ mDopplerShiftInHz = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getDopplerShiftUncertaintyInHz()} is available, false otherwise.
+ */
+ public boolean hasDopplerShiftUncertaintyInHz() {
+ return isFlagSet(HAS_DOPPLER_SHIFT_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the Doppler's Shift uncertainty (1-Sigma) in Hz.
+ * The uncertainty is represented as an absolute (single sided) value.
+ *
+ * The value is only available if {@link #hasDopplerShiftUncertaintyInHz()} is true.
+ */
+ public double getDopplerShiftUncertaintyInHz() {
+ return mDopplerShiftUncertaintyInHz;
+ }
+
+ /**
+ * Sets the Doppler's shift uncertainty (1-Sigma) in Hz.
+ */
+ public void setDopplerShiftUncertaintyInHz(double value) {
+ setFlag(HAS_DOPPLER_SHIFT_UNCERTAINTY);
+ mDopplerShiftUncertaintyInHz = value;
+ }
+
+ /**
+ * Resets the Doppler's shift uncertainty (1-Sigma) in Hz.
+ */
+ public void resetDopplerShiftUncertaintyInHz() {
+ resetFlag(HAS_DOPPLER_SHIFT_UNCERTAINTY);
+ mDopplerShiftUncertaintyInHz = Double.NaN;
+ }
+
+ /**
+ * Gets a value indicating the 'multipath' state of the event.
+ */
+ public byte getMultipathIndicator() {
+ return mMultipathIndicator;
+ }
+
+ /**
+ * Sets the 'multi-path' indicator.
+ */
+ public void setMultipathIndicator(byte value) {
+ mMultipathIndicator = value;
+ }
+
+ /**
+ * Gets a string representation of the 'multi-path indicator'.
+ * For internal and logging use only.
+ */
+ private String getMultipathIndicatorString() {
+ switch(mMultipathIndicator) {
+ case MULTIPATH_INDICATOR_UNKNOWN:
+ return "Unknown";
+ case MULTIPATH_INDICATOR_DETECTED:
+ return "Detected";
+ case MULTIPATH_INDICATOR_NOT_USED:
+ return "NotUsed";
+ default:
+ return "<Invalid:" + mMultipathIndicator + ">";
+ }
+ }
+
+ /**
+ * Returns true if {@link #getSnrInDb()} is available, false otherwise.
+ */
+ public boolean hasSnrInDb() {
+ return isFlagSet(HAS_SNR);
+ }
+
+ /**
+ * Gets the Signal-to-Noise ratio (SNR) in dB.
+ *
+ * The value is only available if {@link #hasSnrInDb()} is true.
+ */
+ public double getSnrInDb() {
+ return mSnrInDb;
+ }
+
+ /**
+ * Sets the Signal-to-noise ratio (SNR) in dB.
+ */
+ public void setSnrInDb(double snrInDb) {
+ setFlag(HAS_SNR);
+ mSnrInDb = snrInDb;
+ }
+
+ /**
+ * Resets the Signal-to-noise ratio (SNR) in dB.
+ */
+ public void resetSnrInDb() {
+ resetFlag(HAS_SNR);
+ mSnrInDb = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getElevationInDeg()} is available, false otherwise.
+ */
+ public boolean hasElevationInDeg() {
+ return isFlagSet(HAS_ELEVATION);
+ }
+
+ /**
+ * Gets the Elevation in degrees.
+ * Range: [-90, 90]
+ * The reported elevation includes {@link #getElevationUncertaintyInDeg()}.
+ *
+ * The value is only available if {@link #hasElevationInDeg()} is true.
+ */
+ public double getElevationInDeg() {
+ return mElevationInDeg;
+ }
+
+ /**
+ * Sets the Elevation in degrees.
+ */
+ public void setElevationInDeg(double elevationInDeg) {
+ setFlag(HAS_ELEVATION);
+ mElevationInDeg = elevationInDeg;
+ }
+
+ /**
+ * Resets the Elevation in degrees.
+ */
+ public void resetElevationInDeg() {
+ resetFlag(HAS_ELEVATION);
+ mElevationInDeg = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getElevationUncertaintyInDeg()} is available, false otherwise.
+ */
+ public boolean hasElevationUncertaintyInDeg() {
+ return isFlagSet(HAS_ELEVATION_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the elevation's uncertainty (1-Sigma) in degrees.
+ * Range: [0, 90]
+ *
+ * The uncertainty is represented as an absolute (single sided) value.
+ *
+ * The value is only available if {@link #hasElevationUncertaintyInDeg()} is true.
+ */
+ public double getElevationUncertaintyInDeg() {
+ return mElevationUncertaintyInDeg;
+ }
+
+ /**
+ * Sets the elevation's uncertainty (1-Sigma) in degrees.
+ */
+ public void setElevationUncertaintyInDeg(double value) {
+ setFlag(HAS_ELEVATION_UNCERTAINTY);
+ mElevationUncertaintyInDeg = value;
+ }
+
+ /**
+ * Resets the elevation's uncertainty (1-Sigma) in degrees.
+ */
+ public void resetElevationUncertaintyInDeg() {
+ resetFlag(HAS_ELEVATION_UNCERTAINTY);
+ mElevationUncertaintyInDeg = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getAzimuthInDeg()} is available, false otherwise.
+ */
+ public boolean hasAzimuthInDeg() {
+ return isFlagSet(HAS_AZIMUTH);
+ }
+
+ /**
+ * Gets the azimuth in degrees.
+ * Range: [0, 360).
+ *
+ * The reported azimuth includes {@link #getAzimuthUncertaintyInDeg()}.
+ *
+ * The value is only available if {@link #hasAzimuthInDeg()} is true.
+ */
+ public double getAzimuthInDeg() {
+ return mAzimuthInDeg;
+ }
+
+ /**
+ * Sets the Azimuth in degrees.
+ */
+ public void setAzimuthInDeg(double value) {
+ setFlag(HAS_AZIMUTH);
+ mAzimuthInDeg = value;
+ }
+
+ /**
+ * Resets the Azimuth in degrees.
+ */
+ public void resetAzimuthInDeg() {
+ resetFlag(HAS_AZIMUTH);
+ mAzimuthInDeg = Double.NaN;
+ }
+
+ /**
+ * Returns true if {@link #getAzimuthUncertaintyInDeg()} is available, false otherwise.
+ */
+ public boolean hasAzimuthUncertaintyInDeg() {
+ return isFlagSet(HAS_AZIMUTH_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the azimuth's uncertainty (1-Sigma) in degrees.
+ * Range: [0, 180].
+ *
+ * The uncertainty is represented as an absolute (single sided) value.
+ *
+ * The value is only available if {@link #hasAzimuthUncertaintyInDeg()} is true.
+ */
+ public double getAzimuthUncertaintyInDeg() {
+ return mAzimuthUncertaintyInDeg;
+ }
+
+ /**
+ * Sets the Azimuth's uncertainty (1-Sigma) in degrees.
+ */
+ public void setAzimuthUncertaintyInDeg(double value) {
+ setFlag(HAS_AZIMUTH_UNCERTAINTY);
+ mAzimuthUncertaintyInDeg = value;
+ }
+
+ /**
+ * Resets the Azimuth's uncertainty (1-Sigma) in degrees.
+ */
+ public void resetAzimuthUncertaintyInDeg() {
+ resetFlag(HAS_AZIMUTH_UNCERTAINTY);
+ mAzimuthUncertaintyInDeg = Double.NaN;
+ }
+
+ /**
+ * Gets a flag indicating whether the GPS represented by the measurement was used for computing
+ * the most recent fix.
+ *
+ * @return A non-null value if the data is available, null otherwise.
+ */
+ public boolean isUsedInFix() {
+ return mUsedInFix;
+ }
+
+ /**
+ * Sets the Used-in-Fix flag.
+ */
+ public void setUsedInFix(boolean value) {
+ mUsedInFix = value;
+ }
+
+ public static final Creator<GpsMeasurement> CREATOR = new Creator<GpsMeasurement>() {
+ @Override
+ public GpsMeasurement createFromParcel(Parcel parcel) {
+ GpsMeasurement gpsMeasurement = new GpsMeasurement();
+
+ gpsMeasurement.mFlags = parcel.readInt();
+ gpsMeasurement.mPrn = parcel.readByte();
+ gpsMeasurement.mTimeOffsetInNs = parcel.readDouble();
+ gpsMeasurement.mState = (short) parcel.readInt();
+ gpsMeasurement.mReceivedGpsTowInNs = parcel.readLong();
+ gpsMeasurement.mReceivedGpsTowUncertaintyInNs = parcel.readLong();
+ gpsMeasurement.mCn0InDbHz = parcel.readDouble();
+ gpsMeasurement.mPseudorangeRateInMetersPerSec = parcel.readDouble();
+ gpsMeasurement.mPseudorangeRateUncertaintyInMetersPerSec = parcel.readDouble();
+ gpsMeasurement.mAccumulatedDeltaRangeState = (short) parcel.readInt();
+ gpsMeasurement.mAccumulatedDeltaRangeInMeters = parcel.readDouble();
+ gpsMeasurement.mAccumulatedDeltaRangeUncertaintyInMeters = parcel.readDouble();
+ gpsMeasurement.mPseudorangeInMeters = parcel.readDouble();
+ gpsMeasurement.mPseudorangeUncertaintyInMeters = parcel.readDouble();
+ gpsMeasurement.mCodePhaseInChips = parcel.readDouble();
+ gpsMeasurement.mCodePhaseUncertaintyInChips = parcel.readDouble();
+ gpsMeasurement.mCarrierFrequencyInHz = parcel.readFloat();
+ gpsMeasurement.mCarrierCycles = parcel.readLong();
+ gpsMeasurement.mCarrierPhase = parcel.readDouble();
+ gpsMeasurement.mCarrierPhaseUncertainty = parcel.readDouble();
+ gpsMeasurement.mLossOfLock = parcel.readByte();
+ gpsMeasurement.mBitNumber = parcel.readInt();
+ gpsMeasurement.mTimeFromLastBitInMs = (short) parcel.readInt();
+ gpsMeasurement.mDopplerShiftInHz = parcel.readDouble();
+ gpsMeasurement.mDopplerShiftUncertaintyInHz = parcel.readDouble();
+ gpsMeasurement.mMultipathIndicator = parcel.readByte();
+ gpsMeasurement.mSnrInDb = parcel.readDouble();
+ gpsMeasurement.mElevationInDeg = parcel.readDouble();
+ gpsMeasurement.mElevationUncertaintyInDeg = parcel.readDouble();
+ gpsMeasurement.mAzimuthInDeg = parcel.readDouble();
+ gpsMeasurement.mAzimuthUncertaintyInDeg = parcel.readDouble();
+ gpsMeasurement.mUsedInFix = parcel.readInt() != 0;
+
+ return gpsMeasurement;
+ }
+
+ @Override
+ public GpsMeasurement[] newArray(int i) {
+ return new GpsMeasurement[i];
+ }
+ };
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mFlags);
+ parcel.writeByte(mPrn);
+ parcel.writeDouble(mTimeOffsetInNs);
+ parcel.writeInt(mState);
+ parcel.writeLong(mReceivedGpsTowInNs);
+ parcel.writeLong(mReceivedGpsTowUncertaintyInNs);
+ parcel.writeDouble(mCn0InDbHz);
+ parcel.writeDouble(mPseudorangeRateInMetersPerSec);
+ parcel.writeDouble(mPseudorangeRateUncertaintyInMetersPerSec);
+ parcel.writeInt(mAccumulatedDeltaRangeState);
+ parcel.writeDouble(mAccumulatedDeltaRangeInMeters);
+ parcel.writeDouble(mAccumulatedDeltaRangeUncertaintyInMeters);
+ parcel.writeDouble(mPseudorangeInMeters);
+ parcel.writeDouble(mPseudorangeUncertaintyInMeters);
+ parcel.writeDouble(mCodePhaseInChips);
+ parcel.writeDouble(mCodePhaseUncertaintyInChips);
+ parcel.writeFloat(mCarrierFrequencyInHz);
+ parcel.writeLong(mCarrierCycles);
+ parcel.writeDouble(mCarrierPhase);
+ parcel.writeDouble(mCarrierPhaseUncertainty);
+ parcel.writeByte(mLossOfLock);
+ parcel.writeInt(mBitNumber);
+ parcel.writeInt(mTimeFromLastBitInMs);
+ parcel.writeDouble(mDopplerShiftInHz);
+ parcel.writeDouble(mDopplerShiftUncertaintyInHz);
+ parcel.writeByte(mMultipathIndicator);
+ parcel.writeDouble(mSnrInDb);
+ parcel.writeDouble(mElevationInDeg);
+ parcel.writeDouble(mElevationUncertaintyInDeg);
+ parcel.writeDouble(mAzimuthInDeg);
+ parcel.writeDouble(mAzimuthUncertaintyInDeg);
+ parcel.writeInt(mUsedInFix ? 1 : 0);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ final String format = " %-29s = %s\n";
+ final String formatWithUncertainty = " %-29s = %-25s %-40s = %s\n";
+ StringBuilder builder = new StringBuilder("GpsMeasurement:\n");
+
+ builder.append(String.format(format, "Prn", mPrn));
+
+ builder.append(String.format(format, "TimeOffsetInNs", mTimeOffsetInNs));
+
+ builder.append(String.format(format, "State", getStateString()));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "ReceivedGpsTowInNs",
+ mReceivedGpsTowInNs,
+ "ReceivedGpsTowUncertaintyInNs",
+ mReceivedGpsTowUncertaintyInNs));
+
+ builder.append(String.format(format, "Cn0InDbHz", mCn0InDbHz));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "PseudorangeRateInMetersPerSec",
+ mPseudorangeRateInMetersPerSec,
+ "PseudorangeRateUncertaintyInMetersPerSec",
+ mPseudorangeRateUncertaintyInMetersPerSec));
+ builder.append(String.format(
+ format,
+ "PseudorangeRateIsCorrected",
+ isPseudorangeRateCorrected()));
+
+ builder.append(String.format(
+ format,
+ "AccumulatedDeltaRangeState",
+ getAccumulatedDeltaRangeStateString()));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "AccumulatedDeltaRangeInMeters",
+ mAccumulatedDeltaRangeInMeters,
+ "AccumulatedDeltaRangeUncertaintyInMeters",
+ mAccumulatedDeltaRangeUncertaintyInMeters));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "PseudorangeInMeters",
+ hasPseudorangeInMeters() ? mPseudorangeInMeters : null,
+ "PseudorangeUncertaintyInMeters",
+ hasPseudorangeUncertaintyInMeters() ? mPseudorangeUncertaintyInMeters : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "CodePhaseInChips",
+ hasCodePhaseInChips() ? mCodePhaseInChips : null,
+ "CodePhaseUncertaintyInChips",
+ hasCodePhaseUncertaintyInChips() ? mCodePhaseUncertaintyInChips : null));
+
+ builder.append(String.format(
+ format,
+ "CarrierFrequencyInHz",
+ hasCarrierFrequencyInHz() ? mCarrierFrequencyInHz : null));
+
+ builder.append(String.format(
+ format,
+ "CarrierCycles",
+ hasCarrierCycles() ? mCarrierCycles : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "CarrierPhase",
+ hasCarrierPhase() ? mCarrierPhase : null,
+ "CarrierPhaseUncertainty",
+ hasCarrierPhaseUncertainty() ? mCarrierPhaseUncertainty : null));
+
+ builder.append(String.format(format, "LossOfLock", getLossOfLockString()));
+
+ builder.append(String.format(
+ format,
+ "BitNumber",
+ hasBitNumber() ? mBitNumber : null));
+
+ builder.append(String.format(
+ format,
+ "TimeFromLastBitInMs",
+ hasTimeFromLastBitInMs() ? mTimeFromLastBitInMs : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "DopplerShiftInHz",
+ hasDopplerShiftInHz() ? mDopplerShiftInHz : null,
+ "DopplerShiftUncertaintyInHz",
+ hasDopplerShiftUncertaintyInHz() ? mDopplerShiftUncertaintyInHz : null));
+
+ builder.append(String.format(format, "MultipathIndicator", getMultipathIndicatorString()));
+
+ builder.append(String.format(
+ format,
+ "SnrInDb",
+ hasSnrInDb() ? mSnrInDb : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "ElevationInDeg",
+ hasElevationInDeg() ? mElevationInDeg : null,
+ "ElevationUncertaintyInDeg",
+ hasElevationUncertaintyInDeg() ? mElevationUncertaintyInDeg : null));
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "AzimuthInDeg",
+ hasAzimuthInDeg() ? mAzimuthInDeg : null,
+ "AzimuthUncertaintyInDeg",
+ hasAzimuthUncertaintyInDeg() ? mAzimuthUncertaintyInDeg : null));
+
+ builder.append(String.format(format, "UsedInFix", mUsedInFix));
+
+ return builder.toString();
+ }
+
+ private void initialize() {
+ mFlags = HAS_NO_FLAGS;
+ setPrn(Byte.MIN_VALUE);
+ setTimeOffsetInNs(Long.MIN_VALUE);
+ setState(STATE_UNKNOWN);
+ setReceivedGpsTowInNs(Long.MIN_VALUE);
+ setReceivedGpsTowUncertaintyInNs(Long.MAX_VALUE);
+ setCn0InDbHz(Double.MIN_VALUE);
+ setPseudorangeRateInMetersPerSec(Double.MIN_VALUE);
+ setPseudorangeRateUncertaintyInMetersPerSec(Double.MIN_VALUE);
+ setAccumulatedDeltaRangeState(ADR_STATE_UNKNOWN);
+ setAccumulatedDeltaRangeInMeters(Double.MIN_VALUE);
+ setAccumulatedDeltaRangeUncertaintyInMeters(Double.MIN_VALUE);
+ resetPseudorangeInMeters();
+ resetPseudorangeUncertaintyInMeters();
+ resetCodePhaseInChips();
+ resetCodePhaseUncertaintyInChips();
+ resetCarrierFrequencyInHz();
+ resetCarrierCycles();
+ resetCarrierPhase();
+ resetCarrierPhaseUncertainty();
+ setLossOfLock(LOSS_OF_LOCK_UNKNOWN);
+ resetBitNumber();
+ resetTimeFromLastBitInMs();
+ resetDopplerShiftInHz();
+ resetDopplerShiftUncertaintyInHz();
+ setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN);
+ resetSnrInDb();
+ resetElevationInDeg();
+ resetElevationUncertaintyInDeg();
+ resetAzimuthInDeg();
+ resetAzimuthUncertaintyInDeg();
+ setUsedInFix(false);
+ }
+
+ private void setFlag(int flag) {
+ mFlags |= flag;
+ }
+
+ private void resetFlag(int flag) {
+ mFlags &= ~flag;
+ }
+
+ private boolean isFlagSet(int flag) {
+ return (mFlags & flag) == flag;
+ }
+}
diff --git a/android/location/GpsMeasurementsEvent.java b/android/location/GpsMeasurementsEvent.java
new file mode 100644
index 00000000..13668733
--- /dev/null
+++ b/android/location/GpsMeasurementsEvent.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 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 android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.InvalidParameterException;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+
+/**
+ * A class implementing a container for data associated with a measurement event.
+ * Events are delivered to registered instances of {@link Listener}.
+ *
+ * @hide
+ */
+@SystemApi
+public class GpsMeasurementsEvent implements Parcelable {
+
+ /**
+ * The system does not support tracking of GPS Measurements. This status will not change in the
+ * future.
+ */
+ public static final int STATUS_NOT_SUPPORTED = 0;
+
+ /**
+ * GPS Measurements are successfully being tracked, it will receive updates once they are
+ * available.
+ */
+ public static final int STATUS_READY = 1;
+
+ /**
+ * GPS provider or Location is disabled, updates will not be received until they are enabled.
+ */
+ public static final int STATUS_GPS_LOCATION_DISABLED = 2;
+
+ private final GpsClock mClock;
+ private final Collection<GpsMeasurement> mReadOnlyMeasurements;
+
+ /**
+ * Used for receiving GPS satellite measurements from the GPS engine.
+ * Each measurement contains raw and computed data identifying a satellite.
+ * You can implement this interface and call {@link LocationManager#addGpsMeasurementListener}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface Listener {
+
+ /**
+ * Returns the latest collected GPS Measurements.
+ */
+ void onGpsMeasurementsReceived(GpsMeasurementsEvent eventArgs);
+
+ /**
+ * Returns the latest status of the GPS Measurements sub-system.
+ */
+ void onStatusChanged(int status);
+ }
+
+ public GpsMeasurementsEvent(GpsClock clock, GpsMeasurement[] measurements) {
+ if (clock == null) {
+ throw new InvalidParameterException("Parameter 'clock' must not be null.");
+ }
+ if (measurements == null || measurements.length == 0) {
+ throw new InvalidParameterException(
+ "Parameter 'measurements' must not be null or empty.");
+ }
+
+ mClock = clock;
+ Collection<GpsMeasurement> measurementCollection = Arrays.asList(measurements);
+ mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection);
+ }
+
+ @NonNull
+ public GpsClock getClock() {
+ return mClock;
+ }
+
+ /**
+ * Gets a read-only collection of measurements associated with the current event.
+ */
+ @NonNull
+ public Collection<GpsMeasurement> getMeasurements() {
+ return mReadOnlyMeasurements;
+ }
+
+ public static final Creator<GpsMeasurementsEvent> CREATOR =
+ new Creator<GpsMeasurementsEvent>() {
+ @Override
+ public GpsMeasurementsEvent createFromParcel(Parcel in) {
+ ClassLoader classLoader = getClass().getClassLoader();
+
+ GpsClock clock = in.readParcelable(classLoader);
+
+ int measurementsLength = in.readInt();
+ GpsMeasurement[] measurementsArray = new GpsMeasurement[measurementsLength];
+ in.readTypedArray(measurementsArray, GpsMeasurement.CREATOR);
+
+ return new GpsMeasurementsEvent(clock, measurementsArray);
+ }
+
+ @Override
+ public GpsMeasurementsEvent[] newArray(int size) {
+ return new GpsMeasurementsEvent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mClock, flags);
+
+ int measurementsCount = mReadOnlyMeasurements.size();
+ GpsMeasurement[] measurementsArray =
+ mReadOnlyMeasurements.toArray(new GpsMeasurement[measurementsCount]);
+ parcel.writeInt(measurementsArray.length);
+ parcel.writeTypedArray(measurementsArray, flags);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("[ GpsMeasurementsEvent:\n\n");
+
+ builder.append(mClock.toString());
+ builder.append("\n");
+
+ for (GpsMeasurement measurement : mReadOnlyMeasurements) {
+ builder.append(measurement.toString());
+ builder.append("\n");
+ }
+
+ builder.append("]");
+
+ return builder.toString();
+ }
+}
diff --git a/android/location/GpsNavigationMessage.java b/android/location/GpsNavigationMessage.java
new file mode 100644
index 00000000..5c3c7101
--- /dev/null
+++ b/android/location/GpsNavigationMessage.java
@@ -0,0 +1,324 @@
+/*
+ * Copyright (C) 2014 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 android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.InvalidParameterException;
+
+/**
+ * A class containing a GPS satellite Navigation Message.
+ *
+ * @hide
+ */
+@SystemApi
+public class GpsNavigationMessage implements Parcelable {
+
+ private static final byte[] EMPTY_ARRAY = new byte[0];
+
+ // The following enumerations must be in sync with the values declared in gps.h
+
+ /**
+ * The type of the navigation message is not available or unknown.
+ */
+ public static final byte TYPE_UNKNOWN = 0;
+
+ /**
+ * The Navigation Message is of type L1 C/A.
+ */
+ public static final byte TYPE_L1CA = 1;
+
+ /**
+ * The Navigation Message is of type L1-CNAV.
+ */
+ public static final byte TYPE_L2CNAV = 2;
+
+ /**
+ * The Navigation Message is of type L5-CNAV.
+ */
+ public static final byte TYPE_L5CNAV = 3;
+
+ /**
+ * The Navigation Message is of type CNAV-2.
+ */
+ public static final byte TYPE_CNAV2 = 4;
+
+ /**
+ * The Navigation Message Status is 'unknown'.
+ */
+ public static final short STATUS_UNKNOWN = 0;
+
+ /**
+ * The Navigation Message was received without any parity error in its navigation words.
+ */
+ public static final short STATUS_PARITY_PASSED = (1<<0);
+
+ /**
+ * The Navigation Message was received with words that failed parity check, but the receiver was
+ * able to correct those words.
+ */
+ public static final short STATUS_PARITY_REBUILT = (1<<1);
+
+ // End enumerations in sync with gps.h
+
+ private byte mType;
+ private byte mPrn;
+ private short mMessageId;
+ private short mSubmessageId;
+ private byte[] mData;
+ private short mStatus;
+
+ GpsNavigationMessage() {
+ initialize();
+ }
+
+ /**
+ * Sets all contents to the values stored in the provided object.
+ */
+ public void set(GpsNavigationMessage navigationMessage) {
+ mType = navigationMessage.mType;
+ mPrn = navigationMessage.mPrn;
+ mMessageId = navigationMessage.mMessageId;
+ mSubmessageId = navigationMessage.mSubmessageId;
+ mData = navigationMessage.mData;
+ mStatus = navigationMessage.mStatus;
+ }
+
+ /**
+ * Resets all the contents to its original state.
+ */
+ public void reset() {
+ initialize();
+ }
+
+ /**
+ * Gets the type of the navigation message contained in the object.
+ */
+ public byte getType() {
+ return mType;
+ }
+
+ /**
+ * Sets the type of the navigation message.
+ */
+ public void setType(byte value) {
+ mType = value;
+ }
+
+ /**
+ * Gets a string representation of the 'type'.
+ * For internal and logging use only.
+ */
+ private String getTypeString() {
+ switch (mType) {
+ case TYPE_UNKNOWN:
+ return "Unknown";
+ case TYPE_L1CA:
+ return "L1 C/A";
+ case TYPE_L2CNAV:
+ return "L2-CNAV";
+ case TYPE_L5CNAV:
+ return "L5-CNAV";
+ case TYPE_CNAV2:
+ return "CNAV-2";
+ default:
+ return "<Invalid:" + mType + ">";
+ }
+ }
+
+ /**
+ * Gets the Pseudo-random number.
+ * Range: [1, 32].
+ */
+ public byte getPrn() {
+ return mPrn;
+ }
+
+ /**
+ * Sets the Pseud-random number.
+ */
+ public void setPrn(byte value) {
+ mPrn = value;
+ }
+
+ /**
+ * Gets the Message Identifier.
+ * It provides an index so the complete Navigation Message can be assembled. i.e. for L1 C/A
+ * subframe 4 and 5, this value corresponds to the 'frame id' of the navigation message.
+ * Subframe 1, 2, 3 does not contain a 'frame id' and this might be reported as -1.
+ */
+ public short getMessageId() {
+ return mMessageId;
+ }
+
+ /**
+ * Sets the Message Identifier.
+ */
+ public void setMessageId(short value) {
+ mMessageId = value;
+ }
+
+ /**
+ * Gets the Sub-message Identifier.
+ * If required by {@link #getType()}, this value contains a sub-index within the current message
+ * (or frame) that is being transmitted. i.e. for L1 C/A the sub-message identifier corresponds
+ * to the sub-frame Id of the navigation message.
+ */
+ public short getSubmessageId() {
+ return mSubmessageId;
+ }
+
+ /**
+ * Sets the Sub-message identifier.
+ */
+ public void setSubmessageId(short value) {
+ mSubmessageId = value;
+ }
+
+ /**
+ * Gets the data associated with the Navigation Message.
+ * The bytes (or words) specified using big endian format (MSB first).
+ */
+ @NonNull
+ public byte[] getData() {
+ return mData;
+ }
+
+ /**
+ * Sets the data associated with the Navigation Message.
+ */
+ public void setData(byte[] value) {
+ if (value == null) {
+ throw new InvalidParameterException("Data must be a non-null array");
+ }
+
+ mData = value;
+ }
+
+ /**
+ * Gets the Status of the navigation message contained in the object.
+ */
+ public short getStatus() {
+ return mStatus;
+ }
+
+ /**
+ * Sets the status of the navigation message.
+ */
+ public void setStatus(short value) {
+ mStatus = value;
+ }
+
+ /**
+ * Gets a string representation of the 'status'.
+ * For internal and logging use only.
+ */
+ private String getStatusString() {
+ switch (mStatus) {
+ case STATUS_UNKNOWN:
+ return "Unknown";
+ case STATUS_PARITY_PASSED:
+ return "ParityPassed";
+ case STATUS_PARITY_REBUILT:
+ return "ParityRebuilt";
+ default:
+ return "<Invalid:" + mStatus + ">";
+ }
+ }
+
+ public static final Creator<GpsNavigationMessage> CREATOR =
+ new Creator<GpsNavigationMessage>() {
+ @Override
+ public GpsNavigationMessage createFromParcel(Parcel parcel) {
+ GpsNavigationMessage navigationMessage = new GpsNavigationMessage();
+
+ navigationMessage.setType(parcel.readByte());
+ navigationMessage.setPrn(parcel.readByte());
+ navigationMessage.setMessageId((short) parcel.readInt());
+ navigationMessage.setSubmessageId((short) parcel.readInt());
+
+ int dataLength = parcel.readInt();
+ byte[] data = new byte[dataLength];
+ parcel.readByteArray(data);
+ navigationMessage.setData(data);
+
+ if (parcel.dataAvail() >= Integer.SIZE) {
+ int status = parcel.readInt();
+ navigationMessage.setStatus((short) status);
+ } else {
+ navigationMessage.setStatus(STATUS_UNKNOWN);
+ }
+
+ return navigationMessage;
+ }
+
+ @Override
+ public GpsNavigationMessage[] newArray(int size) {
+ return new GpsNavigationMessage[size];
+ }
+ };
+
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeByte(mType);
+ parcel.writeByte(mPrn);
+ parcel.writeInt(mMessageId);
+ parcel.writeInt(mSubmessageId);
+ parcel.writeInt(mData.length);
+ parcel.writeByteArray(mData);
+ parcel.writeInt(mStatus);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public String toString() {
+ final String format = " %-15s = %s\n";
+ StringBuilder builder = new StringBuilder("GpsNavigationMessage:\n");
+
+ builder.append(String.format(format, "Type", getTypeString()));
+ builder.append(String.format(format, "Prn", mPrn));
+ builder.append(String.format(format, "Status", getStatusString()));
+ builder.append(String.format(format, "MessageId", mMessageId));
+ builder.append(String.format(format, "SubmessageId", mSubmessageId));
+
+ builder.append(String.format(format, "Data", "{"));
+ String prefix = " ";
+ for(byte value : mData) {
+ builder.append(prefix);
+ builder.append(value);
+ prefix = ", ";
+ }
+ builder.append(" }");
+
+ return builder.toString();
+ }
+
+ private void initialize() {
+ mType = TYPE_UNKNOWN;
+ mPrn = 0;
+ mMessageId = -1;
+ mSubmessageId = -1;
+ mData = EMPTY_ARRAY;
+ mStatus = STATUS_UNKNOWN;
+ }
+}
diff --git a/android/location/GpsNavigationMessageEvent.java b/android/location/GpsNavigationMessageEvent.java
new file mode 100644
index 00000000..bd6921c7
--- /dev/null
+++ b/android/location/GpsNavigationMessageEvent.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 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 android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.security.InvalidParameterException;
+
+/**
+ * A class implementing a container for data associated with a navigation message event.
+ * Events are delivered to registered instances of {@link Listener}.
+ *
+ * @hide
+ */
+@SystemApi
+public class GpsNavigationMessageEvent implements Parcelable {
+
+ /**
+ * The system does not support tracking of GPS Navigation Messages. This status will not change
+ * in the future.
+ */
+ public static int STATUS_NOT_SUPPORTED = 0;
+
+ /**
+ * GPS Navigation Messages are successfully being tracked, it will receive updates once they are
+ * available.
+ */
+ public static int STATUS_READY = 1;
+
+ /**
+ * GPS provider or Location is disabled, updated will not be received until they are enabled.
+ */
+ public static int STATUS_GPS_LOCATION_DISABLED = 2;
+
+ private final GpsNavigationMessage mNavigationMessage;
+
+ /**
+ * Used for receiving GPS satellite Navigation Messages from the GPS engine.
+ * You can implement this interface and call
+ * {@link LocationManager#addGpsNavigationMessageListener}.
+ *
+ * @hide
+ */
+ @SystemApi
+ public interface Listener {
+
+ /**
+ * Returns the latest collected GPS Navigation Message.
+ */
+ void onGpsNavigationMessageReceived(GpsNavigationMessageEvent event);
+
+ /**
+ * Returns the latest status of the GPS Navigation Messages sub-system.
+ */
+ void onStatusChanged(int status);
+ }
+
+ public GpsNavigationMessageEvent(GpsNavigationMessage message) {
+ if (message == null) {
+ throw new InvalidParameterException("Parameter 'message' must not be null.");
+ }
+ mNavigationMessage = message;
+ }
+
+ @NonNull
+ public GpsNavigationMessage getNavigationMessage() {
+ return mNavigationMessage;
+ }
+
+ public static final Creator<GpsNavigationMessageEvent> CREATOR =
+ new Creator<GpsNavigationMessageEvent>() {
+ @Override
+ public GpsNavigationMessageEvent createFromParcel(Parcel in) {
+ ClassLoader classLoader = getClass().getClassLoader();
+ GpsNavigationMessage navigationMessage = in.readParcelable(classLoader);
+ return new GpsNavigationMessageEvent(navigationMessage);
+ }
+
+ @Override
+ public GpsNavigationMessageEvent[] newArray(int size) {
+ return new GpsNavigationMessageEvent[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeParcelable(mNavigationMessage, flags);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder("[ GpsNavigationMessageEvent:\n\n");
+ builder.append(mNavigationMessage.toString());
+ builder.append("\n]");
+ return builder.toString();
+ }
+}
diff --git a/android/location/GpsSatellite.java b/android/location/GpsSatellite.java
new file mode 100644
index 00000000..788d01ec
--- /dev/null
+++ b/android/location/GpsSatellite.java
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ *
+ * @deprecated use {@link GnssStatus} and {@link GnssStatus.Callback}.
+ */
+@Deprecated
+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) {
+ if (satellite == null) {
+ mValid = false;
+ } else {
+ 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/android/location/GpsStatus.java b/android/location/GpsStatus.java
new file mode 100644
index 00000000..b601cde3
--- /dev/null
+++ b/android/location/GpsStatus.java
@@ -0,0 +1,250 @@
+/*
+ * 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 android.util.SparseArray;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+
+/**
+ * This class represents the current state of the GPS engine.
+ *
+ * <p>This class is used in conjunction with the {@link Listener} interface.
+ *
+ * @deprecated use {@link GnssStatus} and {@link GnssStatus.Callback}.
+ */
+@Deprecated
+public final class GpsStatus {
+ private static final int NUM_SATELLITES = 255;
+ private static final int GLONASS_SVID_OFFSET = 64;
+ private static final int BEIDOU_SVID_OFFSET = 200;
+ private static final int SBAS_SVID_OFFSET = -87;
+
+ /* These package private values are modified by the LocationManager class */
+ private int mTimeToFirstFix;
+ private final SparseArray<GpsSatellite> mSatellites = new SparseArray<>();
+
+ private final class SatelliteIterator implements Iterator<GpsSatellite> {
+ private final int mSatellitesCount;
+
+ private int mIndex = 0;
+
+ SatelliteIterator() {
+ mSatellitesCount = mSatellites.size();
+ }
+
+ @Override
+ public boolean hasNext() {
+ for (; mIndex < mSatellitesCount; ++mIndex) {
+ GpsSatellite satellite = mSatellites.valueAt(mIndex);
+ if (satellite.mValid) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public GpsSatellite next() {
+ while (mIndex < mSatellitesCount) {
+ GpsSatellite satellite = mSatellites.valueAt(mIndex);
+ ++mIndex;
+ if (satellite.mValid) {
+ return satellite;
+ }
+ }
+ throw new NoSuchElementException();
+ }
+
+ @Override
+ public void remove() {
+ throw new UnsupportedOperationException();
+ }
+ }
+
+ private Iterable<GpsSatellite> mSatelliteList = new Iterable<GpsSatellite>() {
+ @Override
+ public Iterator<GpsSatellite> iterator() {
+ return new SatelliteIterator();
+ }
+ };
+
+ /**
+ * 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.
+ * @deprecated use {@link GnssStatus.Callback} instead.
+ */
+ @Deprecated
+ 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);
+ }
+
+ /**
+ * Used for receiving NMEA sentences from the GPS.
+ * NMEA 0183 is a standard for communicating with marine electronic devices
+ * and is a common method for receiving data from a GPS, typically over a serial port.
+ * See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
+ * You can implement this interface and call {@link LocationManager#addNmeaListener}
+ * to receive NMEA data from the GPS engine.
+ * @deprecated use {@link OnNmeaMessageListener} instead.
+ */
+ @Deprecated
+ public interface NmeaListener {
+ void onNmeaReceived(long timestamp, String nmea);
+ }
+
+ // For API-compat a public ctor() is not available
+ GpsStatus() {}
+
+ private void setStatus(int svCount, int[] svidWithFlags, float[] cn0s, float[] elevations,
+ float[] azimuths) {
+ clearSatellites();
+ for (int i = 0; i < svCount; i++) {
+ final int constellationType =
+ (svidWithFlags[i] >> GnssStatus.CONSTELLATION_TYPE_SHIFT_WIDTH)
+ & GnssStatus.CONSTELLATION_TYPE_MASK;
+ int prn = svidWithFlags[i] >> GnssStatus.SVID_SHIFT_WIDTH;
+ // Other satellites passed through these APIs before GnssSvStatus was availble.
+ // GPS, SBAS & QZSS can pass through at their nominally
+ // assigned prn number (as long as it fits in the valid 0-255 range below.)
+ // Glonass, and Beidou are passed through with the defacto standard offsets
+ // Other future constellation reporting (e.g. Galileo) needs to use
+ // GnssSvStatus on (N level) HAL & Java layers.
+ if (constellationType == GnssStatus.CONSTELLATION_GLONASS) {
+ prn += GLONASS_SVID_OFFSET;
+ } else if (constellationType == GnssStatus.CONSTELLATION_BEIDOU) {
+ prn += BEIDOU_SVID_OFFSET;
+ } else if (constellationType == GnssStatus.CONSTELLATION_SBAS) {
+ prn += SBAS_SVID_OFFSET;
+ } else if ((constellationType != GnssStatus.CONSTELLATION_GPS) &&
+ (constellationType != GnssStatus.CONSTELLATION_QZSS)) {
+ continue;
+ }
+ if (prn > 0 && prn <= NUM_SATELLITES) {
+ GpsSatellite satellite = mSatellites.get(prn);
+ if (satellite == null) {
+ satellite = new GpsSatellite(prn);
+ mSatellites.put(prn, satellite);
+ }
+
+ satellite.mValid = true;
+ satellite.mSnr = cn0s[i];
+ satellite.mElevation = elevations[i];
+ satellite.mAzimuth = azimuths[i];
+ satellite.mHasEphemeris =
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+ satellite.mHasAlmanac =
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_ALMANAC_DATA) != 0;
+ satellite.mUsedInFix =
+ (svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0;
+ }
+ }
+ }
+
+ /**
+ * Copies GPS satellites information from GnssStatus object.
+ * Since this method is only used within {@link LocationManager#getGpsStatus},
+ * it does not need to be synchronized.
+ * @hide
+ */
+ void setStatus(GnssStatus status, int timeToFirstFix) {
+ mTimeToFirstFix = timeToFirstFix;
+ setStatus(status.mSvCount, status.mSvidWithFlags, status.mCn0DbHz, status.mElevations,
+ status.mAzimuths);
+ }
+
+ 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;
+ }
+
+ private void clearSatellites() {
+ int satellitesCount = mSatellites.size();
+ for (int i = 0; i < satellitesCount; i++) {
+ GpsSatellite satellite = mSatellites.valueAt(i);
+ satellite.mValid = false;
+ }
+ }
+}
diff --git a/android/location/LocalListenerHelper.java b/android/location/LocalListenerHelper.java
new file mode 100644
index 00000000..d7d2c513
--- /dev/null
+++ b/android/location/LocalListenerHelper.java
@@ -0,0 +1,129 @@
+/*
+ * Copyright (C) 2014 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 com.android.internal.util.Preconditions;
+
+import android.annotation.NonNull;
+import android.content.Context;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * A base handler class to manage transport and local listeners.
+ *
+ * @hide
+ */
+abstract class LocalListenerHelper<TListener> {
+ private final HashMap<TListener, Handler> mListeners = new HashMap<>();
+
+ private final String mTag;
+ private final Context mContext;
+
+ protected LocalListenerHelper(Context context, String name) {
+ Preconditions.checkNotNull(name);
+ mContext = context;
+ mTag = name;
+ }
+
+ public boolean add(@NonNull TListener listener, Handler handler) {
+ Preconditions.checkNotNull(listener);
+ synchronized (mListeners) {
+ // we need to register with the service first, because we need to find out if the
+ // service will actually support the request before we attempt anything
+ if (mListeners.isEmpty()) {
+ boolean registeredWithService;
+ try {
+ registeredWithService = registerWithServer();
+ } catch (RemoteException e) {
+ Log.e(mTag, "Error handling first listener.", e);
+ return false;
+ }
+ if (!registeredWithService) {
+ Log.e(mTag, "Unable to register listener transport.");
+ return false;
+ }
+ }
+ if (mListeners.containsKey(listener)) {
+ return true;
+ }
+ mListeners.put(listener, handler);
+ return true;
+ }
+ }
+
+ public void remove(@NonNull TListener listener) {
+ Preconditions.checkNotNull(listener);
+ synchronized (mListeners) {
+ boolean removed = mListeners.containsKey(listener);
+ mListeners.remove(listener);
+ boolean isLastRemoved = removed && mListeners.isEmpty();
+ if (isLastRemoved) {
+ try {
+ unregisterFromServer();
+ } catch (RemoteException e) {
+ Log.v(mTag, "Error handling last listener removal", e);
+ }
+ }
+ }
+ }
+
+ protected abstract boolean registerWithServer() throws RemoteException;
+ protected abstract void unregisterFromServer() throws RemoteException;
+
+ protected interface ListenerOperation<TListener> {
+ void execute(TListener listener) throws RemoteException;
+ }
+
+ protected Context getContext() {
+ return mContext;
+ }
+
+ private void executeOperation(ListenerOperation<TListener> operation, TListener listener) {
+ try {
+ operation.execute(listener);
+ } catch (RemoteException e) {
+ Log.e(mTag, "Error in monitored listener.", e);
+ // don't return, give a fair chance to all listeners to receive the event
+ }
+ }
+
+ protected void foreach(final ListenerOperation<TListener> operation) {
+ Collection<Map.Entry<TListener, Handler>> listeners;
+ synchronized (mListeners) {
+ listeners = new ArrayList<>(mListeners.entrySet());
+ }
+ for (final Map.Entry<TListener, Handler> listener : listeners) {
+ if (listener.getValue() == null) {
+ executeOperation(operation, listener.getKey());
+ } else {
+ listener.getValue().post(new Runnable() {
+ @Override
+ public void run() {
+ executeOperation(operation, listener.getKey());
+ }
+ });
+ }
+ }
+ }
+}
diff --git a/android/location/Location.java b/android/location/Location.java
new file mode 100644
index 00000000..e8eaa59a
--- /dev/null
+++ b/android/location/Location.java
@@ -0,0 +1,1192 @@
+/*
+ * Copyright (C) 2007 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 android.annotation.SystemApi;
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.Printer;
+import android.util.TimeUtils;
+
+import java.text.DecimalFormat;
+import java.util.StringTokenizer;
+
+/**
+ * A data class representing a geographic location.
+ *
+ * <p>A location can consist of a latitude, longitude, timestamp,
+ * and other information such as bearing, altitude and velocity.
+ *
+ * <p>All locations generated by the {@link LocationManager} are
+ * guaranteed to have a valid latitude, longitude, and timestamp
+ * (both UTC time and elapsed real-time since boot), all other
+ * parameters are optional.
+ */
+public class Location implements Parcelable {
+ /**
+ * Constant used to specify formatting of a latitude or longitude
+ * in the form "[+-]DDD.DDDDD where D indicates degrees.
+ */
+ public static final int FORMAT_DEGREES = 0;
+
+ /**
+ * Constant used to specify formatting of a latitude or longitude
+ * in the form "[+-]DDD:MM.MMMMM" where D indicates degrees and
+ * M indicates minutes of arc (1 minute = 1/60th of a degree).
+ */
+ public static final int FORMAT_MINUTES = 1;
+
+ /**
+ * Constant used to specify formatting of a latitude or longitude
+ * in the form "DDD:MM:SS.SSSSS" where D indicates degrees, M
+ * indicates minutes of arc, and S indicates seconds of arc (1
+ * minute = 1/60th of a degree, 1 second = 1/3600th of a degree).
+ */
+ public static final int FORMAT_SECONDS = 2;
+
+ /**
+ * Bundle key for a version of the location that has been fed through
+ * LocationFudger. Allows location providers to flag locations as being
+ * safe for use with ACCESS_COARSE_LOCATION permission.
+ *
+ * @hide
+ */
+ public static final String EXTRA_COARSE_LOCATION = "coarseLocation";
+
+ /**
+ * Bundle key for a version of the location containing no GPS data.
+ * Allows location providers to flag locations as being safe to
+ * feed to LocationFudger.
+ *
+ * @hide
+ */
+ public static final String EXTRA_NO_GPS_LOCATION = "noGPSLocation";
+
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mAltitude.
+ */
+ private static final int HAS_ALTITUDE_MASK = 1;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mSpeed.
+ */
+ private static final int HAS_SPEED_MASK = 2;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mBearing.
+ */
+ private static final int HAS_BEARING_MASK = 4;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mHorizontalAccuracy.
+ */
+ private static final int HAS_HORIZONTAL_ACCURACY_MASK = 8;
+ /**
+ * Bit mask for mFieldsMask indicating location is from a mock provider.
+ */
+ private static final int HAS_MOCK_PROVIDER_MASK = 16;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mVerticalAccuracy.
+ */
+ private static final int HAS_VERTICAL_ACCURACY_MASK = 32;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mSpeedAccuracy.
+ */
+ private static final int HAS_SPEED_ACCURACY_MASK = 64;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mBearingAccuracy.
+ */
+ private static final int HAS_BEARING_ACCURACY_MASK = 128;
+
+ // Cached data to make bearing/distance computations more efficient for the case
+ // where distanceTo and bearingTo are called in sequence. Assume this typically happens
+ // on the same thread for caching purposes.
+ private static ThreadLocal<BearingDistanceCache> sBearingDistanceCache
+ = new ThreadLocal<BearingDistanceCache>() {
+ @Override
+ protected BearingDistanceCache initialValue() {
+ return new BearingDistanceCache();
+ }
+ };
+
+ private String mProvider;
+ private long mTime = 0;
+ private long mElapsedRealtimeNanos = 0;
+ private double mLatitude = 0.0;
+ private double mLongitude = 0.0;
+ private double mAltitude = 0.0f;
+ private float mSpeed = 0.0f;
+ private float mBearing = 0.0f;
+ private float mHorizontalAccuracyMeters = 0.0f;
+ private float mVerticalAccuracyMeters = 0.0f;
+ private float mSpeedAccuracyMetersPerSecond = 0.0f;
+ private float mBearingAccuracyDegrees = 0.0f;
+
+ private Bundle mExtras = null;
+
+ // A bitmask of fields present in this object (see HAS_* constants defined above).
+ private byte mFieldsMask = 0;
+
+ /**
+ * Construct a new Location with a named provider.
+ *
+ * <p>By default time, latitude and longitude are 0, and the location
+ * has no bearing, altitude, speed, accuracy or extras.
+ *
+ * @param provider the name of the provider that generated this location
+ */
+ public Location(String provider) {
+ mProvider = provider;
+ }
+
+ /**
+ * Construct a new Location object that is copied from an existing one.
+ */
+ public Location(Location l) {
+ set(l);
+ }
+
+ /**
+ * Sets the contents of the location to the values from the given location.
+ */
+ public void set(Location l) {
+ mProvider = l.mProvider;
+ mTime = l.mTime;
+ mElapsedRealtimeNanos = l.mElapsedRealtimeNanos;
+ mFieldsMask = l.mFieldsMask;
+ mLatitude = l.mLatitude;
+ mLongitude = l.mLongitude;
+ mAltitude = l.mAltitude;
+ mSpeed = l.mSpeed;
+ mBearing = l.mBearing;
+ mHorizontalAccuracyMeters = l.mHorizontalAccuracyMeters;
+ mVerticalAccuracyMeters = l.mVerticalAccuracyMeters;
+ mSpeedAccuracyMetersPerSecond = l.mSpeedAccuracyMetersPerSecond;
+ mBearingAccuracyDegrees = l.mBearingAccuracyDegrees;
+ mExtras = (l.mExtras == null) ? null : new Bundle(l.mExtras);
+ }
+
+ /**
+ * Clears the contents of the location.
+ */
+ public void reset() {
+ mProvider = null;
+ mTime = 0;
+ mElapsedRealtimeNanos = 0;
+ mFieldsMask = 0;
+ mLatitude = 0;
+ mLongitude = 0;
+ mAltitude = 0;
+ mSpeed = 0;
+ mBearing = 0;
+ mHorizontalAccuracyMeters = 0;
+ mVerticalAccuracyMeters = 0;
+ mSpeedAccuracyMetersPerSecond = 0;
+ mBearingAccuracyDegrees = 0;
+ mExtras = null;
+ }
+
+ /**
+ * Converts a coordinate to a String representation. The outputType
+ * may be one of FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS.
+ * The coordinate must be a valid double between -180.0 and 180.0.
+ * This conversion is performed in a method that is dependent on the
+ * default locale, and so is not guaranteed to round-trip with
+ * {@link #convert(String)}.
+ *
+ * @throws IllegalArgumentException if coordinate is less than
+ * -180.0, greater than 180.0, or is not a number.
+ * @throws IllegalArgumentException if outputType is not one of
+ * FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS.
+ */
+ public static String convert(double coordinate, int outputType) {
+ if (coordinate < -180.0 || coordinate > 180.0 ||
+ Double.isNaN(coordinate)) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+ if ((outputType != FORMAT_DEGREES) &&
+ (outputType != FORMAT_MINUTES) &&
+ (outputType != FORMAT_SECONDS)) {
+ throw new IllegalArgumentException("outputType=" + outputType);
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ // Handle negative values
+ if (coordinate < 0) {
+ sb.append('-');
+ coordinate = -coordinate;
+ }
+
+ DecimalFormat df = new DecimalFormat("###.#####");
+ if (outputType == FORMAT_MINUTES || outputType == FORMAT_SECONDS) {
+ int degrees = (int) Math.floor(coordinate);
+ sb.append(degrees);
+ sb.append(':');
+ coordinate -= degrees;
+ coordinate *= 60.0;
+ if (outputType == FORMAT_SECONDS) {
+ int minutes = (int) Math.floor(coordinate);
+ sb.append(minutes);
+ sb.append(':');
+ coordinate -= minutes;
+ coordinate *= 60.0;
+ }
+ }
+ sb.append(df.format(coordinate));
+ return sb.toString();
+ }
+
+ /**
+ * Converts a String in one of the formats described by
+ * FORMAT_DEGREES, FORMAT_MINUTES, or FORMAT_SECONDS into a
+ * double. This conversion is performed in a locale agnostic
+ * method, and so is not guaranteed to round-trip with
+ * {@link #convert(double, int)}.
+ *
+ * @throws NullPointerException if coordinate is null
+ * @throws IllegalArgumentException if the coordinate is not
+ * in one of the valid formats.
+ */
+ public static double convert(String coordinate) {
+ // IllegalArgumentException if bad syntax
+ if (coordinate == null) {
+ throw new NullPointerException("coordinate");
+ }
+
+ boolean negative = false;
+ if (coordinate.charAt(0) == '-') {
+ coordinate = coordinate.substring(1);
+ negative = true;
+ }
+
+ StringTokenizer st = new StringTokenizer(coordinate, ":");
+ int tokens = st.countTokens();
+ if (tokens < 1) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+ try {
+ String degrees = st.nextToken();
+ double val;
+ if (tokens == 1) {
+ val = Double.parseDouble(degrees);
+ return negative ? -val : val;
+ }
+
+ String minutes = st.nextToken();
+ int deg = Integer.parseInt(degrees);
+ double min;
+ double sec = 0.0;
+ boolean secPresent = false;
+
+ if (st.hasMoreTokens()) {
+ min = Integer.parseInt(minutes);
+ String seconds = st.nextToken();
+ sec = Double.parseDouble(seconds);
+ secPresent = true;
+ } else {
+ min = Double.parseDouble(minutes);
+ }
+
+ boolean isNegative180 = negative && (deg == 180) &&
+ (min == 0) && (sec == 0);
+
+ // deg must be in [0, 179] except for the case of -180 degrees
+ if ((deg < 0.0) || (deg > 179 && !isNegative180)) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+
+ // min must be in [0, 59] if seconds are present, otherwise [0.0, 60.0)
+ if (min < 0 || min >= 60 || (secPresent && (min > 59))) {
+ throw new IllegalArgumentException("coordinate=" +
+ coordinate);
+ }
+
+ // sec must be in [0.0, 60.0)
+ if (sec < 0 || sec >= 60) {
+ throw new IllegalArgumentException("coordinate=" +
+ coordinate);
+ }
+
+ val = deg*3600.0 + min*60.0 + sec;
+ val /= 3600.0;
+ return negative ? -val : val;
+ } catch (NumberFormatException nfe) {
+ throw new IllegalArgumentException("coordinate=" + coordinate);
+ }
+ }
+
+ private static void computeDistanceAndBearing(double lat1, double lon1,
+ double lat2, double lon2, BearingDistanceCache results) {
+ // Based on http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf
+ // using the "Inverse Formula" (section 4)
+
+ int MAXITERS = 20;
+ // Convert lat/long to radians
+ lat1 *= Math.PI / 180.0;
+ lat2 *= Math.PI / 180.0;
+ lon1 *= Math.PI / 180.0;
+ lon2 *= Math.PI / 180.0;
+
+ double a = 6378137.0; // WGS84 major axis
+ double b = 6356752.3142; // WGS84 semi-major axis
+ double f = (a - b) / a;
+ double aSqMinusBSqOverBSq = (a * a - b * b) / (b * b);
+
+ double L = lon2 - lon1;
+ double A = 0.0;
+ double U1 = Math.atan((1.0 - f) * Math.tan(lat1));
+ double U2 = Math.atan((1.0 - f) * Math.tan(lat2));
+
+ double cosU1 = Math.cos(U1);
+ double cosU2 = Math.cos(U2);
+ double sinU1 = Math.sin(U1);
+ double sinU2 = Math.sin(U2);
+ double cosU1cosU2 = cosU1 * cosU2;
+ double sinU1sinU2 = sinU1 * sinU2;
+
+ double sigma = 0.0;
+ double deltaSigma = 0.0;
+ double cosSqAlpha = 0.0;
+ double cos2SM = 0.0;
+ double cosSigma = 0.0;
+ double sinSigma = 0.0;
+ double cosLambda = 0.0;
+ double sinLambda = 0.0;
+
+ double lambda = L; // initial guess
+ for (int iter = 0; iter < MAXITERS; iter++) {
+ double lambdaOrig = lambda;
+ cosLambda = Math.cos(lambda);
+ sinLambda = Math.sin(lambda);
+ double t1 = cosU2 * sinLambda;
+ double t2 = cosU1 * sinU2 - sinU1 * cosU2 * cosLambda;
+ double sinSqSigma = t1 * t1 + t2 * t2; // (14)
+ sinSigma = Math.sqrt(sinSqSigma);
+ cosSigma = sinU1sinU2 + cosU1cosU2 * cosLambda; // (15)
+ sigma = Math.atan2(sinSigma, cosSigma); // (16)
+ double sinAlpha = (sinSigma == 0) ? 0.0 :
+ cosU1cosU2 * sinLambda / sinSigma; // (17)
+ cosSqAlpha = 1.0 - sinAlpha * sinAlpha;
+ cos2SM = (cosSqAlpha == 0) ? 0.0 :
+ cosSigma - 2.0 * sinU1sinU2 / cosSqAlpha; // (18)
+
+ double uSquared = cosSqAlpha * aSqMinusBSqOverBSq; // defn
+ A = 1 + (uSquared / 16384.0) * // (3)
+ (4096.0 + uSquared *
+ (-768 + uSquared * (320.0 - 175.0 * uSquared)));
+ double B = (uSquared / 1024.0) * // (4)
+ (256.0 + uSquared *
+ (-128.0 + uSquared * (74.0 - 47.0 * uSquared)));
+ double C = (f / 16.0) *
+ cosSqAlpha *
+ (4.0 + f * (4.0 - 3.0 * cosSqAlpha)); // (10)
+ double cos2SMSq = cos2SM * cos2SM;
+ deltaSigma = B * sinSigma * // (6)
+ (cos2SM + (B / 4.0) *
+ (cosSigma * (-1.0 + 2.0 * cos2SMSq) -
+ (B / 6.0) * cos2SM *
+ (-3.0 + 4.0 * sinSigma * sinSigma) *
+ (-3.0 + 4.0 * cos2SMSq)));
+
+ lambda = L +
+ (1.0 - C) * f * sinAlpha *
+ (sigma + C * sinSigma *
+ (cos2SM + C * cosSigma *
+ (-1.0 + 2.0 * cos2SM * cos2SM))); // (11)
+
+ double delta = (lambda - lambdaOrig) / lambda;
+ if (Math.abs(delta) < 1.0e-12) {
+ break;
+ }
+ }
+
+ float distance = (float) (b * A * (sigma - deltaSigma));
+ results.mDistance = distance;
+ float initialBearing = (float) Math.atan2(cosU2 * sinLambda,
+ cosU1 * sinU2 - sinU1 * cosU2 * cosLambda);
+ initialBearing *= 180.0 / Math.PI;
+ results.mInitialBearing = initialBearing;
+ float finalBearing = (float) Math.atan2(cosU1 * sinLambda,
+ -sinU1 * cosU2 + cosU1 * sinU2 * cosLambda);
+ finalBearing *= 180.0 / Math.PI;
+ results.mFinalBearing = finalBearing;
+ results.mLat1 = lat1;
+ results.mLat2 = lat2;
+ results.mLon1 = lon1;
+ results.mLon2 = lon2;
+ }
+
+ /**
+ * Computes the approximate distance in meters between two
+ * locations, and optionally the initial and final bearings of the
+ * shortest path between them. Distance and bearing are defined using the
+ * WGS84 ellipsoid.
+ *
+ * <p> The computed distance is stored in results[0]. If results has length
+ * 2 or greater, the initial bearing is stored in results[1]. If results has
+ * length 3 or greater, the final bearing is stored in results[2].
+ *
+ * @param startLatitude the starting latitude
+ * @param startLongitude the starting longitude
+ * @param endLatitude the ending latitude
+ * @param endLongitude the ending longitude
+ * @param results an array of floats to hold the results
+ *
+ * @throws IllegalArgumentException if results is null or has length < 1
+ */
+ public static void distanceBetween(double startLatitude, double startLongitude,
+ double endLatitude, double endLongitude, float[] results) {
+ if (results == null || results.length < 1) {
+ throw new IllegalArgumentException("results is null or has length < 1");
+ }
+ BearingDistanceCache cache = sBearingDistanceCache.get();
+ computeDistanceAndBearing(startLatitude, startLongitude,
+ endLatitude, endLongitude, cache);
+ results[0] = cache.mDistance;
+ if (results.length > 1) {
+ results[1] = cache.mInitialBearing;
+ if (results.length > 2) {
+ results[2] = cache.mFinalBearing;
+ }
+ }
+ }
+
+ /**
+ * Returns the approximate distance in meters between this
+ * location and the given location. Distance is defined using
+ * the WGS84 ellipsoid.
+ *
+ * @param dest the destination location
+ * @return the approximate distance in meters
+ */
+ public float distanceTo(Location dest) {
+ BearingDistanceCache cache = sBearingDistanceCache.get();
+ // See if we already have the result
+ if (mLatitude != cache.mLat1 || mLongitude != cache.mLon1 ||
+ dest.mLatitude != cache.mLat2 || dest.mLongitude != cache.mLon2) {
+ computeDistanceAndBearing(mLatitude, mLongitude,
+ dest.mLatitude, dest.mLongitude, cache);
+ }
+ return cache.mDistance;
+ }
+
+ /**
+ * Returns the approximate initial bearing in degrees East of true
+ * North when traveling along the shortest path between this
+ * location and the given location. The shortest path is defined
+ * using the WGS84 ellipsoid. Locations that are (nearly)
+ * antipodal may produce meaningless results.
+ *
+ * @param dest the destination location
+ * @return the initial bearing in degrees
+ */
+ public float bearingTo(Location dest) {
+ BearingDistanceCache cache = sBearingDistanceCache.get();
+ // See if we already have the result
+ if (mLatitude != cache.mLat1 || mLongitude != cache.mLon1 ||
+ dest.mLatitude != cache.mLat2 || dest.mLongitude != cache.mLon2) {
+ computeDistanceAndBearing(mLatitude, mLongitude,
+ dest.mLatitude, dest.mLongitude, cache);
+ }
+ return cache.mInitialBearing;
+ }
+
+ /**
+ * Returns the name of the provider that generated this fix.
+ *
+ * @return the provider, or null if it has not been set
+ */
+ public String getProvider() {
+ return mProvider;
+ }
+
+ /**
+ * Sets the name of the provider that generated this fix.
+ */
+ public void setProvider(String provider) {
+ mProvider = provider;
+ }
+
+ /**
+ * Return the UTC time of this fix, in milliseconds since January 1, 1970.
+ *
+ * <p>Note that the UTC time on a device is not monotonic: it
+ * can jump forwards or backwards unpredictably. So always use
+ * {@link #getElapsedRealtimeNanos} when calculating time deltas.
+ *
+ * <p>On the other hand, {@link #getTime} is useful for presenting
+ * a human readable time to the user, or for carefully comparing
+ * location fixes across reboot or across devices.
+ *
+ * <p>All locations generated by the {@link LocationManager}
+ * are guaranteed to have a valid UTC time, however remember that
+ * the system time may have changed since the location was generated.
+ *
+ * @return time of fix, in milliseconds since January 1, 1970.
+ */
+ public long getTime() {
+ return mTime;
+ }
+
+ /**
+ * Set the UTC time of this fix, in milliseconds since January 1,
+ * 1970.
+ *
+ * @param time UTC time of this fix, in milliseconds since January 1, 1970
+ */
+ public void setTime(long time) {
+ mTime = time;
+ }
+
+ /**
+ * Return the time of this fix, in elapsed real-time since system boot.
+ *
+ * <p>This value can be reliably compared to
+ * {@link android.os.SystemClock#elapsedRealtimeNanos},
+ * to calculate the age of a fix and to compare Location fixes. This
+ * is reliable because elapsed real-time is guaranteed monotonic for
+ * each system boot and continues to increment even when the system
+ * is in deep sleep (unlike {@link #getTime}.
+ *
+ * <p>All locations generated by the {@link LocationManager}
+ * are guaranteed to have a valid elapsed real-time.
+ *
+ * @return elapsed real-time of fix, in nanoseconds since system boot.
+ */
+ public long getElapsedRealtimeNanos() {
+ return mElapsedRealtimeNanos;
+ }
+
+ /**
+ * Set the time of this fix, in elapsed real-time since system boot.
+ *
+ * @param time elapsed real-time of fix, in nanoseconds since system boot.
+ */
+ public void setElapsedRealtimeNanos(long time) {
+ mElapsedRealtimeNanos = time;
+ }
+
+ /**
+ * Get the latitude, in degrees.
+ *
+ * <p>All locations generated by the {@link LocationManager}
+ * will have a valid latitude.
+ */
+ public double getLatitude() {
+ return mLatitude;
+ }
+
+ /**
+ * Set the latitude, in degrees.
+ */
+ public void setLatitude(double latitude) {
+ mLatitude = latitude;
+ }
+
+ /**
+ * Get the longitude, in degrees.
+ *
+ * <p>All locations generated by the {@link LocationManager}
+ * will have a valid longitude.
+ */
+ public double getLongitude() {
+ return mLongitude;
+ }
+
+ /**
+ * Set the longitude, in degrees.
+ */
+ public void setLongitude(double longitude) {
+ mLongitude = longitude;
+ }
+
+ /**
+ * True if this location has an altitude.
+ */
+ public boolean hasAltitude() {
+ return (mFieldsMask & HAS_ALTITUDE_MASK) != 0;
+ }
+
+ /**
+ * Get the altitude if available, in meters above the WGS 84 reference
+ * ellipsoid.
+ *
+ * <p>If this location does not have an altitude then 0.0 is returned.
+ */
+ public double getAltitude() {
+ return mAltitude;
+ }
+
+ /**
+ * Set the altitude, in meters above the WGS 84 reference ellipsoid.
+ *
+ * <p>Following this call {@link #hasAltitude} will return true.
+ */
+ public void setAltitude(double altitude) {
+ mAltitude = altitude;
+ mFieldsMask |= HAS_ALTITUDE_MASK;
+ }
+
+ /**
+ * Remove the altitude from this location.
+ *
+ * <p>Following this call {@link #hasAltitude} will return false,
+ * and {@link #getAltitude} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
+ */
+ @Deprecated
+ public void removeAltitude() {
+ mAltitude = 0.0f;
+ mFieldsMask &= ~HAS_ALTITUDE_MASK;
+ }
+
+ /**
+ * True if this location has a speed.
+ */
+ public boolean hasSpeed() {
+ return (mFieldsMask & HAS_SPEED_MASK) != 0;
+ }
+
+ /**
+ * Get the speed if it is available, in meters/second over ground.
+ *
+ * <p>If this location does not have a speed then 0.0 is returned.
+ */
+ public float getSpeed() {
+ return mSpeed;
+ }
+
+ /**
+ * Set the speed, in meters/second over ground.
+ *
+ * <p>Following this call {@link #hasSpeed} will return true.
+ */
+ public void setSpeed(float speed) {
+ mSpeed = speed;
+ mFieldsMask |= HAS_SPEED_MASK;
+ }
+
+ /**
+ * Remove the speed from this location.
+ *
+ * <p>Following this call {@link #hasSpeed} will return false,
+ * and {@link #getSpeed} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
+ */
+ @Deprecated
+ public void removeSpeed() {
+ mSpeed = 0.0f;
+ mFieldsMask &= ~HAS_SPEED_MASK;
+ }
+
+ /**
+ * True if this location has a bearing.
+ */
+ public boolean hasBearing() {
+ return (mFieldsMask & HAS_BEARING_MASK) != 0;
+ }
+
+ /**
+ * Get the bearing, in degrees.
+ *
+ * <p>Bearing is the horizontal direction of travel of this device,
+ * and is not related to the device orientation. It is guaranteed to
+ * be in the range (0.0, 360.0] if the device has a bearing.
+ *
+ * <p>If this location does not have a bearing then 0.0 is returned.
+ */
+ public float getBearing() {
+ return mBearing;
+ }
+
+ /**
+ * Set the bearing, in degrees.
+ *
+ * <p>Bearing is the horizontal direction of travel of this device,
+ * and is not related to the device orientation.
+ *
+ * <p>The input will be wrapped into the range (0.0, 360.0].
+ */
+ public void setBearing(float bearing) {
+ while (bearing < 0.0f) {
+ bearing += 360.0f;
+ }
+ while (bearing >= 360.0f) {
+ bearing -= 360.0f;
+ }
+ mBearing = bearing;
+ mFieldsMask |= HAS_BEARING_MASK;
+ }
+
+ /**
+ * Remove the bearing from this location.
+ *
+ * <p>Following this call {@link #hasBearing} will return false,
+ * and {@link #getBearing} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
+ */
+ @Deprecated
+ public void removeBearing() {
+ mBearing = 0.0f;
+ mFieldsMask &= ~HAS_BEARING_MASK;
+ }
+
+ /**
+ * True if this location has a horizontal accuracy.
+ *
+ * <p>All locations generated by the {@link LocationManager} have an horizontal accuracy.
+ */
+ public boolean hasAccuracy() {
+ return (mFieldsMask & HAS_HORIZONTAL_ACCURACY_MASK) != 0;
+ }
+
+ /**
+ * Get the estimated horizontal accuracy of this location, radial, in meters.
+ *
+ * <p>We define horizontal accuracy as the radius of 68% confidence. In other
+ * words, if you draw a circle centered at this location's
+ * latitude and longitude, and with a radius equal to the accuracy,
+ * then there is a 68% probability that the true location is inside
+ * the circle.
+ *
+ * <p>This accuracy estimation is only concerned with horizontal
+ * accuracy, and does not indicate the accuracy of bearing,
+ * velocity or altitude if those are included in this Location.
+ *
+ * <p>If this location does not have a horizontal accuracy, then 0.0 is returned.
+ * All locations generated by the {@link LocationManager} include horizontal accuracy.
+ */
+ public float getAccuracy() {
+ return mHorizontalAccuracyMeters;
+ }
+
+ /**
+ * Set the estimated horizontal accuracy of this location, meters.
+ *
+ * <p>See {@link #getAccuracy} for the definition of horizontal accuracy.
+ *
+ * <p>Following this call {@link #hasAccuracy} will return true.
+ */
+ public void setAccuracy(float horizontalAccuracy) {
+ mHorizontalAccuracyMeters = horizontalAccuracy;
+ mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK;
+ }
+
+ /**
+ * Remove the horizontal accuracy from this location.
+ *
+ * <p>Following this call {@link #hasAccuracy} will return false, and
+ * {@link #getAccuracy} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
+ */
+ @Deprecated
+ public void removeAccuracy() {
+ mHorizontalAccuracyMeters = 0.0f;
+ mFieldsMask &= ~HAS_HORIZONTAL_ACCURACY_MASK;
+ }
+
+ /**
+ * True if this location has a vertical accuracy.
+ */
+ public boolean hasVerticalAccuracy() {
+ return (mFieldsMask & HAS_VERTICAL_ACCURACY_MASK) != 0;
+ }
+
+ /**
+ * Get the estimated vertical accuracy of this location, in meters.
+ *
+ * <p>We define vertical accuracy as the radius of 68% confidence. In other
+ * words, if you draw a circle centered at this location's altitude, and with a radius
+ * equal to the vertical accuracy, then there is a 68% probability that the true altitude is
+ * inside the circle.
+ *
+ * <p>In statistical terms, it is assumed that location errors
+ * are random with a normal distribution, so the 68% confidence circle
+ * represents one standard deviation. Note that in practice, location
+ * errors do not always follow such a simple distribution.
+ *
+ * <p>If this location does not have a vertical accuracy, then 0.0 is returned.
+ */
+ public float getVerticalAccuracyMeters() {
+ return mVerticalAccuracyMeters;
+ }
+
+ /**
+ * Set the estimated vertical accuracy of this location, meters.
+ *
+ * <p>See {@link #getVerticalAccuracyMeters} for the definition of vertical accuracy.
+ *
+ * <p>Following this call {@link #hasVerticalAccuracy} will return true.
+ */
+ public void setVerticalAccuracyMeters(float verticalAccuracyMeters) {
+ mVerticalAccuracyMeters = verticalAccuracyMeters;
+ mFieldsMask |= HAS_VERTICAL_ACCURACY_MASK;
+ }
+
+ /**
+ * Remove the vertical accuracy from this location.
+ *
+ * <p>Following this call {@link #hasVerticalAccuracy} will return false, and
+ * {@link #getVerticalAccuracyMeters} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
+ * @removed
+ */
+ @Deprecated
+ public void removeVerticalAccuracy() {
+ mVerticalAccuracyMeters = 0.0f;
+ mFieldsMask &= ~HAS_VERTICAL_ACCURACY_MASK;
+ }
+
+ /**
+ * True if this location has a speed accuracy.
+ */
+ public boolean hasSpeedAccuracy() {
+ return (mFieldsMask & HAS_SPEED_ACCURACY_MASK) != 0;
+ }
+
+ /**
+ * Get the estimated speed accuracy of this location, in meters per second.
+ *
+ * <p>We define speed accuracy as a 1-standard-deviation value, i.e. as 1-side of the
+ * 2-sided range above and below the estimated
+ * speed reported by {@link #getSpeed()}, within which there is a 68% probability of
+ * finding the true speed.
+ *
+ * <p>For example, if {@link #getSpeed()} returns 5.0, and
+ * {@link #getSpeedAccuracyMetersPerSecond()} returns 1.0, then there is a 68% probably of the
+ * true speed being between 4.0 and 6.0 meters per second.
+ *
+ * <p>Note that the speed and speed accuracy is often better than would be obtained simply from
+ * differencing sequential positions, such as when the Doppler measurements from GNSS satellites
+ * are used.
+ *
+ * <p>If this location does not have a speed accuracy, then 0.0 is returned.
+ */
+ public float getSpeedAccuracyMetersPerSecond() {
+ return mSpeedAccuracyMetersPerSecond;
+ }
+
+ /**
+ * Set the estimated speed accuracy of this location, meters per second.
+ *
+ * <p>See {@link #getSpeedAccuracyMetersPerSecond} for the definition of speed accuracy.
+ *
+ * <p>Following this call {@link #hasSpeedAccuracy} will return true.
+ */
+ public void setSpeedAccuracyMetersPerSecond(float speedAccuracyMeterPerSecond) {
+ mSpeedAccuracyMetersPerSecond = speedAccuracyMeterPerSecond;
+ mFieldsMask |= HAS_SPEED_ACCURACY_MASK;
+ }
+
+ /**
+ * Remove the speed accuracy from this location.
+ *
+ * <p>Following this call {@link #hasSpeedAccuracy} will return false, and
+ * {@link #getSpeedAccuracyMetersPerSecond} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
+ * @removed
+ */
+ @Deprecated
+ public void removeSpeedAccuracy() {
+ mSpeedAccuracyMetersPerSecond = 0.0f;
+ mFieldsMask &= ~HAS_SPEED_ACCURACY_MASK;
+ }
+
+ /**
+ * True if this location has a bearing accuracy.
+ */
+ public boolean hasBearingAccuracy() {
+ return (mFieldsMask & HAS_BEARING_ACCURACY_MASK) != 0;
+ }
+
+ /**
+ * Get the estimated bearing accuracy of this location, in degrees.
+ *
+ * <p>We define bearing accuracy as a 1-standard-deviation value, i.e. as 1-side of the
+ * 2-sided range on each side of the estimated bearing reported by {@link #getBearing()},
+ * within which there is a 68% probability of finding the true bearing.
+ *
+ * <p>For example, if {@link #getBearing()} returns 60., and
+ * {@link #getBearingAccuracyDegrees()} ()} returns 10., then there is a 68% probably of the
+ * true bearing being between 50. and 70. degrees.
+ *
+ * <p>If this location does not have a bearing accuracy, then 0.0 is returned.
+ */
+ public float getBearingAccuracyDegrees() {
+ return mBearingAccuracyDegrees;
+ }
+
+ /**
+ * Set the estimated bearing accuracy of this location, degrees.
+ *
+ * <p>See {@link #getBearingAccuracyDegrees} for the definition of bearing accuracy.
+ *
+ * <p>Following this call {@link #hasBearingAccuracy} will return true.
+ */
+ public void setBearingAccuracyDegrees(float bearingAccuracyDegrees) {
+ mBearingAccuracyDegrees = bearingAccuracyDegrees;
+ mFieldsMask |= HAS_BEARING_ACCURACY_MASK;
+ }
+
+ /**
+ * Remove the bearing accuracy from this location.
+ *
+ * <p>Following this call {@link #hasBearingAccuracy} will return false, and
+ * {@link #getBearingAccuracyDegrees} will return 0.0.
+ *
+ * @deprecated use a new Location object for location updates.
+ * @removed
+ */
+ @Deprecated
+ public void removeBearingAccuracy() {
+ mBearingAccuracyDegrees = 0.0f;
+ mFieldsMask &= ~HAS_BEARING_ACCURACY_MASK;
+ }
+
+ /**
+ * Return true if this Location object is complete.
+ *
+ * <p>A location object is currently considered complete if it has
+ * a valid provider, accuracy, wall-clock time and elapsed real-time.
+ *
+ * <p>All locations supplied by the {@link LocationManager} to
+ * applications must be complete.
+ *
+ * @see #makeComplete
+ * @hide
+ */
+ @SystemApi
+ public boolean isComplete() {
+ if (mProvider == null) return false;
+ if (!hasAccuracy()) return false;
+ if (mTime == 0) return false;
+ if (mElapsedRealtimeNanos == 0) return false;
+ return true;
+ }
+
+ /**
+ * Helper to fill incomplete fields.
+ *
+ * <p>Used to assist in backwards compatibility with
+ * Location objects received from applications.
+ *
+ * @see #isComplete
+ * @hide
+ */
+ @SystemApi
+ public void makeComplete() {
+ if (mProvider == null) mProvider = "?";
+ if (!hasAccuracy()) {
+ mFieldsMask |= HAS_HORIZONTAL_ACCURACY_MASK;
+ mHorizontalAccuracyMeters = 100.0f;
+ }
+ if (mTime == 0) mTime = System.currentTimeMillis();
+ if (mElapsedRealtimeNanos == 0) mElapsedRealtimeNanos = SystemClock.elapsedRealtimeNanos();
+ }
+
+ /**
+ * Returns additional provider-specific information about the
+ * location fix as a Bundle. The keys and values are determined
+ * by the provider. If no additional information is available,
+ * null is returned.
+ *
+ * <p> A number of common key/value pairs are listed
+ * below. Providers that use any of the keys on this list must
+ * provide the corresponding value as described below.
+ *
+ * <ul>
+ * <li> satellites - the number of satellites used to derive the fix
+ * </ul>
+ */
+ public Bundle getExtras() {
+ return mExtras;
+ }
+
+ /**
+ * Sets the extra information associated with this fix to the
+ * given Bundle.
+ */
+ public void setExtras(Bundle extras) {
+ mExtras = (extras == null) ? null : new Bundle(extras);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("Location[");
+ s.append(mProvider);
+ s.append(String.format(" %.6f,%.6f", mLatitude, mLongitude));
+ if (hasAccuracy()) s.append(String.format(" hAcc=%.0f", mHorizontalAccuracyMeters));
+ else s.append(" hAcc=???");
+ if (mTime == 0) {
+ s.append(" t=?!?");
+ }
+ if (mElapsedRealtimeNanos == 0) {
+ s.append(" et=?!?");
+ } else {
+ s.append(" et=");
+ TimeUtils.formatDuration(mElapsedRealtimeNanos / 1000000L, s);
+ }
+ if (hasAltitude()) s.append(" alt=").append(mAltitude);
+ if (hasSpeed()) s.append(" vel=").append(mSpeed);
+ if (hasBearing()) s.append(" bear=").append(mBearing);
+ if (hasVerticalAccuracy()) s.append(String.format(" vAcc=%.0f", mVerticalAccuracyMeters));
+ else s.append(" vAcc=???");
+ if (hasSpeedAccuracy()) s.append(String.format(" sAcc=%.0f", mSpeedAccuracyMetersPerSecond));
+ else s.append(" sAcc=???");
+ if (hasBearingAccuracy()) s.append(String.format(" bAcc=%.0f", mBearingAccuracyDegrees));
+ else s.append(" bAcc=???");
+ if (isFromMockProvider()) s.append(" mock");
+
+ if (mExtras != null) {
+ s.append(" {").append(mExtras).append('}');
+ }
+ s.append(']');
+ return s.toString();
+ }
+
+ public void dump(Printer pw, String prefix) {
+ pw.println(prefix + toString());
+ }
+
+ public static final Parcelable.Creator<Location> CREATOR =
+ new Parcelable.Creator<Location>() {
+ @Override
+ public Location createFromParcel(Parcel in) {
+ String provider = in.readString();
+ Location l = new Location(provider);
+ l.mTime = in.readLong();
+ l.mElapsedRealtimeNanos = in.readLong();
+ l.mFieldsMask = in.readByte();
+ l.mLatitude = in.readDouble();
+ l.mLongitude = in.readDouble();
+ l.mAltitude = in.readDouble();
+ l.mSpeed = in.readFloat();
+ l.mBearing = in.readFloat();
+ l.mHorizontalAccuracyMeters = in.readFloat();
+ l.mVerticalAccuracyMeters = in.readFloat();
+ l.mSpeedAccuracyMetersPerSecond = in.readFloat();
+ l.mBearingAccuracyDegrees = in.readFloat();
+ l.mExtras = Bundle.setDefusable(in.readBundle(), true);
+ return l;
+ }
+
+ @Override
+ public Location[] newArray(int size) {
+ return new Location[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeString(mProvider);
+ parcel.writeLong(mTime);
+ parcel.writeLong(mElapsedRealtimeNanos);
+ parcel.writeByte(mFieldsMask);
+ parcel.writeDouble(mLatitude);
+ parcel.writeDouble(mLongitude);
+ parcel.writeDouble(mAltitude);
+ parcel.writeFloat(mSpeed);
+ parcel.writeFloat(mBearing);
+ parcel.writeFloat(mHorizontalAccuracyMeters);
+ parcel.writeFloat(mVerticalAccuracyMeters);
+ parcel.writeFloat(mSpeedAccuracyMetersPerSecond);
+ parcel.writeFloat(mBearingAccuracyDegrees);
+ parcel.writeBundle(mExtras);
+ }
+
+ /**
+ * Returns one of the optional extra {@link Location}s that can be attached
+ * to this Location.
+ *
+ * @param key the key associated with the desired extra Location
+ * @return the extra Location, or null if unavailable
+ * @hide
+ */
+ public Location getExtraLocation(String key) {
+ if (mExtras != null) {
+ Parcelable value = mExtras.getParcelable(key);
+ if (value instanceof Location) {
+ return (Location) value;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Attaches an extra {@link Location} to this Location.
+ *
+ * @param key the key associated with the Location extra
+ * @param value the Location to attach
+ * @hide
+ */
+ public void setExtraLocation(String key, Location value) {
+ if (mExtras == null) {
+ mExtras = new Bundle();
+ }
+ mExtras.putParcelable(key, value);
+ }
+
+ /**
+ * Returns true if the Location came from a mock provider.
+ *
+ * @return true if this Location came from a mock provider, false otherwise
+ */
+ public boolean isFromMockProvider() {
+ return (mFieldsMask & HAS_MOCK_PROVIDER_MASK) != 0;
+ }
+
+ /**
+ * Flag this Location as having come from a mock provider or not.
+ *
+ * @param isFromMockProvider true if this Location came from a mock provider, false otherwise
+ * @hide
+ */
+ @SystemApi
+ public void setIsFromMockProvider(boolean isFromMockProvider) {
+ if (isFromMockProvider) {
+ mFieldsMask |= HAS_MOCK_PROVIDER_MASK;
+ } else {
+ mFieldsMask &= ~HAS_MOCK_PROVIDER_MASK;
+ }
+ }
+
+ /**
+ * Caches data used to compute distance and bearing (so successive calls to {@link #distanceTo}
+ * and {@link #bearingTo} don't duplicate work.
+ */
+ private static class BearingDistanceCache {
+ private double mLat1 = 0.0;
+ private double mLon1 = 0.0;
+ private double mLat2 = 0.0;
+ private double mLon2 = 0.0;
+ private float mDistance = 0.0f;
+ private float mInitialBearing = 0.0f;
+ private float mFinalBearing = 0.0f;
+ }
+}
diff --git a/android/location/LocationListener.java b/android/location/LocationListener.java
new file mode 100644
index 00000000..88904c82
--- /dev/null
+++ b/android/location/LocationListener.java
@@ -0,0 +1,89 @@
+/*
+ * 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 android.os.Bundle;
+
+/**
+ * Used for receiving notifications from the LocationManager when
+ * the location has changed. These methods are called if the
+ * LocationListener has been registered with the location manager service
+ * using the {@link LocationManager#requestLocationUpdates(String, long, float, LocationListener)}
+ * method.
+ *
+ * <div class="special reference">
+ * <h3>Developer Guides</h3>
+ * <p>For more information about identifying user location, read the
+ * <a href="{@docRoot}guide/topics/location/obtaining-user-location.html">Obtaining User
+ * Location</a> developer guide.</p>
+ * </div>
+ */
+public interface LocationListener {
+
+ /**
+ * Called when the location has changed.
+ *
+ * <p> There are no restrictions on the use of the supplied Location object.
+ *
+ * @param location The new location, as a Location object.
+ */
+ void onLocationChanged(Location location);
+
+ /**
+ * Called when the provider status changes. This method is called when
+ * a provider is unable to fetch a location or if the provider has recently
+ * become available after a period of unavailability.
+ *
+ * @param provider the name of the location provider associated with this
+ * update.
+ * @param status {@link LocationProvider#OUT_OF_SERVICE} if the
+ * provider is out of service, and this is not expected to change in the
+ * near future; {@link LocationProvider#TEMPORARILY_UNAVAILABLE} if
+ * the provider is temporarily unavailable but is expected to be available
+ * shortly; and {@link LocationProvider#AVAILABLE} if the
+ * provider is currently available.
+ * @param extras an optional Bundle which will contain provider specific
+ * status variables.
+ *
+ * <p> A number of common key/value pairs for the extras Bundle are listed
+ * below. Providers that use any of the keys on this list must
+ * provide the corresponding value as described below.
+ *
+ * <ul>
+ * <li> satellites - the number of satellites used to derive the fix
+ * </ul>
+ */
+ void onStatusChanged(String provider, int status, Bundle extras);
+
+ /**
+ * Called when the provider is enabled by the user.
+ *
+ * @param provider the name of the location provider associated with this
+ * update.
+ */
+ void onProviderEnabled(String provider);
+
+ /**
+ * Called when the provider is disabled by the user. If requestLocationUpdates
+ * is called on an already disabled provider, this method is called
+ * immediately.
+ *
+ * @param provider the name of the location provider associated with this
+ * update.
+ */
+ void onProviderDisabled(String provider);
+}
diff --git a/android/location/LocationManager.java b/android/location/LocationManager.java
new file mode 100644
index 00000000..968f596e
--- /dev/null
+++ b/android/location/LocationManager.java
@@ -0,0 +1,2135 @@
+/*
+ * Copyright (C) 2007 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 com.android.internal.location.ProviderProperties;
+
+import android.Manifest;
+import android.annotation.RequiresPermission;
+import android.annotation.SuppressLint;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+
+import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+
+/**
+ * This class provides access to the system location services. These
+ * services allow applications to obtain periodic updates of the
+ * device's geographical location, or to fire an application-specified
+ * {@link Intent} when the device enters the proximity of a given
+ * geographical location.
+ *
+ * <p class="note">Unless noted, all Location API methods require
+ * the {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permissions.
+ * If your application only has the coarse permission then it will not have
+ * access to the GPS or passive location providers. Other providers will still
+ * return location results, but the update rate will be throttled and the exact
+ * location will be obfuscated to a coarse level of accuracy.
+ */
+@SystemService(Context.LOCATION_SERVICE)
+public class LocationManager {
+ private static final String TAG = "LocationManager";
+
+ private final Context mContext;
+ private final ILocationManager mService;
+ private final GnssMeasurementCallbackTransport mGnssMeasurementCallbackTransport;
+ private final GnssNavigationMessageCallbackTransport mGnssNavigationMessageCallbackTransport;
+ private final BatchedLocationCallbackTransport mBatchedLocationCallbackTransport;
+ private final HashMap<GpsStatus.Listener, GnssStatusListenerTransport> mGpsStatusListeners =
+ new HashMap<>();
+ private final HashMap<GpsStatus.NmeaListener, GnssStatusListenerTransport> mGpsNmeaListeners =
+ new HashMap<>();
+ private final HashMap<GnssStatus.Callback, GnssStatusListenerTransport> mGnssStatusListeners =
+ new HashMap<>();
+ private final HashMap<OnNmeaMessageListener, GnssStatusListenerTransport> mGnssNmeaListeners =
+ new HashMap<>();
+ // volatile + GnssStatus final-fields pattern to avoid a partially published object
+ private volatile GnssStatus mGnssStatus;
+ private int mTimeToFirstFix;
+
+ /**
+ * Name of the network location provider.
+ * <p>This provider determines location based on
+ * availability of cell tower and WiFi access points. Results are retrieved
+ * by means of a network lookup.
+ */
+ public static final String NETWORK_PROVIDER = "network";
+
+ /**
+ * Name of the GPS location provider.
+ *
+ * <p>This provider determines location using
+ * satellites. Depending on conditions, this provider may take a while to return
+ * a location fix. Requires the permission
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}.
+ *
+ * <p> The extras Bundle for the GPS location provider can contain the
+ * following key/value pairs:
+ * <ul>
+ * <li> satellites - the number of satellites used to derive the fix
+ * </ul>
+ */
+ public static final String GPS_PROVIDER = "gps";
+
+ /**
+ * A special location provider for receiving locations without actually initiating
+ * a location fix.
+ *
+ * <p>This provider can be used to passively receive location updates
+ * when other applications or services request them without actually requesting
+ * the locations yourself. This provider will return locations generated by other
+ * providers. You can query the {@link Location#getProvider()} method to determine
+ * the origin of the location update. Requires the permission
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION}, although if the GPS is
+ * not enabled this provider might only return coarse fixes.
+ */
+ public static final String PASSIVE_PROVIDER = "passive";
+
+ /**
+ * Name of the Fused location provider.
+ *
+ * <p>This provider combines inputs for all possible location sources
+ * to provide the best possible Location fix. It is implicitly
+ * used for all API's that involve the {@link LocationRequest}
+ * object.
+ *
+ * @hide
+ */
+ public static final String FUSED_PROVIDER = "fused";
+
+ /**
+ * Key used for the Bundle extra holding a boolean indicating whether
+ * a proximity alert is entering (true) or exiting (false)..
+ */
+ 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";
+
+ /**
+ * Broadcast intent action indicating that the GPS has either been
+ * enabled or disabled. An intent extra provides this state as a boolean,
+ * where {@code true} means enabled.
+ * @see #EXTRA_GPS_ENABLED
+ *
+ * @hide
+ */
+ public static final String GPS_ENABLED_CHANGE_ACTION =
+ "android.location.GPS_ENABLED_CHANGE";
+
+ /**
+ * Broadcast intent action when the configured location providers
+ * change. For use with {@link #isProviderEnabled(String)}. If you're interacting with the
+ * {@link android.provider.Settings.Secure#LOCATION_MODE} API, use {@link #MODE_CHANGED_ACTION}
+ * instead.
+ */
+ public static final String PROVIDERS_CHANGED_ACTION =
+ "android.location.PROVIDERS_CHANGED";
+
+ /**
+ * Broadcast intent action when {@link android.provider.Settings.Secure#LOCATION_MODE} changes.
+ * For use with the {@link android.provider.Settings.Secure#LOCATION_MODE} API.
+ * If you're interacting with {@link #isProviderEnabled(String)}, use
+ * {@link #PROVIDERS_CHANGED_ACTION} instead.
+ *
+ * In the future, there may be mode changes that do not result in
+ * {@link #PROVIDERS_CHANGED_ACTION} broadcasts.
+ */
+ public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
+
+ /**
+ * Broadcast intent action indicating that the GPS has either started or
+ * stopped receiving GPS fixes. An intent extra provides this state as a
+ * boolean, where {@code true} means that the GPS is actively receiving fixes.
+ * @see #EXTRA_GPS_ENABLED
+ *
+ * @hide
+ */
+ public static final String GPS_FIX_CHANGE_ACTION =
+ "android.location.GPS_FIX_CHANGE";
+
+ /**
+ * The lookup key for a boolean that indicates whether GPS is enabled or
+ * disabled. {@code true} means GPS is enabled. Retrieve it with
+ * {@link android.content.Intent#getBooleanExtra(String,boolean)}.
+ *
+ * @hide
+ */
+ public static final String EXTRA_GPS_ENABLED = "enabled";
+
+ /**
+ * Broadcast intent action indicating that a high power location requests
+ * has either started or stopped being active. The current state of
+ * active location requests should be read from AppOpsManager using
+ * {@code OP_MONITOR_HIGH_POWER_LOCATION}.
+ *
+ * @hide
+ */
+ public static final String HIGH_POWER_REQUEST_CHANGE_ACTION =
+ "android.location.HIGH_POWER_REQUEST_CHANGE";
+
+ // Map from LocationListeners to their associated ListenerTransport objects
+ private HashMap<LocationListener,ListenerTransport> mListeners =
+ new HashMap<LocationListener,ListenerTransport>();
+
+ private class ListenerTransport extends ILocationListener.Stub {
+ private static final int TYPE_LOCATION_CHANGED = 1;
+ private static final int TYPE_STATUS_CHANGED = 2;
+ private static final int TYPE_PROVIDER_ENABLED = 3;
+ private static final int TYPE_PROVIDER_DISABLED = 4;
+
+ private LocationListener mListener;
+ private final Handler mListenerHandler;
+
+ ListenerTransport(LocationListener listener, Looper looper) {
+ mListener = listener;
+
+ if (looper == null) {
+ mListenerHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ _handleMessage(msg);
+ }
+ };
+ } else {
+ mListenerHandler = new Handler(looper) {
+ @Override
+ public void handleMessage(Message msg) {
+ _handleMessage(msg);
+ }
+ };
+ }
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_LOCATION_CHANGED;
+ msg.obj = location;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onStatusChanged(String provider, int status, Bundle extras) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_STATUS_CHANGED;
+ Bundle b = new Bundle();
+ b.putString("provider", provider);
+ b.putInt("status", status);
+ if (extras != null) {
+ b.putBundle("extras", extras);
+ }
+ msg.obj = b;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_PROVIDER_ENABLED;
+ msg.obj = provider;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ Message msg = Message.obtain();
+ msg.what = TYPE_PROVIDER_DISABLED;
+ msg.obj = provider;
+ mListenerHandler.sendMessage(msg);
+ }
+
+ private void _handleMessage(Message msg) {
+ switch (msg.what) {
+ case TYPE_LOCATION_CHANGED:
+ Location location = new Location((Location) msg.obj);
+ mListener.onLocationChanged(location);
+ break;
+ case TYPE_STATUS_CHANGED:
+ Bundle b = (Bundle) msg.obj;
+ String provider = b.getString("provider");
+ int status = b.getInt("status");
+ Bundle extras = b.getBundle("extras");
+ mListener.onStatusChanged(provider, status, extras);
+ break;
+ case TYPE_PROVIDER_ENABLED:
+ mListener.onProviderEnabled((String) msg.obj);
+ break;
+ case TYPE_PROVIDER_DISABLED:
+ mListener.onProviderDisabled((String) msg.obj);
+ break;
+ }
+ try {
+ mService.locationCallbackFinished(this);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * @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
+ * factory Context.getSystemService.
+ */
+ public LocationManager(Context context, ILocationManager service) {
+ mService = service;
+ mContext = context;
+ mGnssMeasurementCallbackTransport =
+ new GnssMeasurementCallbackTransport(mContext, mService);
+ mGnssNavigationMessageCallbackTransport =
+ new GnssNavigationMessageCallbackTransport(mContext, mService);
+ mBatchedLocationCallbackTransport =
+ new BatchedLocationCallbackTransport(mContext, mService);
+
+ }
+
+ private LocationProvider createProvider(String name, ProviderProperties properties) {
+ return new LocationProvider(name, properties);
+ }
+
+ /**
+ * Returns a list of the names of all known location providers.
+ * <p>All providers are returned, including ones that are not permitted to
+ * be accessed by the calling activity or are currently disabled.
+ *
+ * @return list of Strings containing names of the provider
+ */
+ public List<String> getAllProviders() {
+ try {
+ return mService.getAllProviders();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns a list of the names of location providers.
+ *
+ * @param enabledOnly if true then only the providers which are currently
+ * enabled are returned.
+ * @return list of Strings containing names of the providers
+ */
+ public List<String> getProviders(boolean enabledOnly) {
+ try {
+ return mService.getProviders(null, enabledOnly);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the information associated with the location provider of the
+ * given name, or null if no provider exists by that name.
+ *
+ * @param name the provider name
+ * @return a LocationProvider, or null
+ *
+ * @throws IllegalArgumentException if name is null or does not exist
+ * @throws SecurityException if the caller is not permitted to access the
+ * given provider.
+ */
+ public LocationProvider getProvider(String name) {
+ checkProvider(name);
+ try {
+ ProviderProperties properties = mService.getProviderProperties(name);
+ if (properties == null) {
+ return null;
+ }
+ return createProvider(name, properties);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns a list of the names of LocationProviders that satisfy the given
+ * criteria, or null if none do. Only providers that are permitted to be
+ * accessed by the calling activity will be returned.
+ *
+ * @param criteria the criteria that the returned providers must match
+ * @param enabledOnly if true then only the providers which are currently
+ * enabled are returned.
+ * @return list of Strings containing names of the providers
+ */
+ public List<String> getProviders(Criteria criteria, boolean enabledOnly) {
+ checkCriteria(criteria);
+ try {
+ return mService.getProviders(criteria, enabledOnly);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * 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
+ */
+ public String getBestProvider(Criteria criteria, boolean enabledOnly) {
+ checkCriteria(criteria);
+ try {
+ return mService.getBestProvider(criteria, enabledOnly);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Register for location updates using the named provider, and a
+ * pending intent.
+ *
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this method.
+ *
+ * @param provider the name of the provider with which to register
+ * @param minTime minimum time interval between location updates, in milliseconds
+ * @param minDistance minimum distance between location updates, in meters
+ * @param listener a {@link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called for
+ * each location update
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * on this device
+ * @throws IllegalArgumentException if listener is null
+ * @throws RuntimeException if the calling thread has no Looper
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(String provider, long minTime, float minDistance,
+ LocationListener listener) {
+ checkProvider(provider);
+ checkListener(listener);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTime, minDistance, false);
+ requestLocationUpdates(request, listener, null, null);
+ }
+
+ /**
+ * Register for location updates using the named provider, and a callback on
+ * the specified looper thread.
+ *
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this method.
+ *
+ * @param provider the name of the provider with which to register
+ * @param minTime minimum time interval between location updates, in milliseconds
+ * @param minDistance minimum distance between location updates, in meters
+ * @param listener a {@link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called for
+ * each location update
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism, or null to make callbacks on the calling
+ * thread
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(String provider, long minTime, float minDistance,
+ LocationListener listener, Looper looper) {
+ checkProvider(provider);
+ checkListener(listener);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTime, minDistance, false);
+ requestLocationUpdates(request, listener, looper, null);
+ }
+
+ /**
+ * Register for location updates using a Criteria, and a callback
+ * on the specified looper thread.
+ *
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this method.
+ *
+ * @param minTime minimum time interval between location updates, in milliseconds
+ * @param minDistance minimum distance between location updates, in meters
+ * @param criteria contains parameters for the location manager to choose the
+ * appropriate provider and parameters to compute the location
+ * @param listener a {@link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called for
+ * each location update
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism, or null to make callbacks on the calling
+ * thread
+ *
+ * @throws IllegalArgumentException if criteria is null
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
+ LocationListener listener, Looper looper) {
+ checkCriteria(criteria);
+ checkListener(listener);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, minTime, minDistance, false);
+ requestLocationUpdates(request, listener, looper, null);
+ }
+
+ /**
+ * Register for location updates using the named provider, and a
+ * pending intent.
+ *
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this method.
+ *
+ * @param provider the name of the provider with which to register
+ * @param minTime minimum time interval between location updates, in milliseconds
+ * @param minDistance minimum distance between location updates, in meters
+ * @param intent a {@link PendingIntent} to be sent for each location update
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * on this device
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(String provider, long minTime, float minDistance,
+ PendingIntent intent) {
+ checkProvider(provider);
+ checkPendingIntent(intent);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTime, minDistance, false);
+ requestLocationUpdates(request, null, null, intent);
+ }
+
+ /**
+ * Register for location updates using a Criteria and pending intent.
+ *
+ * <p>The <code>requestLocationUpdates()</code> and
+ * <code>requestSingleUpdate()</code> register the current activity to be
+ * updated periodically by the named provider, or by the provider matching
+ * the specified {@link Criteria}, with location and status updates.
+ *
+ * <p> It may take a while to receive the first location update. If
+ * an immediate location is required, applications may use the
+ * {@link #getLastKnownLocation(String)} method.
+ *
+ * <p> Location updates are received either by {@link LocationListener}
+ * callbacks, or by broadcast intents to a supplied {@link PendingIntent}.
+ *
+ * <p> If the caller supplied a pending intent, then location updates
+ * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
+ * {@link android.location.Location} value.
+ *
+ * <p> The location update interval can be controlled using the minTime parameter.
+ * The elapsed time between location updates will never be less than
+ * minTime, although it can be more depending on the Location Provider
+ * implementation and the update interval requested by other applications.
+ *
+ * <p> Choosing a sensible value for minTime is important to conserve
+ * battery life. Each location update requires power from
+ * GPS, WIFI, Cell and other radios. Select a minTime value as high as
+ * possible while still providing a reasonable user experience.
+ * If your application is not in the foreground and showing
+ * location to the user then your application should avoid using an active
+ * provider (such as {@link #NETWORK_PROVIDER} or {@link #GPS_PROVIDER}),
+ * but if you insist then select a minTime of 5 * 60 * 1000 (5 minutes)
+ * or greater. If your application is in the foreground and showing
+ * location to the user then it is appropriate to select a faster
+ * update interval.
+ *
+ * <p> The minDistance parameter can also be used to control the
+ * frequency of location updates. If it is greater than 0 then the
+ * location provider will only send your application an update when
+ * the location has changed by at least minDistance meters, AND
+ * at least minTime milliseconds have passed. However it is more
+ * difficult for location providers to save power using the minDistance
+ * parameter, so minTime should be the primary tool to conserving battery
+ * life.
+ *
+ * <p> If your application wants to passively observe location
+ * updates triggered by other applications, but not consume
+ * any additional power otherwise, then use the {@link #PASSIVE_PROVIDER}
+ * This provider does not actively turn on or modify active location
+ * providers, so you do not need to be as careful about minTime and
+ * minDistance. However if your application performs heavy work
+ * on a location update (such as network activity) then you should
+ * select non-zero values for minTime and/or minDistance to rate-limit
+ * your update frequency in the case another application enables a
+ * location provider with extremely fast updates.
+ *
+ * <p>In case the provider is disabled by the user, updates will stop,
+ * and a provider availability update will be sent.
+ * As soon as the provider is enabled again,
+ * location updates will immediately resume and a provider availability
+ * update sent. Providers can also send status updates, at any time,
+ * with extra's specific to the provider. If a callback was supplied
+ * then status and availability updates are via
+ * {@link LocationListener#onProviderDisabled},
+ * {@link LocationListener#onProviderEnabled} or
+ * {@link LocationListener#onStatusChanged}. Alternately, if a
+ * pending intent was supplied then status and availability updates
+ * are broadcast intents with extra keys of
+ * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED}.
+ *
+ * <p> If a {@link LocationListener} is used but with no Looper specified
+ * then the calling thread must already
+ * be a {@link android.os.Looper} thread such as the main thread of the
+ * calling Activity. If a Looper is specified with a {@link LocationListener}
+ * then callbacks are made on the supplied Looper thread.
+ *
+ * <p class="note"> Prior to Jellybean, the minTime parameter was
+ * only a hint, and some location provider implementations ignored it.
+ * From Jellybean and onwards it is mandatory for Android compatible
+ * devices to observe both the minTime and minDistance parameters.
+ *
+ * @param minTime minimum time interval between location updates, in milliseconds
+ * @param minDistance minimum distance between location updates, in meters
+ * @param criteria contains parameters for the location manager to choose the
+ * appropriate provider and parameters to compute the location
+ * @param intent a {@link PendingIntent} to be sent for each location update
+ *
+ * @throws IllegalArgumentException if criteria is null
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(long minTime, float minDistance, Criteria criteria,
+ PendingIntent intent) {
+ checkCriteria(criteria);
+ checkPendingIntent(intent);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, minTime, minDistance, false);
+ requestLocationUpdates(request, null, null, intent);
+ }
+
+ /**
+ * Register for a single location update using the named provider and
+ * a callback.
+ *
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this method.
+ *
+ * @param provider the name of the provider with which to register
+ * @param listener a {@link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called when
+ * the location update is available
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism, or null to make callbacks on the calling
+ * thread
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestSingleUpdate(String provider, LocationListener listener, Looper looper) {
+ checkProvider(provider);
+ checkListener(listener);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, 0, 0, true);
+ requestLocationUpdates(request, listener, looper, null);
+ }
+
+ /**
+ * Register for a single location update using a Criteria and
+ * a callback.
+ *
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this method.
+ *
+ * @param criteria contains parameters for the location manager to choose the
+ * appropriate provider and parameters to compute the location
+ * @param listener a {@link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called when
+ * the location update is available
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism, or null to make callbacks on the calling
+ * thread
+ *
+ * @throws IllegalArgumentException if criteria is null
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestSingleUpdate(Criteria criteria, LocationListener listener, Looper looper) {
+ checkCriteria(criteria);
+ checkListener(listener);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, 0, 0, true);
+ requestLocationUpdates(request, listener, looper, null);
+ }
+
+ /**
+ * Register for a single location update using a named provider and pending intent.
+ *
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this method.
+ *
+ * @param provider the name of the provider with which to register
+ * @param intent a {@link PendingIntent} to be sent for the 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
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestSingleUpdate(String provider, PendingIntent intent) {
+ checkProvider(provider);
+ checkPendingIntent(intent);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, 0, 0, true);
+ requestLocationUpdates(request, null, null, intent);
+ }
+
+ /**
+ * Register for a single location update using a Criteria and pending intent.
+ *
+ * <p>See {@link #requestLocationUpdates(long, float, Criteria, PendingIntent)}
+ * for more detail on how to use this method.
+ *
+ * @param criteria contains parameters for the location manager to choose the
+ * appropriate provider and parameters to compute the location
+ * @param intent a {@link PendingIntent} to be sent for the 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
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestSingleUpdate(Criteria criteria, PendingIntent intent) {
+ checkCriteria(criteria);
+ checkPendingIntent(intent);
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, 0, 0, true);
+ requestLocationUpdates(request, null, null, intent);
+ }
+
+ /**
+ * Register for fused location updates using a LocationRequest and callback.
+ *
+ * <p>Upon a location update, the system delivers the new {@link Location} to the
+ * provided {@link LocationListener}, by calling its {@link
+ * LocationListener#onLocationChanged} method.</p>
+ *
+ * <p>The system will automatically select and enable the best providers
+ * to compute a location for your application. It may use only passive
+ * locations, or just a single location source, or it may fuse together
+ * multiple location sources in order to produce the best possible
+ * result, depending on the quality of service requested in the
+ * {@link LocationRequest}.
+ *
+ * <p>LocationRequest can be null, in which case the system will choose
+ * default, low power parameters for location updates. You will occasionally
+ * receive location updates as available, without a major power impact on the
+ * system. If your application just needs an occasional location update
+ * without any strict demands, then pass a null LocationRequest.
+ *
+ * <p>Only one LocationRequest can be registered for each unique callback
+ * or pending intent. So a subsequent request with the same callback or
+ * pending intent will over-write the previous LocationRequest.
+ *
+ * <p> If a pending intent is supplied then location updates
+ * are sent with a key of {@link #KEY_LOCATION_CHANGED} and a
+ * {@link android.location.Location} value. If a callback is supplied
+ * then location updates are made using the
+ * {@link LocationListener#onLocationChanged} callback, on the specified
+ * Looper thread. If a {@link LocationListener} is used
+ * but with a null Looper then the calling thread must already
+ * be a {@link android.os.Looper} thread (such as the main thread) and
+ * callbacks will occur on this thread.
+ *
+ * <p> Provider status updates and availability updates are deprecated
+ * because the system is performing provider fusion on the applications
+ * behalf. So {@link LocationListener#onProviderDisabled},
+ * {@link LocationListener#onProviderEnabled}, {@link LocationListener#onStatusChanged}
+ * will not be called, and intents with extra keys of
+ * {@link #KEY_PROVIDER_ENABLED} or {@link #KEY_STATUS_CHANGED} will not
+ * be received.
+ *
+ * <p> To unregister for Location updates, use: {@link #removeUpdates(LocationListener)}.
+ *
+ * @param request quality of service required, null for default low power
+ * @param listener a {@link LocationListener} whose
+ * {@link LocationListener#onLocationChanged} method will be called when
+ * the location update is available
+ * @param looper a Looper object whose message queue will be used to
+ * implement the callback mechanism, or null to make callbacks on the calling
+ * thread
+ *
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(LocationRequest request, LocationListener listener,
+ Looper looper) {
+ checkListener(listener);
+ requestLocationUpdates(request, listener, looper, null);
+ }
+
+
+ /**
+ * Register for fused location updates using a LocationRequest and a pending intent.
+ *
+ * <p>Upon a location update, the system delivers the new {@link Location} with your provided
+ * {@link PendingIntent}, as the value for {@link LocationManager#KEY_LOCATION_CHANGED}
+ * in the intent's extras.</p>
+ *
+ * <p> To unregister for Location updates, use: {@link #removeUpdates(PendingIntent)}.
+ *
+ * <p> See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)}
+ * for more detail.
+ *
+ * @param request quality of service required, null for default low power
+ * @param intent a {@link PendingIntent} to be sent for the location update
+ *
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(LocationRequest request, PendingIntent intent) {
+ checkPendingIntent(intent);
+ requestLocationUpdates(request, null, null, intent);
+ }
+
+ private ListenerTransport wrapListener(LocationListener listener, Looper looper) {
+ if (listener == null) return null;
+ synchronized (mListeners) {
+ ListenerTransport transport = mListeners.get(listener);
+ if (transport == null) {
+ transport = new ListenerTransport(listener, looper);
+ }
+ mListeners.put(listener, transport);
+ return transport;
+ }
+ }
+
+ private void requestLocationUpdates(LocationRequest request, LocationListener listener,
+ Looper looper, PendingIntent intent) {
+
+ String packageName = mContext.getPackageName();
+
+ // wrap the listener class
+ ListenerTransport transport = wrapListener(listener, looper);
+
+ try {
+ mService.requestLocationUpdates(request, transport, intent, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes all location updates for the specified LocationListener.
+ *
+ * <p>Following this call, updates will no longer
+ * occur for this listener.
+ *
+ * @param listener listener object that no longer needs location updates
+ * @throws IllegalArgumentException if listener is null
+ */
+ public void removeUpdates(LocationListener listener) {
+ checkListener(listener);
+ String packageName = mContext.getPackageName();
+
+ ListenerTransport transport;
+ synchronized (mListeners) {
+ transport = mListeners.remove(listener);
+ }
+ if (transport == null) return;
+
+ try {
+ mService.removeUpdates(transport, null, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes all location updates for the specified pending intent.
+ *
+ * <p>Following this call, updates will no longer for this pending intent.
+ *
+ * @param intent pending intent object that no longer needs location updates
+ * @throws IllegalArgumentException if intent is null
+ */
+ public void removeUpdates(PendingIntent intent) {
+ checkPendingIntent(intent);
+ String packageName = mContext.getPackageName();
+
+ try {
+ mService.removeUpdates(null, intent, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set a proximity alert for the location given by the position
+ * (latitude, longitude) and the given radius.
+ *
+ * <p> When the device
+ * detects that it has entered or exited the area surrounding the
+ * location, the given PendingIntent will be used to create an Intent
+ * to be fired.
+ *
+ * <p> The fired Intent will have a boolean extra added with key
+ * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
+ * entering the proximity region; if false, it is exiting.
+ *
+ * <p> Due to the approximate nature of position estimation, if the
+ * device passes through the given area briefly, it is possible
+ * that no Intent will be fired. Similarly, an Intent could be
+ * fired if the device passes very close to the given area but
+ * does not actually enter it.
+ *
+ * <p> After the number of milliseconds given by the expiration
+ * parameter, the location manager will delete this proximity
+ * alert and no longer monitor it. A value of -1 indicates that
+ * there should be no expiration time.
+ *
+ * <p> Internally, this method uses both {@link #NETWORK_PROVIDER}
+ * and {@link #GPS_PROVIDER}.
+ *
+ * <p>Before API version 17, this method could be used with
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ * From API version 17 and onwards, this method requires
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+ *
+ * @param latitude the latitude of the central point of the
+ * alert region
+ * @param longitude the longitude of the central point of the
+ * alert region
+ * @param radius the radius of the central point of the
+ * alert region, in meters
+ * @param expiration time for this proximity alert, in milliseconds,
+ * or -1 to indicate no expiration
+ * @param intent a PendingIntent that will be used to generate an Intent to
+ * fire when entry to or exit from the alert region is detected
+ *
+ * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission is not present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void addProximityAlert(double latitude, double longitude, float radius, long expiration,
+ PendingIntent intent) {
+ checkPendingIntent(intent);
+ if (expiration < 0) expiration = Long.MAX_VALUE;
+
+ Geofence fence = Geofence.createCircle(latitude, longitude, radius);
+ LocationRequest request = new LocationRequest().setExpireIn(expiration);
+ try {
+ mService.requestGeofence(request, fence, intent, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Add a geofence with the specified LocationRequest quality of service.
+ *
+ * <p> When the device
+ * detects that it has entered or exited the area surrounding the
+ * location, the given PendingIntent will be used to create an Intent
+ * to be fired.
+ *
+ * <p> The fired Intent will have a boolean extra added with key
+ * {@link #KEY_PROXIMITY_ENTERING}. If the value is true, the device is
+ * entering the proximity region; if false, it is exiting.
+ *
+ * <p> The geofence engine fuses results from all location providers to
+ * provide the best balance between accuracy and power. Applications
+ * can choose the quality of service required using the
+ * {@link LocationRequest} object. If it is null then a default,
+ * low power geo-fencing implementation is used. It is possible to cross
+ * a geo-fence without notification, but the system will do its best
+ * to detect, using {@link LocationRequest} as a hint to trade-off
+ * accuracy and power.
+ *
+ * <p> The power required by the geofence engine can depend on many factors,
+ * such as quality and interval requested in {@link LocationRequest},
+ * distance to nearest geofence and current device velocity.
+ *
+ * @param request quality of service required, null for default low power
+ * @param fence a geographical description of the geofence area
+ * @param intent pending intent to receive geofence updates
+ *
+ * @throws IllegalArgumentException if fence is null
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission is not present
+ *
+ * @hide
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void addGeofence(LocationRequest request, Geofence fence, PendingIntent intent) {
+ checkPendingIntent(intent);
+ checkGeofence(fence);
+
+ try {
+ mService.requestGeofence(request, fence, intent, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes the proximity alert with the given PendingIntent.
+ *
+ * <p>Before API version 17, this method could be used with
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} or
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION}.
+ * From API version 17 and onwards, this method requires
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+ *
+ * @param intent the PendingIntent that no longer needs to be notified of
+ * proximity alerts
+ *
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission is not present
+ */
+ public void removeProximityAlert(PendingIntent intent) {
+ checkPendingIntent(intent);
+ String packageName = mContext.getPackageName();
+
+ try {
+ mService.removeGeofence(null, intent, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove a single geofence.
+ *
+ * <p>This removes only the specified geofence associated with the
+ * specified pending intent. All other geofences remain unchanged.
+ *
+ * @param fence a geofence previously passed to {@link #addGeofence}
+ * @param intent a pending intent previously passed to {@link #addGeofence}
+ *
+ * @throws IllegalArgumentException if fence is null
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission is not present
+ *
+ * @hide
+ */
+ public void removeGeofence(Geofence fence, PendingIntent intent) {
+ checkPendingIntent(intent);
+ checkGeofence(fence);
+ String packageName = mContext.getPackageName();
+
+ try {
+ mService.removeGeofence(fence, intent, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Remove all geofences registered to the specified pending intent.
+ *
+ * @param intent a pending intent previously passed to {@link #addGeofence}
+ *
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission is not present
+ *
+ * @hide
+ */
+ public void removeAllGeofences(PendingIntent intent) {
+ checkPendingIntent(intent);
+ String packageName = mContext.getPackageName();
+
+ try {
+ mService.removeGeofence(null, intent, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current enabled/disabled status of the given provider.
+ *
+ * <p>If the user has enabled this provider in the Settings menu, true
+ * is returned otherwise false is returned
+ *
+ * <p>Callers should instead use
+ * {@link android.provider.Settings.Secure#LOCATION_MODE}
+ * unless they depend on provider-specific APIs such as
+ * {@link #requestLocationUpdates(String, long, float, LocationListener)}.
+ *
+ * <p>
+ * Before API version {@link android.os.Build.VERSION_CODES#LOLLIPOP}, this
+ * method would throw {@link SecurityException} if the location permissions
+ * were not sufficient to use the specified provider.
+ *
+ * @param provider the name of the provider
+ * @return true if the provider exists and is enabled
+ *
+ * @throws IllegalArgumentException if provider is null
+ */
+ public boolean isProviderEnabled(String provider) {
+ checkProvider(provider);
+
+ try {
+ return mService.isProviderEnabled(provider);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Get the last known location.
+ *
+ * <p>This location could be very old so use
+ * {@link Location#getElapsedRealtimeNanos} to calculate its age. It can
+ * also return null if no previous location is available.
+ *
+ * <p>Always returns immediately.
+ *
+ * @return The last known location, or null if not available
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @hide
+ */
+ public Location getLastLocation() {
+ String packageName = mContext.getPackageName();
+
+ try {
+ return mService.getLastLocation(null, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns a Location indicating the data from the last known
+ * location fix obtained from the given provider.
+ *
+ * <p> This can be done
+ * without starting the provider. Note that this location could
+ * be out-of-date, for example if the device was turned off and
+ * moved to another location.
+ *
+ * <p> If the provider is currently disabled, null is returned.
+ *
+ * @param provider the name of the provider
+ * @return the last known location for the provider, or null
+ *
+ * @throws SecurityException if no suitable permission is present
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public Location getLastKnownLocation(String provider) {
+ checkProvider(provider);
+ String packageName = mContext.getPackageName();
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, 0, 0, true);
+
+ try {
+ return mService.getLastLocation(request, packageName);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ // --- Mock provider support ---
+ // TODO: It would be fantastic to deprecate mock providers entirely, and replace
+ // with something closer to LocationProviderBase.java
+
+ /**
+ * Creates a mock location provider and adds it to the set of active providers.
+ *
+ * @param name the provider name
+ *
+ * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
+ * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
+ * allowed} for your app.
+ * @throws IllegalArgumentException if a provider with the given name already exists
+ */
+ public void addTestProvider(String name, boolean requiresNetwork, boolean requiresSatellite,
+ boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
+ boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ ProviderProperties properties = new ProviderProperties(requiresNetwork,
+ requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
+ supportsBearing, powerRequirement, accuracy);
+ if (name.matches(LocationProvider.BAD_CHARS_REGEX)) {
+ throw new IllegalArgumentException("provider name contains illegal character: " + name);
+ }
+
+ try {
+ mService.addTestProvider(name, properties, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes the mock location provider with the given name.
+ *
+ * @param provider the provider name
+ *
+ * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
+ * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
+ * allowed} for your app.
+ * @throws IllegalArgumentException if no provider with the given name exists
+ */
+ public void removeTestProvider(String provider) {
+ try {
+ mService.removeTestProvider(provider, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets a mock location for the given provider.
+ * <p>This location will be used in place of any actual location from the provider.
+ * The location object must have a minimum number of fields set to be
+ * considered a valid LocationProvider Location, as per documentation
+ * on {@link Location} class.
+ *
+ * @param provider the provider name
+ * @param loc the mock location
+ *
+ * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
+ * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
+ * allowed} for your app.
+ * @throws IllegalArgumentException if no provider with the given name exists
+ * @throws IllegalArgumentException if the location is incomplete
+ */
+ public void setTestProviderLocation(String provider, Location loc) {
+ if (!loc.isComplete()) {
+ IllegalArgumentException e = new IllegalArgumentException(
+ "Incomplete location object, missing timestamp or accuracy? " + loc);
+ if (mContext.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.JELLY_BEAN) {
+ // just log on old platform (for backwards compatibility)
+ Log.w(TAG, e);
+ loc.makeComplete();
+ } else {
+ // really throw it!
+ throw e;
+ }
+ }
+
+ try {
+ mService.setTestProviderLocation(provider, loc, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes any mock location associated with the given provider.
+ *
+ * @param provider the provider name
+ *
+ * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
+ * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
+ * allowed} for your app.
+ * @throws IllegalArgumentException if no provider with the given name exists
+ */
+ public void clearTestProviderLocation(String provider) {
+ try {
+ mService.clearTestProviderLocation(provider, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets a mock enabled value for the given provider. This value will be used in place
+ * of any actual value from the provider.
+ *
+ * @param provider the provider name
+ * @param enabled the mock enabled value
+ *
+ * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
+ * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
+ * allowed} for your app.
+ * @throws IllegalArgumentException if no provider with the given name exists
+ */
+ public void setTestProviderEnabled(String provider, boolean enabled) {
+ try {
+ mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes any mock enabled value associated with the given provider.
+ *
+ * @param provider the provider name
+ *
+ * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
+ * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
+ * allowed} for your app.
+ * @throws IllegalArgumentException if no provider with the given name exists
+ */
+ public void clearTestProviderEnabled(String provider) {
+ try {
+ mService.clearTestProviderEnabled(provider, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets mock status values for the given provider. These values will be used in place
+ * of any actual values from the provider.
+ *
+ * @param provider the provider name
+ * @param status the mock status
+ * @param extras a Bundle containing mock extras
+ * @param updateTime the mock update time
+ *
+ * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
+ * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
+ * allowed} for your app.
+ * @throws IllegalArgumentException if no provider with the given name exists
+ */
+ public void setTestProviderStatus(String provider, int status, Bundle extras, long updateTime) {
+ try {
+ mService.setTestProviderStatus(provider, status, extras, updateTime,
+ mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes any mock status values associated with the given provider.
+ *
+ * @param provider the provider name
+ *
+ * @throws SecurityException if {@link android.app.AppOpsManager#OPSTR_MOCK_LOCATION
+ * mock location app op} is not set to {@link android.app.AppOpsManager#MODE_ALLOWED
+ * allowed} for your app.
+ * @throws IllegalArgumentException if no provider with the given name exists
+ */
+ public void clearTestProviderStatus(String provider) {
+ try {
+ mService.clearTestProviderStatus(provider, mContext.getOpPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ // --- GPS-specific support ---
+
+ // This class is used to send Gnss status events to the client's specific thread.
+ private class GnssStatusListenerTransport extends IGnssStatusListener.Stub {
+
+ private final GpsStatus.Listener mGpsListener;
+ private final GpsStatus.NmeaListener mGpsNmeaListener;
+ private final GnssStatus.Callback mGnssCallback;
+ private final OnNmeaMessageListener mGnssNmeaListener;
+
+ private class GnssHandler extends Handler {
+ public GnssHandler(Handler handler) {
+ super(handler != null ? handler.getLooper() : Looper.myLooper());
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case NMEA_RECEIVED:
+ synchronized (mNmeaBuffer) {
+ int length = mNmeaBuffer.size();
+ for (int i = 0; i < length; i++) {
+ Nmea nmea = mNmeaBuffer.get(i);
+ mGnssNmeaListener.onNmeaMessage(nmea.mNmea, nmea.mTimestamp);
+ }
+ mNmeaBuffer.clear();
+ }
+ break;
+ case GpsStatus.GPS_EVENT_STARTED:
+ mGnssCallback.onStarted();
+ break;
+ case GpsStatus.GPS_EVENT_STOPPED:
+ mGnssCallback.onStopped();
+ break;
+ case GpsStatus.GPS_EVENT_FIRST_FIX:
+ mGnssCallback.onFirstFix(mTimeToFirstFix);
+ break;
+ case GpsStatus.GPS_EVENT_SATELLITE_STATUS:
+ mGnssCallback.onSatelliteStatusChanged(mGnssStatus);
+ break;
+ default:
+ break;
+ }
+ }
+ }
+
+ private final Handler mGnssHandler;
+
+ // This must not equal any of the GpsStatus event IDs
+ private static final int NMEA_RECEIVED = 1000;
+
+ private class Nmea {
+ long mTimestamp;
+ String mNmea;
+
+ Nmea(long timestamp, String nmea) {
+ mTimestamp = timestamp;
+ mNmea = nmea;
+ }
+ }
+ private final ArrayList<Nmea> mNmeaBuffer;
+
+ GnssStatusListenerTransport(GpsStatus.Listener listener) {
+ this(listener, null);
+ }
+
+ GnssStatusListenerTransport(GpsStatus.Listener listener, Handler handler) {
+ mGpsListener = listener;
+ mGnssHandler = new GnssHandler(handler);
+ mGpsNmeaListener = null;
+ mNmeaBuffer = null;
+ mGnssCallback = mGpsListener != null ? new GnssStatus.Callback() {
+ @Override
+ public void onStarted() {
+ mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STARTED);
+ }
+
+ @Override
+ public void onStopped() {
+ mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_STOPPED);
+ }
+
+ @Override
+ public void onFirstFix(int ttff) {
+ mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_FIRST_FIX);
+ }
+
+ @Override
+ public void onSatelliteStatusChanged(GnssStatus status) {
+ mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
+ }
+ } : null;
+ mGnssNmeaListener = null;
+ }
+
+ GnssStatusListenerTransport(GpsStatus.NmeaListener listener) {
+ this(listener, null);
+ }
+
+ GnssStatusListenerTransport(GpsStatus.NmeaListener listener, Handler handler) {
+ mGpsListener = null;
+ mGnssHandler = new GnssHandler(handler);
+ mGpsNmeaListener = listener;
+ mNmeaBuffer = new ArrayList<Nmea>();
+ mGnssCallback = null;
+ mGnssNmeaListener = mGpsNmeaListener != null ? new OnNmeaMessageListener() {
+ @Override
+ public void onNmeaMessage(String nmea, long timestamp) {
+ mGpsNmeaListener.onNmeaReceived(timestamp, nmea);
+ }
+ } : null;
+ }
+
+ GnssStatusListenerTransport(GnssStatus.Callback callback) {
+ this(callback, null);
+ }
+
+ GnssStatusListenerTransport(GnssStatus.Callback callback, Handler handler) {
+ mGnssCallback = callback;
+ mGnssHandler = new GnssHandler(handler);
+ mGnssNmeaListener = null;
+ mNmeaBuffer = null;
+ mGpsListener = null;
+ mGpsNmeaListener = null;
+ }
+
+ GnssStatusListenerTransport(OnNmeaMessageListener listener) {
+ this(listener, null);
+ }
+
+ GnssStatusListenerTransport(OnNmeaMessageListener listener, Handler handler) {
+ mGnssCallback = null;
+ mGnssHandler = new GnssHandler(handler);
+ mGnssNmeaListener = listener;
+ mGpsListener = null;
+ mGpsNmeaListener = null;
+ mNmeaBuffer = new ArrayList<Nmea>();
+ }
+
+ @Override
+ public void onGnssStarted() {
+ if (mGnssCallback != null) {
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_STARTED;
+ mGnssHandler.sendMessage(msg);
+ }
+ }
+
+ @Override
+ public void onGnssStopped() {
+ if (mGnssCallback != null) {
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_STOPPED;
+ mGnssHandler.sendMessage(msg);
+ }
+ }
+
+ @Override
+ public void onFirstFix(int ttff) {
+ if (mGnssCallback != null) {
+ mTimeToFirstFix = ttff;
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_FIRST_FIX;
+ mGnssHandler.sendMessage(msg);
+ }
+ }
+
+ @Override
+ public void onSvStatusChanged(int svCount, int[] prnWithFlags,
+ float[] cn0s, float[] elevations, float[] azimuths, float[] carrierFreqs) {
+ if (mGnssCallback != null) {
+ mGnssStatus = new GnssStatus(svCount, prnWithFlags, cn0s, elevations, azimuths,
+ carrierFreqs);
+
+ Message msg = Message.obtain();
+ msg.what = GpsStatus.GPS_EVENT_SATELLITE_STATUS;
+ // remove any SV status messages already in the queue
+ mGnssHandler.removeMessages(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
+ mGnssHandler.sendMessage(msg);
+ }
+ }
+
+ @Override
+ public void onNmeaReceived(long timestamp, String nmea) {
+ if (mGnssNmeaListener != null) {
+ synchronized (mNmeaBuffer) {
+ mNmeaBuffer.add(new Nmea(timestamp, nmea));
+ }
+ Message msg = Message.obtain();
+ msg.what = NMEA_RECEIVED;
+ // remove any NMEA_RECEIVED messages already in the queue
+ mGnssHandler.removeMessages(NMEA_RECEIVED);
+ mGnssHandler.sendMessage(msg);
+ }
+ }
+ }
+
+ /**
+ * Adds a GPS status listener.
+ *
+ * @param listener GPS status listener object to register
+ *
+ * @return true if the listener was successfully added
+ *
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ * @deprecated use {@link #registerGnssStatusCallback(GnssStatus.Callback)} instead.
+ */
+ @Deprecated
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean addGpsStatusListener(GpsStatus.Listener listener) {
+ boolean result;
+
+ if (mGpsStatusListeners.get(listener) != null) {
+ // listener is already registered
+ return true;
+ }
+ try {
+ GnssStatusListenerTransport transport = new GnssStatusListenerTransport(listener);
+ result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
+ if (result) {
+ mGpsStatusListeners.put(listener, transport);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ return result;
+ }
+
+ /**
+ * Removes a GPS status listener.
+ *
+ * @param listener GPS status listener object to remove
+ * @deprecated use {@link #unregisterGnssStatusCallback(GnssStatus.Callback)} instead.
+ */
+ @Deprecated
+ public void removeGpsStatusListener(GpsStatus.Listener listener) {
+ try {
+ GnssStatusListenerTransport transport = mGpsStatusListeners.remove(listener);
+ if (transport != null) {
+ mService.unregisterGnssStatusCallback(transport);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a GNSS status callback.
+ *
+ * @param callback GNSS status callback object to register
+ *
+ * @return true if the listener was successfully added
+ *
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssStatusCallback(GnssStatus.Callback callback) {
+ return registerGnssStatusCallback(callback, null);
+ }
+
+ /**
+ * Registers a GNSS status callback.
+ *
+ * @param callback GNSS status callback object to register
+ * @param handler the handler that the callback runs on.
+ *
+ * @return true if the listener was successfully added
+ *
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssStatusCallback(GnssStatus.Callback callback, Handler handler) {
+ boolean result;
+ if (mGnssStatusListeners.get(callback) != null) {
+ // listener is already registered
+ return true;
+ }
+ try {
+ GnssStatusListenerTransport transport =
+ new GnssStatusListenerTransport(callback, handler);
+ result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
+ if (result) {
+ mGnssStatusListeners.put(callback, transport);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ return result;
+ }
+
+ /**
+ * Removes a GNSS status callback.
+ *
+ * @param callback GNSS status callback object to remove
+ */
+ public void unregisterGnssStatusCallback(GnssStatus.Callback callback) {
+ try {
+ GnssStatusListenerTransport transport = mGnssStatusListeners.remove(callback);
+ if (transport != null) {
+ mService.unregisterGnssStatusCallback(transport);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Adds an NMEA listener.
+ *
+ * @param listener a {@link GpsStatus.NmeaListener} object to register
+ *
+ * @return true if the listener was successfully added
+ *
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ * @deprecated use {@link #addNmeaListener(OnNmeaMessageListener)} instead.
+ */
+ @Deprecated
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean addNmeaListener(GpsStatus.NmeaListener listener) {
+ boolean result;
+
+ if (mGpsNmeaListeners.get(listener) != null) {
+ // listener is already registered
+ return true;
+ }
+ try {
+ GnssStatusListenerTransport transport = new GnssStatusListenerTransport(listener);
+ result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
+ if (result) {
+ mGpsNmeaListeners.put(listener, transport);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ return result;
+ }
+
+ /**
+ * Removes an NMEA listener.
+ *
+ * @param listener a {@link GpsStatus.NmeaListener} object to remove
+ * @deprecated use {@link #removeNmeaListener(OnNmeaMessageListener)} instead.
+ */
+ @Deprecated
+ public void removeNmeaListener(GpsStatus.NmeaListener listener) {
+ try {
+ GnssStatusListenerTransport transport = mGpsNmeaListeners.remove(listener);
+ if (transport != null) {
+ mService.unregisterGnssStatusCallback(transport);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Adds an NMEA listener.
+ *
+ * @param listener a {@link OnNmeaMessageListener} object to register
+ *
+ * @return true if the listener was successfully added
+ *
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean addNmeaListener(OnNmeaMessageListener listener) {
+ return addNmeaListener(listener, null);
+ }
+
+ /**
+ * Adds an NMEA listener.
+ *
+ * @param listener a {@link OnNmeaMessageListener} object to register
+ * @param handler the handler that the listener runs on.
+ *
+ * @return true if the listener was successfully added
+ *
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean addNmeaListener(OnNmeaMessageListener listener, Handler handler) {
+ boolean result;
+
+ if (mGpsNmeaListeners.get(listener) != null) {
+ // listener is already registered
+ return true;
+ }
+ try {
+ GnssStatusListenerTransport transport =
+ new GnssStatusListenerTransport(listener, handler);
+ result = mService.registerGnssStatusCallback(transport, mContext.getPackageName());
+ if (result) {
+ mGnssNmeaListeners.put(listener, transport);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+
+ return result;
+ }
+
+ /**
+ * Removes an NMEA listener.
+ *
+ * @param listener a {@link OnNmeaMessageListener} object to remove
+ */
+ public void removeNmeaListener(OnNmeaMessageListener listener) {
+ try {
+ GnssStatusListenerTransport transport = mGnssNmeaListeners.remove(listener);
+ if (transport != null) {
+ mService.unregisterGnssStatusCallback(transport);
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * No-op method to keep backward-compatibility.
+ * Don't use it. Use {@link #registerGnssMeasurementsCallback} instead.
+ * @hide
+ * @deprecated Not supported anymore.
+ */
+ @Deprecated
+ @SystemApi
+ @SuppressLint("Doclava125")
+ public boolean addGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
+ return false;
+ }
+
+ /**
+ * Registers a GPS Measurement callback.
+ *
+ * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
+ * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssMeasurementsCallback(GnssMeasurementsEvent.Callback callback) {
+ return registerGnssMeasurementsCallback(callback, null);
+ }
+
+ /**
+ * Registers a GPS Measurement callback.
+ *
+ * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
+ * @param handler the handler that the callback runs on.
+ * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssMeasurementsCallback(GnssMeasurementsEvent.Callback callback,
+ Handler handler) {
+ return mGnssMeasurementCallbackTransport.add(callback, handler);
+ }
+
+ /**
+ * No-op method to keep backward-compatibility.
+ * Don't use it. Use {@link #unregisterGnssMeasurementsCallback} instead.
+ * @hide
+ * @deprecated use {@link #unregisterGnssMeasurementsCallback(GnssMeasurementsEvent.Callback)}
+ * instead.
+ */
+ @Deprecated
+ @SystemApi
+ @SuppressLint("Doclava125")
+ public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
+ }
+
+ /**
+ * Unregisters a GPS Measurement callback.
+ *
+ * @param callback a {@link GnssMeasurementsEvent.Callback} object to remove.
+ */
+ public void unregisterGnssMeasurementsCallback(GnssMeasurementsEvent.Callback callback) {
+ mGnssMeasurementCallbackTransport.remove(callback);
+ }
+
+ /**
+ * No-op method to keep backward-compatibility.
+ * Don't use it. Use {@link #registerGnssNavigationMessageCallback} instead.
+ * @hide
+ * @deprecated Not supported anymore.
+ */
+ @Deprecated
+ @SystemApi
+ @SuppressLint("Doclava125")
+ public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
+ return false;
+ }
+
+ /**
+ * No-op method to keep backward-compatibility.
+ * Don't use it. Use {@link #unregisterGnssNavigationMessageCallback} instead.
+ * @hide
+ * @deprecated use
+ * {@link #unregisterGnssNavigationMessageCallback(GnssNavigationMessage.Callback)}
+ * instead
+ */
+ @Deprecated
+ @SystemApi
+ @SuppressLint("Doclava125")
+ public void removeGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
+ }
+
+ /**
+ * Registers a GNSS Navigation Message callback.
+ *
+ * @param callback a {@link GnssNavigationMessage.Callback} object to register.
+ * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ */
+ public boolean registerGnssNavigationMessageCallback(
+ GnssNavigationMessage.Callback callback) {
+ return registerGnssNavigationMessageCallback(callback, null);
+ }
+
+ /**
+ * Registers a GNSS Navigation Message callback.
+ *
+ * @param callback a {@link GnssNavigationMessage.Callback} object to register.
+ * @param handler the handler that the callback runs on.
+ * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssNavigationMessageCallback(
+ GnssNavigationMessage.Callback callback, Handler handler) {
+ return mGnssNavigationMessageCallbackTransport.add(callback, handler);
+ }
+
+ /**
+ * Unregisters a GNSS Navigation Message callback.
+ *
+ * @param callback a {@link GnssNavigationMessage.Callback} object to remove.
+ */
+ public void unregisterGnssNavigationMessageCallback(
+ GnssNavigationMessage.Callback callback) {
+ mGnssNavigationMessageCallbackTransport.remove(callback);
+ }
+
+ /**
+ * 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.
+ */
+ @Deprecated
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public GpsStatus getGpsStatus(GpsStatus status) {
+ if (status == null) {
+ status = new GpsStatus();
+ }
+ // When mGnssStatus is null, that means that this method is called outside
+ // onGpsStatusChanged(). Return an empty status to maintain backwards compatibility.
+ if (mGnssStatus != null) {
+ status.setStatus(mGnssStatus, mTimeToFirstFix);
+ }
+ return status;
+ }
+
+ /**
+ * Returns the system information of the GPS hardware.
+ * May return 0 if GPS hardware is earlier than 2016.
+ * @hide
+ */
+ @TestApi
+ public int getGnssYearOfHardware() {
+ try {
+ return mService.getGnssYearOfHardware();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the batch size (in number of Location objects) that are supported by the batching
+ * interface.
+ *
+ * @return Maximum number of location objects that can be returned
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public int getGnssBatchSize() {
+ try {
+ return mService.getGnssBatchSize(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Start hardware-batching of GNSS locations. This API is primarily used when the AP is
+ * asleep and the device can batch GNSS locations in the hardware.
+ *
+ * Note this is designed (as was the fused location interface before it) for a single user
+ * SystemApi - requests are not consolidated. Care should be taken when the System switches
+ * users that may have different batching requests, to stop hardware batching for one user, and
+ * restart it for the next.
+ *
+ * @param periodNanos Time interval, in nanoseconds, that the GNSS locations are requested
+ * within the batch
+ * @param wakeOnFifoFull True if the hardware batching should flush the locations in a
+ * a callback to the listener, when it's internal buffer is full. If
+ * set to false, the oldest location information is, instead,
+ * dropped when the buffer is full.
+ * @param callback The listener on which to return the batched locations
+ * @param handler The handler on which to process the callback
+ *
+ * @return True if batching was successfully started
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public boolean registerGnssBatchedLocationCallback(long periodNanos, boolean wakeOnFifoFull,
+ BatchedLocationCallback callback, Handler handler) {
+ mBatchedLocationCallbackTransport.add(callback, handler);
+
+ try {
+ return mService.startGnssBatch(periodNanos, wakeOnFifoFull, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Flush the batched GNSS locations.
+ * All GNSS locations currently ready in the batch are returned via the callback sent in
+ * startGnssBatch(), and the buffer containing the batched locations is cleared.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void flushGnssBatch() {
+ try {
+ mService.flushGnssBatch(mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Stop batching locations. This API is primarily used when the AP is
+ * asleep and the device can batch locations in the hardware.
+ *
+ * @param callback the specific callback class to remove from the transport layer
+ *
+ * @return True if batching was successfully started
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public boolean unregisterGnssBatchedLocationCallback(BatchedLocationCallback callback) {
+
+ mBatchedLocationCallbackTransport.remove(callback);
+
+ try {
+ return mService.stopGnssBatch();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sends additional commands to a location provider.
+ * Can be used to support provider specific extensions to the Location Manager API
+ *
+ * @param provider name of the location provider.
+ * @param command name of the command to send to the provider.
+ * @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.
+ */
+ public boolean sendExtraCommand(String provider, String command, Bundle extras) {
+ try {
+ return mService.sendExtraCommand(provider, command, extras);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Used by NetInitiatedActivity to report user response
+ * for network initiated GPS fix requests.
+ *
+ * @hide
+ */
+ public boolean sendNiResponse(int notifId, int userResponse) {
+ try {
+ return mService.sendNiResponse(notifId, userResponse);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ private static void checkProvider(String provider) {
+ if (provider == null) {
+ throw new IllegalArgumentException("invalid provider: " + provider);
+ }
+ }
+
+ private static void checkCriteria(Criteria criteria) {
+ if (criteria == null) {
+ throw new IllegalArgumentException("invalid criteria: " + criteria);
+ }
+ }
+
+ private static void checkListener(LocationListener listener) {
+ if (listener == null) {
+ throw new IllegalArgumentException("invalid listener: " + listener);
+ }
+ }
+
+ private void checkPendingIntent(PendingIntent intent) {
+ if (intent == null) {
+ throw new IllegalArgumentException("invalid pending intent: " + intent);
+ }
+ if (!intent.isTargetedToPackage()) {
+ IllegalArgumentException e = new IllegalArgumentException(
+ "pending intent must be targeted to package");
+ if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) {
+ throw e;
+ } else {
+ Log.w(TAG, e);
+ }
+ }
+ }
+
+ private static void checkGeofence(Geofence fence) {
+ if (fence == null) {
+ throw new IllegalArgumentException("invalid geofence: " + fence);
+ }
+ }
+}
diff --git a/android/location/LocationProvider.java b/android/location/LocationProvider.java
new file mode 100644
index 00000000..c4fd0975
--- /dev/null
+++ b/android/location/LocationProvider.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2007 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 com.android.internal.location.ProviderProperties;
+
+/**
+ * An abstract superclass for location providers. A location provider
+ * provides periodic reports on the geographical location of the
+ * device.
+ *
+ * <p> Each provider has a set of criteria under which it may be used;
+ * for example, some providers require GPS hardware and visibility to
+ * a number of satellites; others require the use of the cellular
+ * radio, or access to a specific carrier's network, or to the
+ * internet. They may also have different battery consumption
+ * characteristics or monetary costs to the user. The {@link
+ * Criteria} class allows providers to be selected based on
+ * user-specified criteria.
+ */
+public class LocationProvider {
+ public static final int OUT_OF_SERVICE = 0;
+ public static final int TEMPORARILY_UNAVAILABLE = 1;
+ public static final int AVAILABLE = 2;
+
+ /**
+ * A regular expression matching characters that may not appear
+ * in the name of a LocationProvider
+ * @hide
+ */
+ public static final String BAD_CHARS_REGEX = "[^a-zA-Z0-9]";
+
+ private final String mName;
+ private final ProviderProperties mProperties;
+
+ /**
+ * Constructs a LocationProvider with the given name. Provider names must
+ * consist only of the characters [a-zA-Z0-9].
+ *
+ * @throws IllegalArgumentException if name contains an illegal character
+ *
+ * @hide
+ */
+ public LocationProvider(String name, ProviderProperties properties) {
+ if (name.matches(BAD_CHARS_REGEX)) {
+ throw new IllegalArgumentException("provider name contains illegal character: " + name);
+ }
+ mName = name;
+ mProperties = properties;
+ }
+
+ /**
+ * Returns the name of this provider.
+ */
+ public String getName() {
+ return mName;
+ }
+
+ /**
+ * Returns true if this provider meets the given criteria,
+ * false otherwise.
+ */
+ public boolean meetsCriteria(Criteria criteria) {
+ return propertiesMeetCriteria(mName, mProperties, criteria);
+ }
+
+ /**
+ * @hide
+ */
+ public static boolean propertiesMeetCriteria(String name, ProviderProperties properties,
+ Criteria criteria) {
+ if (LocationManager.PASSIVE_PROVIDER.equals(name)) {
+ // passive provider never matches
+ return false;
+ }
+ if (properties == null) {
+ // unfortunately this can happen for provider in remote services
+ // that have not finished binding yet
+ return false;
+ }
+
+ if (criteria.getAccuracy() != Criteria.NO_REQUIREMENT &&
+ criteria.getAccuracy() < properties.mAccuracy) {
+ return false;
+ }
+ if (criteria.getPowerRequirement() != Criteria.NO_REQUIREMENT &&
+ criteria.getPowerRequirement() < properties.mPowerRequirement) {
+ return false;
+ }
+ if (criteria.isAltitudeRequired() && !properties.mSupportsAltitude) {
+ return false;
+ }
+ if (criteria.isSpeedRequired() && !properties.mSupportsSpeed) {
+ return false;
+ }
+ if (criteria.isBearingRequired() && !properties.mSupportsBearing) {
+ return false;
+ }
+ if (!criteria.isCostAllowed() && properties.mHasMonetaryCost) {
+ return false;
+ }
+ return true;
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * data network (e.g., the Internet), false otherwise.
+ */
+ public boolean requiresNetwork() {
+ return mProperties.mRequiresNetwork;
+ }
+
+ /**
+ * Returns true if the provider requires access to a
+ * satellite-based positioning system (e.g., GPS), false
+ * otherwise.
+ */
+ public boolean requiresSatellite() {
+ return mProperties.mRequiresSatellite;
+ }
+
+ /**
+ * 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 mProperties.mRequiresCell;
+ }
+
+ /**
+ * 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 mProperties.mHasMonetaryCost;
+ }
+
+ /**
+ * 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 mProperties.mSupportsAltitude;
+ }
+
+ /**
+ * 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 mProperties.mSupportsSpeed;
+ }
+
+ /**
+ * 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 mProperties.mSupportsBearing;
+ }
+
+ /**
+ * 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 mProperties.mPowerRequirement;
+ }
+
+ /**
+ * Returns a constant describing horizontal accuracy of this provider.
+ * If the provider returns finer grain or exact location,
+ * {@link Criteria#ACCURACY_FINE} is returned, otherwise if the
+ * location is only approximate then {@link Criteria#ACCURACY_COARSE}
+ * is returned.
+ */
+ public int getAccuracy() {
+ return mProperties.mAccuracy;
+ }
+}
diff --git a/android/location/LocationRequest.java b/android/location/LocationRequest.java
new file mode 100644
index 00000000..65e7cedf
--- /dev/null
+++ b/android/location/LocationRequest.java
@@ -0,0 +1,672 @@
+/*
+ * 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 android.location;
+
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.os.WorkSource;
+import android.util.TimeUtils;
+
+
+/**
+ * A data object that contains quality of service parameters for requests
+ * to the {@link LocationManager}.
+ *
+ * <p>LocationRequest objects are used to request a quality of service
+ * for location updates from the Location Manager.
+ *
+ * <p>For example, if your application wants high accuracy location
+ * it should create a location request with {@link #setQuality} set to
+ * {@link #ACCURACY_FINE} or {@link #POWER_HIGH}, and it should set
+ * {@link #setInterval} to less than one second. This would be
+ * appropriate for mapping applications that are showing your location
+ * in real-time.
+ *
+ * <p>At the other extreme, if you want negligible power
+ * impact, but to still receive location updates when available, then use
+ * {@link #setQuality} with {@link #POWER_NONE}. With this request your
+ * application will not trigger (and therefore will not receive any
+ * power blame) any location updates, but will receive locations
+ * triggered by other applications. This would be appropriate for
+ * applications that have no firm requirement for location, but can
+ * take advantage when available.
+ *
+ * <p>In between these two extremes is a very common use-case, where
+ * applications definitely want to receive
+ * updates at a specified interval, and can receive them faster when
+ * available, but still want a low power impact. These applications
+ * should consider {@link #POWER_LOW} combined with a faster
+ * {@link #setFastestInterval} (such as 1 minute) and a slower
+ * {@link #setInterval} (such as 60 minutes). They will only be assigned
+ * power blame for the interval set by {@link #setInterval}, but can
+ * still receive locations triggered by other applications at a rate up
+ * to {@link #setFastestInterval}. This style of request is appropriate for
+ * many location aware applications, including background usage. Do be
+ * careful to also throttle {@link #setFastestInterval} if you perform
+ * heavy-weight work after receiving an update - such as using the network.
+ *
+ * <p>Activities should strongly consider removing all location
+ * request when entering the background
+ * (for example at {@link android.app.Activity#onPause}), or
+ * at least swap the request to a larger interval and lower quality.
+ * Future version of the location manager may automatically perform background
+ * throttling on behalf of applications.
+ *
+ * <p>Applications cannot specify the exact location sources that are
+ * used by Android's <em>Fusion Engine</em>. In fact, the system
+ * may have multiple location sources (providers) running and may
+ * fuse the results from several sources into a single Location object.
+ *
+ * <p>Location requests from applications with
+ * {@link android.Manifest.permission#ACCESS_COARSE_LOCATION} and not
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} will
+ * be automatically throttled to a slower interval, and the location
+ * object will be obfuscated to only show a coarse level of accuracy.
+ *
+ * <p>All location requests are considered hints, and you may receive
+ * locations that are more accurate, less accurate, and slower
+ * than requested.
+ *
+ * @hide
+ */
+@SystemApi
+public final class LocationRequest implements Parcelable {
+ /**
+ * Used with {@link #setQuality} to request the most accurate locations available.
+ *
+ * <p>This may be up to 1 meter accuracy, although this is implementation dependent.
+ */
+ public static final int ACCURACY_FINE = 100;
+
+ /**
+ * Used with {@link #setQuality} to request "block" level accuracy.
+ *
+ * <p>Block level accuracy is considered to be about 100 meter accuracy,
+ * although this is implementation dependent. Using a coarse accuracy
+ * such as this often consumes less power.
+ */
+ public static final int ACCURACY_BLOCK = 102;
+
+ /**
+ * Used with {@link #setQuality} to request "city" level accuracy.
+ *
+ * <p>City level accuracy is considered to be about 10km accuracy,
+ * although this is implementation dependent. Using a coarse accuracy
+ * such as this often consumes less power.
+ */
+ public static final int ACCURACY_CITY = 104;
+
+ /**
+ * Used with {@link #setQuality} to require no direct power impact (passive locations).
+ *
+ * <p>This location request will not trigger any active location requests,
+ * but will receive locations triggered by other applications. Your application
+ * will not receive any direct power blame for location work.
+ */
+ public static final int POWER_NONE = 200;
+
+ /**
+ * Used with {@link #setQuality} to request low power impact.
+ *
+ * <p>This location request will avoid high power location work where
+ * possible.
+ */
+ public static final int POWER_LOW = 201;
+
+ /**
+ * Used with {@link #setQuality} to allow high power consumption for location.
+ *
+ * <p>This location request will allow high power location work.
+ */
+ public static final int POWER_HIGH = 203;
+
+ /**
+ * By default, mFastestInterval = FASTEST_INTERVAL_MULTIPLE * mInterval
+ */
+ private static final double FASTEST_INTERVAL_FACTOR = 6.0; // 6x
+
+ private int mQuality = POWER_LOW;
+ private long mInterval = 60 * 60 * 1000; // 60 minutes
+ private long mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR); // 10 minutes
+ private boolean mExplicitFastestInterval = false;
+ private long mExpireAt = Long.MAX_VALUE; // no expiry
+ private int mNumUpdates = Integer.MAX_VALUE; // no expiry
+ private float mSmallestDisplacement = 0.0f; // meters
+ private WorkSource mWorkSource = null;
+ private boolean mHideFromAppOps = false; // True if this request shouldn't be counted by AppOps
+
+ private String mProvider = LocationManager.FUSED_PROVIDER; // for deprecated APIs that explicitly request a provider
+
+ /**
+ * Create a location request with default parameters.
+ *
+ * <p>Default parameters are for a low power, slowly updated location.
+ * It can then be adjusted as required by the applications before passing
+ * to the {@link LocationManager}
+ *
+ * @return a new location request
+ */
+ public static LocationRequest create() {
+ LocationRequest request = new LocationRequest();
+ return request;
+ }
+
+ /** @hide */
+ @SystemApi
+ public static LocationRequest createFromDeprecatedProvider(String provider, long minTime,
+ float minDistance, boolean singleShot) {
+ if (minTime < 0) minTime = 0;
+ if (minDistance < 0) minDistance = 0;
+
+ int quality;
+ if (LocationManager.PASSIVE_PROVIDER.equals(provider)) {
+ quality = POWER_NONE;
+ } else if (LocationManager.GPS_PROVIDER.equals(provider)) {
+ quality = ACCURACY_FINE;
+ } else {
+ quality = POWER_LOW;
+ }
+
+ LocationRequest request = new LocationRequest()
+ .setProvider(provider)
+ .setQuality(quality)
+ .setInterval(minTime)
+ .setFastestInterval(minTime)
+ .setSmallestDisplacement(minDistance);
+ if (singleShot) request.setNumUpdates(1);
+ return request;
+ }
+
+ /** @hide */
+ @SystemApi
+ public static LocationRequest createFromDeprecatedCriteria(Criteria criteria, long minTime,
+ float minDistance, boolean singleShot) {
+ if (minTime < 0) minTime = 0;
+ if (minDistance < 0) minDistance = 0;
+
+ int quality;
+ switch (criteria.getAccuracy()) {
+ case Criteria.ACCURACY_COARSE:
+ quality = ACCURACY_BLOCK;
+ break;
+ case Criteria.ACCURACY_FINE:
+ quality = ACCURACY_FINE;
+ break;
+ default: {
+ switch (criteria.getPowerRequirement()) {
+ case Criteria.POWER_HIGH:
+ quality = POWER_HIGH;
+ break;
+ default:
+ quality = POWER_LOW;
+ }
+ }
+ }
+
+ LocationRequest request = new LocationRequest()
+ .setQuality(quality)
+ .setInterval(minTime)
+ .setFastestInterval(minTime)
+ .setSmallestDisplacement(minDistance);
+ if (singleShot) request.setNumUpdates(1);
+ return request;
+ }
+
+ /** @hide */
+ public LocationRequest() { }
+
+ /** @hide */
+ public LocationRequest(LocationRequest src) {
+ mQuality = src.mQuality;
+ mInterval = src.mInterval;
+ mFastestInterval = src.mFastestInterval;
+ mExplicitFastestInterval = src.mExplicitFastestInterval;
+ mExpireAt = src.mExpireAt;
+ mNumUpdates = src.mNumUpdates;
+ mSmallestDisplacement = src.mSmallestDisplacement;
+ mProvider = src.mProvider;
+ mWorkSource = src.mWorkSource;
+ mHideFromAppOps = src.mHideFromAppOps;
+ }
+
+ /**
+ * Set the quality of the request.
+ *
+ * <p>Use with a accuracy constant such as {@link #ACCURACY_FINE}, or a power
+ * constant such as {@link #POWER_LOW}. You cannot request both and accuracy and
+ * power, only one or the other can be specified. The system will then
+ * maximize accuracy or minimize power as appropriate.
+ *
+ * <p>The quality of the request is a strong hint to the system for which
+ * location sources to use. For example, {@link #ACCURACY_FINE} is more likely
+ * to use GPS, and {@link #POWER_LOW} is more likely to use WIFI & Cell tower
+ * positioning, but it also depends on many other factors (such as which sources
+ * are available) and is implementation dependent.
+ *
+ * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
+ * on a location request.
+ *
+ * @param quality an accuracy or power constant
+ * @throws InvalidArgumentException if the quality constant is not valid
+ * @return the same object, so that setters can be chained
+ */
+ public LocationRequest setQuality(int quality) {
+ checkQuality(quality);
+ mQuality = quality;
+ return this;
+ }
+
+ /**
+ * Get the quality of the request.
+ *
+ * @return an accuracy or power constant
+ */
+ public int getQuality() {
+ return mQuality;
+ }
+
+ /**
+ * Set the desired interval for active location updates, in milliseconds.
+ *
+ * <p>The location manager will actively try to obtain location updates
+ * for your application at this interval, so it has a
+ * direct influence on the amount of power used by your application.
+ * Choose your interval wisely.
+ *
+ * <p>This interval is inexact. You may not receive updates at all (if
+ * no location sources are available), or you may receive them
+ * slower than requested. You may also receive them faster than
+ * requested (if other applications are requesting location at a
+ * faster interval). The fastest rate that you will receive
+ * updates can be controlled with {@link #setFastestInterval}.
+ *
+ * <p>Applications with only the coarse location permission may have their
+ * interval silently throttled.
+ *
+ * <p>An interval of 0 is allowed, but not recommended, since
+ * location updates may be extremely fast on future implementations.
+ *
+ * <p>{@link #setQuality} and {@link #setInterval} are the most important parameters
+ * on a location request.
+ *
+ * @param millis desired interval in millisecond, inexact
+ * @throws InvalidArgumentException if the interval is less than zero
+ * @return the same object, so that setters can be chained
+ */
+ public LocationRequest setInterval(long millis) {
+ checkInterval(millis);
+ mInterval = millis;
+ if (!mExplicitFastestInterval) {
+ mFastestInterval = (long)(mInterval / FASTEST_INTERVAL_FACTOR);
+ }
+ return this;
+ }
+
+ /**
+ * Get the desired interval of this request, in milliseconds.
+ *
+ * @return desired interval in milliseconds, inexact
+ */
+ public long getInterval() {
+ return mInterval;
+ }
+
+ /**
+ * Explicitly set the fastest interval for location updates, in
+ * milliseconds.
+ *
+ * <p>This controls the fastest rate at which your application will
+ * receive location updates, which might be faster than
+ * {@link #setInterval} in some situations (for example, if other
+ * applications are triggering location updates).
+ *
+ * <p>This allows your application to passively acquire locations
+ * at a rate faster than it actively acquires locations, saving power.
+ *
+ * <p>Unlike {@link #setInterval}, this parameter is exact. Your
+ * application will never receive updates faster than this value.
+ *
+ * <p>If you don't call this method, a fastest interval
+ * will be selected for you. It will be a value faster than your
+ * active interval ({@link #setInterval}).
+ *
+ * <p>An interval of 0 is allowed, but not recommended, since
+ * location updates may be extremely fast on future implementations.
+ *
+ * <p>If {@link #setFastestInterval} is set slower than {@link #setInterval},
+ * then your effective fastest interval is {@link #setInterval}.
+ *
+ * @param millis fastest interval for updates in milliseconds, exact
+ * @throws InvalidArgumentException if the interval is less than zero
+ * @return the same object, so that setters can be chained
+ */
+ public LocationRequest setFastestInterval(long millis) {
+ checkInterval(millis);
+ mExplicitFastestInterval = true;
+ mFastestInterval = millis;
+ return this;
+ }
+
+ /**
+ * Get the fastest interval of this request, in milliseconds.
+ *
+ * <p>The system will never provide location updates faster
+ * than the minimum of {@link #getFastestInterval} and
+ * {@link #getInterval}.
+ *
+ * @return fastest interval in milliseconds, exact
+ */
+ public long getFastestInterval() {
+ return mFastestInterval;
+ }
+
+ /**
+ * Set the duration of this request, in milliseconds.
+ *
+ * <p>The duration begins immediately (and not when the request
+ * is passed to the location manager), so call this method again
+ * if the request is re-used at a later time.
+ *
+ * <p>The location manager will automatically stop updates after
+ * the request expires.
+ *
+ * <p>The duration includes suspend time. Values less than 0
+ * are allowed, but indicate that the request has already expired.
+ *
+ * @param millis duration of request in milliseconds
+ * @return the same object, so that setters can be chained
+ */
+ public LocationRequest setExpireIn(long millis) {
+ long elapsedRealtime = SystemClock.elapsedRealtime();
+
+ // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0):
+ if (millis > Long.MAX_VALUE - elapsedRealtime) {
+ mExpireAt = Long.MAX_VALUE;
+ } else {
+ mExpireAt = millis + elapsedRealtime;
+ }
+
+ if (mExpireAt < 0) mExpireAt = 0;
+ return this;
+ }
+
+ /**
+ * Set the request expiration time, in millisecond since boot.
+ *
+ * <p>This expiration time uses the same time base as {@link SystemClock#elapsedRealtime}.
+ *
+ * <p>The location manager will automatically stop updates after
+ * the request expires.
+ *
+ * <p>The duration includes suspend time. Values before {@link SystemClock#elapsedRealtime}
+ * are allowed, but indicate that the request has already expired.
+ *
+ * @param millis expiration time of request, in milliseconds since boot including suspend
+ * @return the same object, so that setters can be chained
+ */
+ public LocationRequest setExpireAt(long millis) {
+ mExpireAt = millis;
+ if (mExpireAt < 0) mExpireAt = 0;
+ return this;
+ }
+
+ /**
+ * Get the request expiration time, in milliseconds since boot.
+ *
+ * <p>This value can be compared to {@link SystemClock#elapsedRealtime} to determine
+ * the time until expiration.
+ *
+ * @return expiration time of request, in milliseconds since boot including suspend
+ */
+ public long getExpireAt() {
+ return mExpireAt;
+ }
+
+ /**
+ * Set the number of location updates.
+ *
+ * <p>By default locations are continuously updated until the request is explicitly
+ * removed, however you can optionally request a set number of updates.
+ * For example, if your application only needs a single fresh location,
+ * then call this method with a value of 1 before passing the request
+ * to the location manager.
+ *
+ * @param numUpdates the number of location updates requested
+ * @throws InvalidArgumentException if numUpdates is 0 or less
+ * @return the same object, so that setters can be chained
+ */
+ public LocationRequest setNumUpdates(int numUpdates) {
+ if (numUpdates <= 0) throw new IllegalArgumentException("invalid numUpdates: " + numUpdates);
+ mNumUpdates = numUpdates;
+ return this;
+ }
+
+ /**
+ * Get the number of updates requested.
+ *
+ * <p>By default this is {@link Integer#MAX_VALUE}, which indicates that
+ * locations are updated until the request is explicitly removed.
+ * @return number of updates
+ */
+ public int getNumUpdates() {
+ return mNumUpdates;
+ }
+
+ /** @hide */
+ public void decrementNumUpdates() {
+ if (mNumUpdates != Integer.MAX_VALUE) {
+ mNumUpdates--;
+ }
+ if (mNumUpdates < 0) {
+ mNumUpdates = 0;
+ }
+ }
+
+
+ /** @hide */
+ @SystemApi
+ public LocationRequest setProvider(String provider) {
+ checkProvider(provider);
+ mProvider = provider;
+ return this;
+ }
+
+ /** @hide */
+ @SystemApi
+ public String getProvider() {
+ return mProvider;
+ }
+
+ /** @hide */
+ @SystemApi
+ public LocationRequest setSmallestDisplacement(float meters) {
+ checkDisplacement(meters);
+ mSmallestDisplacement = meters;
+ return this;
+ }
+
+ /** @hide */
+ @SystemApi
+ public float getSmallestDisplacement() {
+ return mSmallestDisplacement;
+ }
+
+ /**
+ * Sets the WorkSource to use for power blaming of this location request.
+ *
+ * <p>No permissions are required to make this call, however the LocationManager
+ * will throw a SecurityException when requesting location updates if the caller
+ * doesn't have the {@link android.Manifest.permission#UPDATE_DEVICE_STATS} permission.
+ *
+ * @param workSource WorkSource defining power blame for this location request.
+ * @hide
+ */
+ @SystemApi
+ public void setWorkSource(WorkSource workSource) {
+ mWorkSource = workSource;
+ }
+
+ /** @hide */
+ @SystemApi
+ public WorkSource getWorkSource() {
+ return mWorkSource;
+ }
+
+ /**
+ * Sets whether or not this location request should be hidden from AppOps.
+ *
+ * <p>Hiding a location request from AppOps will remove user visibility in the UI as to this
+ * request's existence. It does not affect power blaming in the Battery page.
+ *
+ * <p>No permissions are required to make this call, however the LocationManager
+ * will throw a SecurityException when requesting location updates if the caller
+ * doesn't have the {@link android.Manifest.permission#UPDATE_APP_OPS_STATS} permission.
+ *
+ * @param hideFromAppOps If true AppOps won't keep track of this location request.
+ * @see android.app.AppOpsManager
+ * @hide
+ */
+ @SystemApi
+ public void setHideFromAppOps(boolean hideFromAppOps) {
+ mHideFromAppOps = hideFromAppOps;
+ }
+
+ /** @hide */
+ @SystemApi
+ public boolean getHideFromAppOps() {
+ return mHideFromAppOps;
+ }
+
+ private static void checkInterval(long millis) {
+ if (millis < 0) {
+ throw new IllegalArgumentException("invalid interval: " + millis);
+ }
+ }
+
+ private static void checkQuality(int quality) {
+ switch (quality) {
+ case ACCURACY_FINE:
+ case ACCURACY_BLOCK:
+ case ACCURACY_CITY:
+ case POWER_NONE:
+ case POWER_LOW:
+ case POWER_HIGH:
+ break;
+ default:
+ throw new IllegalArgumentException("invalid quality: " + quality);
+ }
+ }
+
+ private static void checkDisplacement(float meters) {
+ if (meters < 0.0f) {
+ throw new IllegalArgumentException("invalid displacement: " + meters);
+ }
+ }
+
+ private static void checkProvider(String name) {
+ if (name == null) {
+ throw new IllegalArgumentException("invalid provider: " + name);
+ }
+ }
+
+ public static final Parcelable.Creator<LocationRequest> CREATOR =
+ new Parcelable.Creator<LocationRequest>() {
+ @Override
+ public LocationRequest createFromParcel(Parcel in) {
+ LocationRequest request = new LocationRequest();
+ request.setQuality(in.readInt());
+ request.setFastestInterval(in.readLong());
+ request.setInterval(in.readLong());
+ request.setExpireAt(in.readLong());
+ request.setNumUpdates(in.readInt());
+ request.setSmallestDisplacement(in.readFloat());
+ request.setHideFromAppOps(in.readInt() != 0);
+ String provider = in.readString();
+ if (provider != null) request.setProvider(provider);
+ WorkSource workSource = in.readParcelable(null);
+ if (workSource != null) request.setWorkSource(workSource);
+ return request;
+ }
+ @Override
+ public LocationRequest[] newArray(int size) {
+ return new LocationRequest[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(Parcel parcel, int flags) {
+ parcel.writeInt(mQuality);
+ parcel.writeLong(mFastestInterval);
+ parcel.writeLong(mInterval);
+ parcel.writeLong(mExpireAt);
+ parcel.writeInt(mNumUpdates);
+ parcel.writeFloat(mSmallestDisplacement);
+ parcel.writeInt(mHideFromAppOps ? 1 : 0);
+ parcel.writeString(mProvider);
+ parcel.writeParcelable(mWorkSource, 0);
+ }
+
+ /** @hide */
+ public static String qualityToString(int quality) {
+ switch (quality) {
+ case ACCURACY_FINE:
+ return "ACCURACY_FINE";
+ case ACCURACY_BLOCK:
+ return "ACCURACY_BLOCK";
+ case ACCURACY_CITY:
+ return "ACCURACY_CITY";
+ case POWER_NONE:
+ return "POWER_NONE";
+ case POWER_LOW:
+ return "POWER_LOW";
+ case POWER_HIGH:
+ return "POWER_HIGH";
+ default:
+ return "???";
+ }
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("Request[").append(qualityToString(mQuality));
+ if (mProvider != null) s.append(' ').append(mProvider);
+ if (mQuality != POWER_NONE) {
+ s.append(" requested=");
+ TimeUtils.formatDuration(mInterval, s);
+ }
+ s.append(" fastest=");
+ TimeUtils.formatDuration(mFastestInterval, s);
+ if (mExpireAt != Long.MAX_VALUE) {
+ long expireIn = mExpireAt - SystemClock.elapsedRealtime();
+ s.append(" expireIn=");
+ TimeUtils.formatDuration(expireIn, s);
+ }
+ if (mNumUpdates != Integer.MAX_VALUE){
+ s.append(" num=").append(mNumUpdates);
+ }
+ s.append(']');
+ return s.toString();
+ }
+}
diff --git a/android/location/OnNmeaMessageListener.java b/android/location/OnNmeaMessageListener.java
new file mode 100644
index 00000000..ccf6ce85
--- /dev/null
+++ b/android/location/OnNmeaMessageListener.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2016 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 NMEA sentences from the GNSS.
+* NMEA 0183 is a standard for communicating with marine electronic devices
+* and is a common method for receiving data from a GNSS, typically over a serial port.
+* See <a href="http://en.wikipedia.org/wiki/NMEA_0183">NMEA 0183</a> for more details.
+* You can implement this interface and call {@link LocationManager#addNmeaListener}
+* to receive NMEA data from the GNSS engine.
+*/
+public interface OnNmeaMessageListener {
+ /**
+ * Called when an NMEA message is received.
+ * @param message NMEA message
+ * @param timestamp milliseconds since January 1, 1970.
+ */
+ void onNmeaMessage(String message, long timestamp);
+}
diff --git a/android/location/SettingInjectorService.java b/android/location/SettingInjectorService.java
new file mode 100644
index 00000000..fcd2cdec
--- /dev/null
+++ b/android/location/SettingInjectorService.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright (C) 2013 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 android.app.Service;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Messenger;
+import android.os.RemoteException;
+import android.util.Log;
+
+/**
+ * Dynamically specifies the enabled status of a preference injected into
+ * the list of app settings displayed by the system settings app
+ * <p/>
+ * For use only by apps that are included in the system image, for preferences that affect multiple
+ * apps. Location settings that apply only to one app should be shown within that app,
+ * rather than in the system settings.
+ * <p/>
+ * To add a preference to the list, a subclass of {@link SettingInjectorService} must be declared in
+ * the manifest as so:
+ *
+ * <pre>
+ * &lt;service android:name="com.example.android.injector.MyInjectorService" &gt;
+ * &lt;intent-filter&gt;
+ * &lt;action android:name="android.location.SettingInjectorService" /&gt;
+ * &lt;/intent-filter&gt;
+ *
+ * &lt;meta-data
+ * android:name="android.location.SettingInjectorService"
+ * android:resource="@xml/my_injected_location_setting" /&gt;
+ * &lt;/service&gt;
+ * </pre>
+ * The resource file specifies the static data for the setting:
+ * <pre>
+ * &lt;injected-location-setting xmlns:android="http://schemas.android.com/apk/res/android"
+ * android:title="@string/injected_setting_title"
+ * android:icon="@drawable/ic_acme_corp"
+ * android:settingsActivity="com.example.android.injector.MySettingActivity"
+ * /&gt;
+ * </pre>
+ * Here:
+ * <ul>
+ * <li>title: The {@link android.preference.Preference#getTitle()} value. The title should make
+ * it clear which apps are affected by the setting, typically by including the name of the
+ * developer. For example, "Acme Corp. ads preferences." </li>
+ *
+ * <li>icon: The {@link android.preference.Preference#getIcon()} value. Typically this will be a
+ * generic icon for the developer rather than the icon for an individual app.</li>
+ *
+ * <li>settingsActivity: the activity which is launched to allow the user to modify the setting
+ * value. The activity must be in the same package as the subclass of
+ * {@link SettingInjectorService}. The activity should use your own branding to help emphasize
+ * to the user that it is not part of the system settings.</li>
+ * </ul>
+ *
+ * To ensure a good user experience, your {@link android.app.Application#onCreate()},
+ * and {@link #onGetEnabled()} methods must all be fast. If either is slow,
+ * it can delay the display of settings values for other apps as well. Note further that these
+ * methods are called on your app's UI thread.
+ * <p/>
+ * For compactness, only one copy of a given setting should be injected. If each account has a
+ * distinct value for the setting, then only {@code settingsActivity} should display the value for
+ * each account.
+ */
+public abstract class SettingInjectorService extends Service {
+
+ private static final String TAG = "SettingInjectorService";
+
+ /**
+ * Intent action that must be declared in the manifest for the subclass. Used to start the
+ * service to read the dynamic status for the setting.
+ */
+ public static final String ACTION_SERVICE_INTENT = "android.location.SettingInjectorService";
+
+ /**
+ * Name of the meta-data tag used to specify the resource file that includes the settings
+ * attributes.
+ */
+ public static final String META_DATA_NAME = "android.location.SettingInjectorService";
+
+ /**
+ * Name of the XML tag that includes the attributes for the setting.
+ */
+ public static final String ATTRIBUTES_NAME = "injected-location-setting";
+
+ /**
+ * Intent action a client should broadcast when the value of one of its injected settings has
+ * changed, so that the setting can be updated in the UI.
+ */
+ public static final String ACTION_INJECTED_SETTING_CHANGED =
+ "android.location.InjectedSettingChanged";
+
+ /**
+ * Name of the bundle key for the string specifying whether the setting is currently enabled.
+ *
+ * @hide
+ */
+ public static final String ENABLED_KEY = "enabled";
+
+ /**
+ * Name of the intent key used to specify the messenger
+ *
+ * @hide
+ */
+ public static final String MESSENGER_KEY = "messenger";
+
+ private final String mName;
+
+ /**
+ * Constructor.
+ *
+ * @param name used to identify your subclass in log messages
+ */
+ public SettingInjectorService(String name) {
+ mName = name;
+ }
+
+ @Override
+ public final IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public final void onStart(Intent intent, int startId) {
+ super.onStart(intent, startId);
+ }
+
+ @Override
+ public final int onStartCommand(Intent intent, int flags, int startId) {
+ onHandleIntent(intent);
+ stopSelf(startId);
+ return START_NOT_STICKY;
+ }
+
+ private void onHandleIntent(Intent intent) {
+
+ boolean enabled;
+ try {
+ enabled = onGetEnabled();
+ } catch (RuntimeException e) {
+ // Exception. Send status anyway, so that settings injector can immediately start
+ // loading the status of the next setting.
+ sendStatus(intent, true);
+ throw e;
+ }
+
+ sendStatus(intent, enabled);
+ }
+
+ /**
+ * Send the enabled values back to the caller via the messenger encoded in the
+ * intent.
+ */
+ private void sendStatus(Intent intent, boolean enabled) {
+ Message message = Message.obtain();
+ Bundle bundle = new Bundle();
+ bundle.putBoolean(ENABLED_KEY, enabled);
+ message.setData(bundle);
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, mName + ": received " + intent
+ + ", enabled=" + enabled + ", sending message: " + message);
+ }
+
+ Messenger messenger = intent.getParcelableExtra(MESSENGER_KEY);
+ try {
+ messenger.send(message);
+ } catch (RemoteException e) {
+ Log.e(TAG, mName + ": sending dynamic status failed", e);
+ }
+ }
+
+ /**
+ * This method is no longer called, because status values are no longer shown for any injected
+ * setting.
+ *
+ * @return ignored
+ *
+ * @deprecated not called any more
+ */
+ @Deprecated
+ protected abstract String onGetSummary();
+
+ /**
+ * Returns the {@link android.preference.Preference#isEnabled()} value. Should not perform
+ * unpredictably-long operations such as network access--see the running-time comments in the
+ * class-level javadoc.
+ * <p/>
+ * Note that to prevent churn in the settings list, there is no support for dynamically choosing
+ * to hide a setting. Instead you should have this method return false, which will disable the
+ * setting and its link to your setting activity. One reason why you might choose to do this is
+ * if {@link android.provider.Settings.Secure#LOCATION_MODE} is {@link
+ * android.provider.Settings.Secure#LOCATION_MODE_OFF}.
+ * <p/>
+ * It is possible that the user may click on the setting before this method returns, so your
+ * settings activity must handle the case where it is invoked even though the setting is
+ * disabled. The simplest approach may be to simply call {@link android.app.Activity#finish()}
+ * when disabled.
+ *
+ * @return the {@link android.preference.Preference#isEnabled()} value
+ */
+ protected abstract boolean onGetEnabled();
+}