1 /* 2 * Copyright (C) 2020 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.util.Stopwatch; 20 import android.stats.connectivity.DhcpErrorCode; 21 import android.stats.connectivity.DhcpFeature; 22 import android.stats.connectivity.DisconnectCode; 23 import android.stats.connectivity.HostnameTransResult; 24 25 import com.android.net.module.util.ConnectivityUtils; 26 27 import java.util.HashSet; 28 import java.util.Set; 29 30 /** 31 * Class to record the network IpProvisioning into statsd. 32 * 1. Fill in NetworkIpProvisioningReported proto. 33 * 2. Write the NetworkIpProvisioningReported proto into statsd. 34 * 3. This class is not thread-safe, and should always be accessed from the same thread. 35 * @hide 36 */ 37 38 public class IpProvisioningMetrics { 39 private static final String TAG = IpProvisioningMetrics.class.getSimpleName(); 40 private final NetworkIpProvisioningReported.Builder mStatsBuilder = 41 NetworkIpProvisioningReported.newBuilder(); 42 private final DhcpSession.Builder mDhcpSessionBuilder = DhcpSession.newBuilder(); 43 private final Stopwatch mIpv4Watch = new Stopwatch().start(); 44 private final Stopwatch mIpv6Watch = new Stopwatch().start(); 45 private final Stopwatch mWatch = new Stopwatch().start(); 46 private final Set<DhcpFeature> mDhcpFeatures = new HashSet<DhcpFeature>(); 47 48 // Define a maximum number of the DhcpErrorCode. 49 public static final int MAX_DHCP_ERROR_COUNT = 20; 50 51 /** 52 * reset this all metrics members 53 */ reset()54 public void reset() { 55 mStatsBuilder.clear(); 56 mDhcpSessionBuilder.clear(); 57 mDhcpFeatures.clear(); 58 mIpv4Watch.restart(); 59 mIpv6Watch.restart(); 60 mWatch.restart(); 61 } 62 63 /** 64 * Write the TransportType into mStatsBuilder. 65 * TODO: implement this 66 */ setTransportType()67 public void setTransportType() {} 68 69 /** 70 * Write the IPv4Provisioned latency into mStatsBuilder. 71 */ setIPv4ProvisionedLatencyOnFirstTime(final boolean isIpv4Provisioned)72 public void setIPv4ProvisionedLatencyOnFirstTime(final boolean isIpv4Provisioned) { 73 if (isIpv4Provisioned && !mStatsBuilder.hasIpv4LatencyMicros()) { 74 mStatsBuilder.setIpv4LatencyMicros(ConnectivityUtils.saturatedCast(mIpv4Watch.stop())); 75 } 76 } 77 78 /** 79 * Write the IPv6Provisioned latency into mStatsBuilder. 80 */ setIPv6ProvisionedLatencyOnFirstTime(final boolean isIpv6Provisioned)81 public void setIPv6ProvisionedLatencyOnFirstTime(final boolean isIpv6Provisioned) { 82 if (isIpv6Provisioned && !mStatsBuilder.hasIpv6LatencyMicros()) { 83 mStatsBuilder.setIpv6LatencyMicros(ConnectivityUtils.saturatedCast(mIpv6Watch.stop())); 84 } 85 } 86 87 /** 88 * Write the DhcpFeature proto into mStatsBuilder. 89 */ setDhcpEnabledFeature(final DhcpFeature feature)90 public void setDhcpEnabledFeature(final DhcpFeature feature) { 91 if (feature == DhcpFeature.DF_UNKNOWN) return; 92 mDhcpFeatures.add(feature); 93 } 94 95 /** 96 * Write the DHCPDISCOVER transmission count into DhcpSession. 97 */ incrementCountForDiscover()98 public void incrementCountForDiscover() { 99 mDhcpSessionBuilder.setDiscoverCount(mDhcpSessionBuilder.getDiscoverCount() + 1); 100 } 101 102 /** 103 * Write the DHCPREQUEST transmission count into DhcpSession. 104 */ incrementCountForRequest()105 public void incrementCountForRequest() { 106 mDhcpSessionBuilder.setRequestCount(mDhcpSessionBuilder.getRequestCount() + 1); 107 } 108 109 /** 110 * Write the IPv4 address conflict count into DhcpSession. 111 */ incrementCountForIpConflict()112 public void incrementCountForIpConflict() { 113 mDhcpSessionBuilder.setConflictCount(mDhcpSessionBuilder.getConflictCount() + 1); 114 } 115 116 /** 117 * Write the hostname transliteration result into DhcpSession. 118 */ setHostnameTransinfo(final boolean isOptionEnabled, final boolean transSuccess)119 public void setHostnameTransinfo(final boolean isOptionEnabled, final boolean transSuccess) { 120 mDhcpSessionBuilder.setHtResult(!isOptionEnabled ? HostnameTransResult.HTR_DISABLE : 121 transSuccess ? HostnameTransResult.HTR_SUCCESS : HostnameTransResult.HTR_FAILURE); 122 } 123 dhcpErrorFromNumberSafe(int number)124 private static DhcpErrorCode dhcpErrorFromNumberSafe(int number) { 125 // See DhcpErrorCode.errorCodeWithOption 126 // TODO: add a DhcpErrorCode method to extract the code; 127 // or replace legacy error codes with the new metrics. 128 final DhcpErrorCode error = DhcpErrorCode.forNumber(number & 0xFFFF0000); 129 if (error == null) return DhcpErrorCode.ET_UNKNOWN; 130 return error; 131 } 132 133 /** 134 * write the DHCP error code into DhcpSession. 135 */ addDhcpErrorCode(final int errorCode)136 public void addDhcpErrorCode(final int errorCode) { 137 if (mDhcpSessionBuilder.getErrorCodeCount() >= MAX_DHCP_ERROR_COUNT) return; 138 mDhcpSessionBuilder.addErrorCode(dhcpErrorFromNumberSafe(errorCode)); 139 } 140 141 /** 142 * Write the IP provision disconnect code into DhcpSession. 143 */ setDisconnectCode(final DisconnectCode disconnectCode)144 public void setDisconnectCode(final DisconnectCode disconnectCode) { 145 if (mStatsBuilder.hasDisconnectCode()) return; 146 mStatsBuilder.setDisconnectCode(disconnectCode); 147 } 148 149 /** 150 * Write the NetworkIpProvisioningReported proto into statsd. 151 */ statsWrite()152 public NetworkIpProvisioningReported statsWrite() { 153 if (!mWatch.isStarted()) return null; 154 for (DhcpFeature feature : mDhcpFeatures) { 155 mDhcpSessionBuilder.addUsedFeatures(feature); 156 } 157 mStatsBuilder.setDhcpSession(mDhcpSessionBuilder); 158 mStatsBuilder.setProvisioningDurationMicros(mWatch.stop()); 159 mStatsBuilder.setRandomNumber((int) (Math.random() * 1000)); 160 final NetworkIpProvisioningReported Stats = mStatsBuilder.build(); 161 final byte[] DhcpSession = Stats.getDhcpSession().toByteArray(); 162 NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_IP_PROVISIONING_REPORTED, 163 Stats.getTransportType().getNumber(), 164 Stats.getIpv4LatencyMicros(), 165 Stats.getIpv6LatencyMicros(), 166 Stats.getProvisioningDurationMicros(), 167 Stats.getDisconnectCode().getNumber(), 168 DhcpSession, 169 Stats.getRandomNumber()); 170 mWatch.reset(); 171 return Stats; 172 } 173 } 174