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