| package android.net.wifi; |
| |
| import android.annotation.NonNull; |
| import android.annotation.SystemApi; |
| import android.content.Context; |
| import android.os.Bundle; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.os.Looper; |
| import android.os.Message; |
| import android.os.Messenger; |
| import android.os.Parcel; |
| import android.os.Parcelable; |
| import android.os.RemoteException; |
| import android.util.Log; |
| import android.util.SparseArray; |
| |
| import com.android.internal.annotations.VisibleForTesting; |
| import com.android.internal.util.AsyncChannel; |
| import com.android.internal.util.Protocol; |
| |
| import java.util.concurrent.CountDownLatch; |
| |
| /** @hide */ |
| @SystemApi |
| public class RttManager { |
| |
| private static final boolean DBG = false; |
| private static final String TAG = "RttManager"; |
| |
| /** @deprecated It is Not supported anymore. */ |
| @Deprecated |
| public static final int RTT_TYPE_UNSPECIFIED = 0; |
| |
| public static final int RTT_TYPE_ONE_SIDED = 1; |
| public static final int RTT_TYPE_TWO_SIDED = 2; |
| |
| /** @deprecated It is not supported anymore. */ |
| @Deprecated |
| public static final int RTT_TYPE_11_V = 2; |
| |
| /** @deprecated It is not supported anymore. */ |
| @Deprecated |
| public static final int RTT_TYPE_11_MC = 4; |
| |
| /** @deprecated It is not supported anymore. */ |
| @Deprecated |
| public static final int RTT_PEER_TYPE_UNSPECIFIED = 0; |
| |
| public static final int RTT_PEER_TYPE_AP = 1; |
| public static final int RTT_PEER_TYPE_STA = 2; /* requires NAN */ |
| public static final int RTT_PEER_P2P_GO = 3; |
| public static final int RTT_PEER_P2P_CLIENT = 4; |
| public static final int RTT_PEER_NAN = 5; |
| |
| /** |
| * @deprecated It is not supported anymore. |
| * Use {@link android.net.wifi.RttManager#RTT_BW_20_SUPPORT} API. |
| */ |
| @Deprecated |
| public static final int RTT_CHANNEL_WIDTH_20 = 0; |
| |
| /** |
| * @deprecated It is not supported anymore. |
| * Use {@link android.net.wifi.RttManager#RTT_BW_40_SUPPORT} API. |
| */ |
| @Deprecated |
| public static final int RTT_CHANNEL_WIDTH_40 = 1; |
| |
| /** |
| * @deprecated It is not supported anymore. |
| * Use {@link android.net.wifi.RttManager#RTT_BW_80_SUPPORT} API. |
| */ |
| @Deprecated |
| public static final int RTT_CHANNEL_WIDTH_80 = 2; |
| |
| /**@deprecated It is not supported anymore. |
| * Use {@link android.net.wifi.RttManager#RTT_BW_160_SUPPORT} API. |
| */ |
| @Deprecated |
| public static final int RTT_CHANNEL_WIDTH_160 = 3; |
| |
| /**@deprecated not supported anymore*/ |
| @Deprecated |
| public static final int RTT_CHANNEL_WIDTH_80P80 = 4; |
| |
| /**@deprecated It is not supported anymore. |
| * Use {@link android.net.wifi.RttManager#RTT_BW_5_SUPPORT} API. |
| */ |
| @Deprecated |
| public static final int RTT_CHANNEL_WIDTH_5 = 5; |
| |
| /**@deprecated It is not supported anymore. |
| * Use {@link android.net.wifi.RttManager#RTT_BW_10_SUPPORT} API. |
| */ |
| @Deprecated |
| public static final int RTT_CHANNEL_WIDTH_10 = 6; |
| |
| /** @deprecated channel info must be specified. */ |
| @Deprecated |
| public static final int RTT_CHANNEL_WIDTH_UNSPECIFIED = -1; |
| |
| public static final int RTT_STATUS_SUCCESS = 0; |
| /** General failure*/ |
| public static final int RTT_STATUS_FAILURE = 1; |
| /** Destination does not respond to RTT request*/ |
| public static final int RTT_STATUS_FAIL_NO_RSP = 2; |
| /** RTT request is rejected by the destination. Double side RTT only*/ |
| public static final int RTT_STATUS_FAIL_REJECTED = 3; |
| /** */ |
| public static final int RTT_STATUS_FAIL_NOT_SCHEDULED_YET = 4; |
| /** Timing measurement timeout*/ |
| public static final int RTT_STATUS_FAIL_TM_TIMEOUT = 5; |
| /** Destination is on a different channel from the RTT Request*/ |
| public static final int RTT_STATUS_FAIL_AP_ON_DIFF_CHANNEL = 6; |
| /** This type of Ranging is not support by Hardware*/ |
| public static final int RTT_STATUS_FAIL_NO_CAPABILITY = 7; |
| /** Request abort fro uncertain reason*/ |
| public static final int RTT_STATUS_ABORTED = 8; |
| /** The T1-T4 or TOD/TOA Timestamp is illegal*/ |
| public static final int RTT_STATUS_FAIL_INVALID_TS = 9; |
| /** 11mc protocol level failed, eg, unrecognized FTMR/FTM frame*/ |
| public static final int RTT_STATUS_FAIL_PROTOCOL = 10; |
| /** Request can not be scheduled by hardware*/ |
| public static final int RTT_STATUS_FAIL_SCHEDULE = 11; |
| /** destination is busy now, you can try after a specified time from destination*/ |
| public static final int RTT_STATUS_FAIL_BUSY_TRY_LATER = 12; |
| /** Bad Request argument*/ |
| public static final int RTT_STATUS_INVALID_REQ = 13; |
| /** Wifi is not enabled*/ |
| public static final int RTT_STATUS_NO_WIFI = 14; |
| /** Responder overrides param info, cannot range with new params 2-side RTT only*/ |
| public static final int RTT_STATUS_FAIL_FTM_PARAM_OVERRIDE = 15; |
| |
| public static final int REASON_UNSPECIFIED = -1; |
| public static final int REASON_NOT_AVAILABLE = -2; |
| public static final int REASON_INVALID_LISTENER = -3; |
| public static final int REASON_INVALID_REQUEST = -4; |
| /** Do not have required permission */ |
| public static final int REASON_PERMISSION_DENIED = -5; |
| /** Ranging failed because responder role is enabled in STA mode.*/ |
| public static final int |
| REASON_INITIATOR_NOT_ALLOWED_WHEN_RESPONDER_ON = -6; |
| |
| public static final String DESCRIPTION_KEY = "android.net.wifi.RttManager.Description"; |
| |
| /** |
| * RTT BW supported bit mask, used as RTT param bandWidth too |
| */ |
| public static final int RTT_BW_5_SUPPORT = 0x01; |
| public static final int RTT_BW_10_SUPPORT = 0x02; |
| public static final int RTT_BW_20_SUPPORT = 0x04; |
| public static final int RTT_BW_40_SUPPORT = 0x08; |
| public static final int RTT_BW_80_SUPPORT = 0x10; |
| public static final int RTT_BW_160_SUPPORT = 0x20; |
| |
| /** |
| * RTT Preamble Support bit mask |
| */ |
| public static final int PREAMBLE_LEGACY = 0x01; |
| public static final int PREAMBLE_HT = 0x02; |
| public static final int PREAMBLE_VHT = 0x04; |
| |
| /** @deprecated Use the new {@link android.net.wifi.RttManager.RttCapabilities} API */ |
| @Deprecated |
| public class Capabilities { |
| public int supportedType; |
| public int supportedPeerType; |
| } |
| |
| /** @deprecated Use the new {@link android.net.wifi.RttManager#getRttCapabilities()} API.*/ |
| @Deprecated |
| public Capabilities getCapabilities() { |
| return new Capabilities(); |
| } |
| |
| /** |
| * This class describe the RTT capability of the Hardware |
| */ |
| public static class RttCapabilities implements Parcelable { |
| /** @deprecated It is not supported*/ |
| @Deprecated |
| public boolean supportedType; |
| /** @deprecated It is not supported*/ |
| @Deprecated |
| public boolean supportedPeerType; |
| //1-sided rtt measurement is supported |
| public boolean oneSidedRttSupported; |
| //11mc 2-sided rtt measurement is supported |
| public boolean twoSided11McRttSupported; |
| //location configuration information supported |
| public boolean lciSupported; |
| //location civic records supported |
| public boolean lcrSupported; |
| //preamble supported, see bit mask definition above |
| public int preambleSupported; |
| //RTT bandwidth supported |
| public int bwSupported; |
| // Whether STA responder role is supported. |
| public boolean responderSupported; |
| |
| /** Whether the secure RTT protocol is supported. */ |
| public boolean secureRttSupported; |
| |
| /** Draft 11mc version supported, including major and minor version. e.g, draft 4.3 is 43 */ |
| public int mcVersion; |
| |
| @Override |
| public String toString() { |
| StringBuffer sb = new StringBuffer(); |
| sb.append("oneSidedRtt "). |
| append(oneSidedRttSupported ? "is Supported. " : "is not supported. "). |
| append("twoSided11McRtt "). |
| append(twoSided11McRttSupported ? "is Supported. " : "is not supported. "). |
| append("lci "). |
| append(lciSupported ? "is Supported. " : "is not supported. "). |
| append("lcr "). |
| append(lcrSupported ? "is Supported. " : "is not supported. "); |
| |
| if ((preambleSupported & PREAMBLE_LEGACY) != 0) { |
| sb.append("Legacy "); |
| } |
| |
| if ((preambleSupported & PREAMBLE_HT) != 0) { |
| sb.append("HT "); |
| } |
| |
| if ((preambleSupported & PREAMBLE_VHT) != 0) { |
| sb.append("VHT "); |
| } |
| |
| sb.append("is supported. "); |
| |
| if ((bwSupported & RTT_BW_5_SUPPORT) != 0) { |
| sb.append("5 MHz "); |
| } |
| |
| if ((bwSupported & RTT_BW_10_SUPPORT) != 0) { |
| sb.append("10 MHz "); |
| } |
| |
| if ((bwSupported & RTT_BW_20_SUPPORT) != 0) { |
| sb.append("20 MHz "); |
| } |
| |
| if ((bwSupported & RTT_BW_40_SUPPORT) != 0) { |
| sb.append("40 MHz "); |
| } |
| |
| if ((bwSupported & RTT_BW_80_SUPPORT) != 0) { |
| sb.append("80 MHz "); |
| } |
| |
| if ((bwSupported & RTT_BW_160_SUPPORT) != 0) { |
| sb.append("160 MHz "); |
| } |
| |
| sb.append("is supported."); |
| |
| sb.append(" STA responder role is ") |
| .append(responderSupported ? "supported" : "not supported"); |
| sb.append(" Secure RTT protocol is ") |
| .append(secureRttSupported ? "supported" : "not supported"); |
| sb.append(" 11mc version is " + mcVersion); |
| |
| return sb.toString(); |
| } |
| /** Implement the Parcelable interface {@hide} */ |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeInt(oneSidedRttSupported ? 1 : 0); |
| dest.writeInt(twoSided11McRttSupported ? 1 : 0); |
| dest.writeInt(lciSupported ? 1 : 0); |
| dest.writeInt(lcrSupported ? 1 : 0); |
| dest.writeInt(preambleSupported); |
| dest.writeInt(bwSupported); |
| dest.writeInt(responderSupported ? 1 : 0); |
| dest.writeInt(secureRttSupported ? 1 : 0); |
| dest.writeInt(mcVersion); |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public static final Creator<RttCapabilities> CREATOR = |
| new Creator<RttCapabilities>() { |
| @Override |
| public RttCapabilities createFromParcel(Parcel in) { |
| RttCapabilities capabilities = new RttCapabilities(); |
| capabilities.oneSidedRttSupported = (in.readInt() == 1); |
| capabilities.twoSided11McRttSupported = (in.readInt() == 1); |
| capabilities.lciSupported = (in.readInt() == 1); |
| capabilities.lcrSupported = (in.readInt() == 1); |
| capabilities.preambleSupported = in.readInt(); |
| capabilities.bwSupported = in.readInt(); |
| capabilities.responderSupported = (in.readInt() == 1); |
| capabilities.secureRttSupported = (in.readInt() == 1); |
| capabilities.mcVersion = in.readInt(); |
| return capabilities; |
| } |
| /** Implement the Parcelable interface {@hide} */ |
| @Override |
| public RttCapabilities[] newArray(int size) { |
| return new RttCapabilities[size]; |
| } |
| }; |
| } |
| |
| public RttCapabilities getRttCapabilities() { |
| synchronized (sCapabilitiesLock) { |
| if (mRttCapabilities == null) { |
| try { |
| mRttCapabilities = mService.getRttCapabilities(); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } |
| } |
| return mRttCapabilities; |
| } |
| } |
| |
| /** specifies parameters for RTT request */ |
| public static class RttParams { |
| /** |
| * type of destination device being ranged |
| * currently only support RTT_PEER_TYPE_AP |
| * Range:RTT_PEER_TYPE_xxxx Default value:RTT_PEER_TYPE_AP |
| */ |
| public int deviceType; |
| |
| /** |
| * type of RTT measurement method. Need check scan result and RttCapabilities first |
| * Range: RTT_TYPE_ONE_SIDED or RTT_TYPE_TWO_SIDED |
| * Default value: RTT_TYPE_ONE_SIDED |
| */ |
| public int requestType; |
| |
| /** |
| * Whether the secure RTT protocol needs to be used for ranging this peer device. |
| */ |
| public boolean secure; |
| |
| /** |
| * mac address of the device being ranged |
| * Default value: null |
| */ |
| public String bssid; |
| |
| /** |
| * The primary control channel over which the client is |
| * communicating with the AP.Same as ScanResult.frequency |
| * Default value: 0 |
| */ |
| public int frequency; |
| |
| /** |
| * channel width of the destination AP. Same as ScanResult.channelWidth |
| * Default value: 0 |
| */ |
| public int channelWidth; |
| |
| /** |
| * Not used if the AP bandwidth is 20 MHz |
| * If the AP use 40, 80 or 160 MHz, this is the center frequency |
| * if the AP use 80 + 80 MHz, this is the center frequency of the first segment |
| * same as ScanResult.centerFreq0 |
| * Default value: 0 |
| */ |
| public int centerFreq0; |
| |
| /** |
| * Only used if the AP bandwidth is 80 + 80 MHz |
| * if the AP use 80 + 80 MHz, this is the center frequency of the second segment |
| * same as ScanResult.centerFreq1 |
| * Default value: 0 |
| */ |
| public int centerFreq1; |
| |
| /** |
| * number of samples to be taken |
| * @deprecated Use the new {@link android.net.wifi.RttManager.RttParams#numSamplesPerBurst} |
| */ |
| @Deprecated |
| public int num_samples; |
| |
| /** |
| * number of retries if a sample fails |
| * @deprecated |
| * Use {@link android.net.wifi.RttManager.RttParams#numRetriesPerMeasurementFrame} API. |
| */ |
| @Deprecated |
| public int num_retries; |
| |
| /** Number of burst in exp , 2^x. 0 means single shot measurement, range 0-15 |
| * Currently only single shot is supported |
| * Default value: 0 |
| */ |
| public int numberBurst; |
| |
| /** |
| * valid only if numberBurst > 1, interval between burst(100ms). |
| * Range : 0-31, 0--means no specific |
| * Default value: 0 |
| */ |
| public int interval; |
| |
| /** |
| * number of samples to be taken in one burst |
| * Range: 1-31 |
| * Default value: 8 |
| */ |
| public int numSamplesPerBurst; |
| |
| /** number of retries for each measurement frame if a sample fails |
| * Only used by single side RTT, |
| * Range 0 - 3 Default value: 0 |
| */ |
| public int numRetriesPerMeasurementFrame; |
| |
| /** |
| * number of retries for FTMR frame (control frame) if it fails. |
| * Only used by 80211MC double side RTT |
| * Range: 0-3 Default Value : 0 |
| */ |
| public int numRetriesPerFTMR; |
| |
| /** |
| * Request LCI information, only available when choose double side RTT measurement |
| * need check RttCapabilties first. |
| * Default value: false |
| * */ |
| public boolean LCIRequest; |
| |
| /** |
| * Request LCR information, only available when choose double side RTT measurement |
| * need check RttCapabilties first. |
| * Default value: false |
| * */ |
| public boolean LCRRequest; |
| |
| /** |
| * Timeout for each burst, (250 * 2^x) us, |
| * Range 1-11 and 15. 15 means no control Default value: 15 |
| * */ |
| public int burstTimeout; |
| |
| /** preamble used for RTT measurement |
| * Range: PREAMBLE_LEGACY, PREAMBLE_HT, PREAMBLE_VHT |
| * Default value: PREAMBLE_HT |
| */ |
| public int preamble; |
| |
| /** bandWidth used for RTT measurement.User need verify the highest BW the destination |
| * support (from scan result etc) before set this value. Wider channels result usually give |
| * better accuracy. However, the frame loss can increase too. |
| * should be one of RTT_BW_5_SUPPORT to RTT_BW_160_SUPPORT. However, need check |
| * RttCapabilities firstto verify HW support this bandwidth. |
| * Default value:RTT_BW_20_SUPPORT |
| */ |
| public int bandwidth; |
| |
| public RttParams() { |
| //provide initial value for RttParams |
| deviceType = RTT_PEER_TYPE_AP; |
| requestType = RTT_TYPE_ONE_SIDED; |
| numberBurst = 0; |
| numSamplesPerBurst = 8; |
| numRetriesPerMeasurementFrame = 0; |
| numRetriesPerFTMR = 0; |
| burstTimeout = 15; |
| preamble = PREAMBLE_HT; |
| bandwidth = RTT_BW_20_SUPPORT; |
| } |
| } |
| |
| /** pseudo-private class used to parcel arguments */ |
| public static class ParcelableRttParams implements Parcelable { |
| |
| @NonNull |
| public RttParams mParams[]; |
| |
| /** |
| * @hide |
| */ |
| @VisibleForTesting |
| public ParcelableRttParams(RttParams[] params) { |
| mParams = (params == null ? new RttParams[0] : params); |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeInt(mParams.length); |
| |
| for (RttParams params : mParams) { |
| dest.writeInt(params.deviceType); |
| dest.writeInt(params.requestType); |
| dest.writeByte(params.secure ? (byte) 1 : 0); |
| dest.writeString(params.bssid); |
| dest.writeInt(params.channelWidth); |
| dest.writeInt(params.frequency); |
| dest.writeInt(params.centerFreq0); |
| dest.writeInt(params.centerFreq1); |
| dest.writeInt(params.numberBurst); |
| dest.writeInt(params.interval); |
| dest.writeInt(params.numSamplesPerBurst); |
| dest.writeInt(params.numRetriesPerMeasurementFrame); |
| dest.writeInt(params.numRetriesPerFTMR); |
| dest.writeInt(params.LCIRequest ? 1 : 0); |
| dest.writeInt(params.LCRRequest ? 1 : 0); |
| dest.writeInt(params.burstTimeout); |
| dest.writeInt(params.preamble); |
| dest.writeInt(params.bandwidth); |
| } |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public static final Creator<ParcelableRttParams> CREATOR = |
| new Creator<ParcelableRttParams>() { |
| @Override |
| public ParcelableRttParams createFromParcel(Parcel in) { |
| |
| int num = in.readInt(); |
| RttParams params[] = new RttParams[num]; |
| for (int i = 0; i < num; i++) { |
| params[i] = new RttParams(); |
| params[i].deviceType = in.readInt(); |
| params[i].requestType = in.readInt(); |
| params[i].secure = (in.readByte() != 0); |
| params[i].bssid = in.readString(); |
| params[i].channelWidth = in.readInt(); |
| params[i].frequency = in.readInt(); |
| params[i].centerFreq0 = in.readInt(); |
| params[i].centerFreq1 = in.readInt(); |
| params[i].numberBurst = in.readInt(); |
| params[i].interval = in.readInt(); |
| params[i].numSamplesPerBurst = in.readInt(); |
| params[i].numRetriesPerMeasurementFrame = in.readInt(); |
| params[i].numRetriesPerFTMR = in.readInt(); |
| params[i].LCIRequest = (in.readInt() == 1); |
| params[i].LCRRequest = (in.readInt() == 1); |
| params[i].burstTimeout = in.readInt(); |
| params[i].preamble = in.readInt(); |
| params[i].bandwidth = in.readInt(); |
| } |
| |
| ParcelableRttParams parcelableParams = new ParcelableRttParams(params); |
| return parcelableParams; |
| } |
| |
| @Override |
| public ParcelableRttParams[] newArray(int size) { |
| return new ParcelableRttParams[size]; |
| } |
| }; |
| } |
| |
| public static class WifiInformationElement { |
| /** Information Element ID 0xFF means element is invalid. */ |
| public byte id; |
| public byte[] data; |
| } |
| /** specifies RTT results */ |
| public static class RttResult { |
| /** mac address of the device being ranged. */ |
| public String bssid; |
| |
| /** # of burst for this measurement. */ |
| public int burstNumber; |
| |
| /** total number of measurement frames attempted in this measurement. */ |
| public int measurementFrameNumber; |
| |
| /** total successful number of measurement frames in this measurement. */ |
| public int successMeasurementFrameNumber; |
| |
| /** |
| * Maximum number of frames per burst supported by peer. Two side RTT only |
| * Valid only if less than request |
| */ |
| public int frameNumberPerBurstPeer; |
| |
| /** status of the request */ |
| public int status; |
| |
| /** |
| * type of the request used |
| * @deprecated Use {@link android.net.wifi.RttManager.RttResult#measurementType} |
| */ |
| @Deprecated |
| public int requestType; |
| |
| /** RTT measurement method type used, should be one of RTT_TYPE_ONE_SIDED or |
| * RTT_TYPE_TWO_SIDED. |
| */ |
| public int measurementType; |
| |
| /** |
| * only valid when status == RTT_STATUS_FAIL_BUSY_TRY_LATER |
| * please retry RTT measurement after this duration since peer indicate busy at ths moment |
| * Unit S Range:1-31 |
| */ |
| public int retryAfterDuration; |
| |
| /** timestamp of completion, in microsecond since boot. */ |
| public long ts; |
| |
| /** average RSSI observed, unit of 0.5 dB. */ |
| public int rssi; |
| |
| /** |
| * RSSI spread (i.e. max - min) |
| * @deprecated Use {@link android.net.wifi.RttManager.RttResult#rssiSpread} API. |
| */ |
| @Deprecated |
| public int rssi_spread; |
| |
| /**RSSI spread (i.e. max - min), unit of 0.5 dB. */ |
| public int rssiSpread; |
| |
| /** |
| * average transmit rate |
| * @deprecated Use {@link android.net.wifi.RttManager.RttResult#txRate} API. |
| */ |
| @Deprecated |
| public int tx_rate; |
| |
| /** average transmit rate. Unit (100kbps). */ |
| public int txRate; |
| |
| /** average receiving rate Unit (100kbps). */ |
| public int rxRate; |
| |
| /** |
| * average round trip time in nano second |
| * @deprecated Use {@link android.net.wifi.RttManager.RttResult#rtt} API. |
| */ |
| @Deprecated |
| public long rtt_ns; |
| |
| /** average round trip time in 0.1 nano second. */ |
| public long rtt; |
| |
| /** |
| * standard deviation observed in round trip time |
| * @deprecated Use {@link android.net.wifi.RttManager.RttResult#rttStandardDeviation} API. |
| */ |
| @Deprecated |
| public long rtt_sd_ns; |
| |
| /** standard deviation of RTT in 0.1 ns. */ |
| public long rttStandardDeviation; |
| |
| /** |
| * spread (i.e. max - min) round trip time |
| * @deprecated Use {@link android.net.wifi.RttManager.RttResult#rttSpread} API. |
| */ |
| @Deprecated |
| public long rtt_spread_ns; |
| |
| /** spread (i.e. max - min) RTT in 0.1 ns. */ |
| public long rttSpread; |
| |
| /** |
| * average distance in centimeter, computed based on rtt_ns |
| * @deprecated use {@link android.net.wifi.RttManager.RttResult#distance} API. |
| */ |
| @Deprecated |
| public int distance_cm; |
| |
| /** average distance in cm, computed based on rtt. */ |
| public int distance; |
| |
| /** |
| * standard deviation observed in distance |
| * @deprecated |
| * Use {@link .android.net.wifi.RttManager.RttResult#distanceStandardDeviation} API. |
| */ |
| @Deprecated |
| public int distance_sd_cm; |
| |
| /** standard deviation observed in distance in cm. */ |
| public int distanceStandardDeviation; |
| |
| /** |
| * spread (i.e. max - min) distance |
| * @deprecate Use {@link android.net.wifi.RttManager.RttResult#distanceSpread} API. |
| */ |
| @Deprecated |
| public int distance_spread_cm; |
| |
| /** spread (i.e. max - min) distance in cm. */ |
| public int distanceSpread; |
| |
| /** the duration of this measurement burst, unit ms. */ |
| public int burstDuration; |
| |
| /** Burst number supported by peer after negotiation, 2side RTT only*/ |
| public int negotiatedBurstNum; |
| |
| /** LCI information Element, only available for double side RTT. */ |
| public WifiInformationElement LCI; |
| |
| /** LCR information Element, only available to double side RTT. */ |
| public WifiInformationElement LCR; |
| |
| /** |
| * Whether the secure RTT protocol was used for ranging. |
| */ |
| public boolean secure; |
| } |
| |
| |
| /** pseudo-private class used to parcel results. */ |
| public static class ParcelableRttResults implements Parcelable { |
| |
| public RttResult mResults[]; |
| |
| public ParcelableRttResults(RttResult[] results) { |
| mResults = results; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| if (mResults != null) { |
| dest.writeInt(mResults.length); |
| for (RttResult result : mResults) { |
| dest.writeString(result.bssid); |
| dest.writeInt(result.burstNumber); |
| dest.writeInt(result.measurementFrameNumber); |
| dest.writeInt(result.successMeasurementFrameNumber); |
| dest.writeInt(result.frameNumberPerBurstPeer); |
| dest.writeInt(result.status); |
| dest.writeInt(result.measurementType); |
| dest.writeInt(result.retryAfterDuration); |
| dest.writeLong(result.ts); |
| dest.writeInt(result.rssi); |
| dest.writeInt(result.rssiSpread); |
| dest.writeInt(result.txRate); |
| dest.writeLong(result.rtt); |
| dest.writeLong(result.rttStandardDeviation); |
| dest.writeLong(result.rttSpread); |
| dest.writeInt(result.distance); |
| dest.writeInt(result.distanceStandardDeviation); |
| dest.writeInt(result.distanceSpread); |
| dest.writeInt(result.burstDuration); |
| dest.writeInt(result.negotiatedBurstNum); |
| dest.writeByte(result.LCI.id); |
| if (result.LCI.id != (byte) 0xFF) { |
| dest.writeByte((byte)result.LCI.data.length); |
| dest.writeByteArray(result.LCI.data); |
| } |
| dest.writeByte(result.LCR.id); |
| if (result.LCR.id != (byte) 0xFF) { |
| dest.writeInt((byte) result.LCR.data.length); |
| dest.writeByte(result.LCR.id); |
| } |
| dest.writeByte(result.secure ? (byte) 1 : 0); |
| } |
| } else { |
| dest.writeInt(0); |
| } |
| } |
| |
| /** Implement the Parcelable interface {@hide} */ |
| public static final Creator<ParcelableRttResults> CREATOR = |
| new Creator<ParcelableRttResults>() { |
| @Override |
| public ParcelableRttResults createFromParcel(Parcel in) { |
| |
| int num = in.readInt(); |
| |
| if (num == 0) { |
| return new ParcelableRttResults(null); |
| } |
| |
| RttResult results[] = new RttResult[num]; |
| for (int i = 0; i < num; i++) { |
| results[i] = new RttResult(); |
| results[i].bssid = in.readString(); |
| results[i].burstNumber = in.readInt(); |
| results[i].measurementFrameNumber = in.readInt(); |
| results[i].successMeasurementFrameNumber = in.readInt(); |
| results[i].frameNumberPerBurstPeer = in.readInt(); |
| results[i].status = in.readInt(); |
| results[i].measurementType = in.readInt(); |
| results[i].retryAfterDuration = in.readInt(); |
| results[i].ts = in.readLong(); |
| results[i].rssi = in.readInt(); |
| results[i].rssiSpread = in.readInt(); |
| results[i].txRate = in.readInt(); |
| results[i].rtt = in.readLong(); |
| results[i].rttStandardDeviation = in.readLong(); |
| results[i].rttSpread = in.readLong(); |
| results[i].distance = in.readInt(); |
| results[i].distanceStandardDeviation = in.readInt(); |
| results[i].distanceSpread = in.readInt(); |
| results[i].burstDuration = in.readInt(); |
| results[i].negotiatedBurstNum = in.readInt(); |
| results[i].LCI = new WifiInformationElement(); |
| results[i].LCI.id = in.readByte(); |
| if (results[i].LCI.id != (byte) 0xFF) { |
| byte length = in.readByte(); |
| results[i].LCI.data = new byte[length]; |
| in.readByteArray(results[i].LCI.data); |
| } |
| results[i].LCR = new WifiInformationElement(); |
| results[i].LCR.id = in.readByte(); |
| if (results[i].LCR.id != (byte) 0xFF) { |
| byte length = in.readByte(); |
| results[i].LCR.data = new byte[length]; |
| in.readByteArray(results[i].LCR.data); |
| } |
| results[i].secure = (in.readByte() != 0); |
| } |
| |
| ParcelableRttResults parcelableResults = new ParcelableRttResults(results); |
| return parcelableResults; |
| } |
| |
| @Override |
| public ParcelableRttResults[] newArray(int size) { |
| return new ParcelableRttResults[size]; |
| } |
| }; |
| } |
| |
| |
| public static interface RttListener { |
| public void onSuccess(RttResult[] results); |
| public void onFailure(int reason, String description); |
| public void onAborted(); |
| } |
| |
| private boolean rttParamSanity(RttParams params, int index) { |
| if (mRttCapabilities == null) { |
| if(getRttCapabilities() == null) { |
| Log.e(TAG, "Can not get RTT capabilities"); |
| throw new IllegalStateException("RTT chip is not working"); |
| } |
| } |
| |
| if (params.deviceType != RTT_PEER_TYPE_AP) { |
| return false; |
| } else if (params.requestType != RTT_TYPE_ONE_SIDED && params.requestType != |
| RTT_TYPE_TWO_SIDED) { |
| Log.e(TAG, "Request " + index + ": Illegal Request Type: " + params.requestType); |
| return false; |
| } else if (params.requestType == RTT_TYPE_ONE_SIDED && |
| !mRttCapabilities.oneSidedRttSupported) { |
| Log.e(TAG, "Request " + index + ": One side RTT is not supported"); |
| return false; |
| } else if (params.requestType == RTT_TYPE_TWO_SIDED && |
| !mRttCapabilities.twoSided11McRttSupported) { |
| Log.e(TAG, "Request " + index + ": two side RTT is not supported"); |
| return false; |
| } else if(params.bssid == null || params.bssid.isEmpty()) { |
| Log.e(TAG,"No BSSID in params"); |
| return false; |
| } else if ( params.numberBurst != 0 ) { |
| Log.e(TAG, "Request " + index + ": Illegal number of burst: " + params.numberBurst); |
| return false; |
| } else if (params.numSamplesPerBurst <= 0 || params.numSamplesPerBurst > 31) { |
| Log.e(TAG, "Request " + index + ": Illegal sample number per burst: " + |
| params.numSamplesPerBurst); |
| return false; |
| } else if (params.numRetriesPerMeasurementFrame < 0 || |
| params.numRetriesPerMeasurementFrame > 3) { |
| Log.e(TAG, "Request " + index + ": Illegal measurement frame retry number:" + |
| params.numRetriesPerMeasurementFrame); |
| return false; |
| } else if(params.numRetriesPerFTMR < 0 || |
| params.numRetriesPerFTMR > 3) { |
| Log.e(TAG, "Request " + index + ": Illegal FTMR frame retry number:" + |
| params.numRetriesPerFTMR); |
| return false; |
| } else if (params.LCIRequest && !mRttCapabilities.lciSupported) { |
| Log.e(TAG, "Request " + index + ": LCI is not supported"); |
| return false; |
| } else if (params.LCRRequest && !mRttCapabilities.lcrSupported) { |
| Log.e(TAG, "Request " + index + ": LCR is not supported"); |
| return false; |
| } else if (params.burstTimeout < 1 || |
| (params.burstTimeout > 11 && params.burstTimeout != 15)){ |
| Log.e(TAG, "Request " + index + ": Illegal burst timeout: " + params.burstTimeout); |
| return false; |
| } else if ((params.preamble & mRttCapabilities.preambleSupported) == 0) { |
| Log.e(TAG, "Request " + index + ": Do not support this preamble: " + params.preamble); |
| return false; |
| } else if ((params.bandwidth & mRttCapabilities.bwSupported) == 0) { |
| Log.e(TAG, "Request " + index + ": Do not support this bandwidth: " + params.bandwidth); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /** |
| * Request to start an RTT ranging |
| * |
| * @param params -- RTT request Parameters |
| * @param listener -- Call back to inform RTT result |
| * @exception throw IllegalArgumentException when params are illegal |
| * throw IllegalStateException when RttCapabilities do not exist |
| */ |
| |
| public void startRanging(RttParams[] params, RttListener listener) { |
| int index = 0; |
| for(RttParams rttParam : params) { |
| if (!rttParamSanity(rttParam, index)) { |
| throw new IllegalArgumentException("RTT Request Parameter Illegal"); |
| } |
| index++; |
| } |
| validateChannel(); |
| ParcelableRttParams parcelableParams = new ParcelableRttParams(params); |
| Log.i(TAG, "Send RTT request to RTT Service"); |
| sAsyncChannel.sendMessage(CMD_OP_START_RANGING, |
| 0, putListener(listener), parcelableParams); |
| } |
| |
| public void stopRanging(RttListener listener) { |
| validateChannel(); |
| sAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener)); |
| } |
| |
| /** |
| * Callbacks for responder operations. |
| * <p> |
| * A {@link ResponderCallback} is the handle to the calling client. {@link RttManager} will keep |
| * a reference to the callback for the entire period when responder is enabled. The same |
| * callback as used in enabling responder needs to be passed for disabling responder. |
| * The client can freely destroy or reuse the callback after {@link RttManager#disableResponder} |
| * is called. |
| */ |
| public abstract static class ResponderCallback { |
| /** Callback when responder is enabled. */ |
| public abstract void onResponderEnabled(ResponderConfig config); |
| /** Callback when enabling responder failed. */ |
| public abstract void onResponderEnableFailure(int reason); |
| // TODO: consider adding onResponderAborted once it's supported. |
| } |
| |
| /** |
| * Enable Wi-Fi RTT responder mode on the device. The enabling result will be delivered via |
| * {@code callback}. |
| * <p> |
| * Note calling this method with the same callback when the responder is already enabled won't |
| * change the responder state, a cached {@link ResponderConfig} from the last enabling will be |
| * returned through the callback. |
| * |
| * @param callback Callback for responder enabling/disabling result. |
| * @throws IllegalArgumentException If {@code callback} is null. |
| */ |
| public void enableResponder(ResponderCallback callback) { |
| if (callback == null) { |
| throw new IllegalArgumentException("callback cannot be null"); |
| } |
| validateChannel(); |
| int key = putListenerIfAbsent(callback); |
| sAsyncChannel.sendMessage(CMD_OP_ENABLE_RESPONDER, 0, key); |
| } |
| |
| /** |
| * Disable Wi-Fi RTT responder mode on the device. The {@code callback} needs to be the |
| * same one used in {@link #enableResponder(ResponderCallback)}. |
| * <p> |
| * Calling this method when responder isn't enabled won't have any effect. The callback can be |
| * reused for enabling responder after this method is called. |
| * |
| * @param callback The same callback used for enabling responder. |
| * @throws IllegalArgumentException If {@code callback} is null. |
| */ |
| public void disableResponder(ResponderCallback callback) { |
| if (callback == null) { |
| throw new IllegalArgumentException("callback cannot be null"); |
| } |
| validateChannel(); |
| int key = removeListener(callback); |
| if (key == INVALID_KEY) { |
| Log.e(TAG, "responder not enabled yet"); |
| return; |
| } |
| sAsyncChannel.sendMessage(CMD_OP_DISABLE_RESPONDER, 0, key); |
| } |
| |
| /** |
| * Configuration used for RTT responder mode. The configuration information can be used by a |
| * peer device to range the responder. |
| * |
| * @see ScanResult |
| */ |
| public static class ResponderConfig implements Parcelable { |
| |
| // TODO: make all fields final once we can get mac address from responder HAL APIs. |
| /** |
| * Wi-Fi mac address used for responder mode. |
| */ |
| public String macAddress = ""; |
| |
| /** |
| * The primary 20 MHz frequency (in MHz) of the channel where responder is enabled. |
| * @see ScanResult#frequency |
| */ |
| public int frequency; |
| |
| /** |
| * Center frequency of the channel where responder is enabled on. Only in use when channel |
| * width is at least 40MHz. |
| * @see ScanResult#centerFreq0 |
| */ |
| public int centerFreq0; |
| |
| /** |
| * Center frequency of the second segment when channel width is 80 + 80 MHz. |
| * @see ScanResult#centerFreq1 |
| */ |
| public int centerFreq1; |
| |
| /** |
| * Width of the channel where responder is enabled on. |
| * @see ScanResult#channelWidth |
| */ |
| public int channelWidth; |
| |
| /** |
| * Preamble supported by responder. |
| */ |
| public int preamble; |
| |
| @Override |
| public String toString() { |
| StringBuilder builder = new StringBuilder(); |
| builder.append("macAddress = ").append(macAddress) |
| .append(" frequency = ").append(frequency) |
| .append(" centerFreq0 = ").append(centerFreq0) |
| .append(" centerFreq1 = ").append(centerFreq1) |
| .append(" channelWidth = ").append(channelWidth) |
| .append(" preamble = ").append(preamble); |
| return builder.toString(); |
| } |
| |
| @Override |
| public int describeContents() { |
| return 0; |
| } |
| |
| @Override |
| public void writeToParcel(Parcel dest, int flags) { |
| dest.writeString(macAddress); |
| dest.writeInt(frequency); |
| dest.writeInt(centerFreq0); |
| dest.writeInt(centerFreq1); |
| dest.writeInt(channelWidth); |
| dest.writeInt(preamble); |
| } |
| |
| /** Implement {@link Parcelable} interface */ |
| public static final Parcelable.Creator<ResponderConfig> CREATOR = |
| new Parcelable.Creator<ResponderConfig>() { |
| @Override |
| public ResponderConfig createFromParcel(Parcel in) { |
| ResponderConfig config = new ResponderConfig(); |
| config.macAddress = in.readString(); |
| config.frequency = in.readInt(); |
| config.centerFreq0 = in.readInt(); |
| config.centerFreq1 = in.readInt(); |
| config.channelWidth = in.readInt(); |
| config.preamble = in.readInt(); |
| return config; |
| } |
| |
| @Override |
| public ResponderConfig[] newArray(int size) { |
| return new ResponderConfig[size]; |
| } |
| }; |
| |
| } |
| |
| /* private methods */ |
| public static final int BASE = Protocol.BASE_WIFI_RTT_MANAGER; |
| |
| public static final int CMD_OP_START_RANGING = BASE + 0; |
| public static final int CMD_OP_STOP_RANGING = BASE + 1; |
| public static final int CMD_OP_FAILED = BASE + 2; |
| public static final int CMD_OP_SUCCEEDED = BASE + 3; |
| public static final int CMD_OP_ABORTED = BASE + 4; |
| public static final int CMD_OP_ENABLE_RESPONDER = BASE + 5; |
| public static final int CMD_OP_DISABLE_RESPONDER = BASE + 6; |
| public static final int |
| CMD_OP_ENALBE_RESPONDER_SUCCEEDED = BASE + 7; |
| public static final int |
| CMD_OP_ENALBE_RESPONDER_FAILED = BASE + 8; |
| |
| private Context mContext; |
| private IRttManager mService; |
| private RttCapabilities mRttCapabilities; |
| |
| private static final int INVALID_KEY = 0; |
| private static int sListenerKey = 1; |
| |
| private static final SparseArray sListenerMap = new SparseArray(); |
| private static final Object sListenerMapLock = new Object(); |
| private static final Object sCapabilitiesLock = new Object(); |
| |
| private static AsyncChannel sAsyncChannel; |
| private static CountDownLatch sConnected; |
| |
| private static final Object sThreadRefLock = new Object(); |
| private static int sThreadRefCount; |
| private static HandlerThread sHandlerThread; |
| |
| /** |
| * Create a new WifiScanner instance. |
| * Applications will almost always want to use |
| * {@link android.content.Context#getSystemService Context.getSystemService()} to retrieve |
| * the standard {@link android.content.Context#WIFI_RTT_SERVICE Context.WIFI_RTT_SERVICE}. |
| * @param context the application context |
| * @param service the Binder interface |
| * @hide |
| */ |
| |
| public RttManager(Context context, IRttManager service) { |
| mContext = context; |
| mService = service; |
| init(); |
| } |
| |
| private void init() { |
| synchronized (sThreadRefLock) { |
| if (++sThreadRefCount == 1) { |
| Messenger messenger = null; |
| try { |
| Log.d(TAG, "Get the messenger from " + mService); |
| messenger = mService.getMessenger(); |
| } catch (RemoteException e) { |
| throw e.rethrowFromSystemServer(); |
| } catch (SecurityException e) { |
| /* do nothing */ |
| } |
| |
| if (messenger == null) { |
| sAsyncChannel = null; |
| return; |
| } |
| |
| sHandlerThread = new HandlerThread("RttManager"); |
| sAsyncChannel = new AsyncChannel(); |
| sConnected = new CountDownLatch(1); |
| |
| sHandlerThread.start(); |
| Handler handler = new ServiceHandler(sHandlerThread.getLooper()); |
| sAsyncChannel.connect(mContext, handler, messenger); |
| try { |
| sConnected.await(); |
| } catch (InterruptedException e) { |
| Log.e(TAG, "interrupted wait at init"); |
| } |
| } |
| } |
| } |
| |
| private void validateChannel() { |
| if (sAsyncChannel == null) throw new IllegalStateException( |
| "No permission to access and change wifi or a bad initialization"); |
| } |
| |
| private static int putListener(Object listener) { |
| if (listener == null) return INVALID_KEY; |
| int key; |
| synchronized (sListenerMapLock) { |
| do { |
| key = sListenerKey++; |
| } while (key == INVALID_KEY); |
| sListenerMap.put(key, listener); |
| } |
| return key; |
| } |
| |
| // Insert a listener if it doesn't exist in sListenerMap. Returns the key of the listener. |
| private static int putListenerIfAbsent(Object listener) { |
| if (listener == null) return INVALID_KEY; |
| synchronized (sListenerMapLock) { |
| int key = getListenerKey(listener); |
| if (key != INVALID_KEY) { |
| return key; |
| } |
| do { |
| key = sListenerKey++; |
| } while (key == INVALID_KEY); |
| sListenerMap.put(key, listener); |
| return key; |
| } |
| |
| } |
| |
| private static Object getListener(int key) { |
| if (key == INVALID_KEY) return null; |
| synchronized (sListenerMapLock) { |
| Object listener = sListenerMap.get(key); |
| return listener; |
| } |
| } |
| |
| private static int getListenerKey(Object listener) { |
| if (listener == null) return INVALID_KEY; |
| synchronized (sListenerMapLock) { |
| int index = sListenerMap.indexOfValue(listener); |
| if (index == -1) { |
| return INVALID_KEY; |
| } else { |
| return sListenerMap.keyAt(index); |
| } |
| } |
| } |
| |
| private static Object removeListener(int key) { |
| if (key == INVALID_KEY) return null; |
| synchronized (sListenerMapLock) { |
| Object listener = sListenerMap.get(key); |
| sListenerMap.remove(key); |
| return listener; |
| } |
| } |
| |
| private static int removeListener(Object listener) { |
| int key = getListenerKey(listener); |
| if (key == INVALID_KEY) return key; |
| synchronized (sListenerMapLock) { |
| sListenerMap.remove(key); |
| return key; |
| } |
| } |
| |
| private static class ServiceHandler extends Handler { |
| ServiceHandler(Looper looper) { |
| super(looper); |
| } |
| @Override |
| public void handleMessage(Message msg) { |
| Log.i(TAG, "RTT manager get message: " + msg.what); |
| switch (msg.what) { |
| case AsyncChannel.CMD_CHANNEL_HALF_CONNECTED: |
| if (msg.arg1 == AsyncChannel.STATUS_SUCCESSFUL) { |
| sAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); |
| } else { |
| Log.e(TAG, "Failed to set up channel connection"); |
| // This will cause all further async API calls on the WifiManager |
| // to fail and throw an exception |
| sAsyncChannel = null; |
| } |
| return; |
| case AsyncChannel.CMD_CHANNEL_FULLY_CONNECTED: |
| sConnected.countDown(); |
| return; |
| case AsyncChannel.CMD_CHANNEL_DISCONNECTED: |
| Log.e(TAG, "Channel connection lost"); |
| // This will cause all further async API calls on the WifiManager |
| // to fail and throw an exception |
| sAsyncChannel = null; |
| getLooper().quit(); |
| return; |
| } |
| |
| Object listener = getListener(msg.arg2); |
| if (listener == null) { |
| Log.e(TAG, "invalid listener key = " + msg.arg2 ); |
| return; |
| } else { |
| Log.i(TAG, "listener key = " + msg.arg2); |
| } |
| |
| switch (msg.what) { |
| /* ActionListeners grouped together */ |
| case CMD_OP_SUCCEEDED : |
| reportSuccess(listener, msg); |
| removeListener(msg.arg2); |
| break; |
| case CMD_OP_FAILED : |
| reportFailure(listener, msg); |
| removeListener(msg.arg2); |
| break; |
| case CMD_OP_ABORTED : |
| ((RttListener) listener).onAborted(); |
| removeListener(msg.arg2); |
| break; |
| case CMD_OP_ENALBE_RESPONDER_SUCCEEDED: |
| ResponderConfig config = (ResponderConfig) msg.obj; |
| ((ResponderCallback) (listener)).onResponderEnabled(config); |
| break; |
| case CMD_OP_ENALBE_RESPONDER_FAILED: |
| ((ResponderCallback) (listener)).onResponderEnableFailure(msg.arg1); |
| removeListener(msg.arg2); |
| break; |
| default: |
| if (DBG) Log.d(TAG, "Ignoring message " + msg.what); |
| return; |
| } |
| } |
| |
| void reportSuccess(Object listener, Message msg) { |
| RttListener rttListener = (RttListener) listener; |
| ParcelableRttResults parcelableResults = (ParcelableRttResults) msg.obj; |
| ((RttListener) listener).onSuccess(parcelableResults.mResults); |
| } |
| |
| void reportFailure(Object listener, Message msg) { |
| RttListener rttListener = (RttListener) listener; |
| Bundle bundle = (Bundle) msg.obj; |
| ((RttListener) listener).onFailure(msg.arg1, bundle.getString(DESCRIPTION_KEY)); |
| } |
| } |
| |
| } |
| |