diff options
16 files changed, 1690 insertions, 5 deletions
diff --git a/api/current.txt b/api/current.txt index 9f40c5639e14..3cde15c128ee 100644 --- a/api/current.txt +++ b/api/current.txt @@ -23477,6 +23477,69 @@ package android.location { 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 @@ package android.location { 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 @@ package android.location { 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 d0da42c49226..229a951b7b0c 100755 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -3805,6 +3805,7 @@ package android.location { 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 000000000000..2b956afda01f --- /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 000000000000..dfcaf814f9c9 --- /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 2e2f98432d2e..930180c6870f 100644 --- a/location/java/android/location/GnssCapabilities.java +++ b/location/java/android/location/GnssCapabilities.java @@ -82,6 +82,12 @@ public final class GnssCapabilities { */ 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 @@ public final class GnssCapabilities { 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 @@ public final class GnssCapabilities { 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 000000000000..30bf546759f1 --- /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 7de4d89dfa4f..8600dc48c6a4 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -24,6 +24,7 @@ import android.location.Geofence; 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 @@ interface ILocationManager 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 0fec611bc522..3d0765bb0855 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -251,7 +251,7 @@ public class LocationManager { * @hide */ public static final String HIGH_POWER_REQUEST_CHANGE_ACTION = - "android.location.HIGH_POWER_REQUEST_CHANGE"; + "android.location.HIGH_POWER_REQUEST_CHANGE"; /** * Broadcast intent action for Settings app to inject a footer at the bottom of location @@ -310,6 +310,8 @@ public class LocationManager { new GnssMeasurementsListenerManager(); private final GnssNavigationMessageListenerManager mGnssNavigationMessageListenerTransport = new GnssNavigationMessageListenerManager(); + private final GnssAntennaInfoListenerManager mGnssAntennaInfoListenerManager = + new GnssAntennaInfoListenerManager(); /** * @hide @@ -2261,6 +2263,41 @@ public class LocationManager { } /** + * 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 @@ -2989,6 +3026,48 @@ public class LocationManager { } } + private class GnssAntennaInfoListenerManager extends + AbstractListenerManager<Void, GnssAntennaInfo.Callback> { + + @Nullable + private IGnssAntennaInfoListener mListenerTransport; + + @Override + protected boolean registerService(Void ignored) throws RemoteException { + Preconditions.checkState(mListenerTransport == null); + + GnssAntennaInfoListener transport = new GnssAntennaInfoListener(); + if (mService.addGnssAntennaInfoListener(transport, mContext.getPackageName(), + mContext.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> { @@ -3001,7 +3080,7 @@ public class LocationManager { 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 6e6c9420c5aa..7aa466146ddd 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -48,6 +48,7 @@ import android.location.Geofence; 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; @@ -2408,6 +2409,22 @@ public class LocationManagerService extends ILocationManager.Stub { } @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 000000000000..09af655e1735 --- /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<Void, 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 88ff6e7d1366..5c8507f7fde0 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 @@ public class GnssCapabilitiesProvider { 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 bcac4730cb8b..36136f49dc42 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.GeofenceHardware; 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 class GnssLocationProvider extends AbstractLocationProvider implements 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 @@ public class GnssLocationProvider extends AbstractLocationProvider implements 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 @@ public class GnssLocationProvider extends AbstractLocationProvider implements return mGnssMeasurementCorrectionsProvider; } + public GnssAntennaInfoProvider getGnssAntennaInfoProvider() { + return mGnssAntennaInfoProvider; + } + public GnssNavigationMessageProvider getGnssNavigationMessageProvider() { return mGnssNavigationMessageProvider; } @@ -693,6 +700,13 @@ public class GnssLocationProvider extends AbstractLocationProvider implements 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 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mGnssMeasurementsProvider.onGpsEnabledChanged(); mGnssNavigationMessageProvider.onGpsEnabledChanged(); + mGnssAntennaInfoProvider.onGpsEnabledChanged(); mGnssBatchingProvider.enable(); if (mGnssVisibilityControl != null) { mGnssVisibilityControl.onGpsEnabledChanged(/* isEnabled= */ true); @@ -1018,6 +1033,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements // do this before releasing wakelock native_cleanup(); + mGnssAntennaInfoProvider.onGpsEnabledChanged(); mGnssMeasurementsProvider.onGpsEnabledChanged(); mGnssNavigationMessageProvider.onGpsEnabledChanged(); } @@ -1563,6 +1579,11 @@ public class GnssLocationProvider extends AbstractLocationProvider implements } @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 @@ public class GnssLocationProvider extends AbstractLocationProvider implements mGnssNavigationMessageProvider.onCapabilitiesUpdated( hasCapability(GPS_CAPABILITY_NAV_MESSAGES)); restartRequests(); + mGnssAntennaInfoProvider.onCapabilitiesUpdated( + hasCapability(GPS_CAPABILITY_ANTENNA_INFO)); mGnssCapabilitiesProvider.setTopHalCapabilities(mTopHalCapabilities); }); @@ -1606,6 +1629,7 @@ public class GnssLocationProvider extends AbstractLocationProvider implements Log.i(TAG, "restartRequests"); restartLocationRequest(); + mGnssAntennaInfoProvider.resumeIfStarted(); mGnssMeasurementsProvider.resumeIfStarted(); mGnssNavigationMessageProvider.resumeIfStarted(); mGnssBatchingProvider.resumeIfStarted(); @@ -2198,6 +2222,8 @@ public class GnssLocationProvider extends AbstractLocationProvider implements 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 87bb28e710c1..c9780483327d 100644 --- a/services/core/java/com/android/server/location/gnss/GnssManagerService.java +++ b/services/core/java/com/android/server/location/gnss/GnssManagerService.java @@ -28,6 +28,7 @@ import android.location.GnssCapabilities; 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; @@ -54,6 +55,7 @@ import com.android.server.LocationManagerServiceUtils.LinkedListener; 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; @@ -90,6 +92,7 @@ public class GnssManagerService { 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; @@ -102,6 +105,11 @@ public class GnssManagerService { private final ArrayMap<IBinder, LinkedListener<GnssRequest, IGnssMeasurementsListener>> mGnssMeasurementsListeners = new ArrayMap<>(); + @GuardedBy("mGnssAntennaInfoListeners") + private final ArrayMap<IBinder, + LinkedListener<Void, IGnssAntennaInfoListener>> + mGnssAntennaInfoListeners = new ArrayMap<>(); + @GuardedBy("mGnssNavigationMessageListeners") private final ArrayMap<IBinder, LinkedListener<Void, IGnssNavigationMessageListener>> mGnssNavigationMessageListeners = new ArrayMap<>(); @@ -151,6 +159,7 @@ public class GnssManagerService { mGnssLocationProvider = gnssLocationProvider; mGnssStatusProvider = mGnssLocationProvider.getGnssStatusProvider(); mGnssMeasurementsProvider = mGnssLocationProvider.getGnssMeasurementsProvider(); + mGnssAntennaInfoProvider = mGnssLocationProvider.getGnssAntennaInfoProvider(); mGnssMeasurementCorrectionsProvider = mGnssLocationProvider.getGnssMeasurementCorrectionsProvider(); mGnssNavigationMessageProvider = mGnssLocationProvider.getGnssNavigationMessageProvider(); @@ -359,6 +368,14 @@ public class GnssManagerService { uid, foreground); } + synchronized (mGnssAntennaInfoListeners) { + updateListenersOnForegroundChangedLocked( + mGnssAntennaInfoListeners, + mGnssAntennaInfoProvider, + IGnssAntennaInfoListener.Stub::asInterface, + uid, + foreground); + } } private <TRequest, TListener extends IInterface> void updateListenersOnForegroundChangedLocked( @@ -546,6 +563,41 @@ public class GnssManagerService { } /** + * 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( + /* request= */ null, + 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 1a8f1f9f0024..d0eaa48022e9 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 "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 "android_runtime/AndroidRuntime.h" -#include "android_runtime/Log.h" #include <arpa/inet.h> #include <cinttypes> @@ -54,6 +55,12 @@ static jclass class_location; 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_reportGeofenceRemoveStatus; 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_gnssNavigationMessageCtor; 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_0 = android::hardware::gnss::V2_0::IGnssConfiguratio 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<IGnssNavigationMessage> gnssNavigationMessageIface = nullptr; 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 @@ Return<void> GnssNavigationMessageCallback::gnssNavigationMessageCb( } /* + * 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 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, "(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 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, 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 @@ static void android_location_GnssLocationProvider_class_init_native(JNIEnv* env, (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 @@ static void android_location_GnssLocationProvider_init_once(JNIEnv* env, jclass } 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 @@ static jboolean android_location_GnssGeofenceProvider_resume_geofence(JNIEnv* /* 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 @@ static const JNINativeMethod sMethodsBatching[] = { 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 @@ static const JNINativeMethod sVisibilityControlMethods[] = { }; 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 000000000000..76f7ad646bb1 --- /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 4ba584e67929..4d0ad96ac223 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.Manifest; 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.GnssNavigationMessage; 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 android.os.RemoteException; 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 @@ public class GnssManagerServiceTest { private GnssMeasurementsProvider mTestGnssMeasurementsProvider; private GnssStatusListenerHelper mTestGnssStatusProvider; private GnssNavigationMessageProvider mTestGnssNavigationMessageProvider; + private GnssAntennaInfoProvider mTestGnssAntennaInfoProvider; // Managers and services @Mock @@ -152,6 +157,8 @@ public class GnssManagerServiceTest { mMockContext, mMockHandler); mTestGnssNavigationMessageProvider = createGnssNavigationMessageProvider( mMockContext, mMockHandler); + mTestGnssAntennaInfoProvider = createGnssAntennaInfoProvider( + mMockContext, mMockHandler); // Setup GnssLocationProvider to return providers when(mMockGnssLocationProvider.getGnssStatusProvider()).thenReturn( @@ -170,6 +177,8 @@ public class GnssManagerServiceTest { 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 @@ public class GnssManagerServiceTest { 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 @@ public class GnssManagerServiceTest { 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 @@ public class GnssManagerServiceTest { }; } + 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 @@ public class GnssManagerServiceTest { } @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(); |