Merge "[RTT] Add LCI and LCR structures"
diff --git a/wifi/java/android/net/wifi/rtt/LocationCivic.java b/wifi/java/android/net/wifi/rtt/LocationCivic.java
new file mode 100644
index 0000000..bdf3189
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/LocationCivic.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2018 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.net.wifi.rtt;
+
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.util.Arrays;
+import java.util.Objects;
+
+/**
+ * Location Civic Report (LCR).
+ * <p>
+ * The information matches the IEEE 802.11-2016 LCR report.
+ * <p>
+ * Note: depending on the mechanism by which this information is returned (i.e. the API which
+ * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case
+ * the information is NOT validated - use with caution. Consider validating it with other sources
+ * of information before using it.
+ *
+ * @hide
+ */
+public final class LocationCivic implements Parcelable {
+    private final byte[] mData;
+
+    /**
+     * Parse the raw LCR information element (byte array) and extract the LocationCivic structure.
+     *
+     * Note: any parsing errors or invalid/unexpected errors will result in a null being returned.
+     *
+     * @hide
+     */
+    @Nullable
+    public static LocationCivic parseInformationElement(byte id, byte[] data) {
+        // TODO
+        return null;
+    }
+
+    /** @hide */
+    public LocationCivic(byte[] data) {
+        mData = data;
+    }
+
+    /**
+     * Return the Location Civic data reported by the peer.
+     *
+     * @return An arbitrary location information.
+     */
+    public byte[] getData() {
+        return mData;
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeByteArray(mData);
+    }
+
+    /** @hide */
+    public static final Parcelable.Creator<LocationCivic> CREATOR =
+            new Parcelable.Creator<LocationCivic>() {
+                @Override
+                public LocationCivic[] newArray(int size) {
+                    return new LocationCivic[size];
+                }
+
+                @Override
+                public LocationCivic createFromParcel(Parcel in) {
+                    byte[] data = in.createByteArray();
+
+                    return new LocationCivic(data);
+                }
+            };
+
+    /** @hide */
+    @Override
+    public String toString() {
+        return new StringBuilder("LCR: data=").append(Arrays.toString(mData)).toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof LocationCivic)) {
+            return false;
+        }
+
+        LocationCivic lhs = (LocationCivic) o;
+
+        return Arrays.equals(mData, lhs.mData);
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mData);
+    }
+}
diff --git a/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java b/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java
new file mode 100644
index 0000000..32ff274
--- /dev/null
+++ b/wifi/java/android/net/wifi/rtt/LocationConfigurationInformation.java
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2018 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.net.wifi.rtt;
+
+import android.annotation.IntDef;
+import android.annotation.Nullable;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.Objects;
+
+/**
+ * The Device Location Configuration Information (LCI) specifies the location information of a peer
+ * device (e.g. an Access Point).
+ * <p>
+ * The information matches the IEEE 802.11-2016 LCI report (Location configuration information
+ * report).
+ * <p>
+ * Note: depending on the mechanism by which this information is returned (i.e. the API which
+ * returns an instance of this class) it is possibly Self Reported (by the peer). In such a case
+ * the information is NOT validated - use with caution. Consider validating it with other sources
+ * of information before using it.
+ *
+ * @hide PLANNED_API
+ */
+public final class LocationConfigurationInformation implements Parcelable {
+    /** @hide */
+    @IntDef({
+            ALTITUDE_UNKNOWN, ALTITUDE_IN_METERS, ALTITUDE_IN_FLOORS })
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface AltitudeTypes {
+    }
+
+    /**
+     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location
+     * does not specify an altitude or altitude uncertainty. The corresponding methods,
+     * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} are not valid and will throw
+     * an exception.
+     */
+    public static final int ALTITUDE_UNKNOWN = 0;
+
+    /**
+     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the location
+     * specifies the altitude and altitude uncertainty in meters. The corresponding methods,
+     * {@link #getAltitude()} and {@link #getAltitudeUncertainty()} return a valid value in meters.
+     */
+    public static final int ALTITUDE_IN_METERS = 1;
+
+    /**
+     * Define an Altitude Type returned by {@link #getAltitudeType()}. Indicates that the
+     * location specifies the altitude in floors, and does not specify an altitude uncertainty.
+     * The {@link #getAltitude()} method returns valid value in floors, and the
+     * {@link #getAltitudeUncertainty()} method is not valid and will throw an exception.
+     */
+    public static final int ALTITUDE_IN_FLOORS = 2;
+
+    private final double mLatitude;
+    private final double mLatitudeUncertainty;
+    private final double mLongitude;
+    private final double mLongitudeUncertainty;
+    private final int mAltitudeType;
+    private final double mAltitude;
+    private final double mAltitudeUncertainty;
+
+    /**
+     * Parse the raw LCI information element (byte array) and extract the
+     * LocationConfigurationInformation structure.
+     *
+     * Note: any parsing errors or invalid/unexpected errors will result in a null being returned.
+     *
+     * @hide
+     */
+    @Nullable
+    public static LocationConfigurationInformation parseInformationElement(byte id, byte[] data) {
+        // TODO
+        return null;
+    }
+
+    /** @hide */
+    public LocationConfigurationInformation(double latitude, double latitudeUncertainty,
+            double longitude, double longitudeUncertainty, @AltitudeTypes int altitudeType,
+            double altitude, double altitudeUncertainty) {
+        mLatitude = latitude;
+        mLatitudeUncertainty = latitudeUncertainty;
+        mLongitude = longitude;
+        mLongitudeUncertainty = longitudeUncertainty;
+        mAltitudeType = altitudeType;
+        mAltitude = altitude;
+        mAltitudeUncertainty = altitudeUncertainty;
+    }
+
+    /**
+     * Get latitude in degrees. Values are per WGS 84 reference system. Valid values are between
+     * -90 and 90.
+     *
+     * @return Latitude in degrees.
+     */
+    public double getLatitude() {
+        return mLatitude;
+    }
+
+    /**
+     * Get the uncertainty of the latitude {@link #getLatitude()} in degrees. A value of 0 indicates
+     * an unknown uncertainty.
+     *
+     * @return Uncertainty of the latitude in degrees.
+     */
+    public double getLatitudeUncertainty() {
+        return mLatitudeUncertainty;
+    }
+
+    /**
+     * Get longitude in degrees. Values are per WGS 84 reference system. Valid values are between
+     * -180 and 180.
+     *
+     * @return Longitude in degrees.
+     */
+    public double getLongitude() {
+        return mLongitude;
+    }
+
+    /**
+     * Get the uncertainty of the longitude {@link #getLongitude()} ()} in degrees.  A value of 0
+     * indicates an unknown uncertainty.
+     *
+     * @return Uncertainty of the longitude in degrees.
+     */
+    public double getLongitudeUncertainty() {
+        return mLongitudeUncertainty;
+    }
+
+    /**
+     * Specifies the type of the altitude measurement returned by {@link #getAltitude()} and
+     * {@link #getAltitudeUncertainty()}. The possible values are:
+     * <li>{@link #ALTITUDE_UNKNOWN}: The altitude and altitude uncertainty are not provided.
+     * <li>{@link #ALTITUDE_IN_METERS}: The altitude and altitude uncertainty are provided in
+     * meters. Values are per WGS 84 reference system.
+     * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors, the altitude uncertainty
+     * is not provided.
+     *
+     * @return The type of the altitude and altitude uncertainty.
+     */
+    public @AltitudeTypes int getAltitudeType() {
+        return mAltitudeType;
+    }
+
+    /**
+     * The altitude is interpreted according to the {@link #getAltitudeType()}. The possible values
+     * are:
+     * <li>{@link #ALTITUDE_UNKNOWN}: The altitude is not provided - this method will throw an
+     * exception.
+     * <li>{@link #ALTITUDE_IN_METERS}: The altitude is provided in meters. Values are per WGS 84
+     * reference system.
+     * <li>{@link #ALTITUDE_IN_FLOORS}: The altitude is provided in floors.
+     *
+     * @return Altitude value whose meaning is specified by {@link #getAltitudeType()}.
+     */
+    public double getAltitude() {
+        if (mAltitudeType == ALTITUDE_UNKNOWN) {
+            throw new IllegalStateException(
+                    "getAltitude(): invoked on an invalid type: getAltitudeType()==UNKNOWN");
+        }
+        return mAltitude;
+    }
+
+    /**
+     * Only valid if the the {@link #getAltitudeType()} is equal to {@link #ALTITUDE_IN_METERS} -
+     * otherwise this method will throw an exception.
+     * <p>
+     * Get the uncertainty of the altitude {@link #getAltitude()} in meters.  A value of 0
+     * indicates an unknown uncertainty.
+     *
+     * @return Uncertainty of the altitude in meters.
+     */
+    public double getAltitudeUncertainty() {
+        if (mAltitudeType != ALTITUDE_IN_METERS) {
+            throw new IllegalStateException(
+                    "getAltitude(): invoked on an invalid type: getAltitudeType()!=IN_METERS");
+        }
+        return mAltitudeUncertainty;
+    }
+
+    /** @hide */
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    /** @hide */
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeDouble(mLatitude);
+        dest.writeDouble(mLatitudeUncertainty);
+        dest.writeDouble(mLongitude);
+        dest.writeDouble(mLongitudeUncertainty);
+        dest.writeInt(mAltitudeType);
+        dest.writeDouble(mAltitude);
+        dest.writeDouble(mAltitudeUncertainty);
+    }
+
+    /** @hide */
+    public static final Creator<LocationConfigurationInformation> CREATOR =
+            new Creator<LocationConfigurationInformation>() {
+        @Override
+        public LocationConfigurationInformation[] newArray(int size) {
+            return new LocationConfigurationInformation[size];
+        }
+
+        @Override
+        public LocationConfigurationInformation createFromParcel(Parcel in) {
+            double latitude = in.readDouble();
+            double latitudeUnc = in.readDouble();
+            double longitude = in.readDouble();
+            double longitudeUnc = in.readDouble();
+            int altitudeType = in.readInt();
+            double altitude = in.readDouble();
+            double altitudeUnc = in.readDouble();
+
+            return new LocationConfigurationInformation(latitude, latitudeUnc, longitude,
+                    longitudeUnc, altitudeType, altitude, altitudeUnc);
+        }
+    };
+
+    /** @hide */
+    @Override
+    public String toString() {
+        return new StringBuilder("LCI: latitude=").append(mLatitude).append(
+                ", latitudeUncertainty=").append(mLatitudeUncertainty).append(
+                ", longitude=").append(mLongitude).append(", longitudeUncertainty=").append(
+                mLongitudeUncertainty).append(", altitudeType=").append(mAltitudeType).append(
+                ", altitude=").append(mAltitude).append(", altitudeUncertainty=").append(
+                mAltitudeUncertainty).toString();
+    }
+
+    @Override
+    public boolean equals(Object o) {
+        if (this == o) {
+            return true;
+        }
+
+        if (!(o instanceof LocationConfigurationInformation)) {
+            return false;
+        }
+
+        LocationConfigurationInformation lhs = (LocationConfigurationInformation) o;
+
+        return mLatitude == lhs.mLatitude && mLatitudeUncertainty == lhs.mLatitudeUncertainty
+                && mLongitude == lhs.mLongitude
+                && mLongitudeUncertainty == lhs.mLongitudeUncertainty
+                && mAltitudeType == lhs.mAltitudeType && mAltitude == lhs.mAltitude
+                && mAltitudeUncertainty == lhs.mAltitudeUncertainty;
+    }
+
+    @Override
+    public int hashCode() {
+        return Objects.hash(mLatitude, mLatitudeUncertainty, mLongitude, mLongitudeUncertainty,
+                mAltitudeType, mAltitude, mAltitudeUncertainty);
+    }
+}
diff --git a/wifi/java/android/net/wifi/rtt/RangingResult.java b/wifi/java/android/net/wifi/rtt/RangingResult.java
index d5ca8f7f..ec4a759 100644
--- a/wifi/java/android/net/wifi/rtt/RangingResult.java
+++ b/wifi/java/android/net/wifi/rtt/RangingResult.java
@@ -65,29 +65,37 @@
     private final int mDistanceMm;
     private final int mDistanceStdDevMm;
     private final int mRssi;
+    private final LocationConfigurationInformation mLci;
+    private final LocationCivic mLcr;
     private final long mTimestamp;
 
     /** @hide */
     public RangingResult(@RangeResultStatus int status, @NonNull MacAddress mac, int distanceMm,
-            int distanceStdDevMm, int rssi, long timestamp) {
+            int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr,
+            long timestamp) {
         mStatus = status;
         mMac = mac;
         mPeerHandle = null;
         mDistanceMm = distanceMm;
         mDistanceStdDevMm = distanceStdDevMm;
         mRssi = rssi;
+        mLci = lci;
+        mLcr = lcr;
         mTimestamp = timestamp;
     }
 
     /** @hide */
     public RangingResult(@RangeResultStatus int status, PeerHandle peerHandle, int distanceMm,
-            int distanceStdDevMm, int rssi, long timestamp) {
+            int distanceStdDevMm, int rssi, LocationConfigurationInformation lci, LocationCivic lcr,
+            long timestamp) {
         mStatus = status;
         mMac = null;
         mPeerHandle = peerHandle;
         mDistanceMm = distanceMm;
         mDistanceStdDevMm = distanceStdDevMm;
         mRssi = rssi;
+        mLci = lci;
+        mLcr = lcr;
         mTimestamp = timestamp;
     }
 
@@ -169,6 +177,42 @@
     }
 
     /**
+     * @return The Location Configuration Information (LCI) as self-reported by the peer.
+     * <p>
+     * Note: the information is NOT validated - use with caution. Consider validating it with
+     * other sources of information before using it.
+     *
+     * @hide PLANNED_API
+     */
+    @Nullable
+    public LocationConfigurationInformation getReportedLocationConfigurationInformation() {
+        if (mStatus != STATUS_SUCCESS) {
+            throw new IllegalStateException(
+                    "getReportedLocationConfigurationInformation(): invoked on an invalid result: "
+                            + "getStatus()=" + mStatus);
+        }
+        return mLci;
+    }
+
+    /**
+     * @return The Location Civic report (LCR) as self-reported by the peer.
+     * <p>
+     * Note: the information is NOT validated - use with caution. Consider validating it with
+     * other sources of information before using it.
+     *
+     * @hide PLANNED_API
+     */
+    @Nullable
+    public LocationCivic getReportedLocationCivic() {
+        if (mStatus != STATUS_SUCCESS) {
+            throw new IllegalStateException(
+                    "getReportedLocationCivic(): invoked on an invalid result: getStatus()="
+                            + mStatus);
+        }
+        return mLcr;
+    }
+
+    /**
      * @return The timestamp, in us since boot, at which the ranging operation was performed.
      * <p>
      * Only valid if {@link #getStatus()} returns {@link #STATUS_SUCCESS}, otherwise will throw an
@@ -205,6 +249,18 @@
         dest.writeInt(mDistanceMm);
         dest.writeInt(mDistanceStdDevMm);
         dest.writeInt(mRssi);
+        if (mLci == null) {
+            dest.writeBoolean(false);
+        } else {
+            dest.writeBoolean(true);
+            mLci.writeToParcel(dest, flags);
+        }
+        if (mLcr == null) {
+            dest.writeBoolean(false);
+        } else {
+            dest.writeBoolean(true);
+            mLcr.writeToParcel(dest, flags);
+        }
         dest.writeLong(mTimestamp);
     }
 
@@ -230,13 +286,23 @@
             int distanceMm = in.readInt();
             int distanceStdDevMm = in.readInt();
             int rssi = in.readInt();
+            boolean lciPresent = in.readBoolean();
+            LocationConfigurationInformation lci = null;
+            if (lciPresent) {
+                lci = LocationConfigurationInformation.CREATOR.createFromParcel(in);
+            }
+            boolean lcrPresent = in.readBoolean();
+            LocationCivic lcr = null;
+            if (lcrPresent) {
+                lcr = LocationCivic.CREATOR.createFromParcel(in);
+            }
             long timestamp = in.readLong();
             if (peerHandlePresent) {
                 return new RangingResult(status, peerHandle, distanceMm, distanceStdDevMm, rssi,
-                        timestamp);
+                        lci, lcr, timestamp);
             } else {
                 return new RangingResult(status, mac, distanceMm, distanceStdDevMm, rssi,
-                        timestamp);
+                        lci, lcr, timestamp);
             }
         }
     };
@@ -248,8 +314,8 @@
                 mMac).append(", peerHandle=").append(
                 mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(", distanceMm=").append(
                 mDistanceMm).append(", distanceStdDevMm=").append(mDistanceStdDevMm).append(
-                ", rssi=").append(mRssi).append(", timestamp=").append(mTimestamp).append(
-                "]").toString();
+                ", rssi=").append(mRssi).append(", lci=").append(mLci).append(", lcr=").append(
+                mLcr).append(", timestamp=").append(mTimestamp).append("]").toString();
     }
 
     @Override
@@ -267,12 +333,13 @@
         return mStatus == lhs.mStatus && Objects.equals(mMac, lhs.mMac) && Objects.equals(
                 mPeerHandle, lhs.mPeerHandle) && mDistanceMm == lhs.mDistanceMm
                 && mDistanceStdDevMm == lhs.mDistanceStdDevMm && mRssi == lhs.mRssi
+                && Objects.equals(mLci, lhs.mLci) && Objects.equals(mLcr, lhs.mLcr)
                 && mTimestamp == lhs.mTimestamp;
     }
 
     @Override
     public int hashCode() {
         return Objects.hash(mStatus, mMac, mPeerHandle, mDistanceMm, mDistanceStdDevMm, mRssi,
-                mTimestamp);
+                mLci, mLcr, mTimestamp);
     }
 }
diff --git a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
index 72e95b9..41c7f86 100644
--- a/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
+++ b/wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
@@ -17,6 +17,7 @@
 package android.net.wifi.rtt;
 
 import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
 import static org.mockito.ArgumentMatchers.any;
 import static org.mockito.ArgumentMatchers.eq;
 import static org.mockito.Mockito.mock;
@@ -32,7 +33,6 @@
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.test.TestLooper;
-import android.test.suitebuilder.annotation.SmallTest;
 
 import org.junit.Before;
 import org.junit.Test;
@@ -46,7 +46,6 @@
 /**
  * Unit test harness for WifiRttManager class.
  */
-@SmallTest
 public class WifiRttManagerTest {
     private WifiRttManager mDut;
     private TestLooper mMockLooper;
@@ -80,7 +79,7 @@
         List<RangingResult> results = new ArrayList<>();
         results.add(
                 new RangingResult(RangingResult.STATUS_SUCCESS, MacAddress.BROADCAST_ADDRESS, 15, 5,
-                        10, 666));
+                        10, null, null, 666));
         RangingResultCallback callbackMock = mock(RangingResultCallback.class);
         ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
 
@@ -236,10 +235,23 @@
         int distanceStdDevCm = 10;
         int rssi = 5;
         long timestamp = System.currentTimeMillis();
+        double latitude = 5.5;
+        double latitudeUncertainty = 6.5;
+        double longitude = 7.5;
+        double longitudeUncertainty = 8.5;
+        int altitudeType = LocationConfigurationInformation.ALTITUDE_IN_METERS;
+        double altitude = 9.5;
+        double altitudeUncertainty = 55.5;
+        byte[] lcrData = { 0x1, 0x2, 0x3, 0xA, 0xB, 0xC };
+
+        LocationConfigurationInformation lci = new LocationConfigurationInformation(latitude,
+                latitudeUncertainty, longitude, longitudeUncertainty, altitudeType, altitude,
+                altitudeUncertainty);
+        LocationCivic lcr = new LocationCivic(lcrData);
 
         // RangingResults constructed with a MAC address
         RangingResult result = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
-                timestamp);
+                lci, lcr, timestamp);
 
         Parcel parcelW = Parcel.obtain();
         result.writeToParcel(parcelW, 0);
@@ -255,7 +267,7 @@
 
         // RangingResults constructed with a PeerHandle
         result = new RangingResult(status, peerHandle, distanceCm, distanceStdDevCm, rssi,
-                timestamp);
+                null, null, timestamp);
 
         parcelW = Parcel.obtain();
         result.writeToParcel(parcelW, 0);
@@ -269,4 +281,83 @@
 
         assertEquals(result, rereadResult);
     }
+
+    /**
+     * Validate that LocationConfigurationInformation parcel works (produces same object on
+     * write/read).
+     */
+    @Test
+    public void testLciParcel() {
+        double latitude = 1.5;
+        double latitudeUncertainty = 2.5;
+        double longitude = 3.5;
+        double longitudeUncertainty = 4.5;
+        int altitudeType = LocationConfigurationInformation.ALTITUDE_IN_FLOORS;
+        double altitude = 5.5;
+        double altitudeUncertainty = 6.5;
+
+        LocationConfigurationInformation lci = new LocationConfigurationInformation(latitude,
+                latitudeUncertainty, longitude, longitudeUncertainty, altitudeType, altitude,
+                altitudeUncertainty);
+
+        Parcel parcelW = Parcel.obtain();
+        lci.writeToParcel(parcelW, 0);
+        byte[] bytes = parcelW.marshall();
+        parcelW.recycle();
+
+        Parcel parcelR = Parcel.obtain();
+        parcelR.unmarshall(bytes, 0, bytes.length);
+        parcelR.setDataPosition(0);
+        LocationConfigurationInformation rereadLci =
+                LocationConfigurationInformation.CREATOR.createFromParcel(parcelR);
+
+        assertEquals(lci, rereadLci);
+    }
+
+    /**
+     * Validate that the LCI throws an exception when accessing invalid fields an certain altitude
+     * types.
+     */
+    @Test
+    public void testLciInvalidAltitudeFieldAccess() {
+        boolean exceptionThrown;
+        LocationConfigurationInformation lci = new LocationConfigurationInformation(0, 0, 0, 0,
+                LocationConfigurationInformation.ALTITUDE_UNKNOWN, 0, 0);
+
+        // UNKNOWN - invalid altitude & altitude uncertainty
+        exceptionThrown = false;
+        try {
+            lci.getAltitude();
+        } catch (IllegalStateException e) {
+            exceptionThrown = true;
+        }
+        assertTrue("UNKNOWN / getAltitude()", exceptionThrown);
+
+        exceptionThrown = false;
+        try {
+            lci.getAltitudeUncertainty();
+        } catch (IllegalStateException e) {
+            exceptionThrown = true;
+        }
+        assertTrue("UNKNOWN / getAltitudeUncertainty()", exceptionThrown);
+
+        lci = new LocationConfigurationInformation(0, 0, 0, 0,
+                LocationConfigurationInformation.ALTITUDE_IN_FLOORS, 0, 0);
+
+        // FLOORS - invalid altitude uncertainty
+        exceptionThrown = false;
+        try {
+            lci.getAltitudeUncertainty();
+        } catch (IllegalStateException e) {
+            exceptionThrown = true;
+        }
+        assertTrue("FLOORS / getAltitudeUncertainty()", exceptionThrown);
+
+        // and good accesses just in case
+        lci.getAltitude();
+        lci = new LocationConfigurationInformation(0, 0, 0, 0,
+                LocationConfigurationInformation.ALTITUDE_IN_METERS, 0, 0);
+        lci.getAltitude();
+        lci.getAltitudeUncertainty();
+    }
 }