diff options
Diffstat (limited to 'location')
34 files changed, 4472 insertions, 90 deletions
diff --git a/location/Android.mk b/location/Android.mk index 12db2f7..feeb8ce 100644 --- a/location/Android.mk +++ b/location/Android.mk @@ -14,6 +14,4 @@ LOCAL_PATH := $(call my-dir) -ifeq ($(TARGET_BUILD_APPS),) include $(call all-makefiles-under, $(LOCAL_PATH)) -endif diff --git a/location/java/android/location/FusedBatchOptions.aidl b/location/java/android/location/FusedBatchOptions.aidl new file mode 100644 index 0000000..94cb6e1 --- /dev/null +++ b/location/java/android/location/FusedBatchOptions.aidl @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2013, 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 FusedBatchOptions;
\ No newline at end of file diff --git a/location/java/android/location/FusedBatchOptions.java b/location/java/android/location/FusedBatchOptions.java new file mode 100644 index 0000000..5600aeb --- /dev/null +++ b/location/java/android/location/FusedBatchOptions.java @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2013 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.location; + +import android.os.Parcel; +import android.os.Parcelable; + +/** + * A data class representing a set of options to configure batching sessions. + * @hide + */ +public class FusedBatchOptions implements Parcelable { + private volatile long mPeriodInNS = 0; + private volatile int mSourcesToUse = 0; + private volatile int mFlags = 0; + + // the default value is set to request fixes at no cost + private volatile double mMaxPowerAllocationInMW = 0; + + /* + * Getters and setters for properties needed to hold the options. + */ + public void setMaxPowerAllocationInMW(double value) { + mMaxPowerAllocationInMW = value; + } + + public double getMaxPowerAllocationInMW() { + return mMaxPowerAllocationInMW; + } + + public void setPeriodInNS(long value) { + mPeriodInNS = value; + } + + public long getPeriodInNS() { + return mPeriodInNS; + } + + public void setSourceToUse(int source) { + mSourcesToUse |= source; + } + + public void resetSourceToUse(int source) { + mSourcesToUse &= ~source; + } + + public boolean isSourceToUseSet(int source) { + return (mSourcesToUse & source) != 0; + } + + public int getSourcesToUse() { + return mSourcesToUse; + } + + public void setFlag(int flag) { + mFlags |= flag; + } + + public void resetFlag(int flag) { + mFlags &= ~flag; + } + + public boolean isFlagSet(int flag) { + return (mFlags & flag) != 0; + } + + public int getFlags() { + return mFlags; + } + + /** + * Definition of enum flag sets needed by this class. + * Such values need to be kept in sync with the ones in fused_location.h + */ + public static final class SourceTechnologies { + public static int GNSS = 1<<0; + public static int WIFI = 1<<1; + public static int SENSORS = 1<<2; + public static int CELL = 1<<3; + public static int BLUETOOTH = 1<<4; + } + + public static final class BatchFlags { + // 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; + } + + /* + * Method definitions to support Parcelable operations. + */ + public static final Parcelable.Creator<FusedBatchOptions> CREATOR = + new Parcelable.Creator<FusedBatchOptions>() { + @Override + public FusedBatchOptions createFromParcel(Parcel parcel) { + FusedBatchOptions options = new FusedBatchOptions(); + options.setMaxPowerAllocationInMW(parcel.readDouble()); + options.setPeriodInNS(parcel.readLong()); + options.setSourceToUse(parcel.readInt()); + options.setFlag(parcel.readInt()); + return options; + } + + @Override + public FusedBatchOptions[] newArray(int size) { + return new FusedBatchOptions[size]; + } + }; + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel parcel, int flags) { + parcel.writeDouble(mMaxPowerAllocationInMW); + parcel.writeLong(mPeriodInNS); + parcel.writeInt(mSourcesToUse); + parcel.writeInt(mFlags); + } +} 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/IFusedGeofenceHardware.aidl b/location/java/android/location/IFusedGeofenceHardware.aidl new file mode 100644 index 0000000..d8c3585 --- /dev/null +++ b/location/java/android/location/IFusedGeofenceHardware.aidl @@ -0,0 +1,93 @@ +/* + * Copyright (C) 2013, 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/license/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.hardware.location.GeofenceHardwareRequestParcelable; + +/** + * Fused Geofence Hardware interface. + * + * <p>This interface is the basic set of supported functionality by Fused Hardware modules that offer + * Geofencing capabilities. + * + * All operations are asynchronous and the status codes can be obtained via a set of callbacks. + * + * @hide + */ +interface IFusedGeofenceHardware { + /** + * Flags if the interface functionality is supported by the platform. + * + * @return true if the functionality is supported, false otherwise. + */ + boolean isSupported(); + + /** + * Adds a given list of geofences to the system. + * + * @param geofenceRequestsArray The list of geofences to add. + */ + void addGeofences(in GeofenceHardwareRequestParcelable[] geofenceRequestsArray); + + /** + * Removes a give list of geofences from the system. + * + * @param geofences The list of geofences to remove. + */ + void removeGeofences(in int[] geofenceIds); + + /** + * Pauses monitoring a particular geofence. + * + * @param geofenceId The geofence to pause monitoring. + */ + void pauseMonitoringGeofence(in int geofenceId); + + /** + * Resumes monitoring a particular geofence. + * + * @param geofenceid The geofence to resume monitoring. + * @param transitionsToMonitor The transitions to monitor upon resume. + * + * Remarks: keep naming of geofence request options consistent with the naming used in + * GeofenceHardwareRequest + */ + void resumeMonitoringGeofence(in int geofenceId, in int monitorTransitions); + + /** + * Modifies the request options if a geofence that is already known by the + * system. + * + * @param geofenceId The geofence to modify. + * @param lastTransition The last known transition state of + * the geofence. + * @param monitorTransitions The set of transitions to monitor. + * @param notificationResponsiveness The notification responsivness needed. + * @param unknownTimer The time span associated with the. + * @param sourcesToUse The source technologies to use. + * + * Remarks: keep the options as separate fields to be able to leverage the class + * GeofenceHardwareRequest without any changes + */ + void modifyGeofenceOptions( + in int geofenceId, + in int lastTransition, + in int monitorTransitions, + in int notificationResponsiveness, + in int unknownTimer, + in int sourcesToUse); +} diff --git a/location/java/android/location/IFusedProvider.aidl b/location/java/android/location/IFusedProvider.aidl new file mode 100644 index 0000000..8870d2a --- /dev/null +++ b/location/java/android/location/IFusedProvider.aidl @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2013 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.hardware.location.IFusedLocationHardware; + +/** + * Interface definition for Location providers that require FLP services. + * @hide + */ +interface IFusedProvider { + /** + * Provides access to a FusedLocationHardware instance needed for the provider to work. + * + * @param instance The FusedLocationHardware available for the provider to use. + */ + void onFusedLocationHardwareChange(in IFusedLocationHardware instance); +}
\ No newline at end of file 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 989178a..ed408e0 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 = @@ -152,12 +154,25 @@ public class LocationManager { /** * Broadcast intent action when the configured location providers - * change. + * change. For use with {@link #isProviderEnabled(String)}. If you're interacting with the + * {@link android.provider.Settings.Secure#LOCATION_MODE} API, use {@link #MODE_CHANGED_ACTION} + * instead. */ public static final String PROVIDERS_CHANGED_ACTION = "android.location.PROVIDERS_CHANGED"; /** + * Broadcast intent action when {@link android.provider.Settings.Secure#LOCATION_MODE} changes. + * For use with the {@link android.provider.Settings.Secure#LOCATION_MODE} API. + * If you're interacting with {@link #isProviderEnabled(String)}, use + * {@link #PROVIDERS_CHANGED_ACTION} instead. + * + * In the future, there may be mode changes that do not result in + * {@link #PROVIDERS_CHANGED_ACTION} broadcasts. + */ + public static final String MODE_CHANGED_ACTION = "android.location.MODE_CHANGED"; + + /** * Broadcast intent action indicating that the GPS has either started or * stopped receiving GPS fixes. An intent extra provides this state as a * boolean, where {@code true} means that the GPS is actively receiving fixes. @@ -177,6 +192,17 @@ public class LocationManager { */ public static final String EXTRA_GPS_ENABLED = "enabled"; + /** + * Broadcast intent action indicating that a high power location requests + * has either started or stopped being active. The current state of + * active location requests should be read from AppOpsManager using + * {@code OP_MONITOR_HIGH_POWER_LOCATION}. + * + * @hide + */ + public static final String HIGH_POWER_REQUEST_CHANGE_ACTION = + "android.location.HIGH_POWER_REQUEST_CHANGE"; + // Map from LocationListeners to their associated ListenerTransport objects private HashMap<LocationListener,ListenerTransport> mListeners = new HashMap<LocationListener,ListenerTransport>(); @@ -285,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) { @@ -780,6 +809,7 @@ public class LocationManager { * * @hide */ + @SystemApi public void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper) { checkListener(listener); @@ -807,6 +837,7 @@ public class LocationManager { * * @hide */ + @SystemApi public void requestLocationUpdates(LocationRequest request, PendingIntent intent) { checkPendingIntent(intent); requestLocationUpdates(request, null, null, intent); @@ -1075,11 +1106,20 @@ public class LocationManager { * <p>If the user has enabled this provider in the Settings menu, true * is returned otherwise false is returned * + * <p>Callers should instead use + * {@link android.provider.Settings.Secure#LOCATION_MODE} + * 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#LOLLIPOP}, 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 is enabled + * @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); @@ -1535,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} @@ -1581,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); @@ -1613,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 b607731..65e7ced 100644 --- a/location/java/android/location/LocationRequest.java +++ b/location/java/android/location/LocationRequest.java @@ -16,9 +16,11 @@ package android.location; +import android.annotation.SystemApi; import android.os.Parcel; import android.os.Parcelable; import android.os.SystemClock; +import android.os.WorkSource; import android.util.TimeUtils; @@ -83,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. @@ -145,6 +148,8 @@ public final class LocationRequest implements Parcelable { private long mExpireAt = Long.MAX_VALUE; // no expiry private int mNumUpdates = Integer.MAX_VALUE; // no expiry private float mSmallestDisplacement = 0.0f; // meters + private WorkSource mWorkSource = null; + private boolean mHideFromAppOps = false; // True if this request shouldn't be counted by AppOps private String mProvider = LocationManager.FUSED_PROVIDER; // for deprecated APIs that explicitly request a provider @@ -163,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; @@ -188,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; @@ -234,6 +241,8 @@ public final class LocationRequest implements Parcelable { mNumUpdates = src.mNumUpdates; mSmallestDisplacement = src.mSmallestDisplacement; mProvider = src.mProvider; + mWorkSource = src.mWorkSource; + mHideFromAppOps = src.mHideFromAppOps; } /** @@ -284,7 +293,7 @@ public final class LocationRequest implements Parcelable { * no location sources are available), or you may receive them * slower than requested. You may also receive them faster than * requested (if other applications are requesting location at a - * faster interval). The fastest rate that that you will receive + * faster interval). The fastest rate that you will receive * updates can be controlled with {@link #setFastestInterval}. * * <p>Applications with only the coarse location permission may have their @@ -471,6 +480,7 @@ public final class LocationRequest implements Parcelable { /** @hide */ + @SystemApi public LocationRequest setProvider(String provider) { checkProvider(provider); mProvider = provider; @@ -478,11 +488,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; @@ -490,10 +502,57 @@ public final class LocationRequest implements Parcelable { } /** @hide */ + @SystemApi public float getSmallestDisplacement() { return mSmallestDisplacement; } + /** + * Sets the WorkSource to use for power blaming of this location request. + * + * <p>No permissions are required to make this call, however the LocationManager + * will throw a SecurityException when requesting location updates if the caller + * doesn't have the {@link android.Manifest.permission#UPDATE_DEVICE_STATS} permission. + * + * @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; + } + + /** + * Sets whether or not this location request should be hidden from AppOps. + * + * <p>Hiding a location request from AppOps will remove user visibility in the UI as to this + * request's existence. It does not affect power blaming in the Battery page. + * + * <p>No permissions are required to make this call, however the LocationManager + * will throw a SecurityException when requesting location updates if the caller + * doesn't have the {@link android.Manifest.permission#UPDATE_APP_OPS_STATS} permission. + * + * @param hideFromAppOps If true AppOps won't keep track of this location request. + * @see android.app.AppOpsManager + * @hide + */ + @SystemApi + public void setHideFromAppOps(boolean hideFromAppOps) { + mHideFromAppOps = hideFromAppOps; + } + + /** @hide */ + @SystemApi + public boolean getHideFromAppOps() { + return mHideFromAppOps; + } + private static void checkInterval(long millis) { if (millis < 0) { throw new IllegalArgumentException("invalid interval: " + millis); @@ -537,8 +596,11 @@ public final class LocationRequest implements Parcelable { request.setExpireAt(in.readLong()); request.setNumUpdates(in.readInt()); request.setSmallestDisplacement(in.readFloat()); + request.setHideFromAppOps(in.readInt() != 0); String provider = in.readString(); if (provider != null) request.setProvider(provider); + WorkSource workSource = in.readParcelable(null); + if (workSource != null) request.setWorkSource(workSource); return request; } @Override @@ -560,7 +622,9 @@ public final class LocationRequest implements Parcelable { parcel.writeLong(mExpireAt); parcel.writeInt(mNumUpdates); parcel.writeFloat(mSmallestDisplacement); + parcel.writeInt(mHideFromAppOps ? 1 : 0); parcel.writeString(mProvider); + parcel.writeParcelable(mWorkSource, 0); } /** @hide */ diff --git a/location/java/android/location/SettingInjectorService.java b/location/java/android/location/SettingInjectorService.java new file mode 100644 index 0000000..fcd2cde --- /dev/null +++ b/location/java/android/location/SettingInjectorService.java @@ -0,0 +1,220 @@ +/* + * Copyright (C) 2013 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.app.Service; +import android.content.Intent; +import android.os.Bundle; +import android.os.IBinder; +import android.os.Message; +import android.os.Messenger; +import android.os.RemoteException; +import android.util.Log; + +/** + * 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 + * apps. Location settings that apply only to one app should be shown within that app, + * rather than in the system settings. + * <p/> + * To add a preference to the list, a subclass of {@link SettingInjectorService} must be declared in + * the manifest as so: + * + * <pre> + * <service android:name="com.example.android.injector.MyInjectorService" > + * <intent-filter> + * <action android:name="android.location.SettingInjectorService" /> + * </intent-filter> + * + * <meta-data + * android:name="android.location.SettingInjectorService" + * android:resource="@xml/my_injected_location_setting" /> + * </service> + * </pre> + * The resource file specifies the static data for the setting: + * <pre> + * <injected-location-setting xmlns:android="http://schemas.android.com/apk/res/android" + * android:title="@string/injected_setting_title" + * android:icon="@drawable/ic_acme_corp" + * android:settingsActivity="com.example.android.injector.MySettingActivity" + * /> + * </pre> + * Here: + * <ul> + * <li>title: The {@link android.preference.Preference#getTitle()} value. The title should make + * it clear which apps are affected by the setting, typically by including the name of the + * developer. For example, "Acme Corp. ads preferences." </li> + * + * <li>icon: The {@link android.preference.Preference#getIcon()} value. Typically this will be a + * generic icon for the developer rather than the icon for an individual app.</li> + * + * <li>settingsActivity: the activity which is launched to allow the user to modify the setting + * value. The activity must be in the same package as the subclass of + * {@link SettingInjectorService}. The activity should use your own branding to help emphasize + * to the user that it is not part of the system settings.</li> + * </ul> + * + * To ensure a good user experience, your {@link android.app.Application#onCreate()}, + * 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 only {@code settingsActivity} should display the value for + * each account. + */ +public abstract class SettingInjectorService extends Service { + + private static final String TAG = "SettingInjectorService"; + + /** + * Intent action that must be declared in the manifest for the subclass. Used to start the + * service to read the dynamic status for the setting. + */ + public static final String ACTION_SERVICE_INTENT = "android.location.SettingInjectorService"; + + /** + * Name of the meta-data tag used to specify the resource file that includes the settings + * attributes. + */ + public static final String META_DATA_NAME = "android.location.SettingInjectorService"; + + /** + * Name of the XML tag that includes the attributes for the setting. + */ + public static final String ATTRIBUTES_NAME = "injected-location-setting"; + + /** + * Intent action a client should broadcast when the value of one of its injected settings has + * changed, so that the setting can be updated in the UI. + */ + public static final String ACTION_INJECTED_SETTING_CHANGED = + "android.location.InjectedSettingChanged"; + + /** + * Name of the bundle key for the string specifying whether the setting is currently enabled. + * + * @hide + */ + public static final String ENABLED_KEY = "enabled"; + + /** + * Name of the intent key used to specify the messenger + * + * @hide + */ + public static final String MESSENGER_KEY = "messenger"; + + private final String mName; + + /** + * Constructor. + * + * @param name used to identify your subclass in log messages + */ + public SettingInjectorService(String name) { + mName = name; + } + + @Override + public final IBinder onBind(Intent intent) { + return null; + } + + @Override + public final void onStart(Intent intent, int startId) { + super.onStart(intent, startId); + } + + @Override + public final int onStartCommand(Intent intent, int flags, int startId) { + onHandleIntent(intent); + stopSelf(startId); + return START_NOT_STICKY; + } + + private void onHandleIntent(Intent intent) { + + 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, true); + throw e; + } + + sendStatus(intent, enabled); + } + + /** + * Send the enabled values back to the caller via the messenger encoded in the + * intent. + */ + private void sendStatus(Intent intent, boolean enabled) { + Message message = Message.obtain(); + Bundle bundle = new Bundle(); + bundle.putBoolean(ENABLED_KEY, enabled); + message.setData(bundle); + + if (Log.isLoggable(TAG, Log.DEBUG)) { + Log.d(TAG, mName + ": received " + intent + + ", enabled=" + enabled + ", sending message: " + message); + } + + Messenger messenger = intent.getParcelableExtra(MESSENGER_KEY); + try { + messenger.send(message); + } catch (RemoteException e) { + Log.e(TAG, mName + ": sending dynamic status failed", e); + } + } + + /** + * This method is no longer called, because status values are no longer shown for any injected + * setting. + * + * @return ignored + * + * @deprecated not called any more + */ + @Deprecated + protected abstract String onGetSummary(); + + /** + * Returns the {@link android.preference.Preference#isEnabled()} value. Should not perform + * unpredictably-long operations such as network access--see the running-time comments in the + * class-level javadoc. + * <p/> + * Note that to prevent churn in the settings list, there is no support for dynamically choosing + * to hide a setting. Instead you should have this method return false, which will disable the + * setting and its link to your setting activity. One reason why you might choose to do this is + * if {@link android.provider.Settings.Secure#LOCATION_MODE} is {@link + * android.provider.Settings.Secure#LOCATION_MODE_OFF}. + * <p/> + * It is possible that the user may click on the setting before this method returns, so your + * settings activity must handle the case where it is invoked even though the setting is + * disabled. The simplest approach may be to simply call {@link android.app.Activity#finish()} + * when disabled. + * + * @return the {@link android.preference.Preference#isEnabled()} value + */ + protected abstract boolean onGetEnabled(); +} diff --git a/location/java/android/location/package.html b/location/java/android/location/package.html index 81fcea4..2355e72 100644 --- a/location/java/android/location/package.html +++ b/location/java/android/location/package.html @@ -1,22 +1,20 @@ <html> <body> -<p>Contains the framework API classes that define Android location-based and related services.</p> -<p class="note"> - <strong>Note:</strong> The Google Location Services API, part of Google Play - Services, provides a more powerful, high-level framework that automates tasks such as - location provider choice and power management. Location Services also provides new - features such as activity detection that aren't available in the framework API. Developers who - are using the framework API, as well as developers who are just now adding location-awareness - to their apps, should strongly consider using the Location Services API. -<br/> - To learn more about the Location Services API, see - <a href="{@docRoot}google/play-services/location.html">Location APIs</a>. +<p>Contains the framework API classes that define Android location-based and + related services.</p> +<p class="warning"> +<strong>This API is not the recommended method for accessing Android location.</strong><br> +The +<a href="{@docRoot}reference/com/google/android/gms/location/package-summary.html">Google Location Services API</a>, +part of Google Play services, is the preferred way to add location-awareness to +your app. It offers a simpler API, higher accuracy, low-power geofencing, and +more. If you are currently using the android.location API, you are strongly +encouraged to switch to the Google Location Services API as soon as +possible. +<br><br> +To learn more about the Google Location Services API, see the +<a href="{@docRoot}google/play-services/location.html">Location API overview</a>. </p> - -<p>For more information about the framework API, see the -<a href="{@docRoot}guide/topics/location/index.html">Location and Maps</a> guide.</p> -{@more} - </body> </html> 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/FusedLocationHardware.java b/location/lib/java/com/android/location/provider/FusedLocationHardware.java new file mode 100644 index 0000000..bc5a8a1 --- /dev/null +++ b/location/lib/java/com/android/location/provider/FusedLocationHardware.java @@ -0,0 +1,280 @@ +/* + * Copyright (C) 2013 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.hardware.location.IFusedLocationHardware; +import android.hardware.location.IFusedLocationHardwareSink; + +import android.location.Location; + +import android.os.Handler; +import android.os.Looper; +import android.os.Message; +import android.os.RemoteException; +import android.util.Log; + +import java.util.HashMap; +import java.util.Map; + +/** + * Class that exposes IFusedLocationHardware functionality to unbundled services. + */ +public final class FusedLocationHardware { + private final String TAG = "FusedLocationHardware"; + + private IFusedLocationHardware mLocationHardware; + + // the list uses a copy-on-write pattern to update its contents + HashMap<FusedLocationHardwareSink, DispatcherHandler> mSinkList = + new HashMap<FusedLocationHardwareSink, DispatcherHandler>(); + + private IFusedLocationHardwareSink mInternalSink = new IFusedLocationHardwareSink.Stub() { + @Override + public void onLocationAvailable(Location[] locations) { + dispatchLocations(locations); + } + + @Override + public void onDiagnosticDataAvailable(String data) { + dispatchDiagnosticData(data); + } + }; + + /** + * @hide + */ + public FusedLocationHardware(IFusedLocationHardware locationHardware) { + mLocationHardware = locationHardware; + } + + /* + * Methods to provide a Facade for IFusedLocationHardware + */ + public void registerSink(FusedLocationHardwareSink sink, Looper looper) { + if(sink == null || looper == null) { + throw new IllegalArgumentException("Parameter sink and looper cannot be null."); + } + + boolean registerSink; + synchronized (mSinkList) { + // register only on first insertion + registerSink = mSinkList.size() == 0; + // guarantee uniqueness + if(mSinkList.containsKey(sink)) { + return; + } + + HashMap<FusedLocationHardwareSink, DispatcherHandler> newSinkList = + new HashMap<FusedLocationHardwareSink, DispatcherHandler>(mSinkList); + newSinkList.put(sink, new DispatcherHandler(looper)); + mSinkList = newSinkList; + } + + if(registerSink) { + try { + mLocationHardware.registerSink(mInternalSink); + } catch(RemoteException e) { + Log.e(TAG, "RemoteException at registerSink"); + } + } + } + + public void unregisterSink(FusedLocationHardwareSink sink) { + if(sink == null) { + throw new IllegalArgumentException("Parameter sink cannot be null."); + } + + boolean unregisterSink; + synchronized(mSinkList) { + if(!mSinkList.containsKey(sink)) { + //done + return; + } + + HashMap<FusedLocationHardwareSink, DispatcherHandler> newSinkList = + new HashMap<FusedLocationHardwareSink, DispatcherHandler>(mSinkList); + newSinkList.remove(sink); + //unregister after the last sink + unregisterSink = newSinkList.size() == 0; + + mSinkList = newSinkList; + } + + if(unregisterSink) { + try { + mLocationHardware.unregisterSink(mInternalSink); + } catch(RemoteException e) { + Log.e(TAG, "RemoteException at unregisterSink"); + } + } + } + + public int getSupportedBatchSize() { + try { + return mLocationHardware.getSupportedBatchSize(); + } catch(RemoteException e) { + Log.e(TAG, "RemoteException at getSupportedBatchSize"); + return 0; + } + } + + public void startBatching(int id, GmsFusedBatchOptions batchOptions) { + try { + mLocationHardware.startBatching(id, batchOptions.getParcelableOptions()); + } catch(RemoteException e) { + Log.e(TAG, "RemoteException at startBatching"); + } + } + + public void stopBatching(int id) { + try { + mLocationHardware.stopBatching(id); + } catch(RemoteException e) { + Log.e(TAG, "RemoteException at stopBatching"); + } + } + + public void updateBatchingOptions(int id, GmsFusedBatchOptions batchOptions) { + try { + mLocationHardware.updateBatchingOptions(id, batchOptions.getParcelableOptions()); + } catch(RemoteException e) { + Log.e(TAG, "RemoteException at updateBatchingOptions"); + } + } + + public void requestBatchOfLocations(int batchSizeRequest) { + try { + mLocationHardware.requestBatchOfLocations(batchSizeRequest); + } catch(RemoteException e) { + Log.e(TAG, "RemoteException at requestBatchOfLocations"); + } + } + + public boolean supportsDiagnosticDataInjection() { + try { + return mLocationHardware.supportsDiagnosticDataInjection(); + } catch(RemoteException e) { + Log.e(TAG, "RemoteException at supportsDiagnisticDataInjection"); + return false; + } + } + + public void injectDiagnosticData(String data) { + try { + mLocationHardware.injectDiagnosticData(data); + } catch(RemoteException e) { + Log.e(TAG, "RemoteException at injectDiagnosticData"); + } + } + + public boolean supportsDeviceContextInjection() { + try { + return mLocationHardware.supportsDeviceContextInjection(); + } catch(RemoteException e) { + Log.e(TAG, "RemoteException at supportsDeviceContextInjection"); + return false; + } + } + + public void injectDeviceContext(int deviceEnabledContext) { + try { + mLocationHardware.injectDeviceContext(deviceEnabledContext); + } catch(RemoteException e) { + Log.e(TAG, "RemoteException at injectDeviceContext"); + } + } + + /* + * Helper methods and classes + */ + private class DispatcherHandler extends Handler { + public static final int DISPATCH_LOCATION = 1; + public static final int DISPATCH_DIAGNOSTIC_DATA = 2; + + public DispatcherHandler(Looper looper) { + super(looper, null /*callback*/ , true /*async*/); + } + + @Override + public void handleMessage(Message message) { + MessageCommand command = (MessageCommand) message.obj; + switch(message.what) { + case DISPATCH_LOCATION: + command.dispatchLocation(); + break; + case DISPATCH_DIAGNOSTIC_DATA: + command.dispatchDiagnosticData(); + default: + Log.e(TAG, "Invalid dispatch message"); + break; + } + } + } + + private class MessageCommand { + private final FusedLocationHardwareSink mSink; + private final Location[] mLocations; + private final String mData; + + public MessageCommand( + FusedLocationHardwareSink sink, + Location[] locations, + String data) { + mSink = sink; + mLocations = locations; + mData = data; + } + + public void dispatchLocation() { + mSink.onLocationAvailable(mLocations); + } + + public void dispatchDiagnosticData() { + mSink.onDiagnosticDataAvailable(mData); + } + } + + private void dispatchLocations(Location[] locations) { + HashMap<FusedLocationHardwareSink, DispatcherHandler> sinks; + synchronized (mSinkList) { + sinks = mSinkList; + } + + for(Map.Entry<FusedLocationHardwareSink, DispatcherHandler> entry : sinks.entrySet()) { + Message message = Message.obtain( + entry.getValue(), + DispatcherHandler.DISPATCH_LOCATION, + new MessageCommand(entry.getKey(), locations, null /*data*/)); + message.sendToTarget(); + } + } + + private void dispatchDiagnosticData(String data) { + HashMap<FusedLocationHardwareSink, DispatcherHandler> sinks; + synchronized(mSinkList) { + sinks = mSinkList; + } + + for(Map.Entry<FusedLocationHardwareSink, DispatcherHandler> entry : sinks.entrySet()) { + Message message = Message.obtain( + entry.getValue(), + DispatcherHandler.DISPATCH_DIAGNOSTIC_DATA, + new MessageCommand(entry.getKey(), null /*locations*/, data)); + message.sendToTarget(); + } + } +} diff --git a/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java b/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java new file mode 100644 index 0000000..2c39fa8 --- /dev/null +++ b/location/lib/java/com/android/location/provider/FusedLocationHardwareSink.java @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2013 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.location.Location; + +/** + * Base class for sinks to interact with FusedLocationHardware. + */ +public abstract class FusedLocationHardwareSink { + /* + * Methods to provide a facade for IFusedLocationHardware + */ + public abstract void onLocationAvailable(Location[] locations); + public abstract void onDiagnosticDataAvailable(String data); +}
\ No newline at end of file diff --git a/location/lib/java/com/android/location/provider/FusedProvider.java b/location/lib/java/com/android/location/provider/FusedProvider.java new file mode 100644 index 0000000..c966ade --- /dev/null +++ b/location/lib/java/com/android/location/provider/FusedProvider.java @@ -0,0 +1,57 @@ +/* + * Copyright (C) 2013 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.hardware.location.IFusedLocationHardware; +import android.location.IFusedProvider; +import android.os.IBinder; + +/** + * Base class for Fused providers implemented as unbundled services. + * + * <p>Fused providers can be implemented as services and return the result of + * {@link com.android.location.provider.FusedProvider#getBinder()} in its getBinder() method. + * + * <p>IMPORTANT: This class is effectively a public API for unbundled applications, and must remain + * API stable. See README.txt in the root of this package for more information. + */ +public abstract class FusedProvider { + private IFusedProvider.Stub mProvider = new IFusedProvider.Stub() { + @Override + public void onFusedLocationHardwareChange(IFusedLocationHardware instance) { + setFusedLocationHardware(new FusedLocationHardware(instance)); + } + }; + + /** + * Gets the Binder associated with the provider. + * This is intended to be used for the onBind() method of a service that implements a fused + * service. + * + * @return The IBinder instance associated with the provider. + */ + public IBinder getBinder() { + return mProvider; + } + + /** + * Sets the FusedLocationHardware instance in the provider.. + * @param value The instance to set. This can be null in cases where the service connection + * is disconnected. + */ + public abstract void setFusedLocationHardware(FusedLocationHardware value); +} 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/GmsFusedBatchOptions.java b/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java new file mode 100644 index 0000000..fd3f402 --- /dev/null +++ b/location/lib/java/com/android/location/provider/GmsFusedBatchOptions.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2013 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.location.FusedBatchOptions; + +/** + * Class that exposes FusedBatchOptions to the GmsCore . + */ +public class GmsFusedBatchOptions { + private FusedBatchOptions mOptions = new FusedBatchOptions(); + + /* + * Methods that provide a facade for properties in FusedBatchOptions. + */ + public void setMaxPowerAllocationInMW(double value) { + mOptions.setMaxPowerAllocationInMW(value); + } + + public double getMaxPowerAllocationInMW() { + return mOptions.getMaxPowerAllocationInMW(); + } + + public void setPeriodInNS(long value) { + mOptions.setPeriodInNS(value); + } + + public long getPeriodInNS() { + return mOptions.getPeriodInNS(); + } + + public void setSourceToUse(int source) { + mOptions.setSourceToUse(source); + } + + public void resetSourceToUse(int source) { + mOptions.resetSourceToUse(source); + } + + public boolean isSourceToUseSet(int source) { + return mOptions.isSourceToUseSet(source); + } + + public int getSourcesToUse() { + return mOptions.getSourcesToUse(); + } + + public void setFlag(int flag) { + mOptions.setFlag(flag); + } + + public void resetFlag(int flag) { + mOptions.resetFlag(flag); + } + + public boolean isFlagSet(int flag) { + return mOptions.isFlagSet(flag); + } + + public int getFlags() { + return mOptions.getFlags(); + } + + /** + * Definition of enum flag sets needed by this class. + * Such values need to be kept in sync with the ones in fused_location.h + */ + + public static final class SourceTechnologies { + public static int GNSS = 1<<0; + public static int WIFI = 1<<1; + public static int SENSORS = 1<<2; + public static int CELL = 1<<3; + public static int BLUETOOTH = 1<<4; + } + + public static final class BatchFlags { + public static int WAKEUP_ON_FIFO_FULL = 1<<0; + public static int CALLBACK_ON_LOCATION_FIX = 1<<1; + } + + /* + * Method definitions for internal use. + */ + + /* + * @hide + */ + public FusedBatchOptions getParcelableOptions() { + return mOptions; + } +} diff --git a/location/lib/java/com/android/location/provider/LocationProviderBase.java b/location/lib/java/com/android/location/provider/LocationProviderBase.java index 8a5a739..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; @@ -35,6 +34,7 @@ import android.util.Log; import com.android.internal.location.ILocationProvider; import com.android.internal.location.ProviderProperties; import com.android.internal.location.ProviderRequest; +import com.android.internal.util.FastPrintWriter; /** * Base class for location providers implemented as unbundled services. @@ -106,7 +106,7 @@ public abstract class LocationProviderBase { } @Override public void dump(FileDescriptor fd, String[] args) { - PrintWriter pw = new PrintWriter(new FileOutputStream(fd)); + PrintWriter pw = new FastPrintWriter(new FileOutputStream(fd)); onDump(fd, pw, args); pw.flush(); } |