[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:
@@ -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 \
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
32
wifi/java/android/net/wifi/rtt/IRttCallback.aidl
Normal file
32
wifi/java/android/net/wifi/rtt/IRttCallback.aidl
Normal 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);
|
||||
}
|
||||
29
wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
Normal file
29
wifi/java/android/net/wifi/rtt/IWifiRttManager.aidl
Normal 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);
|
||||
}
|
||||
19
wifi/java/android/net/wifi/rtt/RangingRequest.aidl
Normal file
19
wifi/java/android/net/wifi/rtt/RangingRequest.aidl
Normal 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;
|
||||
237
wifi/java/android/net/wifi/rtt/RangingRequest.java
Normal file
237
wifi/java/android/net/wifi/rtt/RangingRequest.java
Normal 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();
|
||||
}
|
||||
}
|
||||
}
|
||||
19
wifi/java/android/net/wifi/rtt/RangingResult.aidl
Normal file
19
wifi/java/android/net/wifi/rtt/RangingResult.aidl
Normal 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;
|
||||
194
wifi/java/android/net/wifi/rtt/RangingResult.java
Normal file
194
wifi/java/android/net/wifi/rtt/RangingResult.java
Normal 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);
|
||||
}
|
||||
}
|
||||
56
wifi/java/android/net/wifi/rtt/RangingResultCallback.java
Normal file
56
wifi/java/android/net/wifi/rtt/RangingResultCallback.java
Normal 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);
|
||||
}
|
||||
100
wifi/java/android/net/wifi/rtt/WifiRttManager.java
Normal file
100
wifi/java/android/net/wifi/rtt/WifiRttManager.java
Normal 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();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
226
wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
Normal file
226
wifi/tests/src/android/net/wifi/rtt/WifiRttManagerTest.java
Normal 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);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user