1 /* 2 * Copyright (C) 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.networkstack.metrics; 18 19 import android.net.wifi.WifiInfo; 20 21 import androidx.annotation.IntRange; 22 import androidx.annotation.NonNull; 23 import androidx.annotation.Nullable; 24 import androidx.annotation.VisibleForTesting; 25 26 import com.android.internal.util.HexDump; 27 import com.android.net.module.util.CollectionUtils; 28 import com.android.server.connectivity.nano.CellularData; 29 import com.android.server.connectivity.nano.DataStallEventProto; 30 import com.android.server.connectivity.nano.DnsEvent; 31 import com.android.server.connectivity.nano.WifiData; 32 33 import com.google.protobuf.nano.MessageNano; 34 35 import java.util.ArrayList; 36 import java.util.Arrays; 37 import java.util.List; 38 import java.util.Objects; 39 40 /** 41 * Class to record the stats of detection level information for data stall. 42 * 43 * @hide 44 */ 45 public final class DataStallDetectionStats { 46 public static final int UNKNOWN_SIGNAL_STRENGTH = -1; 47 // Default value of TCP signals. 48 @VisibleForTesting 49 public static final int UNSPECIFIED_TCP_FAIL_RATE = -1; 50 @VisibleForTesting 51 public static final int UNSPECIFIED_TCP_PACKETS_COUNT = -1; 52 @VisibleForTesting 53 @NonNull 54 public final byte[] mCellularInfo; 55 @VisibleForTesting 56 @NonNull 57 public final byte[] mWifiInfo; 58 @NonNull 59 public final byte[] mDns; 60 @VisibleForTesting 61 public final int mEvaluationType; 62 @VisibleForTesting 63 public final int mNetworkType; 64 // The TCP packets fail rate percentage from the latest tcp polling. -1 means the TCP signal is 65 // not known or not supported on the SDK version of this device. 66 @VisibleForTesting @IntRange(from = -1, to = 100) 67 public final int mTcpFailRate; 68 // Number of packets sent since the last received packet. 69 @VisibleForTesting 70 public final int mTcpSentSinceLastRecv; 71 DataStallDetectionStats(@ullable byte[] cell, @Nullable byte[] wifi, @NonNull int[] returnCode, @NonNull long[] dnsTime, int evalType, int netType, int failRate, int sentSinceLastRecv)72 public DataStallDetectionStats(@Nullable byte[] cell, @Nullable byte[] wifi, 73 @NonNull int[] returnCode, @NonNull long[] dnsTime, int evalType, int netType, 74 int failRate, int sentSinceLastRecv) { 75 mCellularInfo = emptyCellDataIfNull(cell); 76 mWifiInfo = emptyWifiInfoIfNull(wifi); 77 78 DnsEvent dns = new DnsEvent(); 79 dns.dnsReturnCode = returnCode; 80 dns.dnsTime = dnsTime; 81 mDns = MessageNano.toByteArray(dns); 82 mEvaluationType = evalType; 83 mNetworkType = netType; 84 mTcpFailRate = failRate; 85 mTcpSentSinceLastRecv = sentSinceLastRecv; 86 } 87 88 /** 89 * Because metrics data must contain data for each field even if it's not supported or not 90 * available, generate a byte array representing an empty {@link CellularData} if the 91 * {@link CellularData} is unavailable. 92 * 93 * @param cell a byte array representing current {@link CellularData} of {@code this} 94 * @return a byte array of a {@link CellularData}. 95 */ 96 @VisibleForTesting emptyCellDataIfNull(@ullable byte[] cell)97 public static byte[] emptyCellDataIfNull(@Nullable byte[] cell) { 98 if (cell != null) return cell; 99 100 CellularData data = new CellularData(); 101 data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_UNKNOWN; 102 data.networkMccmnc = ""; 103 data.simMccmnc = ""; 104 data.signalStrength = UNKNOWN_SIGNAL_STRENGTH; 105 return MessageNano.toByteArray(data); 106 } 107 108 /** 109 * Because metrics data must contain data for each field even if it's not supported or not 110 * available, generate a byte array representing an empty {@link WifiData} if the 111 * {@link WiFiData} is unavailable. 112 * 113 * @param wifi a byte array representing current {@link WiFiData} of {@code this}. 114 * @return a byte array of a {@link WiFiData}. 115 */ 116 @VisibleForTesting emptyWifiInfoIfNull(@ullable byte[] wifi)117 public static byte[] emptyWifiInfoIfNull(@Nullable byte[] wifi) { 118 if (wifi != null) return wifi; 119 120 WifiData data = new WifiData(); 121 data.wifiBand = DataStallEventProto.AP_BAND_UNKNOWN; 122 data.signalStrength = UNKNOWN_SIGNAL_STRENGTH; 123 return MessageNano.toByteArray(data); 124 } 125 126 @Override toString()127 public String toString() { 128 StringBuilder sb = new StringBuilder(); 129 sb.append("type: ").append(mNetworkType) 130 .append(", evaluation type: ") 131 .append(mEvaluationType) 132 .append(", wifi info: ") 133 .append(HexDump.toHexString(mWifiInfo)) 134 .append(", cell info: ") 135 .append(HexDump.toHexString(mCellularInfo)) 136 .append(", dns: ") 137 .append(HexDump.toHexString(mDns)) 138 .append(", tcp fail rate: ") 139 .append(mTcpFailRate) 140 .append(", tcp received: ") 141 .append(mTcpSentSinceLastRecv); 142 return sb.toString(); 143 } 144 145 @Override equals(@ullable final Object o)146 public boolean equals(@Nullable final Object o) { 147 if (!(o instanceof DataStallDetectionStats)) return false; 148 final DataStallDetectionStats other = (DataStallDetectionStats) o; 149 return (mNetworkType == other.mNetworkType) 150 && (mEvaluationType == other.mEvaluationType) 151 && Arrays.equals(mWifiInfo, other.mWifiInfo) 152 && Arrays.equals(mCellularInfo, other.mCellularInfo) 153 && Arrays.equals(mDns, other.mDns) 154 && (mTcpFailRate == other.mTcpFailRate) 155 && (mTcpSentSinceLastRecv == other.mTcpSentSinceLastRecv); 156 } 157 158 @Override hashCode()159 public int hashCode() { 160 return Objects.hash(mNetworkType, mEvaluationType, mWifiInfo, mCellularInfo, mDns, 161 mTcpFailRate, mTcpSentSinceLastRecv); 162 } 163 164 /** 165 * Utility to create an instance of {@Link DataStallDetectionStats} 166 * 167 * @hide 168 */ 169 public static class Builder { 170 @Nullable 171 private byte[] mCellularInfo; 172 @Nullable 173 private byte[] mWifiInfo; 174 @NonNull 175 private final List<Integer> mDnsReturnCode = new ArrayList<Integer>(); 176 @NonNull 177 private final List<Long> mDnsTimeStamp = new ArrayList<Long>(); 178 private int mEvaluationType; 179 private int mNetworkType; 180 private int mTcpFailRate = UNSPECIFIED_TCP_FAIL_RATE; 181 private int mTcpSentSinceLastRecv = UNSPECIFIED_TCP_PACKETS_COUNT; 182 183 /** 184 * Add a dns event into Builder. 185 * 186 * @param code the return code of the dns event. 187 * @param timeMs the elapsedRealtime in ms that the the dns event was received from netd. 188 * @return {@code this} {@link Builder} instance. 189 */ addDnsEvent(int code, long timeMs)190 public Builder addDnsEvent(int code, long timeMs) { 191 mDnsReturnCode.add(code); 192 mDnsTimeStamp.add(timeMs); 193 return this; 194 } 195 196 /** 197 * Set the data stall evaluation type into Builder. 198 * 199 * @param type the signal type causing a data stall to be suspected. 200 * @return {@code this} {@link Builder} instance. 201 */ setEvaluationType(int type)202 public Builder setEvaluationType(int type) { 203 mEvaluationType = type; 204 return this; 205 } 206 207 /** 208 * Set the network type into Builder. 209 * 210 * @param type the network type of the logged network. 211 * @return {@code this} {@link Builder} instance. 212 */ setNetworkType(int type)213 public Builder setNetworkType(int type) { 214 mNetworkType = type; 215 return this; 216 } 217 218 /** 219 * Set the TCP packet fail rate into Builder. The data is included since android R. 220 * 221 * @param rate the TCP packet fail rate of the logged network. The default value is 222 * {@code UNSPECIFIED_TCP_FAIL_RATE}, which means the TCP signal is not known 223 * or not supported on the SDK version of this device. 224 * @return {@code this} {@link Builder} instance. 225 */ setTcpFailRate(@ntRangefrom = -1, to = 100) int rate)226 public Builder setTcpFailRate(@IntRange(from = -1, to = 100) int rate) { 227 mTcpFailRate = rate; 228 return this; 229 } 230 231 /** 232 * Set the number of TCP packets sent since the last received packet into Builder. The data 233 * starts to be included since android R. 234 * 235 * @param count the number of packets sent since the last received packet of the logged 236 * network. Keep it unset as default value or set to 237 * {@code UNSPECIFIED_TCP_PACKETS_COUNT} if the tcp signal is unsupported with 238 * current device android sdk version or the packets count is unknown. 239 * @return {@code this} {@link Builder} instance. 240 */ setTcpSentSinceLastRecv(int count)241 public Builder setTcpSentSinceLastRecv(int count) { 242 mTcpSentSinceLastRecv = count; 243 return this; 244 } 245 246 /** 247 * Set the wifi data into Builder. 248 * 249 * @param info a {@link WifiInfo} of the connected wifi network. 250 * @return {@code this} {@link Builder} instance. 251 */ setWiFiData(@ullable final WifiInfo info)252 public Builder setWiFiData(@Nullable final WifiInfo info) { 253 WifiData data = new WifiData(); 254 data.wifiBand = getWifiBand(info); 255 data.signalStrength = (info != null) ? info.getRssi() : UNKNOWN_SIGNAL_STRENGTH; 256 mWifiInfo = MessageNano.toByteArray(data); 257 return this; 258 } 259 getWifiBand(@ullable final WifiInfo info)260 private static int getWifiBand(@Nullable final WifiInfo info) { 261 if (info == null) return DataStallEventProto.AP_BAND_UNKNOWN; 262 263 int freq = info.getFrequency(); 264 // Refer to ScanResult.is5GHz(), ScanResult.is24GHz() and ScanResult.is6GHz(). 265 if (freq >= 5160 && freq <= 5865) { 266 return DataStallEventProto.AP_BAND_5GHZ; 267 } else if (freq >= 2412 && freq <= 2484) { 268 return DataStallEventProto.AP_BAND_2GHZ; 269 } else if (freq >= 5945 && freq <= 7105) { 270 return DataStallEventProto.AP_BAND_6GHZ; 271 } else { 272 return DataStallEventProto.AP_BAND_UNKNOWN; 273 } 274 } 275 276 /** 277 * Set the cellular data into Builder. 278 * 279 * @param radioType the radio technology of the logged cellular network. 280 * @param roaming a boolean indicates if logged cellular network is roaming or not. 281 * @param networkMccmnc the mccmnc of the camped network. 282 * @param simMccmnc the mccmnc of the sim. 283 * @return {@code this} {@link Builder} instance. 284 */ setCellData(int radioType, boolean roaming, @NonNull String networkMccmnc, @NonNull String simMccmnc, int ss)285 public Builder setCellData(int radioType, boolean roaming, 286 @NonNull String networkMccmnc, @NonNull String simMccmnc, int ss) { 287 CellularData data = new CellularData(); 288 data.ratType = radioType; 289 data.isRoaming = roaming; 290 data.networkMccmnc = networkMccmnc; 291 data.simMccmnc = simMccmnc; 292 data.signalStrength = ss; 293 mCellularInfo = MessageNano.toByteArray(data); 294 return this; 295 } 296 297 /** 298 * Create a new {@Link DataStallDetectionStats}. 299 */ build()300 public DataStallDetectionStats build() { 301 return new DataStallDetectionStats(mCellularInfo, mWifiInfo, 302 CollectionUtils.toIntArray(mDnsReturnCode), 303 CollectionUtils.toLongArray(mDnsTimeStamp), 304 mEvaluationType, mNetworkType, mTcpFailRate, mTcpSentSinceLastRecv); 305 } 306 } 307 } 308