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
+     * &amp; drift models.
+     *
+     * <p>Each time this value changes, vs. the value in a previously reported {@link GnssClock},
+     * that suggests the hardware clock may have experienced a discontinuity (e.g. a power cycle or
+     * other anomaly), so that any assumptions about modelling a smoothly changing
+     * {@code (FullBiasNanos + BiasNanos)} offset, and a smoothly growing {@code (TimeNanos)}
+     * between this and the previously reported {@code GnssClock}, should be reset.
+     */
+    public int getHardwareClockDiscontinuityCount() {
+        return mHardwareClockDiscontinuityCount;
+    }
+
+    /**
+     * Sets count of last hardware clock discontinuity.
+     * @hide
+     */
+    @TestApi
+    public void setHardwareClockDiscontinuityCount(int value) {
+        mHardwareClockDiscontinuityCount = value;
+    }
+
+    public static final @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 &nbsp;(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 &amp; BeiDou D2, the submessage id corresponds to the subframe
+     * number of the navigation message, in the range of 1-5.</li>
+     * <li>For Glonass L1 C/A, this refers to the String number, in the range from 1-15</li>
+     * <li>For Galileo F/NAV, this refers to the page type in the range 1-6</li>
+     * <li>For Galileo I/NAV, this refers to the word type in the range 1-10+</li>
+     * <li>For Galileo in particular, the type information embedded within the data bits may be even
+     * more useful in interpretation, than the nominal page and word types provided in this
+     * field.</li>
+     * <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 &amp; Beidou D2, each subframe contains 10 30-bit words. Each
+     * word (30 bits) should be fit into the last 30 bits in a 4-byte word (skip B31 and B32), with
+     * MSB first, for a total of 40 bytes, covering a time period of 6, 6, and 0.6 seconds,
+     * respectively.</li>
+     * <li>For Glonass L1 C/A, each string contains 85 data bits, including the checksum.  These
+     * bits should be fit into 11 bytes, with MSB first (skip B86-B88), covering a time period of 2
+     * seconds.</li>
+     * <li>For Galileo F/NAV, each word consists of 238-bit (sync &amp; tail symbols excluded). Each
+     * word should be fit into 30-bytes, with MSB first (skip B239, B240), covering a time period of
+     * 10 seconds.</li>
+     * <li>For Galileo I/NAV, each page contains 2 page parts, even and odd, with a total of 2x114 =
+     * 228 bits, (sync &amp; tail excluded) that should be fit into 29 bytes, with MSB first (skip
+     * B229-B232).</li>
+     * <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 &amp; OSN number for
+     * Glonass.
+     *
+     * <p>The distinction is made by looking at constellation field
+     * {@link #getConstellationType(int)} Expected values are in the range of:
+     *
+     * <ul>
+     * <li>GPS: 1-32</li>
+     * <li>SBAS: 120-151, 183-192</li>
+     * <li>GLONASS: One of: OSN, or FCN+100
+     * <ul>
+     * <li>1-24 as the orbital slot number (OSN) (preferred, if known)</li>
+     * <li>93-106 as the frequency channel number (FCN) (-7 to +6) plus 100.
+     * i.e. encode FCN of -7 as 93, 0 as 100, and +6 as 106</li>
+     * </ul></li>
+     * <li>QZSS: 193-200</li>
+     * <li>Galileo: 1-36</li>
+     * <li>Beidou: 1-37</li>
+     * <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>
+     *     &lt;receiver android:name="com.example.android.footer.MyFooterInjector"&gt;
+     *         &lt;intent-filter&gt;
+     *             &lt;action android:name="com.android.settings.location.INJECT_FOOTER" /&gt;
+     *         &lt;/intent-filter&gt;
+     *         &lt;meta-data
+     *             android:name="com.android.settings.location.FOOTER_STRING"
+     *             android:resource="@string/my_injected_footer_string" /&gt;
+     *     &lt;/receiver&gt;
+     * </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>
+ *     &lt;service android:name="com.example.android.injector.MyInjectorService" &gt;
+ *         &lt;intent-filter&gt;
+ *             &lt;action android:name="android.location.SettingInjectorService" /&gt;
+ *         &lt;/intent-filter&gt;
+ *
+ *         &lt;meta-data
+ *             android:name="android.location.SettingInjectorService"
+ *             android:resource="@xml/my_injected_location_setting" /&gt;
+ *     &lt;/service&gt;
+ * </pre>
+ * The resource file specifies the static data for the setting:
+ * <pre>
+ *     &lt;injected-location-setting xmlns:android="http://schemas.android.com/apk/res/android"
+ *         android:title="@string/injected_setting_title"
+ *         android:icon="@drawable/ic_acme_corp"
+ *         android:settingsActivity="com.example.android.injector.MySettingActivity"
+ *     /&gt;
+ * </pre>
+ * Here:
+ * <ul>
+ * <li>title: The {@link android.preference.Preference#getTitle()} value. The title should make
+ * it clear which apps are affected by the setting, typically by including the name of the
+ * developer. For example, "Acme Corp. ads preferences." </li>
+ *
+ * <li>icon: The {@link android.preference.Preference#getIcon()} value. Typically this will be a
+ * generic icon for the developer rather than the icon for an individual app.</li>
+ *
+ * <li>settingsActivity: the activity which is launched to allow the user to modify the setting
+ * value.  The activity must be in the same package as the subclass of
+ * {@link SettingInjectorService}. The activity should use your own branding to help emphasize
+ * to the user that it is not part of the system settings.</li>
+ * </ul>
+ *
+ * To ensure a good user experience, your {@link android.app.Application#onCreate()},
+ * {@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);
+    }
+}