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

Commit 5a100a07 authored by Etan Cohen's avatar Etan Cohen
Browse files

[RTT2] Add support for RTT to Wi-Fi Aware peers

Add API to specify RTT requests to Wi-FI Aware peers specified either
using their MAC address (out-of-band discovery) or their PeerHandle
(in-band discovery).

Bug: 65015034
Test: unit tests + integration tests
Change-Id: I1cb8e90cab7c1acc621576703c9d0c6f4f5ac8d4
parent f73f9ed5
Loading
Loading
Loading
Loading
+157 −2
Original line number Diff line number Diff line
@@ -17,13 +17,22 @@
package android.net.wifi.rtt;

import android.net.wifi.ScanResult;
import android.net.wifi.aware.AttachCallback;
import android.net.wifi.aware.DiscoverySessionCallback;
import android.net.wifi.aware.IdentityChangedListener;
import android.net.wifi.aware.PeerHandle;
import android.net.wifi.aware.WifiAwareManager;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;

import libcore.util.HexEncoding;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.StringJoiner;

/**
@@ -94,11 +103,34 @@ public final class RangingRequest implements Parcelable {
    }

    /** @hide */
    public void enforceValidity() {
    public void enforceValidity(boolean awareSupported) {
        if (mRttPeers.size() > MAX_PEERS) {
            throw new IllegalArgumentException(
                    "Ranging to too many peers requested. Use getMaxPeers() API to get limit.");
        }

        for (RttPeer peer: mRttPeers) {
            if (peer instanceof RttPeerAp) {
                RttPeerAp apPeer = (RttPeerAp) peer;
                if (apPeer.scanResult == null || apPeer.scanResult.BSSID == null) {
                    throw new IllegalArgumentException("Invalid AP peer specification");
                }
            } else if (peer instanceof RttPeerAware) {
                if (!awareSupported) {
                    throw new IllegalArgumentException(
                            "Request contains Aware peers - but Aware isn't supported on this "
                                    + "device");
                }

                RttPeerAware awarePeer = (RttPeerAware) peer;
                if (awarePeer.peerMacAddress == null && awarePeer.peerHandle == null) {
                    throw new IllegalArgumentException("Invalid Aware peer specification");
                }
            } else {
                throw new IllegalArgumentException(
                        "Request contains unknown peer specification types");
            }
        }
    }

    /**
@@ -143,6 +175,44 @@ public final class RangingRequest implements Parcelable {
            return this;
        }

        /**
         * Add the device specified by the {@code peerMacAddress} to the list of devices with
         * which to measure range.
         *
         * The MAC address may be obtained out-of-band from a peer Wi-Fi Aware device. A Wi-Fi
         * Aware device may obtain its MAC address using the {@link IdentityChangedListener}
         * provided to
         * {@link WifiAwareManager#attach(AttachCallback, IdentityChangedListener, Handler)}.
         *
         * * Note: in order to use this API the device must support Wi-Fi Aware
         * {@link android.net.wifi.aware}.
         *
         * @param peerMacAddress The MAC address of the Wi-Fi Aware peer.
         * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
         */
        public Builder addWifiAwarePeer(byte[] peerMacAddress) {
            mRttPeers.add(new RttPeerAware(peerMacAddress));
            return this;
        }

        /**
         * Add a device specified by a {@link PeerHandle} to the list of devices with which to
         * measure range.
         *
         * The {@link PeerHandle} may be obtained as part of the Wi-Fi Aware discovery process. E.g.
         * using {@link DiscoverySessionCallback#onServiceDiscovered(PeerHandle, byte[], List)}.
         *
         * Note: in order to use this API the device must support Wi-Fi Aware
         * {@link android.net.wifi.aware}.
         *
         * @param peerHandle The peer handler of the peer Wi-Fi Aware device.
         * @return The builder, to facilitate chaining {@code builder.setXXX(..).setXXX(..)}.
         */
        public Builder addWifiAwarePeer(PeerHandle peerHandle) {
            mRttPeers.add(new RttPeerAware(peerHandle));
            return this;
        }

        /**
         * Build {@link RangingRequest} given the current configurations made on the
         * builder.
@@ -234,4 +304,89 @@ public final class RangingRequest implements Parcelable {
            return scanResult.hashCode();
        }
    }

    /** @hide */
    public static class RttPeerAware implements RttPeer, Parcelable {
        public PeerHandle peerHandle;
        public byte[] peerMacAddress;

        public RttPeerAware(PeerHandle peerHandle) {
            if (peerHandle == null) {
                throw new IllegalArgumentException("Null peerHandle");
            }
            this.peerHandle = peerHandle;
            peerMacAddress = null;
        }

        public RttPeerAware(byte[] peerMacAddress) {
            if (peerMacAddress == null) {
                throw new IllegalArgumentException("Null peerMacAddress");
            }

            this.peerMacAddress = peerMacAddress;
            peerHandle = null;
        }

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

        @Override
        public void writeToParcel(Parcel dest, int flags) {
            if (peerHandle == null) {
                dest.writeBoolean(false);
                dest.writeByteArray(peerMacAddress);
            } else {
                dest.writeBoolean(true);
                dest.writeInt(peerHandle.peerId);
            }
        }

        public static final Creator<RttPeerAware> CREATOR = new Creator<RttPeerAware>() {
            @Override
            public RttPeerAware[] newArray(int size) {
                return new RttPeerAware[size];
            }

            @Override
            public RttPeerAware createFromParcel(Parcel in) {
                boolean peerHandleAvail = in.readBoolean();
                if (peerHandleAvail) {
                    return new RttPeerAware(new PeerHandle(in.readInt()));
                } else {
                    return new RttPeerAware(in.createByteArray());
                }
            }
        };

        @Override
        public String toString() {
            return new StringBuilder("RttPeerAware: peerHandle=").append(
                    peerHandle == null ? "<null>" : Integer.toString(peerHandle.peerId)).append(
                    ", peerMacAddress=").append(peerMacAddress == null ? "<null>"
                    : new String(HexEncoding.encode(peerMacAddress))).toString();
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) {
                return true;
            }

            if (!(o instanceof RttPeerAware)) {
                return false;
            }

            RttPeerAware lhs = (RttPeerAware) o;

            return Objects.equals(peerHandle, lhs.peerHandle) && Arrays.equals(peerMacAddress,
                    lhs.peerMacAddress);
        }

        @Override
        public int hashCode() {
            return Objects.hash(peerHandle.peerId, peerMacAddress);
        }
    }
}
+59 −7
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.net.wifi.rtt;

import android.net.wifi.aware.PeerHandle;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
@@ -42,6 +43,7 @@ public final class RangingResult implements Parcelable {

    private final int mStatus;
    private final byte[] mMac;
    private final PeerHandle mPeerHandle;
    private final int mDistanceCm;
    private final int mDistanceStdDevCm;
    private final int mRssi;
@@ -52,6 +54,19 @@ public final class RangingResult implements Parcelable {
            long timestamp) {
        mStatus = status;
        mMac = mac;
        mPeerHandle = null;
        mDistanceCm = distanceCm;
        mDistanceStdDevCm = distanceStdDevCm;
        mRssi = rssi;
        mTimestamp = timestamp;
    }

    /** @hide */
    public RangingResult(int status, PeerHandle peerHandle, int distanceCm, int distanceStdDevCm,
            int rssi, long timestamp) {
        mStatus = status;
        mMac = null;
        mPeerHandle = peerHandle;
        mDistanceCm = distanceCm;
        mDistanceStdDevCm = distanceStdDevCm;
        mRssi = rssi;
@@ -70,12 +85,29 @@ public final class RangingResult implements Parcelable {
     * @return The MAC address of the device whose range measurement was requested. Will correspond
     * to the MAC address of the device in the {@link RangingRequest}.
     * <p>
     * Always valid (i.e. when {@link #getStatus()} is either SUCCESS or FAIL.
     * Will return a {@code null} for results corresponding to requests issued using a {@code
     * PeerHandle}, i.e. using the {@link RangingRequest.Builder#addWifiAwarePeer(PeerHandle)} API.
     * <p>
     * Valid whether {@link #getStatus()} is SUCCESS or FAIL.
     */
    public byte[] getMacAddress() {
        return mMac;
    }

    /**
     * @return The PeerHandle of the device whose reange measurement was requested. Will correspond
     * to the PeerHandle of the devices requested using
     * {@link RangingRequest.Builder#addWifiAwarePeer(PeerHandle)}.
     * <p>
     * Will return a {@code null} for results corresponding to requests issued using a MAC address.
     * <p>
     *
     * Valid whether {@link #getStatus()} is SUCCESS or FAIL.
     */
    public PeerHandle getPeerHandle() {
        return mPeerHandle;
    }

    /**
     * @return The distance (in cm) to the device specified by {@link #getMacAddress()}.
     * <p>
@@ -135,6 +167,12 @@ public final class RangingResult implements Parcelable {
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeInt(mStatus);
        dest.writeByteArray(mMac);
        if (mPeerHandle == null) {
            dest.writeBoolean(false);
        } else {
            dest.writeBoolean(true);
            dest.writeInt(mPeerHandle.peerId);
        }
        dest.writeInt(mDistanceCm);
        dest.writeInt(mDistanceStdDevCm);
        dest.writeInt(mRssi);
@@ -152,11 +190,22 @@ public final class RangingResult implements Parcelable {
        public RangingResult createFromParcel(Parcel in) {
            int status = in.readInt();
            byte[] mac = in.createByteArray();
            boolean peerHandlePresent = in.readBoolean();
            PeerHandle peerHandle = null;
            if (peerHandlePresent) {
                peerHandle = new PeerHandle(in.readInt());
            }
            int distanceCm = in.readInt();
            int distanceStdDevCm = in.readInt();
            int rssi = in.readInt();
            long timestamp = in.readLong();
            return new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi, timestamp);
            if (peerHandlePresent) {
                return new RangingResult(status, peerHandle, distanceCm, distanceStdDevCm, rssi,
                        timestamp);
            } else {
                return new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
                        timestamp);
            }
        }
    };

@@ -164,7 +213,8 @@ public final class RangingResult implements Parcelable {
    @Override
    public String toString() {
        return new StringBuilder("RangingResult: [status=").append(mStatus).append(", mac=").append(
                mMac == null ? "<null>" : HexEncoding.encodeToString(mMac)).append(
                mMac == null ? "<null>" : new String(HexEncoding.encodeToString(mMac))).append(
                ", peerHandle=").append(mPeerHandle == null ? "<null>" : mPeerHandle.peerId).append(
                ", distanceCm=").append(mDistanceCm).append(", distanceStdDevCm=").append(
                mDistanceStdDevCm).append(", rssi=").append(mRssi).append(", timestamp=").append(
                mTimestamp).append("]").toString();
@@ -182,13 +232,15 @@ public final class RangingResult implements Parcelable {

        RangingResult lhs = (RangingResult) o;

        return mStatus == lhs.mStatus && Arrays.equals(mMac, lhs.mMac)
                && mDistanceCm == lhs.mDistanceCm && mDistanceStdDevCm == lhs.mDistanceStdDevCm
                && mRssi == lhs.mRssi && mTimestamp == lhs.mTimestamp;
        return mStatus == lhs.mStatus && Arrays.equals(mMac, lhs.mMac) && Objects.equals(
                mPeerHandle, lhs.mPeerHandle) && mDistanceCm == lhs.mDistanceCm
                && mDistanceStdDevCm == lhs.mDistanceStdDevCm && mRssi == lhs.mRssi
                && mTimestamp == lhs.mTimestamp;
    }

    @Override
    public int hashCode() {
        return Objects.hash(mStatus, mMac, mDistanceCm, mDistanceStdDevCm, mRssi, mTimestamp);
        return Objects.hash(mStatus, mMac, mPeerHandle, mDistanceCm, mDistanceStdDevCm, mRssi,
                mTimestamp);
    }
}
 No newline at end of file
+49 −5
Original line number Diff line number Diff line
@@ -26,6 +26,7 @@ import static org.mockito.Mockito.when;

import android.content.Context;
import android.net.wifi.ScanResult;
import android.net.wifi.aware.PeerHandle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
@@ -78,7 +79,9 @@ public class WifiRttManagerTest {
    public void testRangeSuccess() throws Exception {
        RangingRequest request = new RangingRequest.Builder().build();
        List<RangingResult> results = new ArrayList<>();
        results.add(new RangingResult(RangingResultCallback.STATUS_SUCCESS, null, 15, 5, 10, 666));
        results.add(
                new RangingResult(RangingResultCallback.STATUS_SUCCESS, (byte[]) null, 15, 5, 10,
                        666));
        RangingResultCallback callbackMock = mock(RangingResultCallback.class);
        ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);

@@ -132,10 +135,14 @@ public class WifiRttManagerTest {
        List<ScanResult> scanResults2and3 = new ArrayList<>(2);
        scanResults2and3.add(scanResult2);
        scanResults2and3.add(scanResult3);
        final byte[] mac1 = HexEncoding.decode("000102030405".toCharArray(), false);
        PeerHandle peerHandle1 = new PeerHandle(12);

        RangingRequest.Builder builder = new RangingRequest.Builder();
        builder.addAp(scanResult1);
        builder.addAps(scanResults2and3);
        builder.addWifiAwarePeer(mac1);
        builder.addWifiAwarePeer(peerHandle1);
        RangingRequest request = builder.build();

        Parcel parcelW = Parcel.obtain();
@@ -157,20 +164,23 @@ public class WifiRttManagerTest {
    @Test
    public void testRangingRequestAtLimit() {
        ScanResult scanResult = new ScanResult();
        scanResult.BSSID = "AA:BB:CC:DD:EE:FF";
        List<ScanResult> scanResultList = new ArrayList<>();
        for (int i = 0; i < RangingRequest.getMaxPeers() - 2; ++i) {
        for (int i = 0; i < RangingRequest.getMaxPeers() - 3; ++i) {
            scanResultList.add(scanResult);
        }
        final byte[] mac1 = HexEncoding.decode("000102030405".toCharArray(), false);

        // create request
        RangingRequest.Builder builder = new RangingRequest.Builder();
        builder.addAp(scanResult);
        builder.addAps(scanResultList);
        builder.addAp(scanResult);
        builder.addWifiAwarePeer(mac1);
        RangingRequest request = builder.build();

        // verify request
        request.enforceValidity();
        request.enforceValidity(true);
    }

    /**
@@ -180,19 +190,35 @@ public class WifiRttManagerTest {
    public void testRangingRequestPastLimit() {
        ScanResult scanResult = new ScanResult();
        List<ScanResult> scanResultList = new ArrayList<>();
        for (int i = 0; i < RangingRequest.getMaxPeers() - 1; ++i) {
        for (int i = 0; i < RangingRequest.getMaxPeers() - 2; ++i) {
            scanResultList.add(scanResult);
        }
        final byte[] mac1 = HexEncoding.decode("000102030405".toCharArray(), false);

        // create request
        RangingRequest.Builder builder = new RangingRequest.Builder();
        builder.addAp(scanResult);
        builder.addAps(scanResultList);
        builder.addAp(scanResult);
        builder.addWifiAwarePeer(mac1);
        RangingRequest request = builder.build();

        // verify request
        request.enforceValidity(true);
    }

    /**
     * Validate that Aware requests are invalid on devices which do not support Aware
     */
    @Test(expected = IllegalArgumentException.class)
    public void testRangingRequestWithAwareWithNoAwareSupport() {
        // create request
        RangingRequest.Builder builder = new RangingRequest.Builder();
        builder.addWifiAwarePeer(new PeerHandle(10));
        RangingRequest request = builder.build();

        // verify request
        request.enforceValidity();
        request.enforceValidity(false);
    }

    /**
@@ -203,11 +229,13 @@ public class WifiRttManagerTest {
        // Note: not validating parcel code of ScanResult (assumed to work)
        int status = RangingResultCallback.STATUS_SUCCESS;
        final byte[] mac = HexEncoding.decode("000102030405".toCharArray(), false);
        PeerHandle peerHandle = new PeerHandle(10);
        int distanceCm = 105;
        int distanceStdDevCm = 10;
        int rssi = 5;
        long timestamp = System.currentTimeMillis();

        // RangingResults constructed with a MAC address
        RangingResult result = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
                timestamp);

@@ -222,5 +250,21 @@ public class WifiRttManagerTest {
        RangingResult rereadResult = RangingResult.CREATOR.createFromParcel(parcelR);

        assertEquals(result, rereadResult);

        // RangingResults constructed with a PeerHandle
        result = new RangingResult(status, peerHandle, distanceCm, distanceStdDevCm, rssi,
                timestamp);

        parcelW = Parcel.obtain();
        result.writeToParcel(parcelW, 0);
        bytes = parcelW.marshall();
        parcelW.recycle();

        parcelR = Parcel.obtain();
        parcelR.unmarshall(bytes, 0, bytes.length);
        parcelR.setDataPosition(0);
        rereadResult = RangingResult.CREATOR.createFromParcel(parcelR);

        assertEquals(result, rereadResult);
    }
}