diff options
Diffstat (limited to 'location')
25 files changed, 3418 insertions, 104 deletions
diff --git a/location/java/android/location/FusedBatchOptions.java b/location/java/android/location/FusedBatchOptions.java index 623d707..5600aeb 100644 --- a/location/java/android/location/FusedBatchOptions.java +++ b/location/java/android/location/FusedBatchOptions.java @@ -95,8 +95,9 @@ public class FusedBatchOptions implements Parcelable { } public static final class BatchFlags { - public static int WAKEUP_ON_FIFO_FULL = 1<<0; - public static int CALLBACK_ON_LOCATION_FIX = 1<<1; + // follow the definitions to the letter in fused_location.h + public static int WAKEUP_ON_FIFO_FULL = 0x0000001; + public static int CALLBACK_ON_LOCATION_FIX =0x0000002; } /* diff --git a/location/java/android/location/GpsClock.java b/location/java/android/location/GpsClock.java new file mode 100644 index 0000000..963b604 --- /dev/null +++ b/location/java/android/location/GpsClock.java @@ -0,0 +1,512 @@ +/* + * 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 containing a GPS clock timestamp. + * It represents a measurement of the GPS receiver's clock. + * + * @hide + */ +public class GpsClock implements Parcelable { + private static final String TAG = "GpsClock"; + + // The following enumerations must be in sync with the values declared in gps.h + + /** + * The type of the time stored is not available or it is unknown. + */ + public static final byte TYPE_UNKNOWN = 0; + + /** + * The source of the time value reported by this class is the 'Local Hardware Clock'. + */ + public static final byte TYPE_LOCAL_HW_TIME = 1; + + /** + * The source of the time value reported by this class is the 'GPS time' derived from + * satellites (epoch = Jan 6, 1980). + */ + public static final byte TYPE_GPS_TIME = 2; + + private static final short HAS_NO_FLAGS = 0; + private static final short HAS_LEAP_SECOND = (1<<0); + private static final short HAS_TIME_UNCERTAINTY = (1<<1); + private static final short HAS_FULL_BIAS = (1<<2); + private static final short HAS_BIAS = (1<<3); + private static final short HAS_BIAS_UNCERTAINTY = (1<<4); + private static final short HAS_DRIFT = (1<<5); + private static final short HAS_DRIFT_UNCERTAINTY = (1<<6); + + // End enumerations in sync with gps.h + + private short mFlags; + private short mLeapSecond; + private byte mType; + private long mTimeInNs; + private double mTimeUncertaintyInNs; + private long mFullBiasInNs; + private double mBiasInNs; + private double mBiasUncertaintyInNs; + private double mDriftInNsPerSec; + private double mDriftUncertaintyInNsPerSec; + + GpsClock() { + initialize(); + } + + /** + * Sets all contents to the values stored in the provided object. + */ + public void set(GpsClock clock) { + mFlags = clock.mFlags; + mLeapSecond = clock.mLeapSecond; + mType = clock.mType; + mTimeInNs = clock.mTimeInNs; + mTimeUncertaintyInNs = clock.mTimeUncertaintyInNs; + mFullBiasInNs = clock.mFullBiasInNs; + mBiasInNs = clock.mBiasInNs; + mBiasUncertaintyInNs = clock.mBiasUncertaintyInNs; + mDriftInNsPerSec = clock.mDriftInNsPerSec; + mDriftUncertaintyInNsPerSec = clock.mDriftUncertaintyInNsPerSec; + } + + /** + * Resets all the contents to its original state. + */ + public void reset() { + initialize(); + } + + /** + * Gets the type of time reported by {@link #getTimeInNs()}. + */ + public byte getType() { + return mType; + } + + /** + * Sets the type of time reported. + */ + public void setType(byte value) { + switch (value) { + case TYPE_UNKNOWN: + case TYPE_GPS_TIME: + case TYPE_LOCAL_HW_TIME: + mType = value; + break; + default: + Log.d(TAG, "Sanitizing invalid 'type': " + value); + mType = TYPE_UNKNOWN; + break; + } + } + + /** + * Gets a string representation of the 'type'. + * For internal and logging use only. + */ + private String getTypeString() { + switch (mType) { + case TYPE_UNKNOWN: + return "Unknown"; + case TYPE_GPS_TIME: + return "GpsTime"; + case TYPE_LOCAL_HW_TIME: + return "LocalHwClock"; + default: + return "<Invalid>"; + } + } + + /** + * Returns true if {@link #getLeapSecond()} is available, false otherwise. + */ + public boolean hasLeapSecond() { + return isFlagSet(HAS_LEAP_SECOND); + } + + /** + * Gets the leap second associated with the clock's time. + * The sign of the value is defined by the following equation: + * utc_time_ns = time_ns + (full_bias_ns + bias_ns) - leap_second * 1,000,000,000 + * + * 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) { + setFlag(HAS_LEAP_SECOND); + mLeapSecond = leapSecond; + } + + /** + * Resets the leap second associated with the clock's time. + */ + public void resetLeapSecond() { + resetFlag(HAS_LEAP_SECOND); + mLeapSecond = Short.MIN_VALUE; + } + + /** + * Gets the GPS receiver internal clock value in nanoseconds. + * This can be either the 'local hardware clock' value ({@link #TYPE_LOCAL_HW_TIME}), or the + * current GPS time derived inside GPS receiver ({@link #TYPE_GPS_TIME}). + * {@link #getType()} defines the time reported. + * + * For 'local hardware clock' this value is expected to be monotonically increasing during the + * reporting session. The real GPS time can be derived by compensating + * {@link #getFullBiasInNs()} (when it is available) from this value. + * + * For 'GPS time' this value is expected to be the best estimation of current GPS time that GPS + * receiver can achieve. {@link #getTimeUncertaintyInNs()} should be available when GPS time is + * specified. + * + * Sub-nanosecond accuracy can be provided by means of {@link #getBiasInNs()}. + * The reported time includes {@link #getTimeUncertaintyInNs()}. + */ + public long getTimeInNs() { + return mTimeInNs; + } + + /** + * Sets the GPS receiver internal clock in nanoseconds. + */ + public void setTimeInNs(long timeInNs) { + mTimeInNs = timeInNs; + } + + /** + * Returns true if {@link #getTimeUncertaintyInNs()} is available, false otherwise. + */ + public boolean hasTimeUncertaintyInNs() { + return isFlagSet(HAS_TIME_UNCERTAINTY); + } + + /** + * Gets the clock's time Uncertainty (1-Sigma) in nanoseconds. + * The uncertainty is represented as an absolute (single sided) value. + * + * 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) { + setFlag(HAS_TIME_UNCERTAINTY); + mTimeUncertaintyInNs = timeUncertaintyInNs; + } + + /** + * Resets the clock's Time Uncertainty (1-Sigma) in nanoseconds. + */ + public void resetTimeUncertaintyInNs() { + resetFlag(HAS_TIME_UNCERTAINTY); + mTimeUncertaintyInNs = Double.NaN; + } + + /** + * Returns true if {@link @getFullBiasInNs()} is available, false otherwise. + */ + public boolean hasFullBiasInNs() { + return isFlagSet(HAS_FULL_BIAS); + } + + /** + * Gets the difference between hardware clock ({@link #getTimeInNs()}) inside GPS receiver and + * the true GPS time since 0000Z, January 6, 1980, in nanoseconds. + * + * This value is available if {@link #TYPE_LOCAL_HW_TIME} is set, and GPS receiver has solved + * the clock for GPS time. + * {@link #getBiasUncertaintyInNs()} should be used for quality check. + * + * The sign of the value is defined by the following equation: + * true time (GPS time) = time_ns + (full_bias_ns + bias_ns) + * + * The reported full bias includes {@link #getBiasUncertaintyInNs()}. + * The value is onl available if {@link #hasFullBiasInNs()} is true. + */ + public long getFullBiasInNs() { + return mFullBiasInNs; + } + + /** + * Sets the full bias in nanoseconds. + */ + public void setFullBiasInNs(long value) { + setFlag(HAS_FULL_BIAS); + mFullBiasInNs = value; + } + + /** + * Resets the full bias in nanoseconds. + */ + public void resetFullBiasInNs() { + resetFlag(HAS_FULL_BIAS); + mFullBiasInNs = Long.MIN_VALUE; + } + + /** + * Returns true if {@link #getBiasInNs()} is available, false otherwise. + */ + public boolean hasBiasInNs() { + return isFlagSet(HAS_BIAS); + } + + /** + * Gets the clock's sub-nanosecond bias. + * The reported bias includes {@link #getBiasUncertaintyInNs()}. + * + * The value is only available if {@link #hasBiasInNs()} is true. + */ + public double getBiasInNs() { + return mBiasInNs; + } + + /** + * Sets the sub-nanosecond bias. + */ + public void setBiasInNs(double biasInNs) { + setFlag(HAS_BIAS); + mBiasInNs = biasInNs; + } + + /** + * Resets the clock's Bias in nanoseconds. + */ + public void resetBiasInNs() { + resetFlag(HAS_BIAS); + mBiasInNs = Double.NaN; + } + + /** + * Returns true if {@link #getBiasUncertaintyInNs()} is available, false otherwise. + */ + public boolean hasBiasUncertaintyInNs() { + return isFlagSet(HAS_BIAS_UNCERTAINTY); + } + + /** + * 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) { + setFlag(HAS_BIAS_UNCERTAINTY); + mBiasUncertaintyInNs = biasUncertaintyInNs; + } + + /** + * Resets the clock's Bias Uncertainty (1-Sigma) in nanoseconds. + */ + public void resetBiasUncertaintyInNs() { + resetFlag(HAS_BIAS_UNCERTAINTY); + mBiasUncertaintyInNs = Double.NaN; + } + + /** + * Returns true if {@link #getDriftInNsPerSec()} is available, false otherwise. + */ + public boolean hasDriftInNsPerSec() { + return isFlagSet(HAS_DRIFT); + } + + /** + * 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) { + setFlag(HAS_DRIFT); + mDriftInNsPerSec = driftInNsPerSec; + } + + /** + * Resets the clock's Drift in nanoseconds per second. + */ + public void resetDriftInNsPerSec() { + resetFlag(HAS_DRIFT); + mDriftInNsPerSec = Double.NaN; + } + + /** + * Returns true if {@link #getDriftUncertaintyInNsPerSec()} is available, false otherwise. + */ + public boolean hasDriftUncertaintyInNsPerSec() { + return isFlagSet(HAS_DRIFT_UNCERTAINTY); + } + + /** + * 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) { + setFlag(HAS_DRIFT_UNCERTAINTY); + mDriftUncertaintyInNsPerSec = driftUncertaintyInNsPerSec; + } + + /** + * Resets the clock's Drift Uncertainty (1-Sigma) in nanoseconds per second. + */ + public void resetDriftUncertaintyInNsPerSec() { + resetFlag(HAS_DRIFT_UNCERTAINTY); + mDriftUncertaintyInNsPerSec = Double.NaN; + } + + public static final Creator<GpsClock> CREATOR = new Creator<GpsClock>() { + @Override + public GpsClock createFromParcel(Parcel parcel) { + GpsClock gpsClock = new GpsClock(); + + gpsClock.mFlags = (short) parcel.readInt(); + gpsClock.mLeapSecond = (short) parcel.readInt(); + gpsClock.mType = parcel.readByte(); + gpsClock.mTimeInNs = parcel.readLong(); + gpsClock.mTimeUncertaintyInNs = parcel.readDouble(); + gpsClock.mFullBiasInNs = parcel.readLong(); + gpsClock.mBiasInNs = parcel.readDouble(); + gpsClock.mBiasUncertaintyInNs = parcel.readDouble(); + gpsClock.mDriftInNsPerSec = parcel.readDouble(); + gpsClock.mDriftUncertaintyInNsPerSec = parcel.readDouble(); + + return gpsClock; + } + + @Override + public GpsClock[] newArray(int size) { + return new GpsClock[size]; + } + }; + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeInt(mFlags); + parcel.writeInt(mLeapSecond); + parcel.writeByte(mType); + parcel.writeLong(mTimeInNs); + parcel.writeDouble(mTimeUncertaintyInNs); + parcel.writeLong(mFullBiasInNs); + parcel.writeDouble(mBiasInNs); + parcel.writeDouble(mBiasUncertaintyInNs); + parcel.writeDouble(mDriftInNsPerSec); + parcel.writeDouble(mDriftUncertaintyInNsPerSec); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + final String format = " %-15s = %s\n"; + final String formatWithUncertainty = " %-15s = %-25s %-26s = %s\n"; + StringBuilder builder = new StringBuilder("GpsClock:\n"); + + builder.append(String.format(format, "Type", getTypeString())); + + builder.append(String.format(format, "LeapSecond", hasLeapSecond() ? mLeapSecond : null)); + + builder.append(String.format( + formatWithUncertainty, + "TimeInNs", + mTimeInNs, + "TimeUncertaintyInNs", + hasTimeUncertaintyInNs() ? mTimeUncertaintyInNs : null)); + + builder.append(String.format( + format, + "FullBiasInNs", + hasFullBiasInNs() ? mFullBiasInNs : null)); + + builder.append(String.format( + formatWithUncertainty, + "BiasInNs", + hasBiasInNs() ? mBiasInNs : null, + "BiasUncertaintyInNs", + hasBiasUncertaintyInNs() ? mBiasUncertaintyInNs : null)); + + builder.append(String.format( + formatWithUncertainty, + "DriftInNsPerSec", + hasDriftInNsPerSec() ? mDriftInNsPerSec : null, + "DriftUncertaintyInNsPerSec", + hasDriftUncertaintyInNsPerSec() ? mDriftUncertaintyInNsPerSec : null)); + + return builder.toString(); + } + + private void initialize() { + mFlags = HAS_NO_FLAGS; + resetLeapSecond(); + setType(TYPE_UNKNOWN); + setTimeInNs(Long.MIN_VALUE); + resetTimeUncertaintyInNs(); + resetBiasInNs(); + resetBiasUncertaintyInNs(); + resetDriftInNsPerSec(); + resetDriftUncertaintyInNsPerSec(); + } + + private void setFlag(short flag) { + mFlags |= flag; + } + + private void resetFlag(short flag) { + mFlags &= ~flag; + } + + private boolean isFlagSet(short flag) { + return (mFlags & flag) == flag; + } +} diff --git a/location/java/android/location/GpsMeasurement.java b/location/java/android/location/GpsMeasurement.java new file mode 100644 index 0000000..1550dc2 --- /dev/null +++ b/location/java/android/location/GpsMeasurement.java @@ -0,0 +1,1387 @@ +/* + * 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"; + + private int mFlags; + private byte mPrn; + private double mTimeOffsetInNs; + private short mState; + private long mReceivedGpsTowInNs; + private long mReceivedGpsTowUncertaintyInNs; + private double mCn0InDbHz; + private double mPseudorangeRateInMetersPerSec; + private double mPseudorangeRateUncertaintyInMetersPerSec; + private short mAccumulatedDeltaRangeState; + private double mAccumulatedDeltaRangeInMeters; + private double mAccumulatedDeltaRangeUncertaintyInMeters; + private double mPseudorangeInMeters; + private double mPseudorangeUncertaintyInMeters; + private double mCodePhaseInChips; + private double mCodePhaseUncertaintyInChips; + private float mCarrierFrequencyInHz; + private long mCarrierCycles; + private double mCarrierPhase; + private double mCarrierPhaseUncertainty; + private byte mLossOfLock; + private int mBitNumber; + private short mTimeFromLastBitInMs; + private double mDopplerShiftInHz; + private double mDopplerShiftUncertaintyInHz; + private byte mMultipathIndicator; + private double mSnrInDb; + private double mElevationInDeg; + private double mElevationUncertaintyInDeg; + private double mAzimuthInDeg; + private double mAzimuthUncertaintyInDeg; + private boolean mUsedInFix; + + // The following enumerations must be in sync with the values declared in gps.h + + private static final int HAS_NO_FLAGS = 0; + private static final int HAS_SNR = (1<<0); + private static final int HAS_ELEVATION = (1<<1); + private static final int HAS_ELEVATION_UNCERTAINTY = (1<<2); + private static final int HAS_AZIMUTH = (1<<3); + private static final int HAS_AZIMUTH_UNCERTAINTY = (1<<4); + private static final int HAS_PSEUDORANGE = (1<<5); + private static final int HAS_PSEUDORANGE_UNCERTAINTY = (1<<6); + private static final int HAS_CODE_PHASE = (1<<7); + private static final int HAS_CODE_PHASE_UNCERTAINTY = (1<<8); + private static final int HAS_CARRIER_FREQUENCY = (1<<9); + private static final int HAS_CARRIER_CYCLES = (1<<10); + private static final int HAS_CARRIER_PHASE = (1<<11); + private static final int HAS_CARRIER_PHASE_UNCERTAINTY = (1<<12); + private static final int HAS_BIT_NUMBER = (1<<13); + private static final int HAS_TIME_FROM_LAST_BIT = (1<<14); + private static final int HAS_DOPPLER_SHIFT = (1<<15); + private static final int HAS_DOPPLER_SHIFT_UNCERTAINTY = (1<<16); + + /** + * The indicator is not available or it is unknown. + */ + public static final byte LOSS_OF_LOCK_UNKNOWN = 0; + + /** + * The measurement does not present any indication of 'loss of lock'. + */ + public static final byte LOSS_OF_LOCK_OK = 1; + + /** + * 'Loss of lock' detected between the previous and current observation: cycle slip possible. + */ + public static final byte LOSS_OF_LOCK_CYCLE_SLIP = 2; + + /** + * The indicator is not available or it is unknown. + */ + public static final byte MULTIPATH_INDICATOR_UNKNOWN = 0; + + /** + * The measurement has been indicated to use multi-path. + */ + public static final byte MULTIPATH_INDICATOR_DETECTED = 1; + + /** + * The measurement has been indicated not tu use multi-path. + */ + public static final byte MULTIPATH_INDICATOR_NOT_USED = 2; + + /** + * The state of GPS receiver the measurement is invalid or unknown. + */ + public static final short STATE_UNKNOWN = 0; + + /** + * The state of the GPS receiver is ranging code lock. + */ + public static final short STATE_CODE_LOCK = (1<<0); + + /** + * The state of the GPS receiver is in bit sync. + */ + public static final short STATE_BIT_SYNC = (1<<1); + + /** + *The state of the GPS receiver is in sub-frame sync. + */ + public static final short STATE_SUBFRAME_SYNC = (1<<2); + + /** + * The state of the GPS receiver has TOW decoded. + */ + public static final short STATE_TOW_DECODED = (1<<3); + + /** + * The state of the 'Accumulated Delta Range' is invalid or unknown. + */ + public static final short ADR_STATE_UNKNOWN = 0; + + /** + * The state of the 'Accumulated Delta Range' is valid. + */ + public static final short ADR_STATE_VALID = (1<<0); + + /** + * The state of the 'Accumulated Delta Range' has detected a reset. + */ + public static final short ADR_STATE_RESET = (1<<1); + + /** + * The state of the 'Accumulated Delta Range' has a cycle slip detected. + */ + public static final short ADR_STATE_CYCLE_SLIP = (1<<2); + + // End enumerations in sync with gps.h + + GpsMeasurement() { + initialize(); + } + + /** + * Sets all contents to the values stored in the provided object. + */ + public void set(GpsMeasurement measurement) { + mFlags = measurement.mFlags; + mPrn = measurement.mPrn; + mTimeOffsetInNs = measurement.mTimeOffsetInNs; + mState = measurement.mState; + mReceivedGpsTowInNs = measurement.mReceivedGpsTowInNs; + mReceivedGpsTowUncertaintyInNs = measurement.mReceivedGpsTowUncertaintyInNs; + mCn0InDbHz = measurement.mCn0InDbHz; + mPseudorangeRateInMetersPerSec = measurement.mPseudorangeRateInMetersPerSec; + mPseudorangeRateUncertaintyInMetersPerSec = + measurement.mPseudorangeRateUncertaintyInMetersPerSec; + mAccumulatedDeltaRangeState = measurement.mAccumulatedDeltaRangeState; + mAccumulatedDeltaRangeInMeters = measurement.mAccumulatedDeltaRangeInMeters; + mAccumulatedDeltaRangeUncertaintyInMeters = + measurement.mAccumulatedDeltaRangeUncertaintyInMeters; + mPseudorangeInMeters = measurement.mPseudorangeInMeters; + mPseudorangeUncertaintyInMeters = measurement.mPseudorangeUncertaintyInMeters; + mCodePhaseInChips = measurement.mCodePhaseInChips; + mCodePhaseUncertaintyInChips = measurement.mCodePhaseUncertaintyInChips; + mCarrierFrequencyInHz = measurement.mCarrierFrequencyInHz; + mCarrierCycles = measurement.mCarrierCycles; + mCarrierPhase = measurement.mCarrierPhase; + mCarrierPhaseUncertainty = measurement.mCarrierPhaseUncertainty; + mLossOfLock = measurement.mLossOfLock; + mBitNumber = measurement.mBitNumber; + mTimeFromLastBitInMs = measurement.mTimeFromLastBitInMs; + mDopplerShiftInHz = measurement.mDopplerShiftInHz; + mDopplerShiftUncertaintyInHz = measurement.mDopplerShiftUncertaintyInHz; + mMultipathIndicator = measurement.mMultipathIndicator; + mSnrInDb = measurement.mSnrInDb; + mElevationInDeg = measurement.mElevationInDeg; + mElevationUncertaintyInDeg = measurement.mElevationUncertaintyInDeg; + mAzimuthInDeg = measurement.mAzimuthInDeg; + mAzimuthUncertaintyInDeg = measurement.mAzimuthUncertaintyInDeg; + mUsedInFix = measurement.mUsedInFix; + } + + /** + * Resets all the contents to its original state. + */ + public void reset() { + initialize(); + } + + /** + * 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 time offset at which the measurement was taken in nanoseconds. + * The reference receiver's time is specified by {@link GpsClock#getTimeInNs()} and should be + * interpreted in the same way as indicated by {@link GpsClock#getType()}. + * + * The sign of this value is given by the following equation: + * measurement time = time_ns + time_offset_ns + * + * The value provides an individual time-stamp for the measurement, and allows sub-nanosecond + * accuracy. + */ + public double getTimeOffsetInNs() { + return mTimeOffsetInNs; + } + + /** + * Sets the time offset at which the measurement was taken in nanoseconds. + */ + public void setTimeOffsetInNs(double value) { + mTimeOffsetInNs = value; + } + + /** + * Gets per-satellite sync state. + * It represents the current sync state for the associated satellite. + * + * This value helps interpret {@link #getReceivedGpsTowInNs()}. + */ + public short getState() { + return mState; + } + + /** + * Sets the sync state. + */ + public void setState(short value) { + switch (value) { + case STATE_UNKNOWN: + case STATE_BIT_SYNC: + case STATE_CODE_LOCK: + case STATE_SUBFRAME_SYNC: + case STATE_TOW_DECODED: + mState = value; + break; + default: + Log.d(TAG, "Sanitizing invalid 'sync state': " + value); + mState = STATE_UNKNOWN; + break; + } + } + + /** + * Gets a string representation of the 'sync state'. + * For internal and logging use only. + */ + private String getStateString() { + switch (mState) { + case STATE_UNKNOWN: + return "Unknown"; + case STATE_BIT_SYNC: + return "BitSync"; + case STATE_CODE_LOCK: + return "CodeLock"; + case STATE_SUBFRAME_SYNC: + return "SubframeSync"; + case STATE_TOW_DECODED: + return "TowDecoded"; + default: + return "<Invalid>"; + } + } + + /** + * Gets the received GPS Time-of-Week at the measurement time, in nanoseconds. + * The value is relative to the beginning of the current GPS week. + * + * Given {@link #getState()} of the GPS receiver, the range of this field can be: + * Searching : [ 0 ] : {@link #STATE_UNKNOWN} is set + * Ranging code lock : [ 0 1 ms ] : {@link #STATE_CODE_LOCK} is set + * Bit sync : [ 0 20 ms ] : {@link #STATE_BIT_SYNC} is set + * Subframe sync : [ 0 6 ms ] : {@link #STATE_SUBFRAME_SYNC} is set + * TOW decoded : [ 0 1 week ] : {@link #STATE_TOW_DECODED} is set + */ + public long getReceivedGpsTowInNs() { + return mReceivedGpsTowInNs; + } + + /** + * Sets the received GPS time-of-week in nanoseconds. + */ + public void setReceivedGpsTowInNs(long value) { + mReceivedGpsTowInNs = value; + } + + /** + * Gets the received GPS time-of-week's uncertainty (1-Sigma) in nanoseconds. + */ + public long getReceivedGpsTowUncertaintyInNs() { + return mReceivedGpsTowUncertaintyInNs; + } + + /** + * Sets the received GPS time-of-week's uncertainty (1-Sigma) in nanoseconds. + */ + public void setReceivedGpsTowUncertaintyInNs(long value) { + mReceivedGpsTowUncertaintyInNs = 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 'Accumulated Delta Range' state. + * It indicates whether {@link #getAccumulatedDeltaRangeInMeters()} is reset or there is a + * cycle slip (indicating 'loss of lock'). + */ + public short getAccumulatedDeltaRangeState() { + return mAccumulatedDeltaRangeState; + } + + /** + * Sets the 'Accumulated Delta Range' state. + */ + public void setAccumulatedDeltaRangeState(short value) { + switch (value) { + case ADR_STATE_UNKNOWN: + case ADR_STATE_VALID: + case ADR_STATE_RESET: + case ADR_STATE_CYCLE_SLIP: + mAccumulatedDeltaRangeState = value; + break; + default: + Log.d(TAG, "Sanitizing invalid 'Accumulated Delta Range state': " + value); + mAccumulatedDeltaRangeState = ADR_STATE_UNKNOWN; + break; + } + } + + /** + * Gets a string representation of the 'Accumulated Delta Range state'. + * For internal and logging use only. + */ + private String getAccumulatedDeltaRangeStateString() { + switch (mAccumulatedDeltaRangeState) { + case ADR_STATE_UNKNOWN: + return "Unknown"; + case ADR_STATE_VALID: + return "Valid"; + case ADR_STATE_RESET: + return "Reset"; + case ADR_STATE_CYCLE_SLIP: + return "CycleSlip"; + default: + return "<Invalid>"; + } + } + + /** + * Gets the accumulated delta range since the last channel reset, in meters. + * The reported value includes {@link #getAccumulatedDeltaRangeUncertaintyInMeters()}. + * + * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}. + */ + 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. + * + * The availability of the value is represented by {@link #getAccumulatedDeltaRangeState()}. + */ + public void setAccumulatedDeltaRangeUncertaintyInMeters(double value) { + mAccumulatedDeltaRangeUncertaintyInMeters = value; + } + + /** + * Returns true if {@link #getPseudorangeInMeters()} is available, false otherwise. + */ + public boolean hasPseudorangeInMeters() { + return isFlagSet(HAS_PSEUDORANGE); + } + + /** + * 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) { + setFlag(HAS_PSEUDORANGE); + mPseudorangeInMeters = value; + } + + /** + * Resets the Pseudo-range in meters. + */ + public void resetPseudorangeInMeters() { + resetFlag(HAS_PSEUDORANGE); + mPseudorangeInMeters = Double.NaN; + } + + /** + * Returns true if {@link #getPseudorangeUncertaintyInMeters()} is available, false otherwise. + */ + public boolean hasPseudorangeUncertaintyInMeters() { + return isFlagSet(HAS_PSEUDORANGE_UNCERTAINTY); + } + + /** + * 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) { + setFlag(HAS_PSEUDORANGE_UNCERTAINTY); + mPseudorangeUncertaintyInMeters = value; + } + + /** + * Resets the pseudo-range's uncertainty (1-Sigma) in meters. + */ + public void resetPseudorangeUncertaintyInMeters() { + resetFlag(HAS_PSEUDORANGE_UNCERTAINTY); + mPseudorangeUncertaintyInMeters = Double.NaN; + } + + /** + * Returns true if {@link #getCodePhaseInChips()} is available, false otherwise. + */ + public boolean hasCodePhaseInChips() { + return isFlagSet(HAS_CODE_PHASE); + } + + /** + * 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) { + setFlag(HAS_CODE_PHASE); + mCodePhaseInChips = value; + } + + /** + * Resets the Code-phase in chips. + */ + public void resetCodePhaseInChips() { + resetFlag(HAS_CODE_PHASE); + mCodePhaseInChips = Double.NaN; + } + + /** + * Returns true if {@link #getCodePhaseUncertaintyInChips()} is available, false otherwise. + */ + public boolean hasCodePhaseUncertaintyInChips() { + return isFlagSet(HAS_CODE_PHASE_UNCERTAINTY); + } + + /** + * 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) { + setFlag(HAS_CODE_PHASE_UNCERTAINTY); + mCodePhaseUncertaintyInChips = value; + } + + /** + * Resets the Code-phase's uncertainty (1-Sigma) in fractions of chips. + */ + public void resetCodePhaseUncertaintyInChips() { + resetFlag(HAS_CODE_PHASE_UNCERTAINTY); + mCodePhaseUncertaintyInChips = Double.NaN; + } + + /** + * Returns true if {@link #getCarrierFrequencyInHz()} is available, false otherwise. + */ + public boolean hasCarrierFrequencyInHz() { + return isFlagSet(HAS_CARRIER_FREQUENCY); + } + + /** + * 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) { + setFlag(HAS_CARRIER_FREQUENCY); + mCarrierFrequencyInHz = carrierFrequencyInHz; + } + + /** + * Resets the Carrier frequency (L1 or L2) in Hz. + */ + public void resetCarrierFrequencyInHz() { + resetFlag(HAS_CARRIER_FREQUENCY); + mCarrierFrequencyInHz = Float.NaN; + } + + /** + * Returns true if {@link #getCarrierCycles()} is available, false otherwise. + */ + public boolean hasCarrierCycles() { + return isFlagSet(HAS_CARRIER_CYCLES); + } + + /** + * 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) { + setFlag(HAS_CARRIER_CYCLES); + mCarrierCycles = value; + } + + /** + * Resets the number of full carrier cycles between the satellite and the receiver. + */ + public void resetCarrierCycles() { + resetFlag(HAS_CARRIER_CYCLES); + mCarrierCycles = Long.MIN_VALUE; + } + + /** + * Returns true if {@link #getCarrierPhase()} is available, false otherwise. + */ + public boolean hasCarrierPhase() { + return isFlagSet(HAS_CARRIER_PHASE); + } + + /** + * 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) { + setFlag(HAS_CARRIER_PHASE); + mCarrierPhase = value; + } + + /** + * Resets the RF phase detected by the receiver. + */ + public void resetCarrierPhase() { + resetFlag(HAS_CARRIER_PHASE); + mCarrierPhase = Double.NaN; + } + + /** + * Returns true if {@link #getCarrierPhaseUncertainty()} is available, false otherwise. + */ + public boolean hasCarrierPhaseUncertainty() { + return isFlagSet(HAS_CARRIER_PHASE_UNCERTAINTY); + } + + /** + * 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) { + setFlag(HAS_CARRIER_PHASE_UNCERTAINTY); + mCarrierPhaseUncertainty = value; + } + + /** + * Resets the Carrier-phase's uncertainty (1-Sigma) in cycles. + */ + public void resetCarrierPhaseUncertainty() { + resetFlag(HAS_CARRIER_PHASE_UNCERTAINTY); + mCarrierPhaseUncertainty = Double.NaN; + } + + /** + * Gets a value indicating the 'loss of lock' state of the event. + */ + public byte getLossOfLock() { + return mLossOfLock; + } + + /** + * Sets the 'loss of lock' status. + */ + public void setLossOfLock(byte 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 isFlagSet(HAS_BIT_NUMBER); + } + + /** + * Gets the number of GPS bits transmitted since Sat-Sun midnight (GPS week). + * + * The value is only available if {@link #hasBitNumber()} is true. + */ + public int getBitNumber() { + return mBitNumber; + } + + /** + * Sets the bit number within the broadcast frame. + */ + public void setBitNumber(int bitNumber) { + setFlag(HAS_BIT_NUMBER); + mBitNumber = bitNumber; + } + + /** + * Resets the bit number within the broadcast frame. + */ + public void resetBitNumber() { + resetFlag(HAS_BIT_NUMBER); + mBitNumber = Integer.MIN_VALUE; + } + + /** + * Returns true if {@link #getTimeFromLastBitInMs()} is available, false otherwise. + */ + public boolean hasTimeFromLastBitInMs() { + return isFlagSet(HAS_TIME_FROM_LAST_BIT); + } + + /** + * Gets the elapsed time since the last received bit in milliseconds. + * Range: [0, 20]. + * + * The value is only available if {@link #hasTimeFromLastBitInMs()} is true. + */ + public short getTimeFromLastBitInMs() { + return mTimeFromLastBitInMs; + } + + /** + * Sets the elapsed time since the last received bit in milliseconds. + */ + public void setTimeFromLastBitInMs(short value) { + setFlag(HAS_TIME_FROM_LAST_BIT); + mTimeFromLastBitInMs = value; + } + + /** + * Resets the elapsed time since the last received bit in milliseconds. + */ + public void resetTimeFromLastBitInMs() { + resetFlag(HAS_TIME_FROM_LAST_BIT); + mTimeFromLastBitInMs = Short.MIN_VALUE; + } + + /** + * Returns true if {@link #getDopplerShiftInHz()} is available, false otherwise. + */ + public boolean hasDopplerShiftInHz() { + return isFlagSet(HAS_DOPPLER_SHIFT); + } + + /** + * 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) { + setFlag(HAS_DOPPLER_SHIFT); + mDopplerShiftInHz = value; + } + + /** + * Resets the Doppler shift in Hz. + */ + public void resetDopplerShiftInHz() { + resetFlag(HAS_DOPPLER_SHIFT); + mDopplerShiftInHz = Double.NaN; + } + + /** + * Returns true if {@link #getDopplerShiftUncertaintyInHz()} is available, false otherwise. + */ + public boolean hasDopplerShiftUncertaintyInHz() { + return isFlagSet(HAS_DOPPLER_SHIFT_UNCERTAINTY); + } + + /** + * 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) { + setFlag(HAS_DOPPLER_SHIFT_UNCERTAINTY); + mDopplerShiftUncertaintyInHz = value; + } + + /** + * Resets the Doppler's shift uncertainty (1-Sigma) in Hz. + */ + public void resetDopplerShiftUncertaintyInHz() { + resetFlag(HAS_DOPPLER_SHIFT_UNCERTAINTY); + mDopplerShiftUncertaintyInHz = Double.NaN; + } + + /** + * Gets a value indicating the 'multipath' state of the event. + */ + public byte getMultipathIndicator() { + return mMultipathIndicator; + } + + /** + * Sets the 'multi-path' indicator. + */ + public void setMultipathIndicator(byte 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 "NotUsed"; + default: + return "<Invalid>"; + } + } + + /** + * Returns true if {@link #getSnrInDb()} is available, false otherwise. + */ + public boolean hasSnrInDb() { + return isFlagSet(HAS_SNR); + } + + /** + * 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) { + setFlag(HAS_SNR); + mSnrInDb = snrInDb; + } + + /** + * Resets the Signal-to-noise ratio (SNR) in dB. + */ + public void resetSnrInDb() { + resetFlag(HAS_SNR); + mSnrInDb = Double.NaN; + } + + /** + * Returns true if {@link #getElevationInDeg()} is available, false otherwise. + */ + public boolean hasElevationInDeg() { + return isFlagSet(HAS_ELEVATION); + } + + /** + * 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) { + setFlag(HAS_ELEVATION); + mElevationInDeg = elevationInDeg; + } + + /** + * Resets the Elevation in degrees. + */ + public void resetElevationInDeg() { + resetFlag(HAS_ELEVATION); + mElevationInDeg = Double.NaN; + } + + /** + * Returns true if {@link #getElevationUncertaintyInDeg()} is available, false otherwise. + */ + public boolean hasElevationUncertaintyInDeg() { + return isFlagSet(HAS_ELEVATION_UNCERTAINTY); + } + + /** + * 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) { + setFlag(HAS_ELEVATION_UNCERTAINTY); + mElevationUncertaintyInDeg = value; + } + + /** + * Resets the elevation's uncertainty (1-Sigma) in degrees. + */ + public void resetElevationUncertaintyInDeg() { + resetFlag(HAS_ELEVATION_UNCERTAINTY); + mElevationUncertaintyInDeg = Double.NaN; + } + + /** + * Returns true if {@link #getAzimuthInDeg()} is available, false otherwise. + */ + public boolean hasAzimuthInDeg() { + return isFlagSet(HAS_AZIMUTH); + } + + /** + * 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) { + setFlag(HAS_AZIMUTH); + mAzimuthInDeg = value; + } + + /** + * Resets the Azimuth in degrees. + */ + public void resetAzimuthInDeg() { + resetFlag(HAS_AZIMUTH); + mAzimuthInDeg = Double.NaN; + } + + /** + * Returns true if {@link #getAzimuthUncertaintyInDeg()} is available, false otherwise. + */ + public boolean hasAzimuthUncertaintyInDeg() { + return isFlagSet(HAS_AZIMUTH_UNCERTAINTY); + } + + /** + * 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) { + setFlag(HAS_AZIMUTH_UNCERTAINTY); + mAzimuthUncertaintyInDeg = value; + } + + /** + * Resets the Azimuth's uncertainty (1-Sigma) in degrees. + */ + public void resetAzimuthUncertaintyInDeg() { + resetFlag(HAS_AZIMUTH_UNCERTAINTY); + 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.mFlags = parcel.readInt(); + gpsMeasurement.mPrn = parcel.readByte(); + gpsMeasurement.mTimeOffsetInNs = parcel.readDouble(); + gpsMeasurement.mState = (short) parcel.readInt(); + gpsMeasurement.mReceivedGpsTowInNs = parcel.readLong(); + gpsMeasurement.mReceivedGpsTowUncertaintyInNs = parcel.readLong(); + gpsMeasurement.mCn0InDbHz = parcel.readDouble(); + gpsMeasurement.mPseudorangeRateInMetersPerSec = parcel.readDouble(); + gpsMeasurement.mPseudorangeRateUncertaintyInMetersPerSec = parcel.readDouble(); + gpsMeasurement.mAccumulatedDeltaRangeState = (short) parcel.readInt(); + gpsMeasurement.mAccumulatedDeltaRangeInMeters = parcel.readDouble(); + gpsMeasurement.mAccumulatedDeltaRangeUncertaintyInMeters = parcel.readDouble(); + gpsMeasurement.mPseudorangeInMeters = parcel.readDouble(); + gpsMeasurement.mPseudorangeUncertaintyInMeters = parcel.readDouble(); + gpsMeasurement.mCodePhaseInChips = parcel.readDouble(); + gpsMeasurement.mCodePhaseUncertaintyInChips = parcel.readDouble(); + gpsMeasurement.mCarrierFrequencyInHz = parcel.readFloat(); + gpsMeasurement.mCarrierCycles = parcel.readLong(); + gpsMeasurement.mCarrierPhase = parcel.readDouble(); + gpsMeasurement.mCarrierPhaseUncertainty = parcel.readDouble(); + gpsMeasurement.mLossOfLock = parcel.readByte(); + gpsMeasurement.mBitNumber = parcel.readInt(); + gpsMeasurement.mTimeFromLastBitInMs = (short) parcel.readInt(); + gpsMeasurement.mDopplerShiftInHz = parcel.readDouble(); + gpsMeasurement.mDopplerShiftUncertaintyInHz = parcel.readDouble(); + gpsMeasurement.mMultipathIndicator = parcel.readByte(); + gpsMeasurement.mSnrInDb = parcel.readDouble(); + gpsMeasurement.mElevationInDeg = parcel.readDouble(); + gpsMeasurement.mElevationUncertaintyInDeg = parcel.readDouble(); + gpsMeasurement.mAzimuthInDeg = parcel.readDouble(); + 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.writeInt(mFlags); + parcel.writeByte(mPrn); + parcel.writeDouble(mTimeOffsetInNs); + parcel.writeInt(mState); + parcel.writeLong(mReceivedGpsTowInNs); + parcel.writeLong(mReceivedGpsTowUncertaintyInNs); + parcel.writeDouble(mCn0InDbHz); + parcel.writeDouble(mPseudorangeRateInMetersPerSec); + parcel.writeDouble(mPseudorangeRateUncertaintyInMetersPerSec); + parcel.writeInt(mAccumulatedDeltaRangeState); + parcel.writeDouble(mAccumulatedDeltaRangeInMeters); + parcel.writeDouble(mAccumulatedDeltaRangeUncertaintyInMeters); + parcel.writeDouble(mPseudorangeInMeters); + parcel.writeDouble(mPseudorangeUncertaintyInMeters); + parcel.writeDouble(mCodePhaseInChips); + parcel.writeDouble(mCodePhaseUncertaintyInChips); + parcel.writeFloat(mCarrierFrequencyInHz); + parcel.writeLong(mCarrierCycles); + parcel.writeDouble(mCarrierPhase); + parcel.writeDouble(mCarrierPhaseUncertainty); + parcel.writeByte(mLossOfLock); + parcel.writeInt(mBitNumber); + parcel.writeInt(mTimeFromLastBitInMs); + parcel.writeDouble(mDopplerShiftInHz); + parcel.writeDouble(mDopplerShiftUncertaintyInHz); + parcel.writeByte(mMultipathIndicator); + parcel.writeDouble(mSnrInDb); + parcel.writeDouble(mElevationInDeg); + parcel.writeDouble(mElevationUncertaintyInDeg); + parcel.writeDouble(mAzimuthInDeg); + 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, "TimeOffsetInNs", mTimeOffsetInNs)); + + builder.append(String.format(format, "State", getStateString())); + + builder.append(String.format( + formatWithUncertainty, + "ReceivedGpsTowInNs", + mReceivedGpsTowInNs, + "ReceivedGpsTowUncertaintyInNs", + mReceivedGpsTowUncertaintyInNs)); + + builder.append(String.format(format, "Cn0InDbHz", mCn0InDbHz)); + + builder.append(String.format( + formatWithUncertainty, + "PseudorangeRateInMetersPerSec", + mPseudorangeRateInMetersPerSec, + "PseudorangeRateUncertaintyInMetersPerSec", + mPseudorangeRateUncertaintyInMetersPerSec)); + + builder.append(String.format( + format, + "AccumulatedDeltaRangeState", + getAccumulatedDeltaRangeStateString())); + + builder.append(String.format( + formatWithUncertainty, + "AccumulatedDeltaRangeInMeters", + mAccumulatedDeltaRangeInMeters, + "AccumulatedDeltaRangeUncertaintyInMeters", + mAccumulatedDeltaRangeUncertaintyInMeters)); + + builder.append(String.format( + formatWithUncertainty, + "PseudorangeInMeters", + hasPseudorangeInMeters() ? mPseudorangeInMeters : null, + "PseudorangeUncertaintyInMeters", + hasPseudorangeUncertaintyInMeters() ? mPseudorangeUncertaintyInMeters : null)); + + builder.append(String.format( + formatWithUncertainty, + "CodePhaseInChips", + hasCodePhaseInChips() ? mCodePhaseInChips : null, + "CodePhaseUncertaintyInChips", + hasCodePhaseUncertaintyInChips() ? mCodePhaseUncertaintyInChips : null)); + + builder.append(String.format( + format, + "CarrierFrequencyInHz", + hasCarrierFrequencyInHz() ? mCarrierFrequencyInHz : null)); + + builder.append(String.format( + format, + "CarrierCycles", + hasCarrierCycles() ? mCarrierCycles : null)); + + builder.append(String.format( + formatWithUncertainty, + "CarrierPhase", + hasCarrierPhase() ? mCarrierPhase : null, + "CarrierPhaseUncertainty", + hasCarrierPhaseUncertainty() ? mCarrierPhaseUncertainty : null)); + + builder.append(String.format(format, "LossOfLock", getLossOfLockString())); + + builder.append(String.format( + format, + "BitNumber", + hasBitNumber() ? mBitNumber : null)); + + builder.append(String.format( + format, + "TimeFromLastBitInMs", + hasTimeFromLastBitInMs() ? mTimeFromLastBitInMs : null)); + + builder.append(String.format( + formatWithUncertainty, + "DopplerShiftInHz", + hasDopplerShiftInHz() ? mDopplerShiftInHz : null, + "DopplerShiftUncertaintyInHz", + hasDopplerShiftUncertaintyInHz() ? mDopplerShiftUncertaintyInHz : null)); + + builder.append(String.format(format, "MultipathIndicator", getMultipathIndicatorString())); + + builder.append(String.format( + format, + "SnrInDb", + hasSnrInDb() ? mSnrInDb : null)); + + builder.append(String.format( + formatWithUncertainty, + "ElevationInDeg", + hasElevationInDeg() ? mElevationInDeg : null, + "ElevationUncertaintyInDeg", + hasElevationUncertaintyInDeg() ? mElevationUncertaintyInDeg : null)); + + builder.append(String.format( + formatWithUncertainty, + "AzimuthInDeg", + hasAzimuthInDeg() ? mAzimuthInDeg : null, + "AzimuthUncertaintyInDeg", + hasAzimuthUncertaintyInDeg() ? mAzimuthUncertaintyInDeg : null)); + + builder.append(String.format(format, "UsedInFix", mUsedInFix)); + + return builder.toString(); + } + + private void initialize() { + mFlags = HAS_NO_FLAGS; + setPrn(Byte.MIN_VALUE); + setTimeOffsetInNs(Long.MIN_VALUE); + setState(STATE_UNKNOWN); + setReceivedGpsTowInNs(Long.MIN_VALUE); + setReceivedGpsTowUncertaintyInNs(Long.MAX_VALUE); + setCn0InDbHz(Double.MIN_VALUE); + setPseudorangeRateInMetersPerSec(Double.MIN_VALUE); + setPseudorangeRateUncertaintyInMetersPerSec(Double.MIN_VALUE); + setAccumulatedDeltaRangeState(ADR_STATE_UNKNOWN); + setAccumulatedDeltaRangeInMeters(Double.MIN_VALUE); + setAccumulatedDeltaRangeUncertaintyInMeters(Double.MIN_VALUE); + resetPseudorangeInMeters(); + resetPseudorangeUncertaintyInMeters(); + resetCodePhaseInChips(); + resetCodePhaseUncertaintyInChips(); + resetCarrierFrequencyInHz(); + resetCarrierCycles(); + resetCarrierPhase(); + resetCarrierPhaseUncertainty(); + setLossOfLock(LOSS_OF_LOCK_UNKNOWN); + resetBitNumber(); + resetTimeFromLastBitInMs(); + resetDopplerShiftInHz(); + resetDopplerShiftUncertaintyInHz(); + setMultipathIndicator(MULTIPATH_INDICATOR_UNKNOWN); + resetSnrInDb(); + resetElevationInDeg(); + resetElevationUncertaintyInDeg(); + resetAzimuthInDeg(); + resetAzimuthUncertaintyInDeg(); + setUsedInFix(false); + } + + private void setFlag(int flag) { + mFlags |= flag; + } + + private void resetFlag(int flag) { + mFlags &= ~flag; + } + + private boolean isFlagSet(int flag) { + return (mFlags & flag) == flag; + } +} diff --git a/location/java/android/location/GpsMeasurementListenerTransport.java b/location/java/android/location/GpsMeasurementListenerTransport.java new file mode 100644 index 0000000..2d9a372 --- /dev/null +++ b/location/java/android/location/GpsMeasurementListenerTransport.java @@ -0,0 +1,66 @@ +/* + * 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.content.Context; +import android.os.RemoteException; + +/** + * A handler class to manage transport listeners for {@link GpsMeasurementsEvent.Listener}. + * + * @hide + */ +class GpsMeasurementListenerTransport + extends LocalListenerHelper<GpsMeasurementsEvent.Listener> { + private final Context mContext; + private final ILocationManager mLocationManager; + + private final IGpsMeasurementsListener mListenerTransport = new ListenerTransport(); + + public GpsMeasurementListenerTransport(Context context, ILocationManager locationManager) { + super("GpsMeasurementListenerTransport"); + mContext = context; + mLocationManager = locationManager; + } + + @Override + protected boolean registerWithServer() throws RemoteException { + return mLocationManager.addGpsMeasurementsListener( + mListenerTransport, + mContext.getPackageName()); + } + + @Override + protected void unregisterFromServer() throws RemoteException { + mLocationManager.removeGpsMeasurementsListener(mListenerTransport); + } + + private class ListenerTransport extends IGpsMeasurementsListener.Stub { + @Override + public void onGpsMeasurementsReceived(final GpsMeasurementsEvent event) { + ListenerOperation<GpsMeasurementsEvent.Listener> operation = + new ListenerOperation<GpsMeasurementsEvent.Listener>() { + @Override + public void execute(GpsMeasurementsEvent.Listener listener) throws RemoteException { + listener.onGpsMeasurementsReceived(event); + } + }; + + foreach(operation); + } + } +} diff --git a/location/java/android/location/GpsMeasurementsEvent.aidl b/location/java/android/location/GpsMeasurementsEvent.aidl new file mode 100644 index 0000000..2c46262 --- /dev/null +++ b/location/java/android/location/GpsMeasurementsEvent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +parcelable GpsMeasurementsEvent; diff --git a/location/java/android/location/GpsMeasurementsEvent.java b/location/java/android/location/GpsMeasurementsEvent.java new file mode 100644 index 0000000..e04ed81 --- /dev/null +++ b/location/java/android/location/GpsMeasurementsEvent.java @@ -0,0 +1,127 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License + */ + +package android.location; + +import android.annotation.NonNull; +import android.os.Parcel; +import android.os.Parcelable; + +import java.security.InvalidParameterException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; + +/** + * A class implementing a container for data associated with a measurement event. + * Events are delivered to registered instances of {@link Listener}. + * + * @hide + */ +public class GpsMeasurementsEvent implements Parcelable { + private final GpsClock mClock; + private final Collection<GpsMeasurement> mReadOnlyMeasurements; + + /** + * Used for receiving GPS satellite measurements from the GPS engine. + * Each measurement contains raw and computed data identifying a satellite. + * You can implement this interface and call {@link LocationManager#addGpsMeasurementListener}. + * + * @hide + */ + public interface Listener { + void onGpsMeasurementsReceived(GpsMeasurementsEvent eventArgs); + } + + public GpsMeasurementsEvent(GpsClock clock, GpsMeasurement[] measurements) { + if (clock == null) { + throw new InvalidParameterException("Parameter 'clock' must not be null."); + } + if (measurements == null || measurements.length == 0) { + throw new InvalidParameterException( + "Parameter 'measurements' must not be null or empty."); + } + + mClock = clock; + Collection<GpsMeasurement> measurementCollection = Arrays.asList(measurements); + mReadOnlyMeasurements = Collections.unmodifiableCollection(measurementCollection); + } + + @NonNull + public GpsClock getClock() { + return mClock; + } + + /** + * Gets a read-only collection of measurements associated with the current event. + */ + @NonNull + public Collection<GpsMeasurement> getMeasurements() { + return mReadOnlyMeasurements; + } + + public static final Creator<GpsMeasurementsEvent> CREATOR = + new Creator<GpsMeasurementsEvent>() { + @Override + public GpsMeasurementsEvent createFromParcel(Parcel in) { + ClassLoader classLoader = getClass().getClassLoader(); + + GpsClock clock = in.readParcelable(classLoader); + + int measurementsLength = in.readInt(); + GpsMeasurement[] measurementsArray = new GpsMeasurement[measurementsLength]; + in.readTypedArray(measurementsArray, GpsMeasurement.CREATOR); + + return new GpsMeasurementsEvent(clock, measurementsArray); + } + + @Override + public GpsMeasurementsEvent[] newArray(int size) { + return new GpsMeasurementsEvent[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeParcelable(mClock, flags); + + GpsMeasurement[] measurementsArray = mReadOnlyMeasurements.toArray(new GpsMeasurement[0]); + parcel.writeInt(measurementsArray.length); + parcel.writeTypedArray(measurementsArray, flags); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("[ GpsMeasurementsEvent:\n\n"); + + builder.append(mClock.toString()); + builder.append("\n"); + + for (GpsMeasurement measurement : mReadOnlyMeasurements) { + builder.append(measurement.toString()); + builder.append("\n"); + } + + builder.append("]"); + + return builder.toString(); + } +} diff --git a/location/java/android/location/GpsNavigationMessage.java b/location/java/android/location/GpsNavigationMessage.java new file mode 100644 index 0000000..2eb4708 --- /dev/null +++ b/location/java/android/location/GpsNavigationMessage.java @@ -0,0 +1,276 @@ +/* + * 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 android.util.Log; + +import java.security.InvalidParameterException; + +/** + * A class containing a GPS satellite Navigation Message. + * + * @hide + */ +public class GpsNavigationMessage implements Parcelable { + private static final String TAG = "GpsNavigationMessage"; + private static final byte[] EMPTY_ARRAY = new byte[0]; + + // The following enumerations must be in sync with the values declared in gps.h + + /** + * The type of the navigation message is not available or unknown. + */ + public static final byte TYPE_UNKNOWN = 0; + + /** + * The Navigation Message is of type L1 C/A. + */ + public static final byte TYPE_L1CA = 1; + + /** + * The Navigation Message is of type L1-CNAV. + */ + public static final byte TYPE_L2CNAV = 2; + + /** + * The Navigation Message is of type L5-CNAV. + */ + public static final byte TYPE_L5CNAV = 3; + + /** + * The Navigation Message is of type CNAV-2. + */ + public static final byte TYPE_CNAV2 = 4; + + // End enumerations in sync with gps.h + + private byte mType; + private byte mPrn; + private short mMessageId; + private short mSubmessageId; + private byte[] mData; + + GpsNavigationMessage() { + initialize(); + } + + /** + * Sets all contents to the values stored in the provided object. + */ + public void set(GpsNavigationMessage navigationMessage) { + mType = navigationMessage.mType; + mPrn = navigationMessage.mPrn; + mMessageId = navigationMessage.mMessageId; + mSubmessageId = navigationMessage.mSubmessageId; + mData = navigationMessage.mData; + } + + /** + * Resets all the contents to its original state. + */ + public void reset() { + initialize(); + } + + /** + * Gets the type of the navigation message contained in the object. + */ + public byte getType() { + return mType; + } + + /** + * Sets the type of the navigation message. + */ + public void setType(byte value) { + switch (value) { + case TYPE_UNKNOWN: + case TYPE_L1CA: + case TYPE_L2CNAV: + case TYPE_L5CNAV: + case TYPE_CNAV2: + mType = value; + break; + default: + Log.d(TAG, "Sanitizing invalid 'type': " + value); + mType = TYPE_UNKNOWN; + break; + } + } + + /** + * Gets a string representation of the 'type'. + * For internal and logging use only. + */ + private String getTypeString() { + switch (mType) { + case TYPE_UNKNOWN: + return "Unknown"; + case TYPE_L1CA: + return "L1 C/A"; + case TYPE_L2CNAV: + return "L2-CNAV"; + case TYPE_L5CNAV: + return "L5-CNAV"; + case TYPE_CNAV2: + return "CNAV-2"; + default: + return "<Invalid>"; + } + } + + /** + * Gets the Pseudo-random number. + * Range: [1, 32]. + */ + public byte getPrn() { + return mPrn; + } + + /** + * Sets the Pseud-random number. + */ + public void setPrn(byte value) { + mPrn = value; + } + + /** + * Gets the Message Identifier. + * It provides an index so the complete Navigation Message can be assembled. i.e. for L1 C/A + * subframe 4 and 5, this value corresponds to the 'frame id' of the navigation message. + * Subframe 1, 2, 3 does not contain a 'frame id' and this might be reported as -1. + */ + public short getMessageId() { + return mMessageId; + } + + /** + * Sets the Message Identifier. + */ + public void setMessageId(short value) { + mMessageId = value; + } + + /** + * Gets the Sub-message Identifier. + * If required by {@link #getType()}, this value contains a sub-index within the current message + * (or frame) that is being transmitted. i.e. for L1 C/A the sub-message identifier corresponds + * to the sub-frame Id of the navigation message. + */ + public short getSubmessageId() { + return mSubmessageId; + } + + /** + * Sets the Sub-message identifier. + */ + public void setSubmessageId(short value) { + mSubmessageId = value; + } + + /** + * Gets the data associated with the Navigation Message. + * The bytes (or words) specified using big endian format (MSB first). + */ + @NonNull + public byte[] getData() { + return mData; + } + + /** + * Sets the data associated with the Navigation Message. + */ + public void setData(byte[] value) { + if (value == null) { + throw new InvalidParameterException("Data must be a non-null array"); + } + + mData = value; + } + + public static final Creator<GpsNavigationMessage> CREATOR = + new Creator<GpsNavigationMessage>() { + @Override + public GpsNavigationMessage createFromParcel(Parcel parcel) { + GpsNavigationMessage navigationMessage = new GpsNavigationMessage(); + + navigationMessage.setType(parcel.readByte()); + navigationMessage.setPrn(parcel.readByte()); + navigationMessage.setMessageId((short) parcel.readInt()); + navigationMessage.setSubmessageId((short) parcel.readInt()); + + int dataLength = parcel.readInt(); + byte[] data = new byte[dataLength]; + parcel.readByteArray(data); + navigationMessage.setData(data); + + return navigationMessage; + } + + @Override + public GpsNavigationMessage[] newArray(int size) { + return new GpsNavigationMessage[size]; + } + }; + + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeByte(mType); + parcel.writeByte(mPrn); + parcel.writeInt(mMessageId); + parcel.writeInt(mSubmessageId); + parcel.writeInt(mData.length); + parcel.writeByteArray(mData); + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public String toString() { + final String format = " %-15s = %s\n"; + StringBuilder builder = new StringBuilder("GpsNavigationMessage:\n"); + + builder.append(String.format(format, "Type", getTypeString())); + builder.append(String.format(format, "Prn", mPrn)); + builder.append(String.format(format, "MessageId", mMessageId)); + builder.append(String.format(format, "SubmessageId", mSubmessageId)); + + builder.append(String.format(format, "Data", "{")); + String prefix = " "; + for(byte value : mData) { + builder.append(prefix); + builder.append(value); + prefix = ", "; + } + builder.append(" }"); + + return builder.toString(); + } + + private void initialize() { + mType = TYPE_UNKNOWN; + mPrn = 0; + mMessageId = -1; + mSubmessageId = -1; + mData = EMPTY_ARRAY; + } +} diff --git a/location/java/android/location/GpsNavigationMessageEvent.aidl b/location/java/android/location/GpsNavigationMessageEvent.aidl new file mode 100644 index 0000000..f84c2f7 --- /dev/null +++ b/location/java/android/location/GpsNavigationMessageEvent.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +parcelable GpsNavigationMessageEvent; diff --git a/location/java/android/location/GpsNavigationMessageEvent.java b/location/java/android/location/GpsNavigationMessageEvent.java new file mode 100644 index 0000000..50ffa75 --- /dev/null +++ b/location/java/android/location/GpsNavigationMessageEvent.java @@ -0,0 +1,92 @@ +/* + * 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 navigation message event. + * Events are delivered to registered instances of {@link Listener}. + * + * @hide + */ +public class GpsNavigationMessageEvent implements Parcelable { + private final GpsNavigationMessage mNavigationMessage; + + /** + * Used for receiving GPS satellite Navigation Messages from the GPS engine. + * You can implement this interface and call + * {@link LocationManager#addGpsNavigationMessageListener}. + * + * @hide + */ + public interface Listener { + void onGpsNavigationMessageReceived(GpsNavigationMessageEvent event); + } + + public GpsNavigationMessageEvent(GpsNavigationMessage message) { + if (message == null) { + throw new InvalidParameterException("Parameter 'message' must not be null."); + } + mNavigationMessage = message; + } + + @NonNull + public GpsNavigationMessage getNavigationMessage() { + return mNavigationMessage; + } + + public static final Creator<GpsNavigationMessageEvent> CREATOR = + new Creator<GpsNavigationMessageEvent>() { + @Override + public GpsNavigationMessageEvent createFromParcel(Parcel in) { + ClassLoader classLoader = getClass().getClassLoader(); + GpsNavigationMessage navigationMessage = in.readParcelable(classLoader); + return new GpsNavigationMessageEvent(navigationMessage); + } + + @Override + public GpsNavigationMessageEvent[] newArray(int size) { + return new GpsNavigationMessageEvent[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeParcelable(mNavigationMessage, flags); + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("[ GpsNavigationMessageEvent:\n\n"); + builder.append(mNavigationMessage.toString()); + builder.append("\n]"); + return builder.toString(); + } +} diff --git a/location/java/android/location/GpsNavigationMessageListenerTransport.java b/location/java/android/location/GpsNavigationMessageListenerTransport.java new file mode 100644 index 0000000..ec4812b --- /dev/null +++ b/location/java/android/location/GpsNavigationMessageListenerTransport.java @@ -0,0 +1,69 @@ +/* + * 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.content.Context; +import android.os.RemoteException; + +/** + * A handler class to manage transport listeners for {@link GpsNavigationMessageEvent.Listener}. + * + * @hide + */ +class GpsNavigationMessageListenerTransport + extends LocalListenerHelper<GpsNavigationMessageEvent.Listener> { + private final Context mContext; + private final ILocationManager mLocationManager; + + private final IGpsNavigationMessageListener mListenerTransport = new ListenerTransport(); + + public GpsNavigationMessageListenerTransport( + Context context, + ILocationManager locationManager) { + super("GpsNavigationMessageListenerTransport"); + mContext = context; + mLocationManager = locationManager; + } + + @Override + protected boolean registerWithServer() throws RemoteException { + return mLocationManager.addGpsNavigationMessageListener( + mListenerTransport, + mContext.getPackageName()); + } + + @Override + protected void unregisterFromServer() throws RemoteException { + mLocationManager.removeGpsNavigationMessageListener(mListenerTransport); + } + + private class ListenerTransport extends IGpsNavigationMessageListener.Stub { + @Override + public void onGpsNavigationMessageReceived(final GpsNavigationMessageEvent event) { + ListenerOperation<GpsNavigationMessageEvent.Listener> operation = + new ListenerOperation<GpsNavigationMessageEvent.Listener>() { + @Override + public void execute(GpsNavigationMessageEvent.Listener listener) + throws RemoteException { + listener.onGpsNavigationMessageReceived(event); + } + }; + + foreach(operation); + } + } +} diff --git a/location/java/android/location/IGpsMeasurementsListener.aidl b/location/java/android/location/IGpsMeasurementsListener.aidl new file mode 100644 index 0000000..b34bb6c --- /dev/null +++ b/location/java/android/location/IGpsMeasurementsListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.location.GpsMeasurementsEvent; + +/** + * {@hide} + */ +oneway interface IGpsMeasurementsListener { + void onGpsMeasurementsReceived(in GpsMeasurementsEvent event); +} diff --git a/location/java/android/location/IGpsNavigationMessageListener.aidl b/location/java/android/location/IGpsNavigationMessageListener.aidl new file mode 100644 index 0000000..18603fe --- /dev/null +++ b/location/java/android/location/IGpsNavigationMessageListener.aidl @@ -0,0 +1,26 @@ +/* + * Copyright (C) 2014, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.location.GpsNavigationMessageEvent; + +/** + * {@hide} + */ +oneway interface IGpsNavigationMessageListener { + void onGpsNavigationMessageReceived(in GpsNavigationMessageEvent event); +} diff --git a/location/java/android/location/ILocationManager.aidl b/location/java/android/location/ILocationManager.aidl index c353ec6..1501710 100644 --- a/location/java/android/location/ILocationManager.aidl +++ b/location/java/android/location/ILocationManager.aidl @@ -21,7 +21,8 @@ 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.IGpsNavigationMessageListener; import android.location.IGpsStatusListener; import android.location.ILocationListener; import android.location.Location; @@ -60,6 +61,14 @@ interface ILocationManager boolean sendNiResponse(int notifId, int userResponse); + boolean addGpsMeasurementsListener(in IGpsMeasurementsListener listener, in String packageName); + boolean removeGpsMeasurementsListener(in IGpsMeasurementsListener listener); + + boolean addGpsNavigationMessageListener( + in IGpsNavigationMessageListener listener, + in String packageName); + boolean removeGpsNavigationMessageListener(in IGpsNavigationMessageListener listener); + // --- deprecated --- List<String> getAllProviders(); List<String> getProviders(in Criteria criteria, boolean enabledOnly); diff --git a/location/java/android/location/LocalListenerHelper.java b/location/java/android/location/LocalListenerHelper.java new file mode 100644 index 0000000..1f3bf67 --- /dev/null +++ b/location/java/android/location/LocalListenerHelper.java @@ -0,0 +1,109 @@ +/* + * 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.os.RemoteException; +import android.util.Log; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; + +/** + * A base handler class to manage transport and local listeners. + * + * @hide + */ +abstract class LocalListenerHelper<TListener> { + private final HashSet<TListener> mListeners = new HashSet<TListener>(); + private final String mTag; + + protected LocalListenerHelper(String name) { + Preconditions.checkNotNull(name); + mTag = name; + } + + public boolean add(@NonNull TListener 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 registeredWithService; + try { + registeredWithService = registerWithServer(); + } catch (RemoteException e) { + Log.e(mTag, "Error handling first listener.", e); + return false; + } + if (!registeredWithService) { + Log.e(mTag, "Unable to register listener transport."); + return false; + } + } + + if (mListeners.contains(listener)) { + return true; + } + mListeners.add(listener); + } + return true; + } + + public void remove(@NonNull TListener listener) { + Preconditions.checkNotNull(listener); + + synchronized (mListeners) { + boolean removed = mListeners.remove(listener); + boolean isLastRemoved = removed && mListeners.isEmpty(); + if (isLastRemoved) { + try { + unregisterFromServer(); + } catch (RemoteException e) { + + } + } + } + } + + protected abstract boolean registerWithServer() throws RemoteException; + protected abstract void unregisterFromServer() throws RemoteException; + + protected interface ListenerOperation<TListener> { + void execute(TListener listener) throws RemoteException; + } + + protected void foreach(ListenerOperation operation) { + Collection<TListener> listeners; + synchronized (mListeners) { + listeners = new ArrayList<TListener>(mListeners); + } + + for (TListener listener : listeners) { + try { + operation.execute(listener); + } catch (RemoteException e) { + Log.e(mTag, "Error in monitored listener.", e); + // don't return, give a fair chance to all listeners to receive the event + } + } + } +} diff --git a/location/java/android/location/Location.java b/location/java/android/location/Location.java index f70110c..fcf222b 100644 --- a/location/java/android/location/Location.java +++ b/location/java/android/location/Location.java @@ -16,6 +16,7 @@ package android.location; +import android.annotation.SystemApi; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -583,7 +584,8 @@ public class Location implements Parcelable { } /** - * Get the altitude if available, in meters above sea level. + * Get the altitude if available, in meters above the WGS 84 reference + * ellipsoid. * * <p>If this location does not have an altitude then 0.0 is returned. */ @@ -592,7 +594,7 @@ public class Location implements Parcelable { } /** - * Set the altitude, in meters above sea level. + * Set the altitude, in meters above the WGS 84 reference ellipsoid. * * <p>Following this call {@link #hasAltitude} will return true. */ @@ -770,6 +772,7 @@ public class Location implements Parcelable { * @see #makeComplete * @hide */ + @SystemApi public boolean isComplete() { if (mProvider == null) return false; if (!mHasAccuracy) return false; @@ -787,6 +790,7 @@ public class Location implements Parcelable { * @see #isComplete * @hide */ + @SystemApi public void makeComplete() { if (mProvider == null) mProvider = "?"; if (!mHasAccuracy) { @@ -956,6 +960,7 @@ public class Location implements Parcelable { * @param isFromMockProvider true if this Location came from a mock provider, false otherwise * @hide */ + @SystemApi public void setIsFromMockProvider(boolean isFromMockProvider) { mIsFromMockProvider = isFromMockProvider; } diff --git a/location/java/android/location/LocationManager.java b/location/java/android/location/LocationManager.java index ccb4304..0445869 100644 --- a/location/java/android/location/LocationManager.java +++ b/location/java/android/location/LocationManager.java @@ -16,24 +16,24 @@ package android.location; +import com.android.internal.location.ProviderProperties; + +import android.annotation.SystemApi; 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.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 @@ -59,6 +59,8 @@ public class LocationManager { private final Context mContext; private final ILocationManager mService; + private final GpsMeasurementListenerTransport mGpsMeasurementListenerTransport; + private final GpsNavigationMessageListenerTransport mGpsNavigationMessageListenerTransport; private final HashMap<GpsStatus.Listener, GpsStatusListenerTransport> mGpsStatusListeners = new HashMap<GpsStatus.Listener, GpsStatusListenerTransport>(); private final HashMap<GpsStatus.NmeaListener, GpsStatusListenerTransport> mNmeaListeners = @@ -309,6 +311,9 @@ public class LocationManager { public LocationManager(Context context, ILocationManager service) { mService = service; mContext = context; + mGpsMeasurementListenerTransport = new GpsMeasurementListenerTransport(mContext, mService); + mGpsNavigationMessageListenerTransport = + new GpsNavigationMessageListenerTransport(mContext, mService); } private LocationProvider createProvider(String name, ProviderProperties properties) { @@ -804,6 +809,7 @@ public class LocationManager { * * @hide */ + @SystemApi public void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper) { checkListener(listener); @@ -831,6 +837,7 @@ public class LocationManager { * * @hide */ + @SystemApi public void requestLocationUpdates(LocationRequest request, PendingIntent intent) { checkPendingIntent(intent); requestLocationUpdates(request, null, null, intent); @@ -1104,11 +1111,15 @@ public class LocationManager { * unless they depend on provider-specific APIs such as * {@link #requestLocationUpdates(String, long, float, LocationListener)}. * + * <p> + * Before API version {@link android.os.Build.VERSION_CODES#L}, this + * method would throw {@link SecurityException} if the location permissions + * were not sufficient to use the specified provider. + * * @param provider the name of the provider * @return true if the provider exists and is enabled * * @throws IllegalArgumentException if provider is null - * @throws SecurityException if no suitable permission is present */ public boolean isProviderEnabled(String provider) { checkProvider(provider); @@ -1564,6 +1575,53 @@ public class LocationManager { } } + /** + * Adds a GPS Measurement listener. + * + * @param listener a {@link 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); + } + + /** + * Adds a GPS Navigation Message listener. + * + * @param listener a {@link GpsNavigationMessageEvent.Listener} object to register. + * @return {@code true} if the listener was successfully registered, {@code false} otherwise. + * + * @hide + */ + public boolean addGpsNavigationMessageListener(GpsNavigationMessageEvent.Listener listener) { + return mGpsNavigationMessageListenerTransport.add(listener); + } + + /** + * Removes a GPS Navigation Message listener. + * + * @param listener a {@link GpsNavigationMessageEvent.Listener} object to remove. + * + * @hide + */ + public void removeGpsNavigationMessageListener( + GpsNavigationMessageEvent.Listener listener) { + mGpsNavigationMessageListenerTransport.remove(listener); + } + /** * Retrieves information about the current status of the GPS engine. * This should only be called from the {@link GpsStatus.Listener#onGpsStatusChanged} @@ -1610,7 +1668,7 @@ public class LocationManager { * @hide */ public boolean sendNiResponse(int notifId, int userResponse) { - try { + try { return mService.sendNiResponse(notifId, userResponse); } catch (RemoteException e) { Log.e(TAG, "RemoteException in sendNiResponse: ", e); @@ -1642,7 +1700,7 @@ public class LocationManager { } if (!intent.isTargetedToPackage()) { IllegalArgumentException e = new IllegalArgumentException( - "pending intent msut be targeted to package"); + "pending intent must be targeted to package"); if (mContext.getApplicationInfo().targetSdkVersion > Build.VERSION_CODES.JELLY_BEAN) { throw e; } else { diff --git a/location/java/android/location/LocationRequest.java b/location/java/android/location/LocationRequest.java index c9162fe..271f2bb 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -16,6 +16,7 @@ package android.location; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; @@ -84,6 +85,7 @@ import android.util.TimeUtils; * * @hide */ +@SystemApi public final class LocationRequest implements Parcelable { /** * Used with {@link #setQuality} to request the most accurate locations available. @@ -166,6 +168,7 @@ public final class LocationRequest implements Parcelable { } /** @hide */ + @SystemApi public static LocationRequest createFromDeprecatedProvider(String provider, long minTime, float minDistance, boolean singleShot) { if (minTime < 0) minTime = 0; @@ -191,6 +194,7 @@ public final class LocationRequest implements Parcelable { } /** @hide */ + @SystemApi public static LocationRequest createFromDeprecatedCriteria(Criteria criteria, long minTime, float minDistance, boolean singleShot) { if (minTime < 0) minTime = 0; @@ -475,6 +479,7 @@ public final class LocationRequest implements Parcelable { /** @hide */ + @SystemApi public LocationRequest setProvider(String provider) { checkProvider(provider); mProvider = provider; @@ -482,11 +487,13 @@ public final class LocationRequest implements Parcelable { } /** @hide */ + @SystemApi public String getProvider() { return mProvider; } /** @hide */ + @SystemApi public LocationRequest setSmallestDisplacement(float meters) { checkDisplacement(meters); mSmallestDisplacement = meters; @@ -494,6 +501,7 @@ public final class LocationRequest implements Parcelable { } /** @hide */ + @SystemApi public float getSmallestDisplacement() { return mSmallestDisplacement; } @@ -508,11 +516,13 @@ public final class LocationRequest implements Parcelable { * @param workSource WorkSource defining power blame for this location request. * @hide */ + @SystemApi public void setWorkSource(WorkSource workSource) { mWorkSource = workSource; } /** @hide */ + @SystemApi public WorkSource getWorkSource() { return mWorkSource; } @@ -531,11 +541,13 @@ public final class LocationRequest implements Parcelable { * @see android.app.AppOpsManager * @hide */ + @SystemApi public void setHideFromAppOps(boolean hideFromAppOps) { mHideFromAppOps = hideFromAppOps; } /** @hide */ + @SystemApi public boolean getHideFromAppOps() { return mHideFromAppOps; } diff --git a/location/java/android/location/SettingInjectorService.java b/location/java/android/location/SettingInjectorService.java index 9f321f3..fcd2cde 100644 --- a/location/java/android/location/SettingInjectorService.java +++ b/location/java/android/location/SettingInjectorService.java @@ -26,7 +26,7 @@ import android.os.RemoteException; import android.util.Log; /** - * Dynamically specifies the summary (subtitle) and enabled status of a preference injected into + * Dynamically specifies the enabled status of a preference injected into * the list of app settings displayed by the system settings app * <p/> * For use only by apps that are included in the system image, for preferences that affect multiple @@ -71,13 +71,12 @@ import android.util.Log; * </ul> * * To ensure a good user experience, your {@link android.app.Application#onCreate()}, - * {@link #onGetSummary()}, and {@link #onGetEnabled()} methods must all be fast. If any are slow, - * it can delay the display of settings values for other apps as well. Note further that all are - * called on your app's UI thread. + * and {@link #onGetEnabled()} methods must all be fast. If either is slow, + * it can delay the display of settings values for other apps as well. Note further that these + * methods are called on your app's UI thread. * <p/> * For compactness, only one copy of a given setting should be injected. If each account has a - * distinct value for the setting, then the {@link #onGetSummary()} value should represent a summary - * of the state across all of the accounts and {@code settingsActivity} should display the value for + * distinct value for the setting, then only {@code settingsActivity} should display the value for * each account. */ public abstract class SettingInjectorService extends Service { @@ -109,14 +108,6 @@ public abstract class SettingInjectorService extends Service { "android.location.InjectedSettingChanged"; /** - * Name of the bundle key for the string specifying the summary for the setting (e.g., "ON" or - * "OFF"). - * - * @hide - */ - public static final String SUMMARY_KEY = "summary"; - - /** * Name of the bundle key for the string specifying whether the setting is currently enabled. * * @hide @@ -160,42 +151,31 @@ public abstract class SettingInjectorService extends Service { private void onHandleIntent(Intent intent) { - String summary; - try { - summary = onGetSummary(); - } catch (RuntimeException e) { - // Exception. Send status anyway, so that settings injector can immediately start - // loading the status of the next setting. - sendStatus(intent, null, true); - throw e; - } - boolean enabled; try { enabled = onGetEnabled(); } catch (RuntimeException e) { // Exception. Send status anyway, so that settings injector can immediately start // loading the status of the next setting. - sendStatus(intent, summary, true); + sendStatus(intent, true); throw e; } - sendStatus(intent, summary, enabled); + sendStatus(intent, enabled); } /** - * Send the summary and enabled values back to the caller via the messenger encoded in the + * Send the enabled values back to the caller via the messenger encoded in the * intent. */ - private void sendStatus(Intent intent, String summary, boolean enabled) { + private void sendStatus(Intent intent, boolean enabled) { Message message = Message.obtain(); Bundle bundle = new Bundle(); - bundle.putString(SUMMARY_KEY, summary); bundle.putBoolean(ENABLED_KEY, enabled); message.setData(bundle); if (Log.isLoggable(TAG, Log.DEBUG)) { - Log.d(TAG, mName + ": received " + intent + ", summary=" + summary + Log.d(TAG, mName + ": received " + intent + ", enabled=" + enabled + ", sending message: " + message); } @@ -208,12 +188,14 @@ public abstract class SettingInjectorService extends Service { } /** - * Returns the {@link android.preference.Preference#getSummary()} value (allowed to be null or - * empty). Should not perform unpredictably-long operations such as network access--see the - * running-time comments in the class-level javadoc. + * This method is no longer called, because status values are no longer shown for any injected + * setting. + * + * @return ignored * - * @return the {@link android.preference.Preference#getSummary()} value + * @deprecated not called any more */ + @Deprecated protected abstract String onGetSummary(); /** diff --git a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java index 57e2786..e9e475c 100644 --- a/location/java/com/android/internal/location/GpsNetInitiatedHandler.java +++ b/location/java/com/android/internal/location/GpsNetInitiatedHandler.java @@ -21,16 +21,25 @@ import java.io.UnsupportedEncodingException; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.location.LocationManager; +import android.location.INetInitiatedListener; +import android.telephony.TelephonyManager; +import android.telephony.PhoneNumberUtils; +import android.telephony.PhoneStateListener; import android.os.Bundle; import android.os.RemoteException; import android.os.UserHandle; +import android.os.SystemProperties; +import android.provider.Settings; import android.util.Log; import com.android.internal.R; import com.android.internal.telephony.GsmAlphabet; +import com.android.internal.telephony.TelephonyProperties; /** * A GPS Network-initiated Handler class used by LocationManager. @@ -46,55 +55,70 @@ public class GpsNetInitiatedHandler { // NI verify activity for bringing up UI (not used yet) public static final String ACTION_NI_VERIFY = "android.intent.action.NETWORK_INITIATED_VERIFY"; - + // string constants for defining data fields in NI Intent public static final String NI_INTENT_KEY_NOTIF_ID = "notif_id"; public static final String NI_INTENT_KEY_TITLE = "title"; public static final String NI_INTENT_KEY_MESSAGE = "message"; public static final String NI_INTENT_KEY_TIMEOUT = "timeout"; public static final String NI_INTENT_KEY_DEFAULT_RESPONSE = "default_resp"; - + // the extra command to send NI response to GpsLocationProvider public static final String NI_RESPONSE_EXTRA_CMD = "send_ni_response"; - + // the extra command parameter names in the Bundle public static final String NI_EXTRA_CMD_NOTIF_ID = "notif_id"; public static final String NI_EXTRA_CMD_RESPONSE = "response"; - + // these need to match GpsNiType constants in gps_ni.h public static final int GPS_NI_TYPE_VOICE = 1; public static final int GPS_NI_TYPE_UMTS_SUPL = 2; public static final int GPS_NI_TYPE_UMTS_CTRL_PLANE = 3; - - // these need to match GpsUserResponseType constants in gps_ni.h + public static final int GPS_NI_TYPE_EMERGENCY_SUPL = 4; + + // these need to match GpsUserResponseType constants in gps_ni.h public static final int GPS_NI_RESPONSE_ACCEPT = 1; public static final int GPS_NI_RESPONSE_DENY = 2; - public static final int GPS_NI_RESPONSE_NORESP = 3; - + public static final int GPS_NI_RESPONSE_NORESP = 3; + public static final int GPS_NI_RESPONSE_IGNORE = 4; + // these need to match GpsNiNotifyFlags constants in gps_ni.h public static final int GPS_NI_NEED_NOTIFY = 0x0001; public static final int GPS_NI_NEED_VERIFY = 0x0002; public static final int GPS_NI_PRIVACY_OVERRIDE = 0x0004; - + // these need to match GpsNiEncodingType in gps_ni.h public static final int GPS_ENC_NONE = 0; public static final int GPS_ENC_SUPL_GSM_DEFAULT = 1; public static final int GPS_ENC_SUPL_UTF8 = 2; public static final int GPS_ENC_SUPL_UCS2 = 3; public static final int GPS_ENC_UNKNOWN = -1; - + private final Context mContext; - + private final TelephonyManager mTelephonyManager; + private final PhoneStateListener mPhoneStateListener; + // parent gps location provider private final LocationManager mLocationManager; - + // configuration of notificaiton behavior private boolean mPlaySounds = false; private boolean mPopupImmediately = true; - - // Set to true if string from HAL is encoded as Hex, e.g., "3F0039" + + // read the SUPL_ES form gps.conf + private volatile boolean mIsSuplEsEnabled; + + // Set to true if the phone is having emergency call. + private volatile boolean mIsInEmergency; + + // If Location function is enabled. + private volatile boolean mIsLocationEnabled = false; + + private final INetInitiatedListener mNetInitiatedListener; + + // Set to true if string from HAL is encoded as Hex, e.g., "3F0039" static private boolean mIsHexInput = true; - + public static class GpsNiNotification { public int notificationId; @@ -110,58 +134,137 @@ public class GpsNetInitiatedHandler { public int textEncoding; public Bundle extras; }; - + public static class GpsNiResponse { - /* User reponse, one of the values in GpsUserResponseType */ + /* User response, one of the values in GpsUserResponseType */ int userResponse; /* Optional extra data to pass with the user response */ Bundle extras; }; - + + private final BroadcastReceiver mBroadcastReciever = new BroadcastReceiver() { + + @Override public void onReceive(Context context, Intent intent) { + String action = intent.getAction(); + if (action.equals(Intent.ACTION_NEW_OUTGOING_CALL)) { + String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER); + /* + Emergency Mode is when during emergency call or in emergency call back mode. + For checking if it is during emergency call: + mIsInEmergency records if the phone is in emergency call or not. It will + be set to true when the phone is having emergency call, and then will + be set to false by mPhoneStateListener when the emergency call ends. + For checking if it is in emergency call back mode: + Emergency call back mode will be checked by reading system properties + when necessary: SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE) + */ + setInEmergency(PhoneNumberUtils.isEmergencyNumber(phoneNumber)); + if (DEBUG) Log.v(TAG, "ACTION_NEW_OUTGOING_CALL - " + getInEmergency()); + } else if (action.equals(LocationManager.MODE_CHANGED_ACTION)) { + updateLocationMode(); + if (DEBUG) Log.d(TAG, "location enabled :" + getLocationEnabled()); + } + } + }; + /** * The notification that is shown when a network-initiated notification - * (and verification) event is received. + * (and verification) event is received. * <p> * This is lazily created, so use {@link #setNINotification()}. */ private Notification mNiNotification; - - public GpsNetInitiatedHandler(Context context) { + + public GpsNetInitiatedHandler(Context context, + INetInitiatedListener netInitiatedListener, + boolean isSuplEsEnabled) { mContext = context; + + if (netInitiatedListener == null) { + throw new IllegalArgumentException("netInitiatedListener is null"); + } else { + mNetInitiatedListener = netInitiatedListener; + } + + setSuplEsEnabled(isSuplEsEnabled); mLocationManager = (LocationManager)context.getSystemService(Context.LOCATION_SERVICE); + updateLocationMode(); + mTelephonyManager = + (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); + + mPhoneStateListener = new PhoneStateListener() { + @Override + public void onCallStateChanged(int state, String incomingNumber) { + if (DEBUG) Log.d(TAG, "onCallStateChanged(): state is "+ state); + // listening for emergency call ends + if (state == TelephonyManager.CALL_STATE_IDLE) { + setInEmergency(false); + } + } + }; + mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); + + IntentFilter intentFilter = new IntentFilter(); + intentFilter.addAction(Intent.ACTION_NEW_OUTGOING_CALL); + intentFilter.addAction(LocationManager.MODE_CHANGED_ACTION); + mContext.registerReceiver(mBroadcastReciever, intentFilter); } - - // Handles NI events from HAL - public void handleNiNotification(GpsNiNotification notif) - { - if (DEBUG) Log.d(TAG, "handleNiNotification" + " notificationId: " + notif.notificationId - + " requestorId: " + notif.requestorId + " text: " + notif.text); - // Notify and verify with immediate pop-up - if (notif.needNotify && notif.needVerify && mPopupImmediately) - { - // Popup the dialog box now - openNiDialog(notif); - } + public void setSuplEsEnabled(boolean isEnabled) { + mIsSuplEsEnabled = isEnabled; + } - // Notify only, or delayed pop-up (change mPopupImmediately to FALSE) - if (notif.needNotify && !notif.needVerify || - notif.needNotify && notif.needVerify && !mPopupImmediately) - { - // Show the notification + public boolean getSuplEsEnabled() { + return mIsSuplEsEnabled; + } - // if mPopupImmediately == FALSE and needVerify == TRUE, a dialog will be opened - // when the user opens the notification message + /** + * Updates Location enabler based on location setting. + */ + public void updateLocationMode() { + mIsLocationEnabled = mLocationManager.isProviderEnabled(LocationManager.GPS_PROVIDER); + } - setNiNotification(notif); - } + /** + * Checks if user agreed to use location. + */ + public boolean getLocationEnabled() { + return mIsLocationEnabled; + } - // ACCEPT cases: 1. Notify, no verify; 2. no notify, no verify; 3. privacy override. - if ( notif.needNotify && !notif.needVerify || - !notif.needNotify && !notif.needVerify || - notif.privacyOverride) - { - mLocationManager.sendNiResponse(notif.notificationId, GPS_NI_RESPONSE_ACCEPT); + // Note: Currently, there are two mechanisms involved to determine if a + // phone is in emergency mode: + // 1. If the user is making an emergency call, this is provided by activly + // monitoring the outgoing phone number; + // 2. If the device is in a emergency callback state, this is provided by + // system properties. + // If either one of above exists, the phone is considered in an emergency + // mode. Because of this complexity, we need to be careful about how to set + // and clear the emergency state. + public void setInEmergency(boolean isInEmergency) { + mIsInEmergency = isInEmergency; + } + + public boolean getInEmergency() { + boolean isInEmergencyCallback = Boolean.parseBoolean( + SystemProperties.get(TelephonyProperties.PROPERTY_INECM_MODE)); + return mIsInEmergency || isInEmergencyCallback; + } + + + // Handles NI events from HAL + public void handleNiNotification(GpsNiNotification notif) { + if (DEBUG) Log.d(TAG, "in handleNiNotification () :" + + " notificationId: " + notif.notificationId + + " requestorId: " + notif.requestorId + + " text: " + notif.text + + " mIsSuplEsEnabled" + getSuplEsEnabled() + + " mIsLocationEnabled" + getLocationEnabled()); + + if (getSuplEsEnabled()) { + handleNiInEs(notif); + } else { + handleNi(notif); } ////////////////////////////////////////////////////////////////////////// @@ -177,6 +280,78 @@ public class GpsNetInitiatedHandler { // } + // handle NI form HAL when SUPL_ES is disabled. + private void handleNi(GpsNiNotification notif) { + if (DEBUG) Log.d(TAG, "in handleNi () :" + + " needNotify: " + notif.needNotify + + " needVerify: " + notif.needVerify + + " privacyOverride: " + notif.privacyOverride + + " mPopupImmediately: " + mPopupImmediately + + " mInEmergency: " + getInEmergency()); + + if (!getLocationEnabled() && !getInEmergency()) { + // Location is currently disabled, ignore all NI requests. + try { + mNetInitiatedListener.sendNiResponse(notif.notificationId, + GPS_NI_RESPONSE_IGNORE); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in sendNiResponse"); + } + } + if (notif.needNotify) { + // If NI does not need verify or the dialog is not requested + // to pop up immediately, the dialog box will not pop up. + if (notif.needVerify && mPopupImmediately) { + // Popup the dialog box now + openNiDialog(notif); + } else { + // Show the notification + setNiNotification(notif); + } + } + // ACCEPT cases: 1. Notify, no verify; 2. no notify, no verify; + // 3. privacy override. + if (!notif.needVerify || notif.privacyOverride) { + try { + mNetInitiatedListener.sendNiResponse(notif.notificationId, + GPS_NI_RESPONSE_ACCEPT); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in sendNiResponse"); + } + } + } + + // handle NI from HAL when the SUPL_ES is enabled + private void handleNiInEs(GpsNiNotification notif) { + + if (DEBUG) Log.d(TAG, "in handleNiInEs () :" + + " niType: " + notif.niType + + " notificationId: " + notif.notificationId); + + // UE is in emergency mode when in emergency call mode or in emergency call back mode + /* + 1. When SUPL ES bit is off and UE is not in emergency mode: + Call handleNi() to do legacy behaviour. + 2. When SUPL ES bit is on and UE is in emergency mode: + Call handleNi() to do acceptance behaviour. + 3. When SUPL ES bit is off but UE is in emergency mode: + Ignore the emergency SUPL INIT. + 4. When SUPL ES bit is on but UE is not in emergency mode: + Ignore the emergency SUPL INIT. + */ + boolean isNiTypeES = (notif.niType == GPS_NI_TYPE_EMERGENCY_SUPL); + if (isNiTypeES != getInEmergency()) { + try { + mNetInitiatedListener.sendNiResponse(notif.notificationId, + GPS_NI_RESPONSE_IGNORE); + } catch (RemoteException e) { + Log.e(TAG, "RemoteException in sendNiResponse"); + } + } else { + handleNi(notif); + } + } + // Sets the NI notification. private synchronized void setNiNotification(GpsNiNotification notif) { NotificationManager notificationManager = (NotificationManager) mContext @@ -203,14 +378,16 @@ public class GpsNetInitiatedHandler { mNiNotification.defaults |= Notification.DEFAULT_SOUND; } else { mNiNotification.defaults &= ~Notification.DEFAULT_SOUND; - } + } mNiNotification.flags = Notification.FLAG_ONGOING_EVENT | Notification.FLAG_AUTO_CANCEL; mNiNotification.tickerText = getNotifTicker(notif, mContext); // if not to popup dialog immediately, pending intent will open the dialog Intent intent = !mPopupImmediately ? getDlgIntent(notif) : new Intent(); - PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent, 0); + mNiNotification.color = mContext.getResources().getColor( + com.android.internal.R.color.system_notification_accent_color); mNiNotification.setLatestEventInfo(mContext, title, message, pi); notificationManager.notifyAsUser(null, notif.notificationId, mNiNotification, @@ -229,7 +406,7 @@ public class GpsNetInitiatedHandler { mContext.startActivity(intent); } - // Construct the intent for bringing up the dialog activity, which shows the + // Construct the intent for bringing up the dialog activity, which shows the // notification and takes user input private Intent getDlgIntent(GpsNiNotification notif) { @@ -238,7 +415,7 @@ public class GpsNetInitiatedHandler { String message = getDialogMessage(notif, mContext); // directly bring up the NI activity - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK); intent.setClass(mContext, com.android.internal.app.NetInitiatedActivity.class); // put data in the intent @@ -408,7 +585,7 @@ public class GpsNetInitiatedHandler { decodeString(notif.requestorId, mIsHexInput, notif.requestorIdEncoding), decodeString(notif.text, mIsHexInput, notif.textEncoding)); return message; - } + } // change this to configure dialog display (for verification) static public String getDialogTitle(GpsNiNotification notif, Context context) diff --git a/location/lib/java/com/android/location/provider/ActivityChangedEvent.java b/location/lib/java/com/android/location/provider/ActivityChangedEvent.java new file mode 100644 index 0000000..c7dfc88 --- /dev/null +++ b/location/lib/java/com/android/location/provider/ActivityChangedEvent.java @@ -0,0 +1,56 @@ +/* + * 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.location.provider; + +import android.annotation.NonNull; + +import java.security.InvalidParameterException; +import java.util.List; + +/** + * A class representing an event for Activity changes. + */ +public class ActivityChangedEvent { + private final List<ActivityRecognitionEvent> mActivityRecognitionEvents; + + public ActivityChangedEvent(List<ActivityRecognitionEvent> activityRecognitionEvents) { + if (activityRecognitionEvents == null) { + throw new InvalidParameterException( + "Parameter 'activityRecognitionEvents' must not be null."); + } + + mActivityRecognitionEvents = activityRecognitionEvents; + } + + @NonNull + public Iterable<ActivityRecognitionEvent> getActivityRecognitionEvents() { + return mActivityRecognitionEvents; + } + + @Override + public String toString() { + StringBuilder builder = new StringBuilder("[ ActivityChangedEvent:"); + + for (ActivityRecognitionEvent event : mActivityRecognitionEvents) { + builder.append("\n "); + builder.append(event.toString()); + } + builder.append("\n]"); + + return builder.toString(); + } +} diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java b/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java new file mode 100644 index 0000000..a39cff2 --- /dev/null +++ b/location/lib/java/com/android/location/provider/ActivityRecognitionEvent.java @@ -0,0 +1,70 @@ +/* + * 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.location.provider; + +/** + * A class that represents an Activity Recognition Event. + */ +public class ActivityRecognitionEvent { + private final String mActivity; + private final int mEventType; + private final long mTimestampNs; + + public ActivityRecognitionEvent(String activity, int eventType, long timestampNs) { + mActivity = activity; + mEventType = eventType; + mTimestampNs = timestampNs; + } + + public String getActivity() { + return mActivity; + } + + public int getEventType() { + return mEventType; + } + + public long getTimestampNs() { + return mTimestampNs; + } + + @Override + public String toString() { + String eventString; + switch (mEventType) { + case ActivityRecognitionProvider.EVENT_TYPE_ENTER: + eventString = "Enter"; + break; + case ActivityRecognitionProvider.EVENT_TYPE_EXIT: + eventString = "Exit"; + break; + case ActivityRecognitionProvider.EVENT_TYPE_FLUSH_COMPLETE: + eventString = "FlushComplete"; + break; + default: + eventString = "<Invalid>"; + break; + } + + return String.format( + "Activity='%s', EventType=%s(%s), TimestampNs=%s", + mActivity, + eventString, + mEventType, + mTimestampNs); + } +} diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java new file mode 100644 index 0000000..da33464 --- /dev/null +++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProvider.java @@ -0,0 +1,134 @@ +/* + * 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.location.provider; + +import com.android.internal.util.Preconditions; + +import android.hardware.location.IActivityRecognitionHardware; +import android.hardware.location.IActivityRecognitionHardwareSink; +import android.os.RemoteException; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashSet; + +/** + * A class that exposes {@link IActivityRecognitionHardware} functionality to unbundled services. + */ +public final class ActivityRecognitionProvider { + private final IActivityRecognitionHardware mService; + private final HashSet<Sink> mSinkSet = new HashSet<Sink>(); + private final SinkTransport mSinkTransport = new SinkTransport(); + + // the following constants must remain in sync with activity_recognition.h + + public static final String ACTIVITY_IN_VEHICLE = "android.activity_recognition.in_vehicle"; + public static final String ACTIVITY_ON_BICYCLE = "android.activity_recognition.on_bicycle"; + public static final String ACTIVITY_WALKING = "android.activity_recognition.walking"; + public static final String ACTIVITY_RUNNING = "android.activity_recognition.running"; + public static final String ACTIVITY_STILL = "android.activity_recognition.still"; + public static final String ACTIVITY_TILTING = "android.activity_recognition.tilting"; + + public static final int EVENT_TYPE_FLUSH_COMPLETE = 0; + public static final int EVENT_TYPE_ENTER = 1; + public static final int EVENT_TYPE_EXIT = 2; + + // end constants activity_recognition.h + + /** + * Used to receive Activity-Recognition events. + */ + public interface Sink { + void onActivityChanged(ActivityChangedEvent event); + } + + public ActivityRecognitionProvider(IActivityRecognitionHardware service) + throws RemoteException { + Preconditions.checkNotNull(service); + mService = service; + mService.registerSink(mSinkTransport); + } + + public String[] getSupportedActivities() throws RemoteException { + return mService.getSupportedActivities(); + } + + public boolean isActivitySupported(String activity) throws RemoteException { + return mService.isActivitySupported(activity); + } + + public void registerSink(Sink sink) { + Preconditions.checkNotNull(sink); + synchronized (mSinkSet) { + mSinkSet.add(sink); + } + } + + // TODO: if this functionality is exposed to 3rd party developers, handle unregistration (here + // and in the service) of all sinks while failing to disable all events + public void unregisterSink(Sink sink) { + Preconditions.checkNotNull(sink); + synchronized (mSinkSet) { + mSinkSet.remove(sink); + } + } + + public boolean enableActivityEvent(String activity, int eventType, long reportLatencyNs) + throws RemoteException { + return mService.enableActivityEvent(activity, eventType, reportLatencyNs); + } + + public boolean disableActivityEvent(String activity, int eventType) throws RemoteException { + return mService.disableActivityEvent(activity, eventType); + } + + public boolean flush() throws RemoteException { + return mService.flush(); + } + + private final class SinkTransport extends IActivityRecognitionHardwareSink.Stub { + @Override + public void onActivityChanged( + android.hardware.location.ActivityChangedEvent activityChangedEvent) { + Collection<Sink> sinks; + synchronized (mSinkSet) { + if (mSinkSet.isEmpty()) { + return; + } + + sinks = new ArrayList<Sink>(mSinkSet); + } + + // translate the event from platform internal and GmsCore types + ArrayList<ActivityRecognitionEvent> gmsEvents = + new ArrayList<ActivityRecognitionEvent>(); + for (android.hardware.location.ActivityRecognitionEvent event + : activityChangedEvent.getActivityRecognitionEvents()) { + ActivityRecognitionEvent gmsEvent = new ActivityRecognitionEvent( + event.getActivity(), + event.getEventType(), + event.getTimestampNs()); + gmsEvents.add(gmsEvent); + } + ActivityChangedEvent gmsEvent = new ActivityChangedEvent(gmsEvents); + + for (Sink sink : sinks) { + sink.onActivityChanged(gmsEvent); + } + } + } +} diff --git a/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java new file mode 100644 index 0000000..03dd042 --- /dev/null +++ b/location/lib/java/com/android/location/provider/ActivityRecognitionProviderWatcher.java @@ -0,0 +1,86 @@ +/* + * 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.location.provider; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.hardware.location.IActivityRecognitionHardware; +import android.hardware.location.IActivityRecognitionHardwareWatcher; +import android.os.Binder; +import android.os.IBinder; +import android.os.Process; +import android.os.RemoteException; +import android.util.Log; + +/** + * A watcher class for Activity-Recognition instances. + */ +public class ActivityRecognitionProviderWatcher { + private static final String TAG = "ActivityRecognitionProviderWatcher"; + + private static ActivityRecognitionProviderWatcher sWatcher; + private static final Object sWatcherLock = new Object(); + + private ActivityRecognitionProvider mActivityRecognitionProvider; + + private ActivityRecognitionProviderWatcher() {} + + public static ActivityRecognitionProviderWatcher getInstance() { + synchronized (sWatcherLock) { + if (sWatcher == null) { + sWatcher = new ActivityRecognitionProviderWatcher(); + } + return sWatcher; + } + } + + private IActivityRecognitionHardwareWatcher.Stub mWatcherStub = + new IActivityRecognitionHardwareWatcher.Stub() { + @Override + public void onInstanceChanged(IActivityRecognitionHardware instance) { + int callingUid = Binder.getCallingUid(); + if (callingUid != Process.SYSTEM_UID) { + Log.d(TAG, "Ignoring calls from non-system server. Uid: " + callingUid); + return; + } + + try { + mActivityRecognitionProvider = new ActivityRecognitionProvider(instance); + } catch (RemoteException e) { + Log.e(TAG, "Error creating Hardware Activity-Recognition", e); + } + } + }; + + /** + * Gets the binder needed to interact with proxy provider in the platform. + */ + @NonNull + public IBinder getBinder() { + return mWatcherStub; + } + + /** + * Gets an object that supports the functionality of {@link ActivityRecognitionProvider}. + * + * @return Non-null value if the functionality is supported by the platform, false otherwise. + */ + @Nullable + public ActivityRecognitionProvider getActivityRecognitionProvider() { + return mActivityRecognitionProvider; + } +} diff --git a/location/lib/java/com/android/location/provider/GeofenceProvider.java b/location/lib/java/com/android/location/provider/GeofenceProvider.java index 2618f34..fafaa84 100644 --- a/location/lib/java/com/android/location/provider/GeofenceProvider.java +++ b/location/lib/java/com/android/location/provider/GeofenceProvider.java @@ -21,9 +21,6 @@ import android.hardware.location.IGeofenceHardware; import android.os.IBinder; import android.location.IGeofenceProvider; -import android.util.Log; - -import java.lang.Long; /** * Base class for geofence providers implemented as unbundled services. diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java index 150c289..d717f40 100644 --- a/location/lib/java/com/android/location/provider/LocationProviderBase.java +++ b/location/lib/java/com/android/location/provider/LocationProviderBase.java @@ -24,7 +24,6 @@ import android.content.Context; import android.location.ILocationManager; import android.location.Location; import android.location.LocationManager; -import android.location.LocationRequest; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; |