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