OSDN Git Service

Update location framework to accept raw data from GPS HAL.
authordestradaa <destradaa@google.com>
Tue, 24 Jun 2014 01:19:03 +0000 (18:19 -0700)
committerdestradaa <destradaa@google.com>
Fri, 11 Jul 2014 19:20:39 +0000 (12:20 -0700)
Change-Id: Ib4feca004b53fa89dcece4299974ab08913455a0

15 files changed:
Android.mk
location/java/android/location/GpsClock.java [new file with mode: 0644]
location/java/android/location/GpsMeasurement.java [new file with mode: 0644]
location/java/android/location/GpsMeasurementListenerTransport.java [new file with mode: 0644]
location/java/android/location/GpsMeasurementsEvent.aidl [new file with mode: 0644]
location/java/android/location/GpsMeasurementsEvent.java [new file with mode: 0644]
location/java/android/location/IGpsMeasurementsListener.aidl [new file with mode: 0644]
location/java/android/location/ILocationManager.aidl
location/java/android/location/LocationManager.java
services/core/java/com/android/server/LocationManagerService.java
services/core/java/com/android/server/location/GpsLocationProvider.java
services/core/java/com/android/server/location/GpsMeasurementsProvider.java [new file with mode: 0644]
services/core/java/com/android/server/location/GpsStatusListenerHelper.java [new file with mode: 0644]
services/core/java/com/android/server/location/RemoteListenerHelper.java [new file with mode: 0644]
services/core/jni/com_android_server_location_GpsLocationProvider.cpp

index 5d957d0..a595d88 100644 (file)
@@ -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 (file)
index 0000000..f327c4e
--- /dev/null
@@ -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 (file)
index 0000000..e5a8c8c
--- /dev/null
@@ -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 (file)
index 0000000..48a4b44
--- /dev/null
@@ -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 (file)
index 0000000..2c46262
--- /dev/null
@@ -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 (file)
index 0000000..e04ed81
--- /dev/null
@@ -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 (file)
index 0000000..b34bb6c
--- /dev/null
@@ -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);
+}
index c353ec6..a1acaf1 100644 (file)
@@ -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);
index 4502a5b..d6a8fb8 100644 (file)
 
 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}
index 10382ba..67e58a6 100644 (file)
 
 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
index c5b6c7b..8222155 100644 (file)
 
 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 (file)
index 0000000..001f638
--- /dev/null
@@ -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 (file)
index 0000000..b741e75
--- /dev/null
@@ -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 (file)
index 0000000..79335f7
--- /dev/null
@@ -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);
+        }
+    }
+}
index 5bafb52..87626d0 100644 (file)
@@ -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 */