Merge "Add GnssAntennaInfo framework APIs"
diff --git a/api/current.txt b/api/current.txt
index 475506a..d004273 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23477,6 +23477,69 @@
     method public static boolean isPresent();
   }
 
+  public final class GnssAntennaInfo implements android.os.Parcelable {
+    ctor public GnssAntennaInfo(double, @NonNull android.location.GnssAntennaInfo.PhaseCenterOffsetCoordinates, @Nullable android.location.GnssAntennaInfo.PhaseCenterVariationCorrections, @Nullable android.location.GnssAntennaInfo.SignalGainCorrections);
+    method public int describeContents();
+    method public double getCarrierFrequencyMHz();
+    method @NonNull public android.location.GnssAntennaInfo.PhaseCenterOffsetCoordinates getPhaseCenterOffsetCoordinates();
+    method @Nullable public android.location.GnssAntennaInfo.PhaseCenterVariationCorrections getPhaseCenterVariationCorrections();
+    method @Nullable public android.location.GnssAntennaInfo.SignalGainCorrections getSignalGainCorrections();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo> CREATOR;
+  }
+
+  public abstract static class GnssAntennaInfo.Callback {
+    ctor public GnssAntennaInfo.Callback();
+    method public void onGnssAntennaInfoReceived(@NonNull java.util.List<android.location.GnssAntennaInfo>);
+    method public void onStatusChanged(int);
+    field public static final int STATUS_LOCATION_DISABLED = 2; // 0x2
+    field public static final int STATUS_NOT_SUPPORTED = 0; // 0x0
+    field public static final int STATUS_READY = 1; // 0x1
+  }
+
+  public static final class GnssAntennaInfo.PhaseCenterOffsetCoordinates implements android.os.Parcelable {
+    ctor public GnssAntennaInfo.PhaseCenterOffsetCoordinates(double, double, double, double, double, double);
+    method public int describeContents();
+    method public double getXCoordMillimeters();
+    method public double getXCoordUncertaintyMillimeters();
+    method public double getYCoordMillimeters();
+    method public double getYCoordUncertaintyMillimeters();
+    method public double getZCoordMillimeters();
+    method public double getZCoordUncertaintyMillimeters();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.PhaseCenterOffsetCoordinates> CREATOR;
+  }
+
+  public static final class GnssAntennaInfo.PhaseCenterVariationCorrections implements android.os.Parcelable {
+    ctor public GnssAntennaInfo.PhaseCenterVariationCorrections(@NonNull double[][], @NonNull double[][]);
+    method public int describeContents();
+    method public double getDeltaPhi();
+    method public double getDeltaTheta();
+    method public int getNumColumns();
+    method public int getNumRows();
+    method public double getPhaseCenterVariationCorrectionMillimetersAt(int, int);
+    method public double getPhaseCenterVariationCorrectionUncertaintyMillimetersAt(int, int);
+    method @NonNull public double[][] getRawCorrectionUncertaintiesArray();
+    method @NonNull public double[][] getRawCorrectionsArray();
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.PhaseCenterVariationCorrections> CREATOR;
+  }
+
+  public static final class GnssAntennaInfo.SignalGainCorrections implements android.os.Parcelable {
+    ctor public GnssAntennaInfo.SignalGainCorrections(@NonNull double[][], @NonNull double[][]);
+    method public int describeContents();
+    method public double getDeltaPhi();
+    method public double getDeltaTheta();
+    method public int getNumColumns();
+    method public int getNumRows();
+    method @NonNull public double[][] getRawCorrectionUncertaintiesArray();
+    method @NonNull public double[][] getRawCorrectionsArray();
+    method public double getSignalGainCorrectionDbiAt(int, int);
+    method public double getSignalGainCorrectionUncertaintyDbiAt(int, int);
+    method public void writeToParcel(@NonNull android.os.Parcel, int);
+    field @NonNull public static final android.os.Parcelable.Creator<android.location.GnssAntennaInfo.SignalGainCorrections> CREATOR;
+  }
+
   public final class GnssClock implements android.os.Parcelable {
     method public int describeContents();
     method public double getBiasNanos();
@@ -23794,6 +23857,7 @@
     method @NonNull public java.util.List<java.lang.String> getProviders(@NonNull android.location.Criteria, boolean);
     method public boolean isLocationEnabled();
     method public boolean isProviderEnabled(@NonNull String);
+    method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerAntennaInfoCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssAntennaInfo.Callback);
     method @Deprecated @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback, @Nullable android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.ACCESS_FINE_LOCATION) public boolean registerGnssMeasurementsCallback(@NonNull java.util.concurrent.Executor, @NonNull android.location.GnssMeasurementsEvent.Callback);
@@ -23825,6 +23889,7 @@
     method public void setTestProviderEnabled(@NonNull String, boolean);
     method public void setTestProviderLocation(@NonNull String, @NonNull android.location.Location);
     method @Deprecated public void setTestProviderStatus(@NonNull String, int, @Nullable android.os.Bundle, long);
+    method public void unregisterAntennaInfoCallback(@NonNull android.location.GnssAntennaInfo.Callback);
     method public void unregisterGnssMeasurementsCallback(@NonNull android.location.GnssMeasurementsEvent.Callback);
     method public void unregisterGnssNavigationMessageCallback(@NonNull android.location.GnssNavigationMessage.Callback);
     method public void unregisterGnssStatusCallback(@NonNull android.location.GnssStatus.Callback);
diff --git a/api/system-current.txt b/api/system-current.txt
index a722db9..157e351 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3800,6 +3800,7 @@
 
   public final class GnssCapabilities {
     method public boolean hasGeofencing();
+    method public boolean hasGnssAntennaInfo();
     method public boolean hasLowPowerMode();
     method public boolean hasMeasurementCorrections();
     method public boolean hasMeasurementCorrectionsExcessPathLength();
diff --git a/location/java/android/location/GnssAntennaInfo.aidl b/location/java/android/location/GnssAntennaInfo.aidl
new file mode 100644
index 0000000..2b956af
--- /dev/null
+++ b/location/java/android/location/GnssAntennaInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * 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;
+
+parcelable GnssAntennaInfo;
\ No newline at end of file
diff --git a/location/java/android/location/GnssAntennaInfo.java b/location/java/android/location/GnssAntennaInfo.java
new file mode 100644
index 0000000..dfcaf81
--- /dev/null
+++ b/location/java/android/location/GnssAntennaInfo.java
@@ -0,0 +1,654 @@
+/*
+ * 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.IntDef;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.List;
+
+/**
+ * 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 Callback}.
+ */
+public final class GnssAntennaInfo implements Parcelable {
+    private final double mCarrierFrequencyMHz;
+    private final PhaseCenterOffsetCoordinates mPhaseCenterOffsetCoordinates;
+    private final PhaseCenterVariationCorrections mPhaseCenterVariationCorrections;
+    private final SignalGainCorrections mSignalGainCorrections;
+
+    /**
+     * Used for receiving GNSS antenna info from the GNSS engine. You can implement this interface
+     * and call {@link LocationManager#registerAntennaInfoCallback};
+     */
+    public abstract static class Callback {
+        /**
+         * The status of GNSS antenna info.
+         *
+         * @hide
+         */
+        @Retention(RetentionPolicy.SOURCE)
+        @IntDef({STATUS_NOT_SUPPORTED, STATUS_READY, STATUS_LOCATION_DISABLED})
+        public @interface GnssAntennaInfoStatus {
+        }
+
+        /**
+         * The system does not support GNSS antenna info.
+         *
+         * This status will not change in the future.
+         */
+        public static final int STATUS_NOT_SUPPORTED = 0;
+
+        /**
+         * GNSS antenna info updates are being successfully tracked.
+         */
+        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 GNSS antenna info. This event is triggered when a callback is
+         * registered, and whenever the antenna info changes (due to a device configuration change).
+         */
+        public void onGnssAntennaInfoReceived(@NonNull List<GnssAntennaInfo> gnssAntennaInfos) {}
+
+        /**
+         * Returns the latest status of the GNSS antenna info sub-system.
+         */
+        public void onStatusChanged(@GnssAntennaInfoStatus int status) {}
+    }
+
+    /**
+     * 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 PhaseCenterOffsetCoordinates implements Parcelable {
+        private final double mPhaseCenterOffsetCoordinateXMillimeters;
+        private final double mPhaseCenterOffsetCoordinateXUncertaintyMillimeters;
+        private final double mPhaseCenterOffsetCoordinateYMillimeters;
+        private final double mPhaseCenterOffsetCoordinateYUncertaintyMillimeters;
+        private final double mPhaseCenterOffsetCoordinateZMillimeters;
+        private final double mPhaseCenterOffsetCoordinateZUncertaintyMillimeters;
+
+        @VisibleForTesting
+        public PhaseCenterOffsetCoordinates(double phaseCenterOffsetCoordinateXMillimeters,
+                double phaseCenterOffsetCoordinateXUncertaintyMillimeters,
+                double phaseCenterOffsetCoordinateYMillimeters,
+                double phaseCenterOffsetCoordinateYUncertaintyMillimeters,
+                double phaseCenterOffsetCoordinateZMillimeters,
+                double phaseCenterOffsetCoordinateZUncertaintyMillimeters) {
+            mPhaseCenterOffsetCoordinateXMillimeters = phaseCenterOffsetCoordinateXMillimeters;
+            mPhaseCenterOffsetCoordinateYMillimeters = phaseCenterOffsetCoordinateYMillimeters;
+            mPhaseCenterOffsetCoordinateZMillimeters = phaseCenterOffsetCoordinateZMillimeters;
+            mPhaseCenterOffsetCoordinateXUncertaintyMillimeters =
+                    phaseCenterOffsetCoordinateXUncertaintyMillimeters;
+            mPhaseCenterOffsetCoordinateYUncertaintyMillimeters =
+                    phaseCenterOffsetCoordinateYUncertaintyMillimeters;
+            mPhaseCenterOffsetCoordinateZUncertaintyMillimeters =
+                    phaseCenterOffsetCoordinateZUncertaintyMillimeters;
+        }
+
+        public static final @NonNull Creator<PhaseCenterOffsetCoordinates> CREATOR =
+                new Creator<PhaseCenterOffsetCoordinates>() {
+                    @Override
+                    public PhaseCenterOffsetCoordinates createFromParcel(Parcel in) {
+                        return new PhaseCenterOffsetCoordinates(
+                                in.readDouble(),
+                                in.readDouble(),
+                                in.readDouble(),
+                                in.readDouble(),
+                                in.readDouble(),
+                                in.readDouble()
+                        );
+                    }
+
+                    @Override
+                    public PhaseCenterOffsetCoordinates[] newArray(int size) {
+                        return new PhaseCenterOffsetCoordinates[size];
+                    }
+                };
+
+        public double getXCoordMillimeters() {
+            return mPhaseCenterOffsetCoordinateXMillimeters;
+        }
+
+        public double getXCoordUncertaintyMillimeters() {
+            return mPhaseCenterOffsetCoordinateXUncertaintyMillimeters;
+        }
+
+        public double getYCoordMillimeters() {
+            return mPhaseCenterOffsetCoordinateYMillimeters;
+        }
+
+        public double getYCoordUncertaintyMillimeters() {
+            return mPhaseCenterOffsetCoordinateYUncertaintyMillimeters;
+        }
+
+        public double getZCoordMillimeters() {
+            return mPhaseCenterOffsetCoordinateZMillimeters;
+        }
+
+        public double getZCoordUncertaintyMillimeters() {
+            return mPhaseCenterOffsetCoordinateZUncertaintyMillimeters;
+        }
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            dest.writeDouble(mPhaseCenterOffsetCoordinateXMillimeters);
+            dest.writeDouble(mPhaseCenterOffsetCoordinateXUncertaintyMillimeters);
+            dest.writeDouble(mPhaseCenterOffsetCoordinateYMillimeters);
+            dest.writeDouble(mPhaseCenterOffsetCoordinateYUncertaintyMillimeters);
+            dest.writeDouble(mPhaseCenterOffsetCoordinateZMillimeters);
+            dest.writeDouble(mPhaseCenterOffsetCoordinateZUncertaintyMillimeters);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder("PhaseCenteroffset:\n");
+            builder.append("X: " + mPhaseCenterOffsetCoordinateXMillimeters + " +/- "
+                    + mPhaseCenterOffsetCoordinateXUncertaintyMillimeters + "\n");
+            builder.append("Y: " + mPhaseCenterOffsetCoordinateYMillimeters + " +/- "
+                    + mPhaseCenterOffsetCoordinateYUncertaintyMillimeters + "\n");
+            builder.append("Z: " + mPhaseCenterOffsetCoordinateZMillimeters + " +/- "
+                    + mPhaseCenterOffsetCoordinateZUncertaintyMillimeters + "\n");
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Class containing information about the phase center variation (PCV) corrections. The PCV
+     * correction is added to the phase measurement to obtain the corrected value.
+     *
+     * 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 PhaseCenterVariationCorrections extends SphericalCorrections {
+
+        @VisibleForTesting
+        public PhaseCenterVariationCorrections(
+                @NonNull double[][] phaseCenterVariationCorrectionsMillimeters,
+                @NonNull double[][] phaseCenterVariationCorrectionUncertaintiesMillimeters) {
+            super(phaseCenterVariationCorrectionsMillimeters,
+                    phaseCenterVariationCorrectionUncertaintiesMillimeters);
+        }
+
+        private PhaseCenterVariationCorrections(@NonNull Parcel in) {
+            super(in);
+        }
+
+        /**
+         * Get the phase center variation correction in millimeters at the specified row and column
+         * in the underlying 2D array.
+         * @param row zero-based major index in the array
+         * @param column zero-based minor index in the array
+         * @return phase center correction in millimeters
+         */
+        public double getPhaseCenterVariationCorrectionMillimetersAt(int row, int column) {
+            return super.getCorrectionAt(row, column);
+        }
+
+        /**
+         * Get the phase center variation correction uncertainty in millimeters at the specified row
+         * and column in the underlying 2D array.
+         * @param row zero-based major index in the array
+         * @param column zero-based minor index in the array
+         * @return 1-sigma phase center correction uncertainty in millimeters
+         */
+        public double getPhaseCenterVariationCorrectionUncertaintyMillimetersAt(
+                int row, int column) {
+            return super.getCorrectionUncertaintyAt(row, column);
+        }
+
+        public @NonNull double[][] getRawCorrectionsArray() {
+            return super.getRawCorrectionsArray().clone();
+        }
+
+        public @NonNull double[][] getRawCorrectionUncertaintiesArray() {
+            return super.getRawCorrectionUncertaintiesArray().clone();
+        }
+
+        public int getNumRows() {
+            return super.getNumRows();
+        }
+
+        public int getNumColumns() {
+            return super.getNumColumns();
+        }
+
+        /**
+         * The fixed theta angle separation between successive rows.
+         */
+        public double getDeltaTheta() {
+            return super.getDeltaTheta();
+        }
+
+        /**
+         * The fixed phi angle separation between successive columns.
+         */
+        public double getDeltaPhi() {
+            return super.getDeltaPhi();
+        }
+
+        public static final @NonNull Creator<PhaseCenterVariationCorrections> CREATOR =
+                new Creator<PhaseCenterVariationCorrections>() {
+                    @Override
+                    public PhaseCenterVariationCorrections createFromParcel(Parcel in) {
+                        return new PhaseCenterVariationCorrections(in);
+                    }
+
+                    @Override
+                    public PhaseCenterVariationCorrections[] newArray(int size) {
+                        return new PhaseCenterVariationCorrections[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder("PhaseCenterVariationCorrections:\n");
+            builder.append(super.toString());
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Class containing information about the signal gain (SG) corrections. The SG
+     * correction is added to the signal gain to obtain the corrected value.
+     *
+     * 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 SignalGainCorrections extends SphericalCorrections {
+
+        @VisibleForTesting
+        public SignalGainCorrections(
+                @NonNull double[][] signalGainCorrectionsDbi,
+                @NonNull double[][] signalGainCorrectionUncertaintiesDbi) {
+            super(signalGainCorrectionsDbi,
+                    signalGainCorrectionUncertaintiesDbi);
+        }
+
+        private SignalGainCorrections(@NonNull Parcel in) {
+            super(in);
+        }
+
+        /**
+         * Get the signal gain correction in dbi at the specified row and column
+         * in the underlying 2D array.
+         * @param row zero-based major index in the array
+         * @param column zero-based minor index in the array
+         * @return signal gain correction in dbi
+         */
+        public double getSignalGainCorrectionDbiAt(int row, int column) {
+            return super.getCorrectionAt(row, column);
+        }
+
+        /**
+         * Get the signal gain correction correction uncertainty in dbi at the specified row
+         * and column in the underlying 2D array.
+         * @param row zero-based major index in the array
+         * @param column zero-based minor index in the array
+         * @return 1-sigma signal gain correction uncertainty in dbi
+         */
+        public double getSignalGainCorrectionUncertaintyDbiAt(int row, int column) {
+            return super.getCorrectionUncertaintyAt(row, column);
+        }
+
+        public @NonNull double[][] getRawCorrectionsArray() {
+            return super.getRawCorrectionsArray().clone();
+        }
+
+        public @NonNull double[][] getRawCorrectionUncertaintiesArray() {
+            return super.getRawCorrectionUncertaintiesArray().clone();
+        }
+
+        public int getNumRows() {
+            return super.getNumRows();
+        }
+
+        public int getNumColumns() {
+            return super.getNumColumns();
+        }
+
+        /**
+         * The fixed theta angle separation between successive rows.
+         */
+        public double getDeltaTheta() {
+            return super.getDeltaTheta();
+        }
+
+        /**
+         * The fixed phi angle separation between successive columns.
+         */
+        public double getDeltaPhi() {
+            return super.getDeltaPhi();
+        }
+
+        public static final @NonNull Creator<SignalGainCorrections> CREATOR =
+                new Creator<SignalGainCorrections>() {
+                    @Override
+                    public SignalGainCorrections createFromParcel(Parcel in) {
+                        return new SignalGainCorrections(in);
+                    }
+
+                    @Override
+                    public SignalGainCorrections[] newArray(int size) {
+                        return new SignalGainCorrections[size];
+                    }
+                };
+
+        @Override
+        public int describeContents() {
+            return 0;
+        }
+
+        @Override
+        public void writeToParcel(@NonNull Parcel dest, int flags) {
+            super.writeToParcel(dest, flags);
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder("SignalGainCorrections:\n");
+            builder.append(super.toString());
+            return builder.toString();
+        }
+    }
+
+    /**
+     * Represents 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).
+     */
+    private abstract static 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;
+
+        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 = corrections.length;
+            mNumColumns = corrections[0].length;
+            mCorrections = corrections;
+            mCorrectionUncertainties = correctionUncertainties;
+            mDeltaTheta = 360.0d / mNumRows;
+            mDeltaPhi = 180.0d / (mNumColumns - 1);
+        }
+
+        private double getCorrectionAt(int row, int column) {
+            return mCorrections[row][column];
+        }
+
+        private double getCorrectionUncertaintyAt(int row, int column) {
+            return mCorrectionUncertainties[row][column];
+        }
+
+        @NonNull
+        private double[][] getRawCorrectionsArray() {
+            return mCorrections;
+        }
+
+        @NonNull
+        private double[][] getRawCorrectionUncertaintiesArray() {
+            return mCorrectionUncertainties;
+        }
+
+        private int getNumRows() {
+            return mNumRows;
+        }
+
+        private int getNumColumns() {
+            return mNumColumns;
+        }
+
+        /**
+         * The fixed theta angle separation between successive rows.
+         */
+        private double getDeltaTheta() {
+            return mDeltaTheta;
+        }
+
+        /**
+         * The fixed phi angle separation between successive columns.
+         */
+        private double getDeltaPhi() {
+            return mDeltaPhi;
+        }
+
+        @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);
+            }
+        }
+
+        private String arrayToString(double[][] array) {
+            StringBuilder builder = new StringBuilder();
+            for (int row = 0; row < mNumRows; row++) {
+                builder.append("[ ");
+                for (int column = 0; column < mNumColumns - 1; column++) {
+                    builder.append(array[row][column] + ", ");
+                }
+                builder.append(array[row][mNumColumns - 1] + " ]\n");
+            }
+            return builder.toString();
+        }
+
+        @Override
+        public String toString() {
+            StringBuilder builder = new StringBuilder();
+            builder.append("DeltaTheta: " + mDeltaTheta + "\n");
+            builder.append("DeltaPhi: " + mDeltaPhi + "\n");
+            builder.append("CorrectionsArray:\n");
+            builder.append(arrayToString(mCorrections));
+            builder.append("CorrectionUncertaintiesArray:\n");
+            builder.append(arrayToString(mCorrectionUncertainties));
+            return builder.toString();
+        }
+    }
+
+    @VisibleForTesting
+    public GnssAntennaInfo(
+            double carrierFrequencyMHz,
+            @NonNull PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates,
+            @Nullable PhaseCenterVariationCorrections phaseCenterVariationCorrections,
+            @Nullable SignalGainCorrections signalGainCorrectionDbi) {
+        if (phaseCenterOffsetCoordinates == null) {
+            throw new IllegalArgumentException("Phase Center Offset Coordinates cannot be null.");
+        }
+        mCarrierFrequencyMHz = carrierFrequencyMHz;
+        mPhaseCenterOffsetCoordinates = phaseCenterOffsetCoordinates;
+        mPhaseCenterVariationCorrections = phaseCenterVariationCorrections;
+        mSignalGainCorrections = signalGainCorrectionDbi;
+    }
+
+    public double getCarrierFrequencyMHz() {
+        return mCarrierFrequencyMHz;
+    }
+
+    @NonNull
+    public PhaseCenterOffsetCoordinates getPhaseCenterOffsetCoordinates() {
+        return mPhaseCenterOffsetCoordinates;
+    }
+
+    @Nullable
+    public PhaseCenterVariationCorrections getPhaseCenterVariationCorrections() {
+        return mPhaseCenterVariationCorrections;
+    }
+
+    @Nullable
+    public SignalGainCorrections 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();
+                                PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates =
+                                        in.readParcelable(classLoader);
+                                PhaseCenterVariationCorrections phaseCenterVariationCorrections =
+                                        in.readParcelable(classLoader);
+                                SignalGainCorrections signalGainCorrections =
+                                        in.readParcelable(classLoader);
+
+                                return new GnssAntennaInfo(carrierFrequencyMHz,
+                                        phaseCenterOffsetCoordinates,
+                                        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);
+
+        // Write Phase Center Offset
+        parcel.writeParcelable(mPhaseCenterOffsetCoordinates, flags);
+
+        // Write Phase Center Variation Corrections
+        parcel.writeParcelable(mPhaseCenterVariationCorrections, flags);
+
+        // Write Signal Gain Corrections
+        parcel.writeParcelable(mSignalGainCorrections, flags);
+    }
+
+    @Override
+    public String toString() {
+        StringBuilder builder = new StringBuilder("[ GnssAntennaInfo:\n");
+        builder.append("CarrierFrequencyMHz: " + mCarrierFrequencyMHz + "\n");
+        builder.append(mPhaseCenterOffsetCoordinates.toString());
+        builder.append(mPhaseCenterVariationCorrections == null
+                ? "PhaseCenterVariationCorrections: null\n"
+                : mPhaseCenterVariationCorrections.toString());
+        builder.append(mSignalGainCorrections == null
+                ? "SignalGainCorrections: null\n"
+                : mSignalGainCorrections.toString());
+        builder.append("]");
+        return builder.toString();
+    }
+}
diff --git a/location/java/android/location/GnssCapabilities.java b/location/java/android/location/GnssCapabilities.java
index 2e2f984..930180c 100644
--- a/location/java/android/location/GnssCapabilities.java
+++ b/location/java/android/location/GnssCapabilities.java
@@ -82,6 +82,12 @@
      */
     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;
 
@@ -165,6 +171,13 @@
         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);
+    }
+
     @NonNull
     @Override
     public String toString() {
@@ -172,6 +185,7 @@
         if (hasLowPowerMode()) sb.append("LOW_POWER_MODE ");
         if (hasSatelliteBlacklist()) sb.append("SATELLITE_BLACKLIST ");
         if (hasGeofencing()) sb.append("GEOFENCING ");
+        if (hasGnssAntennaInfo()) sb.append("ANTENNA_INFO ");
         if (hasMeasurements()) sb.append("MEASUREMENTS ");
         if (hasNavMessages()) sb.append("NAV_MESSAGES ");
         if (hasMeasurementCorrections()) sb.append("MEASUREMENT_CORRECTIONS ");
diff --git a/location/java/android/location/IGnssAntennaInfoListener.aidl b/location/java/android/location/IGnssAntennaInfoListener.aidl
new file mode 100644
index 0000000..30bf5467
--- /dev/null
+++ b/location/java/android/location/IGnssAntennaInfoListener.aidl
@@ -0,0 +1,27 @@
+/*
+ * 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.location.GnssAntennaInfo;
+
+/**
+ * {@hide}
+ */
+oneway interface IGnssAntennaInfoListener {
+    void onGnssAntennaInfoReceived(in List<GnssAntennaInfo> gnssAntennaInfo);
+    void onStatusChanged(in int status);
+}
\ No newline at end of file
diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl
index a99e68f..3172d7e 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -24,6 +24,7 @@
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssRequest;
 import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssStatusListener;
 import android.location.IGnssNavigationMessageListener;
@@ -79,6 +80,10 @@
     long getGnssCapabilities(in String packageName);
     void removeGnssMeasurementsListener(in IGnssMeasurementsListener listener);
 
+    boolean addGnssAntennaInfoListener(in IGnssAntennaInfoListener listener,
+             String packageName, String featureId, String listenerIdentifier);
+    void removeGnssAntennaInfoListener(in IGnssAntennaInfoListener listener);
+
     boolean addGnssNavigationMessageListener(in IGnssNavigationMessageListener listener,
              String packageName, String featureId, String listenerIdentifier);
     void removeGnssNavigationMessageListener(in IGnssNavigationMessageListener listener);
diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java
index 8ae967f..4462433 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -310,6 +310,8 @@
             new GnssMeasurementsListenerManager();
     private final GnssNavigationMessageListenerManager mGnssNavigationMessageListenerTransport =
             new GnssNavigationMessageListenerManager();
+    private final GnssAntennaInfoListenerManager mGnssAntennaInfoListenerManager =
+            new GnssAntennaInfoListenerManager();
 
     /**
      * @hide
@@ -2260,6 +2262,41 @@
     }
 
     /**
+     * Registers a Gnss Antenna Info callback.
+     *
+     * @param executor the executor that the callback runs on.
+     * @param callback a {@link GnssAntennaInfo.Callback} object to register.
+     * @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 registerAntennaInfoCallback(
+            @NonNull @CallbackExecutor Executor executor,
+            @NonNull GnssAntennaInfo.Callback callback) {
+        try {
+            return mGnssAntennaInfoListenerManager.addListener(callback, executor);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
+     * Unregisters a GNSS Antenna Info callback.
+     *
+     * @param callback a {@link GnssAntennaInfo.Callback} object to remove.
+     */
+    public void unregisterAntennaInfoCallback(@NonNull GnssAntennaInfo.Callback callback) {
+        try {
+            mGnssAntennaInfoListenerManager.removeListener(callback);
+        } catch (RemoteException e) {
+            throw e.rethrowFromSystemServer();
+        }
+    }
+
+    /**
      * No-op method to keep backward-compatibility.
      *
      * @hide
@@ -2988,6 +3025,48 @@
         }
     }
 
+    private class GnssAntennaInfoListenerManager extends
+            AbstractListenerManager<GnssAntennaInfo.Callback> {
+
+        @Nullable
+        private IGnssAntennaInfoListener mListenerTransport;
+
+        @Override
+        protected boolean registerService() throws RemoteException {
+            Preconditions.checkState(mListenerTransport == null);
+
+            GnssAntennaInfoListener transport = new GnssAntennaInfoListener();
+            if (mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(),
+                    mContext.getFeatureId(), "gnss antenna info callback")) {
+                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));
+            }
+
+            @Override
+            public void onStatusChanged(int status) {
+                execute((listener) -> listener.onStatusChanged(status));
+            }
+        }
+
+    }
+
     private class BatchedLocationCallbackManager extends
             AbstractListenerManager<Void, BatchedLocationCallback> {
 
@@ -3000,7 +3079,7 @@
 
             BatchedLocationCallback transport = new BatchedLocationCallback();
             if (mService.addGnssBatchingCallback(transport, mContext.getPackageName(),
-                     mContext.getFeatureId(), "batched location callback")) {
+                    mContext.getFeatureId(), "batched location callback")) {
                 mListenerTransport = transport;
                 return true;
             } else {
diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java
index 2c91a11..420064a 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -45,6 +45,7 @@
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssRequest;
 import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
 import android.location.IGnssStatusListener;
@@ -2322,6 +2323,22 @@
     }
 
     @Override
+    public boolean addGnssAntennaInfoListener(IGnssAntennaInfoListener listener,
+            String packageName, String featureId, String listenerIdentifier) {
+        Objects.requireNonNull(listenerIdentifier);
+
+        return mGnssManagerService != null && mGnssManagerService.addGnssAntennaInfoListener(
+                listener, packageName, featureId, listenerIdentifier);
+    }
+
+    @Override
+    public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) {
+        if (mGnssManagerService != null) {
+            mGnssManagerService.removeGnssAntennaInfoListener(listener);
+        }
+    }
+
+    @Override
     public boolean addGnssNavigationMessageListener(IGnssNavigationMessageListener listener,
             String packageName, String featureId, String listenerIdentifier) {
         Objects.requireNonNull(listenerIdentifier);
diff --git a/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java b/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java
new file mode 100644
index 0000000..674389f
--- /dev/null
+++ b/services/core/java/com/android/server/location/GnssAntennaInfoProvider.java
@@ -0,0 +1,178 @@
+/*
+ * 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 com.android.server.location;
+
+import android.content.Context;
+import android.location.GnssAntennaInfo;
+import android.location.IGnssAntennaInfoListener;
+import android.os.Handler;
+import android.os.RemoteException;
+import android.util.Log;
+
+import com.android.internal.annotations.VisibleForTesting;
+
+import java.util.List;
+
+/**
+ * An base implementation for GNSS antenna info provider. It abstracts out the responsibility of
+ * handling listeners, while still allowing technology specific implementations to be built.
+ *
+ * @hide
+ */
+public abstract class GnssAntennaInfoProvider
+        extends RemoteListenerHelper<IGnssAntennaInfoListener> {
+    private static final String TAG = "GnssAntennaInfoProvider";
+    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);
+
+    private final GnssAntennaInfoProviderNative mNative;
+
+    private boolean mIsListeningStarted;
+
+    protected GnssAntennaInfoProvider(Context context, Handler handler) {
+        this(context, handler, new GnssAntennaInfoProviderNative());
+    }
+
+    @VisibleForTesting
+    public GnssAntennaInfoProvider(
+            Context context, Handler handler, GnssAntennaInfoProviderNative aNative) {
+        super(context, handler, TAG);
+        mNative = aNative;
+    }
+
+    void resumeIfStarted() {
+        if (DEBUG) {
+            Log.d(TAG, "resumeIfStarted");
+        }
+        if (mIsListeningStarted) {
+            mNative.startAntennaInfoListening();
+        }
+    }
+
+
+    @Override
+    public boolean isAvailableInPlatform() {
+        return mNative.isAntennaInfoSupported();
+    }
+
+    @Override
+    protected int registerWithService() {
+        boolean started = mNative.startAntennaInfoListening();
+        if (started) {
+            mIsListeningStarted = true;
+            return RemoteListenerHelper.RESULT_SUCCESS;
+        }
+        return RemoteListenerHelper.RESULT_INTERNAL_ERROR;
+    }
+
+    @Override
+    protected void unregisterFromService() {
+        boolean stopped = mNative.stopAntennaInfoListening();
+        if (stopped) {
+            mIsListeningStarted = false;
+        }
+    }
+
+    /** Handle GNSS capabilities update from the GNSS HAL implementation. */
+    public void onCapabilitiesUpdated(boolean isAntennaInfoSupported) {
+        setSupported(isAntennaInfoSupported);
+        updateResult();
+    }
+
+    /** Handle GNSS enabled changes.*/
+    public void onGpsEnabledChanged() {
+        tryUpdateRegistrationWithService();
+        updateResult();
+    }
+
+    @Override
+    protected ListenerOperation<IGnssAntennaInfoListener> getHandlerOperation(int result) {
+        int status;
+        switch (result) {
+            case RESULT_SUCCESS:
+                status = GnssAntennaInfo.Callback.STATUS_READY;
+                break;
+            case RESULT_NOT_AVAILABLE:
+            case RESULT_NOT_SUPPORTED:
+            case RESULT_INTERNAL_ERROR:
+                status = GnssAntennaInfo.Callback.STATUS_NOT_SUPPORTED;
+                break;
+            case RESULT_GPS_LOCATION_DISABLED:
+                status = GnssAntennaInfo.Callback.STATUS_LOCATION_DISABLED;
+                break;
+            case RESULT_UNKNOWN:
+                return null;
+            default:
+                Log.v(TAG, "Unhandled addListener result: " + result);
+                return null;
+        }
+        return new StatusChangedOperation(status);
+    }
+
+    private static class StatusChangedOperation
+            implements ListenerOperation<IGnssAntennaInfoListener> {
+        private final int mStatus;
+
+        StatusChangedOperation(int status) {
+            mStatus = status;
+        }
+
+        @Override
+        public void execute(IGnssAntennaInfoListener listener,
+                CallerIdentity callerIdentity) throws RemoteException {
+            listener.onStatusChanged(mStatus);
+        }
+    }
+
+    /** Handle Gnss Antenna Info report. */
+    public void onGnssAntennaInfoAvailable(final List<GnssAntennaInfo> gnssAntennaInfos) {
+        foreach((IGnssAntennaInfoListener listener, CallerIdentity callerIdentity) -> {
+            if (!hasPermission(mContext, callerIdentity)) {
+                logPermissionDisabledEventNotReported(
+                        TAG, callerIdentity.mPackageName, "GNSS antenna info");
+                return;
+            }
+            listener.onGnssAntennaInfoReceived(gnssAntennaInfos);
+        });
+    }
+
+    /**
+     * Wrapper class for native methods. This is mocked for testing.
+     */
+    @VisibleForTesting
+    public static class GnssAntennaInfoProviderNative {
+
+        public boolean isAntennaInfoSupported() {
+            return native_is_antenna_info_supported();
+        }
+
+        /** Start antenna info listening. */
+        public boolean startAntennaInfoListening() {
+            return native_start_antenna_info_listening();
+        }
+
+        /** Stop antenna info listening. */
+        public boolean stopAntennaInfoListening() {
+            return native_stop_antenna_info_listening();
+        }
+    }
+
+    private static native boolean native_is_antenna_info_supported();
+
+    private static native boolean native_start_antenna_info_listening();
+
+    private static native boolean native_stop_antenna_info_listening();
+}
diff --git a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java b/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java
index 88ff6e7..5c8507f 100644
--- a/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java
+++ b/services/core/java/com/android/server/location/GnssCapabilitiesProvider.java
@@ -77,6 +77,9 @@
         if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_NAV_MESSAGES)) {
             gnssCapabilities |= GnssCapabilities.NAV_MESSAGES;
         }
+        if (hasCapability(topHalCapabilities, GnssLocationProvider.GPS_CAPABILITY_ANTENNA_INFO)) {
+            gnssCapabilities |= GnssCapabilities.ANTENNA_INFO;
+        }
 
         synchronized (this) {
             mGnssCapabilities &= ~GNSS_CAPABILITIES_TOP_HAL;
diff --git a/services/core/java/com/android/server/location/GnssLocationProvider.java b/services/core/java/com/android/server/location/GnssLocationProvider.java
index bcac473..36136f4 100644
--- a/services/core/java/com/android/server/location/GnssLocationProvider.java
+++ b/services/core/java/com/android/server/location/GnssLocationProvider.java
@@ -29,6 +29,7 @@
 import android.hardware.location.GeofenceHardwareImpl;
 import android.location.Criteria;
 import android.location.FusedBatchOptions;
+import android.location.GnssAntennaInfo;
 import android.location.GnssMeasurementsEvent;
 import android.location.GnssNavigationMessage;
 import android.location.GnssStatus;
@@ -182,6 +183,7 @@
     public static final int GPS_CAPABILITY_LOW_POWER_MODE = 0x0000100;
     public static final int GPS_CAPABILITY_SATELLITE_BLACKLIST = 0x0000200;
     public static final int GPS_CAPABILITY_MEASUREMENT_CORRECTIONS = 0x0000400;
+    public static final int GPS_CAPABILITY_ANTENNA_INFO = 0x0000800;
 
     // The AGPS SUPL mode
     private static final int AGPS_SUPL_MODE_MSA = 0x02;
@@ -397,6 +399,7 @@
     private final GnssStatusListenerHelper mGnssStatusListenerHelper;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
     private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider;
+    private final GnssAntennaInfoProvider mGnssAntennaInfoProvider;
     private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
     private final LocationChangeListener mNetworkLocationListener = new NetworkLocationListener();
     private final LocationChangeListener mFusedLocationListener = new FusedLocationListener();
@@ -469,6 +472,10 @@
         return mGnssMeasurementCorrectionsProvider;
     }
 
+    public GnssAntennaInfoProvider getGnssAntennaInfoProvider() {
+        return mGnssAntennaInfoProvider;
+    }
+
     public GnssNavigationMessageProvider getGnssNavigationMessageProvider() {
         return mGnssNavigationMessageProvider;
     }
@@ -693,6 +700,13 @@
 
         mGnssMeasurementCorrectionsProvider = new GnssMeasurementCorrectionsProvider(mHandler);
 
+        mGnssAntennaInfoProvider = new GnssAntennaInfoProvider(mContext, mHandler) {
+            @Override
+            protected boolean isGpsEnabled() {
+                return GnssLocationProvider.this.isGpsEnabled();
+            }
+        };
+
         mGnssNavigationMessageProvider = new GnssNavigationMessageProvider(mContext, mHandler) {
             @Override
             protected boolean isGpsEnabled() {
@@ -992,6 +1006,7 @@
 
             mGnssMeasurementsProvider.onGpsEnabledChanged();
             mGnssNavigationMessageProvider.onGpsEnabledChanged();
+            mGnssAntennaInfoProvider.onGpsEnabledChanged();
             mGnssBatchingProvider.enable();
             if (mGnssVisibilityControl != null) {
                 mGnssVisibilityControl.onGpsEnabledChanged(/* isEnabled= */ true);
@@ -1018,6 +1033,7 @@
         // do this before releasing wakelock
         native_cleanup();
 
+        mGnssAntennaInfoProvider.onGpsEnabledChanged();
         mGnssMeasurementsProvider.onGpsEnabledChanged();
         mGnssNavigationMessageProvider.onGpsEnabledChanged();
     }
@@ -1563,6 +1579,11 @@
     }
 
     @NativeEntryPoint
+    private void reportAntennaInfo(List<GnssAntennaInfo> antennaInfos) {
+        mHandler.post(() -> mGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(antennaInfos));
+    }
+
+    @NativeEntryPoint
     private void reportNavigationMessage(GnssNavigationMessage event) {
         if (!mItarSpeedLimitExceeded) {
             // send to handler to allow native to return quickly
@@ -1585,6 +1606,8 @@
             mGnssNavigationMessageProvider.onCapabilitiesUpdated(
                     hasCapability(GPS_CAPABILITY_NAV_MESSAGES));
             restartRequests();
+            mGnssAntennaInfoProvider.onCapabilitiesUpdated(
+                    hasCapability(GPS_CAPABILITY_ANTENNA_INFO));
 
             mGnssCapabilitiesProvider.setTopHalCapabilities(mTopHalCapabilities);
         });
@@ -1606,6 +1629,7 @@
         Log.i(TAG, "restartRequests");
 
         restartLocationRequest();
+        mGnssAntennaInfoProvider.resumeIfStarted();
         mGnssMeasurementsProvider.resumeIfStarted();
         mGnssNavigationMessageProvider.resumeIfStarted();
         mGnssBatchingProvider.resumeIfStarted();
@@ -2198,6 +2222,8 @@
         s.append(" ago)").append('\n');
         s.append("mFixInterval=").append(mFixInterval).append('\n');
         s.append("mLowPowerMode=").append(mLowPowerMode).append('\n');
+        s.append("mGnssAntennaInfoProvider.isRegistered()=")
+                .append(mGnssAntennaInfoProvider.isRegistered()).append('\n');
         s.append("mGnssMeasurementsProvider.isRegistered()=")
                 .append(mGnssMeasurementsProvider.isRegistered()).append('\n');
         s.append("mGnssNavigationMessageProvider.isRegistered()=")
diff --git a/services/core/java/com/android/server/location/gnss/GnssManagerService.java b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
index 02c1bdd..bcb7a44 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -27,6 +27,7 @@
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssRequest;
 import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
 import android.location.IGnssStatusListener;
@@ -52,6 +53,7 @@
 import com.android.server.LocationManagerServiceUtils.LinkedListenerBase;
 import com.android.server.location.AppForegroundHelper;
 import com.android.server.location.CallerIdentity;
+import com.android.server.location.GnssAntennaInfoProvider;
 import com.android.server.location.GnssBatchingProvider;
 import com.android.server.location.GnssCapabilitiesProvider;
 import com.android.server.location.GnssLocationProvider;
@@ -88,6 +90,7 @@
     private final GnssStatusListenerHelper mGnssStatusProvider;
     private final GnssMeasurementsProvider mGnssMeasurementsProvider;
     private final GnssMeasurementCorrectionsProvider mGnssMeasurementCorrectionsProvider;
+    private final GnssAntennaInfoProvider mGnssAntennaInfoProvider;
     private final GnssNavigationMessageProvider mGnssNavigationMessageProvider;
     private final GnssLocationProvider.GnssSystemInfoProvider mGnssSystemInfoProvider;
     private final GnssLocationProvider.GnssMetricsProvider mGnssMetricsProvider;
@@ -100,6 +103,11 @@
     private final ArrayMap<IBinder, LinkedListener<GnssRequest, IGnssMeasurementsListener>>
             mGnssMeasurementsListeners = new ArrayMap<>();
 
+    @GuardedBy("mGnssAntennaInfoListeners")
+    private final ArrayMap<IBinder,
+            LinkedListener<IGnssAntennaInfoListener>>
+            mGnssAntennaInfoListeners = new ArrayMap<>();
+
     @GuardedBy("mGnssNavigationMessageListeners")
     private final ArrayMap<IBinder, LinkedListener<Void, IGnssNavigationMessageListener>>
             mGnssNavigationMessageListeners = new ArrayMap<>();
@@ -149,6 +157,7 @@
         mGnssLocationProvider = gnssLocationProvider;
         mGnssStatusProvider = mGnssLocationProvider.getGnssStatusProvider();
         mGnssMeasurementsProvider = mGnssLocationProvider.getGnssMeasurementsProvider();
+        mGnssAntennaInfoProvider = mGnssLocationProvider.getGnssAntennaInfoProvider();
         mGnssMeasurementCorrectionsProvider =
                 mGnssLocationProvider.getGnssMeasurementCorrectionsProvider();
         mGnssNavigationMessageProvider = mGnssLocationProvider.getGnssNavigationMessageProvider();
@@ -357,6 +366,14 @@
                     uid,
                     foreground);
         }
+        synchronized (mGnssAntennaInfoListeners) {
+            updateListenersOnForegroundChangedLocked(
+                    mGnssAntennaInfoListeners,
+                    mGnssAntennaInfoProvider,
+                    IGnssAntennaInfoListener.Stub::asInterface,
+                    uid,
+                    foreground);
+        }
     }
 
     private <TRequest, TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
@@ -544,6 +561,40 @@
     }
 
     /**
+     * Adds a GNSS Antenna Info listener.
+     *
+     * @param listener    called when GNSS antenna info is received
+     * @param packageName name of requesting package
+     * @return true if listener is successfully added, false otherwise
+     */
+    public boolean addGnssAntennaInfoListener(
+            IGnssAntennaInfoListener listener, String packageName,
+            @Nullable String featureId, @NonNull String listenerIdentifier) {
+        synchronized (mGnssAntennaInfoListeners) {
+            return addGnssDataListenerLocked(
+                    listener,
+                    packageName,
+                    featureId,
+                    listenerIdentifier,
+                    mGnssAntennaInfoProvider,
+                    mGnssAntennaInfoListeners,
+                    this::removeGnssAntennaInfoListener);
+        }
+    }
+
+    /**
+     * Removes a GNSS Antenna Info listener.
+     *
+     * @param listener called when GNSS antenna info is received
+     */
+    public void removeGnssAntennaInfoListener(IGnssAntennaInfoListener listener) {
+        synchronized (mGnssAntennaInfoListeners) {
+            removeGnssDataListenerLocked(
+                    listener, mGnssAntennaInfoProvider, mGnssAntennaInfoListeners);
+        }
+    }
+
+    /**
      * Adds a GNSS navigation message listener.
      */
     public boolean addGnssNavigationMessageListener(
diff --git a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
index 1a8f1f9..d0eaa48 100644
--- a/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
+++ b/services/core/jni/com_android_server_location_GnssLocationProvider.cpp
@@ -26,17 +26,18 @@
 #include <android/hardware/gnss/1.0/IGnssMeasurement.h>
 #include <android/hardware/gnss/1.1/IGnssMeasurement.h>
 #include <android/hardware/gnss/2.0/IGnssMeasurement.h>
+#include <android/hardware/gnss/2.1/IGnssAntennaInfo.h>
 #include <android/hardware/gnss/2.1/IGnssMeasurement.h>
 #include <android/hardware/gnss/measurement_corrections/1.0/IMeasurementCorrections.h>
 #include <android/hardware/gnss/measurement_corrections/1.1/IMeasurementCorrections.h>
 #include <android/hardware/gnss/visibility_control/1.0/IGnssVisibilityControl.h>
 #include <nativehelper/JNIHelp.h>
-#include "jni.h"
-#include "hardware_legacy/power.h"
-#include "utils/Log.h"
-#include "utils/misc.h"
 #include "android_runtime/AndroidRuntime.h"
 #include "android_runtime/Log.h"
+#include "hardware_legacy/power.h"
+#include "jni.h"
+#include "utils/Log.h"
+#include "utils/misc.h"
 
 #include <arpa/inet.h>
 #include <cinttypes>
@@ -54,6 +55,12 @@
 static jclass class_gnssNavigationMessage;
 static jclass class_gnssClock;
 static jclass class_gnssConfiguration_halInterfaceVersion;
+static jclass class_gnssAntennaInfo;
+static jclass class_phaseCenterOffsetCoordinates;
+static jclass class_phaseCenterVariationCorrections;
+static jclass class_signalGainCorrections;
+static jclass class_arrayList;
+static jclass class_doubleArray;
 
 static jobject mCallbacksObj = nullptr;
 
@@ -78,6 +85,7 @@
 static jmethodID method_reportGeofencePauseStatus;
 static jmethodID method_reportGeofenceResumeStatus;
 static jmethodID method_reportMeasurementData;
+static jmethodID method_reportAntennaInfo;
 static jmethodID method_reportNavigationMessages;
 static jmethodID method_reportLocationBatch;
 static jmethodID method_reportGnssServiceDied;
@@ -114,6 +122,12 @@
 static jmethodID method_gnssClockCtor;
 static jmethodID method_gnssMeasurementCtor;
 static jmethodID method_halInterfaceVersionCtor;
+static jmethodID method_gnssAntennaInfoCtor;
+static jmethodID method_phaseCenterOffsetCoordinatesCtor;
+static jmethodID method_phaseCenterVariationCorrectionsCtor;
+static jmethodID method_signalGainCorrectionsCtor;
+static jmethodID method_arrayListCtor;
+static jmethodID method_arrayListAdd;
 
 /*
  * Save a pointer to JavaVm to attach/detach threads executing
@@ -171,6 +185,8 @@
 using IGnssConfiguration_V2_1 = android::hardware::gnss::V2_1::IGnssConfiguration;
 using IGnssDebug_V1_0 = android::hardware::gnss::V1_0::IGnssDebug;
 using IGnssDebug_V2_0 = android::hardware::gnss::V2_0::IGnssDebug;
+using IGnssAntennaInfo = android::hardware::gnss::V2_1::IGnssAntennaInfo;
+using IGnssAntennaInfoCallback = android::hardware::gnss::V2_1::IGnssAntennaInfoCallback;
 using IGnssMeasurement_V1_0 = android::hardware::gnss::V1_0::IGnssMeasurement;
 using IGnssMeasurement_V1_1 = android::hardware::gnss::V1_1::IGnssMeasurement;
 using IGnssMeasurement_V2_0 = android::hardware::gnss::V2_0::IGnssMeasurement;
@@ -241,6 +257,7 @@
 sp<IMeasurementCorrections_V1_0> gnssCorrectionsIface_V1_0 = nullptr;
 sp<IMeasurementCorrections_V1_1> gnssCorrectionsIface_V1_1 = nullptr;
 sp<IGnssVisibilityControl> gnssVisibilityControlIface = nullptr;
+sp<IGnssAntennaInfo> gnssAntennaInfoIface = nullptr;
 
 #define WAKE_LOCK_NAME  "GPS"
 
@@ -1051,6 +1068,195 @@
 }
 
 /*
+ * GnssAntennaInfoCallback implements the callback methods required for the
+ * GnssAntennaInfo interface.
+ */
+struct GnssAntennaInfoCallback : public IGnssAntennaInfoCallback {
+    // Methods from V2_1::GnssAntennaInfoCallback follow.
+    Return<void> gnssAntennaInfoCb(
+            const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
+
+private:
+    jobject translateAllGnssAntennaInfos(
+            JNIEnv* env,
+            const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
+    jobject translateSingleGnssAntennaInfo(
+            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
+    jobject translatePhaseCenterOffsetCoordinates(
+            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
+    jobject translatePhaseCenterVariationCorrections(
+            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
+    jobject translateSignalGainCorrections(
+            JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo);
+    jobjectArray translate2dDoubleArray(JNIEnv* env,
+                                        const hidl_vec<IGnssAntennaInfoCallback::Row>& array);
+    void translateAndReportGnssAntennaInfo(
+            const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos);
+    void reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray);
+};
+
+Return<void> GnssAntennaInfoCallback::gnssAntennaInfoCb(
+        const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+    translateAndReportGnssAntennaInfo(gnssAntennaInfos);
+    return Void();
+}
+
+jobjectArray GnssAntennaInfoCallback::translate2dDoubleArray(
+        JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::Row>& array) {
+    jsize numRows = array.size();
+    if (numRows == 0) {
+        // Empty array
+        return NULL;
+    }
+    jsize numCols = array[0].row.size();
+    if (numCols <= 1) {
+        // phi angle separation is computed as 180.0 / (numColumns - 1), so can't be < 2.
+        return NULL;
+    }
+
+    // Allocate array of double arrays
+    jobjectArray returnArray = env->NewObjectArray(numRows, class_doubleArray, NULL);
+
+    // Create each double array
+    for (uint8_t i = 0; i < numRows; i++) {
+        jdoubleArray doubleArray = env->NewDoubleArray(numCols);
+        env->SetDoubleArrayRegion(doubleArray, (jsize)0, numCols, array[i].row.data());
+        env->SetObjectArrayElement(returnArray, (jsize)i, doubleArray);
+        env->DeleteLocalRef(doubleArray);
+    }
+    return returnArray;
+}
+
+jobject GnssAntennaInfoCallback::translateAllGnssAntennaInfos(
+        JNIEnv* env, const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+    jobject arrayList = env->NewObject(class_arrayList,
+                                       method_arrayListCtor); // Create new ArrayList instance
+
+    for (auto gnssAntennaInfo : gnssAntennaInfos) {
+        jobject gnssAntennaInfoObject = translateSingleGnssAntennaInfo(env, gnssAntennaInfo);
+
+        env->CallBooleanMethod(arrayList, method_arrayListAdd,
+                               gnssAntennaInfoObject); // Add the antennaInfo to the ArrayList
+
+        // Delete Local Refs
+        env->DeleteLocalRef(gnssAntennaInfoObject);
+    }
+    return arrayList;
+}
+
+jobject GnssAntennaInfoCallback::translatePhaseCenterOffsetCoordinates(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    jobject phaseCenterOffsetCoordinates =
+            env->NewObject(class_phaseCenterOffsetCoordinates,
+                           method_phaseCenterOffsetCoordinatesCtor,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.x,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.xUncertainty,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.y,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.yUncertainty,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.z,
+                           gnssAntennaInfo.phaseCenterOffsetCoordinateMillimeters.zUncertainty);
+
+    return phaseCenterOffsetCoordinates;
+}
+
+jobject GnssAntennaInfoCallback::translatePhaseCenterVariationCorrections(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    if (gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters == NULL ||
+        gnssAntennaInfo.phaseCenterVariationCorrectionUncertaintyMillimeters == NULL) {
+        return NULL;
+    }
+
+    jobjectArray phaseCenterVariationCorrectionsArray =
+            translate2dDoubleArray(env, gnssAntennaInfo.phaseCenterVariationCorrectionMillimeters);
+    jobjectArray phaseCenterVariationCorrectionsUncertaintiesArray =
+            translate2dDoubleArray(env,
+                                   gnssAntennaInfo
+                                           .phaseCenterVariationCorrectionUncertaintyMillimeters);
+
+    if (phaseCenterVariationCorrectionsArray == NULL ||
+        phaseCenterVariationCorrectionsUncertaintiesArray == NULL) {
+        return NULL;
+    }
+
+    jobject phaseCenterVariationCorrections =
+            env->NewObject(class_phaseCenterVariationCorrections,
+                           method_phaseCenterVariationCorrectionsCtor,
+                           phaseCenterVariationCorrectionsArray,
+                           phaseCenterVariationCorrectionsUncertaintiesArray);
+
+    env->DeleteLocalRef(phaseCenterVariationCorrectionsArray);
+    env->DeleteLocalRef(phaseCenterVariationCorrectionsUncertaintiesArray);
+
+    return phaseCenterVariationCorrections;
+}
+
+jobject GnssAntennaInfoCallback::translateSignalGainCorrections(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    if (gnssAntennaInfo.signalGainCorrectionDbi == NULL ||
+        gnssAntennaInfo.signalGainCorrectionUncertaintyDbi == NULL) {
+        return NULL;
+    }
+    jobjectArray signalGainCorrectionsArray =
+            translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionDbi);
+    jobjectArray signalGainCorrectionsUncertaintiesArray =
+            translate2dDoubleArray(env, gnssAntennaInfo.signalGainCorrectionUncertaintyDbi);
+
+    if (signalGainCorrectionsArray == NULL || signalGainCorrectionsUncertaintiesArray == NULL) {
+        return NULL;
+    }
+
+    jobject signalGainCorrections =
+            env->NewObject(class_signalGainCorrections, method_signalGainCorrectionsCtor,
+                           signalGainCorrectionsArray, signalGainCorrectionsUncertaintiesArray);
+
+    env->DeleteLocalRef(signalGainCorrectionsArray);
+    env->DeleteLocalRef(signalGainCorrectionsUncertaintiesArray);
+
+    return signalGainCorrections;
+}
+
+jobject GnssAntennaInfoCallback::translateSingleGnssAntennaInfo(
+        JNIEnv* env, const IGnssAntennaInfoCallback::GnssAntennaInfo& gnssAntennaInfo) {
+    jobject phaseCenterOffsetCoordinates =
+            translatePhaseCenterOffsetCoordinates(env, gnssAntennaInfo);
+
+    // Nullable
+    jobject phaseCenterVariationCorrections =
+            translatePhaseCenterVariationCorrections(env, gnssAntennaInfo);
+
+    // Nullable
+    jobject signalGainCorrections = translateSignalGainCorrections(env, gnssAntennaInfo);
+
+    jobject gnssAntennaInfoObject =
+            env->NewObject(class_gnssAntennaInfo, method_gnssAntennaInfoCtor,
+                           gnssAntennaInfo.carrierFrequencyMHz, phaseCenterOffsetCoordinates,
+                           phaseCenterVariationCorrections, signalGainCorrections);
+
+    // Delete Local Refs
+    env->DeleteLocalRef(phaseCenterOffsetCoordinates);
+    env->DeleteLocalRef(phaseCenterVariationCorrections);
+    env->DeleteLocalRef(signalGainCorrections);
+
+    return gnssAntennaInfoObject;
+}
+
+void GnssAntennaInfoCallback::translateAndReportGnssAntennaInfo(
+        const hidl_vec<IGnssAntennaInfoCallback::GnssAntennaInfo>& gnssAntennaInfos) {
+    JNIEnv* env = getJniEnv();
+
+    jobject arrayList = translateAllGnssAntennaInfos(env, gnssAntennaInfos);
+
+    reportAntennaInfo(env, arrayList);
+
+    env->DeleteLocalRef(arrayList);
+}
+
+void GnssAntennaInfoCallback::reportAntennaInfo(JNIEnv* env, const jobject antennaInfosArray) {
+    env->CallVoidMethod(mCallbacksObj, method_reportAntennaInfo, antennaInfosArray);
+    checkAndClearExceptionFromCallback(env, __FUNCTION__);
+}
+
+/*
  * GnssMeasurementCallback implements the callback methods required for the
  * GnssMeasurement interface.
  */
@@ -1708,6 +1914,7 @@
             "(II)V");
     method_reportGeofencePauseStatus = env->GetMethodID(clazz, "reportGeofencePauseStatus",
             "(II)V");
+    method_reportAntennaInfo = env->GetMethodID(clazz, "reportAntennaInfo", "(Ljava/util/List;)V");
     method_reportMeasurementData = env->GetMethodID(
             clazz,
             "reportMeasurementData",
@@ -1791,6 +1998,36 @@
     class_gnssMeasurement = (jclass) env->NewGlobalRef(gnssMeasurementClass);
     method_gnssMeasurementCtor = env->GetMethodID(class_gnssMeasurement, "<init>", "()V");
 
+    jclass gnssAntennaInfoClass = env->FindClass("android/location/GnssAntennaInfo");
+    class_gnssAntennaInfo = (jclass)env->NewGlobalRef(gnssAntennaInfoClass);
+    method_gnssAntennaInfoCtor =
+            env->GetMethodID(class_gnssAntennaInfo, "<init>",
+                             "(D"
+                             "Landroid/location/GnssAntennaInfo$PhaseCenterOffsetCoordinates;"
+                             "Landroid/location/GnssAntennaInfo$PhaseCenterVariationCorrections;"
+                             "Landroid/location/GnssAntennaInfo$SignalGainCorrections;"
+                             ")V");
+
+    jclass phaseCenterOffsetCoordinatesClass =
+            env->FindClass("android/location/GnssAntennaInfo$PhaseCenterOffsetCoordinates");
+    class_phaseCenterOffsetCoordinates =
+            (jclass)env->NewGlobalRef(phaseCenterOffsetCoordinatesClass);
+    method_phaseCenterOffsetCoordinatesCtor =
+            env->GetMethodID(class_phaseCenterOffsetCoordinates, "<init>", "(DDDDDD)V");
+
+    jclass phaseCenterVariationCorrectionsClass =
+            env->FindClass("android/location/GnssAntennaInfo$PhaseCenterVariationCorrections");
+    class_phaseCenterVariationCorrections =
+            (jclass)env->NewGlobalRef(phaseCenterVariationCorrectionsClass);
+    method_phaseCenterVariationCorrectionsCtor =
+            env->GetMethodID(class_phaseCenterVariationCorrections, "<init>", "([[D[[D)V");
+
+    jclass signalGainCorrectionsClass =
+            env->FindClass("android/location/GnssAntennaInfo$SignalGainCorrections");
+    class_signalGainCorrections = (jclass)env->NewGlobalRef(signalGainCorrectionsClass);
+    method_signalGainCorrectionsCtor =
+            env->GetMethodID(class_signalGainCorrections, "<init>", "([[D[[D)V");
+
     jclass locationClass = env->FindClass("android/location/Location");
     class_location = (jclass) env->NewGlobalRef(locationClass);
     method_locationCtor = env->GetMethodID(class_location, "<init>", "(Ljava/lang/String;)V");
@@ -1809,6 +2046,14 @@
             (jclass) env->NewGlobalRef(gnssConfiguration_halInterfaceVersionClass);
     method_halInterfaceVersionCtor =
             env->GetMethodID(class_gnssConfiguration_halInterfaceVersion, "<init>", "(II)V");
+
+    jclass arrayListClass = env->FindClass("java/util/ArrayList");
+    class_arrayList = (jclass)env->NewGlobalRef(arrayListClass);
+    method_arrayListCtor = env->GetMethodID(class_arrayList, "<init>", "()V");
+    method_arrayListAdd = env->GetMethodID(class_arrayList, "add", "(Ljava/lang/Object;)Z");
+
+    jclass doubleArrayClass = env->FindClass("[D");
+    class_doubleArray = (jclass)env->NewGlobalRef(doubleArrayClass);
 }
 
 /* Initialization needed at system boot and whenever GNSS service dies. */
@@ -1935,6 +2180,15 @@
     }
 
     if (gnssHal_V2_1 != nullptr) {
+        auto gnssAntennaInfo = gnssHal_V2_1->getExtensionGnssAntennaInfo();
+        if (!gnssAntennaInfo.isOk()) {
+            ALOGD("Unable to get a handle to GnssAntennaInfo");
+        } else {
+            gnssAntennaInfoIface = gnssAntennaInfo;
+        }
+    }
+
+    if (gnssHal_V2_1 != nullptr) {
         auto gnssCorrections = gnssHal_V2_1->getExtensionMeasurementCorrections_1_1();
         if (!gnssCorrections.isOk()) {
             ALOGD("Unable to get a handle to GnssMeasurementCorrections 1.1 interface");
@@ -2725,6 +2979,52 @@
     return checkHidlReturn(result, "IGnssGeofencing resumeGeofence() failed.");
 }
 
+static jboolean android_location_GnssAntennaInfoProvider_is_antenna_info_supported(JNIEnv* env,
+                                                                                   jclass clazz) {
+    if (gnssAntennaInfoIface != nullptr) {
+        return JNI_TRUE;
+    }
+    return JNI_FALSE;
+}
+
+static jboolean android_location_GnssAntennaInfoProvider_start_antenna_info_listening(
+        JNIEnv* /* env */, jobject /* obj */) {
+    if (gnssAntennaInfoIface == nullptr) {
+        ALOGE("%s: IGnssAntennaInfo interface not available.", __func__);
+        return JNI_FALSE;
+    }
+
+    sp<GnssAntennaInfoCallback> cbIface = new GnssAntennaInfoCallback();
+
+    auto result = gnssAntennaInfoIface->setCallback(cbIface);
+
+    if (!checkHidlReturn(result, "IGnssAntennaInfo setCallback() failed.")) {
+        return JNI_FALSE;
+    }
+
+    IGnssAntennaInfo::GnssAntennaInfoStatus initRet = result;
+    if (initRet != IGnssAntennaInfo::GnssAntennaInfoStatus::SUCCESS) {
+        ALOGE("An error has been found on GnssAntennaInfoInterface::init, status=%d",
+              static_cast<int32_t>(initRet));
+        return JNI_FALSE;
+    } else {
+        ALOGD("gnss antenna info has been enabled");
+    }
+
+    return JNI_TRUE;
+}
+
+static jboolean android_location_GnssAntennaInfoProvider_stop_antenna_info_listening(
+        JNIEnv* /* env */, jobject /* obj */) {
+    if (gnssAntennaInfoIface == nullptr) {
+        ALOGE("%s: IGnssAntennaInfo interface not available.", __func__);
+        return JNI_FALSE;
+    }
+
+    auto result = gnssAntennaInfoIface->close();
+    return checkHidlReturn(result, "IGnssAntennaInfo close() failed.");
+}
+
 static jboolean android_location_GnssMeasurementsProvider_is_measurement_supported(
     JNIEnv* env, jclass clazz) {
     if (gnssMeasurementIface != nullptr) {
@@ -3286,6 +3586,19 @@
             reinterpret_cast<void *>(android_location_GnssBatchingProvider_cleanup_batching)},
 };
 
+static const JNINativeMethod sAntennaInfoMethods[] = {
+        /* name, signature, funcPtr */
+        {"native_is_antenna_info_supported", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_GnssAntennaInfoProvider_is_antenna_info_supported)},
+        {"native_start_antenna_info_listening", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_GnssAntennaInfoProvider_start_antenna_info_listening)},
+        {"native_stop_antenna_info_listening", "()Z",
+         reinterpret_cast<void*>(
+                 android_location_GnssAntennaInfoProvider_stop_antenna_info_listening)},
+};
+
 static const JNINativeMethod sGeofenceMethods[] = {
      /* name, signature, funcPtr */
     {"native_is_geofence_supported",
@@ -3407,6 +3720,8 @@
 };
 
 int register_android_server_location_GnssLocationProvider(JNIEnv* env) {
+    jniRegisterNativeMethods(env, "com/android/server/location/GnssAntennaInfoProvider",
+                             sAntennaInfoMethods, NELEM(sAntennaInfoMethods));
     jniRegisterNativeMethods(
             env,
             "com/android/server/location/GnssBatchingProvider",
diff --git a/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java b/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java
new file mode 100644
index 0000000..76f7ad6
--- /dev/null
+++ b/services/robotests/src/com/android/server/location/GnssAntennaInfoProviderTest.java
@@ -0,0 +1,94 @@
+/*
+ * 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 com.android.server.location;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.os.Handler;
+import android.os.Looper;
+import android.platform.test.annotations.Presubmit;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+/**
+ * Unit tests for {@link GnssAntennaInfoProvider}.
+ */
+@RunWith(RobolectricTestRunner.class)
+@Presubmit
+public class GnssAntennaInfoProviderTest {
+    @Mock
+    private GnssAntennaInfoProvider.GnssAntennaInfoProviderNative mMockNative;
+    private GnssAntennaInfoProvider mTestProvider;
+
+    /** Setup. */
+    @Before
+    public void setUp() {
+        MockitoAnnotations.initMocks(this);
+        when(mMockNative.startAntennaInfoListening()).thenReturn(true);
+        when(mMockNative.stopAntennaInfoListening()).thenReturn(true);
+
+        mTestProvider = new GnssAntennaInfoProvider(RuntimeEnvironment.application,
+                new Handler(Looper.myLooper()), mMockNative) {
+            @Override
+            public boolean isGpsEnabled() {
+                return true;
+            }
+        };
+    }
+
+    /**
+     * Test that registerWithService calls the native startAntennaInfoListening method.
+     */
+    @Test
+    public void register_nativeStarted() {
+        mTestProvider.registerWithService();
+        verify(mMockNative, times(1)).startAntennaInfoListening();
+    }
+
+    /**
+     * Test that unregisterFromService calls the native stopAntennaInfoListening method.
+     */
+    @Test
+    public void unregister_nativeStopped() {
+        mTestProvider.registerWithService();
+        mTestProvider.unregisterFromService();
+        verify(mMockNative, times(1)).stopAntennaInfoListening();
+    }
+
+    /**
+     * Test that GnssAntennaInfoProvider.isAntennaInfoSupported() returns the result of the
+     * native isAntennaInfoSupported method.
+     */
+    @Test
+    public void isSupported_nativeIsSupported() {
+        when(mMockNative.isAntennaInfoSupported()).thenReturn(true);
+        assertThat(mTestProvider.isAvailableInPlatform()).isTrue();
+
+        when(mMockNative.isAntennaInfoSupported()).thenReturn(false);
+        assertThat(mTestProvider.isAvailableInPlatform()).isFalse();
+    }
+}
diff --git a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
index 4ba584e..4d0ad96 100644
--- a/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
+++ b/services/tests/servicestests/src/com/android/server/location/gnss/GnssManagerServiceTest.java
@@ -35,6 +35,7 @@
 import android.app.AppOpsManager;
 import android.content.Context;
 import android.content.pm.PackageManager;
+import android.location.GnssAntennaInfo;
 import android.location.GnssClock;
 import android.location.GnssMeasurementCorrections;
 import android.location.GnssMeasurementsEvent;
@@ -42,6 +43,7 @@
 import android.location.GnssRequest;
 import android.location.GnssSingleSatCorrection;
 import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
 import android.location.IGnssMeasurementsListener;
 import android.location.IGnssNavigationMessageListener;
 import android.location.IGnssStatusListener;
@@ -56,6 +58,8 @@
 
 import com.android.server.LocalServices;
 import com.android.server.location.AppForegroundHelper;
+import com.android.server.location.GnssAntennaInfoProvider;
+import com.android.server.location.GnssAntennaInfoProvider.GnssAntennaInfoProviderNative;
 import com.android.server.location.GnssBatchingProvider;
 import com.android.server.location.GnssCapabilitiesProvider;
 import com.android.server.location.GnssLocationProvider;
@@ -102,6 +106,7 @@
     private GnssMeasurementsProvider mTestGnssMeasurementsProvider;
     private GnssStatusListenerHelper mTestGnssStatusProvider;
     private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider;
+    private GnssAntennaInfoProvider mTestGnssAntennaInfoProvider;
 
     // Managers and services
     @Mock
@@ -152,6 +157,8 @@
                 mMockContext, mMockHandler);
         mTestGnssNavigationMessageProvider = createGnssNavigationMessageProvider(
                 mMockContext, mMockHandler);
+        mTestGnssAntennaInfoProvider = createGnssAntennaInfoProvider(
+                mMockContext, mMockHandler);
 
         // Setup GnssLocationProvider to return providers
         when(mMockGnssLocationProvider.getGnssStatusProvider()).thenReturn(
@@ -170,6 +177,8 @@
                 mTestGnssNavigationMessageProvider);
         when(mMockGnssLocationProvider.getNetInitiatedListener()).thenReturn(
                 mNetInitiatedListener);
+        when(mMockGnssLocationProvider.getGnssAntennaInfoProvider()).thenReturn(
+                mTestGnssAntennaInfoProvider);
 
         // Setup GnssBatching provider
         when(mMockGnssBatchingProvider.start(anyLong(), anyBoolean())).thenReturn(true);
@@ -205,6 +214,12 @@
         return mockListener;
     }
 
+    private IGnssAntennaInfoListener createMockGnssAntennaInfoListener() {
+        IGnssAntennaInfoListener mockListener = mock(IGnssAntennaInfoListener.class);
+        overrideAsBinder(mockListener);
+        return mockListener;
+    }
+
     private IBatchedLocationCallback createMockBatchedLocationCallback() {
         IBatchedLocationCallback mockedCallback = mock(IBatchedLocationCallback.class);
         overrideAsBinder(mockedCallback);
@@ -225,6 +240,39 @@
                         Arrays.asList(gnssSingleSatCorrection)).build();
     }
 
+    private static List<GnssAntennaInfo> createDummyGnssAntennaInfos() {
+        double carrierFrequencyMHz = 13758.0;
+
+        GnssAntennaInfo.PhaseCenterOffsetCoordinates phaseCenterOffsetCoordinates = new
+                GnssAntennaInfo.PhaseCenterOffsetCoordinates(
+                4.3d,
+                1.4d,
+                2.10d,
+                2.1d,
+                3.12d,
+                0.5d);
+
+        double[][] phaseCenterVariationCorrectionsMillimeters = new double[10][10];
+        double[][] phaseCenterVariationCorrectionsUncertaintyMillimeters = new double[10][10];
+        GnssAntennaInfo.PhaseCenterVariationCorrections
+                phaseCenterVariationCorrections =
+                new GnssAntennaInfo.PhaseCenterVariationCorrections(
+                        phaseCenterVariationCorrectionsMillimeters,
+                        phaseCenterVariationCorrectionsUncertaintyMillimeters);
+
+        double[][] signalGainCorrectionsDbi = new double[10][10];
+        double[][] signalGainCorrectionsUncertaintyDbi = new double[10][10];
+        GnssAntennaInfo.SignalGainCorrections signalGainCorrections = new
+                GnssAntennaInfo.SignalGainCorrections(
+                signalGainCorrectionsDbi,
+                signalGainCorrectionsUncertaintyDbi);
+
+        List<GnssAntennaInfo> gnssAntennaInfos = new ArrayList();
+        gnssAntennaInfos.add(new GnssAntennaInfo(carrierFrequencyMHz, phaseCenterOffsetCoordinates,
+                phaseCenterVariationCorrections, signalGainCorrections));
+        return gnssAntennaInfos;
+    }
+
     private void enableLocationPermissions() {
         Mockito.doThrow(new SecurityException()).when(
                 mMockContext).enforceCallingPermission(
@@ -299,6 +347,18 @@
         };
     }
 
+    private GnssAntennaInfoProvider createGnssAntennaInfoProvider(Context context,
+            Handler handler) {
+        GnssAntennaInfoProviderNative mockGnssAntenaInfoProviderNative = mock(
+                GnssAntennaInfoProviderNative.class);
+        return new GnssAntennaInfoProvider(context, handler, mockGnssAntenaInfoProviderNative) {
+            @Override
+            protected boolean isGpsEnabled() {
+                return true;
+            }
+        };
+    }
+
     @Test
     public void getGnssYearOfHardwareTest() {
         final int gnssYearOfHardware = 2012;
@@ -670,6 +730,82 @@
     }
 
     @Test
+    public void addGnssAntennaInfoListenerWithoutPermissionsTest() throws RemoteException {
+        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
+                createMockGnssAntennaInfoListener();
+        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
+
+        disableLocationPermissions();
+
+        assertThrows(SecurityException.class,
+                () -> mGnssManagerService.addGnssAntennaInfoListener(
+                        mockGnssAntennaInfoListener,
+                        "com.android.server", "abcd123", "TestGnssAntennaInfoListener"));
+
+        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
+        verify(mockGnssAntennaInfoListener, times(0))
+                .onGnssAntennaInfoReceived(gnssAntennaInfos);
+    }
+
+    @Test
+    public void addGnssAntennaInfoListenerWithPermissionsTest() throws RemoteException {
+        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
+                createMockGnssAntennaInfoListener();
+        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
+
+        enableLocationPermissions();
+
+        assertThat(mGnssManagerService.addGnssAntennaInfoListener(mockGnssAntennaInfoListener,
+                "com.android.server", "abcd123", "TestGnssAntennaInfoListener")).isEqualTo(true);
+
+        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
+        verify(mockGnssAntennaInfoListener, times(1))
+                .onGnssAntennaInfoReceived(gnssAntennaInfos);
+    }
+
+    @Test
+    public void removeGnssAntennaInfoListenerWithoutPermissionsTest() throws RemoteException {
+        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
+                createMockGnssAntennaInfoListener();
+        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
+
+        enableLocationPermissions();
+
+        mGnssManagerService.addGnssAntennaInfoListener(
+                mockGnssAntennaInfoListener,
+                "com.android.server", "abcd123", "TestGnssAntennaInfoListener");
+
+        disableLocationPermissions();
+
+        mGnssManagerService.removeGnssAntennaInfoListener(
+                mockGnssAntennaInfoListener);
+
+        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
+        verify(mockGnssAntennaInfoListener, times(0)).onGnssAntennaInfoReceived(
+                gnssAntennaInfos);
+    }
+
+    @Test
+    public void removeGnssAntennaInfoListenerWithPermissionsTest() throws RemoteException {
+        IGnssAntennaInfoListener mockGnssAntennaInfoListener =
+                createMockGnssAntennaInfoListener();
+        List<GnssAntennaInfo> gnssAntennaInfos = createDummyGnssAntennaInfos();
+
+        enableLocationPermissions();
+
+        mGnssManagerService.addGnssAntennaInfoListener(
+                mockGnssAntennaInfoListener,
+                "com.android.server", "abcd123", "TestGnssAntennaInfoListener");
+
+        mGnssManagerService.removeGnssAntennaInfoListener(
+                mockGnssAntennaInfoListener);
+
+        mTestGnssAntennaInfoProvider.onGnssAntennaInfoAvailable(gnssAntennaInfos);
+        verify(mockGnssAntennaInfoListener, times(0)).onGnssAntennaInfoReceived(
+                gnssAntennaInfos);
+    }
+
+    @Test
     public void addGnssNavigationMessageListenerWithoutPermissionsTest() throws RemoteException {
         IGnssNavigationMessageListener mockGnssNavigationMessageListener =
                 createMockGnssNavigationMessageListener();