diff options
author | destradaa <destradaa@google.com> | 2014-06-23 18:19:03 -0700 |
---|---|---|
committer | destradaa <destradaa@google.com> | 2014-07-11 12:20:39 -0700 |
commit | ea8a8a6076f04360de2d25b3e5853cde8026cd5f (patch) | |
tree | 7c416b43028165443b11a3e433d9044b54b9fe81 | |
parent | 34257d8544190979a0c1dfe63350fc50136e4dd0 (diff) | |
download | frameworks_base-ea8a8a6076f04360de2d25b3e5853cde8026cd5f.zip frameworks_base-ea8a8a6076f04360de2d25b3e5853cde8026cd5f.tar.gz frameworks_base-ea8a8a6076f04360de2d25b3e5853cde8026cd5f.tar.bz2 |
Update location framework to accept raw data from GPS HAL.
Change-Id: Ib4feca004b53fa89dcece4299974ab08913455a0
15 files changed, 2835 insertions, 212 deletions
@@ -288,6 +288,7 @@ LOCAL_SRC_FILES += \ location/java/android/location/IFusedProvider.aidl \ location/java/android/location/IGeocodeProvider.aidl \ location/java/android/location/IGeofenceProvider.aidl \ + location/java/android/location/IGpsMeasurementsListener.aidl \ location/java/android/location/IGpsStatusListener.aidl \ location/java/android/location/IGpsStatusProvider.aidl \ location/java/android/location/ILocationListener.aidl \ diff --git a/location/java/android/location/GpsClock.java b/location/java/android/location/GpsClock.java new file mode 100644 index 0000000..f327c4e --- /dev/null +++ b/location/java/android/location/GpsClock.java @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.location; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A class containing a GPS clock timestamp. + * It represents a measurement of the GPS receiver's clock. + * + * @hide + */ +public class GpsClock implements Parcelable { + // mandatory parameters + private long mTimeInNs; + + // optional parameters + private boolean mHasLeapSecond; + private short mLeapSecond; + private boolean mHasTimeUncertaintyInNs; + private double mTimeUncertaintyInNs; + private boolean mHasBiasInNs; + private double mBiasInNs; + private boolean mHasBiasUncertaintyInNs; + private double mBiasUncertaintyInNs; + private boolean mHasDriftInNsPerSec; + private double mDriftInNsPerSec; + private boolean mHasDriftUncertaintyInNsPerSec; + private double mDriftUncertaintyInNsPerSec; + + GpsClock() { + reset(); + } + + /** + * Sets all contents to the values stored in the provided object. + */ + public void set(GpsClock clock) { + mTimeInNs = clock.mTimeInNs; + + mHasLeapSecond = clock.mHasLeapSecond; + mLeapSecond = clock.mLeapSecond; + mHasTimeUncertaintyInNs = clock.mHasTimeUncertaintyInNs; + mTimeUncertaintyInNs = clock.mTimeUncertaintyInNs; + mHasBiasInNs = clock.mHasBiasInNs; + mBiasInNs = clock.mBiasInNs; + mHasBiasUncertaintyInNs = clock.mHasBiasUncertaintyInNs; + mBiasUncertaintyInNs = clock.mBiasUncertaintyInNs; + mHasDriftInNsPerSec = clock.mHasDriftInNsPerSec; + mDriftInNsPerSec = clock.mDriftInNsPerSec; + mHasDriftUncertaintyInNsPerSec = clock.mHasDriftUncertaintyInNsPerSec; + mDriftUncertaintyInNsPerSec = clock.mDriftUncertaintyInNsPerSec; + } + + /** + * Resets all the contents to its original state. + */ + public void reset() { + mTimeInNs = Long.MIN_VALUE; + + resetLeapSecond(); + resetTimeUncertaintyInNs(); + resetBiasInNs(); + resetBiasUncertaintyInNs(); + resetDriftInNsPerSec(); + resetDriftUncertaintyInNsPerSec(); + } + + /** + * Returns true if {@link #getLeapSecond()} is available, false otherwise. + */ + public boolean hasLeapSecond() { + return mHasLeapSecond; + } + + /** + * Gets the leap second associated with the clock's time. + * + * The value is only available if {@link #hasLeapSecond()} is true. + */ + public short getLeapSecond() { + return mLeapSecond; + } + + /** + * Sets the leap second associated with the clock's time. + */ + public void setLeapSecond(short leapSecond) { + mHasLeapSecond = true; + mLeapSecond = leapSecond; + } + + /** + * Resets the leap second associated with the clock's time. + */ + public void resetLeapSecond() { + mHasLeapSecond = false; + mLeapSecond = Short.MIN_VALUE; + } + + /** + * Gets the GPS clock Time in nanoseconds; it represents the uncorrected receiver's GPS time + * since 0000Z, January 6, 1980; this is, including {@link #getBiasInNs()}. + * The reported time includes {@link #getTimeUncertaintyInNs()}. + */ + public long getTimeInNs() { + return mTimeInNs; + } + + /** + * Sets the GPS clock Time in nanoseconds. + */ + public void setTimeInNs(long timeInNs) { + mTimeInNs = timeInNs; + } + + /** + * Returns true if {@link #getTimeUncertaintyInNs()} is available, false otherwise. + */ + public boolean hasTimeUncertaintyInNs() { + return mHasTimeUncertaintyInNs; + } + + /** + * Gets the clock's time Uncertainty (1-Sigma) in nanoseconds. + * + * The value is only available if {@link #hasTimeUncertaintyInNs()} is true. + */ + public double getTimeUncertaintyInNs() { + return mTimeUncertaintyInNs; + } + + /** + * Sets the clock's Time Uncertainty (1-Sigma) in nanoseconds. + */ + public void setTimeUncertaintyInNs(double timeUncertaintyInNs) { + mHasTimeUncertaintyInNs = true; + mTimeUncertaintyInNs = timeUncertaintyInNs; + } + + /** + * Resets the clock's Time Uncertainty (1-Sigma) in nanoseconds. + */ + public void resetTimeUncertaintyInNs() { + mHasTimeUncertaintyInNs = false; + mTimeUncertaintyInNs = Double.NaN; + } + + /** + * Returns true if {@link #getBiasInNs()} is available, false otherwise. + */ + public boolean hasBiasInNs() { + return mHasBiasInNs; + } + + /** + * Gets the clock's Bias in nanoseconds. + * The sign of the value (if available), is defined by the following equation: + * true time = time - bias. + * The reported bias includes {@link #getBiasUncertaintyInNs()}. + * + * The value is only available if {@link #hasBiasInNs()} is true. + */ + public Double getBiasInNs() { + return mBiasInNs; + } + + /** + * Sets the clock's Bias in nanoseconds. + */ + public void setBiasInNs(double biasInNs) { + mHasBiasInNs = true; + mBiasInNs = biasInNs; + } + + /** + * Resets the clock's Bias in nanoseconds. + */ + public void resetBiasInNs() { + mHasBiasInNs = false; + mBiasInNs = Double.NaN; + } + + /** + * Returns true if {@link #getBiasUncertaintyInNs()} is available, false otherwise. + */ + public boolean hasBiasUncertaintyInNs() { + return mHasBiasUncertaintyInNs; + } + + /** + * Gets the clock's Bias Uncertainty (1-Sigma) in nanoseconds. + * + * The value is only available if {@link #hasBiasUncertaintyInNs()} is true. + */ + public double getBiasUncertaintyInNs() { + return mBiasUncertaintyInNs; + } + + /** + * Sets the clock's Bias Uncertainty (1-Sigma) in nanoseconds. + */ + public void setBiasUncertaintyInNs(double biasUncertaintyInNs) { + mHasBiasUncertaintyInNs = true; + mBiasUncertaintyInNs = biasUncertaintyInNs; + } + + /** + * Resets the clock's Bias Uncertainty (1-Sigma) in nanoseconds. + */ + public void resetBiasUncertaintyInNs() { + mHasBiasUncertaintyInNs = false; + mBiasUncertaintyInNs = Double.NaN; + } + + /** + * Returns true if {@link #getDriftInNsPerSec()} is available, false otherwise. + */ + public boolean hasDriftInNsPerSec() { + return mHasDriftInNsPerSec; + } + + /** + * Gets the clock's Drift in nanoseconds per second. + * A positive value indicates that the frequency is higher than the nominal frequency. + * The reported drift includes {@link #getDriftUncertaintyInNsPerSec()}. + * + * The value is only available if {@link #hasDriftInNsPerSec()} is true. + */ + public double getDriftInNsPerSec() { + return mDriftInNsPerSec; + } + + /** + * Sets the clock's Drift in nanoseconds per second. + */ + public void setDriftInNsPerSec(double driftInNsPerSec) { + mHasDriftInNsPerSec = true; + mDriftInNsPerSec = driftInNsPerSec; + } + + /** + * Resets the clock's Drift in nanoseconds per second. + */ + public void resetDriftInNsPerSec() { + mHasDriftInNsPerSec = false; + mDriftInNsPerSec = Double.NaN; + } + + /** + * Returns true if {@link #getDriftUncertaintyInNsPerSec()} is available, false otherwise. + */ + public boolean hasDriftUncertaintyInNsPerSec() { + return mHasDriftUncertaintyInNsPerSec; + } + + /** + * Gets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second. + * + * The value is only available if {@link #hasDriftUncertaintyInNsPerSec()} is true. + */ + public double getDriftUncertaintyInNsPerSec() { + return mDriftUncertaintyInNsPerSec; + } + + /** + * Sets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second. + */ + public void setDriftUncertaintyInNsPerSec(double driftUncertaintyInNsPerSec) { + mHasDriftUncertaintyInNsPerSec = true; + mDriftUncertaintyInNsPerSec = driftUncertaintyInNsPerSec; + } + + /** + * Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second. + */ + public void resetDriftUncertaintyInNsPerSec() { + mHasDriftUncertaintyInNsPerSec = false; + mDriftUncertaintyInNsPerSec = Double.NaN; + } + + public static final Creator<GpsClock> CREATOR = new Creator<GpsClock>() { + @Override + public GpsClock createFromParcel(Parcel parcel) { + GpsClock gpsClock = new GpsClock(); + gpsClock.mTimeInNs = parcel.readLong(); + + gpsClock.mHasLeapSecond = parcel.readInt() != 0; + gpsClock.mLeapSecond = (short) parcel.readInt(); + gpsClock.mHasTimeUncertaintyInNs = parcel.readInt() != 0; + gpsClock.mTimeUncertaintyInNs = parcel.readDouble(); + gpsClock.mHasBiasInNs = parcel.readInt() != 0; + gpsClock.mBiasInNs = parcel.readDouble(); + gpsClock.mHasBiasUncertaintyInNs = parcel.readInt() != 0; + gpsClock.mBiasUncertaintyInNs = parcel.readDouble(); + gpsClock.mHasDriftInNsPerSec = parcel.readInt() != 0; + gpsClock.mDriftInNsPerSec = parcel.readDouble(); + gpsClock.mHasDriftUncertaintyInNsPerSec = parcel.readInt() != 0; + gpsClock.mDriftUncertaintyInNsPerSec = parcel.readDouble(); + + return gpsClock; + } + + @Override + public GpsClock[] newArray(int size) { + return new GpsClock[size]; + } + }; + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeLong(mTimeInNs); + + parcel.writeInt(mHasLeapSecond ? 1 : 0); + parcel.writeInt(mLeapSecond); + parcel.writeInt(mHasTimeUncertaintyInNs ? 1 : 0); + parcel.writeDouble(mTimeUncertaintyInNs); + parcel.writeInt(mHasBiasInNs ? 1 : 0); + parcel.writeDouble(mBiasInNs); + parcel.writeInt(mHasBiasUncertaintyInNs ? 1 : 0); + parcel.writeDouble(mBiasUncertaintyInNs); + parcel.writeInt(mHasDriftInNsPerSec ? 1 : 0); + parcel.writeDouble(mDriftInNsPerSec); + parcel.writeInt(mHasDriftUncertaintyInNsPerSec ? 1 : 0); + parcel.writeDouble(mDriftUncertaintyInNsPerSec); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + final String format = " %-15s = %-25s %-26s = %s\n"; + StringBuilder builder = new StringBuilder("GpsClock:\n"); + + builder.append(String.format( + format, + "LeapSecond", + mHasLeapSecond ? mLeapSecond : null, + "", + "")); + + builder.append(String.format( + format, + "TimeInNs", + mTimeInNs, + "TimeUncertaintyInNs", + mHasTimeUncertaintyInNs ? mTimeUncertaintyInNs : null)); + + builder.append(String.format( + format, + "BiasInNs", + mHasBiasInNs ? mBiasInNs : null, + "BiasUncertaintyInNs", + mHasBiasUncertaintyInNs ? mBiasUncertaintyInNs : null)); + + builder.append(String.format( + format, + "DriftInNsPerSec", + mHasDriftInNsPerSec ? mDriftInNsPerSec : null, + "DriftUncertaintyInNsPerSec", + mHasDriftUncertaintyInNsPerSec ? mDriftUncertaintyInNsPerSec : null)); + + return builder.toString(); + } +} diff --git a/location/java/android/location/GpsMeasurement.java b/location/java/android/location/GpsMeasurement.java new file mode 100644 index 0000000..e5a8c8c --- /dev/null +++ b/location/java/android/location/GpsMeasurement.java @@ -0,0 +1,1222 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.location; + +import android.os.Parcel; +import android.os.Parcelable; +import android.util.Log; + +/** + * A class representing a GPS satellite measurement, containing raw and computed information. + * + * @hide + */ +public class GpsMeasurement implements Parcelable { + private static final String TAG = "GpsMeasurement"; + + // mandatory parameters + private byte mPrn; + private long mLocalTimeInNs; + private long mReceivedGpsTowInNs; + private double mCn0InDbHz; + private double mPseudorangeRateInMetersPerSec; + private double mPseudorangeRateUncertaintyInMetersPerSec; + private double mAccumulatedDeltaRangeInMeters; + private double mAccumulatedDeltaRangeUncertaintyInMeters; + + // optional parameters + private boolean mHasPseudorangeInMeters; + private double mPseudorangeInMeters; + private boolean mHasPseudorangeUncertaintyInMeters; + private double mPseudorangeUncertaintyInMeters; + private boolean mHasCodePhaseInChips; + private double mCodePhaseInChips; + private boolean mHasCodePhaseUncertaintyInChips; + private double mCodePhaseUncertaintyInChips; + private boolean mHasCarrierFrequencyInHz; + private float mCarrierFrequencyInHz; + private boolean mHasCarrierCycles; + private long mCarrierCycles; + private boolean mHasCarrierPhase; + private double mCarrierPhase; + private boolean mHasCarrierPhaseUncertainty; + private double mCarrierPhaseUncertainty; + private short mLossOfLock; + private boolean mHasBitNumber; + private short mBitNumber; + private boolean mHasTimeFromLastBitInNs; + private long mTimeFromLastBitInNs; + private boolean mHasDopplerShiftInHz; + private double mDopplerShiftInHz; + private boolean mHasDopplerShiftUncertaintyInHz; + private double mDopplerShiftUncertaintyInHz; + private short mMultipathIndicator; + private boolean mHasSnrInDb; + private double mSnrInDb; + private boolean mHasElevationInDeg; + private double mElevationInDeg; + private boolean mHasElevationUncertaintyInDeg; + private double mElevationUncertaintyInDeg; + private boolean mHasAzimuthInDeg; + private double mAzimuthInDeg; + private boolean mHasAzimuthUncertaintyInDeg; + private double mAzimuthUncertaintyInDeg; + private boolean mUsedInFix; + + // The following enumerations must be in sync with the values declared in gps.h + + /** + * The indicator is not available or it is unknown. + */ + public static final short LOSS_OF_LOCK_UNKNOWN = 0; + + /** + * The measurement does not present any indication of 'loss of lock'. + */ + public static final short LOSS_OF_LOCK_OK = 1; + + /** + * 'Loss of lock' detected between the previous and current observation: cycle slip possible. + */ + public static final short LOSS_OF_LOCK_CYCLE_SLIP = 2; + + /** + * The indicator is not available or it is unknown. + */ + public static final short MULTIPATH_INDICATOR_UNKNOWN = 0; + + /** + * The measurement has been indicated to use multi-path. + */ + public static final short MULTIPATH_INDICATOR_DETECTED = 1; + + /** + * The measurement has been indicated not tu use multi-path. + */ + public static final short MULTIPATH_INDICATOR_NOT_USED = 2; + + // End enumerations in sync with gps.h + + GpsMeasurement() { + reset(); + } + + /** + * Sets all contents to the values stored in the provided object. + */ + public void set(GpsMeasurement measurement) { + mPrn = measurement.mPrn; + mLocalTimeInNs = measurement.mLocalTimeInNs; + mReceivedGpsTowInNs = measurement.mReceivedGpsTowInNs; + mCn0InDbHz = measurement.mCn0InDbHz; + mPseudorangeRateInMetersPerSec = measurement.mPseudorangeRateInMetersPerSec; + mPseudorangeRateUncertaintyInMetersPerSec = + measurement.mPseudorangeRateUncertaintyInMetersPerSec; + mAccumulatedDeltaRangeInMeters = measurement.mAccumulatedDeltaRangeInMeters; + mAccumulatedDeltaRangeUncertaintyInMeters = + measurement.mAccumulatedDeltaRangeUncertaintyInMeters; + + mHasPseudorangeInMeters = measurement.mHasPseudorangeInMeters; + mPseudorangeInMeters = measurement.mPseudorangeInMeters; + mHasPseudorangeUncertaintyInMeters = measurement.mHasPseudorangeUncertaintyInMeters; + mPseudorangeUncertaintyInMeters = measurement.mPseudorangeUncertaintyInMeters; + mHasCodePhaseInChips = measurement.mHasCodePhaseInChips; + mCodePhaseInChips = measurement.mCodePhaseInChips; + mHasCodePhaseUncertaintyInChips = measurement.mHasCodePhaseUncertaintyInChips; + mCodePhaseUncertaintyInChips = measurement.mCodePhaseUncertaintyInChips; + mHasCarrierFrequencyInHz = measurement.mHasCarrierFrequencyInHz; + mCarrierFrequencyInHz = measurement.mCarrierFrequencyInHz; + mHasCarrierCycles = measurement.mHasCarrierCycles; + mCarrierCycles = measurement.mCarrierCycles; + mHasCarrierPhase = measurement.mHasCarrierPhase; + mCarrierPhase = measurement.mCarrierPhase; + mHasCarrierPhaseUncertainty = measurement.mHasCarrierPhaseUncertainty; + mCarrierPhaseUncertainty = measurement.mCarrierPhaseUncertainty; + mLossOfLock = measurement.mLossOfLock; + mHasBitNumber = measurement.mHasBitNumber; + mBitNumber = measurement.mBitNumber; + mHasTimeFromLastBitInNs = measurement.mHasTimeFromLastBitInNs; + mTimeFromLastBitInNs = measurement.mTimeFromLastBitInNs; + mHasDopplerShiftInHz = measurement.mHasDopplerShiftInHz; + mDopplerShiftInHz = measurement.mDopplerShiftInHz; + mHasDopplerShiftUncertaintyInHz = measurement.mHasDopplerShiftUncertaintyInHz; + mDopplerShiftUncertaintyInHz = measurement.mDopplerShiftUncertaintyInHz; + mMultipathIndicator = measurement.mMultipathIndicator; + mHasSnrInDb = measurement.mHasSnrInDb; + mSnrInDb = measurement.mSnrInDb; + mHasElevationInDeg = measurement.mHasElevationInDeg; + mElevationInDeg = measurement.mElevationInDeg; + mHasElevationUncertaintyInDeg = measurement.mHasElevationUncertaintyInDeg; + mElevationUncertaintyInDeg = measurement.mElevationUncertaintyInDeg; + mHasAzimuthInDeg = measurement.mHasAzimuthInDeg; + mAzimuthInDeg = measurement.mAzimuthInDeg; + mHasAzimuthUncertaintyInDeg = measurement.mHasAzimuthUncertaintyInDeg; + mAzimuthUncertaintyInDeg = measurement.mAzimuthUncertaintyInDeg; + mUsedInFix = measurement.mUsedInFix; + } + + /** + * Resets all the contents to its original state. + */ + public void reset() { + mPrn = Byte.MIN_VALUE; + mLocalTimeInNs = Long.MIN_VALUE; + mReceivedGpsTowInNs = Long.MIN_VALUE; + mCn0InDbHz = Double.MIN_VALUE; + mPseudorangeRateInMetersPerSec = Double.MIN_VALUE; + mPseudorangeRateUncertaintyInMetersPerSec = Double.MIN_VALUE; + mAccumulatedDeltaRangeInMeters = Double.MIN_VALUE; + mAccumulatedDeltaRangeUncertaintyInMeters = Double.MIN_VALUE; + + resetPseudorangeInMeters(); + resetPseudorangeUncertaintyInMeters(); + resetCodePhaseInChips(); + resetCodePhaseUncertaintyInChips(); + resetCarrierFrequencyInHz(); + resetCarrierCycles(); + resetCarrierPhase(); + resetCarrierPhaseUncertainty(); + setLossOfLock(LOSS_OF_LOCK_UNKNOWN); + resetBitNumber(); + resetTimeFromLastBitInNs(); + resetDopplerShiftInHz(); + resetDopplerShiftUncertaintyInHz(); + setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN); + resetSnrInDb(); + resetElevationInDeg(); + resetElevationUncertaintyInDeg(); + resetAzimuthInDeg(); + resetAzimuthUncertaintyInDeg(); + setUsedInFix(false); + } + + /** + * Gets the Pseudo-random number (PRN). + * Range: [1, 32] + */ + public byte getPrn() { + return mPrn; + } + + /** + * Sets the Pseud-random number (PRN). + */ + public void setPrn(byte value) { + mPrn = value; + } + + /** + * Gets the local (hardware) time at which the measurement was taken in nanoseconds. + */ + public long getLocalTimeInNs() { + return mLocalTimeInNs; + } + + /** + * Sets the measurement's local (hardware) time in nanoseconds. + */ + public void setLocalTimeInNs(long value) { + mLocalTimeInNs = value; + } + + /** + * Gets the received GPS Time-of-Week in nanoseconds. + * The value is relative to the beginning of the current GPS week. + */ + public long getReceivedGpsTowInNs() { + return mReceivedGpsTowInNs; + } + + /** + * Sets the received GPS time-of-week in nanoseconds. + */ + public void setReceivedGpsTowInNs(long value) { + mReceivedGpsTowInNs = value; + } + + /** + * Gets the Carrier-to-noise density in dB-Hz. + * Range: [0, 63]. + * + * The value contains the measured C/N0 for the signal at the antenna input. + */ + public double getCn0InDbHz() { + return mCn0InDbHz; + } + + /** + * Sets the carrier-to-noise density in dB-Hz. + */ + public void setCn0InDbHz(double value) { + mCn0InDbHz = value; + } + + /** + * Gets the Pseudorange rate at the timestamp in m/s. + * The reported value includes {@link #getPseudorangeRateUncertaintyInMetersPerSec()}. + */ + public double getPseudorangeRateInMetersPerSec() { + return mPseudorangeRateInMetersPerSec; + } + + /** + * Sets the pseudorange rate at the timestamp in m/s. + */ + public void setPseudorangeRateInMetersPerSec(double value) { + mPseudorangeRateInMetersPerSec = value; + } + + /** + * Gets the pseudorange's rate uncertainty (1-Sigma) in m/s. + * The uncertainty is represented as an absolute (single sided) value. + */ + public double getPseudorangeRateUncertaintyInMetersPerSec() { + return mPseudorangeRateUncertaintyInMetersPerSec; + } + + /** + * Sets the pseudorange's rate uncertainty (1-Sigma) in m/s. + */ + public void setPseudorangeRateUncertaintyInMetersPerSec(double value) { + mPseudorangeRateUncertaintyInMetersPerSec = value; + } + + /** + * Gets the accumulated delta range since the last channel reset, in meters. + * The reported value includes {@link #getAccumulatedDeltaRangeUncertaintyInMeters()}. + */ + public double getAccumulatedDeltaRangeInMeters() { + return mAccumulatedDeltaRangeInMeters; + } + + /** + * Sets the accumulated delta range in meters. + */ + public void setAccumulatedDeltaRangeInMeters(double value) { + mAccumulatedDeltaRangeInMeters = value; + } + + /** + * Gets the accumulated delta range's uncertainty (1-Sigma) in meters. + * The uncertainty is represented as an absolute (single sided) value. + */ + public double getAccumulatedDeltaRangeUncertaintyInMeters() { + return mAccumulatedDeltaRangeUncertaintyInMeters; + } + + /** + * Sets the accumulated delta range's uncertainty (1-sigma) in meters. + */ + public void setAccumulatedDeltaRangeUncertaintyInMeters(double value) { + mAccumulatedDeltaRangeUncertaintyInMeters = value; + } + + /** + * Returns true if {@link #getPseudorangeInMeters()} is available, false otherwise. + */ + public boolean hasPseudorangeInMeters() { + return mHasPseudorangeInMeters; + } + + /** + * Gets the best derived pseudorange by the chipset, in meters. + * The reported pseudorange includes {@link #getPseudorangeUncertaintyInMeters()}. + * + * The value is only available if {@link #hasPseudorangeInMeters()} is true. + */ + public double getPseudorangeInMeters() { + return mPseudorangeInMeters; + } + + /** + * Sets the Pseudo-range in meters. + */ + public void setPseudorangeInMeters(double value) { + mHasPseudorangeInMeters = true; + mPseudorangeInMeters = value; + } + + /** + * Resets the Pseudo-range in meters. + */ + public void resetPseudorangeInMeters() { + mHasPseudorangeInMeters = false; + mPseudorangeInMeters = Double.NaN; + } + + /** + * Returns true if {@link #getPseudorangeUncertaintyInMeters()} is available, false otherwise. + */ + public boolean hasPseudorangeUncertaintyInMeters() { + return mHasPseudorangeUncertaintyInMeters; + } + + /** + * Gets the pseudorange's uncertainty (1-Sigma) in meters. + * The value contains the 'pseudorange' and 'clock' uncertainty in it. + * The uncertainty is represented as an absolute (single sided) value. + * + * The value is only available if {@link #hasPseudorangeUncertaintyInMeters()} is true. + */ + public double getPseudorangeUncertaintyInMeters() { + return mPseudorangeUncertaintyInMeters; + } + + /** + * Sets the pseudo-range's uncertainty (1-Sigma) in meters. + */ + public void setPseudorangeUncertaintyInMeters(double value) { + mHasPseudorangeUncertaintyInMeters = true; + mPseudorangeUncertaintyInMeters = value; + } + + /** + * Resets the pseudo-range's uncertainty (1-Sigma) in meters. + */ + public void resetPseudorangeUncertaintyInMeters() { + mHasPseudorangeUncertaintyInMeters = false; + mPseudorangeUncertaintyInMeters = Double.NaN; + } + + /** + * Returns true if {@link #getCodePhaseInChips()} is available, false otherwise. + */ + public boolean hasCodePhaseInChips() { + return mHasCodePhaseInChips; + } + + /** + * Gets the fraction of the current C/A code cycle. + * Range: [0, 1023] + * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}. + * The reported code-phase includes {@link #getCodePhaseUncertaintyInChips()}. + * + * The value is only available if {@link #hasCodePhaseInChips()} is true. + */ + public double getCodePhaseInChips() { + return mCodePhaseInChips; + } + + /** + * Sets the Code-phase in chips. + */ + public void setCodePhaseInChips(double value) { + mHasCodePhaseInChips = true; + mCodePhaseInChips = value; + } + + /** + * Resets the Code-phase in chips. + */ + public void resetCodePhaseInChips() { + mHasCodePhaseInChips = false; + mCodePhaseInChips = Double.NaN; + } + + /** + * Returns true if {@link #getCodePhaseUncertaintyInChips()} is available, false otherwise. + */ + public boolean hasCodePhaseUncertaintyInChips() { + return mHasCodePhaseUncertaintyInChips; + } + + /** + * Gets the code-phase's uncertainty (1-Sigma) as a fraction of chips. + * The uncertainty is represented as an absolute (single sided) value. + * + * The value is only available if {@link #hasCodePhaseUncertaintyInChips()} is true. + */ + public double getCodePhaseUncertaintyInChips() { + return mCodePhaseUncertaintyInChips; + } + + /** + * Sets the Code-phase's uncertainty (1-Sigma) in fractions of chips. + */ + public void setCodePhaseUncertaintyInChips(double value) { + mHasCodePhaseUncertaintyInChips = true; + mCodePhaseUncertaintyInChips = value; + } + + /** + * Resets the Code-phase's uncertainty (1-Sigma) in fractions of chips. + */ + public void resetCodePhaseUncertaintyInChips() { + mHasCodePhaseUncertaintyInChips = false; + mCodePhaseUncertaintyInChips = Double.NaN; + } + + /** + * Returns true if {@link #getCarrierFrequencyInHz()} is available, false otherwise. + */ + public boolean hasCarrierFrequencyInHz() { + return mHasCarrierFrequencyInHz; + } + + /** + * Gets the carrier frequency at which codes and messages are modulated, it can be L1 or L2. + * If the field is not set, the carrier frequency corresponds to L1. + * + * The value is only available if {@link #hasCarrierFrequencyInHz()} is true. + */ + public float getCarrierFrequencyInHz() { + return mCarrierFrequencyInHz; + } + + /** + * Sets the Carrier frequency (L1 or L2) in Hz. + */ + public void setCarrierFrequencyInHz(float carrierFrequencyInHz) { + mHasCarrierFrequencyInHz = true; + mCarrierFrequencyInHz = carrierFrequencyInHz; + } + + /** + * Resets the Carrier frequency (L1 or L2) in Hz. + */ + public void resetCarrierFrequencyInHz() { + mHasCarrierFrequencyInHz = false; + mCarrierFrequencyInHz = Float.NaN; + } + + /** + * Returns true if {@link #getCarrierCycles()} is available, false otherwise. + */ + public boolean hasCarrierCycles() { + return mHasCarrierCycles; + } + + /** + * The number of full carrier cycles between the satellite and the receiver. + * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}. + * + * The value is only available if {@link #hasCarrierCycles()} is true. + */ + public long getCarrierCycles() { + return mCarrierCycles; + } + + /** + * Sets the number of full carrier cycles between the satellite and the receiver. + */ + public void setCarrierCycles(long value) { + mHasCarrierCycles = true; + mCarrierCycles = value; + } + + /** + * Resets the number of full carrier cycles between the satellite and the receiver. + */ + public void resetCarrierCycles() { + mHasCarrierCycles = false; + mCarrierCycles = Long.MIN_VALUE; + } + + /** + * Returns true if {@link #getCarrierPhase()} is available, false otherwise. + */ + public boolean hasCarrierPhase() { + return mHasCarrierPhase; + } + + /** + * Gets the RF phase detected by the receiver. + * Range: [0.0, 1.0]. + * This is usually the fractional part of the complete carrier phase measurement. + * + * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}. + * The reported carrier-phase includes {@link #getCarrierPhaseUncertainty()}. + * + * The value is only available if {@link #hasCarrierPhase()} is true. + */ + public double getCarrierPhase() { + return mCarrierPhase; + } + + /** + * Sets the RF phase detected by the receiver. + */ + public void setCarrierPhase(double value) { + mHasCarrierPhase = true; + mCarrierPhase = value; + } + + /** + * Resets the RF phase detected by the receiver. + */ + public void resetCarrierPhase() { + mHasCarrierPhase = false; + mCarrierPhase = Double.NaN; + } + + /** + * Returns true if {@link #getCarrierPhaseUncertainty()} is available, false otherwise. + */ + public boolean hasCarrierPhaseUncertainty() { + return mHasCarrierPhaseUncertainty; + } + + /** + * Gets the carrier-phase's uncertainty (1-Sigma). + * The uncertainty is represented as an absolute (single sided) value. + * + * The value is only available if {@link #hasCarrierPhaseUncertainty()} is true. + */ + public double getCarrierPhaseUncertainty() { + return mCarrierPhaseUncertainty; + } + + /** + * Sets the Carrier-phase's uncertainty (1-Sigma) in cycles. + */ + public void setCarrierPhaseUncertainty(double value) { + mHasCarrierPhaseUncertainty = true; + mCarrierPhaseUncertainty = value; + } + + /** + * Resets the Carrier-phase's uncertainty (1-Sigma) in cycles. + */ + public void resetCarrierPhaseUncertainty() { + mHasCarrierPhaseUncertainty = false; + mCarrierPhaseUncertainty = Double.NaN; + } + + /** + * Gets a value indicating the 'loss of lock' state of the event. + */ + public short getLossOfLock() { + return mLossOfLock; + } + + /** + * Sets the 'loss of lock' status. + */ + public void setLossOfLock(short value) { + switch (value) { + case LOSS_OF_LOCK_UNKNOWN: + case LOSS_OF_LOCK_OK: + case LOSS_OF_LOCK_CYCLE_SLIP: + mLossOfLock = value; + break; + default: + Log.d(TAG, "Sanitizing invalid 'loss of lock': " + value); + mLossOfLock = LOSS_OF_LOCK_UNKNOWN; + break; + } + } + + /** + * Gets a string representation of the 'loss of lock'. + * For internal and logging use only. + */ + private String getLossOfLockString() { + switch (mLossOfLock) { + case LOSS_OF_LOCK_UNKNOWN: + return "Unknown"; + case LOSS_OF_LOCK_OK: + return "Ok"; + case LOSS_OF_LOCK_CYCLE_SLIP: + return "CycleSlip"; + default: + return "Invalid"; + } + } + + /** + * Returns true if {@link #getBitNumber()} is available, false otherwise. + */ + public boolean hasBitNumber() { + return mHasBitNumber; + } + + /** + * Gets the number of GPS bits transmitted since Sat-Sun midnight (GPS week). + * + * The value is only available if {@link #hasBitNumber()} is true. + */ + public short getBitNumber() { + return mBitNumber; + } + + /** + * Sets the bit number within the broadcast frame. + */ + public void setBitNumber(short bitNumber) { + mHasBitNumber = true; + mBitNumber = bitNumber; + } + + /** + * Resets the bit number within the broadcast frame. + */ + public void resetBitNumber() { + mHasBitNumber = false; + mBitNumber = Short.MIN_VALUE; + } + + /** + * Returns true if {@link #getTimeFromLastBitInNs()} is available, false otherwise. + */ + public boolean hasTimeFromLastBitInNs() { + return mHasTimeFromLastBitInNs; + } + + /** + * Gets the elapsed time since the last received bit in nanoseconds. + * Range: [0, 20000000]. + * + * The value is only available if {@link #hasTimeFromLastBitInNs()} is true. + */ + public long getTimeFromLastBitInNs() { + return mTimeFromLastBitInNs; + } + + /** + * Sets the elapsed time since the last received bit in nanoseconds. + */ + public void setTimeFromLastBitInNs(long value) { + mHasTimeFromLastBitInNs = true; + mTimeFromLastBitInNs = value; + } + + /** + * Resets the elapsed time since the last received bit in nanoseconds. + */ + public void resetTimeFromLastBitInNs() { + mHasTimeFromLastBitInNs = false; + mTimeFromLastBitInNs = Long.MIN_VALUE; + } + + /** + * Returns true if {@link #getDopplerShiftInHz()} is available, false otherwise. + */ + public boolean hasDopplerShiftInHz() { + return mHasDopplerShiftInHz; + } + + /** + * Gets the Doppler Shift in Hz. + * A positive value indicates that the SV is moving toward the receiver. + * + * The reference frequency is given by the value of {@link #getCarrierFrequencyInHz()}. + * The reported doppler shift includes {@link #getDopplerShiftUncertaintyInHz()}. + * + * The value is only available if {@link #hasDopplerShiftInHz()} is true. + */ + public double getDopplerShiftInHz() { + return mDopplerShiftInHz; + } + + /** + * Sets the Doppler shift in Hz. + */ + public void setDopplerShiftInHz(double value) { + mHasDopplerShiftInHz = true; + mDopplerShiftInHz = value; + } + + /** + * Resets the Doppler shift in Hz. + */ + public void resetDopplerShiftInHz() { + mHasDopplerShiftInHz = false; + mDopplerShiftInHz = Double.NaN; + } + + /** + * Returns true if {@link #getDopplerShiftUncertaintyInHz()} is available, false otherwise. + */ + public boolean hasDopplerShiftUncertaintyInHz() { + return mHasDopplerShiftUncertaintyInHz; + } + + /** + * Gets the Doppler's Shift uncertainty (1-Sigma) in Hz. + * The uncertainty is represented as an absolute (single sided) value. + * + * The value is only available if {@link #hasDopplerShiftUncertaintyInHz()} is true. + */ + public double getDopplerShiftUncertaintyInHz() { + return mDopplerShiftUncertaintyInHz; + } + + /** + * Sets the Doppler's shift uncertainty (1-Sigma) in Hz. + */ + public void setDopplerShiftUncertaintyInHz(double value) { + mHasDopplerShiftUncertaintyInHz = true; + mDopplerShiftUncertaintyInHz = value; + } + + /** + * Resets the Doppler's shift uncertainty (1-Sigma) in Hz. + */ + public void resetDopplerShiftUncertaintyInHz() { + mHasDopplerShiftUncertaintyInHz = false; + mDopplerShiftUncertaintyInHz = Double.NaN; + } + + /** + * Gets a value indicating the 'multipath' state of the event. + */ + public short getMultipathIndicator() { + return mMultipathIndicator; + } + + /** + * Sets the 'multi-path' indicator. + */ + public void setMultipathIndicator(short value) { + switch (value) { + case MULTIPATH_INDICATOR_UNKNOWN: + case MULTIPATH_INDICATOR_DETECTED: + case MULTIPATH_INDICATOR_NOT_USED: + mMultipathIndicator = value; + break; + default: + Log.d(TAG, "Sanitizing invalid 'muti-path indicator': " + value); + mMultipathIndicator = MULTIPATH_INDICATOR_UNKNOWN; + break; + } + } + + /** + * Gets a string representation of the 'multi-path indicator'. + * For internal and logging use only. + */ + private String getMultipathIndicatorString() { + switch(mMultipathIndicator) { + case MULTIPATH_INDICATOR_UNKNOWN: + return "Unknown"; + case MULTIPATH_INDICATOR_DETECTED: + return "Detected"; + case MULTIPATH_INDICATOR_NOT_USED: + return "NotDetected"; + default: + return "Invalid"; + } + } + + /** + * Returns true if {@link #getSnrInDb()} is available, false otherwise. + */ + public boolean hasSnrInDb() { + return mHasSnrInDb; + } + + /** + * Gets the Signal-to-Noise ratio (SNR) in dB. + * + * The value is only available if {@link #hasSnrInDb()} is true. + */ + public double getSnrInDb() { + return mSnrInDb; + } + + /** + * Sets the Signal-to-noise ratio (SNR) in dB. + */ + public void setSnrInDb(double snrInDb) { + mHasSnrInDb = true; + mSnrInDb = snrInDb; + } + + /** + * Resets the Signal-to-noise ratio (SNR) in dB. + */ + public void resetSnrInDb() { + mHasSnrInDb = false; + mSnrInDb = Double.NaN; + } + + /** + * Returns true if {@link #getElevationInDeg()} is available, false otherwise. + */ + public boolean hasElevationInDeg() { + return mHasElevationInDeg; + } + + /** + * Gets the Elevation in degrees. + * Range: [-90, 90] + * The reported elevation includes {@link #getElevationUncertaintyInDeg()}. + * + * The value is only available if {@link #hasElevationInDeg()} is true. + */ + public double getElevationInDeg() { + return mElevationInDeg; + } + + /** + * Sets the Elevation in degrees. + */ + public void setElevationInDeg(double elevationInDeg) { + mHasElevationInDeg = true; + mElevationInDeg = elevationInDeg; + } + + /** + * Resets the Elevation in degrees. + */ + public void resetElevationInDeg() { + mHasElevationInDeg = false; + mElevationInDeg = Double.NaN; + } + + /** + * Returns true if {@link #getElevationUncertaintyInDeg()} is available, false otherwise. + */ + public boolean hasElevationUncertaintyInDeg() { + return mHasElevationUncertaintyInDeg; + } + + /** + * Gets the elevation's uncertainty (1-Sigma) in degrees. + * Range: [0, 90] + * + * The uncertainty is represented as an absolute (single sided) value. + * + * The value is only available if {@link #hasElevationUncertaintyInDeg()} is true. + */ + public double getElevationUncertaintyInDeg() { + return mElevationUncertaintyInDeg; + } + + /** + * Sets the elevation's uncertainty (1-Sigma) in degrees. + */ + public void setElevationUncertaintyInDeg(double value) { + mHasElevationUncertaintyInDeg = true; + mElevationUncertaintyInDeg = value; + } + + /** + * Resets the elevation's uncertainty (1-Sigma) in degrees. + */ + public void resetElevationUncertaintyInDeg() { + mHasElevationUncertaintyInDeg = false; + mElevationUncertaintyInDeg = Double.NaN; + } + + /** + * Returns true if {@link #getAzimuthInDeg()} is available, false otherwise. + */ + public boolean hasAzimuthInDeg() { + return mHasAzimuthInDeg; + } + + /** + * Gets the azimuth in degrees. + * Range: [0, 360). + * + * The reported azimuth includes {@link #getAzimuthUncertaintyInDeg()}. + * + * The value is only available if {@link #hasAzimuthInDeg()} is true. + */ + public double getAzimuthInDeg() { + return mAzimuthInDeg; + } + + /** + * Sets the Azimuth in degrees. + */ + public void setAzimuthInDeg(double value) { + mHasAzimuthInDeg = true; + mAzimuthInDeg = value; + } + + /** + * Resets the Azimuth in degrees. + */ + public void resetAzimuthInDeg() { + mHasAzimuthInDeg = false; + mAzimuthInDeg = Double.NaN; + } + + /** + * Returns true if {@link #getAzimuthUncertaintyInDeg()} is available, false otherwise. + */ + public boolean hasAzimuthUncertaintyInDeg() { + return mHasAzimuthUncertaintyInDeg; + } + + /** + * Gets the azimuth's uncertainty (1-Sigma) in degrees. + * Range: [0, 180]. + * + * The uncertainty is represented as an absolute (single sided) value. + * + * The value is only available if {@link #hasAzimuthUncertaintyInDeg()} is true. + */ + public double getAzimuthUncertaintyInDeg() { + return mAzimuthUncertaintyInDeg; + } + + /** + * Sets the Azimuth's uncertainty (1-Sigma) in degrees. + */ + public void setAzimuthUncertaintyInDeg(double value) { + mHasAzimuthUncertaintyInDeg = true; + mAzimuthUncertaintyInDeg = value; + } + + /** + * Resets the Azimuth's uncertainty (1-Sigma) in degrees. + */ + public void resetAzimuthUncertaintyInDeg() { + mHasAzimuthUncertaintyInDeg = false; + mAzimuthUncertaintyInDeg = Double.NaN; + } + + /** + * Gets a flag indicating whether the GPS represented by the measurement was used for computing + * the most recent fix. + * + * @return A non-null value if the data is available, null otherwise. + */ + public Boolean isUsedInFix() { + return mUsedInFix; + } + + /** + * Sets the Used-in-Fix flag. + */ + public void setUsedInFix(boolean value) { + mUsedInFix = value; + } + + public static final Creator<GpsMeasurement> CREATOR = new Creator<GpsMeasurement>() { + @Override + public GpsMeasurement createFromParcel(Parcel parcel) { + GpsMeasurement gpsMeasurement = new GpsMeasurement(); + + gpsMeasurement.mPrn = parcel.readByte(); + gpsMeasurement.mLocalTimeInNs = parcel.readLong(); + gpsMeasurement.mReceivedGpsTowInNs = parcel.readLong(); + gpsMeasurement.mCn0InDbHz = parcel.readDouble(); + gpsMeasurement.mPseudorangeRateInMetersPerSec = parcel.readDouble(); + gpsMeasurement.mPseudorangeRateUncertaintyInMetersPerSec = parcel.readDouble(); + gpsMeasurement.mAccumulatedDeltaRangeInMeters = parcel.readDouble(); + gpsMeasurement.mAccumulatedDeltaRangeUncertaintyInMeters = parcel.readDouble(); + + gpsMeasurement.mHasPseudorangeInMeters = parcel.readInt() != 0; + gpsMeasurement.mPseudorangeInMeters = parcel.readDouble(); + gpsMeasurement.mHasPseudorangeUncertaintyInMeters = parcel.readInt() != 0; + gpsMeasurement.mPseudorangeUncertaintyInMeters = parcel.readDouble(); + gpsMeasurement.mHasCodePhaseInChips = parcel.readInt() != 0; + gpsMeasurement.mCodePhaseInChips = parcel.readDouble(); + gpsMeasurement.mHasCodePhaseUncertaintyInChips = parcel.readInt() != 0; + gpsMeasurement.mCodePhaseUncertaintyInChips = parcel.readDouble(); + gpsMeasurement.mHasCarrierFrequencyInHz = parcel.readInt() != 0; + gpsMeasurement.mCarrierFrequencyInHz = parcel.readFloat(); + gpsMeasurement.mHasCarrierCycles = parcel.readInt() != 0; + gpsMeasurement.mCarrierCycles = parcel.readLong(); + gpsMeasurement.mHasCarrierPhase = parcel.readInt() != 0; + gpsMeasurement.mCarrierPhase = parcel.readDouble(); + gpsMeasurement.mHasCarrierPhaseUncertainty = parcel.readInt() != 0; + gpsMeasurement.mCarrierPhaseUncertainty = parcel.readDouble(); + gpsMeasurement.mLossOfLock = (short) parcel.readInt(); + gpsMeasurement.mHasBitNumber = parcel.readInt() != 0; + gpsMeasurement.mBitNumber = (short) parcel.readInt(); + gpsMeasurement.mHasTimeFromLastBitInNs = parcel.readInt() != 0; + gpsMeasurement.mTimeFromLastBitInNs = parcel.readLong(); + gpsMeasurement.mHasDopplerShiftInHz = parcel.readInt() != 0; + gpsMeasurement.mDopplerShiftInHz = parcel.readDouble(); + gpsMeasurement.mHasDopplerShiftUncertaintyInHz = parcel.readInt() != 0; + gpsMeasurement.mDopplerShiftUncertaintyInHz = parcel.readDouble(); + gpsMeasurement.mMultipathIndicator = (short) parcel.readInt(); + gpsMeasurement.mHasSnrInDb = parcel.readInt() != 0; + gpsMeasurement.mSnrInDb = parcel.readDouble(); + gpsMeasurement.mHasElevationInDeg = parcel.readInt() != 0; + gpsMeasurement.mElevationInDeg = parcel.readDouble(); + gpsMeasurement.mHasElevationUncertaintyInDeg = parcel.readInt() != 0; + gpsMeasurement.mElevationUncertaintyInDeg = parcel.readDouble(); + gpsMeasurement.mHasAzimuthInDeg = parcel.readInt() != 0; + gpsMeasurement.mAzimuthInDeg = parcel.readDouble(); + gpsMeasurement.mHasAzimuthUncertaintyInDeg = parcel.readInt() != 0; + gpsMeasurement.mAzimuthUncertaintyInDeg = parcel.readDouble(); + gpsMeasurement.mUsedInFix = parcel.readInt() != 0; + + return gpsMeasurement; + } + + @Override + public GpsMeasurement[] newArray(int i) { + return new GpsMeasurement[i]; + } + }; + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeByte(mPrn); + parcel.writeLong(mLocalTimeInNs); + parcel.writeLong(mReceivedGpsTowInNs); + parcel.writeDouble(mCn0InDbHz); + parcel.writeDouble(mPseudorangeRateInMetersPerSec); + parcel.writeDouble(mPseudorangeRateUncertaintyInMetersPerSec); + parcel.writeDouble(mAccumulatedDeltaRangeInMeters); + parcel.writeDouble(mAccumulatedDeltaRangeUncertaintyInMeters); + + parcel.writeInt(mHasPseudorangeInMeters ? 1 : 0); + parcel.writeDouble(mPseudorangeInMeters); + parcel.writeInt(mHasPseudorangeUncertaintyInMeters ? 1 : 0); + parcel.writeDouble(mPseudorangeUncertaintyInMeters); + parcel.writeInt(mHasCodePhaseInChips ? 1 : 0); + parcel.writeDouble(mCodePhaseInChips); + parcel.writeInt(mHasCodePhaseUncertaintyInChips ? 1 : 0); + parcel.writeDouble(mCodePhaseUncertaintyInChips); + parcel.writeInt(mHasCarrierFrequencyInHz ? 1 : 0); + parcel.writeFloat(mCarrierFrequencyInHz); + parcel.writeInt(mHasCarrierCycles ? 1 : 0); + parcel.writeLong(mCarrierCycles); + parcel.writeInt(mHasCarrierPhase ? 1 : 0); + parcel.writeDouble(mCarrierPhase); + parcel.writeInt(mHasCarrierPhaseUncertainty ? 1 : 0); + parcel.writeDouble(mCarrierPhaseUncertainty); + parcel.writeInt(mLossOfLock); + parcel.writeInt(mHasBitNumber ? 1 : 0); + parcel.writeInt(mBitNumber); + parcel.writeInt(mHasTimeFromLastBitInNs ? 1 : 0); + parcel.writeLong(mTimeFromLastBitInNs); + parcel.writeInt(mHasDopplerShiftInHz ? 1 : 0); + parcel.writeDouble(mDopplerShiftInHz); + parcel.writeInt(mHasDopplerShiftUncertaintyInHz ? 1 : 0); + parcel.writeDouble(mDopplerShiftUncertaintyInHz); + parcel.writeInt(mMultipathIndicator); + parcel.writeInt(mHasSnrInDb ? 1 : 0); + parcel.writeDouble(mSnrInDb); + parcel.writeInt(mHasElevationInDeg ? 1 : 0); + parcel.writeDouble(mElevationInDeg); + parcel.writeInt(mHasElevationUncertaintyInDeg ? 1 : 0); + parcel.writeDouble(mElevationUncertaintyInDeg); + parcel.writeInt(mHasAzimuthInDeg ? 1 : 0); + parcel.writeDouble(mAzimuthInDeg); + parcel.writeInt(mHasAzimuthUncertaintyInDeg ? 1 : 0); + parcel.writeDouble(mAzimuthUncertaintyInDeg); + parcel.writeInt(mUsedInFix ? 1 : 0); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + final String format = " %-29s = %s\n"; + final String formatWithUncertainty = " %-29s = %-25s %-40s = %s\n"; + StringBuilder builder = new StringBuilder("GpsMeasurement:\n"); + + builder.append(String.format(format, "Prn", mPrn)); + + builder.append(String.format(format, "LocalTimeInNs", mLocalTimeInNs)); + + builder.append(String.format(format, "ReceivedGpsTowInNs", mReceivedGpsTowInNs)); + + builder.append(String.format(format, "Cn0InDbHz", mCn0InDbHz)); + + builder.append(String.format( + formatWithUncertainty, + "PseudorangeRateInMetersPerSec", + mPseudorangeRateInMetersPerSec, + "PseudorangeRateUncertaintyInMetersPerSec", + mPseudorangeRateUncertaintyInMetersPerSec)); + + builder.append(String.format( + formatWithUncertainty, + "AccumulatedDeltaRangeInMeters", + mAccumulatedDeltaRangeInMeters, + "AccumulatedDeltaRangeUncertaintyInMeters", + mAccumulatedDeltaRangeUncertaintyInMeters)); + + + builder.append(String.format( + formatWithUncertainty, + "PseudorangeInMeters", + mHasPseudorangeInMeters ? mPseudorangeInMeters : null, + "PseudorangeUncertaintyInMeters", + mHasPseudorangeUncertaintyInMeters ? mPseudorangeUncertaintyInMeters : null)); + + builder.append(String.format( + formatWithUncertainty, + "CodePhaseInChips", + mHasCodePhaseInChips ? mCodePhaseInChips : null, + "CodePhaseUncertaintyInChips", + mHasCodePhaseUncertaintyInChips ? mCodePhaseUncertaintyInChips : null)); + + builder.append(String.format( + format, + "CarrierFrequencyInHz", + mHasCarrierFrequencyInHz ? mCarrierFrequencyInHz : null)); + + builder.append(String.format( + format, + "CarrierCycles", + mHasCarrierCycles ? mCarrierCycles : null)); + + builder.append(String.format( + formatWithUncertainty, + "CarrierPhase", + mHasCarrierPhase ? mCarrierPhase : null, + "CarrierPhaseUncertainty", + mHasCarrierPhaseUncertainty ? mCarrierPhaseUncertainty : null)); + + builder.append(String.format(format, "LossOfLock", getLossOfLockString())); + + builder.append(String.format( + format, + "BitNumber", + mHasBitNumber ? mBitNumber : null)); + + builder.append(String.format( + format, + "TimeFromLastBitInNs", + mHasTimeFromLastBitInNs ? mTimeFromLastBitInNs : null)); + + builder.append(String.format( + formatWithUncertainty, + "DopplerShiftInHz", + mHasDopplerShiftInHz ? mDopplerShiftInHz : null, + "DopplerShiftUncertaintyInHz", + mHasDopplerShiftUncertaintyInHz ? mDopplerShiftUncertaintyInHz : null)); + + builder.append(String.format(format, "MultipathIndicator", getMultipathIndicatorString())); + + builder.append(String.format( + format, + "SnrInDb", + mHasSnrInDb ? mSnrInDb : null)); + + builder.append(String.format( + formatWithUncertainty, + "ElevationInDeg", + mHasElevationInDeg ? mElevationInDeg : null, + "ElevationUncertaintyInDeg", + mHasElevationUncertaintyInDeg ? mElevationUncertaintyInDeg : null)); + + builder.append(String.format( + formatWithUncertainty, + "AzimuthInDeg", + mHasAzimuthInDeg ? mAzimuthInDeg : null, + "AzimuthUncertaintyInDeg", + mHasAzimuthUncertaintyInDeg ? mAzimuthUncertaintyInDeg : null)); + + builder.append(String.format(format, "UsedInFix", mUsedInFix)); + + return builder.toString(); + } +} diff --git a/location/java/android/location/GpsMeasurementListenerTransport.java b/location/java/android/location/GpsMeasurementListenerTransport.java new file mode 100644 index 0000000..48a4b44 --- /dev/null +++ b/location/java/android/location/GpsMeasurementListenerTransport.java @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.location; + +import com.android.internal.util.Preconditions; + +import android.annotation.NonNull; +import android.content.Context; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; + +/** + * A handler class to manage transport listeners for {@link GpsMeasurementsEvent.Listener}, + * and post the events in a handler. + * + * @hide + */ +class GpsMeasurementListenerTransport { + private static final String TAG = "GpsMeasurementListenerTransport"; + + private final Context mContext; + private final ILocationManager mLocationManager; + + private final IGpsMeasurementsListener mListenerTransport = new ListenerTransport(); + private final HashSet<GpsMeasurementsEvent.Listener> mListeners = + new HashSet<GpsMeasurementsEvent.Listener>(); + + public GpsMeasurementListenerTransport(Context context, ILocationManager locationManager) { + mContext = context; + mLocationManager = locationManager; + } + + public boolean add(@NonNull GpsMeasurementsEvent.Listener listener) { + Preconditions.checkNotNull(listener); + + synchronized (mListeners) { + // we need to register with the service first, because we need to find out if the + // service will actually support the request before we attempt anything + if (mListeners.isEmpty()) { + boolean registeredWithServer; + try { + registeredWithServer = mLocationManager.addGpsMeasurementsListener( + mListenerTransport, + mContext.getPackageName()); + } catch (RemoteException e) { + Log.e(TAG, "Error handling first listener.", e); + return false; + } + + if (!registeredWithServer) { + Log.e(TAG, "Unable to register listener transport."); + return false; + } + } + + if (mListeners.contains(listener)) { + return true; + } + + mListeners.add(listener); + } + + return true; + } + + public void remove(@NonNull GpsMeasurementsEvent.Listener listener) { + Preconditions.checkNotNull(listener); + + synchronized (mListeners) { + boolean removed = mListeners.remove(listener); + + boolean isLastListener = removed && mListeners.isEmpty(); + if (isLastListener) { + try { + mLocationManager.removeGpsMeasurementsListener(mListenerTransport); + } catch (RemoteException e) { + Log.e(TAG, "Error handling last listener.", e); + } + } + } + } + + private class ListenerTransport extends IGpsMeasurementsListener.Stub { + @Override + public void onGpsMeasurementsReceived(final GpsMeasurementsEvent eventArgs) { + Collection<GpsMeasurementsEvent.Listener> listeners; + synchronized (mListeners) { + listeners = new ArrayList<GpsMeasurementsEvent.Listener>(mListeners); + } + + for (final GpsMeasurementsEvent.Listener listener : listeners) { + listener.onGpsMeasurementsReceived(eventArgs); + } + } + } +} diff --git a/location/java/android/location/GpsMeasurementsEvent.aidl b/location/java/android/location/GpsMeasurementsEvent.aidl new file mode 100644 index 0000000..2c46262 --- /dev/null +++ b/location/java/android/location/GpsMeasurementsEvent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +parcelable GpsMeasurementsEvent; diff --git a/location/java/android/location/GpsMeasurementsEvent.java b/location/java/android/location/GpsMeasurementsEvent.java new file mode 100644 index 0000000..e04ed81 --- /dev/null +++ b/location/java/android/location/GpsMeasurementsEvent.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.location; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.security.InvalidParameterException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +/** + * A class implementing a container for data associated with a measurement event. + * Events are delivered to registered instances of {@link Listener}. + * + * @hide + */ +public class GpsMeasurementsEvent implements Parcelable { + private final GpsClock mClock; + private final Collection<GpsMeasurement> mReadOnlyMeasurements; + + /** + * Used for receiving GPS satellite measurements from the GPS engine. + * Each measurement contains raw and computed data identifying a satellite. + * You can implement this interface and call {@link LocationManager#addGpsMeasurementListener}. + * + * @hide + */ + public interface Listener { + void onGpsMeasurementsReceived(GpsMeasurementsEvent eventArgs); + } + + public GpsMeasurementsEvent(GpsClock clock, GpsMeasurement[] measurements) { + if (clock == null) { + throw new InvalidParameterException("Parameter 'clock' must not be null."); + } + if (measurements == null || measurements.length == 0) { + throw new InvalidParameterException( + "Parameter 'measurements' must not be null or empty."); + } + + mClock = clock; + Collection<GpsMeasurement> measurementCollection = Arrays.asList(measurements); + mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection); + } + + @NonNull + public GpsClock getClock() { + return mClock; + } + + /** + * Gets a read-only collection of measurements associated with the current event. + */ + @NonNull + public Collection<GpsMeasurement> getMeasurements() { + return mReadOnlyMeasurements; + } + + public static final Creator<GpsMeasurementsEvent> CREATOR = + new Creator<GpsMeasurementsEvent>() { + @Override + public GpsMeasurementsEvent createFromParcel(Parcel in) { + ClassLoader classLoader = getClass().getClassLoader(); + + GpsClock clock = in.readParcelable(classLoader); + + int measurementsLength = in.readInt(); + GpsMeasurement[] measurementsArray = new GpsMeasurement[measurementsLength]; + in.readTypedArray(measurementsArray, GpsMeasurement.CREATOR); + + return new GpsMeasurementsEvent(clock, measurementsArray); + } + + @Override + public GpsMeasurementsEvent[] newArray(int size) { + return new GpsMeasurementsEvent[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeParcelable(mClock, flags); + + GpsMeasurement[] measurementsArray = mReadOnlyMeasurements.toArray(new GpsMeasurement[0]); + parcel.writeInt(measurementsArray.length); + parcel.writeTypedArray(measurementsArray, flags); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("[ GpsMeasurementsEvent:\n\n"); + + builder.append(mClock.toString()); + builder.append("\n"); + + for (GpsMeasurement measurement : mReadOnlyMeasurements) { + builder.append(measurement.toString()); + builder.append("\n"); + } + + builder.append("]"); + + return builder.toString(); + } +} diff --git a/location/java/android/location/IGpsMeasurementsListener.aidl b/location/java/android/location/IGpsMeasurementsListener.aidl new file mode 100644 index 0000000..b34bb6c --- /dev/null +++ b/location/java/android/location/IGpsMeasurementsListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.location.GpsMeasurementsEvent; + +/** + * {@hide} + */ +oneway interface IGpsMeasurementsListener { + void onGpsMeasurementsReceived(in GpsMeasurementsEvent event); +} diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index c353ec6..a1acaf1 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -21,7 +21,7 @@ import android.location.Address; import android.location.Criteria; import android.location.GeocoderParams; import android.location.Geofence; -import android.location.IGeocodeProvider; +import android.location.IGpsMeasurementsListener; import android.location.IGpsStatusListener; import android.location.ILocationListener; import android.location.Location; @@ -60,6 +60,9 @@ interface ILocationManager boolean sendNiResponse(int notifId, int userResponse); + boolean addGpsMeasurementsListener(in IGpsMeasurementsListener listener, in String packageName); + boolean removeGpsMeasurementsListener(in IGpsMeasurementsListener listener); + // --- deprecated --- List<String> getAllProviders(); List<String> getProviders(in Criteria criteria, boolean enabledOnly); diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index 4502a5b..d6a8fb8 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -16,25 +16,23 @@ package android.location; +import com.android.internal.location.ProviderProperties; + import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; -import android.os.Looper; -import android.os.RemoteException; import android.os.Handler; +import android.os.Looper; import android.os.Message; +import android.os.RemoteException; import android.util.Log; - -import java.lang.SecurityException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; -import com.android.internal.location.ProviderProperties; - /** * This class provides access to the system location services. These * services allow applications to obtain periodic updates of the @@ -60,6 +58,7 @@ public class LocationManager { private final Context mContext; private final ILocationManager mService; + private final GpsMeasurementListenerTransport mGpsMeasurementListenerTransport; private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners = new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>(); private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners = @@ -310,6 +309,7 @@ public class LocationManager { public LocationManager(Context context, ILocationManager service) { mService = service; mContext = context; + mGpsMeasurementListenerTransport = new GpsMeasurementListenerTransport(mContext, mService); } private LocationProvider createProvider(String name, ProviderProperties properties) { @@ -1570,6 +1570,29 @@ public class LocationManager { } } + /** + * Adds a GPS Measurement listener. + * + * @param listener a {@link android.location.GpsMeasurementsEvent.Listener} object to register. + * @return {@code true} if the listener was successfully registered, {@code false} otherwise. + * + * @hide + */ + public boolean addGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) { + return mGpsMeasurementListenerTransport.add(listener); + } + + /** + * Removes a GPS Measurement listener. + * + * @param listener a {@link GpsMeasurementsEvent.Listener} object to remove. + * + * @hide + */ + public void removeGpsMeasurementListener(GpsMeasurementsEvent.Listener listener) { + mGpsMeasurementListenerTransport.remove(listener); + } + /** * Retrieves information about the current status of the GPS engine. * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged} diff --git a/services/core/java/com/android/server/LocationManagerService.java b/services/core/java/com/android/server/LocationManagerService.java index 10382ba..67e58a6 100644 --- a/services/core/java/com/android/server/LocationManagerService.java +++ b/services/core/java/com/android/server/LocationManagerService.java @@ -16,6 +16,27 @@ package com.android.server; +import com.android.internal.content.PackageMonitor; +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; +import com.android.internal.os.BackgroundThread; +import com.android.server.location.FlpHardwareProvider; +import com.android.server.location.FusedProxy; +import com.android.server.location.GeocoderProxy; +import com.android.server.location.GeofenceManager; +import com.android.server.location.GeofenceProxy; +import com.android.server.location.GpsLocationProvider; +import com.android.server.location.GpsMeasurementsProvider; +import com.android.server.location.LocationBlacklist; +import com.android.server.location.LocationFudger; +import com.android.server.location.LocationProviderInterface; +import com.android.server.location.LocationProviderProxy; +import com.android.server.location.LocationRequestStatistics; +import com.android.server.location.LocationRequestStatistics.PackageProviderKey; +import com.android.server.location.LocationRequestStatistics.PackageStatistics; +import com.android.server.location.MockProvider; +import com.android.server.location.PassiveProvider; + import android.app.AppOpsManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; @@ -36,6 +57,7 @@ import android.location.Address; import android.location.Criteria; import android.location.GeocoderParams; import android.location.Geofence; +import android.location.IGpsMeasurementsListener; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; import android.location.ILocationListener; @@ -62,26 +84,6 @@ import android.provider.Settings; import android.util.Log; import android.util.Slog; -import com.android.internal.content.PackageMonitor; -import com.android.internal.location.ProviderProperties; -import com.android.internal.location.ProviderRequest; -import com.android.internal.os.BackgroundThread; -import com.android.server.location.FlpHardwareProvider; -import com.android.server.location.FusedProxy; -import com.android.server.location.GeocoderProxy; -import com.android.server.location.GeofenceProxy; -import com.android.server.location.GeofenceManager; -import com.android.server.location.GpsLocationProvider; -import com.android.server.location.LocationBlacklist; -import com.android.server.location.LocationFudger; -import com.android.server.location.LocationProviderInterface; -import com.android.server.location.LocationProviderProxy; -import com.android.server.location.LocationRequestStatistics; -import com.android.server.location.LocationRequestStatistics.PackageProviderKey; -import com.android.server.location.LocationRequestStatistics.PackageStatistics; -import com.android.server.location.MockProvider; -import com.android.server.location.PassiveProvider; - import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; @@ -154,6 +156,7 @@ public class LocationManagerService extends ILocationManager.Stub { private LocationWorkerHandler mLocationHandler; private PassiveProvider mPassiveProvider; // track passive provider for special cases private LocationBlacklist mBlacklist; + private GpsMeasurementsProvider mGpsMeasurementsProvider; // --- fields below are protected by mLock --- // Set of providers that are explicitly enabled @@ -403,6 +406,7 @@ public class LocationManagerService extends ILocationManager.Stub { addProviderLocked(gpsProvider); mRealProviders.put(LocationManager.GPS_PROVIDER, gpsProvider); } + mGpsMeasurementsProvider = gpsProvider.getGpsMeasurementsProvider(); /* Load package name(s) containing location provider support. @@ -1804,6 +1808,36 @@ public class LocationManagerService extends ILocationManager.Stub { } @Override + public boolean addGpsMeasurementsListener( + IGpsMeasurementsListener listener, + String packageName) { + int allowedResolutionLevel = getCallerAllowedResolutionLevel(); + checkResolutionLevelIsSufficientForProviderUse( + allowedResolutionLevel, + LocationManager.GPS_PROVIDER); + + int uid = Binder.getCallingUid(); + long identity = Binder.clearCallingIdentity(); + boolean hasLocationAccess; + try { + hasLocationAccess = checkLocationAccess(uid, packageName, allowedResolutionLevel); + } finally { + Binder.restoreCallingIdentity(identity); + } + + if (!hasLocationAccess) { + return false; + } + + return mGpsMeasurementsProvider.addListener(listener); + } + + @Override + public boolean removeGpsMeasurementsListener(IGpsMeasurementsListener listener) { + return mGpsMeasurementsProvider.removeListener(listener); + } + + @Override public boolean sendExtraCommand(String provider, String command, Bundle extras) { if (provider == null) { // throw NullPointerException to remain compatible with previous implementation diff --git a/services/core/java/com/android/server/location/GpsLocationProvider.java b/services/core/java/com/android/server/location/GpsLocationProvider.java index c5b6c7b..8222155 100644 --- a/services/core/java/com/android/server/location/GpsLocationProvider.java +++ b/services/core/java/com/android/server/location/GpsLocationProvider.java @@ -16,6 +16,15 @@ package com.android.server.location; +import com.android.internal.app.IAppOpsService; +import com.android.internal.app.IBatteryStats; +import com.android.internal.location.GpsNetInitiatedHandler; +import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification; +import com.android.internal.location.ProviderProperties; +import com.android.internal.location.ProviderRequest; +import com.android.internal.telephony.Phone; +import com.android.internal.telephony.PhoneConstants; + import android.app.AlarmManager; import android.app.AppOpsManager; import android.app.PendingIntent; @@ -28,6 +37,7 @@ import android.hardware.location.GeofenceHardware; import android.hardware.location.GeofenceHardwareImpl; import android.location.Criteria; import android.location.FusedBatchOptions; +import android.location.GpsMeasurementsEvent; import android.location.IGpsGeofenceHardware; import android.location.IGpsStatusListener; import android.location.IGpsStatusProvider; @@ -46,7 +56,6 @@ import android.os.BatteryStats; import android.os.Binder; import android.os.Bundle; import android.os.Handler; -import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PowerManager; @@ -64,27 +73,17 @@ import android.telephony.gsm.GsmCellLocation; import android.util.Log; import android.util.NtpTrustedTime; -import com.android.internal.app.IAppOpsService; -import com.android.internal.app.IBatteryStats; -import com.android.internal.location.GpsNetInitiatedHandler; -import com.android.internal.location.ProviderProperties; -import com.android.internal.location.ProviderRequest; -import com.android.internal.location.GpsNetInitiatedHandler.GpsNiNotification; -import com.android.internal.telephony.Phone; -import com.android.internal.telephony.PhoneConstants; - import java.io.File; import java.io.FileDescriptor; import java.io.FileInputStream; import java.io.IOException; import java.io.PrintWriter; import java.io.StringReader; -import java.util.ArrayList; +import java.net.InetAddress; +import java.net.UnknownHostException; import java.util.Date; import java.util.Map.Entry; import java.util.Properties; -import java.net.InetAddress; -import java.net.UnknownHostException; /** * A GPS implementation of LocationProvider used by LocationManager. @@ -314,7 +313,12 @@ public class GpsLocationProvider implements LocationProviderInterface { private final ILocationManager mILocationManager; private Location mLocation = new Location(LocationManager.GPS_PROVIDER); private Bundle mLocationExtras = new Bundle(); - private ArrayList<Listener> mListeners = new ArrayList<Listener>(); + private GpsStatusListenerHelper mListenerHelper = new GpsStatusListenerHelper() { + @Override + protected boolean isSupported() { + return native_is_measurement_supported(); + } + }; // Handler for processing events private Handler mHandler; @@ -348,49 +352,29 @@ public class GpsLocationProvider implements LocationProviderInterface { private final IGpsStatusProvider mGpsStatusProvider = new IGpsStatusProvider.Stub() { @Override public void addGpsStatusListener(IGpsStatusListener listener) throws RemoteException { - if (listener == null) { - throw new NullPointerException("listener is null in addGpsStatusListener"); - } - - synchronized (mListeners) { - IBinder binder = listener.asBinder(); - int size = mListeners.size(); - for (int i = 0; i < size; i++) { - Listener test = mListeners.get(i); - if (binder.equals(test.mListener.asBinder())) { - // listener already added - return; - } - } - - Listener l = new Listener(listener); - binder.linkToDeath(l, 0); - mListeners.add(l); - } + mListenerHelper.addListener(listener); } @Override public void removeGpsStatusListener(IGpsStatusListener listener) { - if (listener == null) { - throw new NullPointerException("listener is null in addGpsStatusListener"); - } + mListenerHelper.removeListener(listener); + } + }; - synchronized (mListeners) { - IBinder binder = listener.asBinder(); - Listener l = null; - int size = mListeners.size(); - for (int i = 0; i < size && l == null; i++) { - Listener test = mListeners.get(i); - if (binder.equals(test.mListener.asBinder())) { - l = test; - } - } + private final GpsMeasurementsProvider mGpsMeasurementsProvider = new GpsMeasurementsProvider() { + @Override + public boolean isSupported() { + return native_is_measurement_supported(); + } - if (l != null) { - mListeners.remove(l); - binder.unlinkToDeath(l, 0); - } - } + @Override + protected void onFirstListenerAdded() { + native_start_measurement_collection(); + } + + @Override + protected void onLastListenerRemoved() { + native_stop_measurement_collection(); } }; @@ -402,6 +386,10 @@ public class GpsLocationProvider implements LocationProviderInterface { return mGpsGeofenceBinder; } + public GpsMeasurementsProvider getGpsMeasurementsProvider() { + return mGpsMeasurementsProvider; + } + private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); @@ -892,26 +880,6 @@ public class GpsLocationProvider implements LocationProviderInterface { } } - private final class Listener implements IBinder.DeathRecipient { - final IGpsStatusListener mListener; - - Listener(IGpsStatusListener listener) { - mListener = listener; - } - - @Override - public void binderDied() { - if (DEBUG) Log.d(TAG, "GPS status listener died"); - - synchronized (mListeners) { - mListeners.remove(this); - } - if (mListener != null) { - mListener.asBinder().unlinkToDeath(this, 0); - } - } - } - private void updateClientUids(WorkSource source) { // Update work source. WorkSource[] changes = mClientSource.setReturningDiffs(source); @@ -1185,20 +1153,7 @@ public class GpsLocationProvider implements LocationProviderInterface { if (DEBUG) Log.d(TAG, "TTFF: " + mTimeToFirstFix); // notify status listeners - synchronized (mListeners) { - int size = mListeners.size(); - for (int i = 0; i < size; i++) { - Listener listener = mListeners.get(i); - try { - listener.mListener.onFirstFix(mTimeToFirstFix); - } catch (RemoteException e) { - Log.w(TAG, "RemoteException in stopNavigating"); - mListeners.remove(listener); - // adjust for size of list changing - size--; - } - } - } + mListenerHelper.onFirstFix(mTimeToFirstFix); } if (mSingleShot) { @@ -1232,49 +1187,31 @@ public class GpsLocationProvider implements LocationProviderInterface { private void reportStatus(int status) { if (DEBUG) Log.v(TAG, "reportStatus status: " + status); - synchronized (mListeners) { - boolean wasNavigating = mNavigating; - - switch (status) { - case GPS_STATUS_SESSION_BEGIN: - mNavigating = true; - mEngineOn = true; - break; - case GPS_STATUS_SESSION_END: - mNavigating = false; - break; - case GPS_STATUS_ENGINE_ON: - mEngineOn = true; - break; - case GPS_STATUS_ENGINE_OFF: - mEngineOn = false; - mNavigating = false; - break; - } + boolean wasNavigating = mNavigating; + switch (status) { + case GPS_STATUS_SESSION_BEGIN: + mNavigating = true; + mEngineOn = true; + break; + case GPS_STATUS_SESSION_END: + mNavigating = false; + break; + case GPS_STATUS_ENGINE_ON: + mEngineOn = true; + break; + case GPS_STATUS_ENGINE_OFF: + mEngineOn = false; + mNavigating = false; + break; + } - if (wasNavigating != mNavigating) { - int size = mListeners.size(); - for (int i = 0; i < size; i++) { - Listener listener = mListeners.get(i); - try { - if (mNavigating) { - listener.mListener.onGpsStarted(); - } else { - listener.mListener.onGpsStopped(); - } - } catch (RemoteException e) { - Log.w(TAG, "RemoteException in reportStatus"); - mListeners.remove(listener); - // adjust for size of list changing - size--; - } - } + if (wasNavigating != mNavigating) { + mListenerHelper.onStatusChanged(mNavigating); - // send an intent to notify that the GPS has been enabled or disabled. - Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION); - intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating); - mContext.sendBroadcastAsUser(intent, UserHandle.ALL); - } + // send an intent to notify that the GPS has been enabled or disabled + Intent intent = new Intent(LocationManager.GPS_ENABLED_CHANGE_ACTION); + intent.putExtra(LocationManager.EXTRA_GPS_ENABLED, mNavigating); + mContext.sendBroadcastAsUser(intent, UserHandle.ALL); } } @@ -1282,25 +1219,16 @@ public class GpsLocationProvider implements LocationProviderInterface { * called from native code to update SV info */ private void reportSvStatus() { - int svCount = native_read_sv_status(mSvs, mSnrs, mSvElevations, mSvAzimuths, mSvMasks); - - synchronized (mListeners) { - int size = mListeners.size(); - for (int i = 0; i < size; i++) { - Listener listener = mListeners.get(i); - try { - listener.mListener.onSvStatusChanged(svCount, mSvs, mSnrs, - mSvElevations, mSvAzimuths, mSvMasks[EPHEMERIS_MASK], - mSvMasks[ALMANAC_MASK], mSvMasks[USED_FOR_FIX_MASK]); - } catch (RemoteException e) { - Log.w(TAG, "RemoteException in reportSvInfo"); - mListeners.remove(listener); - // adjust for size of list changing - size--; - } - } - } + mListenerHelper.onSvStatusChanged( + svCount, + mSvs, + mSnrs, + mSvElevations, + mSvAzimuths, + mSvMasks[EPHEMERIS_MASK], + mSvMasks[ALMANAC_MASK], + mSvMasks[USED_FOR_FIX_MASK]); if (VERBOSE) { Log.v(TAG, "SV count: " + svCount + @@ -1398,26 +1326,16 @@ public class GpsLocationProvider implements LocationProviderInterface { * called from native code to report NMEA data received */ private void reportNmea(long timestamp) { - synchronized (mListeners) { - int size = mListeners.size(); - if (size > 0) { - // don't bother creating the String if we have no listeners - int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length); - String nmea = new String(mNmeaBuffer, 0, length); - - for (int i = 0; i < size; i++) { - Listener listener = mListeners.get(i); - try { - listener.mListener.onNmeaReceived(timestamp, nmea); - } catch (RemoteException e) { - Log.w(TAG, "RemoteException in reportNmea"); - mListeners.remove(listener); - // adjust for size of list changing - size--; - } - } - } - } + int length = native_read_nmea(mNmeaBuffer, mNmeaBuffer.length); + String nmea = new String(mNmeaBuffer, 0 /* offset */, length); + mListenerHelper.onNmeaReceived(timestamp, nmea); + } + + /** + * called from native code - Gps Data callback + */ + private void reportMeasurementData(GpsMeasurementsEvent event) { + mGpsMeasurementsProvider.onMeasurementsAvailable(event); } /** @@ -1845,7 +1763,7 @@ public class GpsLocationProvider implements LocationProviderInterface { Log.e(TAG, "No APN found to select."); } } catch (Exception e) { - Log.e(TAG, "Error encountered on selectiong the APN.", e); + Log.e(TAG, "Error encountered on selecting the APN.", e); } finally { if (cursor != null) { cursor.close(); @@ -2008,4 +1926,9 @@ public class GpsLocationProvider implements LocationProviderInterface { private static native boolean native_remove_geofence(int geofenceId); private static native boolean native_resume_geofence(int geofenceId, int transitions); private static native boolean native_pause_geofence(int geofenceId); + + // Gps Hal measurements support. + private static native boolean native_is_measurement_supported(); + private static native boolean native_start_measurement_collection(); + private static native boolean native_stop_measurement_collection(); } diff --git a/services/core/java/com/android/server/location/GpsMeasurementsProvider.java b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java new file mode 100644 index 0000000..001f638 --- /dev/null +++ b/services/core/java/com/android/server/location/GpsMeasurementsProvider.java @@ -0,0 +1,44 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.location; + +import android.location.GpsMeasurementsEvent; +import android.location.IGpsMeasurementsListener; +import android.os.RemoteException; + +/** + * An base implementation for GPS measurements provider. + * It abstracts out the responsibility of handling listeners, while still allowing technology + * specific implementations to be built. + * + * @hide + */ +public abstract class GpsMeasurementsProvider + extends RemoteListenerHelper<IGpsMeasurementsListener> { + + public void onMeasurementsAvailable(final GpsMeasurementsEvent event) { + ListenerOperation<IGpsMeasurementsListener> operation = + new ListenerOperation<IGpsMeasurementsListener>() { + @Override + public void execute(IGpsMeasurementsListener listener) throws RemoteException { + listener.onGpsMeasurementsReceived(event); + } + }; + + foreach(operation); + } +} diff --git a/services/core/java/com/android/server/location/GpsStatusListenerHelper.java b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java new file mode 100644 index 0000000..b741e75 --- /dev/null +++ b/services/core/java/com/android/server/location/GpsStatusListenerHelper.java @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.location; + +import android.location.IGpsStatusListener; +import android.os.RemoteException; + +/** + * Implementation of a handler for {@link IGpsStatusListener}. + */ +abstract class GpsStatusListenerHelper extends RemoteListenerHelper<IGpsStatusListener> { + public void onFirstFix(final int timeToFirstFix) { + Operation operation = new Operation() { + @Override + public void execute(IGpsStatusListener listener) throws RemoteException { + listener.onFirstFix(timeToFirstFix); + } + }; + + foreach(operation); + } + + public void onStatusChanged(final boolean isNavigating) { + Operation operation = new Operation() { + @Override + public void execute(IGpsStatusListener listener) throws RemoteException { + if (isNavigating) { + listener.onGpsStarted(); + } else { + listener.onGpsStopped(); + } + } + }; + + foreach(operation); + } + + public void onSvStatusChanged( + final int svCount, + final int[] prns, + final float[] snrs, + final float[] elevations, + final float[] azimuths, + final int ephemerisMask, + final int almanacMask, + final int usedInFixMask) { + Operation operation = new Operation() { + @Override + public void execute(IGpsStatusListener listener) throws RemoteException { + listener.onSvStatusChanged( + svCount, + prns, + snrs, + elevations, + azimuths, + ephemerisMask, + almanacMask, + usedInFixMask); + } + }; + + foreach(operation); + } + + public void onNmeaReceived(final long timestamp, final String nmea) { + Operation operation = new Operation() { + @Override + public void execute(IGpsStatusListener listener) throws RemoteException { + listener.onNmeaReceived(timestamp, nmea); + } + }; + + foreach(operation); + } + + private abstract class Operation implements ListenerOperation<IGpsStatusListener> { } +} diff --git a/services/core/java/com/android/server/location/RemoteListenerHelper.java b/services/core/java/com/android/server/location/RemoteListenerHelper.java new file mode 100644 index 0000000..79335f7 --- /dev/null +++ b/services/core/java/com/android/server/location/RemoteListenerHelper.java @@ -0,0 +1,148 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package com.android.server.location; + +import com.android.internal.util.Preconditions; + +import android.annotation.NonNull; +import android.os.IBinder; +import android.os.IInterface; +import android.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; + +/** + * A helper class, that handles operations in remote listeners, and tracks for remote process death. + */ +abstract class RemoteListenerHelper<TListener extends IInterface> { + private static final String TAG = "RemoteListenerHelper"; + + private final HashMap<IBinder, LinkedListener> mListenerMap = + new HashMap<IBinder, LinkedListener>(); + + public boolean addListener(@NonNull TListener listener) { + Preconditions.checkNotNull(listener, "Attempted to register a 'null' listener."); + + if (!isSupported()) { + Log.e(TAG, "Refused to add listener, the feature is not supported."); + return false; + } + + IBinder binder = listener.asBinder(); + LinkedListener deathListener = new LinkedListener(listener); + synchronized (mListenerMap) { + if (mListenerMap.containsKey(binder)) { + // listener already added + return true; + } + + try { + binder.linkToDeath(deathListener, 0 /* flags */); + } catch (RemoteException e) { + // if the remote process registering the listener is already death, just swallow the + // exception and continue + Log.e(TAG, "Remote listener already died.", e); + return false; + } + + mListenerMap.put(binder, deathListener); + if (mListenerMap.size() == 1) { + onFirstListenerAdded(); + } + } + + return true; + } + + public boolean removeListener(@NonNull TListener listener) { + Preconditions.checkNotNull(listener, "Attempted to remove a 'null' listener."); + + if (!isSupported()) { + Log.e(TAG, "Refused to remove listener, the feature is not supported."); + return false; + } + + IBinder binder = listener.asBinder(); + LinkedListener linkedListener; + synchronized (mListenerMap) { + linkedListener = mListenerMap.remove(binder); + if (mListenerMap.isEmpty() && linkedListener != null) { + onLastListenerRemoved(); + } + } + + if (linkedListener != null) { + binder.unlinkToDeath(linkedListener, 0 /* flags */); + } + + return true; + } + + protected abstract boolean isSupported(); + + protected void onFirstListenerAdded() { + // event triggered when the first listener has been added + } + + protected void onLastListenerRemoved() { + // event triggered when the last listener has bee removed + } + + protected interface ListenerOperation<TListener extends IInterface> { + void execute(TListener listener) throws RemoteException; + } + + protected void foreach(ListenerOperation operation) { + Collection<LinkedListener> linkedListeners; + synchronized (mListenerMap) { + Collection<LinkedListener> values = mListenerMap.values(); + linkedListeners = new ArrayList<LinkedListener>(values); + } + + for (LinkedListener linkedListener : linkedListeners) { + TListener listener = linkedListener.getUnderlyingListener(); + try { + operation.execute(listener); + } catch (RemoteException e) { + Log.e(TAG, "Error in monitored listener.", e); + removeListener(listener); + } + } + } + + private class LinkedListener implements IBinder.DeathRecipient { + private final TListener mListener; + + public LinkedListener(@NonNull TListener listener) { + mListener = listener; + } + + @NonNull + public TListener getUnderlyingListener() { + return mListener; + } + + @Override + public void binderDied() { + Log.d(TAG, "Remote Listener died: " + mListener); + removeListener(mListener); + } + } +} diff --git a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp index 5bafb52..87626d0 100644 --- a/services/core/jni/com_android_server_location_GpsLocationProvider.cpp +++ b/services/core/jni/com_android_server_location_GpsLocationProvider.cpp @@ -52,6 +52,7 @@ static jmethodID method_reportGeofenceAddStatus; static jmethodID method_reportGeofenceRemoveStatus; static jmethodID method_reportGeofencePauseStatus; static jmethodID method_reportGeofenceResumeStatus; +static jmethodID method_reportMeasurementData; static const GpsInterface* sGpsInterface = NULL; static const GpsXtraInterface* sGpsXtraInterface = NULL; @@ -60,6 +61,7 @@ static const GpsNiInterface* sGpsNiInterface = NULL; static const GpsDebugInterface* sGpsDebugInterface = NULL; static const AGpsRilInterface* sAGpsRilInterface = NULL; static const GpsGeofencingInterface* sGpsGeofencingInterface = NULL; +static const GpsMeasurementInterface* sGpsMeasurementInterface = NULL; // temporary storage for GPS callbacks static GpsSvStatus sGpsSvStatus; @@ -441,6 +443,10 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, "(II)V"); method_reportGeofencePauseStatus = env->GetMethodID(clazz,"reportGeofencePauseStatus", "(II)V"); + method_reportMeasurementData = env->GetMethodID( + clazz, + "reportMeasurementData", + "(Landroid/location/GpsMeasurementsEvent;)V"); err = hw_get_module(GPS_HARDWARE_MODULE_ID, (hw_module_t const**)&module); if (err == 0) { @@ -464,6 +470,8 @@ static void android_location_GpsLocationProvider_class_init_native(JNIEnv* env, (const AGpsRilInterface*)sGpsInterface->get_extension(AGPS_RIL_INTERFACE); sGpsGeofencingInterface = (const GpsGeofencingInterface*)sGpsInterface->get_extension(GPS_GEOFENCING_INTERFACE); + sGpsMeasurementInterface = + (const GpsMeasurementInterface*)sGpsInterface->get_extension(GPS_MEASUREMENT_INTERFACE); } } @@ -851,42 +859,500 @@ static jboolean android_location_GpsLocationProvider_resume_geofence(JNIEnv* env return JNI_FALSE; } +static jobject translate_gps_clock(JNIEnv* env, GpsClock* clock) { + const char* doubleSignature = "(D)V"; + + jclass gpsClockClass = env->FindClass("android/location/GpsClock"); + jmethodID gpsClockCtor = env->GetMethodID(gpsClockClass, "<init>", "()V"); + + jobject gpsClockObject = env->NewObject(gpsClockClass, gpsClockCtor); + GpsClockFlags flags = clock->flags; + + if (flags & GPS_CLOCK_HAS_LEAP_SECOND) { + jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setLeapSecond", "(S)V"); + env->CallObjectMethod(gpsClockObject, setterMethod, clock->leap_second); + } + + jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setTimeInNs", "(J)V"); + env->CallObjectMethod(gpsClockObject, setterMethod, clock->time_ns); + + if (flags & GPS_CLOCK_HAS_TIME_UNCERTAINTY) { + jmethodID setterMethod = env->GetMethodID( + gpsClockClass, + "setTimeUncertaintyInNs", + doubleSignature); + env->CallObjectMethod(gpsClockObject, setterMethod, clock->time_uncertainty_ns); + } + + if (flags & GPS_CLOCK_HAS_BIAS) { + jmethodID setterMethod = env->GetMethodID(gpsClockClass, "setBiasInNs", doubleSignature); + env->CallObjectMethod(gpsClockObject, setterMethod, clock->bias_ns); + } + + if (flags & GPS_CLOCK_HAS_BIAS_UNCERTAINTY) { + jmethodID setterMethod = env->GetMethodID( + gpsClockClass, + "setBiasUncertaintyInNs", + doubleSignature); + env->CallObjectMethod(gpsClockObject, setterMethod, clock->bias_uncertainty_ns); + } + + if (flags & GPS_CLOCK_HAS_DRIFT) { + jmethodID setterMethod = env->GetMethodID( + gpsClockClass, + "setDriftInNsPerSec", + doubleSignature); + env->CallObjectMethod(gpsClockObject, setterMethod, clock->drift_nsps); + } + + if (flags & GPS_CLOCK_HAS_DRIFT_UNCERTAINTY) { + jmethodID setterMethod = env->GetMethodID( + gpsClockClass, + "setDriftUncertaintyInNsPerSec", + doubleSignature); + env->CallObjectMethod(gpsClockObject, setterMethod, clock->drift_uncertainty_nsps); + } + + return gpsClockObject; +} + +static jobject translate_gps_measurement( + JNIEnv* env, + GpsMeasurement* measurement, + uint32_t time_ns) { + const char* shortSignature = "(S)V"; + const char* longSignature = "(J)V"; + const char* floatSignature = "(F)V"; + const char* doubleSignature = "(D)V"; + + jclass gpsMeasurementClass = env->FindClass("android/location/GpsMeasurement"); + jmethodID gpsMeasurementCtor = env->GetMethodID(gpsMeasurementClass, "<init>", "()V"); + + jobject gpsMeasurementObject = env->NewObject(gpsMeasurementClass, gpsMeasurementCtor); + GpsMeasurementFlags flags = measurement->flags; + + + jmethodID prnSetterMethod = env->GetMethodID(gpsMeasurementClass, "setPrn", "(B)V"); + env->CallObjectMethod(gpsMeasurementObject, prnSetterMethod, measurement->prn); + + jmethodID localTimeSetterMethod = + env->GetMethodID(gpsMeasurementClass, "setLocalTimeInNs", longSignature); + env->CallObjectMethod( + gpsMeasurementObject, + localTimeSetterMethod, + time_ns + measurement->time_offset_ns); + + jmethodID receivedGpsTowSetterMethod = + env->GetMethodID(gpsMeasurementClass, "setReceivedGpsTowInNs", longSignature); + env->CallObjectMethod( + gpsMeasurementObject, + receivedGpsTowSetterMethod, + measurement->received_gps_tow_ns); + + jmethodID cn0SetterMethod = env->GetMethodID( + gpsMeasurementClass, + "setCn0InDbHz", + doubleSignature); + env->CallObjectMethod(gpsMeasurementObject, cn0SetterMethod, measurement->c_n0_dbhz); + + jmethodID pseudorangeRateSetterMethod = env->GetMethodID( + gpsMeasurementClass, + "setPseudorangeRateInMetersPerSec", + doubleSignature); + env->CallObjectMethod( + gpsMeasurementObject, + pseudorangeRateSetterMethod, + measurement->pseudorange_rate_mpersec); + + jmethodID pseudorangeRateUncertaintySetterMethod = env->GetMethodID( + gpsMeasurementClass, + "setPseudorangeRateUncertaintyInMetersPerSec", + doubleSignature); + env->CallObjectMethod( + gpsMeasurementObject, + pseudorangeRateUncertaintySetterMethod, + measurement->pseudorange_rate_uncertainty_mpersec); + + jmethodID accumulatedDeltaRangeSetterMethod = env->GetMethodID( + gpsMeasurementClass, + "setAccumulatedDeltaRangeInMeters", + doubleSignature); + env->CallVoidMethod( + gpsMeasurementObject, + accumulatedDeltaRangeSetterMethod, + measurement->accumulated_delta_range_m); + + jmethodID accumulatedDeltaRangeUncertaintySetterMethod = env->GetMethodID( + gpsMeasurementClass, + "setAccumulatedDeltaRangeUncertaintyInMeters", + doubleSignature); + env->CallVoidMethod( + gpsMeasurementObject, + accumulatedDeltaRangeUncertaintySetterMethod, + measurement->accumulated_delta_range_uncertainty_m); + + + if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setPseudorangeInMeters", + doubleSignature); + env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->pseudorange_m); + } + + if (flags & GPS_MEASUREMENT_HAS_PSEUDORANGE_UNCERTAINTY) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setPseudorangeUncertaintyInMeters", + doubleSignature); + env->CallObjectMethod( + gpsMeasurementObject, + setterMethod, + measurement->pseudorange_uncertainty_m); + } + + if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setCodePhaseInChips", + doubleSignature); + env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->code_phase_chips); + } + + if (flags & GPS_MEASUREMENT_HAS_CODE_PHASE_UNCERTAINTY) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setCodePhaseUncertaintyInChips", + doubleSignature); + env->CallObjectMethod( + gpsMeasurementObject, + setterMethod, + measurement->code_phase_uncertainty_chips); + } + + if (flags & GPS_MEASUREMENT_HAS_CARRIER_FREQUENCY) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setCarrierFrequencyInHz", + floatSignature); + env->CallObjectMethod( + gpsMeasurementObject, + setterMethod, + measurement->carrier_frequency_hz); + } + + if (flags & GPS_MEASUREMENT_HAS_CARRIER_CYCLES) { + jmethodID setterMethod = + env->GetMethodID(gpsMeasurementClass, "setCarrierCycles", longSignature); + env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->carrier_cycles); + } + + if (flags & GPS_MEASUREMENT_HAS_CARRIER_PHASE) { + jmethodID setterMethod = + env->GetMethodID(gpsMeasurementClass, "setCarrierPhase", doubleSignature); + env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->carrier_phase); + } + + if (flags & GPS_MEASUREMENT_HAS_CARRIER_PHASE_UNCERTAINTY) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setCarrierPhaseUncertainty", + doubleSignature); + env->CallObjectMethod( + gpsMeasurementObject, + setterMethod, + measurement->carrier_phase_uncertainty); + } + + jmethodID lossOfLockSetterMethod = + env->GetMethodID(gpsMeasurementClass, "setLossOfLock", shortSignature); + env->CallObjectMethod(gpsMeasurementObject, lossOfLockSetterMethod, measurement->loss_of_lock); + + if (flags & GPS_MEASUREMENT_HAS_BIT_NUMBER) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setBitNumber", + shortSignature); + env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->bit_number); + } + + if (flags & GPS_MEASUREMENT_HAS_TIME_FROM_LAST_BIT) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setTimeFromLastBitInNs", + longSignature); + env->CallObjectMethod( + gpsMeasurementObject, + setterMethod, + measurement->time_from_last_bit_ns); + } + + if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setDopplerShiftInHz", + doubleSignature); + env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->doppler_shift_hz); + } + + if (flags & GPS_MEASUREMENT_HAS_DOPPLER_SHIFT_UNCERTAINTY) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setDopplerShiftUncertaintyInHz", + doubleSignature); + env->CallObjectMethod( + gpsMeasurementObject, + setterMethod, + measurement->doppler_shift_uncertainty_hz); + } + + jmethodID multipathIndicatorSetterMethod = env->GetMethodID( + gpsMeasurementClass, + "setMultipathIndicator", + shortSignature); + env->CallObjectMethod( + gpsMeasurementObject, + multipathIndicatorSetterMethod, + measurement->multipath_indicator); + + if (flags & GPS_MEASUREMENT_HAS_SNR) { + jmethodID setterMethod = + env->GetMethodID(gpsMeasurementClass, "setSnrInDb", doubleSignature); + env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->snr_db); + } + + if (flags & GPS_MEASUREMENT_HAS_ELEVATION) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setElevationInDeg", + doubleSignature); + env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->elevation_deg); + } + + if (flags & GPS_MEASUREMENT_HAS_ELEVATION_UNCERTAINTY) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setElevationUncertaintyInDeg", + doubleSignature); + env->CallObjectMethod( + gpsMeasurementObject, + setterMethod, + measurement->elevation_uncertainty_deg); + } + + if (flags & GPS_MEASUREMENT_HAS_AZIMUTH) { + jmethodID setterMethod = + env->GetMethodID(gpsMeasurementClass, "setAzimuthInDeg", doubleSignature); + env->CallObjectMethod(gpsMeasurementObject, setterMethod, measurement->azimuth_deg); + } + + if (flags & GPS_MEASUREMENT_HAS_AZIMUTH_UNCERTAINTY) { + jmethodID setterMethod = env->GetMethodID( + gpsMeasurementClass, + "setAzimuthUncertaintyInDeg", + doubleSignature); + env->CallObjectMethod( + gpsMeasurementObject, + setterMethod, + measurement->azimuth_uncertainty_deg); + } + + jmethodID usedInFixSetterMethod = env->GetMethodID(gpsMeasurementClass, "setUsedInFix", "(Z)V"); + env->CallObjectMethod( + gpsMeasurementObject, + usedInFixSetterMethod, + (flags & GPS_MEASUREMENT_HAS_USED_IN_FIX) && measurement->used_in_fix); + + return gpsMeasurementObject; +} + +static jobjectArray translate_gps_measurements(JNIEnv* env, GpsData* data) { + size_t measurementCount = data->measurement_count; + if (measurementCount == 0) { + return NULL; + } + + jclass gpsMeasurementClass = env->FindClass("android/location/GpsMeasurement"); + jobjectArray gpsMeasurementArray = env->NewObjectArray( + measurementCount, + gpsMeasurementClass, + NULL /* initialElement */); + + GpsMeasurement* gpsMeasurements = data->measurements; + for (uint16_t i = 0; i < measurementCount; ++i) { + jobject gpsMeasurement = translate_gps_measurement( + env, + &gpsMeasurements[i], + data->clock.time_ns); + env->SetObjectArrayElement(gpsMeasurementArray, i, gpsMeasurement); + env->DeleteLocalRef(gpsMeasurement); + } + + env->DeleteLocalRef(gpsMeasurementClass); + return gpsMeasurementArray; +} + +static void measurement_callback(GpsData* data) { + JNIEnv* env = AndroidRuntime::getJNIEnv(); + if (data == NULL) { + ALOGE("Invalid data provided to gps_measurement_callback"); + return; + } + + if (data->size == sizeof(GpsData)) { + jobject gpsClock = translate_gps_clock(env, &data->clock); + jobjectArray measurementArray = translate_gps_measurements(env, data); + + jclass gpsMeasurementsEventClass = env->FindClass("android/location/GpsMeasurementsEvent"); + jmethodID gpsMeasurementsEventCtor = env->GetMethodID( + gpsMeasurementsEventClass, + "<init>", + "(Landroid/location/GpsClock;[Landroid/location/GpsMeasurement;)V"); + + jobject gpsMeasurementsEvent = env->NewObject( + gpsMeasurementsEventClass, + gpsMeasurementsEventCtor, + gpsClock, + measurementArray); + + env->CallVoidMethod(mCallbacksObj, method_reportMeasurementData, gpsMeasurementsEvent); + checkAndClearExceptionFromCallback(env, __FUNCTION__); + } else { + ALOGE("Invalid GpsData size found in gps_measurement_callback, size=%d", data->size); + return; + } +} + +GpsMeasurementCallbacks sGpsMeasurementCallbacks = { + sizeof(GpsMeasurementCallbacks), + measurement_callback, +}; + +static jboolean android_location_GpsLocationProvider_is_measurement_supported( + JNIEnv* env, + jobject obj) { + if (sGpsMeasurementInterface != NULL) { + return JNI_TRUE; + } + return JNI_FALSE; +} + +static jboolean android_location_GpsLocationProvider_start_measurement_collection( + JNIEnv* env, + jobject obj) { + if (sGpsMeasurementInterface == NULL) { + ALOGE("Measurement interface is not available."); + return JNI_FALSE; + } + + int result = sGpsMeasurementInterface->init(&sGpsMeasurementCallbacks); + if (result != GPS_GEOFENCE_OPERATION_SUCCESS) { + ALOGE("An error has been found on GpsMeasurementInterface::init, status=%d", result); + return JNI_FALSE; + } + + return JNI_TRUE; +} + +static jboolean android_location_GpsLocationProvider_stop_measurement_collection( + JNIEnv* env, + jobject obj) { + if (sGpsMeasurementInterface == NULL) { + ALOGE("Measurement interface not available"); + return JNI_FALSE; + } + + sGpsMeasurementInterface->close(); + return JNI_TRUE; +} + static JNINativeMethod sMethods[] = { /* name, signature, funcPtr */ {"class_init_native", "()V", (void *)android_location_GpsLocationProvider_class_init_native}, {"native_is_supported", "()Z", (void*)android_location_GpsLocationProvider_is_supported}, {"native_init", "()Z", (void*)android_location_GpsLocationProvider_init}, {"native_cleanup", "()V", (void*)android_location_GpsLocationProvider_cleanup}, - {"native_set_position_mode", "(IIIII)Z", (void*)android_location_GpsLocationProvider_set_position_mode}, + {"native_set_position_mode", + "(IIIII)Z", + (void*)android_location_GpsLocationProvider_set_position_mode}, {"native_start", "()Z", (void*)android_location_GpsLocationProvider_start}, {"native_stop", "()Z", (void*)android_location_GpsLocationProvider_stop}, - {"native_delete_aiding_data", "(I)V", (void*)android_location_GpsLocationProvider_delete_aiding_data}, - {"native_read_sv_status", "([I[F[F[F[I)I", (void*)android_location_GpsLocationProvider_read_sv_status}, + {"native_delete_aiding_data", + "(I)V", + (void*)android_location_GpsLocationProvider_delete_aiding_data}, + {"native_read_sv_status", + "([I[F[F[F[I)I", + (void*)android_location_GpsLocationProvider_read_sv_status}, {"native_read_nmea", "([BI)I", (void*)android_location_GpsLocationProvider_read_nmea}, {"native_inject_time", "(JJI)V", (void*)android_location_GpsLocationProvider_inject_time}, - {"native_inject_location", "(DDF)V", (void*)android_location_GpsLocationProvider_inject_location}, + {"native_inject_location", + "(DDF)V", + (void*)android_location_GpsLocationProvider_inject_location}, {"native_supports_xtra", "()Z", (void*)android_location_GpsLocationProvider_supports_xtra}, - {"native_inject_xtra_data", "([BI)V", (void*)android_location_GpsLocationProvider_inject_xtra_data}, - {"native_agps_data_conn_open", "(Ljava/lang/String;I)V", (void*)android_location_GpsLocationProvider_agps_data_conn_open}, - {"native_agps_data_conn_closed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_closed}, - {"native_agps_data_conn_failed", "()V", (void*)android_location_GpsLocationProvider_agps_data_conn_failed}, - {"native_agps_set_id","(ILjava/lang/String;)V",(void*)android_location_GpsLocationProvider_agps_set_id}, - {"native_agps_set_ref_location_cellid","(IIIII)V",(void*)android_location_GpsLocationProvider_agps_set_reference_location_cellid}, - {"native_set_agps_server", "(ILjava/lang/String;I)V", (void*)android_location_GpsLocationProvider_set_agps_server}, - {"native_send_ni_response", "(II)V", (void*)android_location_GpsLocationProvider_send_ni_response}, - {"native_agps_ni_message", "([BI)V", (void *)android_location_GpsLocationProvider_agps_send_ni_message}, - {"native_get_internal_state", "()Ljava/lang/String;", (void*)android_location_GpsLocationProvider_get_internal_state}, - {"native_update_network_state", "(ZIZZLjava/lang/String;Ljava/lang/String;)V", (void*)android_location_GpsLocationProvider_update_network_state }, - {"native_is_geofence_supported", "()Z", (void*) android_location_GpsLocationProvider_is_geofence_supported}, - {"native_add_geofence", "(IDDDIIII)Z", (void *)android_location_GpsLocationProvider_add_geofence}, - {"native_remove_geofence", "(I)Z", (void *)android_location_GpsLocationProvider_remove_geofence}, + {"native_inject_xtra_data", + "([BI)V", + (void*)android_location_GpsLocationProvider_inject_xtra_data}, + {"native_agps_data_conn_open", + "(Ljava/lang/String;I)V", + (void*)android_location_GpsLocationProvider_agps_data_conn_open}, + {"native_agps_data_conn_closed", + "()V", + (void*)android_location_GpsLocationProvider_agps_data_conn_closed}, + {"native_agps_data_conn_failed", + "()V", + (void*)android_location_GpsLocationProvider_agps_data_conn_failed}, + {"native_agps_set_id", + "(ILjava/lang/String;)V", + (void*)android_location_GpsLocationProvider_agps_set_id}, + {"native_agps_set_ref_location_cellid", + "(IIIII)V", + (void*)android_location_GpsLocationProvider_agps_set_reference_location_cellid}, + {"native_set_agps_server", + "(ILjava/lang/String;I)V", + (void*)android_location_GpsLocationProvider_set_agps_server}, + {"native_send_ni_response", + "(II)V", + (void*)android_location_GpsLocationProvider_send_ni_response}, + {"native_agps_ni_message", + "([BI)V", + (void *)android_location_GpsLocationProvider_agps_send_ni_message}, + {"native_get_internal_state", + "()Ljava/lang/String;", + (void*)android_location_GpsLocationProvider_get_internal_state}, + {"native_update_network_state", + "(ZIZZLjava/lang/String;Ljava/lang/String;)V", + (void*)android_location_GpsLocationProvider_update_network_state }, + {"native_is_geofence_supported", + "()Z", + (void*) android_location_GpsLocationProvider_is_geofence_supported}, + {"native_add_geofence", + "(IDDDIIII)Z", + (void *)android_location_GpsLocationProvider_add_geofence}, + {"native_remove_geofence", + "(I)Z", + (void *)android_location_GpsLocationProvider_remove_geofence}, {"native_pause_geofence", "(I)Z", (void *)android_location_GpsLocationProvider_pause_geofence}, - {"native_resume_geofence", "(II)Z", (void *)android_location_GpsLocationProvider_resume_geofence} + {"native_resume_geofence", + "(II)Z", + (void *)android_location_GpsLocationProvider_resume_geofence}, + {"native_is_measurement_supported", + "()Z", + (void*) android_location_GpsLocationProvider_is_measurement_supported}, + {"native_start_measurement_collection", + "()Z", + (void*) android_location_GpsLocationProvider_start_measurement_collection}, + {"native_stop_measurement_collection", + "()Z", + (void*) android_location_GpsLocationProvider_stop_measurement_collection} }; int register_android_server_location_GpsLocationProvider(JNIEnv* env) { - return jniRegisterNativeMethods(env, "com/android/server/location/GpsLocationProvider", sMethods, NELEM(sMethods)); + return jniRegisterNativeMethods( + env, + "com/android/server/location/GpsLocationProvider", + sMethods, + NELEM(sMethods)); } } /* namespace android */ |