1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package android.net.wifi.rtt;
18 
19 import static java.lang.annotation.RetentionPolicy.SOURCE;
20 
21 import android.annotation.IntDef;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.SuppressLint;
25 import android.annotation.SystemApi;
26 import android.location.Address;
27 import android.location.Location;
28 import android.net.MacAddress;
29 import android.net.Uri;
30 import android.net.wifi.rtt.CivicLocationKeys.CivicLocationKeysType;
31 import android.os.Parcel;
32 import android.os.Parcelable;
33 import android.text.TextUtils;
34 import android.util.SparseArray;
35 import android.webkit.MimeTypeMap;
36 
37 import java.lang.annotation.Retention;
38 import java.nio.charset.StandardCharsets;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.Collections;
42 import java.util.List;
43 import java.util.Objects;
44 
45 /**
46  * ResponderLocation is both a Location Configuration Information (LCI) decoder and a Location Civic
47  * Report (LCR) decoder for information received from a Wi-Fi Access Point (AP) during Wi-Fi RTT
48  * ranging process.
49  *
50  * <p>This is based on the IEEE P802.11-REVmc/D8.0 spec section 9.4.2.22, under Measurement Report
51  * Element. Subelement location data-fields parsed from separate input LCI and LCR Information
52  * Elements are unified in this class.</p>
53  *
54  * <p>Note: The information provided by this class is broadcast by a responder (usually an Access
55  * Point), and passed on as-is. There is no guarantee this information is accurate or correct, and
56  * as a result developers should carefully consider how this information should be used and provide
57  * corresponding advice to users.</p>
58  */
59 public final class ResponderLocation implements Parcelable {
60     private static final int BYTE_MASK = 0xFF;
61     private static final int LSB_IN_BYTE = 0x01;
62     private static final int MSB_IN_BYTE = 0x80;
63     private static final int MIN_BUFFER_SIZE = 3; // length of LEAD_LCI_ELEMENT_BYTES
64     private static final int MAX_BUFFER_SIZE = 256;
65 
66     // Information Element (IE) fields
67     private static final byte MEASUREMENT_TOKEN_AUTONOMOUS = 0x01;
68     private static final byte MEASUREMENT_REPORT_MODE = 0x00;
69     private static final byte MEASUREMENT_TYPE_LCI = 0x08;
70     private static final byte MEASUREMENT_TYPE_LCR = 0x0b;
71 
72     // LCI Subelement IDs
73     private static final byte SUBELEMENT_LCI = 0x00;
74     private static final byte SUBELEMENT_Z = 0x04;
75     private static final byte SUBELEMENT_USAGE = 0x06;
76     private static final byte SUBELEMENT_BSSID_LIST = 0x07;
77 
78     // LCI Subelement Lengths
79     private static final int SUBELEMENT_LCI_LENGTH = 16;
80     private static final int SUBELEMENT_Z_LENGTH = 6;
81     private static final int SUBELEMENT_USAGE_LENGTH1 = 1;
82     private static final int SUBELEMENT_USAGE_LENGTH3 = 3;
83     private static final int SUBELEMENT_BSSID_LIST_MIN_BUFFER_LENGTH = 1;
84 
85     private static final byte[] LEAD_LCI_ELEMENT_BYTES = {
86             MEASUREMENT_TOKEN_AUTONOMOUS, MEASUREMENT_REPORT_MODE, MEASUREMENT_TYPE_LCI
87     };
88 
89     // Subelement LCI constants
90 
91     /* The LCI subelement bit-field lengths are described in Figure 9-214 of the REVmc spec and
92     represented here as a an array of integers */
93     private static final int[] SUBELEMENT_LCI_BIT_FIELD_LENGTHS = {
94             6, 34, 6, 34, 4, 6, 30, 3, 1, 1, 1, 2
95     };
96     private static final int LATLNG_FRACTION_BITS = 25;
97     private static final int LATLNG_UNCERTAINTY_BASE = 8;
98     private static final int ALTITUDE_FRACTION_BITS = 8;
99     private static final int ALTITUDE_UNCERTAINTY_BASE = 21;
100     private static final double LAT_ABS_LIMIT = 90.0;
101     private static final double LNG_ABS_LIMIT = 180.0;
102     private static final int UNCERTAINTY_UNDEFINED = 0;
103 
104     // Subelement LCI fields indices
105     private static final int SUBELEMENT_LCI_LAT_UNCERTAINTY_INDEX = 0;
106     private static final int SUBELEMENT_LCI_LAT_INDEX = 1;
107     private static final int SUBELEMENT_LCI_LNG_UNCERTAINTY_INDEX = 2;
108     private static final int SUBELEMENT_LCI_LNG_INDEX = 3;
109     private static final int SUBELEMENT_LCI_ALT_TYPE_INDEX = 4;
110     private static final int SUBELEMENT_LCI_ALT_UNCERTAINTY_INDEX = 5;
111     private static final int SUBELEMENT_LCI_ALT_INDEX = 6;
112     private static final int SUBELEMENT_LCI_DATUM_INDEX = 7;
113     private static final int SUBELEMENT_LCI_REGLOC_AGREEMENT_INDEX = 8;
114     private static final int SUBELEMENT_LCI_REGLOC_DSE_INDEX = 9;
115     private static final int SUBELEMENT_LCI_DEPENDENT_STA_INDEX = 10;
116     private static final int SUBELEMENT_LCI_VERSION_INDEX = 11;
117 
118     /**
119      * The Altitude value is interpreted based on the Altitude Type, and the selected mDatum.
120      *
121      * @hide
122      */
123     @Retention(SOURCE)
124     @IntDef({ALTITUDE_UNDEFINED, ALTITUDE_METERS, ALTITUDE_FLOORS})
125     public @interface AltitudeType {
126     }
127 
128     /**
129      * Altitude is not defined for the Responder.
130      * The altitude and altitude uncertainty should not be used: see section 2.4 of IETF RFC 6225.
131      */
132     public static final int ALTITUDE_UNDEFINED = 0;
133     /** Responder Altitude is measured in meters.  */
134     public static final int ALTITUDE_METERS = 1;
135     /** Responder Altitude is measured in floors. */
136     public static final int ALTITUDE_FLOORS = 2;
137 
138     /**
139      * The Datum value determines how coordinates are organized in relation to the real world.
140      *
141      * @hide
142      */
143     @Retention(SOURCE)
144     @IntDef({DATUM_UNDEFINED, DATUM_WGS84, DATUM_NAD83_NAV88, DATUM_NAD83_MLLW})
145     public @interface DatumType {
146     }
147 
148     /** Datum is not defined. */
149     public static final int DATUM_UNDEFINED = 0;
150     /** Datum used is WGS84. */
151     public static final int DATUM_WGS84 = 1;
152     /** Datum used is NAD83 NAV88. */
153     public static final int DATUM_NAD83_NAV88 = 2;
154     /** Datum used is NAD83 MLLW. */
155     public static final int DATUM_NAD83_MLLW = 3;
156 
157 
158     /** Version of the LCI protocol is 1.0, the only defined protocol at this time. */
159     public static final int LCI_VERSION_1 = 1;
160 
161     /** Provider/Source of the location */
162     private static final String LOCATION_PROVIDER = "WiFi Access Point";
163 
164     // LCI Subelement Z constants
165     private static final int[] SUBELEMENT_Z_BIT_FIELD_LENGTHS = {2, 14, 24, 8};
166     private static final int Z_FLOOR_NUMBER_FRACTION_BITS = 4;
167     private static final int Z_FLOOR_HEIGHT_FRACTION_BITS = 12;
168     private static final int Z_MAX_HEIGHT_UNCERTAINTY_FACTOR = 25;
169 
170     // LCI Subelement Z fields indices
171     private static final int SUBELEMENT_Z_LAT_EXPECTED_TO_MOVE_INDEX = 0;
172     private static final int SUBELEMENT_Z_FLOOR_NUMBER_INDEX = 1;
173     private static final int SUBELEMENT_Z_HEIGHT_ABOVE_FLOOR_INDEX = 2;
174     private static final int SUBELEMENT_Z_HEIGHT_ABOVE_FLOOR_UNCERTAINTY_INDEX = 3;
175 
176     // LCI Subelement Usage Rules constants
177     private static final int SUBELEMENT_USAGE_MASK_RETRANSMIT = 0x01;
178     private static final int SUBELEMENT_USAGE_MASK_RETENTION_EXPIRES = 0x02;
179     private static final int SUBELEMENT_USAGE_MASK_STA_LOCATION_POLICY = 0x04;
180 
181     // LCI Subelement Usage Rules field indices
182     private static final int SUBELEMENT_USAGE_PARAMS_INDEX = 0; // 8 bits
183 
184     // LCI Subelement BSSID List
185     private static final int SUBELEMENT_BSSID_MAX_INDICATOR_INDEX = 0;
186     private static final int SUBELEMENT_BSSID_LIST_INDEX = 1;
187     private static final int BYTES_IN_A_BSSID = 6;
188 
189     /**
190      * The Expected-To-Move value determines how mobile we expect the STA to be.
191      *
192      * @hide
193      */
194     @Retention(SOURCE)
195     @IntDef({LOCATION_FIXED, LOCATION_VARIABLE, LOCATION_MOVEMENT_UNKNOWN, LOCATION_RESERVED})
196     public @interface ExpectedToMoveType {
197     }
198 
199     /** Location of responder is fixed (does not move) */
200     public static final int LOCATION_FIXED = 0;
201     /** Location of the responder is variable, and may move */
202     public static final int LOCATION_VARIABLE = 1;
203     /** Location of the responder is not known to be either fixed or variable. */
204     public static final int LOCATION_MOVEMENT_UNKNOWN = 2;
205     /** Location of the responder status is a reserved value */
206     public static final int LOCATION_RESERVED = 3;
207 
208     // LCR Subelement IDs
209     private static final byte SUBELEMENT_LOCATION_CIVIC = 0x00;
210     private static final byte SUBELEMENT_MAP_IMAGE = 0x05;
211 
212     // LCR Subelement Lengths
213     private static final int SUBELEMENT_LOCATION_CIVIC_MIN_LENGTH = 2;
214     private static final int SUBELEMENT_LOCATION_CIVIC_MAX_LENGTH = 256;
215     private static final int SUBELEMENT_MAP_IMAGE_URL_MAX_LENGTH = 256;
216 
217     private static final byte[] LEAD_LCR_ELEMENT_BYTES = {
218             MEASUREMENT_TOKEN_AUTONOMOUS, MEASUREMENT_REPORT_MODE, MEASUREMENT_TYPE_LCR
219     };
220 
221     // LCR Location Civic Subelement
222     private static final int CIVIC_COUNTRY_CODE_INDEX = 0;
223     private static final int CIVIC_TLV_LIST_INDEX = 2;
224 
225     // LCR Map Image Subelement field indexes.
226     private static final int SUBELEMENT_IMAGE_MAP_TYPE_INDEX = 0;
227     private static final int MAP_TYPE_URL_DEFINED = 0;
228     private static final String[] SUPPORTED_IMAGE_FILE_EXTENSIONS = {
229             "",
230             "png",
231             "gif",
232             "jpg",
233             "svg",
234             "dxf",
235             "dwg",
236             "dwf",
237             "cad",
238             "tif",
239             "gml",
240             "kml",
241             "bmp",
242             "pgm",
243             "ppm",
244             "xbm",
245             "xpm",
246             "ico"
247     };
248 
249     // General LCI and LCR state
250     private final boolean mIsValid;
251 
252     /*
253       These members are not final because we are not sure if the corresponding subelement will be
254       present until after the parsing process. However, the default value should be set as listed.
255     */
256     private boolean mIsLciValid = false;
257     private boolean mIsZValid = false;
258     private boolean mIsUsageValid = true; // By default this is assumed true
259     private boolean mIsBssidListValid = false;
260     private boolean mIsLocationCivicValid = false;
261     private boolean mIsMapImageValid = false;
262 
263     // LCI Subelement LCI state
264     private double mLatitudeUncertainty;
265     private double mLatitude;
266     private double mLongitudeUncertainty;
267     private double mLongitude;
268     private int mAltitudeType;
269     private double mAltitudeUncertainty;
270     private double mAltitude;
271     private int mDatum;
272     private boolean mLciRegisteredLocationAgreement;
273     private boolean mLciRegisteredLocationDse;
274     private boolean mLciDependentStation;
275     private int mLciVersion;
276 
277     // LCI Subelement Z state
278     private int mExpectedToMove;
279     private double mFloorNumber;
280     private double mHeightAboveFloorMeters;
281     private double mHeightAboveFloorUncertaintyMeters;
282 
283     // LCI Subelement Usage Rights state
284     private boolean mUsageRetransmit;
285     private boolean mUsageRetentionExpires;
286     private boolean mUsageExtraInfoOnAssociation;
287 
288     // LCI Subelement BSSID List state
289     private ArrayList<MacAddress> mBssidList;
290 
291     // LCR Subelement Location Civic state
292     private String mCivicLocationCountryCode;
293     private String mCivicLocationString;
294     private CivicLocation mCivicLocation;
295 
296     // LCR Subelement Map Image state
297     private int mMapImageType;
298     private Uri mMapImageUri;
299 
300     /**
301      * Constructor
302      *
303      * @param lciBuffer the bytes received in the LCI Measurement Report Information Element
304      * @param lcrBuffer the bytes received in the LCR Measurement Report Information Element
305      *
306      * @hide
307      */
ResponderLocation(byte[] lciBuffer, byte[] lcrBuffer)308     public ResponderLocation(byte[] lciBuffer, byte[] lcrBuffer) {
309         boolean isLciIeValid = false;
310         boolean isLcrIeValid = false;
311         setLciSubelementDefaults();
312         setZaxisSubelementDefaults();
313         setUsageSubelementDefaults();
314         setBssidListSubelementDefaults();
315         setCivicLocationSubelementDefaults();
316         setMapImageSubelementDefaults();
317         if (lciBuffer != null && lciBuffer.length > LEAD_LCI_ELEMENT_BYTES.length) {
318             isLciIeValid = parseInformationElementBuffer(
319                 MEASUREMENT_TYPE_LCI, lciBuffer, LEAD_LCI_ELEMENT_BYTES);
320         }
321         if (lcrBuffer != null && lcrBuffer.length > LEAD_LCR_ELEMENT_BYTES.length) {
322             isLcrIeValid = parseInformationElementBuffer(
323                 MEASUREMENT_TYPE_LCR, lcrBuffer, LEAD_LCR_ELEMENT_BYTES);
324         }
325 
326         boolean isLciValid = isLciIeValid && mIsUsageValid
327                 && (mIsLciValid || mIsZValid || mIsBssidListValid);
328 
329         boolean isLcrValid = isLcrIeValid && mIsUsageValid
330                 && (mIsLocationCivicValid || mIsMapImageValid);
331 
332         mIsValid = isLciValid || isLcrValid;
333 
334         if (!mIsValid) {
335             setLciSubelementDefaults();
336             setZaxisSubelementDefaults();
337             setCivicLocationSubelementDefaults();
338             setMapImageSubelementDefaults();
339         }
340     }
341 
ResponderLocation(Parcel in)342     private ResponderLocation(Parcel in) {
343         // Object Validation
344         mIsValid = in.readByte() != 0;
345         mIsLciValid = in.readByte() != 0;
346         mIsZValid = in.readByte() != 0;
347         mIsUsageValid = in.readByte() != 0;
348         mIsBssidListValid = in.readByte() != 0;
349         mIsLocationCivicValid = in.readByte() != 0;
350         mIsMapImageValid = in.readByte() != 0;
351 
352         // LCI Subelement LCI state
353         mLatitudeUncertainty = in.readDouble();
354         mLatitude = in.readDouble();
355         mLongitudeUncertainty = in.readDouble();
356         mLongitude = in.readDouble();
357         mAltitudeType = in.readInt();
358         mAltitudeUncertainty = in.readDouble();
359         mAltitude = in.readDouble();
360         mDatum = in.readInt();
361         mLciRegisteredLocationAgreement = in.readByte() != 0;
362         mLciRegisteredLocationDse = in.readByte() != 0;
363         mLciDependentStation = in.readByte() != 0;
364         mLciVersion = in.readInt();
365 
366         // LCI Subelement Z state
367         mExpectedToMove = in.readInt();
368         mFloorNumber = in.readDouble();
369         mHeightAboveFloorMeters = in.readDouble();
370         mHeightAboveFloorUncertaintyMeters = in.readDouble();
371 
372         // LCI Usage Rights
373         mUsageRetransmit = in.readByte() != 0;
374         mUsageRetentionExpires = in.readByte() != 0;
375         mUsageExtraInfoOnAssociation = in.readByte() != 0;
376 
377         // LCI Subelement BSSID List
378         mBssidList = in.readArrayList(MacAddress.class.getClassLoader());
379 
380         // LCR Subelement Location Civic
381         mCivicLocationCountryCode = in.readString();
382         mCivicLocationString = in.readString();
383         mCivicLocation = in.readParcelable(this.getClass().getClassLoader());
384 
385         // LCR Subelement Map Image
386         mMapImageType = in.readInt();
387         String urlString = in.readString();
388         if (TextUtils.isEmpty(urlString)) {
389             mMapImageUri = null;
390         } else {
391             mMapImageUri = Uri.parse(urlString);
392         }
393     }
394 
395     public static final @android.annotation.NonNull Creator<ResponderLocation> CREATOR = new Creator<ResponderLocation>() {
396         @Override
397         public ResponderLocation createFromParcel(Parcel in) {
398             return new ResponderLocation(in);
399         }
400 
401         @Override
402         public ResponderLocation[] newArray(int size) {
403             return new ResponderLocation[size];
404         }
405     };
406 
407     @Override
describeContents()408     public int describeContents() {
409         return 0;
410     }
411 
412     @Override
writeToParcel(Parcel parcel, int flags)413     public void writeToParcel(Parcel parcel, int flags) {
414         // Object
415         parcel.writeByte((byte) (mIsValid ? 1 : 0));
416         parcel.writeByte((byte) (mIsLciValid ? 1 : 0));
417         parcel.writeByte((byte) (mIsZValid ? 1 : 0));
418         parcel.writeByte((byte) (mIsUsageValid ? 1 : 0));
419         parcel.writeByte((byte) (mIsBssidListValid ? 1 : 0));
420         parcel.writeByte((byte) (mIsLocationCivicValid ? 1 : 0));
421         parcel.writeByte((byte) (mIsMapImageValid ? 1 : 0));
422 
423         // LCI Subelement LCI state
424         parcel.writeDouble(mLatitudeUncertainty);
425         parcel.writeDouble(mLatitude);
426         parcel.writeDouble(mLongitudeUncertainty);
427         parcel.writeDouble(mLongitude);
428         parcel.writeInt(mAltitudeType);
429         parcel.writeDouble(mAltitudeUncertainty);
430         parcel.writeDouble(mAltitude);
431         parcel.writeInt(mDatum);
432         parcel.writeByte((byte) (mLciRegisteredLocationAgreement ? 1 : 0));
433         parcel.writeByte((byte) (mLciRegisteredLocationDse ? 1 : 0));
434         parcel.writeByte((byte) (mLciDependentStation ? 1 : 0));
435         parcel.writeInt(mLciVersion);
436 
437         // LCI Subelement Z state
438         parcel.writeInt(mExpectedToMove);
439         parcel.writeDouble(mFloorNumber);
440         parcel.writeDouble(mHeightAboveFloorMeters);
441         parcel.writeDouble(mHeightAboveFloorUncertaintyMeters);
442 
443         // LCI Usage Rights
444         parcel.writeByte((byte) (mUsageRetransmit ? 1 : 0));
445         parcel.writeByte((byte) (mUsageRetentionExpires ? 1 : 0));
446         parcel.writeByte((byte) (mUsageExtraInfoOnAssociation ? 1 : 0));
447 
448         // LCI Subelement BSSID List
449         parcel.writeList(mBssidList);
450 
451         // LCR Subelement Location Civic
452         parcel.writeString(mCivicLocationCountryCode);
453         parcel.writeString(mCivicLocationString);
454         parcel.writeParcelable(mCivicLocation, flags);
455 
456         // LCR Subelement Map Image
457         parcel.writeInt(mMapImageType);
458         if (mMapImageUri != null) {
459             parcel.writeString(mMapImageUri.toString());
460         } else {
461             parcel.writeString("");
462         }
463     }
464 
465     /**
466      * Test if the Information Element (IE) is in the correct format, and then parse its Subelements
467      * based on their type, and setting state in this object when present.
468      *
469      * @return a boolean indicating the success of the parsing function
470      */
parseInformationElementBuffer( int ieType, byte[] buffer, byte[] expectedLeadBytes)471     private boolean parseInformationElementBuffer(
472             int ieType, byte[] buffer, byte[] expectedLeadBytes) {
473         int bufferPtr = 0;
474         int bufferLength = buffer.length;
475 
476         // Ensure the buffer size is within expected limits
477         if (bufferLength < MIN_BUFFER_SIZE || bufferLength > MAX_BUFFER_SIZE) {
478             return false;
479         }
480 
481         // Ensure the IE contains the correct leading bytes
482         byte[] leadBufferBytes = Arrays.copyOfRange(buffer, bufferPtr, expectedLeadBytes.length);
483         if (!Arrays.equals(leadBufferBytes, expectedLeadBytes)) {
484             return false;
485         }
486 
487         // Iterate through the sub-elements contained in the Information Element (IE)
488         bufferPtr += expectedLeadBytes.length;
489         // Loop over the buffer ensuring there are 2-bytes available for each new subelement tested.
490         while (bufferPtr + 1 < bufferLength) {
491             byte subelement = buffer[bufferPtr++];
492             int subelementLength = buffer[bufferPtr++];
493             // Check there is enough data for the next subelement
494             if ((bufferPtr + subelementLength) > bufferLength || subelementLength <= 0) {
495                 return false;
496             }
497 
498             byte[] subelementData =
499                     Arrays.copyOfRange(buffer, bufferPtr, bufferPtr + subelementLength);
500 
501             if (ieType == MEASUREMENT_TYPE_LCI) {
502                 switch (subelement) {
503                     case SUBELEMENT_LCI:
504                         mIsLciValid = parseSubelementLci(subelementData);
505                         if (!mIsLciValid || mLciVersion != LCI_VERSION_1) {
506                             setLciSubelementDefaults();
507                         }
508                         break;
509                     case SUBELEMENT_Z:
510                         mIsZValid = parseSubelementZ(subelementData);
511                         if (!mIsZValid) {
512                             setZaxisSubelementDefaults();
513                         }
514                         break;
515                     case SUBELEMENT_USAGE:
516                         mIsUsageValid = parseSubelementUsage(subelementData);
517                         // Note: if the Usage Subelement is not valid, don't reset the state, as
518                         // it is now indicating the whole ResponderLocation is invalid.
519                         break;
520                     case SUBELEMENT_BSSID_LIST:
521                         mIsBssidListValid = parseSubelementBssidList(subelementData);
522                         if (!mIsBssidListValid) {
523                             setBssidListSubelementDefaults();
524                         }
525                         break;
526                     default:
527                         break; // skip over unused or vendor specific subelements
528                 }
529             } else if (ieType == MEASUREMENT_TYPE_LCR) {
530                 switch (subelement) {
531                     case SUBELEMENT_LOCATION_CIVIC:
532                         mIsLocationCivicValid = parseSubelementLocationCivic(subelementData);
533                         if (!mIsLocationCivicValid) {
534                             setCivicLocationSubelementDefaults();
535                         }
536                         break;
537                     case SUBELEMENT_MAP_IMAGE:
538                         mIsMapImageValid = parseSubelementMapImage(subelementData);
539                         if (!mIsMapImageValid) {
540                             setMapImageSubelementDefaults();
541                         }
542                         break;
543                     default:
544                         break; // skip over unused or other vendor specific subelements
545                 }
546             }
547 
548             bufferPtr += subelementLength;
549         }
550         return true;
551     }
552 
553     /**
554      * Parse the LCI Sub-Element in the LCI Information Element (IE).
555      *
556      * @param buffer a buffer containing the subelement
557      * @return boolean true indicates success
558      */
parseSubelementLci(byte[] buffer)559     private boolean parseSubelementLci(byte[] buffer) {
560         if (buffer.length > SUBELEMENT_LCI_LENGTH) {
561             return false;
562         }
563         swapEndianByteByByte(buffer);
564         long[] subelementLciFields = getFieldData(buffer, SUBELEMENT_LCI_BIT_FIELD_LENGTHS);
565         if (subelementLciFields == null) {
566             return false;
567         }
568         // Set member state based on parsed buffer data
569         mLatitudeUncertainty = decodeLciLatLngUncertainty(
570                 subelementLciFields[SUBELEMENT_LCI_LAT_UNCERTAINTY_INDEX]);
571         mLatitude = decodeLciLatLng(subelementLciFields, SUBELEMENT_LCI_BIT_FIELD_LENGTHS,
572                 SUBELEMENT_LCI_LAT_INDEX, LAT_ABS_LIMIT);
573         mLongitudeUncertainty = decodeLciLatLngUncertainty(
574                 subelementLciFields[SUBELEMENT_LCI_LNG_UNCERTAINTY_INDEX]);
575         mLongitude =
576                 decodeLciLatLng(subelementLciFields, SUBELEMENT_LCI_BIT_FIELD_LENGTHS,
577                         SUBELEMENT_LCI_LNG_INDEX, LNG_ABS_LIMIT);
578         mAltitudeType = (int) subelementLciFields[SUBELEMENT_LCI_ALT_TYPE_INDEX] & BYTE_MASK;
579         mAltitudeUncertainty =
580                 decodeLciAltUncertainty(subelementLciFields[SUBELEMENT_LCI_ALT_UNCERTAINTY_INDEX]);
581         mAltitude =
582                 Math.scalb(subelementLciFields[SUBELEMENT_LCI_ALT_INDEX], -ALTITUDE_FRACTION_BITS);
583         mDatum = (int) subelementLciFields[SUBELEMENT_LCI_DATUM_INDEX] & BYTE_MASK;
584         mLciRegisteredLocationAgreement =
585                 (subelementLciFields[SUBELEMENT_LCI_REGLOC_AGREEMENT_INDEX] == 1);
586         mLciRegisteredLocationDse =
587                 (subelementLciFields[SUBELEMENT_LCI_REGLOC_DSE_INDEX] == 1);
588         mLciDependentStation =
589                 (subelementLciFields[SUBELEMENT_LCI_DEPENDENT_STA_INDEX] == 1);
590         mLciVersion = (int) subelementLciFields[SUBELEMENT_LCI_VERSION_INDEX];
591         return true;
592     }
593 
594     /**
595      * Decode the floating point value of an encoded lat or lng in the LCI subelement field.
596      *
597      * @param fields        the array of field data represented as longs
598      * @param bitFieldSizes the lengths of each field
599      * @param offset        the offset of the field being decoded
600      * @param limit the maximum absolute value (note: different for lat vs lng)
601      * @return the floating point value of the lat or lng
602      */
decodeLciLatLng(long[] fields, int[] bitFieldSizes, int offset, double limit)603     private double decodeLciLatLng(long[] fields, int[] bitFieldSizes, int offset, double limit) {
604         double angle;
605         if ((fields[offset] & (long) Math.pow(2, bitFieldSizes[offset] - 1)) != 0) {
606             // Negative 2's complement value
607             // Note: The Math.pow(...) method cannot return a NaN value because the bitFieldSize
608             // for Lat or Lng is limited to exactly 34 bits.
609             angle = Math.scalb((double) fields[offset] - Math.pow(2, bitFieldSizes[offset]),
610                     -LATLNG_FRACTION_BITS);
611         } else {
612             // Positive 2's complement value
613             angle = Math.scalb((double) fields[offset], -LATLNG_FRACTION_BITS);
614         }
615         if (angle > limit) {
616             angle = limit;
617         } else if (angle < -limit) {
618             angle = -limit;
619         }
620         return angle;
621     }
622 
623     /**
624      * Coverts an encoded Lat/Lng uncertainty into a number of degrees.
625      *
626      * @param encodedValue the encoded uncertainty
627      * @return the value in degrees
628      */
decodeLciLatLngUncertainty(long encodedValue)629     private double decodeLciLatLngUncertainty(long encodedValue) {
630         return Math.pow(2, LATLNG_UNCERTAINTY_BASE - encodedValue);
631     }
632 
633     /**
634      * Converts an encoded Alt uncertainty into a number of degrees.
635      *
636      * @param encodedValue the encoded uncertainty
637      * @return the value in degrees
638      */
decodeLciAltUncertainty(long encodedValue)639     private double decodeLciAltUncertainty(long encodedValue) {
640         return Math.pow(2, ALTITUDE_UNCERTAINTY_BASE - encodedValue);
641     }
642 
643     /**
644      * Parse the Z subelement of the LCI IE.
645      *
646      * @param buffer a buffer containing the subelement
647      * @return boolean true indicates success
648      */
parseSubelementZ(byte[] buffer)649     private boolean parseSubelementZ(byte[] buffer) {
650         if (buffer.length != SUBELEMENT_Z_LENGTH) {
651             return false;
652         }
653         swapEndianByteByByte(buffer);
654         long[] subelementZFields = getFieldData(buffer, SUBELEMENT_Z_BIT_FIELD_LENGTHS);
655         if (subelementZFields == null) {
656             return false;
657         }
658 
659         mExpectedToMove =
660                 (int) subelementZFields[SUBELEMENT_Z_LAT_EXPECTED_TO_MOVE_INDEX] & BYTE_MASK;
661         mFloorNumber = decodeZUnsignedToSignedValue(subelementZFields,
662                 SUBELEMENT_Z_BIT_FIELD_LENGTHS, SUBELEMENT_Z_FLOOR_NUMBER_INDEX,
663                 Z_FLOOR_NUMBER_FRACTION_BITS);
664 
665         mHeightAboveFloorMeters = decodeZUnsignedToSignedValue(subelementZFields,
666                 SUBELEMENT_Z_BIT_FIELD_LENGTHS, SUBELEMENT_Z_HEIGHT_ABOVE_FLOOR_INDEX,
667                 Z_FLOOR_HEIGHT_FRACTION_BITS);
668 
669         long zHeightUncertainty =
670                 subelementZFields[SUBELEMENT_Z_HEIGHT_ABOVE_FLOOR_UNCERTAINTY_INDEX];
671         if (zHeightUncertainty > 0 && zHeightUncertainty < Z_MAX_HEIGHT_UNCERTAINTY_FACTOR) {
672             mHeightAboveFloorUncertaintyMeters =
673                     Math.pow(2, Z_FLOOR_HEIGHT_FRACTION_BITS - zHeightUncertainty - 1);
674         } else {
675             return false;
676         }
677         return true;
678     }
679 
680     /**
681      * Decode a two's complement encoded value, to a signed double based on the field length.
682      *
683      * @param fieldValues the array of field values reprented as longs
684      * @param fieldLengths the array of field lengths
685      * @param index the index of the field being decoded
686      * @param fraction the number of fractional bits in the value
687      * @return the signed value represented as a double
688      */
decodeZUnsignedToSignedValue(long[] fieldValues, int[] fieldLengths, int index, int fraction)689     private double decodeZUnsignedToSignedValue(long[] fieldValues, int[] fieldLengths, int index,
690             int fraction) {
691         int value = (int) fieldValues[index];
692         int maxPositiveValue = (int) Math.pow(2, fieldLengths[index] - 1) - 1;
693         if (value > maxPositiveValue) {
694             value -= Math.pow(2, fieldLengths[index]);
695         }
696         return Math.scalb(value, -fraction);
697     }
698 
699     /**
700      * Parse Subelement Usage Rights
701      */
parseSubelementUsage(byte[] buffer)702     private boolean parseSubelementUsage(byte[] buffer) {
703         if (buffer.length != SUBELEMENT_USAGE_LENGTH1
704                 && buffer.length != SUBELEMENT_USAGE_LENGTH3) {
705             return false;
706         }
707         mUsageRetransmit =
708                 (buffer[SUBELEMENT_USAGE_PARAMS_INDEX] & SUBELEMENT_USAGE_MASK_RETRANSMIT) != 0;
709         mUsageRetentionExpires =
710                 (buffer[SUBELEMENT_USAGE_PARAMS_INDEX] & SUBELEMENT_USAGE_MASK_RETENTION_EXPIRES)
711                         != 0;
712         mUsageExtraInfoOnAssociation =
713                 (buffer[SUBELEMENT_USAGE_PARAMS_INDEX] & SUBELEMENT_USAGE_MASK_STA_LOCATION_POLICY)
714                         != 0;
715         // Note: the Retransmit flag must be true, and RetentionExpires, false for the
716         // ResponderLocation object to be usable by public applications.
717         return (mUsageRetransmit && !mUsageRetentionExpires);
718     }
719 
720     /**
721      * Parse the BSSID List Subelement of the LCI IE.
722      *
723      * @param buffer a buffer containing the subelement
724      * @return boolean true indicates success
725      */
parseSubelementBssidList(byte[] buffer)726     private boolean parseSubelementBssidList(byte[] buffer) {
727         if (buffer.length < SUBELEMENT_BSSID_LIST_MIN_BUFFER_LENGTH) {
728             return false;
729         }
730         if ((buffer.length - 1) % BYTES_IN_A_BSSID != 0) {
731             return false;
732         }
733 
734         int maxBssidIndicator = (int) buffer[SUBELEMENT_BSSID_MAX_INDICATOR_INDEX] & BYTE_MASK;
735         int bssidListLength = (buffer.length - 1) / BYTES_IN_A_BSSID;
736         // The maxBSSIDIndicator is ignored. Its use is still being clarified in 802.11REVmd,
737         // which is not published at this time. This field will be used in a future
738         // release of Android after 802.11REVmd is public. Here, we interpret the following
739         // params as an explicit list of BSSIDs.
740 
741 
742         int bssidOffset = SUBELEMENT_BSSID_LIST_INDEX;
743         for (int i = 0; i < bssidListLength; i++) {
744             byte[] bssid = Arrays.copyOfRange(buffer, bssidOffset, bssidOffset + BYTES_IN_A_BSSID);
745             MacAddress macAddress = MacAddress.fromBytes(bssid);
746             mBssidList.add(macAddress);
747             bssidOffset += BYTES_IN_A_BSSID;
748         }
749         return true;
750     }
751 
752     /**
753      * Parse the Location Civic subelement in the LCR IE.
754      *
755      * @param buffer a buffer containing the subelement
756      * @return boolean true indicates success
757      */
parseSubelementLocationCivic(byte[] buffer)758     private boolean parseSubelementLocationCivic(byte[] buffer) {
759         if (buffer.length <  SUBELEMENT_LOCATION_CIVIC_MIN_LENGTH
760                 || buffer.length > SUBELEMENT_LOCATION_CIVIC_MAX_LENGTH) {
761             return false;
762         }
763         mCivicLocationCountryCode =
764                 new String(Arrays.copyOfRange(buffer, CIVIC_COUNTRY_CODE_INDEX,
765                         CIVIC_TLV_LIST_INDEX)).toUpperCase();
766         CivicLocation civicLocation =
767                 new CivicLocation(
768                         Arrays.copyOfRange(buffer, CIVIC_TLV_LIST_INDEX, buffer.length),
769                         mCivicLocationCountryCode);
770         if (!civicLocation.isValid()) {
771             return false;
772         }
773         this.mCivicLocation = civicLocation;
774         mCivicLocationString = civicLocation.toString();
775         return true;
776     }
777 
778     /**
779      * Parse the Map Image subelement in the LCR IE.
780      *
781      * @param buffer a buffer containing the subelement
782      * @return boolean true indicates success
783      */
parseSubelementMapImage(byte[] buffer)784     private boolean parseSubelementMapImage(byte[] buffer) {
785         if (buffer.length > SUBELEMENT_MAP_IMAGE_URL_MAX_LENGTH) {
786             return false;
787         }
788         int mapImageType = buffer[SUBELEMENT_IMAGE_MAP_TYPE_INDEX];
789         int supportedTypesMax = SUPPORTED_IMAGE_FILE_EXTENSIONS.length - 1;
790         if (mapImageType < MAP_TYPE_URL_DEFINED || mapImageType > supportedTypesMax) {
791             return false;
792         }
793         this.mMapImageType = mapImageType;
794         byte[] urlBytes = Arrays.copyOfRange(buffer, 1, buffer.length);
795         mMapImageUri = Uri.parse(new String(urlBytes, StandardCharsets.UTF_8));
796         return true;
797     }
798 
799     /**
800      * Convert an image type code to a Mime type
801      *
802      * @param imageTypeCode encoded as an integer
803      * @return the mime type of the image file
804      */
imageTypeToMime(int imageTypeCode, String imageUrl)805     private String imageTypeToMime(int imageTypeCode, String imageUrl) {
806         int supportedExtensionsMax = SUPPORTED_IMAGE_FILE_EXTENSIONS.length - 1;
807         if ((imageTypeCode == 0 && imageUrl == null) || imageTypeCode > supportedExtensionsMax) {
808             return null;
809         }
810         MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
811         if (imageTypeCode == 0) {
812             return mimeTypeMap.getMimeTypeFromExtension(
813                     MimeTypeMap.getFileExtensionFromUrl(imageUrl));
814         } else {
815             return mimeTypeMap.getMimeTypeFromExtension(
816                     SUPPORTED_IMAGE_FILE_EXTENSIONS[imageTypeCode]);
817         }
818     }
819 
820     /**
821      * Converts a byte array containing fields of variable size, into an array of longs where the
822      * field boundaries are defined in a constant int array passed as an argument.
823      *
824      * @param buffer        the byte array containing all the fields
825      * @param bitFieldSizes the int array defining the size of each field
826      */
getFieldData(byte[] buffer, int[] bitFieldSizes)827     private long[] getFieldData(byte[] buffer, int[] bitFieldSizes) {
828         int bufferLengthBits = buffer.length * Byte.SIZE;
829         int sumBitFieldSizes = 0;
830         for (int i : bitFieldSizes) {
831             if (i > Long.SIZE) {
832                 return null;
833             }
834             sumBitFieldSizes += i;
835         }
836         if (bufferLengthBits != sumBitFieldSizes) {
837             return null;
838         }
839         long[] fieldData = new long[bitFieldSizes.length];
840         int bufferBitPos = 0;
841         for (int fieldIndex = 0; fieldIndex < bitFieldSizes.length; fieldIndex++) {
842             int bitFieldSize = bitFieldSizes[fieldIndex];
843             long field = 0;
844             for (int n = 0; n < bitFieldSize; n++) {
845                 field |= ((long) getBitAtBitOffsetInByteArray(buffer, bufferBitPos + n) << n);
846             }
847             fieldData[fieldIndex] = field;
848             bufferBitPos += bitFieldSize;
849         }
850         return fieldData;
851     }
852 
853     /**
854      * Retrieves state of a bit at the bit-offset in a byte array, where the offset represents the
855      * bytes in contiguous data with each value in big endian order.
856      *
857      * @param buffer          the data buffer of bytes containing all the fields
858      * @param bufferBitOffset the bit offset into the entire buffer
859      * @return a zero or one value, representing the state of that bit.
860      */
getBitAtBitOffsetInByteArray(byte[] buffer, int bufferBitOffset)861     private int getBitAtBitOffsetInByteArray(byte[] buffer, int bufferBitOffset) {
862         int bufferIndex = bufferBitOffset / Byte.SIZE; // The byte index that contains the bit
863         int bitOffsetInByte = bufferBitOffset % Byte.SIZE; // The bit offset within that byte
864         int result = (buffer[bufferIndex] & (MSB_IN_BYTE >> bitOffsetInByte)) == 0 ? 0 : 1;
865         return result;
866     }
867 
868     /**
869      * Reverses the order of the bits in each byte of a byte array.
870      *
871      * @param buffer the array containing each byte that will be reversed
872      */
swapEndianByteByByte(byte[] buffer)873     private void swapEndianByteByByte(byte[] buffer) {
874         for (int n = 0; n < buffer.length; n++) {
875             byte currentByte = buffer[n];
876             byte reversedByte = 0; // Cleared value
877             byte bitSelectorMask = LSB_IN_BYTE;
878             for (int i = 0; i < Byte.SIZE; i++) {
879                 reversedByte = (byte) (reversedByte << 1);
880                 if ((currentByte & bitSelectorMask) != 0) {
881                     reversedByte = (byte) (reversedByte | LSB_IN_BYTE);
882                 }
883                 bitSelectorMask = (byte) (bitSelectorMask << 1);
884             }
885             buffer[n] = reversedByte;
886         }
887     }
888 
889     /**
890      * Sets the LCI subelement fields to the default undefined values.
891      */
setLciSubelementDefaults()892     private void setLciSubelementDefaults() {
893         mIsLciValid = false;
894         mLatitudeUncertainty = UNCERTAINTY_UNDEFINED;
895         mLatitude = 0;
896         mLongitudeUncertainty = UNCERTAINTY_UNDEFINED;
897         mLongitude = 0;
898         mAltitudeType = ALTITUDE_UNDEFINED;
899         mAltitudeUncertainty = UNCERTAINTY_UNDEFINED;
900         mAltitude = 0;
901         mDatum = DATUM_UNDEFINED;
902         mLciRegisteredLocationAgreement = false;
903         mLciRegisteredLocationDse = false;
904         mLciDependentStation = false;
905         mLciVersion = 0;
906     }
907 
908     /**
909      * Sets the Z subelement fields to the default values when undefined.
910      */
setZaxisSubelementDefaults()911     private void setZaxisSubelementDefaults() {
912         mIsZValid = false;
913         mExpectedToMove = 0;
914         mFloorNumber = 0;
915         mHeightAboveFloorMeters = 0;
916         mHeightAboveFloorUncertaintyMeters = 0;
917     }
918 
919     /**
920      * Sets the Usage Policy subelement fields to the default undefined values.
921      */
setUsageSubelementDefaults()922     private void setUsageSubelementDefaults() {
923         mUsageRetransmit = true;
924         mUsageRetentionExpires = false;
925         mUsageExtraInfoOnAssociation = false;
926     }
927 
928     /**
929      * Sets the BSSID List subelement fields to the default values when undefined.
930      */
setBssidListSubelementDefaults()931     private void setBssidListSubelementDefaults() {
932         mIsBssidListValid = false;
933         mBssidList = new ArrayList<>();
934     }
935 
936     /**
937      * Sets the LCR Civic Location subelement field to the default undefined value.
938      *
939      * @hide
940      */
setCivicLocationSubelementDefaults()941     public void setCivicLocationSubelementDefaults() {
942         mIsLocationCivicValid = false;
943         mCivicLocationCountryCode = "";
944         mCivicLocationString = "";
945         mCivicLocation = null;
946     }
947 
948     /**
949      * Sets the LCR Map Image subelement field to the default values when undefined.
950      */
setMapImageSubelementDefaults()951     private void setMapImageSubelementDefaults() {
952         mIsMapImageValid = false;
953         mMapImageType = MAP_TYPE_URL_DEFINED;
954         mMapImageUri = null;
955     }
956 
957     @Override
equals(Object obj)958     public boolean equals(Object obj) {
959         if (this == obj) {
960             return true;
961         }
962         if (obj == null || getClass() != obj.getClass()) {
963             return false;
964         }
965         ResponderLocation other = (ResponderLocation) obj;
966         return mIsValid == other.mIsValid
967                 && mIsLciValid == other.mIsLciValid
968                 && mIsZValid == other.mIsZValid
969                 && mIsUsageValid == other.mIsUsageValid
970                 && mIsBssidListValid == other.mIsBssidListValid
971                 && mIsLocationCivicValid == other.mIsLocationCivicValid
972                 && mIsMapImageValid == other.mIsMapImageValid
973                 && mLatitudeUncertainty == other.mLatitudeUncertainty
974                 && mLatitude == other.mLatitude
975                 && mLongitudeUncertainty == other.mLongitudeUncertainty
976                 && mLongitude == other.mLongitude
977                 && mAltitudeType == other.mAltitudeType
978                 && mAltitudeUncertainty == other.mAltitudeUncertainty
979                 && mAltitude == other.mAltitude
980                 && mDatum == other.mDatum
981                 && mLciRegisteredLocationAgreement == other.mLciRegisteredLocationAgreement
982                 && mLciRegisteredLocationDse == other.mLciRegisteredLocationDse
983                 && mLciDependentStation == other.mLciDependentStation
984                 && mLciVersion == other.mLciVersion
985                 && mExpectedToMove == other.mExpectedToMove
986                 && mFloorNumber == other.mFloorNumber
987                 && mHeightAboveFloorMeters == other.mHeightAboveFloorMeters
988                 && mHeightAboveFloorUncertaintyMeters
989                         == other.mHeightAboveFloorUncertaintyMeters
990                 && mUsageRetransmit == other.mUsageRetransmit
991                 && mUsageRetentionExpires == other.mUsageRetentionExpires
992                 && mUsageExtraInfoOnAssociation == other.mUsageExtraInfoOnAssociation
993                 && mBssidList.equals(other.mBssidList)
994                 && mCivicLocationCountryCode.equals(other.mCivicLocationCountryCode)
995                 && mCivicLocationString.equals(other.mCivicLocationString)
996                 && Objects.equals(mCivicLocation, other.mCivicLocation)
997                 && mMapImageType == other.mMapImageType
998                 && Objects.equals(mMapImageUri, other.mMapImageUri);
999     }
1000 
1001     @Override
hashCode()1002     public int hashCode() {
1003         return Objects.hash(mIsValid, mIsLciValid, mIsZValid, mIsUsageValid, mIsBssidListValid,
1004                 mIsLocationCivicValid, mIsMapImageValid, mLatitudeUncertainty, mLatitude,
1005                 mLongitudeUncertainty, mLongitude, mAltitudeType, mAltitudeUncertainty, mAltitude,
1006                 mDatum, mLciRegisteredLocationAgreement,
1007                 mLciRegisteredLocationDse, mLciDependentStation, mLciVersion,
1008                 mExpectedToMove, mFloorNumber, mHeightAboveFloorMeters,
1009                 mHeightAboveFloorUncertaintyMeters, mUsageRetransmit, mUsageRetentionExpires,
1010                 mUsageExtraInfoOnAssociation, mBssidList, mCivicLocationCountryCode,
1011                 mCivicLocationString, mCivicLocation, mMapImageType, mMapImageUri);
1012     }
1013 
1014     /**
1015      * @return true if the ResponderLocation object is valid and contains useful information
1016      * relevant to the location of the Responder. If this is ever false, this object will not be
1017      * available to developers, and have a null value.
1018      *
1019      * @hide
1020      */
isValid()1021     public boolean isValid() {
1022         return mIsValid;
1023     }
1024 
1025     /**
1026      * @return true if the LCI subelement (containing Latitude, Longitude and Altitude) is valid.
1027      *
1028      * <p> This method tells us if the responder has provided its Location Configuration
1029      * Information (LCI) directly, and is useful when an external database of responder locations
1030      * is not available</p>
1031      *
1032      * <p>If isLciSubelementValid() returns true, all the LCI values provided by the corresponding
1033      * getter methods will have been set as described by the responder, or else if false, they
1034      * should not be used and will throw an IllegalStateException.</p>
1035      */
isLciSubelementValid()1036     public boolean isLciSubelementValid() {
1037         return mIsLciValid;
1038     }
1039 
1040     /**
1041      * @return the latitude uncertainty in degrees.
1042      * <p>
1043      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1044      * </p>
1045      * <p> An unknown uncertainty is indicated by 0.</p>
1046      */
getLatitudeUncertainty()1047     public double getLatitudeUncertainty() {
1048         if (!mIsLciValid) {
1049             throw new IllegalStateException(
1050                 "getLatitudeUncertainty(): invoked on an invalid result: mIsLciValid = false.");
1051         }
1052         return mLatitudeUncertainty;
1053     }
1054 
1055     /**
1056      * @return the latitude in degrees
1057      * <p>
1058      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1059      */
getLatitude()1060     public double getLatitude() {
1061         if (!mIsLciValid) {
1062             throw new IllegalStateException(
1063                 "getLatitude(): invoked on an invalid result: mIsLciValid = false.");
1064         }
1065         return mLatitude;
1066     }
1067 
1068     /**
1069      * @return the Longitude uncertainty in degrees.
1070      * <p>
1071      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1072      * </p>
1073      * <p> An unknown uncertainty is indicated by 0.</p>
1074      */
getLongitudeUncertainty()1075     public double getLongitudeUncertainty() {
1076         if (!mIsLciValid) {
1077             throw new IllegalStateException(
1078                 "getLongitudeUncertainty(): invoked on an invalid result: mIsLciValid = false.");
1079         }
1080         return mLongitudeUncertainty;
1081     }
1082 
1083     /**
1084      * @return the Longitude in degrees..
1085      * <p>
1086      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1087      */
getLongitude()1088     public double getLongitude() {
1089         if (!mIsLciValid) {
1090             throw new IllegalStateException(
1091                 "getLatitudeUncertainty(): invoked on an invalid result: mIsLciValid = false.");
1092         }
1093         return mLongitude;
1094     }
1095 
1096     /**
1097      * @return the Altitude type.
1098      * <p>
1099      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1100      */
1101     @AltitudeType
getAltitudeType()1102     public int getAltitudeType() {
1103         if (!mIsLciValid) {
1104             throw new IllegalStateException(
1105                 "getLatitudeUncertainty(): invoked on an invalid result: mIsLciValid = false.");
1106         }
1107         return mAltitudeType;
1108     }
1109 
1110     /**
1111      * @return the Altitude uncertainty in meters.
1112      * <p>
1113      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1114      * </p>
1115      * <p>An unknown uncertainty is indicated by 0.</p>
1116      */
getAltitudeUncertainty()1117     public double getAltitudeUncertainty() {
1118         if (!mIsLciValid) {
1119             throw new IllegalStateException(
1120                 "getLatitudeUncertainty(): invoked on an invalid result: mIsLciValid = false.");
1121         }
1122         return mAltitudeUncertainty;
1123     }
1124 
1125     /**
1126      * @return the Altitude in units defined by the altitude type.
1127      * <p>
1128      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1129      */
getAltitude()1130     public double getAltitude() {
1131         if (!mIsLciValid) {
1132             throw new IllegalStateException(
1133                 "getAltitude(): invoked on an invalid result: mIsLciValid = false.");
1134         }
1135         return mAltitude;
1136     }
1137 
1138     /**
1139      * @return the Datum used for the LCI positioning information.
1140      * <p>
1141      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1142      */
1143     @DatumType
getDatum()1144     public int getDatum() {
1145         if (!mIsLciValid) {
1146             throw new IllegalStateException(
1147                 "getDatum(): invoked on an invalid result: mIsLciValid = false.");
1148         }
1149         return mDatum;
1150     }
1151 
1152     /**
1153      * @return true if the station is operating within a national policy area or an international
1154      * agreement area near a national border, otherwise false
1155      * (see 802.11REVmc Section 11.12.3 - Registered STA Operation).
1156      * <p>
1157      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1158      */
getRegisteredLocationAgreementIndication()1159     public boolean getRegisteredLocationAgreementIndication() {
1160         if (!mIsLciValid) {
1161             throw new IllegalStateException(
1162                 "getRegisteredLocationAgreementIndication(): "
1163                         + "invoked on an invalid result: mIsLciValid = false.");
1164         }
1165         return mLciRegisteredLocationAgreement;
1166     }
1167 
1168     /**
1169      * @return true indicating this is an enabling station, enabling the operation of nearby STAs
1170      * with Dynamic Station Enablement (DSE), otherwise false.
1171      * (see 802.11REVmc Section 11.12.3 - Registered STA Operation).
1172      * <p>
1173      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1174      *
1175      * @hide
1176      */
getRegisteredLocationDseIndication()1177     public boolean getRegisteredLocationDseIndication() {
1178         if (!mIsLciValid) {
1179             throw new IllegalStateException(
1180                 "getRegisteredLocationDseIndication(): "
1181                     + "invoked on an invalid result: mIsLciValid = false.");
1182         }
1183         return mLciRegisteredLocationDse;
1184     }
1185 
1186     /**
1187      * @return true indicating this is a dependent station that is operating with the enablement of
1188      * an enabling station whose LCI is being reported, otherwise false.
1189      * (see 802.11REVmc Section 11.12.3 - Registered STA Operation).
1190      * <p>
1191      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1192      *
1193      * @hide
1194      */
getDependentStationIndication()1195     public boolean getDependentStationIndication() {
1196         if (!mIsLciValid) {
1197             throw new IllegalStateException(
1198                 "getDependentStationIndication(): "
1199                     + "invoked on an invalid result: mIsLciValid = false.");
1200         }
1201         return mLciDependentStation;
1202     }
1203 
1204     /**
1205      * @return a value greater or equal to 1, indicating the current version number
1206      * of the LCI protocol.
1207      * <p>
1208      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1209      */
getLciVersion()1210     public int getLciVersion() {
1211         if (!mIsLciValid) {
1212             throw new IllegalStateException(
1213                 "getLciVersion(): "
1214                     + "invoked on an invalid result: mIsLciValid = false.");
1215         }
1216         return mLciVersion;
1217     }
1218 
1219     /**
1220      * @return the LCI location represented as a {@link Location} object (best effort).
1221      * <p>
1222      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1223      */
1224     @NonNull
toLocation()1225     public Location toLocation() {
1226         if (!mIsLciValid) {
1227             throw new IllegalStateException(
1228                 "toLocation(): "
1229                     + "invoked on an invalid result: mIsLciValid = false.");
1230         }
1231         Location location = new Location(LOCATION_PROVIDER);
1232         location.setLatitude(mLatitude);
1233         location.setLongitude(mLongitude);
1234         location.setAccuracy((float) (mLatitudeUncertainty + mLongitudeUncertainty) / 2);
1235         location.setAltitude(mAltitude);
1236         location.setVerticalAccuracyMeters((float) mAltitudeUncertainty);
1237         location.setTime(System.currentTimeMillis());
1238         return location;
1239     }
1240 
1241     /**
1242      * @return if the Z subelement (containing mobility, Floor, Height above floor) is valid.
1243      */
isZaxisSubelementValid()1244     public boolean isZaxisSubelementValid() {
1245         return mIsZValid;
1246     }
1247 
1248     /**
1249      * @return an integer representing the mobility of the responder.
1250      * <p>
1251      * Only valid if {@link #isZaxisSubelementValid()} returns true, or will throw an exception.
1252      */
1253     @ExpectedToMoveType
getExpectedToMove()1254     public int getExpectedToMove() {
1255         if (!mIsZValid) {
1256             throw new IllegalStateException(
1257                 "getExpectedToMove(): invoked on an invalid result: mIsZValid = false.");
1258         }
1259         return mExpectedToMove;
1260     }
1261 
1262     /**
1263      * @return the Z sub element Floor Number.
1264      * <p>
1265      * Only valid if {@link #isZaxisSubelementValid()} returns true, or will throw an exception.
1266      * </p>
1267      * <p>Note: this number can be positive or negative, with value increments of +/- 1/16 of a
1268      * floor.</p>.
1269      */
getFloorNumber()1270     public double getFloorNumber() {
1271         if (!mIsZValid) {
1272             throw new IllegalStateException(
1273                 "getFloorNumber(): invoked on an invalid result: mIsZValid = false)");
1274         }
1275         return mFloorNumber;
1276     }
1277 
1278     /**
1279      * @return the Z subelement Height above the floor in meters.
1280      * <p>
1281      * Only valid if {@link #isZaxisSubelementValid()} returns true, or will throw an exception.
1282      * </p>
1283      * <p>This value can be positive or negative. </p>
1284      */
getHeightAboveFloorMeters()1285     public double getHeightAboveFloorMeters() {
1286         if (!mIsZValid) {
1287             throw new IllegalStateException(
1288                 "getHeightAboveFloorMeters(): invoked on an invalid result: mIsZValid = false)");
1289         }
1290         return mHeightAboveFloorMeters;
1291     }
1292 
1293     /**
1294      * @return the Z subelement Height above the floor uncertainty in meters.
1295      * <p>
1296      * Only valid if {@link #isZaxisSubelementValid()} returns true, or will throw an exception.
1297      * </p>
1298      * <p>An unknown uncertainty is indicated by 0.</p>
1299      */
getHeightAboveFloorUncertaintyMeters()1300     public double getHeightAboveFloorUncertaintyMeters() {
1301         if (!mIsZValid) {
1302             throw new IllegalStateException(
1303                 "getHeightAboveFloorUncertaintyMeters():"
1304                     + "invoked on an invalid result: mIsZValid = false)");
1305         }
1306         return mHeightAboveFloorUncertaintyMeters;
1307     }
1308 
1309     /**
1310      * @return true if the location information received from the responder can be
1311      * retransmitted to another device, physically separate from the one that received it.
1312      *
1313      * @hide
1314      */
getRetransmitPolicyIndication()1315     public boolean getRetransmitPolicyIndication() {
1316         return mUsageRetransmit;
1317     }
1318 
1319     /**
1320      * @return true if location-data received should expire (and be deleted)
1321      * by the time provided in the getRelativeExpirationTimeHours() method.
1322      *
1323      * @hide
1324      */
getRetentionExpiresIndication()1325     public boolean getRetentionExpiresIndication() {
1326         return mUsageRetentionExpires;
1327     }
1328 
1329     /**
1330      * @return true if there is extra location info available on association.
1331      *
1332      * @hide
1333      */
1334     @SystemApi
getExtraInfoOnAssociationIndication()1335     public boolean getExtraInfoOnAssociationIndication() {
1336         return mUsageExtraInfoOnAssociation;
1337     }
1338 
1339     /**
1340      * @return the Immutable list of colocated BSSIDs at the responder.
1341      *
1342      * <p> Will return an empty list when there are no bssids listed.
1343      */
getColocatedBssids()1344     public List<MacAddress> getColocatedBssids() {
1345         return Collections.unmodifiableList(mBssidList);
1346     }
1347 
1348     /**
1349      * @return the civic location represented as an {@link Address} object (best effort).
1350      *
1351      * <p> Will return a {@code null} when there is no Civic Location defined.
1352      */
1353     @Nullable
toCivicLocationAddress()1354     public Address toCivicLocationAddress() {
1355         if (mCivicLocation != null && mCivicLocation.isValid()) {
1356             return mCivicLocation.toAddress();
1357         } else {
1358             return null;
1359         }
1360     }
1361 
1362     /**
1363      * @return the civic location represented as a {@link SparseArray}
1364      * <p>
1365      * Valid keys to access the SparseArray can be found in {@code CivicLocationKeys}.
1366      * </p>
1367      * <p> Will return a {@code null} when there is no Civic Location defined.
1368      *
1369      */
1370     @Nullable
1371     @SuppressLint("ChangedType")
toCivicLocationSparseArray()1372     public SparseArray<String> toCivicLocationSparseArray() {
1373         if (mCivicLocation != null && mCivicLocation.isValid()) {
1374             return mCivicLocation.toSparseArray();
1375         } else {
1376             return null;
1377         }
1378     }
1379 
1380     /**
1381      * @return the civic location two upper-case ASCII character country code defined in ISO 3166.
1382      *
1383      * <p> Will return a {@code null} when there is no country code defined.
1384      *
1385      * @hide
1386      */
1387     @Nullable
getCivicLocationCountryCode()1388     public String getCivicLocationCountryCode() {
1389         return mCivicLocationCountryCode;
1390     }
1391 
1392     /**
1393      * @return the value of the Civic Location String associated with a key.
1394      *
1395      * <p> Will return a {@code null} when there is no value associated with the key provided.
1396      *
1397      * @param key used to find a corresponding value in the Civic Location Tuple list
1398      *
1399      * @hide
1400      */
1401     @Nullable
getCivicLocationElementValue(@ivicLocationKeysType int key)1402     public String getCivicLocationElementValue(@CivicLocationKeysType int key) {
1403         return mCivicLocation.getCivicElementValue(key);
1404     }
1405 
1406     /**
1407      * @return the Map Image file Mime type, referred to by getMapImageUrl().
1408      */
1409     @Nullable
getMapImageMimeType()1410     public String getMapImageMimeType() {
1411         if (mMapImageUri == null) {
1412             return null;
1413         } else {
1414             return imageTypeToMime(mMapImageType, mMapImageUri.toString());
1415         }
1416     }
1417 
1418     /**
1419      * @return a URI referencing a map-file showing the local floor plan.
1420      *
1421      * <p> Will return a {@code null} when there is no URI defined.
1422      */
1423     @Nullable
getMapImageUri()1424     public Uri getMapImageUri() {
1425         return mMapImageUri;
1426     }
1427 }
1428