Add GnssAntennaInfo framework APIs
Test: End to end test: run "atest GnssAntennaInfoRegistrationTest" on cuttlefish and watch "adb logcat | grep -i GnssAntennaInfo". Other tests: atest GnssAntennaInfoTest, atest GnssAntennaInfoProviderTest, atest GnssManagerServiceTest, atest VtsHalGnssV2_1TargetTest, atest LocationManagerFineTest.
Bug:124556515
Change-Id: I70e4014dd3959b0570c35bd2aa8bb839ef167d70
diff --git a/api/current.txt b/api/current.txt
index 80d4788..6ec0da1 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -23465,6 +23465,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();
@@ -23782,6 +23845,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);
@@ -23813,6 +23877,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 9c32a3c..3224d0f 100755
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -3806,6 +3806,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 6a5c0ec..75292d5 100644
--- a/location/java/android/location/ILocationManager.aidl
+++ b/location/java/android/location/ILocationManager.aidl
@@ -23,6 +23,7 @@
import android.location.Geofence;
import android.location.GnssMeasurementCorrections;
import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssStatusListener;
import android.location.IGnssNavigationMessageListener;
@@ -76,6 +77,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 197787e..4e26c1e 100644
--- a/location/java/android/location/LocationManager.java
+++ b/location/java/android/location/LocationManager.java
@@ -278,6 +278,8 @@
new GnssMeasurementsListenerManager();
private final GnssNavigationMessageListenerManager mGnssNavigationMessageListenerTransport =
new GnssNavigationMessageListenerManager();
+ private final GnssAntennaInfoListenerManager mGnssAntennaInfoListenerManager =
+ new GnssAntennaInfoListenerManager();
/**
* @hide
@@ -2199,6 +2201,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
@@ -2929,6 +2966,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<BatchedLocationCallback> {
@@ -2941,7 +3020,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 0ab8af6a..239e9ec 100644
--- a/services/core/java/com/android/server/LocationManagerService.java
+++ b/services/core/java/com/android/server/LocationManagerService.java
@@ -43,6 +43,7 @@
import android.location.Geofence;
import android.location.GnssMeasurementCorrections;
import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
import android.location.IGnssStatusListener;
@@ -2318,6 +2319,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 2bab9fa..cbf010a 100644
--- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java
+++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java
@@ -26,6 +26,7 @@
import android.location.GnssCapabilities;
import android.location.GnssMeasurementCorrections;
import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
import android.location.IGnssStatusListener;
@@ -51,6 +52,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;
@@ -87,6 +89,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;
@@ -99,6 +102,11 @@
private final ArrayMap<IBinder, LinkedListener<IGnssMeasurementsListener>>
mGnssMeasurementsListeners = new ArrayMap<>();
+ @GuardedBy("mGnssAntennaInfoListeners")
+ private final ArrayMap<IBinder,
+ LinkedListener<IGnssAntennaInfoListener>>
+ mGnssAntennaInfoListeners = new ArrayMap<>();
+
@GuardedBy("mGnssNavigationMessageListeners")
private final ArrayMap<IBinder, LinkedListener<IGnssNavigationMessageListener>>
mGnssNavigationMessageListeners = new ArrayMap<>();
@@ -147,6 +155,7 @@
mGnssLocationProvider = gnssLocationProvider;
mGnssStatusProvider = mGnssLocationProvider.getGnssStatusProvider();
mGnssMeasurementsProvider = mGnssLocationProvider.getGnssMeasurementsProvider();
+ mGnssAntennaInfoProvider = mGnssLocationProvider.getGnssAntennaInfoProvider();
mGnssMeasurementCorrectionsProvider =
mGnssLocationProvider.getGnssMeasurementCorrectionsProvider();
mGnssNavigationMessageProvider = mGnssLocationProvider.getGnssNavigationMessageProvider();
@@ -354,6 +363,14 @@
uid,
foreground);
}
+ synchronized (mGnssAntennaInfoListeners) {
+ updateListenersOnForegroundChangedLocked(
+ mGnssAntennaInfoListeners,
+ mGnssAntennaInfoProvider,
+ IGnssAntennaInfoListener.Stub::asInterface,
+ uid,
+ foreground);
+ }
}
private <TListener extends IInterface> void updateListenersOnForegroundChangedLocked(
@@ -531,6 +548,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 f16cf35..dfe5fb8 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,12 +35,14 @@
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;
import android.location.GnssNavigationMessage;
import android.location.GnssSingleSatCorrection;
import android.location.IBatchedLocationCallback;
+import android.location.IGnssAntennaInfoListener;
import android.location.IGnssMeasurementsListener;
import android.location.IGnssNavigationMessageListener;
import android.location.IGnssStatusListener;
@@ -55,6 +57,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;
@@ -101,6 +105,7 @@
private GnssMeasurementsProvider mTestGnssMeasurementsProvider;
private GnssStatusListenerHelper mTestGnssStatusProvider;
private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider;
+ private GnssAntennaInfoProvider mTestGnssAntennaInfoProvider;
// Managers and services
@Mock
@@ -151,6 +156,8 @@
mMockContext, mMockHandler);
mTestGnssNavigationMessageProvider = createGnssNavigationMessageProvider(
mMockContext, mMockHandler);
+ mTestGnssAntennaInfoProvider = createGnssAntennaInfoProvider(
+ mMockContext, mMockHandler);
// Setup GnssLocationProvider to return providers
when(mMockGnssLocationProvider.getGnssStatusProvider()).thenReturn(
@@ -169,6 +176,8 @@
mTestGnssNavigationMessageProvider);
when(mMockGnssLocationProvider.getNetInitiatedListener()).thenReturn(
mNetInitiatedListener);
+ when(mMockGnssLocationProvider.getGnssAntennaInfoProvider()).thenReturn(
+ mTestGnssAntennaInfoProvider);
// Setup GnssBatching provider
when(mMockGnssBatchingProvider.start(anyLong(), anyBoolean())).thenReturn(true);
@@ -204,6 +213,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);
@@ -224,6 +239,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(
@@ -298,6 +346,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;
@@ -662,6 +722,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();