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.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; /** @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 CREATOR = new Creator() { @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 (mCapabilitiesLock) { 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 CREATOR = new Creator() { @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 * @deprecated 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 CREATOR = new Creator() { @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"); mAsyncChannel.sendMessage(CMD_OP_START_RANGING, 0, putListener(listener), parcelableParams); } public void stopRanging(RttListener listener) { validateChannel(); mAsyncChannel.sendMessage(CMD_OP_STOP_RANGING, 0, removeListener(listener)); } /** * Callbacks for responder operations. *

* 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}. *

* 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); mAsyncChannel.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)}. *

* 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; } mAsyncChannel.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 CREATOR = new Parcelable.Creator() { @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 static final int INVALID_KEY = 0; private final Context mContext; private final IRttManager mService; private final SparseArray mListenerMap = new SparseArray(); private final Object mListenerMapLock = new Object(); private final Object mCapabilitiesLock = new Object(); private RttCapabilities mRttCapabilities; private int mListenerKey = 1; private AsyncChannel mAsyncChannel; /** * 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 * @param looper Looper for running the callbacks. * * @hide */ public RttManager(Context context, IRttManager service, Looper looper) { mContext = context; mService = service; Messenger messenger = null; try { Log.d(TAG, "Get the messenger from " + mService); messenger = mService.getMessenger(); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } if (messenger == null) { throw new IllegalStateException("getMessenger() returned null! This is invalid."); } mAsyncChannel = new AsyncChannel(); Handler handler = new ServiceHandler(looper); mAsyncChannel.connectSync(mContext, handler, messenger); // We cannot use fullyConnectSync because it sends the FULL_CONNECTION message // synchronously, which causes RttService to receive the wrong replyTo value. mAsyncChannel.sendMessage(AsyncChannel.CMD_CHANNEL_FULL_CONNECTION); } private void validateChannel() { if (mAsyncChannel == null) throw new IllegalStateException( "No permission to access and change wifi or a bad initialization"); } private int putListener(Object listener) { if (listener == null) return INVALID_KEY; int key; synchronized (mListenerMapLock) { do { key = mListenerKey++; } while (key == INVALID_KEY); mListenerMap.put(key, listener); } return key; } // Insert a listener if it doesn't exist in mListenerMap. Returns the key of the listener. private int putListenerIfAbsent(Object listener) { if (listener == null) return INVALID_KEY; synchronized (mListenerMapLock) { int key = getListenerKey(listener); if (key != INVALID_KEY) { return key; } do { key = mListenerKey++; } while (key == INVALID_KEY); mListenerMap.put(key, listener); return key; } } private Object getListener(int key) { if (key == INVALID_KEY) return null; synchronized (mListenerMapLock) { Object listener = mListenerMap.get(key); return listener; } } private int getListenerKey(Object listener) { if (listener == null) return INVALID_KEY; synchronized (mListenerMapLock) { int index = mListenerMap.indexOfValue(listener); if (index == -1) { return INVALID_KEY; } else { return mListenerMap.keyAt(index); } } } private Object removeListener(int key) { if (key == INVALID_KEY) return null; synchronized (mListenerMapLock) { Object listener = mListenerMap.get(key); mListenerMap.remove(key); return listener; } } private int removeListener(Object listener) { int key = getListenerKey(listener); if (key == INVALID_KEY) return key; synchronized (mListenerMapLock) { mListenerMap.remove(key); return key; } } private 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_FULLY_CONNECTED: 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 mAsyncChannel = 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)); } } }