Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b1772f92 authored by Roy Want's avatar Roy Want
Browse files

RTT LCI API (Client Side)

Bug: 111439464
Test: Tests were added for new classes. All unit tests pass.
Change-Id: I5a644a06c512b98e735fc1acb19e38a33bf4b2ad
parent 8ca3230f
Loading
Loading
Loading
Loading
+59 −0
Original line number Diff line number Diff line
@@ -30576,6 +30576,7 @@ package android.net.wifi.rtt {
    method public int getNumSuccessfulMeasurements();
    method @Nullable public android.net.wifi.aware.PeerHandle getPeerHandle();
    method public long getRangingTimestampMillis();
    method @Nullable public android.net.wifi.rtt.ResponderLocation getResponderLocation();
    method public int getRssi();
    method public int getStatus();
    method public void writeToParcel(android.os.Parcel, int);
@@ -30593,6 +30594,64 @@ package android.net.wifi.rtt {
    field public static final int STATUS_CODE_FAIL_RTT_NOT_AVAILABLE = 2; // 0x2
  }
  public final class ResponderLocation implements android.os.Parcelable {
    method public int describeContents();
    method public double getAltitude();
    method public int getAltitudeType();
    method public double getAltitudeUncertainty();
    method public java.util.List<android.net.MacAddress> getColocatedBssids();
    method public int getDatum();
    method public int getExpectedToMove();
    method public double getLatitude();
    method public double getLatitudeUncertainty();
    method public int getLciFlags();
    method public double getLongitude();
    method public double getLongitudeUncertainty();
    method public int getMapImageType();
    method @Nullable public java.net.URL getMapImageUrl();
    method public double getStaFloorNumber();
    method public double getStaHeightAboveFloorMeters();
    method public double getStaHeightAboveFloorUncertaintyMeters();
    method public boolean isLciSubelementValid();
    method public boolean isZsubelementValid();
    method public void writeToParcel(android.os.Parcel, int);
    field public static final int ALTITUDE_FLOORS = 2; // 0x2
    field public static final int ALTITUDE_METERS = 1; // 0x1
    field public static final int ALTITUDE_UNDEFINED = 0; // 0x0
    field public static final android.os.Parcelable.Creator<android.net.wifi.rtt.ResponderLocation> CREATOR;
    field public static final int DATUM_NAD83_MLLW = 3; // 0x3
    field public static final int DATUM_NAD83_NAV88 = 2; // 0x2
    field public static final int DATUM_UNDEFINED = 0; // 0x0
    field public static final int DATUM_WGS84 = 1; // 0x1
    field public static final int LCI_FLAGS_MASK_DEPENDENT_STA = 4; // 0x4
    field public static final int LCI_FLAGS_MASK_REGLOC_AGREEMENT = 16; // 0x10
    field public static final int LCI_FLAGS_MASK_REGLOC_DSE = 8; // 0x8
    field public static final int LCI_FLAGS_MASK_VERSION = 3; // 0x3
    field public static final int LCI_VERSION_1 = 1; // 0x1
    field public static final int LOCATION_FIXED = 0; // 0x0
    field public static final int LOCATION_MOVEMENT_UNKNOWN = 2; // 0x2
    field public static final int LOCATION_RESERVED = 3; // 0x3
    field public static final int LOCATION_VARIABLE = 1; // 0x1
    field public static final int MAP_TYPE_BMP = 12; // 0xc
    field public static final int MAP_TYPE_CAD = 8; // 0x8
    field public static final int MAP_TYPE_DWF = 7; // 0x7
    field public static final int MAP_TYPE_DWG = 6; // 0x6
    field public static final int MAP_TYPE_DXF = 5; // 0x5
    field public static final int MAP_TYPE_GIF = 2; // 0x2
    field public static final int MAP_TYPE_GML = 10; // 0xa
    field public static final int MAP_TYPE_ICO = 17; // 0x11
    field public static final int MAP_TYPE_JPG = 3; // 0x3
    field public static final int MAP_TYPE_KML = 11; // 0xb
    field public static final int MAP_TYPE_PGM = 13; // 0xd
    field public static final int MAP_TYPE_PNG = 1; // 0x1
    field public static final int MAP_TYPE_PPM = 14; // 0xe
    field public static final int MAP_TYPE_SVG = 4; // 0x4
    field public static final int MAP_TYPE_TIFF = 9; // 0x9
    field public static final int MAP_TYPE_URL_DEFINED = 0; // 0x0
    field public static final int MAP_TYPE_XBM = 15; // 0xf
    field public static final int MAP_TYPE_XPM = 16; // 0x10
  }
  public class WifiRttManager {
    method public boolean isAvailable();
    method @RequiresPermission(allOf={android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_WIFI_STATE}) public void startRanging(@NonNull android.net.wifi.rtt.RangingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.rtt.RangingResultCallback);
+4 −0
Original line number Diff line number Diff line
@@ -4922,6 +4922,10 @@ package android.net.wifi.rtt {
    field public final boolean supports80211mc;
  }
  public final class ResponderLocation implements android.os.Parcelable {
    method public boolean getExtraInfoOnAssociationIndication();
  }
  public class WifiRttManager {
    method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE}) public void cancelRanging(@Nullable android.os.WorkSource);
    method @RequiresPermission(allOf={android.Manifest.permission.LOCATION_HARDWARE, android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.CHANGE_WIFI_STATE, android.Manifest.permission.ACCESS_WIFI_STATE}) public void startRanging(@Nullable android.os.WorkSource, @NonNull android.net.wifi.rtt.RangingRequest, @NonNull java.util.concurrent.Executor, @NonNull android.net.wifi.rtt.RangingResultCallback);
+323 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net.wifi.rtt;

import android.annotation.Nullable;
import android.location.Address;
import android.net.wifi.rtt.CivicLocationKeys.CivicLocationKeysType;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.Parcelable.Creator;
import android.util.SparseArray;

import java.nio.charset.StandardCharsets;
import java.util.Locale;
import java.util.Objects;

/**
 * Decodes the Type Length Value (TLV) elements found in a Location Civic Record as defined by IEEE
 * P802.11-REVmc/D8.0 section 9.4.2.22.13 using the format described in IETF RFC 4776.
 *
 * <p>The TLVs each define a key, value pair for a civic address type such as apt, street, city,
 * county, and country. The class provides a general getter method to extract a value for an element
 * key, returning null if not set.
 *
 * @hide
 */
public final class CivicLocation implements Parcelable {
    // Address (class) line indexes
    private static final int ADDRESS_LINE_0_ROOM_DESK_FLOOR = 0;
    private static final int ADDRESS_LINE_1_NUMBER_ROAD_SUFFIX_APT = 1;
    private static final int ADDRESS_LINE_2_CITY = 2;
    private static final int ADDRESS_LINE_3_STATE_POSTAL_CODE = 3;
    private static final int ADDRESS_LINE_4_COUNTRY = 4;

    // Buffer management
    private static final int MIN_CIVIC_BUFFER_SIZE = 3;
    private static final int MAX_CIVIC_BUFFER_SIZE = 256;
    private static final int COUNTRY_CODE_LENGTH = 2;
    private static final int BYTE_MASK = 0xFF;
    private static final int TLV_TYPE_INDEX = 0;
    private static final int TLV_LENGTH_INDEX = 1;
    private static final int TLV_VALUE_INDEX = 2;

    private final boolean mIsValid;
    private final String mCountryCode; // Two character country code (ISO 3166 standard).
    private SparseArray<String> mCivicAddressElements =
            new SparseArray<>(MIN_CIVIC_BUFFER_SIZE);


    /**
     * Constructor
     *
     * @param civicTLVs    a byte buffer containing parameters in the form type, length, value
     * @param countryCode the two letter code defined by the ISO 3166 standard
     *
     * @hide
     */
    public CivicLocation(@Nullable byte[] civicTLVs, @Nullable String countryCode) {
        this.mCountryCode = countryCode;
        if (countryCode == null || countryCode.length() != COUNTRY_CODE_LENGTH) {
            this.mIsValid = false;
            return;
        }
        boolean isValid = false;
        if (civicTLVs != null
                && civicTLVs.length >= MIN_CIVIC_BUFFER_SIZE
                && civicTLVs.length < MAX_CIVIC_BUFFER_SIZE) {
            isValid = parseCivicTLVs(civicTLVs);
        }

        mIsValid = isValid;
    }

    private CivicLocation(Parcel in) {
        mIsValid = in.readByte() != 0;
        mCountryCode = in.readString();
        mCivicAddressElements = in.readSparseArray(this.getClass().getClassLoader());
    }

    public static final Creator<CivicLocation> CREATOR = new Creator<CivicLocation>() {
        @Override
        public CivicLocation createFromParcel(Parcel in) {
            return new CivicLocation(in);
        }

        @Override
        public CivicLocation[] newArray(int size) {
            return new CivicLocation[size];
        }
    };

    @Override
    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel parcel, int flags) {
        parcel.writeByte((byte) (mIsValid ? 1 : 0));
        parcel.writeString(mCountryCode);
        parcel.writeSparseArray((android.util.SparseArray) mCivicAddressElements);
    }

    /**
     * Check TLV format and store TLV key/value pairs in this object so they can be queried by key.
     *
     * @param civicTLVs the buffer of TLV elements
     * @return a boolean indicating success of the parsing process
     */
    private boolean parseCivicTLVs(byte[] civicTLVs) {
        int bufferPtr = 0;
        int bufferLength = civicTLVs.length;

        // Iterate through the sub-elements contained in the LCI IE checking the accumulated
        // element lengths do not overflow the total buffer length
        while (bufferPtr < bufferLength) {
            int civicAddressType = civicTLVs[bufferPtr + TLV_TYPE_INDEX] & BYTE_MASK;
            int civicAddressTypeLength = civicTLVs[bufferPtr + TLV_LENGTH_INDEX];
            if (civicAddressTypeLength != 0) {
                if (bufferPtr + TLV_VALUE_INDEX + civicAddressTypeLength > bufferLength) {
                    return false;
                }
                mCivicAddressElements.put(civicAddressType,
                        new String(civicTLVs, bufferPtr + TLV_VALUE_INDEX,
                                civicAddressTypeLength, StandardCharsets.UTF_8));
            }
            bufferPtr += civicAddressTypeLength + TLV_VALUE_INDEX;
        }
        return true;
    }

    /**
     * Getter for the value of a civic Address element type.
     *
     * @param key an integer code for the element type key
     * @return the string value associated with that element type
     */
    @Nullable
    public String getCivicElementValue(@CivicLocationKeysType int key) {
        return mCivicAddressElements.get(key);
    }

    /**
     * Generates a comma separated string of all the defined elements.
     *
     * @return a compiled string representing all elements
     */
    @Override
    public String toString() {
        return mCivicAddressElements.toString();
    }

    /**
     * Converts Civic Location to the best effort Address Object.
     *
     * @return the {@link Address} object based on the Civic Location data
     */
    @Nullable
    public Address toAddress() {
        if (!mIsValid) {
            return null;
        }
        Address address = new Address(Locale.US);
        String room = formatAddressElement("Room: ", getCivicElementValue(CivicLocationKeys.ROOM));
        String desk =
                formatAddressElement(" Desk: ", getCivicElementValue(CivicLocationKeys.DESK));
        String floor =
                formatAddressElement(", Flr: ", getCivicElementValue(CivicLocationKeys.FLOOR));
        String houseNumber = formatAddressElement("", getCivicElementValue(CivicLocationKeys.HNO));
        String houseNumberSuffix =
                formatAddressElement("", getCivicElementValue(CivicLocationKeys.HNS));
        String road =
                formatAddressElement(" ", getCivicElementValue(
                        CivicLocationKeys.PRIMARY_ROAD_NAME));
        String roadSuffix = formatAddressElement(" ", getCivicElementValue(CivicLocationKeys.STS));
        String apt = formatAddressElement(", Apt: ", getCivicElementValue(CivicLocationKeys.APT));
        String city = formatAddressElement("", getCivicElementValue(CivicLocationKeys.CITY));
        String state = formatAddressElement("", getCivicElementValue(CivicLocationKeys.STATE));
        String postalCode =
                formatAddressElement(" ", getCivicElementValue(CivicLocationKeys.POSTAL_CODE));

        // Aggregation into common address format
        String addressLine0 =
                new StringBuilder().append(room).append(desk).append(floor).toString();
        String addressLine1 =
                new StringBuilder().append(houseNumber).append(houseNumberSuffix).append(road)
                        .append(roadSuffix).append(apt).toString();
        String addressLine2 = city;
        String addressLine3 = new StringBuilder().append(state).append(postalCode).toString();
        String addressLine4 = mCountryCode;

        // Setting Address object line fields by common convention.
        address.setAddressLine(ADDRESS_LINE_0_ROOM_DESK_FLOOR, addressLine0);
        address.setAddressLine(ADDRESS_LINE_1_NUMBER_ROAD_SUFFIX_APT, addressLine1);
        address.setAddressLine(ADDRESS_LINE_2_CITY, addressLine2);
        address.setAddressLine(ADDRESS_LINE_3_STATE_POSTAL_CODE, addressLine3);
        address.setAddressLine(ADDRESS_LINE_4_COUNTRY, addressLine4);

        // Other compatible fields between the CIVIC_ADDRESS and the Address Class.
        address.setFeatureName(getCivicElementValue(CivicLocationKeys.NAM)); // Structure name
        address.setSubThoroughfare(getCivicElementValue(CivicLocationKeys.HNO));
        address.setThoroughfare(getCivicElementValue(CivicLocationKeys.PRIMARY_ROAD_NAME));
        address.setSubLocality(getCivicElementValue(CivicLocationKeys.NEIGHBORHOOD));
        address.setSubAdminArea(getCivicElementValue(CivicLocationKeys.COUNTY));
        address.setAdminArea(getCivicElementValue(CivicLocationKeys.STATE));
        address.setPostalCode(getCivicElementValue(CivicLocationKeys.POSTAL_CODE));
        address.setCountryCode(mCountryCode); // Country
        return address;
    }

    /**
     * Prepares an address element so that it can be integrated into an address line convention.
     *
     * <p>If an address element is null, the return string will be empty e.g. "".
     *
     * @param label a string defining the type of address element
     * @param value a string defining the elements value
     * @return the formatted version of the value, with null values converted to empty strings
     */
    private String formatAddressElement(String label, String value) {
        if (value != null) {
            return label + value;
        } else {
            return "";
        }
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (!(obj instanceof CivicLocation)) {
            return false;
        }
        CivicLocation other = (CivicLocation) obj;
        return mIsValid == other.mIsValid
                && Objects.equals(mCountryCode, other.mCountryCode)
                && isSparseArrayStringEqual(mCivicAddressElements, other.mCivicAddressElements);
    }

    @Override
    public int hashCode() {
        int[] civicAddressKeys = getSparseArrayKeys(mCivicAddressElements);
        String[] civicAddressValues = getSparseArrayValues(mCivicAddressElements);
        return Objects.hash(mIsValid, mCountryCode, civicAddressKeys, civicAddressValues);
    }

    /**
     * Tests if the Civic Location object is valid
     *
     * @return a boolean defining mIsValid
     */
    public boolean isValid() {
        return mIsValid;
    }

    /**
     * Tests if two sparse arrays are equal on a key for key basis
     *
     * @param sa1 the first sparse array
     * @param sa2 the second sparse array
     * @return the boolean result after comparing values key by key
     */
    private boolean isSparseArrayStringEqual(SparseArray<String> sa1, SparseArray<String> sa2) {
        int size = sa1.size();
        if (size != sa2.size()) {
            return false;
        }
        for (int i = 0; i < size; i++) {
            String sa1Value = sa1.valueAt(i);
            String sa2Value = sa2.valueAt(i);
            if (!sa1Value.equals(sa2Value)) {
                return false;
            }
        }
        return true;
    }

    /**
     * Extract an array of all the keys in a SparseArray<String>
     *
     * @param sa the sparse array of Strings
     * @return an integer array of all keys in the SparseArray<String>
     */
    private int[] getSparseArrayKeys(SparseArray<String> sa) {
        int size = sa.size();
        int[] keys = new int[size];
        for (int i = 0; i < size; i++) {
            keys[i] = sa.keyAt(i);
        }
        return keys;
    }

    /**
     * Extract an array of all the String values in a SparseArray<String>
     *
     * @param sa the sparse array of Strings
     * @return a String array of all values in the SparseArray<String>
     */
    private String[] getSparseArrayValues(SparseArray<String> sa) {
        int size = sa.size();
        String[] values = new String[size];
        for (int i = 0; i < size; i++) {
            values[i] = sa.valueAt(i);
        }
        return values;
    }
}
+116 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.net.wifi.rtt;

import static java.lang.annotation.RetentionPolicy.SOURCE;

import android.annotation.IntDef;

import java.lang.annotation.Retention;

/**
 * Civic Address key types used to define address elements.
 *
 * <p>These keys can be used in ResponderLocation look-up the corresponding string values.</p>
 *
 * @hide
 */
public class CivicLocationKeys {

    /**
     * An enumeration of all civic location keys.
     *
     * @hide
     */
    @Retention(SOURCE)
    @IntDef({LANGUAGE, STATE, COUNTY, CITY, BOROUGH, NEIGHBORHOOD, GROUP_OF_STREETS, PRD, POD, STS,
            HNO, HNS, LMK, LOC, NAM, POSTAL_CODE, BUILDING, APT, FLOOR, ROOM, TYPE_OF_PLACE, PCN,
            PO_BOX, ADDITIONAL_CODE, DESK, PRIMARY_ROAD_NAME, ROAD_SECTION, BRANCH_ROAD_NAME,
            SUBBRANCH_ROAD_NAME, STREET_NAME_PRE_MODIFIER, STREET_NAME_POST_MODIFIER, SCRIPT})
    public @interface CivicLocationKeysType {
    }

    /** Language key e.g. i-default. */
    public static final int LANGUAGE = 0;
    /** Category label A1 key e.g. California. */
    public static final int STATE = 1;
    /** Category label A2 key e.g. Marin. */
    public static final int COUNTY = 2;
    /** Category label A3 key e.g. San Francisco. */
    public static final int CITY = 3;
    /** Category label A4 key e.g. Westminster. */
    public static final int BOROUGH = 4;
    /** Category label A5 key e.g. Pacific Heights. */
    public static final int NEIGHBORHOOD = 5;
    /** Category label A6 key e.g. University District. */
    public static final int GROUP_OF_STREETS = 6;
    // 7 - 15 not defined
    /** Leading Street direction key e.g. N. */
    public static final int PRD = 16;
    /** Trailing street suffix key e.g. SW. */
    public static final int POD = 17;
    /** Street suffix or Type key e.g Ave, Platz. */
    public static final int STS = 18;
    /** House Number key e.g. 123. */
    public static final int HNO = 19;
    /** House number suffix key e.g. A, 1/2. */
    public static final int HNS = 20;
    /** Landmark or vanity address key e.g. Columbia Univ. */
    public static final int LMK = 21;
    /** Additional Location info key e.g. South Wing. */
    public static final int LOC = 22;
    /** Name of residence key e.g. Joe's Barbershop. */
    public static final int NAM = 23;
    /** Postal or ZIP code key e.g. 10027-1234. */
    public static final int POSTAL_CODE = 24;
    /** Building key e.g. Low Library. */
    public static final int BUILDING = 25;
    /** Apartment or suite key e.g. Apt 42. */
    public static final int APT = 26;
    /** Floor key e.g. 4. */
    public static final int FLOOR = 27;
    /** Room key e.g. 450F. */
    public static final int ROOM = 28;
    /** Type of place key e.g. office. */
    public static final int TYPE_OF_PLACE = 29;
    /** Postal community name key e.g. Leonia. */
    public static final int PCN = 30;
    /** Post Office Box key e.g. 12345. */
    public static final int PO_BOX = 31;
    /** Additional Code key e.g. 13203000003. */
    public static final int ADDITIONAL_CODE = 32;
    /** Seat, desk, pole, or cubical key e.g. WS 181. */
    public static final int DESK = 33;
    /** Primary road name key e.g. Shoreline. */
    public static final int PRIMARY_ROAD_NAME = 34;
    /** Road Section key e.g. 14. */
    public static final int ROAD_SECTION = 35;
    /** Branch Rd Name key e.g. Lane 7. */
    public static final int BRANCH_ROAD_NAME = 36;
    /** Subbranch Rd Name key e.g. Alley 8. */
    public static final int SUBBRANCH_ROAD_NAME = 37;
    /** Premodifier key e.g. Old. */
    public static final int STREET_NAME_PRE_MODIFIER = 38;
    /** Postmodifier key e.g. Service. */
    public static final int STREET_NAME_POST_MODIFIER = 39;
    /** Script key e.g. Latn. */
    public static final int SCRIPT = 128;

    /** private constructor */
    private CivicLocationKeys() {}
}
+38 −7

File changed.

Preview size limit exceeded, changes collapsed.

Loading