[RTT2] New (v2) Wi-Fi RTT framework

Replace existing Wi-Fi RTT manager framework. Creating new framework
in parallel since there's code using the new framework - will be
switched over once new version ready.

New version is AIDL-based.

Bug: 65014552
Test: unit tests and integration tests
Change-Id: Id468c9b2a3c94eb30959f5ac5e4b1688fb8fc633
This commit is contained in:
Etan Cohen
2017-08-21 10:52:17 -07:00
parent e77a09c7ae
commit 17ba47254c
13 changed files with 944 additions and 2 deletions

View File

@@ -551,6 +551,8 @@ LOCAL_SRC_FILES += \
wifi/java/android/net/wifi/aware/IWifiAwareManager.aidl \
wifi/java/android/net/wifi/aware/IWifiAwareDiscoverySessionCallback.aidl \
wifi/java/android/net/wifi/p2p/IWifiP2pManager.aidl \
wifi/java/android/net/wifi/rtt/IRttCallback.aidl \
wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl \
wifi/java/android/net/wifi/IWifiScanner.aidl \
wifi/java/android/net/wifi/IRttManager.aidl \
packages/services/PacProcessor/com/android/net/IProxyService.aidl \
@@ -719,6 +721,8 @@ aidl_files := \
frameworks/base/wifi/java/android/net/wifi/p2p/WifiP2pGroup.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceRequest.aidl \
frameworks/base/wifi/java/android/net/wifi/p2p/nsd/WifiP2pServiceInfo.aidl \
frameworks/base/wifi/java/android/net/wifi/rtt/RangingRequest.aidl \
frameworks/base/wifi/java/android/net/wifi/rtt/RangingResult.aidl \
frameworks/base/wifi/java/android/net/wifi/WpsInfo.aidl \
frameworks/base/wifi/java/android/net/wifi/ScanResult.aidl \
frameworks/base/wifi/java/android/net/wifi/PasspointManagementObjectDefinition.aidl \

View File

@@ -81,10 +81,10 @@ import android.net.INetworkPolicyManager;
import android.net.IpSecManager;
import android.net.NetworkPolicyManager;
import android.net.NetworkScoreManager;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
import android.net.lowpan.ILowpanManager;
import android.net.lowpan.LowpanManager;
import android.net.nsd.INsdManager;
import android.net.nsd.NsdManager;
import android.net.wifi.IRttManager;
import android.net.wifi.IWifiManager;
import android.net.wifi.IWifiScanner;
@@ -95,6 +95,8 @@ import android.net.wifi.aware.IWifiAwareManager;
import android.net.wifi.aware.WifiAwareManager;
import android.net.wifi.p2p.IWifiP2pManager;
import android.net.wifi.p2p.WifiP2pManager;
import android.net.wifi.rtt.IWifiRttManager;
import android.net.wifi.rtt.WifiRttManager;
import android.nfc.NfcManager;
import android.os.BatteryManager;
import android.os.BatteryStats;
@@ -603,6 +605,16 @@ final class SystemServiceRegistry {
ConnectivityThread.getInstanceLooper());
}});
registerService(Context.WIFI_RTT2_SERVICE, WifiRttManager.class,
new CachedServiceFetcher<WifiRttManager>() {
@Override
public WifiRttManager createService(ContextImpl ctx)
throws ServiceNotFoundException {
IBinder b = ServiceManager.getServiceOrThrow(Context.WIFI_RTT2_SERVICE);
IWifiRttManager service = IWifiRttManager.Stub.asInterface(b);
return new WifiRttManager(ctx.getOuterContext(), service);
}});
registerService(Context.ETHERNET_SERVICE, EthernetManager.class,
new CachedServiceFetcher<EthernetManager>() {
@Override

View File

@@ -3462,6 +3462,19 @@ public abstract class Context {
@SystemApi
public static final String WIFI_RTT_SERVICE = "rttmanager";
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.net.wifi.rtt.WifiRttManager} for ranging devices with wifi
*
* Note: this is a replacement for WIFI_RTT_SERVICE above. It will
* be renamed once final implementation in place.
*
* @see #getSystemService
* @see android.net.wifi.rtt.WifiRttManager
* @hide
*/
public static final String WIFI_RTT2_SERVICE = "rttmanager2";
/**
* Use with {@link #getSystemService} to retrieve a {@link
* android.net.lowpan.LowpanManager} for handling management of

View File

@@ -1079,6 +1079,7 @@ public final class SystemServer {
if (!disableRtt) {
traceBeginAndSlog("StartWifiRtt");
mSystemServiceManager.startService("com.android.server.wifi.RttService");
mSystemServiceManager.startService("com.android.server.wifi.rtt.RttService");
traceEnd();
}

View File

@@ -0,0 +1,32 @@
/*
* Copyright (C) 2017 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.net.wifi.rtt.RangingResult;
/**
* Interface for RTT result callback.
*
* @hide
*/
oneway interface IRttCallback
{
/**
* Service to manager callback providing RTT status and results.
*/
void onRangingResults(int status, in List<RangingResult> results);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright (C) 2017 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.net.wifi.rtt.IRttCallback;
import android.net.wifi.rtt.RangingRequest;
/**
* @hide
*/
interface IWifiRttManager
{
void startRanging(in IBinder binder, in String callingPackage, in RangingRequest request,
in IRttCallback callback);
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2017 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;
parcelable RangingRequest;

View File

@@ -0,0 +1,237 @@
/*
* Copyright (C) 2017 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.net.wifi.ScanResult;
import android.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.TextUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.StringJoiner;
/**
* Defines the ranging request to other devices. The ranging request is built using
* {@link RangingRequest.Builder}.
* A ranging request is executed using
* {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}.
* <p>
* The ranging request is a batch request - specifying a set of devices (specified using
* {@link RangingRequest.Builder#addAp(ScanResult)} and
* {@link RangingRequest.Builder#addAps(List)}).
*
* @hide RTT_API
*/
public final class RangingRequest implements Parcelable {
private static final int MAX_PEERS = 10;
/**
* Returns the maximum number of peers to range which can be specified in a single {@code
* RangingRequest}. The limit applies no matter how the peers are added to the request, e.g.
* through {@link RangingRequest.Builder#addAp(ScanResult)} or
* {@link RangingRequest.Builder#addAps(List)}.
*
* @return Maximum number of peers.
*/
public static int getMaxPeers() {
return MAX_PEERS;
}
/** @hide */
public final List<RttPeer> mRttPeers;
/** @hide */
private RangingRequest(List<RttPeer> rttPeers) {
mRttPeers = rttPeers;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeList(mRttPeers);
}
public static final Creator<RangingRequest> CREATOR = new Creator<RangingRequest>() {
@Override
public RangingRequest[] newArray(int size) {
return new RangingRequest[size];
}
@Override
public RangingRequest createFromParcel(Parcel in) {
return new RangingRequest(in.readArrayList(null));
}
};
/** @hide */
@Override
public String toString() {
StringJoiner sj = new StringJoiner(", ", "RangingRequest: mRttPeers=[", ",");
for (RttPeer rp : mRttPeers) {
sj.add(rp.toString());
}
return sj.toString();
}
/** @hide */
public void enforceValidity() {
if (mRttPeers.size() > MAX_PEERS) {
throw new IllegalArgumentException(
"Ranging to too many peers requested. Use getMaxPeers() API to get limit.");
}
}
/**
* Builder class used to construct {@link RangingRequest} objects.
*/
public static final class Builder {
private List<RttPeer> mRttPeers = new ArrayList<>();
/**
* Add the device specified by the {@link ScanResult} to the list of devices with
* which to measure range. The total number of results added to a request cannot exceed the
* limit specified by {@link #getMaxPeers()}.
*
* @param apInfo Information of an Access Point (AP) obtained in a Scan Result.
* @return The builder to facilitate chaining
* {@code builder.setXXX(..).setXXX(..)}.
*/
public Builder addAp(ScanResult apInfo) {
if (apInfo == null) {
throw new IllegalArgumentException("Null ScanResult!");
}
mRttPeers.add(new RttPeerAp(apInfo));
return this;
}
/**
* Add the devices specified by the {@link ScanResult}s to the list of devices with
* which to measure range. The total number of results added to a request cannot exceed the
* limit specified by {@link #getMaxPeers()}.
*
* @param apInfos Information of an Access Points (APs) obtained in a Scan Result.
* @return The builder to facilitate chaining
* {@code builder.setXXX(..).setXXX(..)}.
*/
public Builder addAps(List<ScanResult> apInfos) {
if (apInfos == null) {
throw new IllegalArgumentException("Null list of ScanResults!");
}
for (ScanResult scanResult : apInfos) {
addAp(scanResult);
}
return this;
}
/**
* Build {@link RangingRequest} given the current configurations made on the
* builder.
*/
public RangingRequest build() {
return new RangingRequest(mRttPeers);
}
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof RangingRequest)) {
return false;
}
RangingRequest lhs = (RangingRequest) o;
return mRttPeers.size() == lhs.mRttPeers.size() && mRttPeers.containsAll(lhs.mRttPeers);
}
@Override
public int hashCode() {
return mRttPeers.hashCode();
}
/** @hide */
public interface RttPeer {
// empty (marker interface)
}
/** @hide */
public static class RttPeerAp implements RttPeer, Parcelable {
public final ScanResult scanResult;
public RttPeerAp(ScanResult scanResult) {
this.scanResult = scanResult;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(Parcel dest, int flags) {
scanResult.writeToParcel(dest, flags);
}
public static final Creator<RttPeerAp> CREATOR = new Creator<RttPeerAp>() {
@Override
public RttPeerAp[] newArray(int size) {
return new RttPeerAp[size];
}
@Override
public RttPeerAp createFromParcel(Parcel in) {
return new RttPeerAp(ScanResult.CREATOR.createFromParcel(in));
}
};
@Override
public String toString() {
return new StringBuilder("RttPeerAp: scanResult=").append(
scanResult.toString()).toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof RttPeerAp)) {
return false;
}
RttPeerAp lhs = (RttPeerAp) o;
// Note: the only thing which matters for the request identity is the BSSID of the AP
return TextUtils.equals(scanResult.BSSID, lhs.scanResult.BSSID);
}
@Override
public int hashCode() {
return scanResult.hashCode();
}
}
}

View File

@@ -0,0 +1,19 @@
/*
* Copyright (C) 2017 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;
parcelable RangingResult;

View File

@@ -0,0 +1,194 @@
/*
* Copyright (C) 2017 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.os.Handler;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import libcore.util.HexEncoding;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
/**
* Ranging result for a request started by
* {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}. Results are
* returned in {@link RangingResultCallback#onRangingResults(List)}.
* <p>
* A ranging result is the distance measurement result for a single device specified in the
* {@link RangingRequest}.
*
* @hide RTT_API
*/
public final class RangingResult implements Parcelable {
private static final String TAG = "RangingResult";
private final int mStatus;
private final byte[] mMac;
private final int mDistanceCm;
private final int mDistanceStdDevCm;
private final int mRssi;
private final long mTimestamp;
/** @hide */
public RangingResult(int status, byte[] mac, int distanceCm, int distanceStdDevCm, int rssi,
long timestamp) {
mStatus = status;
mMac = mac;
mDistanceCm = distanceCm;
mDistanceStdDevCm = distanceStdDevCm;
mRssi = rssi;
mTimestamp = timestamp;
}
/**
* @return The status of ranging measurement: {@link RangingResultCallback#STATUS_SUCCESS} in
* case of success, and {@link RangingResultCallback#STATUS_FAIL} in case of failure.
*/
public int getStatus() {
return mStatus;
}
/**
* @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.
*/
public byte[] getMacAddress() {
return mMac;
}
/**
* @return The distance (in cm) to the device specified by {@link #getMacAddress()}.
* <p>
* Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
*/
public int getDistanceCm() {
if (mStatus != RangingResultCallback.STATUS_SUCCESS) {
Log.e(TAG, "getDistanceCm(): invalid value retrieved");
}
return mDistanceCm;
}
/**
* @return The standard deviation of the measured distance (in cm) to the device specified by
* {@link #getMacAddress()}. The standard deviation is calculated over the measurements
* executed in a single RTT burst.
* <p>
* Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
*/
public int getDistanceStdDevCm() {
if (mStatus != RangingResultCallback.STATUS_SUCCESS) {
Log.e(TAG, "getDistanceStdDevCm(): invalid value retrieved");
}
return mDistanceStdDevCm;
}
/**
* @return The average RSSI (in units of -0.5dB) observed during the RTT measurement.
* <p>
* Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
*/
public int getRssi() {
if (mStatus != RangingResultCallback.STATUS_SUCCESS) {
// TODO: should this be an exception?
Log.e(TAG, "getRssi(): invalid value retrieved");
}
return mRssi;
}
/**
* @return The timestamp (in us) at which the ranging operation was performed
* <p>
* Only valid if {@link #getStatus()} returns {@link RangingResultCallback#STATUS_SUCCESS}.
*/
public long getRangingTimestamp() {
return mTimestamp;
}
/** @hide */
@Override
public int describeContents() {
return 0;
}
/** @hide */
@Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(mStatus);
dest.writeByteArray(mMac);
dest.writeInt(mDistanceCm);
dest.writeInt(mDistanceStdDevCm);
dest.writeInt(mRssi);
dest.writeLong(mTimestamp);
}
/** @hide */
public static final Creator<RangingResult> CREATOR = new Creator<RangingResult>() {
@Override
public RangingResult[] newArray(int size) {
return new RangingResult[size];
}
@Override
public RangingResult createFromParcel(Parcel in) {
int status = in.readInt();
byte[] mac = in.createByteArray();
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);
}
};
/** @hide */
@Override
public String toString() {
return new StringBuilder("RangingResult: [status=").append(mStatus).append(", mac=").append(
mMac == null ? "<null>" : HexEncoding.encodeToString(mMac)).append(
", distanceCm=").append(mDistanceCm).append(", distanceStdDevCm=").append(
mDistanceStdDevCm).append(", rssi=").append(mRssi).append(", timestamp=").append(
mTimestamp).append("]").toString();
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (!(o instanceof RangingResult)) {
return false;
}
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;
}
@Override
public int hashCode() {
return Objects.hash(mStatus, mMac, mDistanceCm, mDistanceStdDevCm, mRssi, mTimestamp);
}
}

View File

@@ -0,0 +1,56 @@
/*
* Copyright (C) 2017 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.os.Handler;
import java.util.List;
/**
* Base class for ranging result callbacks. Should be extended by applications and set when calling
* {@link WifiRttManager#startRanging(RangingRequest, RangingResultCallback, Handler)}. A single
* result from a range request will be called in this object.
*
* @hide RTT_API
*/
public abstract class RangingResultCallback {
/**
* Individual range request status, {@link RangingResult#getStatus()}. Indicates ranging
* operation was successful and distance value is valid.
*/
public static final int STATUS_SUCCESS = 0;
/**
* Individual range request status, {@link RangingResult#getStatus()}. Indicates ranging
* operation failed and the distance value is invalid.
*/
public static final int STATUS_FAIL = 1;
/**
* Called when a ranging operation failed in whole - i.e. no ranging operation to any of the
* devices specified in the request was attempted.
*/
public abstract void onRangingFailure();
/**
* Called when a ranging operation was executed. The list of results corresponds to devices
* specified in the ranging request.
*
* @param results List of range measurements, one per requested device.
*/
public abstract void onRangingResults(List<RangingResult> results);
}

View File

@@ -0,0 +1,100 @@
package android.net.wifi.rtt;
import static android.Manifest.permission.ACCESS_COARSE_LOCATION;
import static android.Manifest.permission.ACCESS_WIFI_STATE;
import static android.Manifest.permission.CHANGE_WIFI_STATE;
import android.annotation.Nullable;
import android.annotation.RequiresPermission;
import android.annotation.SystemService;
import android.content.Context;
import android.os.Binder;
import android.os.Handler;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import java.util.List;
/**
* This class provides the primary API for measuring distance (range) to other devices using the
* IEEE 802.11mc Wi-Fi Round Trip Time (RTT) technology.
* <p>
* The devices which can be ranged include:
* <li>Access Points (APs)
* <p>
* Ranging requests are triggered using
* {@link #startRanging(RangingRequest, RangingResultCallback, Handler)}. Results (in case of
* successful operation) are returned in the {@link RangingResultCallback#onRangingResults(List)}
* callback.
*
* @hide RTT_API
*/
@SystemService(Context.WIFI_RTT2_SERVICE)
public class WifiRttManager {
private static final String TAG = "WifiRttManager";
private static final boolean VDBG = true;
private final Context mContext;
private final IWifiRttManager mService;
/** @hide */
public WifiRttManager(Context context, IWifiRttManager service) {
mContext = context;
mService = service;
}
/**
* Initiate a request to range to a set of devices specified in the {@link RangingRequest}.
* Results will be returned in the {@link RangingResultCallback} set of callbacks.
*
* @param request A request specifying a set of devices whose distance measurements are
* requested.
* @param callback A callback for the result of the ranging request.
* @param handler The Handler on whose thread to execute the callbacks of the {@code
* callback} object. If a null is provided then the application's main thread
* will be used.
*/
@RequiresPermission(allOf = {ACCESS_COARSE_LOCATION, CHANGE_WIFI_STATE, ACCESS_WIFI_STATE})
public void startRanging(RangingRequest request, RangingResultCallback callback,
@Nullable Handler handler) {
if (VDBG) {
Log.v(TAG, "startRanging: request=" + request + ", callback=" + callback + ", handler="
+ handler);
}
Looper looper = (handler == null) ? Looper.getMainLooper() : handler.getLooper();
Binder binder = new Binder();
try {
mService.startRanging(binder, mContext.getOpPackageName(), request,
new RttCallbackProxy(looper, callback));
} catch (RemoteException e) {
throw e.rethrowFromSystemServer();
}
}
private static class RttCallbackProxy extends IRttCallback.Stub {
private final Handler mHandler;
private final RangingResultCallback mCallback;
RttCallbackProxy(Looper looper, RangingResultCallback callback) {
mHandler = new Handler(looper);
mCallback = callback;
}
@Override
public void onRangingResults(int status, List<RangingResult> results) throws RemoteException {
if (VDBG) {
Log.v(TAG, "RttCallbackProxy: onRanginResults: status=" + status + ", results="
+ results);
}
mHandler.post(() -> {
if (status == RangingResultCallback.STATUS_SUCCESS) {
mCallback.onRangingResults(results);
} else {
mCallback.onRangingFailure();
}
});
}
}
}

View File

@@ -0,0 +1,226 @@
/*
* Copyright (C) 2017 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 org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.net.wifi.ScanResult;
import android.os.Handler;
import android.os.IBinder;
import android.os.Parcel;
import android.os.test.TestLooper;
import android.test.suitebuilder.annotation.SmallTest;
import libcore.util.HexEncoding;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import java.util.ArrayList;
import java.util.List;
/**
* Unit test harness for WifiRttManager class.
*/
@SmallTest
public class WifiRttManagerTest {
private WifiRttManager mDut;
private TestLooper mMockLooper;
private Handler mMockLooperHandler;
private final String packageName = "some.package.name.for.rtt.app";
@Mock
public Context mockContext;
@Mock
public IWifiRttManager mockRttService;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mDut = new WifiRttManager(mockContext, mockRttService);
mMockLooper = new TestLooper();
mMockLooperHandler = new Handler(mMockLooper.getLooper());
when(mockContext.getOpPackageName()).thenReturn(packageName);
}
/**
* Validate ranging call flow with succesful results.
*/
@Test
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));
RangingResultCallback callbackMock = mock(RangingResultCallback.class);
ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
// verify ranging request passed to service
mDut.startRanging(request, callbackMock, mMockLooperHandler);
verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(request),
callbackCaptor.capture());
// service calls back with success
callbackCaptor.getValue().onRangingResults(RangingResultCallback.STATUS_SUCCESS, results);
mMockLooper.dispatchAll();
verify(callbackMock).onRangingResults(results);
verifyNoMoreInteractions(mockRttService, callbackMock);
}
/**
* Validate ranging call flow which failed.
*/
@Test
public void testRangeFail() throws Exception {
RangingRequest request = new RangingRequest.Builder().build();
RangingResultCallback callbackMock = mock(RangingResultCallback.class);
ArgumentCaptor<IRttCallback> callbackCaptor = ArgumentCaptor.forClass(IRttCallback.class);
// verify ranging request passed to service
mDut.startRanging(request, callbackMock, mMockLooperHandler);
verify(mockRttService).startRanging(any(IBinder.class), eq(packageName), eq(request),
callbackCaptor.capture());
// service calls back with failure code
callbackCaptor.getValue().onRangingResults(RangingResultCallback.STATUS_FAIL, null);
mMockLooper.dispatchAll();
verify(callbackMock).onRangingFailure();
verifyNoMoreInteractions(mockRttService, callbackMock);
}
/**
* Validate that RangingRequest parcel works (produces same object on write/read).
*/
@Test
public void testRangingRequestParcel() {
// Note: not validating parcel code of ScanResult (assumed to work)
ScanResult scanResult1 = new ScanResult();
scanResult1.BSSID = "00:01:02:03:04:05";
ScanResult scanResult2 = new ScanResult();
scanResult2.BSSID = "06:07:08:09:0A:0B";
ScanResult scanResult3 = new ScanResult();
scanResult3.BSSID = "AA:BB:CC:DD:EE:FF";
List<ScanResult> scanResults2and3 = new ArrayList<>(2);
scanResults2and3.add(scanResult2);
scanResults2and3.add(scanResult3);
RangingRequest.Builder builder = new RangingRequest.Builder();
builder.addAp(scanResult1);
builder.addAps(scanResults2and3);
RangingRequest request = builder.build();
Parcel parcelW = Parcel.obtain();
request.writeToParcel(parcelW, 0);
byte[] bytes = parcelW.marshall();
parcelW.recycle();
Parcel parcelR = Parcel.obtain();
parcelR.unmarshall(bytes, 0, bytes.length);
parcelR.setDataPosition(0);
RangingRequest rereadRequest = RangingRequest.CREATOR.createFromParcel(parcelR);
assertEquals(request, rereadRequest);
}
/**
* Validate that can request as many range operation as the upper limit on number of requests.
*/
@Test
public void testRangingRequestAtLimit() {
ScanResult scanResult = new ScanResult();
List<ScanResult> scanResultList = new ArrayList<>();
for (int i = 0; i < RangingRequest.getMaxPeers() - 2; ++i) {
scanResultList.add(scanResult);
}
// create request
RangingRequest.Builder builder = new RangingRequest.Builder();
builder.addAp(scanResult);
builder.addAps(scanResultList);
builder.addAp(scanResult);
RangingRequest request = builder.build();
// verify request
request.enforceValidity();
}
/**
* Validate that limit on number of requests is applied.
*/
@Test(expected = IllegalArgumentException.class)
public void testRangingRequestPastLimit() {
ScanResult scanResult = new ScanResult();
List<ScanResult> scanResultList = new ArrayList<>();
for (int i = 0; i < RangingRequest.getMaxPeers() - 1; ++i) {
scanResultList.add(scanResult);
}
// create request
RangingRequest.Builder builder = new RangingRequest.Builder();
builder.addAp(scanResult);
builder.addAps(scanResultList);
builder.addAp(scanResult);
RangingRequest request = builder.build();
// verify request
request.enforceValidity();
}
/**
* Validate that RangingResults parcel works (produces same object on write/read).
*/
@Test
public void testRangingResultsParcel() {
// Note: not validating parcel code of ScanResult (assumed to work)
int status = RangingResultCallback.STATUS_SUCCESS;
final byte[] mac = HexEncoding.decode("000102030405".toCharArray(), false);
int distanceCm = 105;
int distanceStdDevCm = 10;
int rssi = 5;
long timestamp = System.currentTimeMillis();
RangingResult result = new RangingResult(status, mac, distanceCm, distanceStdDevCm, rssi,
timestamp);
Parcel parcelW = Parcel.obtain();
result.writeToParcel(parcelW, 0);
byte[] bytes = parcelW.marshall();
parcelW.recycle();
Parcel parcelR = Parcel.obtain();
parcelR.unmarshall(bytes, 0, bytes.length);
parcelR.setDataPosition(0);
RangingResult rereadResult = RangingResult.CREATOR.createFromParcel(parcelR);
assertEquals(result, rereadResult);
}
}