| /* |
| * Copyright (C) 2019 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package com.android.networkstack.metrics; |
| |
| import android.net.wifi.WifiInfo; |
| |
| import androidx.annotation.IntRange; |
| import androidx.annotation.NonNull; |
| import androidx.annotation.Nullable; |
| import androidx.annotation.VisibleForTesting; |
| |
| import com.android.internal.util.HexDump; |
| import com.android.net.module.util.CollectionUtils; |
| import com.android.server.connectivity.nano.CellularData; |
| import com.android.server.connectivity.nano.DataStallEventProto; |
| import com.android.server.connectivity.nano.DnsEvent; |
| import com.android.server.connectivity.nano.WifiData; |
| |
| import com.google.protobuf.nano.MessageNano; |
| |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.Objects; |
| |
| /** |
| * Class to record the stats of detection level information for data stall. |
| * |
| * @hide |
| */ |
| public final class DataStallDetectionStats { |
| public static final int UNKNOWN_SIGNAL_STRENGTH = -1; |
| // Default value of TCP signals. |
| @VisibleForTesting |
| public static final int UNSPECIFIED_TCP_FAIL_RATE = -1; |
| @VisibleForTesting |
| public static final int UNSPECIFIED_TCP_PACKETS_COUNT = -1; |
| @VisibleForTesting |
| @NonNull |
| public final byte[] mCellularInfo; |
| @VisibleForTesting |
| @NonNull |
| public final byte[] mWifiInfo; |
| @NonNull |
| public final byte[] mDns; |
| @VisibleForTesting |
| public final int mEvaluationType; |
| @VisibleForTesting |
| public final int mNetworkType; |
| // The TCP packets fail rate percentage from the latest tcp polling. -1 means the TCP signal is |
| // not known or not supported on the SDK version of this device. |
| @VisibleForTesting @IntRange(from = -1, to = 100) |
| public final int mTcpFailRate; |
| // Number of packets sent since the last received packet. |
| @VisibleForTesting |
| public final int mTcpSentSinceLastRecv; |
| |
| public DataStallDetectionStats(@Nullable byte[] cell, @Nullable byte[] wifi, |
| @NonNull int[] returnCode, @NonNull long[] dnsTime, int evalType, int netType, |
| int failRate, int sentSinceLastRecv) { |
| mCellularInfo = emptyCellDataIfNull(cell); |
| mWifiInfo = emptyWifiInfoIfNull(wifi); |
| |
| DnsEvent dns = new DnsEvent(); |
| dns.dnsReturnCode = returnCode; |
| dns.dnsTime = dnsTime; |
| mDns = MessageNano.toByteArray(dns); |
| mEvaluationType = evalType; |
| mNetworkType = netType; |
| mTcpFailRate = failRate; |
| mTcpSentSinceLastRecv = sentSinceLastRecv; |
| } |
| |
| /** |
| * Because metrics data must contain data for each field even if it's not supported or not |
| * available, generate a byte array representing an empty {@link CellularData} if the |
| * {@link CellularData} is unavailable. |
| * |
| * @param cell a byte array representing current {@link CellularData} of {@code this} |
| * @return a byte array of a {@link CellularData}. |
| */ |
| @VisibleForTesting |
| public static byte[] emptyCellDataIfNull(@Nullable byte[] cell) { |
| if (cell != null) return cell; |
| |
| CellularData data = new CellularData(); |
| data.ratType = DataStallEventProto.RADIO_TECHNOLOGY_UNKNOWN; |
| data.networkMccmnc = ""; |
| data.simMccmnc = ""; |
| data.signalStrength = UNKNOWN_SIGNAL_STRENGTH; |
| return MessageNano.toByteArray(data); |
| } |
| |
| /** |
| * Because metrics data must contain data for each field even if it's not supported or not |
| * available, generate a byte array representing an empty {@link WifiData} if the |
| * {@link WiFiData} is unavailable. |
| * |
| * @param wifi a byte array representing current {@link WiFiData} of {@code this}. |
| * @return a byte array of a {@link WiFiData}. |
| */ |
| @VisibleForTesting |
| public static byte[] emptyWifiInfoIfNull(@Nullable byte[] wifi) { |
| if (wifi != null) return wifi; |
| |
| WifiData data = new WifiData(); |
| data.wifiBand = DataStallEventProto.AP_BAND_UNKNOWN; |
| data.signalStrength = UNKNOWN_SIGNAL_STRENGTH; |
| return MessageNano.toByteArray(data); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append("type: ").append(mNetworkType) |
| .append(", evaluation type: ") |
| .append(mEvaluationType) |
| .append(", wifi info: ") |
| .append(HexDump.toHexString(mWifiInfo)) |
| .append(", cell info: ") |
| .append(HexDump.toHexString(mCellularInfo)) |
| .append(", dns: ") |
| .append(HexDump.toHexString(mDns)) |
| .append(", tcp fail rate: ") |
| .append(mTcpFailRate) |
| .append(", tcp received: ") |
| .append(mTcpSentSinceLastRecv); |
| return sb.toString(); |
| } |
| |
| @Override |
| public boolean equals(@Nullable final Object o) { |
| if (!(o instanceof DataStallDetectionStats)) return false; |
| final DataStallDetectionStats other = (DataStallDetectionStats) o; |
| return (mNetworkType == other.mNetworkType) |
| && (mEvaluationType == other.mEvaluationType) |
| && Arrays.equals(mWifiInfo, other.mWifiInfo) |
| && Arrays.equals(mCellularInfo, other.mCellularInfo) |
| && Arrays.equals(mDns, other.mDns) |
| && (mTcpFailRate == other.mTcpFailRate) |
| && (mTcpSentSinceLastRecv == other.mTcpSentSinceLastRecv); |
| } |
| |
| @Override |
| public int hashCode() { |
| return Objects.hash(mNetworkType, mEvaluationType, mWifiInfo, mCellularInfo, mDns, |
| mTcpFailRate, mTcpSentSinceLastRecv); |
| } |
| |
| /** |
| * Utility to create an instance of {@Link DataStallDetectionStats} |
| * |
| * @hide |
| */ |
| public static class Builder { |
| @Nullable |
| private byte[] mCellularInfo; |
| @Nullable |
| private byte[] mWifiInfo; |
| @NonNull |
| private final List<Integer> mDnsReturnCode = new ArrayList<Integer>(); |
| @NonNull |
| private final List<Long> mDnsTimeStamp = new ArrayList<Long>(); |
| private int mEvaluationType; |
| private int mNetworkType; |
| private int mTcpFailRate = UNSPECIFIED_TCP_FAIL_RATE; |
| private int mTcpSentSinceLastRecv = UNSPECIFIED_TCP_PACKETS_COUNT; |
| |
| /** |
| * Add a dns event into Builder. |
| * |
| * @param code the return code of the dns event. |
| * @param timeMs the elapsedRealtime in ms that the the dns event was received from netd. |
| * @return {@code this} {@link Builder} instance. |
| */ |
| public Builder addDnsEvent(int code, long timeMs) { |
| mDnsReturnCode.add(code); |
| mDnsTimeStamp.add(timeMs); |
| return this; |
| } |
| |
| /** |
| * Set the data stall evaluation type into Builder. |
| * |
| * @param type the signal type causing a data stall to be suspected. |
| * @return {@code this} {@link Builder} instance. |
| */ |
| public Builder setEvaluationType(int type) { |
| mEvaluationType = type; |
| return this; |
| } |
| |
| /** |
| * Set the network type into Builder. |
| * |
| * @param type the network type of the logged network. |
| * @return {@code this} {@link Builder} instance. |
| */ |
| public Builder setNetworkType(int type) { |
| mNetworkType = type; |
| return this; |
| } |
| |
| /** |
| * Set the TCP packet fail rate into Builder. The data is included since android R. |
| * |
| * @param rate the TCP packet fail rate of the logged network. The default value is |
| * {@code UNSPECIFIED_TCP_FAIL_RATE}, which means the TCP signal is not known |
| * or not supported on the SDK version of this device. |
| * @return {@code this} {@link Builder} instance. |
| */ |
| public Builder setTcpFailRate(@IntRange(from = -1, to = 100) int rate) { |
| mTcpFailRate = rate; |
| return this; |
| } |
| |
| /** |
| * Set the number of TCP packets sent since the last received packet into Builder. The data |
| * starts to be included since android R. |
| * |
| * @param count the number of packets sent since the last received packet of the logged |
| * network. Keep it unset as default value or set to |
| * {@code UNSPECIFIED_TCP_PACKETS_COUNT} if the tcp signal is unsupported with |
| * current device android sdk version or the packets count is unknown. |
| * @return {@code this} {@link Builder} instance. |
| */ |
| public Builder setTcpSentSinceLastRecv(int count) { |
| mTcpSentSinceLastRecv = count; |
| return this; |
| } |
| |
| /** |
| * Set the wifi data into Builder. |
| * |
| * @param info a {@link WifiInfo} of the connected wifi network. |
| * @return {@code this} {@link Builder} instance. |
| */ |
| public Builder setWiFiData(@Nullable final WifiInfo info) { |
| WifiData data = new WifiData(); |
| data.wifiBand = getWifiBand(info); |
| data.signalStrength = (info != null) ? info.getRssi() : UNKNOWN_SIGNAL_STRENGTH; |
| mWifiInfo = MessageNano.toByteArray(data); |
| return this; |
| } |
| |
| private static int getWifiBand(@Nullable final WifiInfo info) { |
| if (info == null) return DataStallEventProto.AP_BAND_UNKNOWN; |
| |
| int freq = info.getFrequency(); |
| // Refer to ScanResult.is5GHz(), ScanResult.is24GHz() and ScanResult.is6GHz(). |
| if (freq >= 5160 && freq <= 5865) { |
| return DataStallEventProto.AP_BAND_5GHZ; |
| } else if (freq >= 2412 && freq <= 2484) { |
| return DataStallEventProto.AP_BAND_2GHZ; |
| } else if (freq >= 5945 && freq <= 7105) { |
| return DataStallEventProto.AP_BAND_6GHZ; |
| } else { |
| return DataStallEventProto.AP_BAND_UNKNOWN; |
| } |
| } |
| |
| /** |
| * Set the cellular data into Builder. |
| * |
| * @param radioType the radio technology of the logged cellular network. |
| * @param roaming a boolean indicates if logged cellular network is roaming or not. |
| * @param networkMccmnc the mccmnc of the camped network. |
| * @param simMccmnc the mccmnc of the sim. |
| * @return {@code this} {@link Builder} instance. |
| */ |
| public Builder setCellData(int radioType, boolean roaming, |
| @NonNull String networkMccmnc, @NonNull String simMccmnc, int ss) { |
| CellularData data = new CellularData(); |
| data.ratType = radioType; |
| data.isRoaming = roaming; |
| data.networkMccmnc = networkMccmnc; |
| data.simMccmnc = simMccmnc; |
| data.signalStrength = ss; |
| mCellularInfo = MessageNano.toByteArray(data); |
| return this; |
| } |
| |
| /** |
| * Create a new {@Link DataStallDetectionStats}. |
| */ |
| public DataStallDetectionStats build() { |
| return new DataStallDetectionStats(mCellularInfo, mWifiInfo, |
| CollectionUtils.toIntArray(mDnsReturnCode), |
| CollectionUtils.toLongArray(mDnsTimeStamp), |
| mEvaluationType, mNetworkType, mTcpFailRate, mTcpSentSinceLastRecv); |
| } |
| } |
| } |