Automatic sources dropoff on 2020-06-10 18:32:38.095721
The change is generated with prebuilt drop tool.
Change-Id: I24cbf6ba6db262a1ae1445db1427a08fee35b3b4
diff --git a/android/location/AbstractListenerManager.java b/android/location/AbstractListenerManager.java
new file mode 100644
index 0000000..36b8689
--- /dev/null
+++ b/android/location/AbstractListenerManager.java
@@ -0,0 +1,230 @@
+/*
+ * 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 static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Binder;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.RemoteException;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.util.Preconditions;
+
+import java.util.Objects;
+import java.util.concurrent.Executor;
+import java.util.function.Consumer;
+
+/**
+ * A base class to manage listeners that have a 1:N -> source:listener relationship.
+ *
+ * @hide
+ */
+abstract class AbstractListenerManager<TRequest, TListener> {
+
+ private static class Registration<TRequest, TListener> {
+ private final Executor mExecutor;
+ @Nullable private TRequest mRequest;
+ @Nullable private volatile TListener mListener;
+
+ private Registration(@Nullable TRequest request, Executor executor, TListener listener) {
+ Preconditions.checkArgument(listener != null, "invalid null listener/callback");
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ mExecutor = executor;
+ mListener = listener;
+ mRequest = request;
+ }
+
+ @Nullable
+ public TRequest getRequest() {
+ return mRequest;
+ }
+
+ private void unregister() {
+ mRequest = null;
+ mListener = null;
+ }
+
+ private void execute(Consumer<TListener> operation) {
+ mExecutor.execute(
+ obtainRunnable(Registration<TRequest, TListener>::accept, this, operation)
+ .recycleOnUse());
+ }
+
+ private void accept(Consumer<TListener> operation) {
+ TListener listener = mListener;
+ if (listener == null) {
+ return;
+ }
+
+ // we may be under the binder identity if a direct executor is used
+ long identity = Binder.clearCallingIdentity();
+ try {
+ operation.accept(listener);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ }
+ }
+
+ private final Object mLock = new Object();
+
+ @GuardedBy("mLock")
+ private volatile ArrayMap<Object, Registration<TRequest, TListener>> mListeners =
+ new ArrayMap<>();
+
+ @GuardedBy("mLock")
+ @Nullable
+ private TRequest mMergedRequest;
+
+ public boolean addListener(@NonNull TListener listener, @NonNull Handler handler)
+ throws RemoteException {
+ return addInternal(/* request= */ null, listener, handler);
+ }
+
+ public boolean addListener(@NonNull TListener listener, @NonNull Executor executor)
+ throws RemoteException {
+ return addInternal(/* request= */ null, listener, executor);
+ }
+
+ public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
+ @NonNull Handler handler) throws RemoteException {
+ return addInternal(request, listener, handler);
+ }
+
+ public boolean addListener(@Nullable TRequest request, @NonNull TListener listener,
+ @NonNull Executor executor) throws RemoteException {
+ return addInternal(request, listener, executor);
+ }
+
+ protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
+ @NonNull Handler handler) throws RemoteException {
+ return addInternal(request, listener, new HandlerExecutor(handler));
+ }
+
+ protected final boolean addInternal(@Nullable TRequest request, @NonNull Object listener,
+ @NonNull Executor executor)
+ throws RemoteException {
+ Preconditions.checkArgument(listener != null, "invalid null listener/callback");
+ return addInternal(listener, new Registration<>(request, executor, convertKey(listener)));
+ }
+
+ private boolean addInternal(Object key, Registration<TRequest, TListener> registration)
+ throws RemoteException {
+ Preconditions.checkNotNull(registration);
+
+ synchronized (mLock) {
+ boolean initialRequest = mListeners.isEmpty();
+
+ ArrayMap<Object, Registration<TRequest, TListener>> newListeners = new ArrayMap<>(
+ mListeners.size() + 1);
+ newListeners.putAll(mListeners);
+ Registration<TRequest, TListener> oldRegistration = newListeners.put(key,
+ registration);
+ mListeners = newListeners;
+
+ if (oldRegistration != null) {
+ oldRegistration.unregister();
+ }
+ TRequest merged = mergeRequests();
+
+ if (initialRequest || !Objects.equals(merged, mMergedRequest)) {
+ mMergedRequest = merged;
+ if (!initialRequest) {
+ unregisterService();
+ }
+ registerService(mMergedRequest);
+ }
+
+ return true;
+ }
+ }
+
+ public void removeListener(Object listener) throws RemoteException {
+ synchronized (mLock) {
+ ArrayMap<Object, Registration<TRequest, TListener>> newListeners = new ArrayMap<>(
+ mListeners);
+ Registration<TRequest, TListener> oldRegistration = newListeners.remove(listener);
+ mListeners = newListeners;
+
+ if (oldRegistration == null) {
+ return;
+ }
+ oldRegistration.unregister();
+
+ boolean lastRequest = mListeners.isEmpty();
+ TRequest merged = lastRequest ? null : mergeRequests();
+ boolean newRequest = !lastRequest && !Objects.equals(merged, mMergedRequest);
+
+ if (lastRequest || newRequest) {
+ unregisterService();
+ mMergedRequest = merged;
+ if (newRequest) {
+ registerService(mMergedRequest);
+ }
+ }
+ }
+ }
+
+ @SuppressWarnings("unchecked")
+ protected TListener convertKey(@NonNull Object listener) {
+ return (TListener) listener;
+ }
+
+ protected abstract boolean registerService(TRequest request) throws RemoteException;
+ protected abstract void unregisterService() throws RemoteException;
+
+ @Nullable
+ protected TRequest merge(@NonNull TRequest[] requests) {
+ for (TRequest request : requests) {
+ Preconditions.checkArgument(request == null,
+ "merge() has to be overridden for non-null requests.");
+ }
+ return null;
+ }
+
+ protected void execute(Consumer<TListener> operation) {
+ for (Registration<TRequest, TListener> registration : mListeners.values()) {
+ registration.execute(operation);
+ }
+ }
+
+ @GuardedBy("mLock")
+ @SuppressWarnings("unchecked")
+ @Nullable
+ private TRequest mergeRequests() {
+ Preconditions.checkState(Thread.holdsLock(mLock));
+
+ if (mListeners.isEmpty()) {
+ return null;
+ }
+
+ if (mListeners.size() == 1) {
+ return mListeners.valueAt(0).getRequest();
+ }
+
+ TRequest[] requests = (TRequest[]) new Object[mListeners.size()];
+ for (int index = 0; index < mListeners.size(); index++) {
+ requests[index] = mListeners.valueAt(index).getRequest();
+ }
+ return merge(requests);
+ }
+}
diff --git a/android/location/Address.java b/android/location/Address.java
new file mode 100644
index 0000000..bb97c78
--- /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 phone number.
+ */
+ 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 @android.annotation.NonNull 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 0000000..f1c40ae
--- /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/Country.java b/android/location/Country.java
new file mode 100644
index 0000000..8c40338
--- /dev/null
+++ b/android/location/Country.java
@@ -0,0 +1,206 @@
+/*
+ * 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.compat.annotation.UnsupportedAppUsage;
+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>
+ */
+ @UnsupportedAppUsage
+ 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
+ */
+ @UnsupportedAppUsage
+ 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>
+ */
+ @UnsupportedAppUsage
+ 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 @android.annotation.NonNull 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 0000000..e344b82
--- /dev/null
+++ b/android/location/CountryDetector.java
@@ -0,0 +1,155 @@
+/*
+ * 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.annotation.SystemService;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.os.Build;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.RemoteException;
+import android.util.Log;
+
+import java.util.HashMap;
+
+/**
+ * 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.
+ */
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ 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.
+ */
+ @UnsupportedAppUsage
+ 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.
+ */
+ @UnsupportedAppUsage
+ 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
+ */
+ @UnsupportedAppUsage
+ 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 0000000..eb67205
--- /dev/null
+++ b/android/location/CountryListener.java
@@ -0,0 +1,33 @@
+/*
+ * 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.compat.annotation.UnsupportedAppUsage;
+
+/**
+ * The listener for receiving the notification when the country is detected or
+ * changed
+ *
+ * @hide
+ */
+public interface CountryListener {
+ /**
+ * @param country the changed or detected country.
+ */
+ @UnsupportedAppUsage
+ void onCountryDetected(Country country);
+}
diff --git a/android/location/Criteria.java b/android/location/Criteria.java
new file mode 100644
index 0000000..26f73f7
--- /dev/null
+++ b/android/location/Criteria.java
@@ -0,0 +1,447 @@
+/*
+ * 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.IntDef;
+import android.annotation.NonNull;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * A class indicating the application criteria for selecting a
+ * location provider. Providers may be ordered according to accuracy,
+ * power usage, ability to report altitude, speed, bearing, and monetary
+ * cost.
+ */
+public class Criteria implements Parcelable {
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({NO_REQUIREMENT, POWER_LOW, POWER_MEDIUM, POWER_HIGH})
+ public @interface PowerRequirement {
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({NO_REQUIREMENT, ACCURACY_LOW, ACCURACY_MEDIUM, ACCURACY_HIGH})
+ public @interface AccuracyRequirement {
+ }
+
+ /** @hide */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({NO_REQUIREMENT, ACCURACY_FINE, ACCURACY_COARSE})
+ public @interface LocationAccuracyRequirement {
+ }
+
+ /**
+ * 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(@AccuracyRequirement int accuracy) {
+ mHorizontalAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT,
+ ACCURACY_HIGH, "accuracy");
+ }
+
+ /**
+ * Returns a constant indicating the desired horizontal accuracy (latitude and longitude).
+ *
+ * @see #setHorizontalAccuracy(int)
+ */
+ @AccuracyRequirement
+ 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(@AccuracyRequirement int accuracy) {
+ mVerticalAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT,
+ ACCURACY_HIGH, "accuracy");
+ }
+
+ /**
+ * Returns a constant indicating the desired vertical accuracy (altitude).
+ *
+ * @see #setVerticalAccuracy(int)
+ */
+ @AccuracyRequirement
+ public int getVerticalAccuracy() {
+ return mVerticalAccuracy;
+ }
+
+ /**
+ * Indicates the desired speed accuracy. 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 setSpeedAccuracy(@AccuracyRequirement int accuracy) {
+ mSpeedAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT, ACCURACY_HIGH,
+ "accuracy");
+ }
+
+ /**
+ * Returns a constant indicating the desired speed accuracy.
+ *
+ * @see #setSpeedAccuracy(int)
+ */
+ @AccuracyRequirement
+ public int getSpeedAccuracy() {
+ return mSpeedAccuracy;
+ }
+
+ /**
+ * Indicates the desired bearing accuracy. 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 setBearingAccuracy(@AccuracyRequirement int accuracy) {
+ mBearingAccuracy = Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT,
+ ACCURACY_HIGH, "accuracy");
+ }
+
+ /**
+ * Returns a constant indicating the desired bearing accuracy.
+ *
+ * @see #setBearingAccuracy(int)
+ */
+ @AccuracyRequirement
+ public int getBearingAccuracy() {
+ return mBearingAccuracy;
+ }
+
+ /**
+ * Indicates the desired accuracy for latitude and longitude. Accuracy may be
+ * {@link #ACCURACY_FINE} or {@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(@LocationAccuracyRequirement int accuracy) {
+ Preconditions.checkArgumentInRange(accuracy, NO_REQUIREMENT, ACCURACY_COARSE, "accuracy");
+ switch (accuracy) {
+ case NO_REQUIREMENT:
+ setHorizontalAccuracy(NO_REQUIREMENT);
+ break;
+ case ACCURACY_FINE:
+ setHorizontalAccuracy(ACCURACY_HIGH);
+ break;
+ case ACCURACY_COARSE:
+ setHorizontalAccuracy(ACCURACY_LOW);
+ break;
+ }
+ }
+
+ /**
+ * Returns a constant indicating desired accuracy of location.
+ *
+ * @see #setAccuracy(int)
+ */
+ @LocationAccuracyRequirement
+ public int getAccuracy() {
+ if (mHorizontalAccuracy >= ACCURACY_HIGH) {
+ return ACCURACY_FINE;
+ } else {
+ return ACCURACY_COARSE;
+ }
+ }
+
+ /**
+ * Indicates the desired maximum power requirement. The power requirement parameter may be
+ * {@link #NO_REQUIREMENT}, {@link #POWER_LOW}, {@link #POWER_MEDIUM}, or {@link #POWER_HIGH}.
+ */
+ public void setPowerRequirement(@PowerRequirement int powerRequirement) {
+ mPowerRequirement = Preconditions.checkArgumentInRange(powerRequirement, NO_REQUIREMENT,
+ POWER_HIGH, "powerRequirement");
+ }
+
+ /**
+ * Returns a constant indicating the desired maximum power requirement.
+ *
+ * @see #setPowerRequirement(int)
+ */
+ @PowerRequirement
+ 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.
+ *
+ * @see #setAltitudeRequired(boolean)
+ */
+ 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.
+ *
+ * @see #setSpeedRequired(boolean)
+ */
+ 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.
+ *
+ * @see #setBearingRequired(boolean)
+ */
+ public boolean isBearingRequired() {
+ return mBearingRequired;
+ }
+
+ @NonNull
+ 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);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("Criteria[");
+ s.append("power=").append(requirementToString(mPowerRequirement)).append(", ");
+ s.append("accuracy=").append(requirementToString(mHorizontalAccuracy));
+ if (mVerticalAccuracy != NO_REQUIREMENT) {
+ s.append(", verticalAccuracy=").append(requirementToString(mVerticalAccuracy));
+ }
+ if (mSpeedAccuracy != NO_REQUIREMENT) {
+ s.append(", speedAccuracy=").append(requirementToString(mSpeedAccuracy));
+ }
+ if (mBearingAccuracy != NO_REQUIREMENT) {
+ s.append(", bearingAccuracy=").append(requirementToString(mBearingAccuracy));
+ }
+ if (mAltitudeRequired || mBearingRequired || mSpeedRequired) {
+ s.append(", required=[");
+ if (mAltitudeRequired) {
+ s.append("altitude, ");
+ }
+ if (mBearingRequired) {
+ s.append("bearing, ");
+ }
+ if (mSpeedRequired) {
+ s.append("speed, ");
+ }
+ s.setLength(s.length() - 2);
+ s.append("]");
+ }
+ if (mCostAllowed) {
+ s.append(", costAllowed");
+ }
+ s.append(']');
+ return s.toString();
+ }
+
+ private static String requirementToString(int power) {
+ switch (power) {
+ case NO_REQUIREMENT:
+ return "None";
+ //case ACCURACY_LOW:
+ case POWER_LOW:
+ return "Low";
+ //case ACCURACY_MEDIUM:
+ case POWER_MEDIUM:
+ return "Medium";
+ //case ACCURACY_HIGH:
+ case POWER_HIGH:
+ return "High";
+ default:
+ return "???";
+ }
+ }
+}
diff --git a/android/location/FusedBatchOptions.java b/android/location/FusedBatchOptions.java
new file mode 100644
index 0000000..4a022c3
--- /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 @android.annotation.NonNull 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 0000000..ac7eb8b
--- /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 0000000..1c6e9b6
--- /dev/null
+++ b/android/location/GeocoderParams.java
@@ -0,0 +1,97 @@
+/*
+ * 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.compat.annotation.UnsupportedAppUsage;
+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
+ */
+ @UnsupportedAppUsage
+ public Locale getLocale() {
+ return mLocale;
+ }
+
+ /**
+ * returns the package name of the Geocoder's client
+ */
+ @UnsupportedAppUsage
+ public String getClientPackage() {
+ return mPackageName;
+ }
+
+ public static final @android.annotation.NonNull 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 0000000..af57bfd
--- /dev/null
+++ b/android/location/Geofence.java
@@ -0,0 +1,184 @@
+/*
+ * 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.compat.annotation.UnsupportedAppUsage;
+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);
+ }
+ }
+
+ @UnsupportedAppUsage
+ public static final @android.annotation.NonNull 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/GnssAntennaInfo.java b/android/location/GnssAntennaInfo.java
new file mode 100644
index 0000000..b2f9a0f
--- /dev/null
+++ b/android/location/GnssAntennaInfo.java
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2020 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.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Objects;
+
+/**
+ * A class that contains information about a GNSS antenna. GNSS antenna characteristics can change
+ * with device configuration, such as when a device is folded open or closed. Antenna information is
+ * delivered to registered instances of {@link Listener}.
+ */
+public final class GnssAntennaInfo implements Parcelable {
+ private final double mCarrierFrequencyMHz;
+ private final PhaseCenterOffset mPhaseCenterOffset;
+ private final SphericalCorrections mPhaseCenterVariationCorrections;
+ private final SphericalCorrections mSignalGainCorrections;
+
+ /**
+ * Used for receiving GNSS antenna info from the GNSS engine. You can implement this interface
+ * and call {@link LocationManager#registerAntennaInfoListener};
+ */
+ public interface Listener {
+ /**
+ * Returns the latest GNSS antenna info. This event is triggered when a listener is
+ * registered, and whenever the antenna info changes (due to a device configuration change).
+ */
+ void onGnssAntennaInfoReceived(@NonNull List<GnssAntennaInfo> gnssAntennaInfos);
+ }
+
+ /**
+ * Class containing information about the antenna phase center offset (PCO). PCO is defined with
+ * respect to the origin of the Android sensor coordinate system, e.g., center of primary screen
+ * for mobiles - see sensor or form factor documents for details. Uncertainties are reported
+ * to 1-sigma.
+ */
+ public static final class PhaseCenterOffset implements Parcelable {
+ private final double mOffsetXMm;
+ private final double mOffsetXUncertaintyMm;
+ private final double mOffsetYMm;
+ private final double mOffsetYUncertaintyMm;
+ private final double mOffsetZMm;
+ private final double mOffsetZUncertaintyMm;
+
+ public PhaseCenterOffset(
+ double offsetXMm, double offsetXUncertaintyMm,
+ double offsetYMm, double offsetYUncertaintyMm,
+ double offsetZMm, double offsetZUncertaintyMm) {
+ mOffsetXMm = offsetXMm;
+ mOffsetYMm = offsetYMm;
+ mOffsetZMm = offsetZMm;
+ mOffsetXUncertaintyMm = offsetXUncertaintyMm;
+ mOffsetYUncertaintyMm = offsetYUncertaintyMm;
+ mOffsetZUncertaintyMm = offsetZUncertaintyMm;
+ }
+
+ public static final @NonNull Creator<PhaseCenterOffset> CREATOR =
+ new Creator<PhaseCenterOffset>() {
+ @Override
+ public PhaseCenterOffset createFromParcel(Parcel in) {
+ return new PhaseCenterOffset(
+ in.readDouble(),
+ in.readDouble(),
+ in.readDouble(),
+ in.readDouble(),
+ in.readDouble(),
+ in.readDouble()
+ );
+ }
+
+ @Override
+ public PhaseCenterOffset[] newArray(int size) {
+ return new PhaseCenterOffset[size];
+ }
+ };
+
+ @FloatRange()
+ public double getXOffsetMm() {
+ return mOffsetXMm;
+ }
+
+ @FloatRange()
+ public double getXOffsetUncertaintyMm() {
+ return mOffsetXUncertaintyMm;
+ }
+
+ @FloatRange()
+ public double getYOffsetMm() {
+ return mOffsetYMm;
+ }
+
+ @FloatRange()
+ public double getYOffsetUncertaintyMm() {
+ return mOffsetYUncertaintyMm;
+ }
+
+ @FloatRange()
+ public double getZOffsetMm() {
+ return mOffsetZMm;
+ }
+
+ @FloatRange()
+ public double getZOffsetUncertaintyMm() {
+ return mOffsetZUncertaintyMm;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeDouble(mOffsetXMm);
+ dest.writeDouble(mOffsetXUncertaintyMm);
+ dest.writeDouble(mOffsetYMm);
+ dest.writeDouble(mOffsetYUncertaintyMm);
+ dest.writeDouble(mOffsetZMm);
+ dest.writeDouble(mOffsetZUncertaintyMm);
+ }
+
+ @Override
+ public String toString() {
+ return "PhaseCenterOffset{"
+ + "OffsetXMm=" + mOffsetXMm + " +/-" + mOffsetXUncertaintyMm
+ + ", OffsetYMm=" + mOffsetYMm + " +/-" + mOffsetYUncertaintyMm
+ + ", OffsetZMm=" + mOffsetZMm + " +/-" + mOffsetZUncertaintyMm
+ + '}';
+ }
+ }
+
+ /**
+ * Represents corrections on a spherical mapping. Corrections are added to measurements to
+ * obtain the corrected values.
+ *
+ * The corrections and associated (1-sigma) uncertainties are represented by respect 2D arrays.
+ *
+ * Each row (major indices) represents a fixed theta. The first row corresponds to a
+ * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta)
+ * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e., deltaTheta
+ * = 360 / (number of rows).
+ *
+ * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and ending
+ * at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith angles,
+ * i.e., deltaPhi = 180 / (number of columns - 1).
+ */
+ public static final class SphericalCorrections implements Parcelable{
+ private final double[][] mCorrections;
+ private final double[][] mCorrectionUncertainties;
+ private final double mDeltaTheta;
+ private final double mDeltaPhi;
+ private final int mNumRows;
+ private final int mNumColumns;
+
+ public SphericalCorrections(@NonNull double[][] corrections,
+ @NonNull double[][] correctionUncertainties) {
+ if (corrections.length != correctionUncertainties.length
+ || corrections[0].length != correctionUncertainties[0].length) {
+ throw new IllegalArgumentException("Correction and correction uncertainty arrays "
+ + "must have the same dimensions.");
+ }
+
+ mNumRows = corrections.length;
+ if (mNumRows < 1) {
+ throw new IllegalArgumentException("Arrays must have at least one row.");
+ }
+
+ mNumColumns = corrections[0].length;
+ if (mNumColumns < 2) {
+ throw new IllegalArgumentException("Arrays must have at least two columns.");
+ }
+
+ mCorrections = corrections;
+ mCorrectionUncertainties = correctionUncertainties;
+ mDeltaTheta = 360.0d / mNumRows;
+ mDeltaPhi = 180.0d / (mNumColumns - 1);
+ }
+
+ SphericalCorrections(Parcel in) {
+ int numRows = in.readInt();
+ int numColumns = in.readInt();
+
+ double[][] corrections =
+ new double[numRows][numColumns];
+ double[][] correctionUncertainties =
+ new double[numRows][numColumns];
+
+ for (int row = 0; row < numRows; row++) {
+ in.readDoubleArray(corrections[row]);
+ }
+
+ for (int row = 0; row < numRows; row++) {
+ in.readDoubleArray(correctionUncertainties[row]);
+ }
+
+ mNumRows = numRows;
+ mNumColumns = numColumns;
+ mCorrections = corrections;
+ mCorrectionUncertainties = correctionUncertainties;
+ mDeltaTheta = 360.0d / mNumRows;
+ mDeltaPhi = 180.0d / (mNumColumns - 1);
+ }
+
+ /**
+ * Array representing corrections on a spherical mapping. Corrections are added to
+ * measurements to obtain the corrected values.
+ *
+ * Each row (major indices) represents a fixed theta. The first row corresponds to a
+ * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta)
+ * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e.,
+ * deltaTheta = 360 / (number of rows).
+ *
+ * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and
+ * ending at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith
+ * angles, i.e., deltaPhi = 180 / (number of columns - 1).
+ */
+ @NonNull
+ public double[][] getCorrectionsArray() {
+ return mCorrections;
+ }
+
+ /**
+ * Array representing uncertainty on corrections on a spherical mapping.
+ *
+ * Each row (major indices) represents a fixed theta. The first row corresponds to a
+ * theta angle of 0 degrees. The last row corresponds to a theta angle of (360 - deltaTheta)
+ * degrees, where deltaTheta is the regular spacing between azimuthal angles, i.e.,
+ * deltaTheta = 360 / (number of rows).
+ *
+ * The columns (minor indices) represent fixed zenith angles, beginning at 0 degrees and
+ * ending at 180 degrees. They are separated by deltaPhi, the regular spacing between zenith
+ * angles, i.e., deltaPhi = 180 / (number of columns - 1).
+ */
+ @NonNull
+ public double[][] getCorrectionUncertaintiesArray() {
+ return mCorrectionUncertainties;
+ }
+
+ /**
+ * The fixed theta angle separation between successive rows.
+ */
+ @FloatRange(from = 0.0f, to = 360.0f)
+ public double getDeltaTheta() {
+ return mDeltaTheta;
+ }
+
+ /**
+ * The fixed phi angle separation between successive columns.
+ */
+ @FloatRange(from = 0.0f, to = 180.0f)
+ public double getDeltaPhi() {
+ return mDeltaPhi;
+ }
+
+
+ public static final @NonNull Creator<SphericalCorrections> CREATOR =
+ new Creator<SphericalCorrections>() {
+ @Override
+ public SphericalCorrections createFromParcel(Parcel in) {
+ return new SphericalCorrections(in);
+ }
+
+ @Override
+ public SphericalCorrections[] newArray(int size) {
+ return new SphericalCorrections[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel dest, int flags) {
+ dest.writeInt(mNumRows);
+ dest.writeInt(mNumColumns);
+ for (double[] row: mCorrections) {
+ dest.writeDoubleArray(row);
+ }
+ for (double[] row: mCorrectionUncertainties) {
+ dest.writeDoubleArray(row);
+ }
+ }
+
+ @Override
+ public String toString() {
+ return "SphericalCorrections{"
+ + "Corrections=" + Arrays.toString(mCorrections)
+ + ", CorrectionUncertainties=" + Arrays.toString(mCorrectionUncertainties)
+ + ", DeltaTheta=" + mDeltaTheta
+ + ", DeltaPhi=" + mDeltaPhi
+ + '}';
+ }
+ }
+
+ private GnssAntennaInfo(
+ double carrierFrequencyMHz,
+ @NonNull PhaseCenterOffset phaseCenterOffset,
+ @Nullable SphericalCorrections phaseCenterVariationCorrections,
+ @Nullable SphericalCorrections signalGainCorrectionDbi) {
+ if (phaseCenterOffset == null) {
+ throw new IllegalArgumentException("Phase Center Offset Coordinates cannot be null.");
+ }
+ mCarrierFrequencyMHz = carrierFrequencyMHz;
+ mPhaseCenterOffset = phaseCenterOffset;
+ mPhaseCenterVariationCorrections = phaseCenterVariationCorrections;
+ mSignalGainCorrections = signalGainCorrectionDbi;
+ }
+
+ /**
+ * Builder class for GnssAntennaInfo.
+ */
+ public static class Builder {
+ private double mCarrierFrequencyMHz;
+ private PhaseCenterOffset mPhaseCenterOffset;
+ private SphericalCorrections mPhaseCenterVariationCorrections;
+ private SphericalCorrections mSignalGainCorrections;
+
+ /**
+ * Set antenna carrier frequency (MHz).
+ * @param carrierFrequencyMHz antenna carrier frequency (MHz)
+ * @return Builder builder object
+ */
+ @NonNull
+ public Builder setCarrierFrequencyMHz(@FloatRange(from = 0.0f) double carrierFrequencyMHz) {
+ mCarrierFrequencyMHz = carrierFrequencyMHz;
+ return this;
+ }
+
+ /**
+ * Set antenna phase center offset.
+ * @param phaseCenterOffset phase center offset object
+ * @return Builder builder object
+ */
+ @NonNull
+ public Builder setPhaseCenterOffset(@NonNull PhaseCenterOffset phaseCenterOffset) {
+ mPhaseCenterOffset = Objects.requireNonNull(phaseCenterOffset);
+ return this;
+ }
+
+ /**
+ * Set phase center variation corrections.
+ * @param phaseCenterVariationCorrections phase center variation corrections object
+ * @return Builder builder object
+ */
+ @NonNull
+ public Builder setPhaseCenterVariationCorrections(
+ @Nullable SphericalCorrections phaseCenterVariationCorrections) {
+ mPhaseCenterVariationCorrections = phaseCenterVariationCorrections;
+ return this;
+ }
+
+ /**
+ * Set signal gain corrections.
+ * @param signalGainCorrections signal gain corrections object
+ * @return Builder builder object
+ */
+ @NonNull
+ public Builder setSignalGainCorrections(
+ @Nullable SphericalCorrections signalGainCorrections) {
+ mSignalGainCorrections = signalGainCorrections;
+ return this;
+ }
+
+ /**
+ * Build GnssAntennaInfo object.
+ * @return instance of GnssAntennaInfo
+ */
+ @NonNull
+ public GnssAntennaInfo build() {
+ return new GnssAntennaInfo(mCarrierFrequencyMHz, mPhaseCenterOffset,
+ mPhaseCenterVariationCorrections, mSignalGainCorrections);
+ }
+ }
+
+ @FloatRange(from = 0.0f)
+ public double getCarrierFrequencyMHz() {
+ return mCarrierFrequencyMHz;
+ }
+
+ @NonNull
+ public PhaseCenterOffset getPhaseCenterOffset() {
+ return mPhaseCenterOffset;
+ }
+
+ @Nullable
+ public SphericalCorrections getPhaseCenterVariationCorrections() {
+ return mPhaseCenterVariationCorrections;
+ }
+
+ @Nullable
+ public SphericalCorrections getSignalGainCorrections() {
+ return mSignalGainCorrections;
+ }
+
+ public static final @android.annotation.NonNull
+ Creator<GnssAntennaInfo> CREATOR = new Creator<GnssAntennaInfo>() {
+ @Override
+ public GnssAntennaInfo createFromParcel(Parcel in) {
+ double carrierFrequencyMHz = in.readDouble();
+
+ ClassLoader classLoader = getClass().getClassLoader();
+ PhaseCenterOffset phaseCenterOffset =
+ in.readParcelable(classLoader);
+ SphericalCorrections phaseCenterVariationCorrections =
+ in.readParcelable(classLoader);
+ SphericalCorrections signalGainCorrections =
+ in.readParcelable(classLoader);
+
+ return new GnssAntennaInfo(
+ carrierFrequencyMHz,
+ phaseCenterOffset,
+ phaseCenterVariationCorrections,
+ signalGainCorrections);
+ }
+
+ @Override
+ public GnssAntennaInfo[] newArray(int size) {
+ return new GnssAntennaInfo[size];
+ }
+ };
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mCarrierFrequencyMHz);
+ parcel.writeParcelable(mPhaseCenterOffset, flags);
+ parcel.writeParcelable(mPhaseCenterVariationCorrections, flags);
+ parcel.writeParcelable(mSignalGainCorrections, flags);
+ }
+
+ @Override
+ public String toString() {
+ return "GnssAntennaInfo{"
+ + "CarrierFrequencyMHz=" + mCarrierFrequencyMHz
+ + ", PhaseCenterOffset=" + mPhaseCenterOffset
+ + ", PhaseCenterVariationCorrections=" + mPhaseCenterVariationCorrections
+ + ", SignalGainCorrections=" + mSignalGainCorrections
+ + '}';
+ }
+}
diff --git a/android/location/GnssCapabilities.java b/android/location/GnssCapabilities.java
new file mode 100644
index 0000000..5734bf2
--- /dev/null
+++ b/android/location/GnssCapabilities.java
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2019 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;
+
+/**
+ * A container of supported GNSS chipset capabilities.
+ */
+public final class GnssCapabilities {
+ /**
+ * Bit mask indicating GNSS chipset supports low power mode.
+ * @hide
+ */
+ public static final long LOW_POWER_MODE = 1L << 0;
+
+ /**
+ * Bit mask indicating GNSS chipset supports blacklisting satellites.
+ * @hide
+ */
+ public static final long SATELLITE_BLACKLIST = 1L << 1;
+
+ /**
+ * Bit mask indicating GNSS chipset supports geofencing.
+ * @hide
+ */
+ public static final long GEOFENCING = 1L << 2;
+
+ /**
+ * Bit mask indicating GNSS chipset supports measurements.
+ * @hide
+ */
+ public static final long MEASUREMENTS = 1L << 3;
+
+ /**
+ * Bit mask indicating GNSS chipset supports navigation messages.
+ * @hide
+ */
+ public static final long NAV_MESSAGES = 1L << 4;
+
+ /**
+ * Bit mask indicating GNSS chipset supports measurement corrections.
+ * @hide
+ */
+ public static final long MEASUREMENT_CORRECTIONS = 1L << 5;
+
+ /**
+ * Bit mask indicating GNSS chipset supports line-of-sight satellite identification
+ * measurement corrections.
+ * @hide
+ */
+ public static final long MEASUREMENT_CORRECTIONS_LOS_SATS = 1L << 6;
+
+ /**
+ * Bit mask indicating GNSS chipset supports per satellite excess-path-length
+ * measurement corrections.
+ * @hide
+ */
+ public static final long MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH = 1L << 7;
+
+ /**
+ * Bit mask indicating GNSS chipset supports reflecting planes measurement corrections.
+ * @hide
+ */
+ public static final long MEASUREMENT_CORRECTIONS_REFLECTING_PLANE = 1L << 8;
+
+ /**
+ * Bit mask indicating GNSS chipset supports GNSS antenna info.
+ * @hide
+ */
+ public static final long ANTENNA_INFO = 1L << 9;
+
+ /** @hide */
+ public static final long INVALID_CAPABILITIES = -1;
+
+ /** A bitmask of supported GNSS capabilities. */
+ private final long mGnssCapabilities;
+
+ /** @hide */
+ public static GnssCapabilities of(long gnssCapabilities) {
+ return new GnssCapabilities(gnssCapabilities);
+ }
+
+ private GnssCapabilities(long gnssCapabilities) {
+ mGnssCapabilities = gnssCapabilities;
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports low power mode, {@code false} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean hasLowPowerMode() {
+ return hasCapability(LOW_POWER_MODE);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports blacklisting satellites, {@code false}
+ * otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean hasSatelliteBlacklist() {
+ return hasCapability(SATELLITE_BLACKLIST);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports geofencing, {@code false} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean hasGeofencing() {
+ return hasCapability(GEOFENCING);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports measurements, {@code false} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean hasMeasurements() {
+ return hasCapability(MEASUREMENTS);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports navigation messages, {@code false} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean hasNavMessages() {
+ return hasCapability(NAV_MESSAGES);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports measurement corrections, {@code false}
+ * otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean hasMeasurementCorrections() {
+ return hasCapability(MEASUREMENT_CORRECTIONS);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports line-of-sight satellite identification
+ * measurement corrections, {@code false} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean hasMeasurementCorrectionsLosSats() {
+ return hasCapability(MEASUREMENT_CORRECTIONS_LOS_SATS);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports per satellite excess-path-length measurement
+ * corrections, {@code false} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean hasMeasurementCorrectionsExcessPathLength() {
+ return hasCapability(MEASUREMENT_CORRECTIONS_EXCESS_PATH_LENGTH);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports reflecting planes measurement corrections,
+ * {@code false} otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean hasMeasurementCorrectionsReflectingPane() {
+ return hasCapability(MEASUREMENT_CORRECTIONS_REFLECTING_PLANE);
+ }
+
+ /**
+ * Returns {@code true} if GNSS chipset supports antenna info, {@code false} otherwise.
+ */
+ public boolean hasGnssAntennaInfo() {
+ return hasCapability(ANTENNA_INFO);
+ }
+
+ private boolean hasCapability(long capability) {
+ return (mGnssCapabilities & capability) == capability;
+ }
+}
diff --git a/android/location/GnssClock.java b/android/location/GnssClock.java
new file mode 100644
index 0000000..ed4bf1b
--- /dev/null
+++ b/android/location/GnssClock.java
@@ -0,0 +1,810 @@
+/*
+ * 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.FloatRange;
+import android.annotation.NonNull;
+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);
+ private static final int HAS_ELAPSED_REALTIME_NANOS = (1 << 7);
+ private static final int HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS = (1 << 8);
+ private static final int HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB = (1 << 9);
+ private static final int HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB = (1 << 10);
+ private static final int HAS_REFERENCE_CODE_TYPE_FOR_ISB = (1 << 11);
+
+ // 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;
+ private long mElapsedRealtimeNanos;
+ private double mElapsedRealtimeUncertaintyNanos;
+ private int mReferenceConstellationTypeForIsb;
+ private double mReferenceCarrierFrequencyHzForIsb;
+ private String mReferenceCodeTypeForIsb;
+
+ /**
+ * @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;
+ mElapsedRealtimeNanos = clock.mElapsedRealtimeNanos;
+ mElapsedRealtimeUncertaintyNanos = clock.mElapsedRealtimeUncertaintyNanos;
+ mReferenceConstellationTypeForIsb = clock.mReferenceConstellationTypeForIsb;
+ mReferenceCarrierFrequencyHzForIsb = clock.mReferenceCarrierFrequencyHzForIsb;
+ mReferenceCodeTypeForIsb = clock.mReferenceCodeTypeForIsb;
+ }
+
+ /**
+ * 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.
+ */
+ @FloatRange(from = 0.0f)
+ public double getTimeUncertaintyNanos() {
+ return mTimeUncertaintyNanos;
+ }
+
+ /**
+ * Sets the clock's Time Uncertainty (1-Sigma) in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void setTimeUncertaintyNanos(@FloatRange(from = 0.0f) 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * 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}.
+ */
+ @FloatRange(from = 0.0f)
+ public double getBiasUncertaintyNanos() {
+ return mBiasUncertaintyNanos;
+ }
+
+ /**
+ * Sets the clock's Bias Uncertainty (1-Sigma) in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void setBiasUncertaintyNanos(@FloatRange(from = 0.0f) 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);
+ }
+
+ /**
+ * 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>This value is the instantaneous time-derivative of the value provided by
+ * {@link #getBiasNanos()}.
+ *
+ * <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);
+ }
+
+ /**
+ * 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}.
+ */
+ @FloatRange(from = 0.0f)
+ public double getDriftUncertaintyNanosPerSecond() {
+ return mDriftUncertaintyNanosPerSecond;
+ }
+
+ /**
+ * Sets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second.
+ * @hide
+ */
+ @TestApi
+ public void setDriftUncertaintyNanosPerSecond(
+ @FloatRange(from = 0.0f) 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);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getElapsedRealtimeNanos()} is available, {@code false}
+ * otherwise.
+ */
+ public boolean hasElapsedRealtimeNanos() {
+ return isFlagSet(HAS_ELAPSED_REALTIME_NANOS);
+ }
+
+ /**
+ * Returns the elapsed real-time of this clock since system boot, in nanoseconds.
+ *
+ * <p>The value is only available if {@link #hasElapsedRealtimeNanos()} is
+ * {@code true}.
+ */
+ public long getElapsedRealtimeNanos() {
+ return mElapsedRealtimeNanos;
+ }
+
+ /**
+ * Sets the elapsed real-time of this clock since system boot, in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void setElapsedRealtimeNanos(long elapsedRealtimeNanos) {
+ setFlag(HAS_ELAPSED_REALTIME_NANOS);
+ mElapsedRealtimeNanos = elapsedRealtimeNanos;
+ }
+
+ /**
+ * Resets the elapsed real-time of this clock since system boot, in nanoseconds.
+ * @hide
+ */
+ @TestApi
+ public void resetElapsedRealtimeNanos() {
+ resetFlag(HAS_ELAPSED_REALTIME_NANOS);
+ mElapsedRealtimeNanos = 0;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getElapsedRealtimeUncertaintyNanos()} is available, {@code
+ * false} otherwise.
+ */
+ public boolean hasElapsedRealtimeUncertaintyNanos() {
+ return isFlagSet(HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS);
+ }
+
+ /**
+ * Gets the estimate of the relative precision of the alignment of the
+ * {@link #getElapsedRealtimeNanos()} timestamp, with the reported measurements in
+ * nanoseconds (68% confidence).
+ *
+ * <p>The value is only available if {@link #hasElapsedRealtimeUncertaintyNanos()} is
+ * {@code true}.
+ */
+ @FloatRange(from = 0.0f)
+ public double getElapsedRealtimeUncertaintyNanos() {
+ return mElapsedRealtimeUncertaintyNanos;
+ }
+
+ /**
+ * Sets the estimate of the relative precision of the alignment of the
+ * {@link #getElapsedRealtimeNanos()} timestamp, with the reported measurements in
+ * nanoseconds (68% confidence).
+ * @hide
+ */
+ @TestApi
+ public void setElapsedRealtimeUncertaintyNanos(
+ @FloatRange(from = 0.0f) double elapsedRealtimeUncertaintyNanos) {
+ setFlag(HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS);
+ mElapsedRealtimeUncertaintyNanos = elapsedRealtimeUncertaintyNanos;
+ }
+
+ /**
+ * Resets the estimate of the relative precision of the alignment of the
+ * {@link #getElapsedRealtimeNanos()} timestamp, with the reported measurements in
+ * nanoseconds (68% confidence).
+ * @hide
+ */
+ @TestApi
+ public void resetElapsedRealtimeUncertaintyNanos() {
+ resetFlag(HAS_ELAPSED_REALTIME_UNCERTAINTY_NANOS);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getReferenceConstellationTypeForIsb()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasReferenceConstellationTypeForIsb() {
+ return isFlagSet(HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB);
+ }
+
+ /**
+ * Returns the reference constellation type for inter-signal bias.
+ *
+ * <p>The value is only available if {@link #hasReferenceConstellationTypeForIsb()} is
+ * {@code true}.
+ *
+ * <p>The return value is one of those constants with {@code CONSTELLATION_} prefix in
+ * {@link GnssStatus}.
+ */
+ @GnssStatus.ConstellationType
+ public int getReferenceConstellationTypeForIsb() {
+ return mReferenceConstellationTypeForIsb;
+ }
+
+ /**
+ * Sets the reference constellation type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void setReferenceConstellationTypeForIsb(@GnssStatus.ConstellationType int value) {
+ setFlag(HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB);
+ mReferenceConstellationTypeForIsb = value;
+ }
+
+ /**
+ * Resets the reference constellation type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void resetReferenceConstellationTypeForIsb() {
+ resetFlag(HAS_REFERENCE_CONSTELLATION_TYPE_FOR_ISB);
+ mReferenceConstellationTypeForIsb = GnssStatus.CONSTELLATION_UNKNOWN;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getReferenceCarrierFrequencyHzForIsb()} is available, {@code
+ * false} otherwise.
+ */
+ public boolean hasReferenceCarrierFrequencyHzForIsb() {
+ return isFlagSet(HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB);
+ }
+
+ /**
+ * Returns the reference carrier frequency in Hz for inter-signal bias.
+ *
+ * <p>The value is only available if {@link #hasReferenceCarrierFrequencyHzForIsb()} is
+ * {@code true}.
+ */
+ @FloatRange(from = 0.0)
+ public double getReferenceCarrierFrequencyHzForIsb() {
+ return mReferenceCarrierFrequencyHzForIsb;
+ }
+
+ /**
+ * Sets the reference carrier frequency in Hz for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void setReferenceCarrierFrequencyHzForIsb(@FloatRange(from = 0.0) double value) {
+ setFlag(HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB);
+ mReferenceCarrierFrequencyHzForIsb = value;
+ }
+
+ /**
+ * Resets the reference carrier frequency in Hz for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void resetReferenceCarrierFrequencyHzForIsb() {
+ resetFlag(HAS_REFERENCE_CARRIER_FREQUENCY_FOR_ISB);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getReferenceCodeTypeForIsb()} is available, {@code
+ * false} otherwise.
+ */
+ public boolean hasReferenceCodeTypeForIsb() {
+ return isFlagSet(HAS_REFERENCE_CODE_TYPE_FOR_ISB);
+ }
+
+ /**
+ * Returns the reference code type for inter-signal bias.
+ *
+ * <p>The value is only available if {@link #hasReferenceCodeTypeForIsb()} is
+ * {@code true}.
+ *
+ * <p>The return value is one of those constants defined in
+ * {@link GnssMeasurement#getCodeType()}.
+ */
+ @NonNull
+ public String getReferenceCodeTypeForIsb() {
+ return mReferenceCodeTypeForIsb;
+ }
+
+ /**
+ * Sets the reference code type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void setReferenceCodeTypeForIsb(@NonNull String codeType) {
+ setFlag(HAS_REFERENCE_CODE_TYPE_FOR_ISB);
+ mReferenceCodeTypeForIsb = codeType;
+ }
+
+ /**
+ * Resets the reference code type for inter-signal bias.
+ * @hide
+ */
+ @TestApi
+ public void resetReferenceCodeTypeForIsb() {
+ resetFlag(HAS_REFERENCE_CODE_TYPE_FOR_ISB);
+ mReferenceCodeTypeForIsb = "UNKNOWN";
+ }
+
+ /**
+ * 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
+ * & 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 @android.annotation.NonNull 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();
+ gpsClock.mElapsedRealtimeNanos = parcel.readLong();
+ gpsClock.mElapsedRealtimeUncertaintyNanos = parcel.readDouble();
+ gpsClock.mReferenceConstellationTypeForIsb = parcel.readInt();
+ gpsClock.mReferenceCarrierFrequencyHzForIsb = parcel.readDouble();
+ gpsClock.mReferenceCodeTypeForIsb = parcel.readString();
+
+ 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);
+ parcel.writeLong(mElapsedRealtimeNanos);
+ parcel.writeDouble(mElapsedRealtimeUncertaintyNanos);
+ parcel.writeInt(mReferenceConstellationTypeForIsb);
+ parcel.writeDouble(mReferenceCarrierFrequencyHzForIsb);
+ parcel.writeString(mReferenceCodeTypeForIsb);
+ }
+
+ @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");
+
+ if (hasLeapSecond()) {
+ builder.append(String.format(format, "LeapSecond", mLeapSecond));
+ }
+
+ builder.append(String.format(
+ formatWithUncertainty,
+ "TimeNanos",
+ mTimeNanos,
+ "TimeUncertaintyNanos",
+ hasTimeUncertaintyNanos() ? mTimeUncertaintyNanos : null));
+
+ if (hasFullBiasNanos()) {
+ builder.append(String.format(format, "FullBiasNanos", mFullBiasNanos));
+ }
+
+ if (hasBiasNanos() || hasBiasUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "BiasNanos",
+ hasBiasNanos() ? mBiasNanos : null,
+ "BiasUncertaintyNanos",
+ hasBiasUncertaintyNanos() ? mBiasUncertaintyNanos : null));
+ }
+
+ if (hasDriftNanosPerSecond() || hasDriftUncertaintyNanosPerSecond()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "DriftNanosPerSecond",
+ hasDriftNanosPerSecond() ? mDriftNanosPerSecond : null,
+ "DriftUncertaintyNanosPerSecond",
+ hasDriftUncertaintyNanosPerSecond() ? mDriftUncertaintyNanosPerSecond : null));
+ }
+
+ builder.append(String.format(
+ format,
+ "HardwareClockDiscontinuityCount",
+ mHardwareClockDiscontinuityCount));
+
+ if (hasElapsedRealtimeNanos() || hasElapsedRealtimeUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "ElapsedRealtimeNanos",
+ hasElapsedRealtimeNanos() ? mElapsedRealtimeNanos : null,
+ "ElapsedRealtimeUncertaintyNanos",
+ hasElapsedRealtimeUncertaintyNanos() ? mElapsedRealtimeUncertaintyNanos
+ : null));
+ }
+
+ if (hasReferenceConstellationTypeForIsb()) {
+ builder.append(String.format(format, "ReferenceConstellationTypeForIsb",
+ mReferenceConstellationTypeForIsb));
+ }
+
+ if (hasReferenceCarrierFrequencyHzForIsb()) {
+ builder.append(String.format(format, "ReferenceCarrierFrequencyHzForIsb",
+ mReferenceCarrierFrequencyHzForIsb));
+ }
+
+ if (hasReferenceCodeTypeForIsb()) {
+ builder.append(
+ String.format(format, "ReferenceCodeTypeForIsb", mReferenceCodeTypeForIsb));
+ }
+
+ 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);
+ resetElapsedRealtimeNanos();
+ resetElapsedRealtimeUncertaintyNanos();
+ resetReferenceConstellationTypeForIsb();
+ resetReferenceCarrierFrequencyHzForIsb();
+ resetReferenceCodeTypeForIsb();
+ }
+
+ 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 0000000..cd2af1b
--- /dev/null
+++ b/android/location/GnssMeasurement.java
@@ -0,0 +1,1858 @@
+/*
+ * 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 static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_AUTOMATIC_GAIN_CONTROL;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_CYCLES;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_FREQUENCY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_PHASE;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_CARRIER_PHASE_UNCERTAINTY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_FULL_ISB;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_FULL_ISB_UNCERTAINTY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SATELLITE_ISB;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SATELLITE_ISB_UNCERTAINTY;
+import static android.hardware.gnss.V2_1.IGnssMeasurementCallback.GnssMeasurementFlags.HAS_SNR;
+
+import android.annotation.FloatRange;
+import android.annotation.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+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 mBasebandCn0DbHz;
+ 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;
+ @NonNull private String mCodeType;
+ private double mFullInterSignalBiasNanos;
+ private double mFullInterSignalBiasUncertaintyNanos;
+ private double mSatelliteInterSignalBiasNanos;
+ private double mSatelliteInterSignalBiasUncertaintyNanos;
+
+ // The following enumerations must be in sync with the values declared in GNSS HAL.
+
+ private static final int HAS_NO_FLAGS = 0;
+ private static final int HAS_CODE_TYPE = (1 << 14);
+ private static final int HAS_BASEBAND_CN0 = (1 << 15);
+
+ /**
+ * 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;
+
+ /**
+ * GNSS measurement tracking loop state
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "STATE_" }, value = {
+ STATE_CODE_LOCK, STATE_BIT_SYNC, STATE_SUBFRAME_SYNC,
+ STATE_TOW_DECODED, STATE_MSEC_AMBIGUOUS, STATE_SYMBOL_SYNC, STATE_GLO_STRING_SYNC,
+ STATE_GLO_TOD_DECODED, STATE_BDS_D2_BIT_SYNC, STATE_BDS_D2_SUBFRAME_SYNC,
+ STATE_GAL_E1BC_CODE_LOCK, STATE_GAL_E1C_2ND_CODE_LOCK, STATE_GAL_E1B_PAGE_SYNC,
+ STATE_SBAS_SYNC, STATE_TOW_KNOWN, STATE_GLO_TOD_KNOWN, STATE_2ND_CODE_LOCK
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface State {}
+
+ /** 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);
+
+ /** This GNSS measurement's tracking state has secondary code lock. */
+ public static final int STATE_2ND_CODE_LOCK = (1 << 16);
+
+ /**
+ * 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
+
+ /**
+ * GNSS measurement accumulated delta range state
+ * @hide
+ */
+ @IntDef(flag = true, prefix = { "ADR_STATE_" }, value = {
+ ADR_STATE_VALID, ADR_STATE_RESET, ADR_STATE_CYCLE_SLIP, ADR_STATE_HALF_CYCLE_RESOLVED,
+ ADR_STATE_HALF_CYCLE_REPORTED
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface AdrState {}
+
+ /**
+ * The state of the value {@link #getAccumulatedDeltaRangeMeters()} is invalid or unknown.
+ */
+ public static final int ADR_STATE_UNKNOWN = 0;
+
+ /**
+ * The state of the {@link #getAccumulatedDeltaRangeMeters()} is valid.
+ */
+ public static final int ADR_STATE_VALID = (1<<0);
+
+ /**
+ * The state of the {@link #getAccumulatedDeltaRangeMeters()} has detected a reset.
+ */
+ public static final int ADR_STATE_RESET = (1<<1);
+
+ /**
+ * The state of the {@link #getAccumulatedDeltaRangeMeters()} has a cycle slip detected.
+ */
+ public static final int ADR_STATE_CYCLE_SLIP = (1<<2);
+
+ /**
+ * Reports whether the value {@link #getAccumulatedDeltaRangeMeters()} has resolved the half
+ * cycle ambiguity.
+ *
+ * <p> When this bit is set, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
+ * carrier phase measurement plus an accumulated integer number of carrier full cycles.
+ *
+ * <p> When this bit is unset, the {@link #getAccumulatedDeltaRangeMeters()} corresponds to the
+ * carrier phase measurement plus an accumulated integer number of carrier half cycles.
+ */
+ public static final int ADR_STATE_HALF_CYCLE_RESOLVED = (1<<3);
+
+ /**
+ * Reports whether the flag {@link #ADR_STATE_HALF_CYCLE_RESOLVED} has been reported by the
+ * GNSS hardware.
+ *
+ * <p> When this bit is set, the value of {@link #getAccumulatedDeltaRangeUncertaintyMeters()}
+ * can be low (centimeter level) whether or not the half cycle ambiguity is resolved.
+ *
+ * <p> When this bit is unset, the value of {@link #getAccumulatedDeltaRangeUncertaintyMeters()}
+ * is larger, to cover the potential error due to half cycle ambiguity being unresolved.
+ */
+ public static final int ADR_STATE_HALF_CYCLE_REPORTED = (1<<4);
+
+ /**
+ * All the 'Accumulated Delta Range' flags.
+ * @hide
+ */
+ @TestApi
+ public static final int ADR_STATE_ALL =
+ ADR_STATE_VALID | ADR_STATE_RESET | ADR_STATE_CYCLE_SLIP |
+ ADR_STATE_HALF_CYCLE_RESOLVED | ADR_STATE_HALF_CYCLE_REPORTED;
+
+ // 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;
+ mBasebandCn0DbHz = measurement.mBasebandCn0DbHz;
+ 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;
+ mCodeType = measurement.mCodeType;
+ mFullInterSignalBiasNanos = measurement.mFullInterSignalBiasNanos;
+ mFullInterSignalBiasUncertaintyNanos =
+ measurement.mFullInterSignalBiasUncertaintyNanos;
+ mSatelliteInterSignalBiasNanos = measurement.mSatelliteInterSignalBiasNanos;
+ mSatelliteInterSignalBiasUncertaintyNanos =
+ measurement.mSatelliteInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * 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()}.
+ */
+ @State
+ public int getState() {
+ return mState;
+ }
+
+ /**
+ * Sets the sync state.
+ * @hide
+ */
+ @TestApi
+ public void setState(@State 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|");
+ }
+ if ((mState & STATE_2ND_CODE_LOCK) != 0) {
+ builder.append("2ndCodeLock|");
+ }
+
+ 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>The received satellite time is relative to the beginning of the system week for all
+ * constellations except for Glonass where it is relative to the beginning of the Glonass
+ * system day.
+ *
+ * <p>The table below indicates the valid range of the received GNSS satellite time. These
+ * ranges depend on the constellation and code being tracked and the state of the tracking
+ * algorithms given by the {@link #getState} method. The minimum value of this field is zero.
+ * The maximum value of this field is determined by looking across all of the state flags
+ * that are set, for the given constellation and code type, and finding the the maximum value
+ * in this table.
+ *
+ * <p>For example, for GPS L1 C/A, if STATE_TOW_KNOWN is set, this field can be any value from 0
+ * to 1 week (in nanoseconds), and for GAL E1B code, if only STATE_GAL_E1BC_CODE_LOCK is set,
+ * then this field can be any value from 0 to 4 milliseconds (in nanoseconds.)
+ *
+ * <table border="1">
+ * <thead>
+ * <tr>
+ * <td />
+ * <td colspan="3"><strong>GPS/QZSS</strong></td>
+ * <td><strong>GLNS</strong></td>
+ * <td colspan="2"><strong>BDS</strong></td>
+ * <td colspan="3"><strong>GAL</strong></td>
+ * <td><strong>SBAS</strong></td>
+ * </tr>
+ * <tr>
+ * <td><strong>State Flag</strong></td>
+ * <td><strong>L1 C/A</strong></td>
+ * <td><strong>L5I</strong></td>
+ * <td><strong>L5Q</strong></td>
+ * <td><strong>L1OF</strong></td>
+ * <td><strong>B1I (D1)</strong></td>
+ * <td><strong>B1I (D2)</strong></td>
+ * <td><strong>E1B</strong></td>
+ * <td><strong>E1C</strong></td>
+ * <td><strong>E5AQ</strong></td>
+ * <td><strong>L1 C/A</strong></td>
+ * </tr>
+ * </thead>
+ * <tbody>
+ * <tr>
+ * <td>
+ * <strong>STATE_UNKNOWN</strong>
+ * </td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * <td>0</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_CODE_LOCK</strong>
+ * </td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>1 ms</td>
+ * <td>1 ms</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_SYMBOL_SYNC</strong>
+ * </td>
+ * <td>20 ms (optional)</td>
+ * <td>10 ms</td>
+ * <td>1 ms (optional)</td>
+ * <td>10 ms</td>
+ * <td>20 ms (optional)</td>
+ * <td>2 ms</td>
+ * <td>4 ms (optional)</td>
+ * <td>4 ms (optional)</td>
+ * <td>1 ms (optional)</td>
+ * <td>2 ms</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_BIT_SYNC</strong>
+ * </td>
+ * <td>20 ms</td>
+ * <td>20 ms</td>
+ * <td>1 ms (optional)</td>
+ * <td>20 ms</td>
+ * <td>20 ms</td>
+ * <td>-</td>
+ * <td>8 ms</td>
+ * <td>-</td>
+ * <td>1 ms (optional)</td>
+ * <td>4 ms</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_SUBFRAME_SYNC</strong>
+ * </td>
+ * <td>6s</td>
+ * <td>6s</td>
+ * <td>-</td>
+ * <td>2 s</td>
+ * <td>6 s</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>100 ms</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_TOW_DECODED</strong>
+ * </td>
+ * <td colspan="2">1 week</td>
+ * <td>-</td>
+ * <td>1 day</td>
+ * <td colspan="2">1 week</td>
+ * <td colspan="2">1 week</td>
+ * <td>-</td>
+ * <td>1 week</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_TOW_KNOWN</strong>
+ * </td>
+ * <td colspan="3">1 week</td>
+ * <td>1 day</td>
+ * <td colspan="2">1 week</td>
+ * <td colspan="3">1 week</td>
+ * <td>1 week</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GLO_STRING_SYNC</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>2 s</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GLO_TOD_DECODED</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>1 day</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GLO_TOD_KNOWN</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>1 day</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_BDS_D2_BIT_SYNC</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>2 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_BDS_D2_SUBFRAME_SYNC</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>600 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GAL_E1BC_CODE_LOCK</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>4 ms</td>
+ * <td>4 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GAL_E1C_2ND_CODE_LOCK</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>100 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_2ND_CODE_LOCK</strong>
+ * </td>
+ * <td>-</td>
+ * <td>10 ms (optional)</td>
+ * <td>20 ms</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>100 ms (optional)</td>
+ * <td>100 ms</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_GAL_E1B_PAGE_SYNC</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>2 s</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * </tr>
+ * <tr>
+ * <td>
+ * <strong>STATE_SBAS_SYNC</strong>
+ * </td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>-</td>
+ * <td>1 s</td>
+ * </tr>
+ * </tbody>
+ * </table>
+ *
+ * <p>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, STATE_MSEC_AMBIGUOUS must be
+ * set accordingly, in the 'state' field. This value must be populated, unless the 'state' ==
+ * STATE_UNKNOWN.
+ *
+ * <p>Note on optional flags:
+ * <ul>
+ * <li> For L1 C/A and B1I, STATE_SYMBOL_SYNC is optional since the symbol length is the
+ * same as the bit length.
+ * <li> For L5Q and E5aQ, STATE_BIT_SYNC and STATE_SYMBOL_SYNC are optional since they are
+ * implied by STATE_CODE_LOCK.
+ * <li> STATE_2ND_CODE_LOCK for L5I is optional since it is implied by STATE_SYMBOL_SYNC.
+ * <li> STATE_2ND_CODE_LOCK for E1C is optional since it is implied by
+ * STATE_GAL_E1C_2ND_CODE_LOCK.
+ * <li> For E1B and E1C, STATE_SYMBOL_SYNC is optional, because it is implied by
+ * STATE_GAL_E1BC_CODE_LOCK.
+ * </ul>
+ */
+ 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. The range of possible C/N0 values is 0-63 dB-Hz to handle
+ * some edge cases.
+ *
+ * <p>The value contains the measured C/N0 for the signal at the antenna input.
+ */
+ @FloatRange(from = 0, to = 63)
+ public double getCn0DbHz() {
+ return mCn0DbHz;
+ }
+
+ /**
+ * Sets the carrier-to-noise density in dB-Hz.
+ * @hide
+ */
+ @TestApi
+ public void setCn0DbHz(double value) {
+ mCn0DbHz = value;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getBasebandCn0DbHz()} is available, {@code false} otherwise.
+ */
+ public boolean hasBasebandCn0DbHz() {
+ return isFlagSet(HAS_BASEBAND_CN0);
+ }
+
+ /**
+ * Gets the baseband carrier-to-noise density in dB-Hz.
+ *
+ * <p>Typical range: 10-50 dB-Hz. The range of possible baseband C/N0 values is 0-63 dB-Hz to
+ * handle some edge cases.
+ *
+ * <p>The value contains the measured C/N0 for the signal at the baseband. This is typically
+ * a few dB weaker than the value estimated for C/N0 at the antenna port, which is reported
+ * in {@link #getCn0DbHz()}.
+ */
+ @FloatRange(from = 0, to = 63)
+ public double getBasebandCn0DbHz() {
+ return mBasebandCn0DbHz;
+ }
+
+ /**
+ * Sets the baseband carrier-to-noise density in dB-Hz.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setBasebandCn0DbHz(double value) {
+ setFlag(HAS_BASEBAND_CN0);
+ mBasebandCn0DbHz = value;
+ }
+
+ /**
+ * Resets the baseband carrier-to-noise density in dB-Hz.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetBasebandCn0DbHz() {
+ resetFlag(HAS_BASEBAND_CN0);
+ }
+
+ /**
+ * 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').
+ */
+ @AdrState
+ public int getAccumulatedDeltaRangeState() {
+ return mAccumulatedDeltaRangeState;
+ }
+
+ /**
+ * Sets the 'Accumulated Delta Range' state.
+ * @hide
+ */
+ @TestApi
+ public void setAccumulatedDeltaRangeState(@AdrState 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|");
+ }
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_HALF_CYCLE_RESOLVED) ==
+ ADR_STATE_HALF_CYCLE_RESOLVED) {
+ builder.append("HalfCycleResolved|");
+ }
+ if ((mAccumulatedDeltaRangeState & ADR_STATE_HALF_CYCLE_REPORTED)
+ == ADR_STATE_HALF_CYCLE_REPORTED) {
+ builder.append("HalfCycleReported|");
+ }
+ int remainingStates = mAccumulatedDeltaRangeState & ~ADR_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 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>
+ *
+ * <p>Similar to the concept of an RTCM "Phaserange", when the accumulated delta range is
+ * initially chosen, and whenever it is reset, it will retain the integer nature
+ * of the relative carrier phase offset between satellites observed by this receiver, such that
+ * the double difference of this value between receivers and satellites may be used, together
+ * with integer ambiguity resolution, to determine highly precise relative location between
+ * receivers.
+ *
+ * <p>This includes ensuring that all half-cycle ambiguities are resolved before this value is
+ * reported as {@link #ADR_STATE_VALID}.
+ *
+ * <p>The alignment of the phase measurement will not be adjusted by the receiver so the
+ * in-phase and quadrature phase components will have a quarter cycle offset as they do when
+ * transmitted from the satellites. If the measurement is from a combination of the in-phase
+ * and quadrature phase components, then the alignment of the phase measurement will be aligned
+ * to the in-phase component.
+ */
+ 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.
+ *
+ * <p> 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.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
+ */
+ @Deprecated
+ 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}.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeMeters()} instead.
+ */
+ @Deprecated
+ public long getCarrierCycles() {
+ return mCarrierCycles;
+ }
+
+ /**
+ * Sets the number of full carrier cycles between the satellite and the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
+ * @hide
+ */
+ @TestApi
+ @Deprecated
+ public void setCarrierCycles(long value) {
+ setFlag(HAS_CARRIER_CYCLES);
+ mCarrierCycles = value;
+ }
+
+ /**
+ * Resets the number of full carrier cycles between the satellite and the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ * @hide
+ */
+ @TestApi
+ @Deprecated
+ public void resetCarrierCycles() {
+ resetFlag(HAS_CARRIER_CYCLES);
+ mCarrierCycles = Long.MIN_VALUE;
+ }
+
+ /**
+ * Returns {@code true} if {@link #getCarrierPhase()} is available, {@code false} otherwise.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
+ */
+ @Deprecated
+ 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}.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeMeters()} instead.
+ */
+ @Deprecated
+ public double getCarrierPhase() {
+ return mCarrierPhase;
+ }
+
+ /**
+ * Sets the RF phase detected by the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
+ * @hide
+ */
+ @TestApi
+ @Deprecated
+ public void setCarrierPhase(double value) {
+ setFlag(HAS_CARRIER_PHASE);
+ mCarrierPhase = value;
+ }
+
+ /**
+ * Resets the RF phase detected by the receiver.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
+ * @hide
+ */
+ @TestApi
+ @Deprecated
+ public void resetCarrierPhase() {
+ resetFlag(HAS_CARRIER_PHASE);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getCarrierPhaseUncertainty()} is available, {@code false}
+ * otherwise.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeState()} instead.
+ */
+ @Deprecated
+ 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}.
+ *
+ * @deprecated use {@link #getAccumulatedDeltaRangeUncertaintyMeters()} instead.
+ */
+ @Deprecated
+ public double getCarrierPhaseUncertainty() {
+ return mCarrierPhaseUncertainty;
+ }
+
+ /**
+ * Sets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeUncertaintyMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
+ * @hide
+ */
+ @TestApi
+ @Deprecated
+ public void setCarrierPhaseUncertainty(double value) {
+ setFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
+ mCarrierPhaseUncertainty = value;
+ }
+
+ /**
+ * Resets the Carrier-phase's uncertainty (1-Sigma) in cycles.
+ *
+ * @deprecated use {@link #setAccumulatedDeltaRangeUncertaintyMeters(double)}
+ * and {@link #setAccumulatedDeltaRangeState(int)} instead.
+ *
+ * @hide
+ */
+ @TestApi
+ @Deprecated
+ public void resetCarrierPhaseUncertainty() {
+ resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY);
+ }
+
+ /**
+ * 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 (post-correlation & integration) 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);
+ }
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getCodeType()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasCodeType() {
+ return isFlagSet(HAS_CODE_TYPE);
+ }
+
+ /**
+ * Gets the GNSS measurement's code type.
+ *
+ * <p>Similar to the Attribute field described in RINEX 3.03, e.g., in Tables 4-10, and Table
+ * A2 at the RINEX 3.03 Update 1 Document.
+ *
+ * <p>Returns "A" for GALILEO E1A, GALILEO E6A, IRNSS L5A, IRNSS SA.
+ *
+ * <p>Returns "B" for GALILEO E1B, GALILEO E6B, IRNSS L5B, IRNSS SB.
+ *
+ * <p>Returns "C" for GPS L1 C/A, GPS L2 C/A, GLONASS G1 C/A, GLONASS G2 C/A, GALILEO E1C,
+ * GALILEO E6C, SBAS L1 C/A, QZSS L1 C/A, IRNSS L5C.
+ *
+ * <p>Returns "D" for BDS B1C D.
+ *
+ * <p>Returns "I" for GPS L5 I, GLONASS G3 I, GALILEO E5a I, GALILEO E5b I, GALILEO E5a+b I,
+ * SBAS L5 I, QZSS L5 I, BDS B1 I, BDS B2 I, BDS B3 I.
+ *
+ * <p>Returns "L" for GPS L1C (P), GPS L2C (L), QZSS L1C (P), QZSS L2C (L), LEX(6) L.
+ *
+ * <p>Returns "M" for GPS L1M, GPS L2M.
+ *
+ * <p>Returns "N" for GPS L1 codeless, GPS L2 codeless.
+ *
+ * <p>Returns "P" for GPS L1P, GPS L2P, GLONASS G1P, GLONASS G2P, BDS B1C P.
+ *
+ * <p>Returns "Q" for GPS L5 Q, GLONASS G3 Q, GALILEO E5a Q, GALILEO E5b Q, GALILEO E5a+b Q,
+ * SBAS L5 Q, QZSS L5 Q, BDS B1 Q, BDS B2 Q, BDS B3 Q.
+ *
+ * <p>Returns "S" for GPS L1C (D), GPS L2C (M), QZSS L1C (D), QZSS L2C (M), LEX(6) S.
+ *
+ * <p>Returns "W" for GPS L1 Z-tracking, GPS L2 Z-tracking.
+ *
+ * <p>Returns "X" for GPS L1C (D+P), GPS L2C (M+L), GPS L5 (I+Q), GLONASS G3 (I+Q), GALILEO
+ * E1 (B+C), GALILEO E5a (I+Q), GALILEO E5b (I+Q), GALILEO E5a+b(I+Q), GALILEO E6 (B+C), SBAS
+ * L5 (I+Q), QZSS L1C (D+P), QZSS L2C (M+L), QZSS L5 (I+Q), LEX(6) (S+L), BDS B1 (I+Q), BDS
+ * B1C (D+P), BDS B2 (I+Q), BDS B3 (I+Q), IRNSS L5 (B+C).
+ *
+ * <p>Returns "Y" for GPS L1Y, GPS L2Y.
+ *
+ * <p>Returns "Z" for GALILEO E1 (A+B+C), GALILEO E6 (A+B+C), QZSS L1-SAIF.
+ *
+ * <p>Returns "UNKNOWN" if the GNSS Measurement's code type is unknown.
+ *
+ * <p>This is used to specify the observation descriptor defined in GNSS Observation Data File
+ * Header Section Description in the RINEX standard (Version 3.XX), in cases where the code type
+ * does not align with the above listed values. For example, if a code type "G" is added, this
+ * string shall be set to "G".
+ */
+ @NonNull
+ public String getCodeType() {
+ return mCodeType;
+ }
+
+ /**
+ * Sets the GNSS measurement's code type.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setCodeType(@NonNull String codeType) {
+ setFlag(HAS_CODE_TYPE);
+ mCodeType = codeType;
+ }
+
+ /**
+ * Resets the GNSS measurement's code type.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetCodeType() {
+ resetFlag(HAS_CODE_TYPE);
+ mCodeType = "UNKNOWN";
+ }
+
+ /**
+ * Returns {@code true} if {@link #getFullInterSignalBiasNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasFullInterSignalBiasNanos() {
+ return isFlagSet(HAS_FULL_ISB);
+ }
+
+ /**
+ * Gets the GNSS measurement's inter-signal bias in nanoseconds with sub-nanosecond accuracy.
+ *
+ * <p>This value is the sum of the estimated receiver-side and the space-segment-side
+ * inter-system bias, inter-frequency bias and inter-code bias, including:
+ *
+ * <ul>
+ * <li>Receiver inter-constellation bias (with respect to the constellation in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Receiver inter-frequency bias (with respect to the carrier frequency in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Receiver inter-code bias (with respect to the code type in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPS-UTC Time Offset (TauGps),
+ * BDS-GLO Time Offset (BGTO))(with respect to the constellation in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Group delay (e.g., Total Group Delay (TGD))</li>
+ * <li>Satellite inter-frequency bias (GLO only) (with respect to the carrier frequency in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Satellite inter-code bias (e.g., Differential Code Bias (DCB)) (with respect to the code
+ * type in {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * </ul>
+ *
+ * <p>If a component of the above is already compensated in the provided
+ * {@link GnssMeasurement#getReceivedSvTimeNanos()}, then it must not be included in the
+ * reported full ISB.
+ *
+ * <p>The value does not include the inter-frequency Ionospheric bias.
+ *
+ * <p>The value is only available if {@link #hasFullInterSignalBiasNanos()} is {@code true}.
+ */
+ public double getFullInterSignalBiasNanos() {
+ return mFullInterSignalBiasNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setFullInterSignalBiasNanos(double fullInterSignalBiasNanos) {
+ setFlag(HAS_FULL_ISB);
+ mFullInterSignalBiasNanos = fullInterSignalBiasNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetFullInterSignalBiasNanos() {
+ resetFlag(HAS_FULL_ISB);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getFullInterSignalBiasUncertaintyNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasFullInterSignalBiasUncertaintyNanos() {
+ return isFlagSet(HAS_FULL_ISB_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the GNSS measurement's inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds with sub-nanosecond accuracy.
+ *
+ * <p>The value is only available if {@link #hasFullInterSignalBiasUncertaintyNanos()} is
+ * {@code true}.
+ */
+ @FloatRange(from = 0.0)
+ public double getFullInterSignalBiasUncertaintyNanos() {
+ return mFullInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's inter-signal bias uncertainty (1 sigma) in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setFullInterSignalBiasUncertaintyNanos(@FloatRange(from = 0.0)
+ double fullInterSignalBiasUncertaintyNanos) {
+ setFlag(HAS_FULL_ISB_UNCERTAINTY);
+ mFullInterSignalBiasUncertaintyNanos = fullInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetFullInterSignalBiasUncertaintyNanos() {
+ resetFlag(HAS_FULL_ISB_UNCERTAINTY);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getSatelliteInterSignalBiasNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasSatelliteInterSignalBiasNanos() {
+ return isFlagSet(HAS_SATELLITE_ISB);
+ }
+
+ /**
+ * Gets the GNSS measurement's satellite inter-signal bias in nanoseconds with sub-nanosecond
+ * accuracy.
+ *
+ * <p>This value is the space-segment-side inter-system bias, inter-frequency bias and
+ * inter-code bias, including:
+ *
+ * <ul>
+ * <li>Master clock bias (e.g., GPS-GAL Time Offset (GGTO), GPS-UTC Time Offset (TauGps),
+ * BDS-GLO Time Offset (BGTO))(with respect to the constellation in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Group delay (e.g., Total Group Delay (TGD))</li>
+ * <li>Satellite inter-frequency bias (GLO only) (with respect to the carrier frequency in
+ * {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * <li>Satellite inter-code bias (e.g., Differential Code Bias (DCB)) (with respect to the code
+ * type in {@link GnssClock#getReferenceConstellationTypeForIsb())</li>
+ * </ul>
+ *
+ * <p>The value is only available if {@link #hasSatelliteInterSignalBiasNanos()} is {@code
+ * true}.
+ */
+ public double getSatelliteInterSignalBiasNanos() {
+ return mSatelliteInterSignalBiasNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's satellite inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setSatelliteInterSignalBiasNanos(double satelliteInterSignalBiasNanos) {
+ setFlag(HAS_SATELLITE_ISB);
+ mSatelliteInterSignalBiasNanos = satelliteInterSignalBiasNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's satellite inter-signal bias in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetSatelliteInterSignalBiasNanos() {
+ resetFlag(HAS_SATELLITE_ISB);
+ }
+
+ /**
+ * Returns {@code true} if {@link #getSatelliteInterSignalBiasUncertaintyNanos()} is available,
+ * {@code false} otherwise.
+ */
+ public boolean hasSatelliteInterSignalBiasUncertaintyNanos() {
+ return isFlagSet(HAS_SATELLITE_ISB_UNCERTAINTY);
+ }
+
+ /**
+ * Gets the GNSS measurement's satellite inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds with sub-nanosecond accuracy.
+ *
+ * <p>The value is only available if {@link #hasSatelliteInterSignalBiasUncertaintyNanos()} is
+ * {@code true}.
+ */
+ @FloatRange(from = 0.0)
+ public double getSatelliteInterSignalBiasUncertaintyNanos() {
+ return mSatelliteInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Sets the GNSS measurement's satellite inter-signal bias uncertainty (1 sigma) in nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void setSatelliteInterSignalBiasUncertaintyNanos(@FloatRange(from = 0.0)
+ double satelliteInterSignalBiasUncertaintyNanos) {
+ setFlag(HAS_SATELLITE_ISB_UNCERTAINTY);
+ mSatelliteInterSignalBiasUncertaintyNanos = satelliteInterSignalBiasUncertaintyNanos;
+ }
+
+ /**
+ * Resets the GNSS measurement's satellite inter-signal bias uncertainty (1 sigma) in
+ * nanoseconds.
+ *
+ * @hide
+ */
+ @TestApi
+ public void resetSatelliteInterSignalBiasUncertaintyNanos() {
+ resetFlag(HAS_SATELLITE_ISB_UNCERTAINTY);
+ }
+
+
+ public static final @NonNull 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();
+ gnssMeasurement.mCodeType = parcel.readString();
+ gnssMeasurement.mBasebandCn0DbHz = parcel.readDouble();
+ gnssMeasurement.mFullInterSignalBiasNanos = parcel.readDouble();
+ gnssMeasurement.mFullInterSignalBiasUncertaintyNanos = parcel.readDouble();
+ gnssMeasurement.mSatelliteInterSignalBiasNanos = parcel.readDouble();
+ gnssMeasurement.mSatelliteInterSignalBiasUncertaintyNanos = 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);
+ parcel.writeString(mCodeType);
+ parcel.writeDouble(mBasebandCn0DbHz);
+ parcel.writeDouble(mFullInterSignalBiasNanos);
+ parcel.writeDouble(mFullInterSignalBiasUncertaintyNanos);
+ parcel.writeDouble(mSatelliteInterSignalBiasNanos);
+ parcel.writeDouble(mSatelliteInterSignalBiasUncertaintyNanos);
+ }
+
+ @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));
+
+ if (hasBasebandCn0DbHz()) {
+ builder.append(String.format(format, "BasebandCn0DbHz", mBasebandCn0DbHz));
+ }
+
+ 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));
+
+ if (hasCarrierFrequencyHz()) {
+ builder.append(String.format(format, "CarrierFrequencyHz", mCarrierFrequencyHz));
+ }
+
+ if (hasCarrierCycles()) {
+ builder.append(String.format(format, "CarrierCycles", mCarrierCycles));
+ }
+
+ if (hasCarrierPhase() || hasCarrierPhaseUncertainty()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "CarrierPhase",
+ hasCarrierPhase() ? mCarrierPhase : null,
+ "CarrierPhaseUncertainty",
+ hasCarrierPhaseUncertainty() ? mCarrierPhaseUncertainty : null));
+ }
+
+ builder.append(String.format(format, "MultipathIndicator", getMultipathIndicatorString()));
+
+ if (hasSnrInDb()) {
+ builder.append(String.format(format, "SnrInDb", mSnrInDb));
+ }
+
+ if (hasAutomaticGainControlLevelDb()) {
+ builder.append(String.format(format, "AgcLevelDb", mAutomaticGainControlLevelInDb));
+ }
+
+ if (hasCodeType()) {
+ builder.append(String.format(format, "CodeType", mCodeType));
+ }
+
+ if (hasFullInterSignalBiasNanos() || hasFullInterSignalBiasUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "InterSignalBiasNs",
+ hasFullInterSignalBiasNanos() ? mFullInterSignalBiasNanos : null,
+ "InterSignalBiasUncertaintyNs",
+ hasFullInterSignalBiasUncertaintyNanos()
+ ? mFullInterSignalBiasUncertaintyNanos : null));
+ }
+
+ if (hasSatelliteInterSignalBiasNanos() || hasSatelliteInterSignalBiasUncertaintyNanos()) {
+ builder.append(String.format(
+ formatWithUncertainty,
+ "SatelliteInterSignalBiasNs",
+ hasSatelliteInterSignalBiasNanos() ? mSatelliteInterSignalBiasNanos : null,
+ "SatelliteInterSignalBiasUncertaintyNs",
+ hasSatelliteInterSignalBiasUncertaintyNanos()
+ ? mSatelliteInterSignalBiasUncertaintyNanos
+ : 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();
+ resetCodeType();
+ resetBasebandCn0DbHz();
+ resetFullInterSignalBiasNanos();
+ resetFullInterSignalBiasUncertaintyNanos();
+ resetSatelliteInterSignalBiasNanos();
+ resetSatelliteInterSignalBiasUncertaintyNanos();
+ }
+
+ 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/GnssMeasurementCorrections.java b/android/location/GnssMeasurementCorrections.java
new file mode 100644
index 0000000..ffbe11a
--- /dev/null
+++ b/android/location/GnssMeasurementCorrections.java
@@ -0,0 +1,406 @@
+/*
+ * Copyright (C) 2018 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.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * A class representing a GNSS measurement corrections for all used GNSS satellites at the location
+ * and time specified
+ *
+ * @hide
+ */
+@SystemApi
+public final class GnssMeasurementCorrections implements Parcelable {
+
+ /** Represents latitude in degrees at which the corrections are computed. */
+ @FloatRange(from = -90.0f, to = 90.0f)
+ private final double mLatitudeDegrees;
+ /** Represents longitude in degrees at which the corrections are computed. */
+ @FloatRange(from = -180.0f, to = 180.0f)
+ private final double mLongitudeDegrees;
+ /**
+ * Represents altitude in meters above the WGS 84 reference ellipsoid at which the corrections
+ * are computed.
+ */
+ @FloatRange(from = -1000.0, to = 10000.0f)
+ private final double mAltitudeMeters;
+ /**
+ * Represents the horizontal uncertainty (68% confidence) in meters on the device position at
+ * which the corrections are provided.
+ *
+ * <p> This value is useful for example to judge how accurate the provided corrections are.
+ */
+ @FloatRange(from = 0.0f)
+ private final double mHorizontalPositionUncertaintyMeters;
+ /**
+ * Represents the vertical uncertainty (68% confidence) in meters on the device position at
+ * which the corrections are provided.
+ *
+ * <p> This value is useful for example to judge how accurate the provided corrections are.
+ */
+ @FloatRange(from = 0.0f)
+ private final double mVerticalPositionUncertaintyMeters;
+
+ /** Time Of Applicability, GPS time of week in nanoseconds. */
+ @IntRange(from = 0)
+ private final long mToaGpsNanosecondsOfWeek;
+
+ /**
+ * A set of {@link GnssSingleSatCorrection} each containing measurement corrections for a
+ * satellite in view.
+ */
+ @NonNull
+ private final List<GnssSingleSatCorrection> mSingleSatCorrectionList;
+
+ /**
+ * Indicates whether the environment bearing is available.
+ */
+ private final boolean mHasEnvironmentBearing;
+
+ /**
+ * Environment bearing in degrees clockwise from true north, in the direction of user motion.
+ * Environment bearing is provided when it is known with high probability that velocity is
+ * aligned with an environment feature (such as edge of a building, or road).
+ */
+ @FloatRange(from = 0.0f, to = 360.0f)
+ private final float mEnvironmentBearingDegrees;
+
+ /**
+ * Environment bearing uncertainty in degrees.
+ */
+ @FloatRange(from = 0.0f, to = 180.0f)
+ private final float mEnvironmentBearingUncertaintyDegrees;
+
+ private GnssMeasurementCorrections(Builder builder) {
+ mLatitudeDegrees = builder.mLatitudeDegrees;
+ mLongitudeDegrees = builder.mLongitudeDegrees;
+ mAltitudeMeters = builder.mAltitudeMeters;
+ mHorizontalPositionUncertaintyMeters = builder.mHorizontalPositionUncertaintyMeters;
+ mVerticalPositionUncertaintyMeters = builder.mVerticalPositionUncertaintyMeters;
+ mToaGpsNanosecondsOfWeek = builder.mToaGpsNanosecondsOfWeek;
+ final List<GnssSingleSatCorrection> singleSatCorrList = builder.mSingleSatCorrectionList;
+ Preconditions.checkArgument(singleSatCorrList != null && !singleSatCorrList.isEmpty());
+ mSingleSatCorrectionList = Collections.unmodifiableList(new ArrayList<>(singleSatCorrList));
+ mHasEnvironmentBearing = builder.mEnvironmentBearingIsSet
+ && builder.mEnvironmentBearingUncertaintyIsSet;
+ mEnvironmentBearingDegrees = builder.mEnvironmentBearingDegrees;
+ mEnvironmentBearingUncertaintyDegrees = builder.mEnvironmentBearingUncertaintyDegrees;
+ }
+
+ /** Gets the latitude in degrees at which the corrections are computed. */
+ @FloatRange(from = -90.0f, to = 90.0f)
+ public double getLatitudeDegrees() {
+ return mLatitudeDegrees;
+ }
+
+ /** Gets the longitude in degrees at which the corrections are computed. */
+ @FloatRange(from = -180.0f, to = 180.0f)
+ public double getLongitudeDegrees() {
+ return mLongitudeDegrees;
+ }
+
+ /**
+ * Gets the altitude in meters above the WGS 84 reference ellipsoid at which the corrections are
+ * computed.
+ */
+ @FloatRange(from = -1000.0f, to = 10000.0f)
+ public double getAltitudeMeters() {
+ return mAltitudeMeters;
+ }
+
+ /**
+ * Gets the horizontal uncertainty (68% confidence) in meters on the device position at
+ * which the corrections are provided.
+ */
+ @FloatRange(from = 0.0f)
+ public double getHorizontalPositionUncertaintyMeters() {
+ return mHorizontalPositionUncertaintyMeters;
+ }
+
+ /**
+ * Gets the vertical uncertainty (68% confidence) in meters on the device position at
+ * which the corrections are provided.
+ */
+ @FloatRange(from = 0.0f)
+ public double getVerticalPositionUncertaintyMeters() {
+ return mVerticalPositionUncertaintyMeters;
+ }
+
+ /** Gets the time of applicability, GPS time of week in nanoseconds. */
+ @IntRange(from = 0)
+ public long getToaGpsNanosecondsOfWeek() {
+ return mToaGpsNanosecondsOfWeek;
+ }
+
+ /**
+ * Gets a set of {@link GnssSingleSatCorrection} each containing measurement corrections for a
+ * satellite in view
+ */
+ @NonNull
+ public List<GnssSingleSatCorrection> getSingleSatelliteCorrectionList() {
+ return mSingleSatCorrectionList;
+ }
+
+ /**
+ * If true, environment bearing will be available.
+ */
+ public boolean hasEnvironmentBearing() {
+ return mHasEnvironmentBearing;
+ }
+
+ /**
+ * Gets the environment bearing in degrees clockwise from true north, in the direction of user
+ * motion. Environment bearing is provided when it is known with high probability that
+ * velocity is aligned with an environment feature (such as edge of a building, or road).
+ *
+ * {@link #hasEnvironmentBearing} should be called to check the environment bearing is available
+ * before calling this method. The value is undefined if {@link #hasEnvironmentBearing} returns
+ * false.
+ */
+ @FloatRange(from = 0.0f, to = 360.0f)
+ public float getEnvironmentBearingDegrees() {
+ return mEnvironmentBearingDegrees;
+ }
+
+ /**
+ * Gets the environment bearing uncertainty in degrees. It represents the standard deviation of
+ * the physical structure in the circle of position uncertainty. The uncertainty can take values
+ * between 0 and 180 degrees. The {@link #hasEnvironmentBearing} becomes false as the
+ * uncertainty value passes a predefined threshold depending on the physical structure around
+ * the user.
+ *
+ * {@link #hasEnvironmentBearing} should be called to check the environment bearing is available
+ * before calling this method. The value is undefined if {@link #hasEnvironmentBearing} returns
+ * false.
+ */
+ @FloatRange(from = 0.0f, to = 180.0f)
+ public float getEnvironmentBearingUncertaintyDegrees() {
+ return mEnvironmentBearingUncertaintyDegrees;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<GnssMeasurementCorrections> CREATOR =
+ new Creator<GnssMeasurementCorrections>() {
+ @Override
+ @NonNull
+ public GnssMeasurementCorrections createFromParcel(@NonNull Parcel parcel) {
+ final GnssMeasurementCorrections.Builder gnssMeasurementCorrectons =
+ new Builder()
+ .setLatitudeDegrees(parcel.readDouble())
+ .setLongitudeDegrees(parcel.readDouble())
+ .setAltitudeMeters(parcel.readDouble())
+ .setHorizontalPositionUncertaintyMeters(parcel.readDouble())
+ .setVerticalPositionUncertaintyMeters(parcel.readDouble())
+ .setToaGpsNanosecondsOfWeek(parcel.readLong());
+ List<GnssSingleSatCorrection> singleSatCorrectionList = new ArrayList<>();
+ parcel.readTypedList(singleSatCorrectionList, GnssSingleSatCorrection.CREATOR);
+ gnssMeasurementCorrectons.setSingleSatelliteCorrectionList(
+ singleSatCorrectionList);
+ boolean hasEnvironmentBearing = parcel.readBoolean();
+ if (hasEnvironmentBearing) {
+ gnssMeasurementCorrectons.setEnvironmentBearingDegrees(parcel.readFloat());
+ gnssMeasurementCorrectons.setEnvironmentBearingUncertaintyDegrees(
+ parcel.readFloat());
+ }
+ return gnssMeasurementCorrectons.build();
+ }
+
+ @Override
+ public GnssMeasurementCorrections[] newArray(int i) {
+ return new GnssMeasurementCorrections[i];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ final String format = " %-29s = %s\n";
+ StringBuilder builder = new StringBuilder("GnssMeasurementCorrections:\n");
+ builder.append(String.format(format, "LatitudeDegrees = ", mLatitudeDegrees));
+ builder.append(String.format(format, "LongitudeDegrees = ", mLongitudeDegrees));
+ builder.append(String.format(format, "AltitudeMeters = ", mAltitudeMeters));
+ builder.append(String.format(format, "HorizontalPositionUncertaintyMeters = ",
+ mHorizontalPositionUncertaintyMeters));
+ builder.append(String.format(format, "VerticalPositionUncertaintyMeters = ",
+ mVerticalPositionUncertaintyMeters));
+ builder.append(
+ String.format(format, "ToaGpsNanosecondsOfWeek = ", mToaGpsNanosecondsOfWeek));
+ builder.append(
+ String.format(format, "mSingleSatCorrectionList = ", mSingleSatCorrectionList));
+ builder.append(
+ String.format(format, "HasEnvironmentBearing = ", mHasEnvironmentBearing));
+ builder.append(
+ String.format(format, "EnvironmentBearingDegrees = ",
+ mEnvironmentBearingDegrees));
+ builder.append(
+ String.format(format, "EnvironmentBearingUncertaintyDegrees = ",
+ mEnvironmentBearingUncertaintyDegrees));
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mLatitudeDegrees);
+ parcel.writeDouble(mLongitudeDegrees);
+ parcel.writeDouble(mAltitudeMeters);
+ parcel.writeDouble(mHorizontalPositionUncertaintyMeters);
+ parcel.writeDouble(mVerticalPositionUncertaintyMeters);
+ parcel.writeLong(mToaGpsNanosecondsOfWeek);
+ parcel.writeTypedList(mSingleSatCorrectionList);
+ parcel.writeBoolean(mHasEnvironmentBearing);
+ if (mHasEnvironmentBearing) {
+ parcel.writeFloat(mEnvironmentBearingDegrees);
+ parcel.writeFloat(mEnvironmentBearingUncertaintyDegrees);
+ }
+ }
+
+ /** Builder for {@link GnssMeasurementCorrections} */
+ public static final class Builder {
+ /**
+ * For documentation of below fields, see corresponding fields in {@link
+ * GnssMeasurementCorrections}.
+ */
+ private double mLatitudeDegrees;
+ private double mLongitudeDegrees;
+ private double mAltitudeMeters;
+ private double mHorizontalPositionUncertaintyMeters;
+ private double mVerticalPositionUncertaintyMeters;
+ private long mToaGpsNanosecondsOfWeek;
+ @Nullable private List<GnssSingleSatCorrection> mSingleSatCorrectionList;
+ private float mEnvironmentBearingDegrees;
+ private boolean mEnvironmentBearingIsSet = false;
+ private float mEnvironmentBearingUncertaintyDegrees;
+ private boolean mEnvironmentBearingUncertaintyIsSet = false;
+
+ /** Sets the latitude in degrees at which the corrections are computed. */
+ @NonNull public Builder setLatitudeDegrees(
+ @FloatRange(from = -90.0f, to = 90.0f) double latitudeDegrees) {
+ mLatitudeDegrees = latitudeDegrees;
+ return this;
+ }
+
+ /** Sets the longitude in degrees at which the corrections are computed. */
+ @NonNull public Builder setLongitudeDegrees(
+ @FloatRange(from = -180.0f, to = 180.0f) double longitudeDegrees) {
+ mLongitudeDegrees = longitudeDegrees;
+ return this;
+ }
+
+ /**
+ * Sets the altitude in meters above the WGS 84 reference ellipsoid at which the corrections
+ * are computed.
+ */
+ @NonNull public Builder setAltitudeMeters(
+ @FloatRange(from = -1000.0f, to = 10000.0f) double altitudeMeters) {
+ mAltitudeMeters = altitudeMeters;
+ return this;
+ }
+
+
+ /**
+ * Sets the horizontal uncertainty (68% confidence) in meters on the device position at
+ * which the corrections are provided.
+ */
+ @NonNull public Builder setHorizontalPositionUncertaintyMeters(
+ @FloatRange(from = 0.0f) double horizontalPositionUncertaintyMeters) {
+ mHorizontalPositionUncertaintyMeters = horizontalPositionUncertaintyMeters;
+ return this;
+ }
+
+ /**
+ * Sets the vertical uncertainty (68% confidence) in meters on the device position at which
+ * the corrections are provided.
+ */
+ @NonNull public Builder setVerticalPositionUncertaintyMeters(
+ @FloatRange(from = 0.0f) double verticalPositionUncertaintyMeters) {
+ mVerticalPositionUncertaintyMeters = verticalPositionUncertaintyMeters;
+ return this;
+ }
+
+ /** Sets the time of applicability, GPS time of week in nanoseconds. */
+ @NonNull public Builder setToaGpsNanosecondsOfWeek(
+ @IntRange(from = 0) long toaGpsNanosecondsOfWeek) {
+ mToaGpsNanosecondsOfWeek = toaGpsNanosecondsOfWeek;
+ return this;
+ }
+
+ /**
+ * Sets a the list of {@link GnssSingleSatCorrection} containing measurement corrections for
+ * a satellite in view
+ */
+ @NonNull public Builder setSingleSatelliteCorrectionList(
+ @NonNull List<GnssSingleSatCorrection> singleSatCorrectionList) {
+ mSingleSatCorrectionList = singleSatCorrectionList;
+ return this;
+ }
+
+ /**
+ * Sets the environment bearing in degrees clockwise from true north, in the direction of
+ * user motion. Environment bearing is provided when it is known with high probability
+ * that velocity is aligned with an environment feature (such as edge of a building, or
+ * road).
+ *
+ * Both the bearing and uncertainty must be set for the environment bearing to be valid.
+ */
+ @NonNull public Builder setEnvironmentBearingDegrees(
+ @FloatRange(from = 0.0f, to = 360.0f)
+ float environmentBearingDegrees) {
+ mEnvironmentBearingDegrees = environmentBearingDegrees;
+ mEnvironmentBearingIsSet = true;
+ return this;
+ }
+
+ /**
+ * Sets the environment bearing uncertainty in degrees.
+ *
+ * Both the bearing and uncertainty must be set for the environment bearing to be valid.
+ */
+ @NonNull public Builder setEnvironmentBearingUncertaintyDegrees(
+ @FloatRange(from = 0.0f, to = 180.0f)
+ float environmentBearingUncertaintyDegrees) {
+ mEnvironmentBearingUncertaintyDegrees = environmentBearingUncertaintyDegrees;
+ mEnvironmentBearingUncertaintyIsSet = true;
+ return this;
+ }
+
+ /** Builds a {@link GnssMeasurementCorrections} instance as specified by this builder. */
+ @NonNull public GnssMeasurementCorrections build() {
+ if (mEnvironmentBearingIsSet ^ mEnvironmentBearingUncertaintyIsSet) {
+ throw new IllegalStateException("Both environment bearing and environment bearing "
+ + "uncertainty must be set.");
+ }
+ return new GnssMeasurementCorrections(this);
+ }
+ }
+}
diff --git a/android/location/GnssMeasurementsEvent.java b/android/location/GnssMeasurementsEvent.java
new file mode 100644
index 0000000..98ef2d4
--- /dev/null
+++ b/android/location/GnssMeasurementsEvent.java
@@ -0,0 +1,186 @@
+/*
+ * 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, STATUS_NOT_ALLOWED})
+ 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;
+
+ /**
+ * The client is not allowed to register for GNSS Measurements in general or in the
+ * requested mode.
+ *
+ * <p>Such a status is returned when a client tries to request a functionality from the GNSS
+ * chipset while another client has an ongoing request that does not allow such
+ * functionality to be performed.
+ *
+ * <p>If such a status is received, one would try again at a later time point where no
+ * other client is having a conflicting request.
+ */
+ public static final int STATUS_NOT_ALLOWED = 3;
+
+ /**
+ * 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 @android.annotation.NonNull 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 0000000..27d3637
--- /dev/null
+++ b/android/location/GnssNavigationMessage.java
@@ -0,0 +1,483 @@
+/*
+ * 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.IntDef;
+import android.annotation.NonNull;
+import android.annotation.TestApi;
+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_SBS, TYPE_GLO_L1CA, TYPE_QZS_L1CA, TYPE_BDS_D1, TYPE_BDS_D2, TYPE_BDS_CNAV1,
+ TYPE_BDS_CNAV2, TYPE_GAL_I, TYPE_GAL_F, TYPE_IRN_L5CA})
+ 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;
+ /** SBAS message contained in the structure. */
+ public static final int TYPE_SBS = 0x0201;
+ /** Glonass L1 CA message contained in the structure. */
+ public static final int TYPE_GLO_L1CA = 0x0301;
+ /** QZSS L1 C/A message contained in the structure. */
+ public static final int TYPE_QZS_L1CA = 0x0401;
+ /** 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;
+ /** Beidou CNAV1 message contained in the structure. */
+ public static final int TYPE_BDS_CNAV1 = 0x0503;
+ /** Beidou CNAV2 message contained in the structure. */
+ public static final int TYPE_BDS_CNAV2 = 0x0504;
+ /** 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;
+ /** IRNSS L5 C/A message contained in the structure. */
+ public static final int TYPE_IRN_L5CA = 0x0701;
+
+ /**
+ * 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_SBS:
+ return "SBS";
+ case TYPE_GLO_L1CA:
+ return "Glonass L1 C/A";
+ case TYPE_QZS_L1CA:
+ return "QZSS L1 C/A";
+ case TYPE_BDS_D1:
+ return "Beidou D1";
+ case TYPE_BDS_D2:
+ return "Beidou D2";
+ case TYPE_BDS_CNAV1:
+ return "Beidou CNAV1";
+ case TYPE_BDS_CNAV2:
+ return "Beidou CNAV2";
+ case TYPE_GAL_I:
+ return "Galileo I";
+ case TYPE_GAL_F:
+ return "Galileo F";
+ case TYPE_IRN_L5CA:
+ return "IRNSS L5 C/A";
+ 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>
+ * <li> For SBAS and Beidou CNAV2, this is unused and can be set to -1.</li>
+ * <li> For QZSS 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 Beidou CNAV1 this refers to the page type number in the range of 1-63.</li>
+ * <li> For IRNSS L5 C/A subframe 3 and 4, this value corresponds to the Message Id of the
+ * navigation message, in the range of 1-63. (Subframe 1 and 2 does not contain a message type
+ * id and this value can be set to -1.)</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 & 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>
+ * <li> For SBAS, the submessage id corresponds to the message type, in the range 1-63.</li>
+ * <li> For Beidou CNAV1, the submessage id corresponds to the subframe number of the
+ * navigation message, in the range of 1-3.</li>
+ * <li> For Beidou CNAV2, the submessage id corresponds to the message type, in the range
+ * 1-63.</li>
+ * <li> For IRNSS L5 C/A, the submessage id corresponds to the subframe number of the
+ * navigation message, in the range of 1-4.</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 & 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 & 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 & tail excluded) that should be fit into 29 bytes, with MSB first (skip
+ * B229-B232).</li>
+ * <li>For SBAS, each block consists of 250 data bits, that should be fit into 32 bytes. MSB
+ * first (skip B251-B256).</li>
+ * <li>For Beidou CNAV1, subframe #1 consists of 14 data bits, that should be fit into 2
+ * bytes. MSB first (skip B15-B16). subframe #2 consists of 600 bits that should be fit into
+ * 75 bytes. subframe #3 consists of 264 data bits that should be fit into 33 bytes.</li>
+ * <li>For Beidou CNAV2, each subframe consists of 288 data bits, that should be fit into 36
+ * bytes.</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 @android.annotation.NonNull 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/GnssReflectingPlane.java b/android/location/GnssReflectingPlane.java
new file mode 100644
index 0000000..1acdd1e
--- /dev/null
+++ b/android/location/GnssReflectingPlane.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2018 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.FloatRange;
+import android.annotation.NonNull;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Holds the characteristics of the reflecting plane that a satellite signal has bounced from.
+ *
+ * @hide
+ */
+@SystemApi
+public final class GnssReflectingPlane implements Parcelable {
+
+ /** Represents latitude in degrees of the reflecting plane */
+ @FloatRange(from = -90.0f, to = 90.0f)
+ private final double mLatitudeDegrees;
+ /** Represents longitude in degrees of the reflecting plane. */
+ @FloatRange(from = -180.0f, to = 180.0f)
+ private final double mLongitudeDegrees;
+ /**
+ * Represents altitude in meters above the WGS 84 reference ellipsoid of the reflection point in
+ * the plane
+ */
+ @FloatRange(from = -1000.0f, to = 10000.0f)
+ private final double mAltitudeMeters;
+
+ /** Represents azimuth clockwise from north of the reflecting plane in degrees. */
+ @FloatRange(from = 0.0f, to = 360.0f)
+ private final double mAzimuthDegrees;
+
+ private GnssReflectingPlane(Builder builder) {
+ mLatitudeDegrees = builder.mLatitudeDegrees;
+ mLongitudeDegrees = builder.mLongitudeDegrees;
+ mAltitudeMeters = builder.mAltitudeMeters;
+ mAzimuthDegrees = builder.mAzimuthDegrees;
+ }
+
+ /** Gets the latitude in degrees of the reflecting plane. */
+ @FloatRange(from = -90.0f, to = 90.0f)
+ public double getLatitudeDegrees() {
+ return mLatitudeDegrees;
+ }
+
+ /** Gets the longitude in degrees of the reflecting plane. */
+ @FloatRange(from = -180.0f, to = 180.0f)
+ public double getLongitudeDegrees() {
+ return mLongitudeDegrees;
+ }
+
+ /**
+ * Gets the altitude in meters above the WGS 84 reference ellipsoid of the reflecting point
+ * within the plane
+ */
+ @FloatRange(from = -1000.0f, to = 10000.0f)
+ public double getAltitudeMeters() {
+ return mAltitudeMeters;
+ }
+
+ /** Gets the azimuth clockwise from north of the reflecting plane in degrees. */
+ @FloatRange(from = 0.0f, to = 360.0f)
+ public double getAzimuthDegrees() {
+ return mAzimuthDegrees;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<GnssReflectingPlane> CREATOR =
+ new Creator<GnssReflectingPlane>() {
+ @Override
+ @NonNull
+ public GnssReflectingPlane createFromParcel(@NonNull Parcel parcel) {
+ GnssReflectingPlane reflectingPlane =
+ new Builder()
+ .setLatitudeDegrees(parcel.readDouble())
+ .setLongitudeDegrees(parcel.readDouble())
+ .setAltitudeMeters(parcel.readDouble())
+ .setAzimuthDegrees(parcel.readDouble())
+ .build();
+ return reflectingPlane;
+ }
+
+ @Override
+ public GnssReflectingPlane[] newArray(int i) {
+ return new GnssReflectingPlane[i];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ final String format = " %-29s = %s\n";
+ StringBuilder builder = new StringBuilder("ReflectingPlane:\n");
+ builder.append(String.format(format, "LatitudeDegrees = ", mLatitudeDegrees));
+ builder.append(String.format(format, "LongitudeDegrees = ", mLongitudeDegrees));
+ builder.append(String.format(format, "AltitudeMeters = ", mAltitudeMeters));
+ builder.append(String.format(format, "AzimuthDegrees = ", mAzimuthDegrees));
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeDouble(mLatitudeDegrees);
+ parcel.writeDouble(mLongitudeDegrees);
+ parcel.writeDouble(mAltitudeMeters);
+ parcel.writeDouble(mAzimuthDegrees);
+ }
+
+ /** Builder for {@link GnssReflectingPlane} */
+ public static final class Builder {
+ /** For documentation, see corresponding fields in {@link GnssReflectingPlane}. */
+ private double mLatitudeDegrees;
+ private double mLongitudeDegrees;
+ private double mAltitudeMeters;
+ private double mAzimuthDegrees;
+
+ /** Sets the latitude in degrees of the reflecting plane. */
+ @NonNull public Builder setLatitudeDegrees(
+ @FloatRange(from = -90.0f, to = 90.0f) double latitudeDegrees) {
+ mLatitudeDegrees = latitudeDegrees;
+ return this;
+ }
+
+ /** Sets the longitude in degrees of the reflecting plane. */
+ @NonNull public Builder setLongitudeDegrees(
+ @FloatRange(from = -180.0f, to = 180.0f) double longitudeDegrees) {
+ mLongitudeDegrees = longitudeDegrees;
+ return this;
+ }
+
+ /**
+ * Sets the altitude in meters above the WGS 84 reference ellipsoid of the reflecting point
+ * within the plane
+ */
+ @NonNull public Builder setAltitudeMeters(
+ @FloatRange(from = -1000.0f, to = 10000.0f) double altitudeMeters) {
+ mAltitudeMeters = altitudeMeters;
+ return this;
+ }
+
+ /** Sets the azimuth clockwise from north of the reflecting plane in degrees. */
+ @NonNull public Builder setAzimuthDegrees(
+ @FloatRange(from = 0.0f, to = 360.0f) double azimuthDegrees) {
+ mAzimuthDegrees = azimuthDegrees;
+ return this;
+ }
+
+ /** Builds a {@link GnssReflectingPlane} object as specified by this builder. */
+ @NonNull public GnssReflectingPlane build() {
+ return new GnssReflectingPlane(this);
+ }
+ }
+}
diff --git a/android/location/GnssRequest.java b/android/location/GnssRequest.java
new file mode 100644
index 0000000..2afb265
--- /dev/null
+++ b/android/location/GnssRequest.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2020 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;
+
+/**
+ * This class contains extra parameters to pass to a GNSS provider implementation.
+ * @hide
+ */
+@SystemApi
+public final class GnssRequest implements Parcelable {
+ private final boolean mFullTracking;
+
+ /**
+ * Creates a {@link GnssRequest} with a full list of parameters.
+ */
+ private GnssRequest(boolean fullTracking) {
+ mFullTracking = fullTracking;
+ }
+
+ /**
+ * Represents whether to enable full GNSS tracking.
+ *
+ * <p>If true, GNSS chipset switches off duty cycling. In such a mode, no clock
+ * discontinuities are expected, and when supported, carrier phase should be continuous in
+ * good signal conditions. All non-blacklisted, healthy constellations, satellites and
+ * frequency bands that the chipset supports must be reported in this mode. The GNSS chipset
+ * is allowed to consume more power in this mode. If false, GNSS chipset optimizes power via
+ * duty cycling, constellations and frequency limits, etc.
+ */
+ public boolean isFullTracking() {
+ return mFullTracking;
+ }
+
+ @NonNull
+ public static final Creator<GnssRequest> CREATOR =
+ new Creator<GnssRequest>() {
+ @Override
+ @NonNull
+ public GnssRequest createFromParcel(@NonNull Parcel parcel) {
+ return new GnssRequest(parcel.readBoolean());
+ }
+
+ @Override
+ public GnssRequest[] newArray(int i) {
+ return new GnssRequest[i];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("GnssRequest[");
+ s.append("FullTracking=").append(mFullTracking);
+ s.append(']');
+ return s.toString();
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) return true;
+ if (obj == null) return false;
+ if (!(obj instanceof GnssRequest)) return false;
+
+ GnssRequest other = (GnssRequest) obj;
+ if (mFullTracking != other.mFullTracking) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return mFullTracking ? 1 : 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeBoolean(mFullTracking);
+ }
+
+ /** Builder for {@link GnssRequest} */
+ public static final class Builder {
+ private boolean mFullTracking;
+
+ /**
+ * Constructs a {@link Builder} instance.
+ */
+ public Builder() {
+ }
+
+ /**
+ * Constructs a {@link Builder} instance by copying a {@link GnssRequest}.
+ */
+ public Builder(@NonNull GnssRequest request) {
+ mFullTracking = request.isFullTracking();
+ }
+
+ /**
+ * Set the value of whether to enable full GNSS tracking, which is false by default.
+ *
+ * <p>If true, GNSS chipset switches off duty cycling. In such a mode, no clock
+ * discontinuities are expected, and when supported, carrier phase should be continuous in
+ * good signal conditions. All non-blacklisted, healthy constellations, satellites and
+ * frequency bands that the chipset supports must be reported in this mode. The GNSS chipset
+ * is allowed to consume more power in this mode. If false, GNSS chipset optimizes power via
+ * duty cycling, constellations and frequency limits, etc.
+ *
+ * <p>Full tracking requests always override non-full tracking requests. If any full
+ * tracking request occurs, all listeners on the device will receive full tracking GNSS
+ * measurements.
+ */
+ @NonNull public Builder setFullTracking(boolean value) {
+ mFullTracking = value;
+ return this;
+ }
+
+ /** Builds a {@link GnssRequest} instance as specified by this builder. */
+ @NonNull
+ public GnssRequest build() {
+ return new GnssRequest(mFullTracking);
+ }
+ }
+}
diff --git a/android/location/GnssSingleSatCorrection.java b/android/location/GnssSingleSatCorrection.java
new file mode 100644
index 0000000..aeca562
--- /dev/null
+++ b/android/location/GnssSingleSatCorrection.java
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2018 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.FloatRange;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.SystemApi;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.util.Preconditions;
+
+/**
+ * A container with measurement corrections for a single visible satellite
+ *
+ * @hide
+ */
+@SystemApi
+public final class GnssSingleSatCorrection implements Parcelable {
+
+ /**
+ * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+ * #mProbSatIsLos}.
+ *
+ * @hide
+ */
+ public static final int HAS_PROB_SAT_IS_LOS_MASK = 1 << 0;
+
+ /**
+ * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+ * #mExcessPathLengthMeters}.
+ *
+ * @hide
+ */
+ public static final int HAS_EXCESS_PATH_LENGTH_MASK = 1 << 1;
+
+ /**
+ * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+ * #mExcessPathLengthUncertaintyMeters}.
+ *
+ * @hide
+ */
+ public static final int HAS_EXCESS_PATH_LENGTH_UNC_MASK = 1 << 2;
+
+ /**
+ * Bit mask for {@link #mSingleSatCorrectionFlags} indicating the presence of {@link
+ * #mReflectingPlane}.
+ *
+ * @hide
+ */
+ public static final int HAS_REFLECTING_PLANE_MASK = 1 << 3;
+
+ /** A bitmask of fields present in this object (see HAS_* constants defined above) */
+ private final int mSingleSatCorrectionFlags;
+
+ /** Defines the constellation of the given satellite as defined in {@link GnssStatus}. */
+ @GnssStatus.ConstellationType
+ private final int mConstellationType;
+
+ /**
+ * Satellite vehicle ID number
+ *
+ * <p>Interpretation depends on {@link GnssStatus#getSvid(int)}.
+ */
+ @IntRange(from = 0)
+ private final int mSatId;
+
+ /**
+ * Carrier frequency of the signal to be corrected, for example it can be the GPS center
+ * frequency for L1 = 1,575,420,000 Hz, varying GLO channels, etc.
+ *
+ * <p>For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two correction
+ * objects will be reported for this same satellite, in one of the correction objects, all the
+ * values related to L1 will be filled, and in the other all of the values related to L5 will be
+ * filled.
+ */
+ @FloatRange(from = 0.0f, fromInclusive = false)
+ private final float mCarrierFrequencyHz;
+
+ /**
+ * The probability that the satellite is estimated to be in Line-of-Sight condition at the given
+ * location.
+ */
+ @FloatRange(from = 0.0f, to = 1.0f)
+ private final float mProbSatIsLos;
+
+ /**
+ * Excess path length to be subtracted from pseudorange before using it in calculating location.
+ */
+ @FloatRange(from = 0.0f)
+ private final float mExcessPathLengthMeters;
+
+ /** Error estimate (1-sigma) for the Excess path length estimate */
+ @FloatRange(from = 0.0f)
+ private final float mExcessPathLengthUncertaintyMeters;
+
+ /**
+ * Defines the reflecting plane location and azimuth information
+ *
+ * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite
+ * signal goes through multiple reflections or if reflection plane serving is not supported.
+ */
+ @Nullable
+ private final GnssReflectingPlane mReflectingPlane;
+
+ private GnssSingleSatCorrection(Builder builder) {
+ mSingleSatCorrectionFlags = builder.mSingleSatCorrectionFlags;
+ mSatId = builder.mSatId;
+ mConstellationType = builder.mConstellationType;
+ mCarrierFrequencyHz = builder.mCarrierFrequencyHz;
+ mProbSatIsLos = builder.mProbSatIsLos;
+ mExcessPathLengthMeters = builder.mExcessPathLengthMeters;
+ mExcessPathLengthUncertaintyMeters = builder.mExcessPathLengthUncertaintyMeters;
+ mReflectingPlane = builder.mReflectingPlane;
+ }
+
+ /**
+ * Gets a bitmask of fields present in this object
+ *
+ * @hide
+ */
+ public int getSingleSatelliteCorrectionFlags() {
+ return mSingleSatCorrectionFlags;
+ }
+
+ /**
+ * 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;
+ }
+
+ /**
+ * Gets the satellite ID.
+ *
+ * <p>Interpretation depends on {@link #getConstellationType()}. See {@link
+ * GnssStatus#getSvid(int)}.
+ */
+ @IntRange(from = 0)
+ public int getSatelliteId() {
+ return mSatId;
+ }
+
+ /**
+ * 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.
+ *
+ * <p>For an L1, L5 receiver tracking a satellite on L1 and L5 at the same time, two correction
+ * objects will be reported for this same satellite, in one of the correction objects, all the
+ * values related to L1 will be filled, and in the other all of the values related to L5 will be
+ * filled.
+ *
+ * @return the carrier frequency of the signal tracked in Hz.
+ */
+ @FloatRange(from = 0.0f, fromInclusive = false)
+ public float getCarrierFrequencyHz() {
+ return mCarrierFrequencyHz;
+ }
+
+ /**
+ * Returns the probability that the satellite is in line-of-sight condition at the given
+ * location.
+ */
+ @FloatRange(from = 0.0f, to = 1.0f)
+ public float getProbabilityLineOfSight() {
+ return mProbSatIsLos;
+ }
+
+ /**
+ * Returns the Excess path length to be subtracted from pseudorange before using it in
+ * calculating location.
+ */
+ @FloatRange(from = 0.0f)
+ public float getExcessPathLengthMeters() {
+ return mExcessPathLengthMeters;
+ }
+
+ /** Returns the error estimate (1-sigma) for the Excess path length estimate */
+ @FloatRange(from = 0.0f)
+ public float getExcessPathLengthUncertaintyMeters() {
+ return mExcessPathLengthUncertaintyMeters;
+ }
+
+ /**
+ * Returns the reflecting plane characteristics at which the signal has bounced
+ *
+ * <p>The flag HAS_REFLECTING_PLANE will be used to set this value to invalid if the satellite
+ * signal goes through multiple reflections or if reflection plane serving is not supported
+ */
+ @Nullable
+ public GnssReflectingPlane getReflectingPlane() {
+ return mReflectingPlane;
+ }
+
+ /** Returns {@code true} if {@link #getProbabilityLineOfSight()} is valid. */
+ public boolean hasValidSatelliteLineOfSight() {
+ return (mSingleSatCorrectionFlags & HAS_PROB_SAT_IS_LOS_MASK) != 0;
+ }
+
+ /** Returns {@code true} if {@link #getExcessPathLengthMeters()} is valid. */
+ public boolean hasExcessPathLength() {
+ return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_MASK) != 0;
+ }
+
+ /** Returns {@code true} if {@link #getExcessPathLengthUncertaintyMeters()} is valid. */
+ public boolean hasExcessPathLengthUncertainty() {
+ return (mSingleSatCorrectionFlags & HAS_EXCESS_PATH_LENGTH_UNC_MASK) != 0;
+ }
+
+ /** Returns {@code true} if {@link #getReflectingPlane()} is valid. */
+ public boolean hasReflectingPlane() {
+ return (mSingleSatCorrectionFlags & HAS_REFLECTING_PLANE_MASK) != 0;
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final Creator<GnssSingleSatCorrection> CREATOR =
+ new Creator<GnssSingleSatCorrection>() {
+ @Override
+ @NonNull
+ public GnssSingleSatCorrection createFromParcel(@NonNull Parcel parcel) {
+ int mSingleSatCorrectionFlags = parcel.readInt();
+ boolean hasReflectingPlane =
+ (mSingleSatCorrectionFlags & HAS_REFLECTING_PLANE_MASK) != 0;
+ final GnssSingleSatCorrection.Builder singleSatCorrectionBuilder =
+ new Builder()
+ .setConstellationType(parcel.readInt())
+ .setSatelliteId(parcel.readInt())
+ .setCarrierFrequencyHz(parcel.readFloat())
+ .setProbabilityLineOfSight(parcel.readFloat())
+ .setExcessPathLengthMeters(parcel.readFloat())
+ .setExcessPathLengthUncertaintyMeters(parcel.readFloat());
+ if (hasReflectingPlane) {
+ singleSatCorrectionBuilder.setReflectingPlane(
+ GnssReflectingPlane.CREATOR.createFromParcel(parcel));
+ }
+ return singleSatCorrectionBuilder.build();
+ }
+
+ @Override
+ public GnssSingleSatCorrection[] newArray(int i) {
+ return new GnssSingleSatCorrection[i];
+ }
+ };
+
+ @NonNull
+ @Override
+ public String toString() {
+ final String format = " %-29s = %s\n";
+ StringBuilder builder = new StringBuilder("GnssSingleSatCorrection:\n");
+ builder.append(
+ String.format(format, "SingleSatCorrectionFlags = ", mSingleSatCorrectionFlags));
+ builder.append(String.format(format, "ConstellationType = ", mConstellationType));
+ builder.append(String.format(format, "SatId = ", mSatId));
+ builder.append(String.format(format, "CarrierFrequencyHz = ", mCarrierFrequencyHz));
+ builder.append(String.format(format, "ProbSatIsLos = ", mProbSatIsLos));
+ builder.append(String.format(format, "ExcessPathLengthMeters = ", mExcessPathLengthMeters));
+ builder.append(
+ String.format(
+ format,
+ "ExcessPathLengthUncertaintyMeters = ",
+ mExcessPathLengthUncertaintyMeters));
+ if (hasReflectingPlane()) {
+ builder.append(String.format(format, "ReflectingPlane = ", mReflectingPlane));
+ }
+ return builder.toString();
+ }
+
+ @Override
+ public void writeToParcel(@NonNull Parcel parcel, int flags) {
+ parcel.writeInt(mSingleSatCorrectionFlags);
+ parcel.writeInt(mConstellationType);
+ parcel.writeInt(mSatId);
+ parcel.writeFloat(mCarrierFrequencyHz);
+ parcel.writeFloat(mProbSatIsLos);
+ parcel.writeFloat(mExcessPathLengthMeters);
+ parcel.writeFloat(mExcessPathLengthUncertaintyMeters);
+ if (hasReflectingPlane()) {
+ mReflectingPlane.writeToParcel(parcel, flags);
+ }
+ }
+
+ /** Builder for {@link GnssSingleSatCorrection} */
+ public static final class Builder {
+
+ /**
+ * For documentation of below fields, see corresponding fields in {@link
+ * GnssSingleSatCorrection}.
+ */
+ private int mSingleSatCorrectionFlags;
+
+ private int mConstellationType;
+ private int mSatId;
+ private float mCarrierFrequencyHz;
+ private float mProbSatIsLos;
+ private float mExcessPathLengthMeters;
+ private float mExcessPathLengthUncertaintyMeters;
+ @Nullable
+ private GnssReflectingPlane mReflectingPlane;
+
+ /** Sets the constellation type. */
+ @NonNull public Builder setConstellationType(
+ @GnssStatus.ConstellationType int constellationType) {
+ mConstellationType = constellationType;
+ return this;
+ }
+
+ /** Sets the Satellite ID defined in the ICD of the given constellation. */
+ @NonNull public Builder setSatelliteId(@IntRange(from = 0) int satId) {
+ mSatId = satId;
+ return this;
+ }
+
+ /** Sets the Carrier frequency in Hz. */
+ @NonNull public Builder setCarrierFrequencyHz(
+ @FloatRange(from = 0.0f, fromInclusive = false) float carrierFrequencyHz) {
+ mCarrierFrequencyHz = carrierFrequencyHz;
+ return this;
+ }
+
+ /**
+ * Sets the line-of-sight probability of the satellite at the given location in the range
+ * between 0 and 1.
+ */
+ @NonNull public Builder setProbabilityLineOfSight(
+ @FloatRange(from = 0.0f, to = 1.0f) float probSatIsLos) {
+ Preconditions.checkArgumentInRange(
+ probSatIsLos, 0, 1, "probSatIsLos should be between 0 and 1.");
+ mProbSatIsLos = probSatIsLos;
+ mSingleSatCorrectionFlags =
+ (byte) (mSingleSatCorrectionFlags | HAS_PROB_SAT_IS_LOS_MASK);
+ return this;
+ }
+
+ /**
+ * Sets the Excess path length to be subtracted from pseudorange before using it in
+ * calculating location.
+ */
+ @NonNull public Builder setExcessPathLengthMeters(
+ @FloatRange(from = 0.0f) float excessPathLengthMeters) {
+ mExcessPathLengthMeters = excessPathLengthMeters;
+ mSingleSatCorrectionFlags =
+ (byte) (mSingleSatCorrectionFlags | HAS_EXCESS_PATH_LENGTH_MASK);
+ return this;
+ }
+
+ /** Sets the error estimate (1-sigma) for the Excess path length estimate */
+ @NonNull public Builder setExcessPathLengthUncertaintyMeters(
+ @FloatRange(from = 0.0f) float excessPathLengthUncertaintyMeters) {
+ mExcessPathLengthUncertaintyMeters = excessPathLengthUncertaintyMeters;
+ mSingleSatCorrectionFlags =
+ (byte) (mSingleSatCorrectionFlags | HAS_EXCESS_PATH_LENGTH_UNC_MASK);
+ return this;
+ }
+
+ /** Sets the reflecting plane information */
+ @NonNull public Builder setReflectingPlane(@Nullable GnssReflectingPlane reflectingPlane) {
+ mReflectingPlane = reflectingPlane;
+ if (reflectingPlane != null) {
+ mSingleSatCorrectionFlags =
+ (byte) (mSingleSatCorrectionFlags | HAS_REFLECTING_PLANE_MASK);
+ } else {
+ mSingleSatCorrectionFlags =
+ (byte) (mSingleSatCorrectionFlags & ~HAS_REFLECTING_PLANE_MASK);
+ }
+ return this;
+ }
+
+ /** Builds a {@link GnssSingleSatCorrection} instance as specified by this builder. */
+ @NonNull public GnssSingleSatCorrection build() {
+ return new GnssSingleSatCorrection(this);
+ }
+ }
+}
diff --git a/android/location/GnssStatus.java b/android/location/GnssStatus.java
new file mode 100644
index 0000000..4d01cdc
--- /dev/null
+++ b/android/location/GnssStatus.java
@@ -0,0 +1,487 @@
+/*
+ * 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.FloatRange;
+import android.annotation.IntDef;
+import android.annotation.IntRange;
+import android.annotation.NonNull;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * This class represents the current state of the GNSS engine and is used in conjunction with
+ * {@link GnssStatus.Callback}.
+ *
+ * @see LocationManager#registerGnssStatusCallback
+ * @see GnssStatus.Callback
+ */
+public final class GnssStatus {
+
+ // These must match the definitions in GNSS HAL.
+ //
+ // Note: these constants are also duplicated in GnssStatusCompat.java in the androidx support
+ // library. if adding a constellation, please update that file as well.
+
+ /** 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;
+ /** Constellation type constant for IRNSS. */
+ public static final int CONSTELLATION_IRNSS = 7;
+ /** @hide */
+ public static final int CONSTELLATION_COUNT = 8;
+
+ private static final int SVID_FLAGS_NONE = 0;
+ private static final int SVID_FLAGS_HAS_EPHEMERIS_DATA = (1 << 0);
+ private static final int SVID_FLAGS_HAS_ALMANAC_DATA = (1 << 1);
+ private static final int SVID_FLAGS_USED_IN_FIX = (1 << 2);
+ private static final int SVID_FLAGS_HAS_CARRIER_FREQUENCY = (1 << 3);
+ private static final int SVID_FLAGS_HAS_BASEBAND_CN0 = (1 << 4);
+
+ private static final int SVID_SHIFT_WIDTH = 12;
+ private static final int CONSTELLATION_TYPE_SHIFT_WIDTH = 8;
+ private static final int CONSTELLATION_TYPE_MASK = 0xf;
+
+ /**
+ * Used for receiving notifications when GNSS events happen.
+ *
+ * @see LocationManager#registerGnssStatusCallback
+ */
+ 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(@NonNull GnssStatus status) {
+ }
+ }
+
+ /**
+ * Constellation type.
+ *
+ * @hide
+ */
+ @Retention(RetentionPolicy.SOURCE)
+ @IntDef({CONSTELLATION_UNKNOWN, CONSTELLATION_GPS, CONSTELLATION_SBAS, CONSTELLATION_GLONASS,
+ CONSTELLATION_QZSS, CONSTELLATION_BEIDOU, CONSTELLATION_GALILEO, CONSTELLATION_IRNSS})
+ public @interface ConstellationType {
+ }
+
+ /**
+ * Create a GnssStatus that wraps the given arguments without any additional overhead. Callers
+ * are responsible for guaranteeing that the arguments are never modified after calling this
+ * method.
+ *
+ * @hide
+ */
+ @NonNull
+ public static GnssStatus wrap(int svCount, int[] svidWithFlags, float[] cn0DbHzs,
+ float[] elevations, float[] azimuths, float[] carrierFrequencies,
+ float[] basebandCn0DbHzs) {
+ return new GnssStatus(svCount, svidWithFlags, cn0DbHzs, elevations, azimuths,
+ carrierFrequencies, basebandCn0DbHzs);
+ }
+
+ private final int mSvCount;
+ private final int[] mSvidWithFlags;
+ private final float[] mCn0DbHzs;
+ private final float[] mElevations;
+ private final float[] mAzimuths;
+ private final float[] mCarrierFrequencies;
+ private final float[] mBasebandCn0DbHzs;
+
+ private GnssStatus(int svCount, int[] svidWithFlags, float[] cn0DbHzs, float[] elevations,
+ float[] azimuths, float[] carrierFrequencies, float[] basebandCn0DbHzs) {
+ mSvCount = svCount;
+ mSvidWithFlags = svidWithFlags;
+ mCn0DbHzs = cn0DbHzs;
+ mElevations = elevations;
+ mAzimuths = azimuths;
+ mCarrierFrequencies = carrierFrequencies;
+ mBasebandCn0DbHzs = basebandCn0DbHzs;
+ }
+
+ /**
+ * Gets the total number of satellites in satellite list.
+ */
+ @IntRange(from = 0)
+ public int getSatelliteCount() {
+ return mSvCount;
+ }
+
+ /**
+ * Retrieves the constellation type of the satellite at the specified index.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ @ConstellationType
+ public int getConstellationType(@IntRange(from = 0) int satelliteIndex) {
+ return ((mSvidWithFlags[satelliteIndex] >> 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 & 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>
+ * <li>IRNSS: 1-14</li>
+ * </ul>
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ @IntRange(from = 1, to = 200)
+ public int getSvid(@IntRange(from = 0) int satelliteIndex) {
+ return mSvidWithFlags[satelliteIndex] >> SVID_SHIFT_WIDTH;
+ }
+
+ /**
+ * Retrieves the carrier-to-noise density at the antenna of the satellite at the specified index
+ * in dB-Hz.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ @FloatRange(from = 0, to = 63)
+ public float getCn0DbHz(@IntRange(from = 0) int satelliteIndex) {
+ return mCn0DbHzs[satelliteIndex];
+ }
+
+ /**
+ * Retrieves the elevation of the satellite at the specified index.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ @FloatRange(from = -90, to = 90)
+ public float getElevationDegrees(@IntRange(from = 0) int satelliteIndex) {
+ return mElevations[satelliteIndex];
+ }
+
+ /**
+ * Retrieves the azimuth the satellite at the specified index.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ @FloatRange(from = 0, to = 360)
+ public float getAzimuthDegrees(@IntRange(from = 0) int satelliteIndex) {
+ return mAzimuths[satelliteIndex];
+ }
+
+ /**
+ * Reports whether the satellite at the specified index has ephemeris data.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ public boolean hasEphemerisData(@IntRange(from = 0) int satelliteIndex) {
+ return (mSvidWithFlags[satelliteIndex] & SVID_FLAGS_HAS_EPHEMERIS_DATA) != 0;
+ }
+
+ /**
+ * Reports whether the satellite at the specified index has almanac data.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ public boolean hasAlmanacData(@IntRange(from = 0) int satelliteIndex) {
+ return (mSvidWithFlags[satelliteIndex] & SVID_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 satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ public boolean usedInFix(@IntRange(from = 0) int satelliteIndex) {
+ return (mSvidWithFlags[satelliteIndex] & SVID_FLAGS_USED_IN_FIX) != 0;
+ }
+
+ /**
+ * Reports whether a valid {@link #getCarrierFrequencyHz(int satelliteIndex)} is available.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ public boolean hasCarrierFrequencyHz(@IntRange(from = 0) int satelliteIndex) {
+ return (mSvidWithFlags[satelliteIndex] & SVID_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 satelliteIndex)} is
+ * {@code true}.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ @FloatRange(from = 0)
+ public float getCarrierFrequencyHz(@IntRange(from = 0) int satelliteIndex) {
+ return mCarrierFrequencies[satelliteIndex];
+ }
+
+ /**
+ * Reports whether a valid {@link #getBasebandCn0DbHz(int satelliteIndex)} is available.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ public boolean hasBasebandCn0DbHz(@IntRange(from = 0) int satelliteIndex) {
+ return (mSvidWithFlags[satelliteIndex] & SVID_FLAGS_HAS_BASEBAND_CN0) != 0;
+ }
+
+ /**
+ * Retrieves the baseband carrier-to-noise density of the satellite at the specified index in
+ * dB-Hz.
+ *
+ * @param satelliteIndex An index from zero to {@link #getSatelliteCount()} - 1
+ */
+ @FloatRange(from = 0, to = 63)
+ public float getBasebandCn0DbHz(@IntRange(from = 0) int satelliteIndex) {
+ return mBasebandCn0DbHzs[satelliteIndex];
+ }
+
+ /**
+ * Returns the string representation of a constellation type.
+ *
+ * @param constellationType the constellation type.
+ * @return the string representation.
+ * @hide
+ */
+ @NonNull
+ public static String constellationTypeToString(@ConstellationType int constellationType) {
+ switch (constellationType) {
+ case CONSTELLATION_UNKNOWN:
+ return "UNKNOWN";
+ case CONSTELLATION_GPS:
+ return "GPS";
+ case CONSTELLATION_SBAS:
+ return "SBAS";
+ case CONSTELLATION_GLONASS:
+ return "GLONASS";
+ case CONSTELLATION_QZSS:
+ return "QZSS";
+ case CONSTELLATION_BEIDOU:
+ return "BEIDOU";
+ case CONSTELLATION_GALILEO:
+ return "GALILEO";
+ case CONSTELLATION_IRNSS:
+ return "IRNSS";
+ default:
+ return Integer.toString(constellationType);
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (!(o instanceof GnssStatus)) {
+ return false;
+ }
+
+ GnssStatus that = (GnssStatus) o;
+ return mSvCount == that.mSvCount
+ && Arrays.equals(mSvidWithFlags, that.mSvidWithFlags)
+ && Arrays.equals(mCn0DbHzs, that.mCn0DbHzs)
+ && Arrays.equals(mElevations, that.mElevations)
+ && Arrays.equals(mAzimuths, that.mAzimuths)
+ && Arrays.equals(mCarrierFrequencies, that.mCarrierFrequencies)
+ && Arrays.equals(mBasebandCn0DbHzs, that.mBasebandCn0DbHzs);
+ }
+
+ @Override
+ public int hashCode() {
+ int result = Objects.hash(mSvCount);
+ result = 31 * result + Arrays.hashCode(mSvidWithFlags);
+ result = 31 * result + Arrays.hashCode(mCn0DbHzs);
+ return result;
+ }
+
+ /**
+ * Builder class to help create new GnssStatus instances.
+ */
+ public static final class Builder {
+
+ private final ArrayList<GnssSvInfo> mSatellites = new ArrayList<>();
+
+ /**
+ * Adds a new satellite to the Builder.
+ *
+ * @param constellationType one of the CONSTELLATION_* constants
+ * @param svid the space vehicle identifier
+ * @param cn0DbHz carrier-to-noise density at the antenna in dB-Hz
+ * @param elevation satellite elevation in degrees
+ * @param azimuth satellite azimuth in degrees
+ * @param hasEphemeris whether the satellite has ephemeris data
+ * @param hasAlmanac whether the satellite has almanac data
+ * @param usedInFix whether the satellite was used in the most recent location fix
+ * @param hasCarrierFrequency whether carrier frequency data is available
+ * @param carrierFrequency satellite carrier frequency in Hz
+ * @param hasBasebandCn0DbHz whether baseband carrier-to-noise density is available
+ * @param basebandCn0DbHz baseband carrier-to-noise density in dB-Hz
+ */
+ @NonNull
+ public Builder addSatellite(@ConstellationType int constellationType,
+ @IntRange(from = 1, to = 200) int svid,
+ @FloatRange(from = 0, to = 63) float cn0DbHz,
+ @FloatRange(from = -90, to = 90) float elevation,
+ @FloatRange(from = 0, to = 360) float azimuth,
+ boolean hasEphemeris,
+ boolean hasAlmanac,
+ boolean usedInFix,
+ boolean hasCarrierFrequency,
+ @FloatRange(from = 0) float carrierFrequency,
+ boolean hasBasebandCn0DbHz,
+ @FloatRange(from = 0, to = 63) float basebandCn0DbHz) {
+ mSatellites.add(new GnssSvInfo(constellationType, svid, cn0DbHz, elevation, azimuth,
+ hasEphemeris, hasAlmanac, usedInFix, hasCarrierFrequency, carrierFrequency,
+ hasBasebandCn0DbHz, basebandCn0DbHz));
+ return this;
+ }
+
+ /**
+ * Clears all satellites in the Builder.
+ */
+ @NonNull
+ public Builder clearSatellites() {
+ mSatellites.clear();
+ return this;
+ }
+
+ /**
+ * Builds a new GnssStatus based on the satellite information in the Builder.
+ */
+ @NonNull
+ public GnssStatus build() {
+ int svCount = mSatellites.size();
+ int[] svidWithFlags = new int[svCount];
+ float[] cn0DbHzs = new float[svCount];
+ float[] elevations = new float[svCount];
+ float[] azimuths = new float[svCount];
+ float[] carrierFrequencies = new float[svCount];
+ float[] basebandCn0DbHzs = new float[svCount];
+
+ for (int i = 0; i < svidWithFlags.length; i++) {
+ svidWithFlags[i] = mSatellites.get(i).mSvidWithFlags;
+ }
+ for (int i = 0; i < cn0DbHzs.length; i++) {
+ cn0DbHzs[i] = mSatellites.get(i).mCn0DbHz;
+ }
+ for (int i = 0; i < elevations.length; i++) {
+ elevations[i] = mSatellites.get(i).mElevation;
+ }
+ for (int i = 0; i < azimuths.length; i++) {
+ azimuths[i] = mSatellites.get(i).mAzimuth;
+ }
+ for (int i = 0; i < carrierFrequencies.length; i++) {
+ carrierFrequencies[i] = mSatellites.get(i).mCarrierFrequency;
+ }
+ for (int i = 0; i < basebandCn0DbHzs.length; i++) {
+ basebandCn0DbHzs[i] = mSatellites.get(i).mBasebandCn0DbHz;
+ }
+
+ return wrap(svCount, svidWithFlags, cn0DbHzs, elevations, azimuths,
+ carrierFrequencies, basebandCn0DbHzs);
+ }
+ }
+
+ private static class GnssSvInfo {
+
+ private final int mSvidWithFlags;
+ private final float mCn0DbHz;
+ private final float mElevation;
+ private final float mAzimuth;
+ private final float mCarrierFrequency;
+ private final float mBasebandCn0DbHz;
+
+ private GnssSvInfo(int constellationType, int svid, float cn0DbHz,
+ float elevation, float azimuth, boolean hasEphemeris, boolean hasAlmanac,
+ boolean usedInFix, boolean hasCarrierFrequency, float carrierFrequency,
+ boolean hasBasebandCn0DbHz, float basebandCn0DbHz) {
+ mSvidWithFlags = (svid << SVID_SHIFT_WIDTH)
+ | ((constellationType & CONSTELLATION_TYPE_MASK)
+ << CONSTELLATION_TYPE_SHIFT_WIDTH)
+ | (hasEphemeris ? SVID_FLAGS_HAS_EPHEMERIS_DATA : SVID_FLAGS_NONE)
+ | (hasAlmanac ? SVID_FLAGS_HAS_ALMANAC_DATA : SVID_FLAGS_NONE)
+ | (usedInFix ? SVID_FLAGS_USED_IN_FIX : SVID_FLAGS_NONE)
+ | (hasCarrierFrequency ? SVID_FLAGS_HAS_CARRIER_FREQUENCY : SVID_FLAGS_NONE)
+ | (hasBasebandCn0DbHz ? SVID_FLAGS_HAS_BASEBAND_CN0 : SVID_FLAGS_NONE);
+ mCn0DbHz = cn0DbHz;
+ mElevation = elevation;
+ mAzimuth = azimuth;
+ mCarrierFrequency = hasCarrierFrequency ? carrierFrequency : 0;
+ mBasebandCn0DbHz = hasBasebandCn0DbHz ? basebandCn0DbHz : 0;
+ }
+ }
+}
diff --git a/android/location/GpsClock.java b/android/location/GpsClock.java
new file mode 100644
index 0000000..58af6ee
--- /dev/null
+++ b/android/location/GpsClock.java
@@ -0,0 +1,508 @@
+/*
+ * 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;
+
+/**
+ * A class containing a GPS clock timestamp.
+ * It represents a measurement of the GPS receiver's clock.
+ *
+ * @deprecated use {@link GnssClock} instead.
+ *
+ * @hide
+ */
+@Deprecated
+@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 @android.annotation.NonNull 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;
+ }
+
+ @NonNull
+ @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 0000000..c2ab4ab
--- /dev/null
+++ b/android/location/GpsMeasurement.java
@@ -0,0 +1,1418 @@
+/*
+ * 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;
+
+/**
+ * A class representing a GPS satellite measurement, containing raw and computed information.
+ *
+ * @deprecated use {@link GnssMeasurement} instead.
+ *
+ * @hide
+ */
+@Deprecated
+@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 @android.annotation.NonNull 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;
+ }
+
+ @NonNull
+ @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 0000000..f3feb7a
--- /dev/null
+++ b/android/location/GpsMeasurementsEvent.java
@@ -0,0 +1,163 @@
+/*
+ * 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}.
+ *
+ * @deprecated use {@link GnssMeasurementsEvent} instead.
+ *
+ * @hide
+ */
+@Deprecated
+@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 @android.annotation.NonNull 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);
+ }
+
+ @NonNull
+ @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 0000000..dc1e99f
--- /dev/null
+++ b/android/location/GpsNavigationMessage.java
@@ -0,0 +1,328 @@
+/*
+ * 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.
+ *
+ * @deprecated use {@link GnssNavigationMessage} instead.
+ *
+ * @hide
+ */
+@Deprecated
+@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 @android.annotation.NonNull 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;
+ }
+
+ @NonNull
+ @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 0000000..2d5d6eb
--- /dev/null
+++ b/android/location/GpsNavigationMessageEvent.java
@@ -0,0 +1,123 @@
+/*
+ * 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}.
+ *
+ * @deprecated use {@link GnssNavigationMessage} instead.
+ *
+ * @hide
+ */
+@Deprecated
+@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 @android.annotation.NonNull 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);
+ }
+
+ @NonNull
+ @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 0000000..788d01e
--- /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 0000000..496885c
--- /dev/null
+++ b/android/location/GpsStatus.java
@@ -0,0 +1,235 @@
+/*
+ * 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.annotation.NonNull;
+import android.util.SparseArray;
+
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+
+
+/**
+ * This class represents the current state of the GPS engine and is used in conjunction with {@link
+ * GpsStatus.Listener}.
+ *
+ * @deprecated Use {@link GnssStatus} instead.
+ */
+@Deprecated
+public final class GpsStatus {
+ private static final int MAX_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;
+
+ 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 = SatelliteIterator::new;
+
+ /**
+ * 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);
+ }
+
+ /**
+ * Builds a GpsStatus from the given GnssStatus.
+ */
+ @NonNull
+ public static GpsStatus create(@NonNull GnssStatus gnssStatus, int timeToFirstFix) {
+ GpsStatus status = new GpsStatus();
+ status.setStatus(gnssStatus, timeToFirstFix);
+ return status;
+ }
+
+ private GpsStatus() {
+ }
+
+ /**
+ * @hide
+ */
+ void setStatus(GnssStatus status, int timeToFirstFix) {
+ for (int i = 0; i < mSatellites.size(); i++) {
+ mSatellites.valueAt(i).mValid = false;
+ }
+
+ mTimeToFirstFix = timeToFirstFix;
+ for (int i = 0; i < status.getSatelliteCount(); i++) {
+ int constellationType = status.getConstellationType(i);
+ int prn = status.getSvid(i);
+ // 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 > MAX_SATELLITES) {
+ continue;
+ }
+
+ GpsSatellite satellite = mSatellites.get(prn);
+ if (satellite == null) {
+ satellite = new GpsSatellite(prn);
+ mSatellites.put(prn, satellite);
+ }
+
+ satellite.mValid = true;
+ satellite.mSnr = status.getCn0DbHz(i);
+ satellite.mElevation = status.getElevationDegrees(i);
+ satellite.mAzimuth = status.getAzimuthDegrees(i);
+ satellite.mHasEphemeris = status.hasEphemerisData(i);
+ satellite.mHasAlmanac = status.hasAlmanacData(i);
+ satellite.mUsedInFix = status.usedInFix(i);
+ }
+ }
+
+ /**
+ * 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 mSatellites.size();
+ }
+
+}
diff --git a/android/location/Location.java b/android/location/Location.java
new file mode 100644
index 0000000..9aa0c87
--- /dev/null
+++ b/android/location/Location.java
@@ -0,0 +1,1257 @@
+/*
+ * 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.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+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 containing no GPS data.
+ * Allows location providers to flag locations as being safe to
+ * feed to LocationFudger.
+ *
+ * @hide
+ * @deprecated As of Android R, this extra is longer in use, since it is not necessary to keep
+ * gps locations separate from other locations for coarsening. Providers that do not need to
+ * support platforms below Android R should not use this constant.
+ */
+ @TestApi
+ @SystemApi
+ @Deprecated
+ 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;
+ /**
+ * Bit mask for mFieldsMask indicating the presence of mElapsedRealtimeUncertaintyNanos.
+ */
+ private static final int HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK = 256;
+
+ // 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();
+ }
+ };
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private String mProvider;
+ private long mTime = 0;
+ @UnsupportedAppUsage
+ private long mElapsedRealtimeNanos = 0;
+ // Estimate of the relative precision of the alignment of this SystemClock
+ // timestamp, with the reported measurements in nanoseconds (68% confidence).
+ private double mElapsedRealtimeUncertaintyNanos = 0.0f;
+ 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 int 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 source that provides the location. It can be of type
+ * {@link LocationManager#GPS_PROVIDER}, {@link LocationManager#NETWORK_PROVIDER},
+ * or {@link LocationManager#PASSIVE_PROVIDER}. You can also define your own
+ * provider string, in which case an empty string is a valid provider.
+ */
+ 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;
+ mElapsedRealtimeUncertaintyNanos = l.mElapsedRealtimeUncertaintyNanos;
+ 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;
+ mElapsedRealtimeUncertaintyNanos = 0.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;
+ }
+
+ /** @hide */
+ public long getElapsedRealtimeAgeNanos(long referenceRealtimeNs) {
+ return referenceRealtimeNs - mElapsedRealtimeNanos;
+ }
+
+ /** @hide */
+ public long getElapsedRealtimeAgeNanos() {
+ return getElapsedRealtimeAgeNanos(SystemClock.elapsedRealtimeNanos());
+ }
+
+ /**
+ * 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 estimate of the relative precision of the alignment of the
+ * ElapsedRealtimeNanos timestamp, with the reported measurements in
+ * nanoseconds (68% confidence).
+ *
+ * This means that we have 68% confidence that the true timestamp of the
+ * event is within ElapsedReatimeNanos +/- uncertainty.
+ *
+ * Example :
+ * - getElapsedRealtimeNanos() returns 10000000
+ * - getElapsedRealtimeUncertaintyNanos() returns 1000000 (equivalent to 1millisecond)
+ * This means that the event most likely happened between 9000000 and 11000000.
+ *
+ * @return uncertainty of elapsed real-time of fix, in nanoseconds.
+ */
+ public double getElapsedRealtimeUncertaintyNanos() {
+ return mElapsedRealtimeUncertaintyNanos;
+ }
+
+ /**
+ * Set estimate of the relative precision of the alignment of the
+ * ElapsedRealtimeNanos timestamp, with the reported measurements in
+ * nanoseconds (68% confidence).
+ *
+ * @param time uncertainty of the elapsed real-time of fix, in nanoseconds.
+ */
+ public void setElapsedRealtimeUncertaintyNanos(double time) {
+ mElapsedRealtimeUncertaintyNanos = time;
+ mFieldsMask |= HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK;
+ }
+
+ /**
+ * True if this location has a elapsed realtime accuracy.
+ */
+ public boolean hasElapsedRealtimeUncertaintyNanos() {
+ return (mFieldsMask & HAS_ELAPSED_REALTIME_UNCERTAINTY_MASK) != 0;
+ }
+
+
+ /**
+ * 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 at 68% confidence. Specifically, as 1-side of the
+ * 2-sided range above and below the estimated altitude reported by {@link #getAltitude()},
+ * within which there is a 68% probability of finding the true altitude.
+ *
+ * <p>In the case where the underlying distribution is assumed Gaussian normal, this would be
+ * considered 1 standard deviation.
+ *
+ * <p>For example, if {@link #getAltitude()} returns 150, and
+ * {@link #getVerticalAccuracyMeters()} returns 20 then there is a 68% probability
+ * of the true altitude being between 130 and 170 meters.
+ *
+ * <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 at 68% confidence. Specifically, 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>In the case where the underlying
+ * distribution is assumed Gaussian normal, this would be considered 1 standard deviation.
+ *
+ * <p>For example, if {@link #getSpeed()} returns 5, and
+ * {@link #getSpeedAccuracyMetersPerSecond()} returns 1, then there is a 68% probability of
+ * the true speed being between 4 and 6 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 at 68% confidence. Specifically, 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>In the case where the underlying distribution is assumed Gaussian normal, this would be
+ * considered 1 standard deviation.
+ *
+ * <p>For example, if {@link #getBearing()} returns 60, and
+ * {@link #getBearingAccuracyDegrees()} returns 10, then there is a 68% probability 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
+ */
+ @TestApi
+ @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.
+ *
+ * <p>Note this stores a copy of the given extras, so any changes to extras after calling this
+ * method won't be reflected in the location 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 (hasElapsedRealtimeUncertaintyNanos()) {
+ s.append(" etAcc=");
+ TimeUtils.formatDuration((long) (mElapsedRealtimeUncertaintyNanos / 1000000), 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 @android.annotation.NonNull 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.mElapsedRealtimeUncertaintyNanos = in.readDouble();
+ l.mFieldsMask = in.readInt();
+ 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.writeDouble(mElapsedRealtimeUncertaintyNanos);
+ parcel.writeInt(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;
+ }
+
+ /**
+ * 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 0000000..8df0834
--- /dev/null
+++ b/android/location/LocationListener.java
@@ -0,0 +1,69 @@
+/*
+ * 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.annotation.NonNull;
+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.
+ *
+ * @param location the updated location
+ */
+ void onLocationChanged(@NonNull Location location);
+
+ /**
+ * This callback will never be invoked on Android Q and above, and providers can be considered
+ * as always in the {@link LocationProvider#AVAILABLE} state.
+ *
+ * @deprecated This callback will never be invoked on Android Q and above.
+ */
+ @Deprecated
+ default 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 that has become enabled
+ */
+ default void onProviderEnabled(@NonNull 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 that has become disabled
+ */
+ default void onProviderDisabled(@NonNull String provider) {}
+}
diff --git a/android/location/LocationManager.java b/android/location/LocationManager.java
new file mode 100644
index 0000000..412b43e
--- /dev/null
+++ b/android/location/LocationManager.java
@@ -0,0 +1,3190 @@
+/*
+ * 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 static android.Manifest.permission.ACCESS_COARSE_LOCATION;
+import static android.Manifest.permission.ACCESS_FINE_LOCATION;
+import static android.Manifest.permission.LOCATION_HARDWARE;
+import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
+import static android.app.AlarmManager.ELAPSED_REALTIME;
+
+import static com.android.internal.util.function.pooled.PooledLambda.obtainRunnable;
+
+import android.Manifest;
+import android.annotation.CallbackExecutor;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresFeature;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.SystemService;
+import android.annotation.TestApi;
+import android.app.AlarmManager;
+import android.app.AppOpsManager;
+import android.app.PendingIntent;
+import android.app.PropertyInvalidatedCache;
+import android.compat.Compatibility;
+import android.compat.annotation.ChangeId;
+import android.compat.annotation.EnabledAfter;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.os.Binder;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.CancellationSignal;
+import android.os.Handler;
+import android.os.HandlerExecutor;
+import android.os.ICancellationSignal;
+import android.os.Looper;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.UserHandle;
+import android.provider.Settings;
+import android.util.ArrayMap;
+
+import com.android.internal.annotations.GuardedBy;
+import com.android.internal.location.ProviderProperties;
+import com.android.internal.util.Preconditions;
+import com.android.internal.util.function.pooled.PooledRunnable;
+
+import java.util.Collections;
+import java.util.List;
+import java.util.concurrent.Executor;
+import java.util.concurrent.RejectedExecutionException;
+import java.util.function.Consumer;
+
+/**
+ * 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 be notified 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 fine location providers. Other providers will
+ * still return location results, but the exact location will be obfuscated to a coarse level of
+ * accuracy.
+ */
+@SuppressWarnings({"deprecation"})
+@SystemService(Context.LOCATION_SERVICE)
+@RequiresFeature(PackageManager.FEATURE_LOCATION)
+public class LocationManager {
+
+ @GuardedBy("mLock")
+ private PropertyInvalidatedCache<Integer, Boolean> mLocationEnabledCache =
+ new PropertyInvalidatedCache<Integer, Boolean>(
+ 4,
+ CACHE_KEY_LOCATION_ENABLED_PROPERTY) {
+ @Override
+ protected Boolean recompute(Integer userHandle) {
+ try {
+ return mService.isLocationEnabledForUser(userHandle);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ };
+
+ private final Object mLock = new Object();
+
+ /**
+ * For apps targeting Android R and above, {@link #getProvider(String)} will no longer throw any
+ * security exceptions.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q)
+ private static final long GET_PROVIDER_SECURITY_EXCEPTIONS = 150935354L;
+
+ /**
+ * For apps targeting Android K and above, supplied {@link PendingIntent}s must be targeted to a
+ * specific package.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
+ private static final long TARGETED_PENDING_INTENT = 148963590L;
+
+ /**
+ * For apps targeting Android K and above, incomplete locations may not be passed to
+ * {@link #setTestProviderLocation}.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.JELLY_BEAN)
+ private static final long INCOMPLETE_LOCATION = 148964793L;
+
+ /**
+ * For apps targeting Android S and above, all {@link GpsStatus} API usage must be replaced with
+ * {@link GnssStatus} APIs.
+ *
+ * @hide
+ */
+ @ChangeId
+ @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.R)
+ private static final long GPS_STATUS_USAGE = 144027538L;
+
+ /**
+ * Name of the network location provider.
+ *
+ * <p>This provider determines location based on nearby 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 GNSS location provider.
+ *
+ * <p>This provider determines location using GNSS satellites. Depending on conditions, this
+ * provider may take a while to return a location fix. Requires the
+ * {@link android.Manifest.permission#ACCESS_FINE_LOCATION} permission.
+ *
+ * <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
+ * only return locations generated by other providers. You can query the
+ * {@link Location#getProvider()} method to determine the actual provider that supplied the
+ * location update. Requires the {@link android.Manifest.permission#ACCESS_FINE_LOCATION}
+ * permission, although there is no guarantee of fine locations.
+ */
+ public static final String PASSIVE_PROVIDER = "passive";
+
+ /**
+ * The fused location provider.
+ *
+ * <p>This provider combines may combine inputs from several location sources to provide the
+ * best possible location fix. It is implicitly used for all API's that involve the
+ * {@link LocationRequest} object.
+ *
+ * @hide
+ */
+ @TestApi
+ 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";
+
+ /**
+ * This key is no longer in use.
+ *
+ * <p>Key used for a Bundle extra holding an Integer status value when a status change is
+ * broadcast using a PendingIntent.
+ *
+ * @deprecated Status changes are deprecated and no longer broadcast from Android Q onwards.
+ */
+ @Deprecated
+ public static final String KEY_STATUS_CHANGED = "status";
+
+ /**
+ * Key used for an extra holding a boolean enabled/disabled status value when a provider
+ * enabled/disabled event is broadcast using a PendingIntent.
+ *
+ * @see #requestLocationUpdates(String, long, float, PendingIntent)
+ */
+ public static final String KEY_PROVIDER_ENABLED = "providerEnabled";
+
+ /**
+ * Key used for an extra holding a {@link Location} value when a location change is broadcast
+ * using a PendingIntent.
+ *
+ * @see #requestLocationUpdates(String, long, float, PendingIntent)
+ */
+ public static final String KEY_LOCATION_CHANGED = "location";
+
+ /**
+ * Broadcast intent action when the set of enabled location providers changes. To check the
+ * status of a provider, use {@link #isProviderEnabled(String)}. From Android Q and above, will
+ * include a string intent extra, {@link #EXTRA_PROVIDER_NAME}, with the name of the provider
+ * whose state has changed. From Android R and above, will include a boolean intent extra,
+ * {@link #EXTRA_PROVIDER_ENABLED}, with the enabled state of the provider.
+ *
+ * @see #EXTRA_PROVIDER_NAME
+ * @see #EXTRA_PROVIDER_ENABLED
+ * @see #isProviderEnabled(String)
+ */
+ public static final String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED";
+
+ /**
+ * Intent extra included with {@link #PROVIDERS_CHANGED_ACTION} broadcasts, containing the name
+ * of the location provider that has changed.
+ *
+ * @see #PROVIDERS_CHANGED_ACTION
+ * @see #EXTRA_PROVIDER_ENABLED
+ */
+ public static final String EXTRA_PROVIDER_NAME = "android.location.extra.PROVIDER_NAME";
+
+ /**
+ * Intent extra included with {@link #PROVIDERS_CHANGED_ACTION} broadcasts, containing the
+ * boolean enabled state of the location provider that has changed.
+ *
+ * @see #PROVIDERS_CHANGED_ACTION
+ * @see #EXTRA_PROVIDER_NAME
+ */
+ public static final String EXTRA_PROVIDER_ENABLED = "android.location.extra.PROVIDER_ENABLED";
+
+ /**
+ * Broadcast intent action when the device location enabled state changes. From Android R and
+ * above, will include a boolean intent extra, {@link #EXTRA_LOCATION_ENABLED}, with the enabled
+ * state of location.
+ *
+ * @see #EXTRA_LOCATION_ENABLED
+ * @see #isLocationEnabled()
+ */
+ public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED";
+
+ /**
+ * Intent extra included with {@link #MODE_CHANGED_ACTION} broadcasts, containing the boolean
+ * enabled state of location.
+ *
+ * @see #MODE_CHANGED_ACTION
+ */
+ public static final String EXTRA_LOCATION_ENABLED = "android.location.extra.LOCATION_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";
+
+ /**
+ * Broadcast intent action for Settings app to inject a footer at the bottom of location
+ * settings. This is for use only by apps that are included in the system image.
+ *
+ * <p>To inject a footer to location settings, you must declare a broadcast receiver for
+ * this action in the manifest:
+ * <pre>
+ * <receiver android:name="com.example.android.footer.MyFooterInjector">
+ * <intent-filter>
+ * <action android:name="com.android.settings.location.INJECT_FOOTER" />
+ * </intent-filter>
+ * <meta-data
+ * android:name="com.android.settings.location.FOOTER_STRING"
+ * android:resource="@string/my_injected_footer_string" />
+ * </receiver>
+ * </pre>
+ *
+ * <p>This broadcast receiver will never actually be invoked. See also
+ * {#METADATA_SETTINGS_FOOTER_STRING}.
+ *
+ * @hide
+ */
+ public static final String SETTINGS_FOOTER_DISPLAYED_ACTION =
+ "com.android.settings.location.DISPLAYED_FOOTER";
+
+ /**
+ * Metadata name for {@link LocationManager#SETTINGS_FOOTER_DISPLAYED_ACTION} broadcast
+ * receivers to specify a string resource id as location settings footer text. This is for use
+ * only by apps that are included in the system image.
+ *
+ * <p>See {@link #SETTINGS_FOOTER_DISPLAYED_ACTION} for more detail on how to use.
+ *
+ * @hide
+ */
+ public static final String METADATA_SETTINGS_FOOTER_STRING =
+ "com.android.settings.location.FOOTER_STRING";
+
+ private static final long GET_CURRENT_LOCATION_MAX_TIMEOUT_MS = 30 * 1000;
+
+ private final Context mContext;
+
+ @UnsupportedAppUsage
+ private final ILocationManager mService;
+
+ @GuardedBy("mListeners")
+ private final ArrayMap<LocationListener, LocationListenerTransport> mListeners =
+ new ArrayMap<>();
+
+ @GuardedBy("mBatchedLocationCallbackManager")
+ private final BatchedLocationCallbackManager mBatchedLocationCallbackManager =
+ new BatchedLocationCallbackManager();
+ private final GnssStatusListenerManager
+ mGnssStatusListenerManager = new GnssStatusListenerManager();
+ private final GnssMeasurementsListenerManager mGnssMeasurementsListenerManager =
+ new GnssMeasurementsListenerManager();
+ private final GnssNavigationMessageListenerManager mGnssNavigationMessageListenerTransport =
+ new GnssNavigationMessageListenerManager();
+ private final GnssAntennaInfoListenerManager mGnssAntennaInfoListenerManager =
+ new GnssAntennaInfoListenerManager();
+
+ /**
+ * @hide
+ */
+ public LocationManager(@NonNull Context context, @NonNull ILocationManager service) {
+ mService = service;
+ mContext = context;
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public @NonNull String[] getBackgroundThrottlingWhitelist() {
+ try {
+ return mService.getBackgroundThrottlingWhitelist();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * @hide
+ */
+ @TestApi
+ public @NonNull String[] getIgnoreSettingsWhitelist() {
+ try {
+ return mService.getIgnoreSettingsWhitelist();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the extra location controller package on the device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public @Nullable String getExtraLocationControllerPackage() {
+ try {
+ return mService.getExtraLocationControllerPackage();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return null;
+ }
+ }
+
+ /**
+ * Set the extra location controller package for location services on the device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void setExtraLocationControllerPackage(@Nullable String packageName) {
+ try {
+ mService.setExtraLocationControllerPackage(packageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether the extra location controller package is currently enabled on the device.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void setExtraLocationControllerPackageEnabled(boolean enabled) {
+ try {
+ mService.setExtraLocationControllerPackageEnabled(enabled);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns whether extra location controller package is currently enabled on the device.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isExtraLocationControllerPackageEnabled() {
+ try {
+ return mService.isExtraLocationControllerPackageEnabled();
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return false;
+ }
+ }
+
+ /**
+ * Set the extra location controller package for location services on the device.
+ *
+ * @removed
+ * @deprecated Use {@link #setExtraLocationControllerPackage} instead.
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void setLocationControllerExtraPackage(String packageName) {
+ try {
+ mService.setExtraLocationControllerPackage(packageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set whether the extra location controller package is currently enabled on the device.
+ *
+ * @removed
+ * @deprecated Use {@link #setExtraLocationControllerPackageEnabled} instead.
+ * @hide
+ */
+ @SystemApi
+ @Deprecated
+ @RequiresPermission(Manifest.permission.LOCATION_HARDWARE)
+ public void setLocationControllerExtraPackageEnabled(boolean enabled) {
+ try {
+ mService.setExtraLocationControllerPackageEnabled(enabled);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current enabled/disabled state of location. To listen for changes, see
+ * {@link #MODE_CHANGED_ACTION}.
+ *
+ * @return true if location is enabled and false if location is disabled.
+ */
+ public boolean isLocationEnabled() {
+ return isLocationEnabledForUser(Process.myUserHandle());
+ }
+
+ /**
+ * Returns the current enabled/disabled state of location for the given user.
+ *
+ * @param userHandle the user to query
+ * @return true if location is enabled and false if location is disabled.
+ *
+ * @hide
+ */
+ @SystemApi
+ public boolean isLocationEnabledForUser(@NonNull UserHandle userHandle) {
+ synchronized (mLock) {
+ if (mLocationEnabledCache != null) {
+ return mLocationEnabledCache.query(userHandle.getIdentifier());
+ }
+ }
+
+ // fallback if cache is disabled
+ try {
+ return mService.isLocationEnabledForUser(userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Enables or disables location for the given user.
+ *
+ * @param enabled true to enable location and false to disable location.
+ * @param userHandle the user to set
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(WRITE_SECURE_SETTINGS)
+ public void setLocationEnabledForUser(boolean enabled, @NonNull UserHandle userHandle) {
+ try {
+ mService.setLocationEnabledForUser(enabled, userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the current enabled/disabled status of the given provider. To listen for changes, see
+ * {@link #PROVIDERS_CHANGED_ACTION}.
+ *
+ * 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 a provider listed by {@link #getAllProviders()}
+ * @return true if the provider exists and is enabled
+ *
+ * @throws IllegalArgumentException if provider is null
+ */
+ public boolean isProviderEnabled(@NonNull String provider) {
+ return isProviderEnabledForUser(provider, Process.myUserHandle());
+ }
+
+ /**
+ * Returns the current enabled/disabled status of the given provider and user. Callers should
+ * prefer {@link #isLocationEnabledForUser(UserHandle)} unless they depend on provider-specific
+ * APIs.
+ *
+ * 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 a provider listed by {@link #getAllProviders()}
+ * @param userHandle the user to query
+ * @return true if the provider exists and is enabled
+ *
+ * @throws IllegalArgumentException if provider is null
+ * @hide
+ */
+ @SystemApi
+ public boolean isProviderEnabledForUser(
+ @NonNull String provider, @NonNull UserHandle userHandle) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ try {
+ return mService.isProviderEnabledForUser(provider, userHandle.getIdentifier());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Method for enabling or disabling a single location provider. This method is deprecated and
+ * functions as a best effort. It should not be relied on in any meaningful sense as providers
+ * may no longer be enabled or disabled by clients.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @param enabled whether to enable or disable the provider
+ * @param userHandle the user to set
+ * @return true if the value was set, false otherwise
+ *
+ * @throws IllegalArgumentException if provider is null
+ * @deprecated Do not manipulate providers individually, use
+ * {@link #setLocationEnabledForUser(boolean, UserHandle)} instead.
+ * @hide
+ */
+ @Deprecated
+ @SystemApi
+ @RequiresPermission(WRITE_SECURE_SETTINGS)
+ public boolean setProviderEnabledForUser(
+ @NonNull String provider, boolean enabled, @NonNull UserHandle userHandle) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ return Settings.Secure.putStringForUser(
+ mContext.getContentResolver(),
+ Settings.Secure.LOCATION_PROVIDERS_ALLOWED,
+ (enabled ? "+" : "-") + provider,
+ userHandle.getIdentifier());
+ }
+
+ /**
+ * Gets the last known location from the fused provider, or null if there is no last known
+ * location. The returned location may be quite old in some circumstances, so the age of the
+ * location should always be checked.
+ *
+ * @return the last known location, or null if not available
+ * @throws SecurityException if no suitable location permission is present
+ *
+ * @hide
+ */
+ @Nullable
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public Location getLastLocation() {
+ try {
+ return mService.getLastLocation(null, mContext.getPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Gets the last known location from the given provider, or null if there is no last known
+ * location. The returned location may be quite old in some circumstances, so the age of the
+ * location should always be checked.
+ *
+ * <p>This will never activate sensors to compute a new location, and will only ever return a
+ * cached location.
+ *
+ * <p>See also {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} which
+ * will always attempt to return a current location, but will potentially use additional power
+ * in the course of the attempt as compared to this method.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @return the last known location for the given provider, or null if not available
+ * @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})
+ @Nullable
+ public Location getLastKnownLocation(@NonNull String provider) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, 0, 0, true);
+
+ try {
+ return mService.getLastLocation(request, mContext.getPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Asynchronously returns a single current location fix. This may activate sensors in order to
+ * compute a new location, unlike {@link #getLastKnownLocation(String)}, which will only return
+ * a cached fix if available. The given callback will be invoked once and only once, either with
+ * a valid location fix or with a null location fix if the provider was unable to generate a
+ * valid location.
+ *
+ * <p>A client may supply an optional {@link CancellationSignal}. If this is used to cancel the
+ * operation, no callback should be expected after the cancellation.
+ *
+ * <p>This method may return locations from the very recent past (on the order of several
+ * seconds), but will never return older locations (for example, several minutes old or older).
+ * Clients may rely upon the guarantee that if this method returns a location, it will represent
+ * the best estimation of the location of the device in the present moment.
+ *
+ * <p>Clients calling this method from the background may notice that the method fails to
+ * determine a valid location fix more often than while in the foreground. Background
+ * applications may be throttled in their location accesses to some degree.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this {@link Executor}
+ * @param consumer the callback invoked with either a {@link Location} or null
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if consumer is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void getCurrentLocation(@NonNull String provider,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) {
+ getCurrentLocation(LocationRequest.createFromDeprecatedProvider(provider, 0, 0, true),
+ cancellationSignal, executor, consumer);
+ }
+
+ /**
+ * Asynchronously returns a single current location fix based on the given
+ * {@link LocationRequest}.
+ *
+ * <p>See {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)} for more
+ * information.
+ *
+ * @param locationRequest the location request containing location parameters
+ * @param cancellationSignal an optional signal that allows for cancelling this call
+ * @param executor the callback will take place on this {@link Executor}
+ * @param consumer the callback invoked with either a {@link Location} or null
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if consumer is null
+ * @throws SecurityException if no suitable permission is present
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void getCurrentLocation(@NonNull LocationRequest locationRequest,
+ @Nullable CancellationSignal cancellationSignal,
+ @NonNull @CallbackExecutor Executor executor, @NonNull Consumer<Location> consumer) {
+ LocationRequest currentLocationRequest = new LocationRequest(locationRequest)
+ .setNumUpdates(1);
+ if (currentLocationRequest.getExpireIn() > GET_CURRENT_LOCATION_MAX_TIMEOUT_MS) {
+ currentLocationRequest.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ }
+
+ GetCurrentLocationTransport transport = new GetCurrentLocationTransport(executor,
+ consumer);
+
+ if (cancellationSignal != null) {
+ cancellationSignal.throwIfCanceled();
+ }
+
+ ICancellationSignal remoteCancellationSignal = CancellationSignal.createTransport();
+
+ try {
+ if (mService.getCurrentLocation(currentLocationRequest, remoteCancellationSignal,
+ transport, mContext.getPackageName(), mContext.getAttributionTag(),
+ transport.getListenerId())) {
+ transport.register(mContext.getSystemService(AlarmManager.class),
+ remoteCancellationSignal);
+ if (cancellationSignal != null) {
+ cancellationSignal.setOnCancelListener(transport::cancel);
+ }
+ } else {
+ transport.fail();
+ }
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Register for a single location update using the named provider and a callback.
+ *
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener, Looper)} for
+ * more detail on how to use this method.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @param listener the listener to receive location updates
+ * @param looper the looper handling listener callbacks, or null to use the looper of 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
+ * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)}
+ * instead as it does not carry a risk of extreme battery drain.
+ */
+ @Deprecated
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestSingleUpdate(
+ @NonNull String provider, @NonNull LocationListener listener, @Nullable Looper looper) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ requestLocationUpdates(request, listener, looper);
+ }
+
+ /**
+ * 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 to choose the appropriate provider for location updates
+ * @param listener the listener to receive location updates
+ * @param looper the looper handling listener callbacks, or null to use the looper of the
+ * calling thread
+ *
+ * @throws IllegalArgumentException if criteria is null
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
+ * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)}
+ * instead as it does not carry a risk of extreme battery drain.
+ */
+ @Deprecated
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestSingleUpdate(
+ @NonNull Criteria criteria,
+ @NonNull LocationListener listener,
+ @Nullable Looper looper) {
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ requestLocationUpdates(request, listener, looper);
+ }
+
+ /**
+ * 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 a provider listed by {@link #getAllProviders()}
+ * @param pendingIntent the pending intent to send location updates
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present
+ * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)}
+ * instead as it does not carry a risk of extreme battery drain.
+ */
+ @Deprecated
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestSingleUpdate(@NonNull String provider,
+ @NonNull PendingIntent pendingIntent) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ requestLocationUpdates(request, pendingIntent);
+ }
+
+ /**
+ * 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 to choose the appropriate provider for location
+ * updates
+ * @param pendingIntent the pending intent to send location updates
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if intent is null
+ * @throws SecurityException if no suitable permission is present
+ * @deprecated Use {@link #getCurrentLocation(String, CancellationSignal, Executor, Consumer)}
+ * instead as it does not carry a risk of extreme battery drain.
+ */
+ @Deprecated
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestSingleUpdate(@NonNull Criteria criteria,
+ @NonNull PendingIntent pendingIntent) {
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, 0, 0, true);
+ request.setExpireIn(GET_CURRENT_LOCATION_MAX_TIMEOUT_MS);
+ requestLocationUpdates(request, pendingIntent);
+ }
+
+ /**
+ * Register for location updates from the given provider with the given arguments. {@link
+ * LocationListener} callbacks will take place on the given {@link Looper} or {@link Executor}.
+ * If a null {@link Looper} is supplied, the Looper of the calling thread will be used instead.
+ * Only one request can be registered for each unique listener, so any subsequent requests with
+ * the same listener will overwrite all associated arguments.
+ *
+ * <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> The location update interval can be controlled using the minimum time parameter. The
+ * elapsed time between location updates will never be less than this parameter, although it may
+ * be more depending on location availability and other factors. Choosing a sensible value for
+ * the minimum time parameter is important to conserve battery life. Every location update
+ * requires power from a variety of sensors. Select a minimum time parameter 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 consider switching
+ * to the {@link #PASSIVE_PROVIDER} instead.
+ *
+ * <p> The minimum distance 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 when the minimum
+ * time has elapsed. However it is more difficult for location providers to save power using the
+ * minimum distance parameter, so the minimum time parameter should be the primary tool for
+ * 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 turn on or modify active location providers, so
+ * you do not need to be as careful about minimum time and minimum distance parameters. However,
+ * if your application performs heavy work on a location update (such as network activity) then
+ * you should select non-zero values for the parameters to rate-limit your update frequency in
+ * the case another application enables a location provider with extremely fast updates.
+ *
+ * <p>In case the provider you have selected is disabled, location updates will cease, and a
+ * provider availability update will be sent. As soon as the provider is enabled again, another
+ * provider availability update will be sent and location updates will immediately resume.
+ *
+ * <p> When location callbacks are invoked, the system will hold a wakelock on your
+ * application's behalf for some period of time, but not indefinitely. If your application
+ * requires a long running wakelock within the location callback, you should acquire it
+ * yourself.
+ *
+ * <p class="note"> Prior to Jellybean, the minTime parameter was only a hint, and some location
+ * provider implementations ignored it. For Jellybean and onwards however, it is mandatory for
+ * Android compatible devices to observe both the minTime and minDistance parameters.
+ *
+ * <p>To unregister for location updates, use {@link #removeUpdates(LocationListener)}.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param listener the listener to receive location updates
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @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(@NonNull String provider, long minTimeMs, float minDistanceM,
+ @NonNull LocationListener listener) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, listener, null);
+ }
+
+ /**
+ * Register for location updates using the named provider, and a callback on
+ * the specified {@link Looper}.
+ *
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param listener the listener to receive location updates
+ * @param looper the looper handling listener callbacks, or null to use the looper of 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(@NonNull String provider, long minTimeMs, float minDistanceM,
+ @NonNull LocationListener listener, @Nullable Looper looper) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, listener, looper);
+ }
+
+ /**
+ * Register for location updates using the named provider, and a callback on
+ * the specified {@link Executor}.
+ *
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param executor the executor handling listener callbacks
+ * @param listener the listener to receive location updates
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if executor 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(
+ @NonNull String provider,
+ long minTimeMs,
+ float minDistanceM,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull LocationListener listener) {
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, executor, listener);
+ }
+
+ /**
+ * Register for location updates using a provider selected through the given Criteria, and a
+ * callback on the specified {@link Looper}.
+ *
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
+ *
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param criteria contains parameters to choose the appropriate provider for location updates
+ * @param listener the listener to receive location updates
+ *
+ * @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 minTimeMs, float minDistanceM,
+ @NonNull Criteria criteria, @NonNull LocationListener listener,
+ @Nullable Looper looper) {
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, listener, looper);
+ }
+
+ /**
+ * Register for location updates using a provider selected through the given Criteria, and a
+ * callback on the specified {@link Executor}.
+ *
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
+ *
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param criteria contains parameters to choose the appropriate provider for location updates
+ * @param executor the executor handling listener callbacks
+ * @param listener the listener to receive location updates
+ *
+ * @throws IllegalArgumentException if criteria is null
+ * @throws IllegalArgumentException if executor 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 minTimeMs,
+ float minDistanceM,
+ @NonNull Criteria criteria,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull LocationListener listener) {
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, executor, listener);
+ }
+
+ /**
+ * Register for location updates using the named provider, and callbacks delivered via the
+ * provided {@link PendingIntent}.
+ *
+ * <p>The delivered pending intents will contain extras with the callback information. The keys
+ * used for the extras are {@link #KEY_LOCATION_CHANGED} and {@link #KEY_PROVIDER_ENABLED}. See
+ * the documentation for each respective extra key for information on the values.
+ *
+ * <p>To unregister for location updates, use {@link #removeUpdates(PendingIntent)}.
+ *
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param pendingIntent the pending intent to send location updates
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if pendingIntent is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(@NonNull String provider, long minTimeMs, float minDistanceM,
+ @NonNull PendingIntent pendingIntent) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ LocationRequest request = LocationRequest.createFromDeprecatedProvider(
+ provider, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, pendingIntent);
+ }
+
+ /**
+ * Register for location updates using a provider selected through the given Criteria, and
+ * callbacks delivered via the provided {@link PendingIntent}.
+ *
+ * <p>See {@link #requestLocationUpdates(String, long, float, PendingIntent)} for more detail on
+ * how this method works.
+ *
+ * @param minTimeMs minimum time interval between location updates in milliseconds
+ * @param minDistanceM minimum distance between location updates in meters
+ * @param criteria contains parameters to choose the appropriate provider for location updates
+ * @param pendingIntent the pending intent to send location updates
+ *
+ * @throws IllegalArgumentException if provider is null or doesn't exist
+ * @throws IllegalArgumentException if pendingIntent is null
+ * @throws SecurityException if no suitable permission is present
+ */
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(long minTimeMs, float minDistanceM,
+ @NonNull Criteria criteria, @NonNull PendingIntent pendingIntent) {
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+
+ LocationRequest request = LocationRequest.createFromDeprecatedCriteria(
+ criteria, minTimeMs, minDistanceM, false);
+ requestLocationUpdates(request, pendingIntent);
+ }
+
+ /**
+ * Register for location updates using a {@link LocationRequest}, and a callback on the
+ * specified {@link Looper}.
+ *
+ * <p>The system will automatically select and enable the best provider based on the given
+ * {@link LocationRequest}. The LocationRequest can be null, in which case the system will
+ * choose default low power parameters for location updates, but this is heavily discouraged,
+ * and an explicit LocationRequest should always be provided.
+ *
+ * <p>See {@link #requestLocationUpdates(String, long, float, LocationListener)}
+ * for more detail on how this method works.
+ *
+ * @param locationRequest the location request containing location parameters
+ * @param listener the listener to receive location updates
+ * @param looper the looper handling listener callbacks, or null to use the looper of the
+ * calling thread
+ *
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(
+ @Nullable LocationRequest locationRequest,
+ @NonNull LocationListener listener,
+ @Nullable Looper looper) {
+ Handler handler = looper == null ? new Handler() : new Handler(looper);
+ requestLocationUpdates(locationRequest, new HandlerExecutor(handler), listener);
+ }
+
+ /**
+ * Register for location updates using a {@link LocationRequest}, and a callback on the
+ * specified {@link Executor}.
+ *
+ * <p>See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)} for more
+ * detail on how this method works.
+ *
+ * @param locationRequest the location request containing location parameters
+ * @param executor the executor handling listener callbacks
+ * @param listener the listener to receive location updates
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(
+ @Nullable LocationRequest locationRequest,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull LocationListener listener) {
+ synchronized (mListeners) {
+ LocationListenerTransport transport = mListeners.get(listener);
+ if (transport != null) {
+ transport.unregister();
+ } else {
+ transport = new LocationListenerTransport(listener);
+ mListeners.put(listener, transport);
+ }
+ transport.register(executor);
+
+ boolean registered = false;
+ try {
+ mService.requestLocationUpdates(locationRequest, transport, null,
+ mContext.getPackageName(), mContext.getAttributionTag(),
+ transport.getListenerId());
+ registered = true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ } finally {
+ if (!registered) {
+ // allow gc after exception
+ transport.unregister();
+ mListeners.remove(listener);
+ }
+ }
+ }
+ }
+
+ /**
+ * Register for location updates using a {@link LocationRequest}, and callbacks delivered via
+ * the provided {@link PendingIntent}.
+ *
+ * <p>See {@link #requestLocationUpdates(LocationRequest, LocationListener, Looper)} and
+ * {@link #requestLocationUpdates(String, long, float, PendingIntent)} for more detail on how
+ * this method works.
+ *
+ * @param locationRequest the location request containing location parameters
+ * @param pendingIntent the pending intent to send location updates
+ *
+ * @throws IllegalArgumentException if pendingIntent is null
+ * @throws SecurityException if no suitable permission is present
+ *
+ * @hide
+ */
+ @SystemApi
+ @TestApi
+ @RequiresPermission(anyOf = {ACCESS_COARSE_LOCATION, ACCESS_FINE_LOCATION})
+ public void requestLocationUpdates(
+ @Nullable LocationRequest locationRequest,
+ @NonNull PendingIntent pendingIntent) {
+ Preconditions.checkArgument(locationRequest != null, "invalid null location request");
+ Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
+ if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+ Preconditions.checkArgument(pendingIntent.isTargetedToPackage(),
+ "pending intent must be targeted to a package");
+ }
+
+ try {
+ mService.requestLocationUpdates(locationRequest, null, pendingIntent,
+ mContext.getPackageName(), mContext.getAttributionTag(), null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Set the last known location with a new location.
+ *
+ * <p>A privileged client can inject a {@link Location} if it has a better estimate of what
+ * the recent location is. This is especially useful when the device boots up and the GPS
+ * chipset is in the process of getting the first fix. If the client has cached the location,
+ * it can inject the {@link Location}, so if an app requests for a {@link Location} from {@link
+ * #getLastKnownLocation(String)}, the location information is still useful before getting
+ * the first fix.
+ *
+ * @param location newly available {@link Location} object
+ * @return true if the location was injected, false otherwise
+ *
+ * @throws IllegalArgumentException if location is null
+ * @throws SecurityException if permissions are not present
+ *
+ * @hide
+ */
+ @RequiresPermission(allOf = {LOCATION_HARDWARE, ACCESS_FINE_LOCATION})
+ public boolean injectLocation(@NonNull Location location) {
+ Preconditions.checkArgument(location != null, "invalid null location");
+ Preconditions.checkArgument(location.isComplete(),
+ "incomplete location object, missing timestamp or accuracy?");
+
+ try {
+ mService.injectLocation(location);
+ return true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes location updates for the specified {@link LocationListener}. Following this call,
+ * the listener will no longer receive location updates.
+ *
+ * @param listener listener that no longer needs location updates
+ *
+ * @throws IllegalArgumentException if listener is null
+ */
+ public void removeUpdates(@NonNull LocationListener listener) {
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+
+ synchronized (mListeners) {
+ LocationListenerTransport transport = mListeners.remove(listener);
+ if (transport == null) {
+ return;
+ }
+ transport.unregister();
+
+ try {
+ mService.removeUpdates(transport, null);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ /**
+ * Removes location updates for the specified {@link PendingIntent}. Following this call, the
+ * PendingIntent will no longer receive location updates.
+ *
+ * @param pendingIntent pending intent that no longer needs location updates
+ *
+ * @throws IllegalArgumentException if pendingIntent is null
+ */
+ public void removeUpdates(@NonNull PendingIntent pendingIntent) {
+ Preconditions.checkArgument(pendingIntent != null, "invalid null pending intent");
+
+ try {
+ mService.removeUpdates(null, pendingIntent);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns a list of the names of all known location providers. All providers are returned,
+ * including ones that are not permitted to be accessed by the calling activity or are currently
+ * disabled.
+ *
+ * @return list of provider names
+ */
+ public @NonNull List<String> getAllProviders() {
+ try {
+ return mService.getAllProviders();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns a list of the names of location providers. Only providers that the caller has
+ * permission to access will be returned.
+ *
+ * @param enabledOnly if true then only enabled providers are included
+ * @return list of provider names
+ */
+ public @NonNull List<String> getProviders(boolean enabledOnly) {
+ try {
+ return mService.getProviders(null, enabledOnly);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns a list of the names of providers that satisfy the given criteria. Only providers that
+ * the caller has permission to access will be returned.
+ *
+ * @param criteria the criteria that providers must match
+ * @param enabledOnly if true then only enabled providers are included
+ * @return list of provider names
+ *
+ * @throws IllegalArgumentException if criteria is null
+ */
+ public @NonNull List<String> getProviders(@NonNull Criteria criteria, boolean enabledOnly) {
+ Preconditions.checkArgument(criteria != null, "invalid null 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 caller 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 order:
+ *
+ * <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 enabled providers are included
+ * @return name of the provider that best matches the criteria, or null if none match
+ *
+ * @throws IllegalArgumentException if criteria is null
+ */
+ public @Nullable String getBestProvider(@NonNull Criteria criteria, boolean enabledOnly) {
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+
+ try {
+ return mService.getBestProvider(criteria, enabledOnly);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the information about the location provider with the given name, or null if no
+ * provider exists by that name.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @return location provider information, or null if provider does not exist
+ *
+ * @throws IllegalArgumentException if provider is null
+ */
+ public @Nullable LocationProvider getProvider(@NonNull String provider) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ if (!Compatibility.isChangeEnabled(GET_PROVIDER_SECURITY_EXCEPTIONS)) {
+ if (NETWORK_PROVIDER.equals(provider) || FUSED_PROVIDER.equals(provider)) {
+ try {
+ mContext.enforcePermission(ACCESS_FINE_LOCATION, Process.myPid(),
+ Process.myUid(), null);
+ } catch (SecurityException e) {
+ mContext.enforcePermission(ACCESS_COARSE_LOCATION, Process.myPid(),
+ Process.myUid(), null);
+ }
+ } else {
+ mContext.enforcePermission(ACCESS_FINE_LOCATION, Process.myPid(), Process.myUid(),
+ null);
+ }
+ }
+
+ try {
+ ProviderProperties properties = mService.getProviderProperties(provider);
+ if (properties == null) {
+ return null;
+ }
+ return new LocationProvider(provider, properties);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns true if the given package name matches a location provider package, and false
+ * otherwise.
+ *
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ public boolean isProviderPackage(@NonNull String packageName) {
+ try {
+ return mService.isProviderPackage(packageName);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return false;
+ }
+ }
+
+ /**
+ * Returns a list of packages associated with the given provider,
+ * and an empty list if no package is associated with the provider.
+ *
+ * @hide
+ */
+ @TestApi
+ @RequiresPermission(Manifest.permission.READ_DEVICE_CONFIG)
+ @Nullable
+ public List<String> getProviderPackages(@NonNull String provider) {
+ try {
+ return mService.getProviderPackages(provider);
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ return Collections.emptyList();
+ }
+ }
+
+ /**
+ * Sends additional commands to a location provider. Can be used to support provider specific
+ * extensions to the Location Manager API.
+ *
+ * @param provider a provider listed by {@link #getAllProviders()}
+ * @param command name of the command to send to the provider
+ * @param extras optional arguments for the command, or null
+ * @return true always, the return value may be ignored
+ */
+ public boolean sendExtraCommand(
+ @NonNull String provider, @NonNull String command, @Nullable Bundle extras) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(command != null, "invalid null command");
+
+ try {
+ return mService.sendExtraCommand(provider, command, extras);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Creates a test location provider and adds it to the set of active providers. This provider
+ * will replace any provider with the same name that exists prior to this call.
+ *
+ * @param provider the provider name
+ *
+ * @throws IllegalArgumentException if provider is null
+ * @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.
+ */
+ public void addTestProvider(
+ @NonNull String provider, boolean requiresNetwork, boolean requiresSatellite,
+ boolean requiresCell, boolean hasMonetaryCost, boolean supportsAltitude,
+ boolean supportsSpeed, boolean supportsBearing, int powerRequirement, int accuracy) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ ProviderProperties properties = new ProviderProperties(requiresNetwork,
+ requiresSatellite, requiresCell, hasMonetaryCost, supportsAltitude, supportsSpeed,
+ supportsBearing, powerRequirement, accuracy);
+ try {
+ mService.addTestProvider(provider, properties, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes the test location provider with the given name or does nothing if no such test
+ * location provider exists.
+ *
+ * @param provider the provider name
+ *
+ * @throws IllegalArgumentException if provider is null
+ * @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.
+ */
+ public void removeTestProvider(@NonNull String provider) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ try {
+ mService.removeTestProvider(provider, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Sets a new location for the given test provider. This location will be identiable as a mock
+ * location to all clients via {@link Location#isFromMockProvider()}.
+ *
+ * <p>The location object must have a minimum number of fields set to be considered valid, as
+ * per documentation on {@link Location} class.
+ *
+ * @param provider the provider name
+ * @param location 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 the provider is null or not a test provider
+ * @throws IllegalArgumentException if the location is null or incomplete
+ */
+ public void setTestProviderLocation(@NonNull String provider, @NonNull Location location) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+ Preconditions.checkArgument(location != null, "invalid null location");
+
+ if (Compatibility.isChangeEnabled(INCOMPLETE_LOCATION)) {
+ Preconditions.checkArgument(location.isComplete(),
+ "incomplete location object, missing timestamp or accuracy?");
+ } else {
+ location.makeComplete();
+ }
+
+ try {
+ mService.setTestProviderLocation(provider, location, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Does nothing.
+ *
+ * @deprecated This method has always been a no-op, and may be removed in the future.
+ */
+ @Deprecated
+ public void clearTestProviderLocation(@NonNull String provider) {}
+
+ /**
+ * Sets the given test provider to be enabled or disabled.
+ *
+ * @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 provider is null or not a test provider
+ */
+ public void setTestProviderEnabled(@NonNull String provider, boolean enabled) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ try {
+ mService.setTestProviderEnabled(provider, enabled, mContext.getOpPackageName(),
+ mContext.getAttributionTag());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Equivalent to calling {@link #setTestProviderEnabled(String, boolean)} to disable a test
+ * provider.
+ *
+ * @deprecated Use {@link #setTestProviderEnabled(String, boolean)} instead.
+ */
+ @Deprecated
+ public void clearTestProviderEnabled(@NonNull String provider) {
+ setTestProviderEnabled(provider, false);
+ }
+
+ /**
+ * This method has no effect as provider status has been deprecated and is no longer supported.
+ *
+ * @deprecated This method has no effect.
+ */
+ @Deprecated
+ public void setTestProviderStatus(
+ @NonNull String provider, int status, @Nullable Bundle extras, long updateTime) {}
+
+ /**
+ * This method has no effect as provider status has been deprecated and is no longer supported.
+ *
+ * @deprecated This method has no effect.
+ */
+ @Deprecated
+ public void clearTestProviderStatus(@NonNull String provider) {}
+
+ /**
+ * Get the last list of {@link LocationRequest}s sent to the provider.
+ *
+ * @hide
+ */
+ @TestApi
+ @NonNull
+ public List<LocationRequest> getTestProviderCurrentRequests(String providerName) {
+ Preconditions.checkArgument(providerName != null, "invalid null provider");
+ try {
+ return mService.getTestProviderCurrentRequests(providerName);
+ } 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,
+ @NonNull PendingIntent intent) {
+ Preconditions.checkArgument(intent != null, "invalid null pending intent");
+ if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+ Preconditions.checkArgument(intent.isTargetedToPackage(),
+ "pending intent must be targeted to a package");
+ }
+ 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(),
+ mContext.getAttributionTag());
+ } 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(@NonNull PendingIntent intent) {
+ Preconditions.checkArgument(intent != null, "invalid null pending intent");
+ if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+ Preconditions.checkArgument(intent.isTargetedToPackage(),
+ "pending intent must be targeted to a package");
+ }
+
+ try {
+ mService.removeGeofence(null, 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(
+ @NonNull LocationRequest request,
+ @NonNull Geofence fence,
+ @NonNull PendingIntent intent) {
+ Preconditions.checkArgument(request != null, "invalid null location request");
+ Preconditions.checkArgument(fence != null, "invalid null geofence");
+ Preconditions.checkArgument(intent != null, "invalid null pending intent");
+ if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+ Preconditions.checkArgument(intent.isTargetedToPackage(),
+ "pending intent must be targeted to a package");
+ }
+
+ try {
+ mService.requestGeofence(request, fence, intent, mContext.getPackageName(),
+ mContext.getAttributionTag());
+ } 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(@NonNull Geofence fence, @NonNull PendingIntent intent) {
+ Preconditions.checkArgument(fence != null, "invalid null geofence");
+ Preconditions.checkArgument(intent != null, "invalid null pending intent");
+ if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+ Preconditions.checkArgument(intent.isTargetedToPackage(),
+ "pending intent must be targeted to a package");
+ }
+
+ try {
+ mService.removeGeofence(fence, intent, mContext.getPackageName());
+ } 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(@NonNull PendingIntent intent) {
+ Preconditions.checkArgument(intent != null, "invalid null pending intent");
+ if (Compatibility.isChangeEnabled(TARGETED_PENDING_INTENT)) {
+ Preconditions.checkArgument(intent.isTargetedToPackage(),
+ "pending intent must be targeted to a package");
+ }
+
+ try {
+ mService.removeGeofence(null, intent, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ // ================= GNSS APIs =================
+
+ /**
+ * Returns the supported capabilities of the GNSS chipset.
+ */
+ public @NonNull GnssCapabilities getGnssCapabilities() {
+ try {
+ long gnssCapabilities = mService.getGnssCapabilities();
+ if (gnssCapabilities == GnssCapabilities.INVALID_CAPABILITIES) {
+ gnssCapabilities = 0L;
+ }
+ return GnssCapabilities.of(gnssCapabilities);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the model year of the GNSS hardware and software build. More details, such as build
+ * date, may be available in {@link #getGnssHardwareModelName()}. May return 0 if the model year
+ * is less than 2016.
+ */
+ public int getGnssYearOfHardware() {
+ try {
+ return mService.getGnssYearOfHardware();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Returns the Model Name (including Vendor and Hardware/Software Version) of the GNSS hardware
+ * driver.
+ *
+ * <p> No device-specific serial number or ID is returned from this API.
+ *
+ * <p> Will return null when the GNSS hardware abstraction layer does not support providing
+ * this value.
+ */
+ @Nullable
+ public String getGnssHardwareModelName() {
+ try {
+ return mService.getGnssHardwareModelName();
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Retrieves information about the current status of the GPS engine. This should only be called
+ * from within the {@link GpsStatus.Listener#onGpsStatusChanged} callback to ensure that the
+ * data is copied atomically.
+ *
+ * The caller may either pass in an existing {@link GpsStatus} object to be overwritten, 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 GpsStatus APIs are deprecated, use {@link GnssStatus} APIs instead. No longer
+ * supported in apps targeting S and above.
+ */
+ @Deprecated
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public @Nullable GpsStatus getGpsStatus(@Nullable GpsStatus status) {
+ if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
+ throw new UnsupportedOperationException(
+ "GpsStatus APIs not supported, please use GnssStatus APIs instead");
+ }
+
+ GnssStatus gnssStatus = mGnssStatusListenerManager.getGnssStatus();
+ int ttff = mGnssStatusListenerManager.getTtff();
+ if (gnssStatus != null) {
+ if (status == null) {
+ status = GpsStatus.create(gnssStatus, ttff);
+ } else {
+ status.setStatus(gnssStatus, ttff);
+ }
+ }
+ return status;
+ }
+
+ /**
+ * 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. No longer
+ * supported in apps targeting S and above.
+ */
+ @Deprecated
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean addGpsStatusListener(GpsStatus.Listener listener) {
+ if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
+ throw new UnsupportedOperationException(
+ "GpsStatus APIs not supported, please use GnssStatus APIs instead");
+ }
+
+ try {
+ return mGnssStatusListenerManager.addListener(listener, Runnable::run);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes a GPS status listener.
+ *
+ * @param listener GPS status listener object to remove
+ *
+ * @deprecated use {@link #unregisterGnssStatusCallback(GnssStatus.Callback)} instead. No longer
+ * supported in apps targeting S and above.
+ */
+ @Deprecated
+ public void removeGpsStatusListener(GpsStatus.Listener listener) {
+ if (Compatibility.isChangeEnabled(GPS_STATUS_USAGE)) {
+ throw new UnsupportedOperationException(
+ "GpsStatus APIs not supported, please use GnssStatus APIs instead");
+ }
+
+ try {
+ mGnssStatusListenerManager.removeListener(listener);
+ } 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
+ *
+ * @deprecated Use {@link #registerGnssStatusCallback(GnssStatus.Callback, Handler)} or {@link
+ * #registerGnssStatusCallback(Executor, GnssStatus.Callback)} instead.
+ */
+ @Deprecated
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssStatusCallback(@NonNull GnssStatus.Callback callback) {
+ return registerGnssStatusCallback(callback, null);
+ }
+
+ /**
+ * Registers a GNSS status callback.
+ *
+ * @param callback GNSS status callback object to register
+ * @param handler a handler with a looper that the callback runs on
+ * @return true if the listener was successfully added
+ *
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssStatusCallback(
+ @NonNull GnssStatus.Callback callback, @Nullable Handler handler) {
+ if (handler == null) {
+ handler = new Handler();
+ }
+
+ try {
+ return mGnssStatusListenerManager.addListener(callback, handler);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a GNSS status callback.
+ *
+ * @param executor the executor that the callback runs on
+ * @param callback GNSS status callback object to register
+ * @return true if the listener was successfully added
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssStatusCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GnssStatus.Callback callback) {
+ try {
+ return mGnssStatusListenerManager.addListener(callback, executor);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes a GNSS status callback.
+ *
+ * @param callback GNSS status callback object to remove
+ */
+ public void unregisterGnssStatusCallback(@NonNull GnssStatus.Callback callback) {
+ try {
+ mGnssStatusListenerManager.removeListener(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * No-op method to keep backward-compatibility.
+ *
+ * @deprecated Use {@link #addNmeaListener} instead.
+ */
+ @Deprecated
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean addNmeaListener(@NonNull GpsStatus.NmeaListener listener) {
+ return false;
+ }
+
+ /**
+ * No-op method to keep backward-compatibility.
+ *
+ * @deprecated Use {@link #removeNmeaListener(OnNmeaMessageListener)} instead.
+ */
+ @Deprecated
+ public void removeNmeaListener(@NonNull GpsStatus.NmeaListener listener) {}
+
+ /**
+ * 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
+ * @deprecated Use {@link #addNmeaListener(OnNmeaMessageListener, Handler)} or {@link
+ * #addNmeaListener(Executor, OnNmeaMessageListener)} instead.
+ */
+ @Deprecated
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean addNmeaListener(@NonNull OnNmeaMessageListener listener) {
+ return addNmeaListener(Runnable::run, listener);
+ }
+
+ /**
+ * Adds an NMEA listener.
+ *
+ * @param listener a {@link OnNmeaMessageListener} object to register
+ * @param handler a handler with the looper that the listener runs on.
+ * @return true if the listener was successfully added
+ *
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean addNmeaListener(
+ @NonNull OnNmeaMessageListener listener, @Nullable Handler handler) {
+ if (handler == null) {
+ handler = new Handler();
+ }
+ try {
+ return mGnssStatusListenerManager.addListener(listener, handler);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Adds an NMEA listener.
+ *
+ * @param listener a {@link OnNmeaMessageListener} object to register
+ * @param executor the {@link Executor} that the listener runs on.
+ * @return true if the listener was successfully added
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean addNmeaListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull OnNmeaMessageListener listener) {
+ try {
+ return mGnssStatusListenerManager.addListener(listener, executor);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Removes an NMEA listener.
+ *
+ * @param listener a {@link OnNmeaMessageListener} object to remove
+ */
+ public void removeNmeaListener(@NonNull OnNmeaMessageListener listener) {
+ try {
+ mGnssStatusListenerManager.removeListener(listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * No-op method to keep backward-compatibility.
+ *
+ * @hide
+ * @deprecated Use {@link #registerGnssMeasurementsCallback} instead.
+ * @removed
+ */
+ @Deprecated
+ @SystemApi
+ public boolean addGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {
+ return false;
+ }
+
+ /**
+ * No-op method to keep backward-compatibility.
+ *
+ * @hide
+ * @deprecated Use {@link #unregisterGnssMeasurementsCallback} instead.
+ * @removed
+ */
+ @Deprecated
+ @SystemApi
+ public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) {}
+
+ /**
+ * Registers a GPS Measurement callback which will run on a binder thread.
+ *
+ * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
+ * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ * @deprecated Use {@link
+ * #registerGnssMeasurementsCallback(GnssMeasurementsEvent.Callback, Handler)} or {@link
+ * #registerGnssMeasurementsCallback(Executor, GnssMeasurementsEvent.Callback)} instead.
+ */
+ @Deprecated
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssMeasurementsCallback(
+ @NonNull GnssMeasurementsEvent.Callback callback) {
+ return registerGnssMeasurementsCallback(Runnable::run, callback);
+ }
+
+ /**
+ * 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.
+ *
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssMeasurementsCallback(
+ @NonNull GnssMeasurementsEvent.Callback callback, @Nullable Handler handler) {
+ if (handler == null) {
+ handler = new Handler();
+ }
+ try {
+ return mGnssMeasurementsListenerManager.addListener(callback, handler);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a GPS Measurement callback.
+ *
+ * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
+ * @param executor the executor that the callback runs on.
+ * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssMeasurementsCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GnssMeasurementsEvent.Callback callback) {
+ try {
+ return mGnssMeasurementsListenerManager.addListener(callback, executor);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a GNSS Measurement callback.
+ *
+ * @param request extra parameters to pass to GNSS measurement provider. For example, if {@link
+ * GnssRequest#isFullTracking()} is true, GNSS chipset switches off duty
+ * cycling.
+ * @param executor the executor that the callback runs on.
+ * @param callback a {@link GnssMeasurementsEvent.Callback} object to register.
+ * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ * @throws IllegalArgumentException if request is null
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(allOf = {ACCESS_FINE_LOCATION, LOCATION_HARDWARE})
+ public boolean registerGnssMeasurementsCallback(
+ @NonNull GnssRequest request,
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GnssMeasurementsEvent.Callback callback) {
+ Preconditions.checkArgument(request != null, "invalid null request");
+ try {
+ return mGnssMeasurementsListenerManager.addListener(request, callback, executor);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Injects GNSS measurement corrections into the GNSS chipset.
+ *
+ * @param measurementCorrections a {@link GnssMeasurementCorrections} object with the GNSS
+ * measurement corrections to be injected into the GNSS chipset.
+ *
+ * @throws IllegalArgumentException if measurementCorrections is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ * @hide
+ */
+ @SystemApi
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public void injectGnssMeasurementCorrections(
+ @NonNull GnssMeasurementCorrections measurementCorrections) {
+ Preconditions.checkArgument(measurementCorrections != null);
+ try {
+ mService.injectGnssMeasurementCorrections(
+ measurementCorrections, mContext.getPackageName());
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters a GPS Measurement callback.
+ *
+ * @param callback a {@link GnssMeasurementsEvent.Callback} object to remove.
+ */
+ public void unregisterGnssMeasurementsCallback(
+ @NonNull GnssMeasurementsEvent.Callback callback) {
+ try {
+ mGnssMeasurementsListenerManager.removeListener(callback);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a Gnss Antenna Info listener. Only expect results if
+ * {@link GnssCapabilities#hasGnssAntennaInfo()} shows that antenna info is supported.
+ *
+ * @param executor the executor that the listener runs on.
+ * @param listener a {@link GnssAntennaInfo.Listener} object to register.
+ * @return {@code true} if the listener was added successfully, {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if listener is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerAntennaInfoListener(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GnssAntennaInfo.Listener listener) {
+ try {
+ return mGnssAntennaInfoListenerManager.addListener(listener, executor);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters a GNSS Antenna Info listener.
+ *
+ * @param listener a {@link GnssAntennaInfo.Listener} object to remove.
+ */
+ public void unregisterAntennaInfoListener(@NonNull GnssAntennaInfo.Listener listener) {
+ try {
+ mGnssAntennaInfoListenerManager.removeListener(listener);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * No-op method to keep backward-compatibility.
+ *
+ * @hide
+ * @deprecated Use {@link #registerGnssNavigationMessageCallback} instead.
+ * @removed
+ */
+ @Deprecated
+ @SystemApi
+ public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {
+ return false;
+ }
+
+ /**
+ * No-op method to keep backward-compatibility.
+ *
+ * @hide
+ * @deprecated Use {@link #unregisterGnssNavigationMessageCallback} instead.
+ * @removed
+ */
+ @Deprecated
+ @SystemApi
+ public void removeGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) {}
+
+ /**
+ * Registers a GNSS Navigation Message callback which will run on a binder thread.
+ *
+ * @param callback a {@link GnssNavigationMessage.Callback} object to register.
+ * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ * @deprecated Use {@link
+ * #registerGnssNavigationMessageCallback(GnssNavigationMessage.Callback, Handler)} or {@link
+ * #registerGnssNavigationMessageCallback(Executor, GnssNavigationMessage.Callback)} instead.
+ */
+ @Deprecated
+ public boolean registerGnssNavigationMessageCallback(
+ @NonNull GnssNavigationMessage.Callback callback) {
+ return registerGnssNavigationMessageCallback(Runnable::run, callback);
+ }
+
+ /**
+ * 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.
+ *
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssNavigationMessageCallback(
+ @NonNull GnssNavigationMessage.Callback callback, @Nullable Handler handler) {
+ if (handler == null) {
+ handler = new Handler();
+ }
+
+ try {
+ return mGnssNavigationMessageListenerTransport.addListener(callback, handler);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Registers a GNSS Navigation Message callback.
+ *
+ * @param callback a {@link GnssNavigationMessage.Callback} object to register.
+ * @param executor the looper that the callback runs on.
+ * @return {@code true} if the callback was added successfully, {@code false} otherwise.
+ *
+ * @throws IllegalArgumentException if executor is null
+ * @throws IllegalArgumentException if callback is null
+ * @throws SecurityException if the ACCESS_FINE_LOCATION permission is not present
+ */
+ @RequiresPermission(ACCESS_FINE_LOCATION)
+ public boolean registerGnssNavigationMessageCallback(
+ @NonNull @CallbackExecutor Executor executor,
+ @NonNull GnssNavigationMessage.Callback callback) {
+ try {
+ return mGnssNavigationMessageListenerTransport.addListener(callback, executor);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+
+ /**
+ * Unregisters a GNSS Navigation Message callback.
+ *
+ * @param callback a {@link GnssNavigationMessage.Callback} object to remove.
+ */
+ public void unregisterGnssNavigationMessageCallback(
+ @NonNull GnssNavigationMessage.Callback callback) {
+ try {
+ mGnssNavigationMessageListenerTransport.removeListener(callback);
+ } 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,
+ @NonNull BatchedLocationCallback callback, @Nullable Handler handler) {
+ if (handler == null) {
+ handler = new Handler();
+ }
+
+ synchronized (mBatchedLocationCallbackManager) {
+ try {
+ if (mBatchedLocationCallbackManager.addListener(callback, handler)) {
+ return mService.startGnssBatch(periodNanos, wakeOnFifoFull,
+ mContext.getPackageName(), mContext.getAttributionTag());
+ }
+ return false;
+ } 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(
+ @NonNull BatchedLocationCallback callback) {
+ synchronized (mBatchedLocationCallbackManager) {
+ try {
+ mBatchedLocationCallbackManager.removeListener(callback);
+ mService.stopGnssBatch();
+ return true;
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private static class GetCurrentLocationTransport extends ILocationListener.Stub implements
+ AlarmManager.OnAlarmListener {
+
+ @GuardedBy("this")
+ @Nullable
+ private Executor mExecutor;
+
+ @GuardedBy("this")
+ @Nullable
+ private Consumer<Location> mConsumer;
+
+ @GuardedBy("this")
+ @Nullable
+ private AlarmManager mAlarmManager;
+
+ @GuardedBy("this")
+ @Nullable
+ private ICancellationSignal mRemoteCancellationSignal;
+
+ private GetCurrentLocationTransport(Executor executor, Consumer<Location> consumer) {
+ Preconditions.checkArgument(executor != null, "illegal null executor");
+ Preconditions.checkArgument(consumer != null, "illegal null consumer");
+ mExecutor = executor;
+ mConsumer = consumer;
+ mAlarmManager = null;
+ mRemoteCancellationSignal = null;
+ }
+
+ public String getListenerId() {
+ return AppOpsManager.toReceiverId(mConsumer);
+ }
+
+ public synchronized void register(AlarmManager alarmManager,
+ ICancellationSignal remoteCancellationSignal) {
+ if (mConsumer == null) {
+ return;
+ }
+
+ mAlarmManager = alarmManager;
+ mAlarmManager.set(
+ ELAPSED_REALTIME,
+ SystemClock.elapsedRealtime() + GET_CURRENT_LOCATION_MAX_TIMEOUT_MS,
+ "GetCurrentLocation",
+ this,
+ null);
+
+ mRemoteCancellationSignal = remoteCancellationSignal;
+ }
+
+ public void cancel() {
+ ICancellationSignal cancellationSignal;
+ synchronized (this) {
+ mExecutor = null;
+ mConsumer = null;
+
+ if (mAlarmManager != null) {
+ mAlarmManager.cancel(this);
+ mAlarmManager = null;
+ }
+
+ // ensure only one cancel event will go through
+ cancellationSignal = mRemoteCancellationSignal;
+ mRemoteCancellationSignal = null;
+ }
+
+ if (cancellationSignal != null) {
+ try {
+ cancellationSignal.cancel();
+ } catch (RemoteException e) {
+ // ignore
+ }
+ }
+ }
+
+ public void fail() {
+ deliverResult(null);
+ }
+
+ @Override
+ public void onAlarm() {
+ synchronized (this) {
+ // save ourselves a pointless x-process call to cancel the alarm
+ mAlarmManager = null;
+ }
+
+ deliverResult(null);
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ synchronized (this) {
+ // save ourselves a pointless x-process call to cancel the location request
+ mRemoteCancellationSignal = null;
+ }
+
+ deliverResult(location);
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {}
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ // in the event of the provider being disabled it is unlikely that we will get further
+ // locations, so fail early so the client isn't left waiting hopelessly
+ deliverResult(null);
+ }
+
+ @Override
+ public void onRemoved() {
+ deliverResult(null);
+ }
+
+ private synchronized void deliverResult(@Nullable Location location) {
+ if (mExecutor == null) {
+ return;
+ }
+
+ PooledRunnable runnable =
+ obtainRunnable(GetCurrentLocationTransport::acceptResult, this, location)
+ .recycleOnUse();
+ try {
+ mExecutor.execute(runnable);
+ } catch (RejectedExecutionException e) {
+ runnable.recycle();
+ throw e;
+ }
+ }
+
+ private void acceptResult(Location location) {
+ Consumer<Location> consumer;
+ synchronized (this) {
+ if (mConsumer == null) {
+ return;
+ }
+ consumer = mConsumer;
+ cancel();
+ }
+
+ consumer.accept(location);
+ }
+ }
+
+ private class LocationListenerTransport extends ILocationListener.Stub {
+
+ private final LocationListener mListener;
+ @Nullable private volatile Executor mExecutor = null;
+
+ private LocationListenerTransport(@NonNull LocationListener listener) {
+ Preconditions.checkArgument(listener != null, "invalid null listener");
+ mListener = listener;
+ }
+
+ public LocationListener getKey() {
+ return mListener;
+ }
+
+ public String getListenerId() {
+ return AppOpsManager.toReceiverId(mListener);
+ }
+
+ public void register(@NonNull Executor executor) {
+ Preconditions.checkArgument(executor != null, "invalid null executor");
+ mExecutor = executor;
+ }
+
+ public void unregister() {
+ mExecutor = null;
+ }
+
+ @Override
+ public void onLocationChanged(Location location) {
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ return;
+ }
+
+ PooledRunnable runnable =
+ obtainRunnable(LocationListenerTransport::acceptLocation, this, currentExecutor,
+ location).recycleOnUse();
+ try {
+ currentExecutor.execute(runnable);
+ } catch (RejectedExecutionException e) {
+ runnable.recycle();
+ locationCallbackFinished();
+ throw e;
+ }
+ }
+
+ private void acceptLocation(Executor currentExecutor, Location location) {
+ try {
+ if (currentExecutor != mExecutor) {
+ return;
+ }
+
+ // we may be under the binder identity if a direct executor is used
+ long identity = Binder.clearCallingIdentity();
+ try {
+ mListener.onLocationChanged(location);
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } finally {
+ locationCallbackFinished();
+ }
+ }
+
+ @Override
+ public void onProviderEnabled(String provider) {
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ return;
+ }
+
+ PooledRunnable runnable =
+ obtainRunnable(LocationListenerTransport::acceptProviderChange, this,
+ currentExecutor, provider, true).recycleOnUse();
+ try {
+ currentExecutor.execute(runnable);
+ } catch (RejectedExecutionException e) {
+ runnable.recycle();
+ locationCallbackFinished();
+ throw e;
+ }
+ }
+
+ @Override
+ public void onProviderDisabled(String provider) {
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ return;
+ }
+
+ PooledRunnable runnable =
+ obtainRunnable(LocationListenerTransport::acceptProviderChange, this,
+ currentExecutor, provider, false).recycleOnUse();
+ try {
+ currentExecutor.execute(runnable);
+ } catch (RejectedExecutionException e) {
+ runnable.recycle();
+ locationCallbackFinished();
+ throw e;
+ }
+ }
+
+ private void acceptProviderChange(Executor currentExecutor, String provider,
+ boolean enabled) {
+ try {
+ if (currentExecutor != mExecutor) {
+ return;
+ }
+
+ // we may be under the binder identity if a direct executor is used
+ long identity = Binder.clearCallingIdentity();
+ try {
+ if (enabled) {
+ mListener.onProviderEnabled(provider);
+ } else {
+ mListener.onProviderDisabled(provider);
+ }
+ } finally {
+ Binder.restoreCallingIdentity(identity);
+ }
+ } finally {
+ locationCallbackFinished();
+ }
+ }
+
+ @Override
+ public void onRemoved() {
+ // TODO: onRemoved is necessary to GC hanging listeners, but introduces some interesting
+ // broken edge cases. luckily these edge cases are quite unlikely. consider the
+ // following interleaving for instance:
+ // 1) client adds single shot location request (A)
+ // 2) client gets removal callback, and schedules it for execution
+ // 3) client replaces single shot request with a different location request (B)
+ // 4) prior removal callback is executed, removing location request (B) incorrectly
+ // what's needed is a way to identify which listener a callback belongs to. currently
+ // we reuse the same transport object for the same listeners (so that we don't leak
+ // transport objects on the server side). there seem to be two solutions:
+ // 1) when reregistering a request, first unregister the current transport, then
+ // register with a new transport object (never reuse transport objects) - the
+ // downside is that this breaks the server's knowledge that the request is the
+ // same object, and thus breaks optimizations such as reusing the same transport
+ // state.
+ // 2) pass some other type of marker in addition to the transport (for example an
+ // incrementing integer representing the transport "version"), and pass this
+ // marker back into callbacks so that each callback knows which transport
+ // "version" it belongs to and can not execute itself if the version does not
+ // match.
+ // (1) seems like the preferred solution as it's simpler to implement and the above
+ // mentioned server optimizations are not terribly important (they can be bypassed by
+ // clients that use a new listener every time anyways).
+
+ Executor currentExecutor = mExecutor;
+ if (currentExecutor == null) {
+ // we've already been unregistered, no work to do anyways
+ return;
+ }
+
+ // must be executed on the same executor so callback execution cannot be reordered
+ currentExecutor.execute(() -> {
+ if (currentExecutor != mExecutor) {
+ return;
+ }
+
+ unregister();
+ synchronized (mListeners) {
+ mListeners.remove(mListener, this);
+ }
+ });
+ }
+
+ private void locationCallbackFinished() {
+ try {
+ mService.locationCallbackFinished(this);
+ } catch (RemoteException e) {
+ throw e.rethrowFromSystemServer();
+ }
+ }
+ }
+
+ private static class NmeaAdapter extends GnssStatus.Callback implements OnNmeaMessageListener {
+
+ private final OnNmeaMessageListener mListener;
+
+ private NmeaAdapter(OnNmeaMessageListener listener) {
+ mListener = listener;
+ }
+
+ @Override
+ public void onNmeaMessage(String message, long timestamp) {
+ mListener.onNmeaMessage(message, timestamp);
+ }
+ }
+
+ private class GnssStatusListenerManager extends
+ AbstractListenerManager<Void, GnssStatus.Callback> {
+ @Nullable
+ private IGnssStatusListener mListenerTransport;
+
+ @Nullable
+ private volatile GnssStatus mGnssStatus;
+ private volatile int mTtff;
+
+ public GnssStatus getGnssStatus() {
+ return mGnssStatus;
+ }
+
+ public int getTtff() {
+ return mTtff;
+ }
+
+ public boolean addListener(@NonNull GpsStatus.Listener listener, @NonNull Executor executor)
+ throws RemoteException {
+ return addInternal(null, listener, executor);
+ }
+
+ public boolean addListener(@NonNull OnNmeaMessageListener listener,
+ @NonNull Handler handler)
+ throws RemoteException {
+ return addInternal(null, listener, handler);
+ }
+
+ public boolean addListener(@NonNull OnNmeaMessageListener listener,
+ @NonNull Executor executor)
+ throws RemoteException {
+ return addInternal(null, listener, executor);
+ }
+
+ @Override
+ protected GnssStatus.Callback convertKey(Object listener) {
+ if (listener instanceof GnssStatus.Callback) {
+ return (GnssStatus.Callback) listener;
+ } else if (listener instanceof GpsStatus.Listener) {
+ return new GnssStatus.Callback() {
+ private final GpsStatus.Listener mGpsListener = (GpsStatus.Listener) listener;
+
+ @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 ttffMillis) {
+ mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_FIRST_FIX);
+ }
+
+ @Override
+ public void onSatelliteStatusChanged(GnssStatus status) {
+ mGpsListener.onGpsStatusChanged(GpsStatus.GPS_EVENT_SATELLITE_STATUS);
+ }
+ };
+ } else if (listener instanceof OnNmeaMessageListener) {
+ return new NmeaAdapter((OnNmeaMessageListener) listener);
+ } else {
+ throw new IllegalStateException();
+ }
+ }
+
+ @Override
+ protected boolean registerService(Void ignored) throws RemoteException {
+ Preconditions.checkState(mListenerTransport == null);
+
+ GnssStatusListener transport = new GnssStatusListener();
+ if (mService.registerGnssStatusCallback(transport, mContext.getPackageName(),
+ mContext.getAttributionTag())) {
+ mListenerTransport = transport;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected void unregisterService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport != null);
+
+ mService.unregisterGnssStatusCallback(mListenerTransport);
+ mListenerTransport = null;
+ }
+
+ private class GnssStatusListener extends IGnssStatusListener.Stub {
+ @Override
+ public void onGnssStarted() {
+ execute(GnssStatus.Callback::onStarted);
+ }
+
+ @Override
+ public void onGnssStopped() {
+ execute(GnssStatus.Callback::onStopped);
+ }
+
+ @Override
+ public void onFirstFix(int ttff) {
+ mTtff = ttff;
+ execute((callback) -> callback.onFirstFix(ttff));
+ }
+
+ @Override
+ public void onSvStatusChanged(int svCount, int[] svidWithFlags, float[] cn0s,
+ float[] elevations, float[] azimuths, float[] carrierFreqs,
+ float[] basebandCn0s) {
+ GnssStatus localStatus = GnssStatus.wrap(svCount, svidWithFlags, cn0s,
+ elevations, azimuths, carrierFreqs, basebandCn0s);
+ mGnssStatus = localStatus;
+ execute((callback) -> callback.onSatelliteStatusChanged(localStatus));
+ }
+
+ @Override
+ public void onNmeaReceived(long timestamp, String nmea) {
+ execute((callback) -> {
+ if (callback instanceof NmeaAdapter) {
+ ((NmeaAdapter) callback).onNmeaMessage(nmea, timestamp);
+ }
+ });
+ }
+ }
+ }
+
+ private class GnssMeasurementsListenerManager extends
+ AbstractListenerManager<GnssRequest, GnssMeasurementsEvent.Callback> {
+
+ @Nullable
+ private IGnssMeasurementsListener mListenerTransport;
+
+ @Override
+ protected boolean registerService(GnssRequest request) throws RemoteException {
+ Preconditions.checkState(mListenerTransport == null);
+
+ GnssMeasurementsListener transport = new GnssMeasurementsListener();
+ if (mService.addGnssMeasurementsListener(request, transport, mContext.getPackageName(),
+ mContext.getAttributionTag())) {
+ mListenerTransport = transport;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected void unregisterService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport != null);
+
+ mService.removeGnssMeasurementsListener(mListenerTransport);
+ mListenerTransport = null;
+ }
+
+ @Override
+ @Nullable
+ protected GnssRequest merge(@NonNull GnssRequest[] requests) {
+ Preconditions.checkArgument(requests.length > 0);
+ for (GnssRequest request : requests) {
+ if (request.isFullTracking()) {
+ return request;
+ }
+ }
+ return requests[0];
+ }
+
+ private class GnssMeasurementsListener extends IGnssMeasurementsListener.Stub {
+ @Override
+ public void onGnssMeasurementsReceived(final GnssMeasurementsEvent event) {
+ execute((callback) -> callback.onGnssMeasurementsReceived(event));
+ }
+
+ @Override
+ public void onStatusChanged(int status) {
+ execute((callback) -> callback.onStatusChanged(status));
+ }
+ }
+ }
+
+ private class GnssNavigationMessageListenerManager extends
+ AbstractListenerManager<Void, GnssNavigationMessage.Callback> {
+
+ @Nullable
+ private IGnssNavigationMessageListener mListenerTransport;
+
+ @Override
+ protected boolean registerService(Void ignored) throws RemoteException {
+ Preconditions.checkState(mListenerTransport == null);
+
+ GnssNavigationMessageListener transport = new GnssNavigationMessageListener();
+ if (mService.addGnssNavigationMessageListener(transport, mContext.getPackageName(),
+ mContext.getAttributionTag())) {
+ mListenerTransport = transport;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected void unregisterService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport != null);
+
+ mService.removeGnssNavigationMessageListener(mListenerTransport);
+ mListenerTransport = null;
+ }
+
+ private class GnssNavigationMessageListener extends IGnssNavigationMessageListener.Stub {
+ @Override
+ public void onGnssNavigationMessageReceived(GnssNavigationMessage event) {
+ execute((listener) -> listener.onGnssNavigationMessageReceived(event));
+ }
+
+ @Override
+ public void onStatusChanged(int status) {
+ execute((listener) -> listener.onStatusChanged(status));
+ }
+ }
+ }
+
+ private class GnssAntennaInfoListenerManager extends
+ AbstractListenerManager<Void, GnssAntennaInfo.Listener> {
+
+ @Nullable
+ private IGnssAntennaInfoListener mListenerTransport;
+
+ @Override
+ protected boolean registerService(Void ignored) throws RemoteException {
+ Preconditions.checkState(mListenerTransport == null);
+
+ GnssAntennaInfoListener transport = new GnssAntennaInfoListener();
+ if (mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(),
+ mContext.getAttributionTag())) {
+ mListenerTransport = transport;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected void unregisterService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport != null);
+
+ mService.removeGnssAntennaInfoListener(mListenerTransport);
+ mListenerTransport = null;
+ }
+
+ private class GnssAntennaInfoListener extends IGnssAntennaInfoListener.Stub {
+ @Override
+ public void onGnssAntennaInfoReceived(final List<GnssAntennaInfo> gnssAntennaInfos) {
+ execute((callback) -> callback.onGnssAntennaInfoReceived(gnssAntennaInfos));
+ }
+ }
+
+ }
+
+ private class BatchedLocationCallbackManager extends
+ AbstractListenerManager<Void, BatchedLocationCallback> {
+
+ @Nullable
+ private IBatchedLocationCallback mListenerTransport;
+
+ @Override
+ protected boolean registerService(Void ignored) throws RemoteException {
+ Preconditions.checkState(mListenerTransport == null);
+
+ BatchedLocationCallback transport = new BatchedLocationCallback();
+ if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(),
+ mContext.getAttributionTag())) {
+ mListenerTransport = transport;
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ @Override
+ protected void unregisterService() throws RemoteException {
+ Preconditions.checkState(mListenerTransport != null);
+
+ mService.removeGnssBatchingCallback();
+ mListenerTransport = null;
+ }
+
+ private class BatchedLocationCallback extends IBatchedLocationCallback.Stub {
+ @Override
+ public void onLocationBatch(List<Location> locations) {
+ execute((listener) -> listener.onLocationBatch(locations));
+ }
+
+ }
+ }
+
+ /**
+ * @hide
+ */
+ public static final String CACHE_KEY_LOCATION_ENABLED_PROPERTY =
+ "cache_key.location_enabled";
+
+ /**
+ * @hide
+ */
+ public static void invalidateLocalLocationEnabledCaches() {
+ PropertyInvalidatedCache.invalidateCache(CACHE_KEY_LOCATION_ENABLED_PROPERTY);
+ }
+
+ /**
+ * @hide
+ */
+ public void disableLocalLocationEnabledCaches() {
+ synchronized (mLock) {
+ mLocationEnabledCache = null;
+ }
+ }
+}
diff --git a/android/location/LocationManagerInternal.java b/android/location/LocationManagerInternal.java
new file mode 100644
index 0000000..6006d50
--- /dev/null
+++ b/android/location/LocationManagerInternal.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2020 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;
+
+/**
+ * Location manager local system service interface.
+ *
+ * @hide Only for use within the system server.
+ */
+public abstract class LocationManagerInternal {
+
+ /**
+ * Returns true if the given provider is enabled for the given user.
+ *
+ * @param provider A location provider as listed by {@link LocationManager#getAllProviders()}
+ * @param userId The user id to check
+ * @return True if the provider is enabled, false otherwise
+ */
+ public abstract boolean isProviderEnabledForUser(@NonNull String provider, int userId);
+
+ /**
+ * Returns true if the given package belongs to a location provider, and so should be afforded
+ * some special privileges.
+ *
+ * @param packageName The package name to check
+ * @return True is the given package belongs to a location provider, false otherwise
+ */
+ public abstract boolean isProviderPackage(@NonNull String packageName);
+
+ /**
+ * Should only be used by GNSS code.
+ */
+ // TODO: there is no reason for this to exist as part of any API. move all the logic into gnss
+ public abstract void sendNiResponse(int notifId, int userResponse);
+}
diff --git a/android/location/LocationProvider.java b/android/location/LocationProvider.java
new file mode 100644
index 0000000..52a03b6
--- /dev/null
+++ b/android/location/LocationProvider.java
@@ -0,0 +1,202 @@
+/*
+ * 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 {
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
+ public static final int OUT_OF_SERVICE = 0;
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
+ public static final int TEMPORARILY_UNAVAILABLE = 1;
+
+ /**
+ * @deprecated Location provider statuses are no longer supported.
+ */
+ @Deprecated
+ public static final int AVAILABLE = 2;
+
+ private final String mName;
+ private final ProviderProperties mProperties;
+
+ LocationProvider(String name, ProviderProperties properties) {
+ 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 0000000..5f0acc8
--- /dev/null
+++ b/android/location/LocationRequest.java
@@ -0,0 +1,799 @@
+/*
+ * 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.Manifest;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
+import android.compat.annotation.UnsupportedAppUsage;
+import android.os.Build;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.os.WorkSource;
+import android.util.TimeUtils;
+
+import com.android.internal.util.Preconditions;
+
+
+/**
+ * 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, 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
+@TestApi
+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;
+
+ private static final long DEFAULT_INTERVAL_MS = 60 * 60 * 1000; // 1 hour
+ private static final double FASTEST_INTERVAL_FACTOR = 6.0; // 6x
+
+ @UnsupportedAppUsage
+ private String mProvider;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private int mQuality;
+ @UnsupportedAppUsage
+ private long mInterval;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private long mFastestInterval;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private boolean mExplicitFastestInterval;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private long mExpireAt;
+ private long mExpireIn;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private int mNumUpdates;
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ private float mSmallestDisplacement;
+ @UnsupportedAppUsage
+ private boolean mHideFromAppOps;
+ private boolean mLocationSettingsIgnored;
+ private boolean mLowPowerMode;
+ @UnsupportedAppUsage
+ private @Nullable WorkSource mWorkSource;
+
+ /**
+ * 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
+ */
+ @NonNull
+ public static LocationRequest create() {
+ return new LocationRequest();
+ }
+
+ /** @hide */
+ @SystemApi
+ @NonNull
+ public static LocationRequest createFromDeprecatedProvider(
+ @NonNull String provider, long minTime, float minDistance, boolean singleShot) {
+ Preconditions.checkArgument(provider != null, "invalid null provider");
+
+ 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
+ @NonNull
+ public static LocationRequest createFromDeprecatedCriteria(
+ @NonNull Criteria criteria, long minTime, float minDistance, boolean singleShot) {
+ Preconditions.checkArgument(criteria != null, "invalid null criteria");
+
+ 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: {
+ if (criteria.getPowerRequirement() == Criteria.POWER_HIGH) {
+ quality = POWER_HIGH;
+ } else {
+ 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() {
+ this(
+ /* provider= */ LocationManager.FUSED_PROVIDER,
+ /* quality= */ POWER_LOW,
+ /* interval= */ DEFAULT_INTERVAL_MS,
+ /* fastestInterval= */ (long) (DEFAULT_INTERVAL_MS / FASTEST_INTERVAL_FACTOR),
+ /* explicitFastestInterval= */ false,
+ /* expireAt= */ Long.MAX_VALUE,
+ /* expireIn= */ Long.MAX_VALUE,
+ /* numUpdates= */ Integer.MAX_VALUE,
+ /* smallestDisplacement= */ 0,
+ /* hideFromAppOps= */ false,
+ /* locationSettingsIgnored= */ false,
+ /* lowPowerMode= */ false,
+ /* workSource= */ null);
+ }
+
+ /** @hide */
+ public LocationRequest(LocationRequest src) {
+ this(
+ src.mProvider,
+ src.mQuality,
+ src.mInterval,
+ src.mFastestInterval,
+ src.mExplicitFastestInterval,
+ src.mExpireAt,
+ src.mExpireIn,
+ src.mNumUpdates,
+ src.mSmallestDisplacement,
+ src.mHideFromAppOps,
+ src.mLocationSettingsIgnored,
+ src.mLowPowerMode,
+ src.mWorkSource);
+ }
+
+ private LocationRequest(
+ @NonNull String provider,
+ int quality,
+ long intervalMs,
+ long fastestIntervalMs,
+ boolean explicitFastestInterval,
+ long expireAt,
+ long expireInMs,
+ int numUpdates,
+ float smallestDisplacementM,
+ boolean hideFromAppOps,
+ boolean locationSettingsIgnored,
+ boolean lowPowerMode,
+ WorkSource workSource) {
+ Preconditions.checkArgument(provider != null, "invalid provider: null");
+ checkQuality(quality);
+
+ mProvider = provider;
+ mQuality = quality;
+ mInterval = intervalMs;
+ mFastestInterval = fastestIntervalMs;
+ mExplicitFastestInterval = explicitFastestInterval;
+ mExpireAt = expireAt;
+ mExpireIn = expireInMs;
+ mNumUpdates = numUpdates;
+ mSmallestDisplacement = Preconditions.checkArgumentInRange(smallestDisplacementM, 0,
+ Float.MAX_VALUE, "smallestDisplacementM");
+ mHideFromAppOps = hideFromAppOps;
+ mLowPowerMode = lowPowerMode;
+ mLocationSettingsIgnored = locationSettingsIgnored;
+ mWorkSource = workSource;
+ }
+
+ /**
+ * 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 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
+ * @return the same object, so that setters can be chained
+ * @throws IllegalArgumentException if the quality constant is not valid
+ */
+ public @NonNull 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
+ * @return the same object, so that setters can be chained
+ * @throws IllegalArgumentException if the interval is less than zero
+ */
+ public @NonNull LocationRequest setInterval(long millis) {
+ Preconditions.checkArgument(millis >= 0, "invalid interval: + 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;
+ }
+
+
+ /**
+ * Requests the GNSS chipset to run in a low power mode and make strong tradeoffs to
+ * substantially restrict power.
+ *
+ * <p>In this mode, the GNSS chipset will not, on average, run power hungry operations like RF &
+ * signal searches for more than one second per interval (specified by
+ * {@link #setInterval(long)}).
+ *
+ * @param enabled Enable or disable low power mode
+ * @return the same object, so that setters can be chained
+ */
+ public @NonNull LocationRequest setLowPowerMode(boolean enabled) {
+ mLowPowerMode = enabled;
+ return this;
+ }
+
+ /**
+ * Returns true if low power mode is enabled.
+ */
+ public boolean isLowPowerMode() {
+ return mLowPowerMode;
+ }
+
+ /**
+ * Requests that user location settings be ignored in order to satisfy this request. This API
+ * is only for use in extremely rare scenarios where it is appropriate to ignore user location
+ * settings, such as a user initiated emergency (dialing 911 for instance).
+ *
+ * @param locationSettingsIgnored Whether to ignore location settings
+ * @return the same object, so that setters can be chained
+ */
+ @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS)
+ public @NonNull LocationRequest setLocationSettingsIgnored(boolean locationSettingsIgnored) {
+ mLocationSettingsIgnored = locationSettingsIgnored;
+ return this;
+ }
+
+ /**
+ * Returns true if location settings will be ignored in order to satisfy this request.
+ */
+ public boolean isLocationSettingsIgnored() {
+ return mLocationSettingsIgnored;
+ }
+
+ /**
+ * 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 the fastest interval set is slower than {@link #setInterval},
+ * then your effective fastest interval is {@link #setInterval}.
+ *
+ * @param millis fastest interval for updates in milliseconds
+ * @return the same object, so that setters can be chained
+ * @throws IllegalArgumentException if the interval is less than zero
+ */
+ public @NonNull LocationRequest setFastestInterval(long millis) {
+ Preconditions.checkArgument(millis >= 0, "invalid interval: + millis");
+ mExplicitFastestInterval = true;
+ mFastestInterval = millis;
+ return this;
+ }
+
+ /**
+ * Get the fastest interval of this request in milliseconds. The system will never provide
+ * location updates faster than the minimum of the fastest interval and {@link #getInterval}.
+ *
+ * @return fastest interval in milliseconds
+ */
+ public long getFastestInterval() {
+ return mFastestInterval;
+ }
+
+ /**
+ * Set the expiration time of this request in milliseconds of realtime since boot. Values in the
+ * past are allowed, but indicate that the request has already expired. The location manager
+ * will automatically stop updates after the request expires.
+ *
+ * @param millis expiration time of request in milliseconds since boot
+ * @return the same object, so that setters can be chained
+ * @see SystemClock#elapsedRealtime()
+ * @deprecated Prefer {@link #setExpireIn(long)}.
+ */
+ @Deprecated
+ public @NonNull LocationRequest setExpireAt(long millis) {
+ mExpireAt = Math.max(millis, 0);
+ return this;
+ }
+
+ /**
+ * Get the request expiration time in milliseconds of realtime since boot.
+ *
+ * @return request expiration time in milliseconds since boot
+ * @see SystemClock#elapsedRealtime()
+ * @deprecated Prefer {@link #getExpireIn()}.
+ */
+ @Deprecated
+ public long getExpireAt() {
+ return mExpireAt;
+ }
+
+ /**
+ * Set the duration of this request in milliseconds of realtime. Values less than 0 are allowed,
+ * but indicate that the request has already expired. The location manager will automatically
+ * stop updates after the request expires.
+ *
+ * @param millis duration of request in milliseconds
+ * @return the same object, so that setters can be chained
+ * @see SystemClock#elapsedRealtime()
+ */
+ public @NonNull LocationRequest setExpireIn(long millis) {
+ mExpireIn = millis;
+ return this;
+ }
+
+ /**
+ * Get the request expiration duration in milliseconds of realtime.
+ *
+ * @return request expiration duration in milliseconds
+ * @see SystemClock#elapsedRealtime()
+ */
+ public long getExpireIn() {
+ return mExpireIn;
+ }
+
+ /**
+ * Returns the realtime at which this request expires, taking into account both
+ * {@link #setExpireAt(long)} and {@link #setExpireIn(long)} relative to the given realtime.
+ *
+ * @hide
+ */
+ public long getExpirationRealtimeMs(long startRealtimeMs) {
+ long expirationRealtimeMs;
+ // Check for > Long.MAX_VALUE overflow (elapsedRealtime > 0):
+ if (mExpireIn > Long.MAX_VALUE - startRealtimeMs) {
+ expirationRealtimeMs = Long.MAX_VALUE;
+ } else {
+ expirationRealtimeMs = startRealtimeMs + mExpireIn;
+ }
+ return Math.min(expirationRealtimeMs, 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
+ * @return the same object, so that setters can be chained
+ * @throws IllegalArgumentException if numUpdates is 0 or less
+ */
+ public @NonNull 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;
+ }
+ }
+
+ /** Sets the provider to use for this location request. */
+ public @NonNull LocationRequest setProvider(@NonNull String provider) {
+ Preconditions.checkArgument(provider != null, "invalid provider: null");
+ mProvider = provider;
+ return this;
+ }
+
+ /** @hide */
+ @SystemApi
+ public @NonNull String getProvider() {
+ return mProvider;
+ }
+
+ /** @hide */
+ @SystemApi
+ public @NonNull LocationRequest setSmallestDisplacement(float smallestDisplacementM) {
+ mSmallestDisplacement = Preconditions.checkArgumentInRange(smallestDisplacementM, 0,
+ Float.MAX_VALUE, "smallestDisplacementM");
+ 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(@Nullable WorkSource workSource) {
+ mWorkSource = workSource;
+ }
+
+ /** @hide */
+ @SystemApi
+ public @Nullable 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.
+ * @hide
+ * @see android.app.AppOpsManager
+ */
+ @SystemApi
+ public void setHideFromAppOps(boolean hideFromAppOps) {
+ mHideFromAppOps = hideFromAppOps;
+ }
+
+ /** @hide */
+ @SystemApi
+ public boolean getHideFromAppOps() {
+ return mHideFromAppOps;
+ }
+
+ @UnsupportedAppUsage(maxTargetSdk = Build.VERSION_CODES.P, trackingBug = 115609023)
+ 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);
+ }
+ }
+
+ public static final @NonNull Parcelable.Creator<LocationRequest> CREATOR =
+ new Parcelable.Creator<LocationRequest>() {
+ @Override
+ public LocationRequest createFromParcel(Parcel in) {
+ return new LocationRequest(
+ /* provider= */ in.readString(),
+ /* quality= */ in.readInt(),
+ /* interval= */ in.readLong(),
+ /* fastestInterval= */ in.readLong(),
+ /* explicitFastestInterval= */ in.readBoolean(),
+ /* expireAt= */ in.readLong(),
+ /* expireIn= */ in.readLong(),
+ /* numUpdates= */ in.readInt(),
+ /* smallestDisplacement= */ in.readFloat(),
+ /* hideFromAppOps= */ in.readBoolean(),
+ /* locationSettingsIgnored= */ in.readBoolean(),
+ /* lowPowerMode= */ in.readBoolean(),
+ /* workSource= */ in.readTypedObject(WorkSource.CREATOR));
+ }
+
+ @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.writeString(mProvider);
+ parcel.writeInt(mQuality);
+ parcel.writeLong(mInterval);
+ parcel.writeLong(mFastestInterval);
+ parcel.writeBoolean(mExplicitFastestInterval);
+ parcel.writeLong(mExpireAt);
+ parcel.writeLong(mExpireIn);
+ parcel.writeInt(mNumUpdates);
+ parcel.writeFloat(mSmallestDisplacement);
+ parcel.writeBoolean(mHideFromAppOps);
+ parcel.writeBoolean(mLocationSettingsIgnored);
+ parcel.writeBoolean(mLowPowerMode);
+ parcel.writeTypedObject(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 "???";
+ }
+ }
+
+ @NonNull
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder();
+ s.append("Request[");
+ s.append(qualityToString(mQuality));
+ s.append(" ").append(mProvider);
+ if (mQuality != POWER_NONE) {
+ s.append(" interval=");
+ TimeUtils.formatDuration(mInterval, s);
+ if (mExplicitFastestInterval) {
+ s.append(" fastestInterval=");
+ TimeUtils.formatDuration(mFastestInterval, s);
+ }
+ }
+ if (mExpireAt != Long.MAX_VALUE) {
+ s.append(" expireAt=").append(TimeUtils.formatRealtime(mExpireAt));
+ }
+ if (mExpireIn != Long.MAX_VALUE) {
+ s.append(" expireIn=");
+ TimeUtils.formatDuration(mExpireIn, s);
+ }
+ if (mNumUpdates != Integer.MAX_VALUE) {
+ s.append(" num=").append(mNumUpdates);
+ }
+ if (mLowPowerMode) {
+ s.append(" lowPowerMode");
+ }
+ if (mLocationSettingsIgnored) {
+ s.append(" locationSettingsIgnored");
+ }
+ s.append(']');
+ return s.toString();
+ }
+}
diff --git a/android/location/LocationTime.java b/android/location/LocationTime.java
new file mode 100644
index 0000000..e5535d1
--- /dev/null
+++ b/android/location/LocationTime.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * Data class for passing location derived time.
+ * @hide
+ */
+public final class LocationTime implements Parcelable {
+
+ private final long mTime;
+ private final long mElapsedRealtimeNanos;
+
+ public LocationTime(long time, long elapsedRealtimeNanos) {
+ mTime = time;
+ mElapsedRealtimeNanos = elapsedRealtimeNanos;
+ }
+
+ /**
+ * The current time, according to the Gnss location provider. */
+ public long getTime() {
+ return mTime;
+ }
+
+ /**
+ * The elapsed nanos since boot {@link #getTime} was computed at.
+ */
+ public long getElapsedRealtimeNanos() {
+ return mElapsedRealtimeNanos;
+ }
+
+ @Override
+ public void writeToParcel(Parcel out, int flags) {
+ out.writeLong(mTime);
+ out.writeLong(mElapsedRealtimeNanos);
+ }
+
+ @Override
+ public int describeContents() {
+ return 0;
+ }
+
+ public static final @NonNull Parcelable.Creator<LocationTime> CREATOR =
+ new Parcelable.Creator<LocationTime>() {
+ public LocationTime createFromParcel(Parcel in) {
+ long time = in.readLong();
+ long elapsedRealtimeNanos = in.readLong();
+ return new LocationTime(time, elapsedRealtimeNanos);
+ }
+
+ public LocationTime[] newArray(int size) {
+ return new LocationTime[size];
+ }
+ };
+}
diff --git a/android/location/OnNmeaMessageListener.java b/android/location/OnNmeaMessageListener.java
new file mode 100644
index 0000000..05647bc
--- /dev/null
+++ b/android/location/OnNmeaMessageListener.java
@@ -0,0 +1,36 @@
+/*
+ * 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 Date and time of the location fix, as reported by the GNSS
+ * chipset. The value is specified in milliseconds since 0:00
+ * UTC 1 January 1970.
+ */
+ void onNmeaMessage(String message, long timestamp);
+}
diff --git a/android/location/SettingInjectorService.java b/android/location/SettingInjectorService.java
new file mode 100644
index 0000000..d6f8a7c
--- /dev/null
+++ b/android/location/SettingInjectorService.java
@@ -0,0 +1,244 @@
+/*
+ * 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.annotation.NonNull;
+import android.app.Service;
+import android.content.Context;
+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 summary (subtitle) and 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>
+ * <service android:name="com.example.android.injector.MyInjectorService" >
+ * <intent-filter>
+ * <action android:name="android.location.SettingInjectorService" />
+ * </intent-filter>
+ *
+ * <meta-data
+ * android:name="android.location.SettingInjectorService"
+ * android:resource="@xml/my_injected_location_setting" />
+ * </service>
+ * </pre>
+ * The resource file specifies the static data for the setting:
+ * <pre>
+ * <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"
+ * />
+ * </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()},
+ * {@link #onGetSummary()}, and {@link #onGetEnabled()} methods must all be fast. If any are slow,
+ * it can delay the display of settings values for other apps as well. Note further that all 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 the {@link #onGetSummary()} value should represent a summary
+ * of the state across all of the accounts and {@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 the summary for the setting (e.g., "ON" or
+ * "OFF").
+ *
+ * @hide
+ */
+ public static final String SUMMARY_KEY = "summary";
+
+ /**
+ * 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) {
+ String summary = null;
+ boolean enabled = false;
+ try {
+ summary = onGetSummary();
+ enabled = onGetEnabled();
+ } finally {
+ // If exception happens, send status anyway, so that settings injector can immediately
+ // start loading the status of the next setting. But leave the exception uncaught to
+ // crash the injector service itself.
+ sendStatus(intent, summary, enabled);
+ }
+ }
+
+ /**
+ * Send the enabled values back to the caller via the messenger encoded in the
+ * intent.
+ */
+ private void sendStatus(Intent intent, String summary, boolean enabled) {
+ Messenger messenger = intent.getParcelableExtra(MESSENGER_KEY);
+ // Bail out to avoid crashing GmsCore with incoming malicious Intent.
+ if (messenger == null) {
+ return;
+ }
+
+ Message message = Message.obtain();
+ Bundle bundle = new Bundle();
+ bundle.putString(SUMMARY_KEY, summary);
+ bundle.putBoolean(ENABLED_KEY, enabled);
+ message.setData(bundle);
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, mName + ": received " + intent + ", summary=" + summary
+ + ", enabled=" + enabled + ", sending message: " + message);
+ }
+
+ try {
+ messenger.send(message);
+ } catch (RemoteException e) {
+ Log.e(TAG, mName + ": sending dynamic status failed", e);
+ }
+ }
+
+ /**
+ * Returns the {@link android.preference.Preference#getSummary()} value (allowed to be null or
+ * empty). Should not perform unpredictably-long operations such as network access--see the
+ * running-time comments in the class-level javadoc.
+ * <p/>
+ * This method is called on KitKat, and Q+ devices.
+ *
+ * @return the {@link android.preference.Preference#getSummary()} value
+ */
+ 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();
+
+ /**
+ * Sends a broadcast to refresh the injected settings on location settings page.
+ */
+ public static final void refreshSettings(@NonNull Context context) {
+ Intent intent = new Intent(ACTION_INJECTED_SETTING_CHANGED);
+ context.sendBroadcast(intent);
+ }
+}