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         // Ignore variable field measurement token number check, which is the first byte
484         for (int i = 1; i < leadBufferBytes.length; i++) {
485             if (leadBufferBytes[i] != expectedLeadBytes[i]) {
486                 return false;
487             }
488         }
489 
490         // Iterate through the sub-elements contained in the Information Element (IE)
491         bufferPtr += expectedLeadBytes.length;
492         // Loop over the buffer ensuring there are 2-bytes available for each new subelement tested.
493         while (bufferPtr + 1 < bufferLength) {
494             byte subelement = buffer[bufferPtr++];
495             int subelementLength = buffer[bufferPtr++];
496             // Check there is enough data for the next subelement
497             if ((bufferPtr + subelementLength) > bufferLength || subelementLength <= 0) {
498                 return false;
499             }
500 
501             byte[] subelementData =
502                     Arrays.copyOfRange(buffer, bufferPtr, bufferPtr + subelementLength);
503 
504             if (ieType == MEASUREMENT_TYPE_LCI) {
505                 switch (subelement) {
506                     case SUBELEMENT_LCI:
507                         mIsLciValid = parseSubelementLci(subelementData);
508                         if (!mIsLciValid || mLciVersion != LCI_VERSION_1) {
509                             setLciSubelementDefaults();
510                         }
511                         break;
512                     case SUBELEMENT_Z:
513                         mIsZValid = parseSubelementZ(subelementData);
514                         if (!mIsZValid) {
515                             setZaxisSubelementDefaults();
516                         }
517                         break;
518                     case SUBELEMENT_USAGE:
519                         mIsUsageValid = parseSubelementUsage(subelementData);
520                         // Note: if the Usage Subelement is not valid, don't reset the state, as
521                         // it is now indicating the whole ResponderLocation is invalid.
522                         break;
523                     case SUBELEMENT_BSSID_LIST:
524                         mIsBssidListValid = parseSubelementBssidList(subelementData);
525                         if (!mIsBssidListValid) {
526                             setBssidListSubelementDefaults();
527                         }
528                         break;
529                     default:
530                         break; // skip over unused or vendor specific subelements
531                 }
532             } else if (ieType == MEASUREMENT_TYPE_LCR) {
533                 switch (subelement) {
534                     case SUBELEMENT_LOCATION_CIVIC:
535                         mIsLocationCivicValid = parseSubelementLocationCivic(subelementData);
536                         if (!mIsLocationCivicValid) {
537                             setCivicLocationSubelementDefaults();
538                         }
539                         break;
540                     case SUBELEMENT_MAP_IMAGE:
541                         mIsMapImageValid = parseSubelementMapImage(subelementData);
542                         if (!mIsMapImageValid) {
543                             setMapImageSubelementDefaults();
544                         }
545                         break;
546                     default:
547                         break; // skip over unused or other vendor specific subelements
548                 }
549             }
550 
551             bufferPtr += subelementLength;
552         }
553         return true;
554     }
555 
556     /**
557      * Parse the LCI Sub-Element in the LCI Information Element (IE).
558      *
559      * @param buffer a buffer containing the subelement
560      * @return boolean true indicates success
561      */
parseSubelementLci(byte[] buffer)562     private boolean parseSubelementLci(byte[] buffer) {
563         if (buffer.length > SUBELEMENT_LCI_LENGTH) {
564             return false;
565         }
566         swapEndianByteByByte(buffer);
567         long[] subelementLciFields = getFieldData(buffer, SUBELEMENT_LCI_BIT_FIELD_LENGTHS);
568         if (subelementLciFields == null) {
569             return false;
570         }
571         // Set member state based on parsed buffer data
572         mLatitudeUncertainty = decodeLciLatLngUncertainty(
573                 subelementLciFields[SUBELEMENT_LCI_LAT_UNCERTAINTY_INDEX]);
574         mLatitude = decodeLciLatLng(subelementLciFields, SUBELEMENT_LCI_BIT_FIELD_LENGTHS,
575                 SUBELEMENT_LCI_LAT_INDEX, LAT_ABS_LIMIT);
576         mLongitudeUncertainty = decodeLciLatLngUncertainty(
577                 subelementLciFields[SUBELEMENT_LCI_LNG_UNCERTAINTY_INDEX]);
578         mLongitude =
579                 decodeLciLatLng(subelementLciFields, SUBELEMENT_LCI_BIT_FIELD_LENGTHS,
580                         SUBELEMENT_LCI_LNG_INDEX, LNG_ABS_LIMIT);
581         mAltitudeType = (int) subelementLciFields[SUBELEMENT_LCI_ALT_TYPE_INDEX] & BYTE_MASK;
582         mAltitudeUncertainty =
583                 decodeLciAltUncertainty(subelementLciFields[SUBELEMENT_LCI_ALT_UNCERTAINTY_INDEX]);
584         mAltitude =
585                 Math.scalb(subelementLciFields[SUBELEMENT_LCI_ALT_INDEX], -ALTITUDE_FRACTION_BITS);
586         mDatum = (int) subelementLciFields[SUBELEMENT_LCI_DATUM_INDEX] & BYTE_MASK;
587         mLciRegisteredLocationAgreement =
588                 (subelementLciFields[SUBELEMENT_LCI_REGLOC_AGREEMENT_INDEX] == 1);
589         mLciRegisteredLocationDse =
590                 (subelementLciFields[SUBELEMENT_LCI_REGLOC_DSE_INDEX] == 1);
591         mLciDependentStation =
592                 (subelementLciFields[SUBELEMENT_LCI_DEPENDENT_STA_INDEX] == 1);
593         mLciVersion = (int) subelementLciFields[SUBELEMENT_LCI_VERSION_INDEX];
594         return true;
595     }
596 
597     /**
598      * Decode the floating point value of an encoded lat or lng in the LCI subelement field.
599      *
600      * @param fields        the array of field data represented as longs
601      * @param bitFieldSizes the lengths of each field
602      * @param offset        the offset of the field being decoded
603      * @param limit the maximum absolute value (note: different for lat vs lng)
604      * @return the floating point value of the lat or lng
605      */
decodeLciLatLng(long[] fields, int[] bitFieldSizes, int offset, double limit)606     private double decodeLciLatLng(long[] fields, int[] bitFieldSizes, int offset, double limit) {
607         double angle;
608         if ((fields[offset] & (long) Math.pow(2, bitFieldSizes[offset] - 1)) != 0) {
609             // Negative 2's complement value
610             // Note: The Math.pow(...) method cannot return a NaN value because the bitFieldSize
611             // for Lat or Lng is limited to exactly 34 bits.
612             angle = Math.scalb((double) fields[offset] - Math.pow(2, bitFieldSizes[offset]),
613                     -LATLNG_FRACTION_BITS);
614         } else {
615             // Positive 2's complement value
616             angle = Math.scalb((double) fields[offset], -LATLNG_FRACTION_BITS);
617         }
618         if (angle > limit) {
619             angle = limit;
620         } else if (angle < -limit) {
621             angle = -limit;
622         }
623         return angle;
624     }
625 
626     /**
627      * Coverts an encoded Lat/Lng uncertainty into a number of degrees.
628      *
629      * @param encodedValue the encoded uncertainty
630      * @return the value in degrees
631      */
decodeLciLatLngUncertainty(long encodedValue)632     private double decodeLciLatLngUncertainty(long encodedValue) {
633         return Math.pow(2, LATLNG_UNCERTAINTY_BASE - encodedValue);
634     }
635 
636     /**
637      * Converts an encoded Alt uncertainty into a number of degrees.
638      *
639      * @param encodedValue the encoded uncertainty
640      * @return the value in degrees
641      */
decodeLciAltUncertainty(long encodedValue)642     private double decodeLciAltUncertainty(long encodedValue) {
643         return Math.pow(2, ALTITUDE_UNCERTAINTY_BASE - encodedValue);
644     }
645 
646     /**
647      * Parse the Z subelement of the LCI IE.
648      *
649      * @param buffer a buffer containing the subelement
650      * @return boolean true indicates success
651      */
parseSubelementZ(byte[] buffer)652     private boolean parseSubelementZ(byte[] buffer) {
653         if (buffer.length != SUBELEMENT_Z_LENGTH) {
654             return false;
655         }
656         swapEndianByteByByte(buffer);
657         long[] subelementZFields = getFieldData(buffer, SUBELEMENT_Z_BIT_FIELD_LENGTHS);
658         if (subelementZFields == null) {
659             return false;
660         }
661 
662         mExpectedToMove =
663                 (int) subelementZFields[SUBELEMENT_Z_LAT_EXPECTED_TO_MOVE_INDEX] & BYTE_MASK;
664         mFloorNumber = decodeZUnsignedToSignedValue(subelementZFields,
665                 SUBELEMENT_Z_BIT_FIELD_LENGTHS, SUBELEMENT_Z_FLOOR_NUMBER_INDEX,
666                 Z_FLOOR_NUMBER_FRACTION_BITS);
667 
668         mHeightAboveFloorMeters = decodeZUnsignedToSignedValue(subelementZFields,
669                 SUBELEMENT_Z_BIT_FIELD_LENGTHS, SUBELEMENT_Z_HEIGHT_ABOVE_FLOOR_INDEX,
670                 Z_FLOOR_HEIGHT_FRACTION_BITS);
671 
672         long zHeightUncertainty =
673                 subelementZFields[SUBELEMENT_Z_HEIGHT_ABOVE_FLOOR_UNCERTAINTY_INDEX];
674         if (zHeightUncertainty >= 0 && zHeightUncertainty < Z_MAX_HEIGHT_UNCERTAINTY_FACTOR) {
675             mHeightAboveFloorUncertaintyMeters = zHeightUncertainty == 0
676                     ? 0 : Math.pow(2, Z_FLOOR_HEIGHT_FRACTION_BITS - zHeightUncertainty - 1);
677         } else {
678             return false;
679         }
680         return true;
681     }
682 
683     /**
684      * Decode a two's complement encoded value, to a signed double based on the field length.
685      *
686      * @param fieldValues the array of field values reprented as longs
687      * @param fieldLengths the array of field lengths
688      * @param index the index of the field being decoded
689      * @param fraction the number of fractional bits in the value
690      * @return the signed value represented as a double
691      */
decodeZUnsignedToSignedValue(long[] fieldValues, int[] fieldLengths, int index, int fraction)692     private double decodeZUnsignedToSignedValue(long[] fieldValues, int[] fieldLengths, int index,
693             int fraction) {
694         int value = (int) fieldValues[index];
695         int maxPositiveValue = (int) Math.pow(2, fieldLengths[index] - 1) - 1;
696         if (value > maxPositiveValue) {
697             value -= Math.pow(2, fieldLengths[index]);
698         }
699         return Math.scalb(value, -fraction);
700     }
701 
702     /**
703      * Parse Subelement Usage Rights
704      */
parseSubelementUsage(byte[] buffer)705     private boolean parseSubelementUsage(byte[] buffer) {
706         if (buffer.length != SUBELEMENT_USAGE_LENGTH1
707                 && buffer.length != SUBELEMENT_USAGE_LENGTH3) {
708             return false;
709         }
710         mUsageRetransmit =
711                 (buffer[SUBELEMENT_USAGE_PARAMS_INDEX] & SUBELEMENT_USAGE_MASK_RETRANSMIT) != 0;
712         mUsageRetentionExpires =
713                 (buffer[SUBELEMENT_USAGE_PARAMS_INDEX] & SUBELEMENT_USAGE_MASK_RETENTION_EXPIRES)
714                         != 0;
715         mUsageExtraInfoOnAssociation =
716                 (buffer[SUBELEMENT_USAGE_PARAMS_INDEX] & SUBELEMENT_USAGE_MASK_STA_LOCATION_POLICY)
717                         != 0;
718         // Note: the Retransmit flag must be true, and RetentionExpires, false for the
719         // ResponderLocation object to be usable by public applications.
720         return (mUsageRetransmit && !mUsageRetentionExpires);
721     }
722 
723     /**
724      * Parse the BSSID List Subelement of the LCI IE.
725      *
726      * @param buffer a buffer containing the subelement
727      * @return boolean true indicates success
728      */
parseSubelementBssidList(byte[] buffer)729     private boolean parseSubelementBssidList(byte[] buffer) {
730         if (buffer.length < SUBELEMENT_BSSID_LIST_MIN_BUFFER_LENGTH) {
731             return false;
732         }
733         if ((buffer.length - 1) % BYTES_IN_A_BSSID != 0) {
734             return false;
735         }
736 
737         int maxBssidIndicator = (int) buffer[SUBELEMENT_BSSID_MAX_INDICATOR_INDEX] & BYTE_MASK;
738         int bssidListLength = (buffer.length - 1) / BYTES_IN_A_BSSID;
739         // The maxBSSIDIndicator is ignored. Its use is still being clarified in 802.11REVmd,
740         // which is not published at this time. This field will be used in a future
741         // release of Android after 802.11REVmd is public. Here, we interpret the following
742         // params as an explicit list of BSSIDs.
743 
744 
745         int bssidOffset = SUBELEMENT_BSSID_LIST_INDEX;
746         for (int i = 0; i < bssidListLength; i++) {
747             byte[] bssid = Arrays.copyOfRange(buffer, bssidOffset, bssidOffset + BYTES_IN_A_BSSID);
748             MacAddress macAddress = MacAddress.fromBytes(bssid);
749             mBssidList.add(macAddress);
750             bssidOffset += BYTES_IN_A_BSSID;
751         }
752         return true;
753     }
754 
755     /**
756      * Parse the Location Civic subelement in the LCR IE.
757      *
758      * @param buffer a buffer containing the subelement
759      * @return boolean true indicates success
760      */
parseSubelementLocationCivic(byte[] buffer)761     private boolean parseSubelementLocationCivic(byte[] buffer) {
762         if (buffer.length <  SUBELEMENT_LOCATION_CIVIC_MIN_LENGTH
763                 || buffer.length > SUBELEMENT_LOCATION_CIVIC_MAX_LENGTH) {
764             return false;
765         }
766         mCivicLocationCountryCode =
767                 new String(Arrays.copyOfRange(buffer, CIVIC_COUNTRY_CODE_INDEX,
768                         CIVIC_TLV_LIST_INDEX)).toUpperCase();
769         CivicLocation civicLocation =
770                 new CivicLocation(
771                         Arrays.copyOfRange(buffer, CIVIC_TLV_LIST_INDEX, buffer.length),
772                         mCivicLocationCountryCode);
773         if (!civicLocation.isValid()) {
774             return false;
775         }
776         this.mCivicLocation = civicLocation;
777         mCivicLocationString = civicLocation.toString();
778         return true;
779     }
780 
781     /**
782      * Parse the Map Image subelement in the LCR IE.
783      *
784      * @param buffer a buffer containing the subelement
785      * @return boolean true indicates success
786      */
parseSubelementMapImage(byte[] buffer)787     private boolean parseSubelementMapImage(byte[] buffer) {
788         if (buffer.length > SUBELEMENT_MAP_IMAGE_URL_MAX_LENGTH) {
789             return false;
790         }
791         int mapImageType = buffer[SUBELEMENT_IMAGE_MAP_TYPE_INDEX];
792         int supportedTypesMax = SUPPORTED_IMAGE_FILE_EXTENSIONS.length - 1;
793         if (mapImageType < MAP_TYPE_URL_DEFINED || mapImageType > supportedTypesMax) {
794             return false;
795         }
796         this.mMapImageType = mapImageType;
797         byte[] urlBytes = Arrays.copyOfRange(buffer, 1, buffer.length);
798         mMapImageUri = Uri.parse(new String(urlBytes, StandardCharsets.UTF_8));
799         return true;
800     }
801 
802     /**
803      * Convert an image type code to a Mime type
804      *
805      * @param imageTypeCode encoded as an integer
806      * @return the mime type of the image file
807      */
imageTypeToMime(int imageTypeCode, String imageUrl)808     private String imageTypeToMime(int imageTypeCode, String imageUrl) {
809         int supportedExtensionsMax = SUPPORTED_IMAGE_FILE_EXTENSIONS.length - 1;
810         if ((imageTypeCode == 0 && imageUrl == null) || imageTypeCode > supportedExtensionsMax) {
811             return null;
812         }
813         MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton();
814         if (imageTypeCode == 0) {
815             return mimeTypeMap.getMimeTypeFromExtension(
816                     MimeTypeMap.getFileExtensionFromUrl(imageUrl));
817         } else {
818             return mimeTypeMap.getMimeTypeFromExtension(
819                     SUPPORTED_IMAGE_FILE_EXTENSIONS[imageTypeCode]);
820         }
821     }
822 
823     /**
824      * Converts a byte array containing fields of variable size, into an array of longs where the
825      * field boundaries are defined in a constant int array passed as an argument.
826      *
827      * @param buffer        the byte array containing all the fields
828      * @param bitFieldSizes the int array defining the size of each field
829      */
getFieldData(byte[] buffer, int[] bitFieldSizes)830     private long[] getFieldData(byte[] buffer, int[] bitFieldSizes) {
831         int bufferLengthBits = buffer.length * Byte.SIZE;
832         int sumBitFieldSizes = 0;
833         for (int i : bitFieldSizes) {
834             if (i > Long.SIZE) {
835                 return null;
836             }
837             sumBitFieldSizes += i;
838         }
839         if (bufferLengthBits != sumBitFieldSizes) {
840             return null;
841         }
842         long[] fieldData = new long[bitFieldSizes.length];
843         int bufferBitPos = 0;
844         for (int fieldIndex = 0; fieldIndex < bitFieldSizes.length; fieldIndex++) {
845             int bitFieldSize = bitFieldSizes[fieldIndex];
846             long field = 0;
847             for (int n = 0; n < bitFieldSize; n++) {
848                 field |= ((long) getBitAtBitOffsetInByteArray(buffer, bufferBitPos + n) << n);
849             }
850             fieldData[fieldIndex] = field;
851             bufferBitPos += bitFieldSize;
852         }
853         return fieldData;
854     }
855 
856     /**
857      * Retrieves state of a bit at the bit-offset in a byte array, where the offset represents the
858      * bytes in contiguous data with each value in big endian order.
859      *
860      * @param buffer          the data buffer of bytes containing all the fields
861      * @param bufferBitOffset the bit offset into the entire buffer
862      * @return a zero or one value, representing the state of that bit.
863      */
getBitAtBitOffsetInByteArray(byte[] buffer, int bufferBitOffset)864     private int getBitAtBitOffsetInByteArray(byte[] buffer, int bufferBitOffset) {
865         int bufferIndex = bufferBitOffset / Byte.SIZE; // The byte index that contains the bit
866         int bitOffsetInByte = bufferBitOffset % Byte.SIZE; // The bit offset within that byte
867         int result = (buffer[bufferIndex] & (MSB_IN_BYTE >> bitOffsetInByte)) == 0 ? 0 : 1;
868         return result;
869     }
870 
871     /**
872      * Reverses the order of the bits in each byte of a byte array.
873      *
874      * @param buffer the array containing each byte that will be reversed
875      */
swapEndianByteByByte(byte[] buffer)876     private void swapEndianByteByByte(byte[] buffer) {
877         for (int n = 0; n < buffer.length; n++) {
878             byte currentByte = buffer[n];
879             byte reversedByte = 0; // Cleared value
880             byte bitSelectorMask = LSB_IN_BYTE;
881             for (int i = 0; i < Byte.SIZE; i++) {
882                 reversedByte = (byte) (reversedByte << 1);
883                 if ((currentByte & bitSelectorMask) != 0) {
884                     reversedByte = (byte) (reversedByte | LSB_IN_BYTE);
885                 }
886                 bitSelectorMask = (byte) (bitSelectorMask << 1);
887             }
888             buffer[n] = reversedByte;
889         }
890     }
891 
892     /**
893      * Sets the LCI subelement fields to the default undefined values.
894      */
setLciSubelementDefaults()895     private void setLciSubelementDefaults() {
896         mIsLciValid = false;
897         mLatitudeUncertainty = UNCERTAINTY_UNDEFINED;
898         mLatitude = 0;
899         mLongitudeUncertainty = UNCERTAINTY_UNDEFINED;
900         mLongitude = 0;
901         mAltitudeType = ALTITUDE_UNDEFINED;
902         mAltitudeUncertainty = UNCERTAINTY_UNDEFINED;
903         mAltitude = 0;
904         mDatum = DATUM_UNDEFINED;
905         mLciRegisteredLocationAgreement = false;
906         mLciRegisteredLocationDse = false;
907         mLciDependentStation = false;
908         mLciVersion = 0;
909     }
910 
911     /**
912      * Sets the Z subelement fields to the default values when undefined.
913      */
setZaxisSubelementDefaults()914     private void setZaxisSubelementDefaults() {
915         mIsZValid = false;
916         mExpectedToMove = 0;
917         mFloorNumber = 0;
918         mHeightAboveFloorMeters = 0;
919         mHeightAboveFloorUncertaintyMeters = 0;
920     }
921 
922     /**
923      * Sets the Usage Policy subelement fields to the default undefined values.
924      */
setUsageSubelementDefaults()925     private void setUsageSubelementDefaults() {
926         mUsageRetransmit = true;
927         mUsageRetentionExpires = false;
928         mUsageExtraInfoOnAssociation = false;
929     }
930 
931     /**
932      * Sets the BSSID List subelement fields to the default values when undefined.
933      */
setBssidListSubelementDefaults()934     private void setBssidListSubelementDefaults() {
935         mIsBssidListValid = false;
936         mBssidList = new ArrayList<>();
937     }
938 
939     /**
940      * Sets the LCR Civic Location subelement field to the default undefined value.
941      *
942      * @hide
943      */
setCivicLocationSubelementDefaults()944     public void setCivicLocationSubelementDefaults() {
945         mIsLocationCivicValid = false;
946         mCivicLocationCountryCode = "";
947         mCivicLocationString = "";
948         mCivicLocation = null;
949     }
950 
951     /**
952      * Sets the LCR Map Image subelement field to the default values when undefined.
953      */
setMapImageSubelementDefaults()954     private void setMapImageSubelementDefaults() {
955         mIsMapImageValid = false;
956         mMapImageType = MAP_TYPE_URL_DEFINED;
957         mMapImageUri = null;
958     }
959 
960     @Override
equals(Object obj)961     public boolean equals(Object obj) {
962         if (this == obj) {
963             return true;
964         }
965         if (obj == null || getClass() != obj.getClass()) {
966             return false;
967         }
968         ResponderLocation other = (ResponderLocation) obj;
969         return mIsValid == other.mIsValid
970                 && mIsLciValid == other.mIsLciValid
971                 && mIsZValid == other.mIsZValid
972                 && mIsUsageValid == other.mIsUsageValid
973                 && mIsBssidListValid == other.mIsBssidListValid
974                 && mIsLocationCivicValid == other.mIsLocationCivicValid
975                 && mIsMapImageValid == other.mIsMapImageValid
976                 && mLatitudeUncertainty == other.mLatitudeUncertainty
977                 && mLatitude == other.mLatitude
978                 && mLongitudeUncertainty == other.mLongitudeUncertainty
979                 && mLongitude == other.mLongitude
980                 && mAltitudeType == other.mAltitudeType
981                 && mAltitudeUncertainty == other.mAltitudeUncertainty
982                 && mAltitude == other.mAltitude
983                 && mDatum == other.mDatum
984                 && mLciRegisteredLocationAgreement == other.mLciRegisteredLocationAgreement
985                 && mLciRegisteredLocationDse == other.mLciRegisteredLocationDse
986                 && mLciDependentStation == other.mLciDependentStation
987                 && mLciVersion == other.mLciVersion
988                 && mExpectedToMove == other.mExpectedToMove
989                 && mFloorNumber == other.mFloorNumber
990                 && mHeightAboveFloorMeters == other.mHeightAboveFloorMeters
991                 && mHeightAboveFloorUncertaintyMeters
992                         == other.mHeightAboveFloorUncertaintyMeters
993                 && mUsageRetransmit == other.mUsageRetransmit
994                 && mUsageRetentionExpires == other.mUsageRetentionExpires
995                 && mUsageExtraInfoOnAssociation == other.mUsageExtraInfoOnAssociation
996                 && mBssidList.equals(other.mBssidList)
997                 && mCivicLocationCountryCode.equals(other.mCivicLocationCountryCode)
998                 && mCivicLocationString.equals(other.mCivicLocationString)
999                 && Objects.equals(mCivicLocation, other.mCivicLocation)
1000                 && mMapImageType == other.mMapImageType
1001                 && Objects.equals(mMapImageUri, other.mMapImageUri);
1002     }
1003 
1004     @Override
hashCode()1005     public int hashCode() {
1006         return Objects.hash(mIsValid, mIsLciValid, mIsZValid, mIsUsageValid, mIsBssidListValid,
1007                 mIsLocationCivicValid, mIsMapImageValid, mLatitudeUncertainty, mLatitude,
1008                 mLongitudeUncertainty, mLongitude, mAltitudeType, mAltitudeUncertainty, mAltitude,
1009                 mDatum, mLciRegisteredLocationAgreement,
1010                 mLciRegisteredLocationDse, mLciDependentStation, mLciVersion,
1011                 mExpectedToMove, mFloorNumber, mHeightAboveFloorMeters,
1012                 mHeightAboveFloorUncertaintyMeters, mUsageRetransmit, mUsageRetentionExpires,
1013                 mUsageExtraInfoOnAssociation, mBssidList, mCivicLocationCountryCode,
1014                 mCivicLocationString, mCivicLocation, mMapImageType, mMapImageUri);
1015     }
1016 
1017     /**
1018      * @return true if the ResponderLocation object is valid and contains useful information
1019      * relevant to the location of the Responder. If this is ever false, this object will not be
1020      * available to developers, and have a null value.
1021      *
1022      * @hide
1023      */
isValid()1024     public boolean isValid() {
1025         return mIsValid;
1026     }
1027 
1028     /**
1029      * @return true if the LCI subelement (containing Latitude, Longitude and Altitude) is valid.
1030      *
1031      * <p> This method tells us if the responder has provided its Location Configuration
1032      * Information (LCI) directly, and is useful when an external database of responder locations
1033      * is not available</p>
1034      *
1035      * <p>If isLciSubelementValid() returns true, all the LCI values provided by the corresponding
1036      * getter methods will have been set as described by the responder, or else if false, they
1037      * should not be used and will throw an IllegalStateException.</p>
1038      */
isLciSubelementValid()1039     public boolean isLciSubelementValid() {
1040         return mIsLciValid;
1041     }
1042 
1043     /**
1044      * @return the latitude uncertainty in degrees.
1045      * <p>
1046      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1047      * </p>
1048      * <p> An unknown uncertainty is indicated by 0.</p>
1049      */
getLatitudeUncertainty()1050     public double getLatitudeUncertainty() {
1051         if (!mIsLciValid) {
1052             throw new IllegalStateException(
1053                 "getLatitudeUncertainty(): invoked on an invalid result: mIsLciValid = false.");
1054         }
1055         return mLatitudeUncertainty;
1056     }
1057 
1058     /**
1059      * @return the latitude in degrees
1060      * <p>
1061      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1062      */
getLatitude()1063     public double getLatitude() {
1064         if (!mIsLciValid) {
1065             throw new IllegalStateException(
1066                 "getLatitude(): invoked on an invalid result: mIsLciValid = false.");
1067         }
1068         return mLatitude;
1069     }
1070 
1071     /**
1072      * @return the Longitude uncertainty in degrees.
1073      * <p>
1074      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1075      * </p>
1076      * <p> An unknown uncertainty is indicated by 0.</p>
1077      */
getLongitudeUncertainty()1078     public double getLongitudeUncertainty() {
1079         if (!mIsLciValid) {
1080             throw new IllegalStateException(
1081                 "getLongitudeUncertainty(): invoked on an invalid result: mIsLciValid = false.");
1082         }
1083         return mLongitudeUncertainty;
1084     }
1085 
1086     /**
1087      * @return the Longitude in degrees..
1088      * <p>
1089      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1090      */
getLongitude()1091     public double getLongitude() {
1092         if (!mIsLciValid) {
1093             throw new IllegalStateException(
1094                 "getLatitudeUncertainty(): invoked on an invalid result: mIsLciValid = false.");
1095         }
1096         return mLongitude;
1097     }
1098 
1099     /**
1100      * @return the Altitude type.
1101      * <p>
1102      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1103      */
1104     @AltitudeType
getAltitudeType()1105     public int getAltitudeType() {
1106         if (!mIsLciValid) {
1107             throw new IllegalStateException(
1108                 "getLatitudeUncertainty(): invoked on an invalid result: mIsLciValid = false.");
1109         }
1110         return mAltitudeType;
1111     }
1112 
1113     /**
1114      * @return the Altitude uncertainty in meters.
1115      * <p>
1116      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1117      * </p>
1118      * <p>An unknown uncertainty is indicated by 0.</p>
1119      */
getAltitudeUncertainty()1120     public double getAltitudeUncertainty() {
1121         if (!mIsLciValid) {
1122             throw new IllegalStateException(
1123                 "getLatitudeUncertainty(): invoked on an invalid result: mIsLciValid = false.");
1124         }
1125         return mAltitudeUncertainty;
1126     }
1127 
1128     /**
1129      * @return the Altitude in units defined by the altitude type.
1130      * <p>
1131      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1132      */
getAltitude()1133     public double getAltitude() {
1134         if (!mIsLciValid) {
1135             throw new IllegalStateException(
1136                 "getAltitude(): invoked on an invalid result: mIsLciValid = false.");
1137         }
1138         return mAltitude;
1139     }
1140 
1141     /**
1142      * @return the Datum used for the LCI positioning information.
1143      * <p>
1144      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1145      */
1146     @DatumType
getDatum()1147     public int getDatum() {
1148         if (!mIsLciValid) {
1149             throw new IllegalStateException(
1150                 "getDatum(): invoked on an invalid result: mIsLciValid = false.");
1151         }
1152         return mDatum;
1153     }
1154 
1155     /**
1156      * @return true if the station is operating within a national policy area or an international
1157      * agreement area near a national border, otherwise false
1158      * (see 802.11REVmc Section 11.12.3 - Registered STA Operation).
1159      * <p>
1160      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1161      */
getRegisteredLocationAgreementIndication()1162     public boolean getRegisteredLocationAgreementIndication() {
1163         if (!mIsLciValid) {
1164             throw new IllegalStateException(
1165                 "getRegisteredLocationAgreementIndication(): "
1166                         + "invoked on an invalid result: mIsLciValid = false.");
1167         }
1168         return mLciRegisteredLocationAgreement;
1169     }
1170 
1171     /**
1172      * @return true indicating this is an enabling station, enabling the operation of nearby STAs
1173      * with Dynamic Station Enablement (DSE), otherwise false.
1174      * (see 802.11REVmc Section 11.12.3 - Registered STA Operation).
1175      * <p>
1176      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1177      *
1178      * @hide
1179      */
getRegisteredLocationDseIndication()1180     public boolean getRegisteredLocationDseIndication() {
1181         if (!mIsLciValid) {
1182             throw new IllegalStateException(
1183                 "getRegisteredLocationDseIndication(): "
1184                     + "invoked on an invalid result: mIsLciValid = false.");
1185         }
1186         return mLciRegisteredLocationDse;
1187     }
1188 
1189     /**
1190      * @return true indicating this is a dependent station that is operating with the enablement of
1191      * an enabling station whose LCI is being reported, otherwise false.
1192      * (see 802.11REVmc Section 11.12.3 - Registered STA Operation).
1193      * <p>
1194      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1195      *
1196      * @hide
1197      */
getDependentStationIndication()1198     public boolean getDependentStationIndication() {
1199         if (!mIsLciValid) {
1200             throw new IllegalStateException(
1201                 "getDependentStationIndication(): "
1202                     + "invoked on an invalid result: mIsLciValid = false.");
1203         }
1204         return mLciDependentStation;
1205     }
1206 
1207     /**
1208      * @return a value greater or equal to 1, indicating the current version number
1209      * of the LCI protocol.
1210      * <p>
1211      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1212      */
getLciVersion()1213     public int getLciVersion() {
1214         if (!mIsLciValid) {
1215             throw new IllegalStateException(
1216                 "getLciVersion(): "
1217                     + "invoked on an invalid result: mIsLciValid = false.");
1218         }
1219         return mLciVersion;
1220     }
1221 
1222     /**
1223      * @return the LCI location represented as a {@link Location} object (best effort).
1224      * <p>
1225      * Only valid if {@link #isLciSubelementValid()} returns true, or will throw an exception.
1226      */
1227     @NonNull
toLocation()1228     public Location toLocation() {
1229         if (!mIsLciValid) {
1230             throw new IllegalStateException(
1231                 "toLocation(): "
1232                     + "invoked on an invalid result: mIsLciValid = false.");
1233         }
1234         Location location = new Location(LOCATION_PROVIDER);
1235         location.setLatitude(mLatitude);
1236         location.setLongitude(mLongitude);
1237         location.setAccuracy((float) (mLatitudeUncertainty + mLongitudeUncertainty) / 2);
1238         location.setAltitude(mAltitude);
1239         location.setVerticalAccuracyMeters((float) mAltitudeUncertainty);
1240         location.setTime(System.currentTimeMillis());
1241         return location;
1242     }
1243 
1244     /**
1245      * @return if the Z subelement (containing mobility, Floor, Height above floor) is valid.
1246      */
isZaxisSubelementValid()1247     public boolean isZaxisSubelementValid() {
1248         return mIsZValid;
1249     }
1250 
1251     /**
1252      * @return an integer representing the mobility of the responder.
1253      * <p>
1254      * Only valid if {@link #isZaxisSubelementValid()} returns true, or will throw an exception.
1255      */
1256     @ExpectedToMoveType
getExpectedToMove()1257     public int getExpectedToMove() {
1258         if (!mIsZValid) {
1259             throw new IllegalStateException(
1260                 "getExpectedToMove(): invoked on an invalid result: mIsZValid = false.");
1261         }
1262         return mExpectedToMove;
1263     }
1264 
1265     /**
1266      * @return the Z sub element Floor Number.
1267      * <p>
1268      * Only valid if {@link #isZaxisSubelementValid()} returns true, or will throw an exception.
1269      * </p>
1270      * <p>Note: this number can be positive or negative, with value increments of +/- 1/16 of a
1271      * floor.</p>.
1272      */
getFloorNumber()1273     public double getFloorNumber() {
1274         if (!mIsZValid) {
1275             throw new IllegalStateException(
1276                 "getFloorNumber(): invoked on an invalid result: mIsZValid = false)");
1277         }
1278         return mFloorNumber;
1279     }
1280 
1281     /**
1282      * @return the Z subelement Height above the floor in meters.
1283      * <p>
1284      * Only valid if {@link #isZaxisSubelementValid()} returns true, or will throw an exception.
1285      * </p>
1286      * <p>This value can be positive or negative. </p>
1287      */
getHeightAboveFloorMeters()1288     public double getHeightAboveFloorMeters() {
1289         if (!mIsZValid) {
1290             throw new IllegalStateException(
1291                 "getHeightAboveFloorMeters(): invoked on an invalid result: mIsZValid = false)");
1292         }
1293         return mHeightAboveFloorMeters;
1294     }
1295 
1296     /**
1297      * @return the Z subelement Height above the floor uncertainty in meters.
1298      * <p>
1299      * Only valid if {@link #isZaxisSubelementValid()} returns true, or will throw an exception.
1300      * </p>
1301      * <p>An unknown uncertainty is indicated by 0.</p>
1302      */
getHeightAboveFloorUncertaintyMeters()1303     public double getHeightAboveFloorUncertaintyMeters() {
1304         if (!mIsZValid) {
1305             throw new IllegalStateException(
1306                 "getHeightAboveFloorUncertaintyMeters():"
1307                     + "invoked on an invalid result: mIsZValid = false)");
1308         }
1309         return mHeightAboveFloorUncertaintyMeters;
1310     }
1311 
1312     /**
1313      * @return true if the location information received from the responder can be
1314      * retransmitted to another device, physically separate from the one that received it.
1315      *
1316      * @hide
1317      */
getRetransmitPolicyIndication()1318     public boolean getRetransmitPolicyIndication() {
1319         return mUsageRetransmit;
1320     }
1321 
1322     /**
1323      * @return true if location-data received should expire (and be deleted)
1324      * by the time provided in the getRelativeExpirationTimeHours() method.
1325      *
1326      * @hide
1327      */
getRetentionExpiresIndication()1328     public boolean getRetentionExpiresIndication() {
1329         return mUsageRetentionExpires;
1330     }
1331 
1332     /**
1333      * @return true if there is extra location info available on association.
1334      *
1335      * @hide
1336      */
1337     @SystemApi
getExtraInfoOnAssociationIndication()1338     public boolean getExtraInfoOnAssociationIndication() {
1339         return mUsageExtraInfoOnAssociation;
1340     }
1341 
1342     /**
1343      * @return the Immutable list of colocated BSSIDs at the responder.
1344      *
1345      * <p> Will return an empty list when there are no bssids listed.
1346      */
getColocatedBssids()1347     public List<MacAddress> getColocatedBssids() {
1348         return Collections.unmodifiableList(mBssidList);
1349     }
1350 
1351     /**
1352      * @return the civic location represented as an {@link Address} object (best effort).
1353      *
1354      * <p> Will return a {@code null} when there is no Civic Location defined.
1355      */
1356     @Nullable
toCivicLocationAddress()1357     public Address toCivicLocationAddress() {
1358         if (mCivicLocation != null && mCivicLocation.isValid()) {
1359             return mCivicLocation.toAddress();
1360         } else {
1361             return null;
1362         }
1363     }
1364 
1365     /**
1366      * @return the civic location represented as a {@link SparseArray}
1367      * <p>
1368      * Valid keys to access the SparseArray can be found in {@code CivicLocationKeys}.
1369      * </p>
1370      * <p> Will return a {@code null} when there is no Civic Location defined.
1371      *
1372      */
1373     @Nullable
1374     @SuppressLint("ChangedType")
toCivicLocationSparseArray()1375     public SparseArray<String> toCivicLocationSparseArray() {
1376         if (mCivicLocation != null && mCivicLocation.isValid()) {
1377             return mCivicLocation.toSparseArray();
1378         } else {
1379             return null;
1380         }
1381     }
1382 
1383     /**
1384      * @return the civic location two upper-case ASCII character country code defined in ISO 3166.
1385      *
1386      * <p> Will return a {@code null} when there is no country code defined.
1387      *
1388      * @hide
1389      */
1390     @Nullable
getCivicLocationCountryCode()1391     public String getCivicLocationCountryCode() {
1392         return mCivicLocationCountryCode;
1393     }
1394 
1395     /**
1396      * @return the value of the Civic Location String associated with a key.
1397      *
1398      * <p> Will return a {@code null} when there is no value associated with the key provided.
1399      *
1400      * @param key used to find a corresponding value in the Civic Location Tuple list
1401      *
1402      * @hide
1403      */
1404     @Nullable
getCivicLocationElementValue(@ivicLocationKeysType int key)1405     public String getCivicLocationElementValue(@CivicLocationKeysType int key) {
1406         return mCivicLocation.getCivicElementValue(key);
1407     }
1408 
1409     /**
1410      * @return the Map Image file Mime type, referred to by getMapImageUrl().
1411      */
1412     @Nullable
getMapImageMimeType()1413     public String getMapImageMimeType() {
1414         if (mMapImageUri == null) {
1415             return null;
1416         } else {
1417             return imageTypeToMime(mMapImageType, mMapImageUri.toString());
1418         }
1419     }
1420 
1421     /**
1422      * @return a URI referencing a map-file showing the local floor plan.
1423      *
1424      * <p> Will return a {@code null} when there is no URI defined.
1425      */
1426     @Nullable
getMapImageUri()1427     public Uri getMapImageUri() {
1428         return mMapImageUri;
1429     }
1430 }
1431