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