Loading src/android/net/dhcp/DhcpClient.java +30 −1 Original line number Diff line number Diff line Loading @@ -80,6 +80,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.SystemClock; import android.provider.Settings; import android.stats.connectivity.DhcpFeature; import android.system.ErrnoException; import android.system.Os; import android.util.EventLog; Loading @@ -101,6 +102,7 @@ import com.android.networkstack.apishim.CaptivePortalDataShimImpl; import com.android.networkstack.apishim.SocketUtilsShimImpl; import com.android.networkstack.apishim.common.ShimUtils; import com.android.networkstack.arp.ArpPacket; import com.android.networkstack.metrics.IpProvisioningMetrics; import java.io.FileDescriptor; import java.io.IOException; Loading Loading @@ -305,6 +307,8 @@ public class DhcpClient extends StateMachine { private final Context mContext; private final Random mRandom; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); @NonNull private final IpProvisioningMetrics mMetrics; // We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can // be off-link as well as on-link). Loading Loading @@ -378,9 +382,11 @@ public class DhcpClient extends StateMachine { */ public static class Dependencies { private final NetworkStackIpMemoryStore mNetworkStackIpMemoryStore; private final IpProvisioningMetrics mMetrics; public Dependencies(NetworkStackIpMemoryStore store) { public Dependencies(NetworkStackIpMemoryStore store, IpProvisioningMetrics metrics) { mNetworkStackIpMemoryStore = store; mMetrics = metrics; } /** Loading @@ -406,6 +412,13 @@ public class DhcpClient extends StateMachine { return mNetworkStackIpMemoryStore; } /** * Get a IpProvisioningMetrics instance. */ public IpProvisioningMetrics getIpProvisioningMetrics() { return mMetrics; } /** * Return whether a feature guarded by a feature flag is enabled. * @see NetworkStackUtils#isFeatureEnabled(Context, String, String) Loading Loading @@ -444,6 +457,7 @@ public class DhcpClient extends StateMachine { mController = controller; mIfaceName = iface; mIpMemoryStore = deps.getIpMemoryStore(); mMetrics = deps.getIpProvisioningMetrics(); // CHECKSTYLE:OFF IndentationCheck addState(mStoppedState); Loading Loading @@ -484,6 +498,7 @@ public class DhcpClient extends StateMachine { final boolean sendHostname = deps.getSendHostnameOption(context); mHostname = sendHostname ? new HostnameTransliterator().transliterate( deps.getDeviceName(mContext)) : null; mMetrics.setHostnameTransinfo(sendHostname, mHostname != null); } public void registerForPreDhcpNotification() { Loading Loading @@ -529,6 +544,15 @@ public class DhcpClient extends StateMachine { false /* defaultEnabled */); } private void recordMetricEnabledFeatures() { if (isDhcpLeaseCacheEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_INITREBOOT); if (isDhcpRapidCommitEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_RAPIDCOMMIT); if (isDhcpIpConflictDetectEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_DAD); if (mConfiguration.isPreconnectionEnabled) { mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_FILS); } } private void confirmDhcpLease(DhcpPacket packet, DhcpResults results) { setDhcpLeaseExpiry(packet); acceptDhcpResults(results, "Confirmed"); Loading Loading @@ -610,6 +634,7 @@ public class DhcpClient extends StateMachine { EventLog.writeEvent(snetTagId, bugId, uid, data); } mMetricsLog.log(mIfaceName, new DhcpErrorEvent(e.errorCode)); mMetrics.addDhcpErrorCode(e.errorCode); } } Loading Loading @@ -687,6 +712,7 @@ public class DhcpClient extends StateMachine { final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, DO_UNICAST, getRequestedParams(), isDhcpRapidCommitEnabled(), mHostname); mMetrics.incrementCountForDiscover(); return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); } Loading @@ -705,6 +731,7 @@ public class DhcpClient extends StateMachine { String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + " request=" + requestedAddress.getHostAddress() + " serverid=" + serverStr; mMetrics.incrementCountForRequest(); return transmitPacket(packet, description, encap, to); } Loading Loading @@ -937,6 +964,7 @@ public class DhcpClient extends StateMachine { } else { startInitRebootOrInit(); } recordMetricEnabledFeatures(); return HANDLED; default: return NOT_HANDLED; Loading Loading @@ -1422,6 +1450,7 @@ public class DhcpClient extends StateMachine { try { final ArpPacket packet = ArpPacket.parseArpPacket(recvbuf, length); if (hasIpAddressConflict(packet, mTargetIp)) { mMetrics.incrementCountForIpConflict(); sendMessage(EVENT_IP_CONFLICT); } } catch (ArpPacket.ParseException e) { Loading src/android/net/ip/IpClient.java +51 −19 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; import android.stats.connectivity.DisconnectCode; import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; Loading @@ -79,6 +80,7 @@ import com.android.internal.util.WakeupMessage; import com.android.networkstack.apishim.NetworkInformationShimImpl; import com.android.networkstack.apishim.common.NetworkInformationShim; import com.android.networkstack.apishim.common.ShimUtils; import com.android.networkstack.metrics.IpProvisioningMetrics; import com.android.server.NetworkObserverRegistry; import com.android.server.NetworkStackService.NetworkStackServiceManager; Loading Loading @@ -129,6 +131,7 @@ public class IpClient extends StateMachine { private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>(); private final NetworkStackIpMemoryStore mIpMemoryStore; private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance(); private final IpProvisioningMetrics mIpProvisioningMetrics = new IpProvisioningMetrics(); /** * Dump all state machine and connectivity packet logs to the specified writer. Loading Loading @@ -527,8 +530,8 @@ public class IpClient extends StateMachine { * Get a DhcpClient Dependencies instance. */ public DhcpClient.Dependencies getDhcpClientDependencies( NetworkStackIpMemoryStore ipMemoryStore) { return new DhcpClient.Dependencies(ipMemoryStore); NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics) { return new DhcpClient.Dependencies(ipMemoryStore, metrics); } /** Loading Loading @@ -818,7 +821,10 @@ public class IpClient extends StateMachine { * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}. */ public void stop() { sendMessage(CMD_STOP); // The message "arg1" parameter is used to record the disconnect code metrics. // Usually this method is called by the peer (e.g. wifi) intentionally to stop IpClient, // consider that's the normal user termination. sendMessage(CMD_STOP, DisconnectCode.DC_NORMAL_TERMINATION.getNumber()); } /** Loading Loading @@ -1072,6 +1078,14 @@ public class IpClient extends StateMachine { mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration)); } // Record the DisconnectCode and transition to StoppingState. // When jumping to mStoppingState This function will ensure // that you will not forget to fill in DisconnectCode. private void transitionToStoppingState(final DisconnectCode code) { mIpProvisioningMetrics.setDisconnectCode(code); transitionTo(mStoppingState); } // For now: use WifiStateMachine's historical notion of provisioned. @VisibleForTesting static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) { Loading Loading @@ -1352,6 +1366,12 @@ public class IpClient extends StateMachine { if (Objects.equals(newLp, mLinkProperties)) { return true; } // Either success IPv4 or IPv6 provisioning triggers new LinkProperties update, // wait for the provisioning completion and record the latency. mIpProvisioningMetrics.setIPv4ProvisionedLatencyOnFirstTime(newLp.isIpv4Provisioned()); mIpProvisioningMetrics.setIPv6ProvisionedLatencyOnFirstTime(newLp.isIpv6Provisioned()); final int delta = setLinkProperties(newLp); // Most of the attributes stored in the memory store are deduced from // the link properties, therefore when the properties update the memory Loading Loading @@ -1447,10 +1467,10 @@ public class IpClient extends StateMachine { } mCallback.onNewDhcpResults(null); handleProvisioningFailure(); handleProvisioningFailure(DisconnectCode.DC_PROVISIONING_FAIL); } private void handleProvisioningFailure() { private void handleProvisioningFailure(final DisconnectCode code) { final LinkProperties newLp = assembleLinkProperties(); int delta = setLinkProperties(newLp); // If we've gotten here and we're still not provisioned treat that as Loading @@ -1467,7 +1487,7 @@ public class IpClient extends StateMachine { dispatchCallback(delta, newLp); if (delta == PROV_CHANGE_LOST_PROVISIONING) { transitionTo(mStoppingState); transitionToStoppingState(code); } } Loading Loading @@ -1723,7 +1743,7 @@ public class IpClient extends StateMachine { private void startDhcpClient() { // Start DHCPv4. mDhcpClient = mDependencies.makeDhcpClient(mContext, IpClient.this, mInterfaceParams, mDependencies.getDhcpClientDependencies(mIpMemoryStore)); mDependencies.getDhcpClientDependencies(mIpMemoryStore, mIpProvisioningMetrics)); // If preconnection is enabled, there is no need to ask Wi-Fi to disable powersaving // during DHCP, because the DHCP handshake will happen during association. In order to Loading @@ -1744,7 +1764,8 @@ public class IpClient extends StateMachine { if (mInterfaceParams == null) { logError("Failed to find InterfaceParams for " + mInterfaceName); doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND); deferMessage(obtainMessage(CMD_STOP)); deferMessage(obtainMessage(CMD_STOP, DisconnectCode.DC_INTERFACE_NOT_FOUND.getNumber())); return; } Loading Loading @@ -1836,6 +1857,7 @@ public class IpClient extends StateMachine { class StartedState extends State { @Override public void enter() { mIpProvisioningMetrics.reset(); mStartTimeMillis = SystemClock.elapsedRealtime(); if (mConfiguration.mProvisioningTimeoutMs > 0) { final long alarmTime = SystemClock.elapsedRealtime() Loading @@ -1847,13 +1869,17 @@ public class IpClient extends StateMachine { @Override public void exit() { mProvisioningTimeoutAlarm.cancel(); // Record metrics information once this provisioning has completed due to certain // reason (normal termination, provisioning timeout, lost provisioning and etc). mIpProvisioningMetrics.statsWrite(); } @Override public boolean processMessage(Message msg) { switch (msg.what) { case CMD_STOP: transitionTo(mStoppingState); transitionToStoppingState(DisconnectCode.forNumber(msg.arg1)); break; case CMD_UPDATE_L2KEY_CLUSTER: { Loading @@ -1875,7 +1901,7 @@ public class IpClient extends StateMachine { break; case EVENT_PROVISIONING_TIMEOUT: handleProvisioningFailure(); handleProvisioningFailure(DisconnectCode.DC_PROVISIONING_TIMEOUT); break; default: Loading Loading @@ -1912,13 +1938,13 @@ public class IpClient extends StateMachine { if (mConfiguration.mEnableIPv6 && !startIPv6()) { doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6); enqueueJumpToStoppingState(); enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV6); return; } if (mConfiguration.mEnableIPv4 && !isUsingPreconnection() && !startIPv4()) { doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4); enqueueJumpToStoppingState(); enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV4); return; } Loading @@ -1926,14 +1952,14 @@ public class IpClient extends StateMachine { if ((config != null) && !applyInitialConfig(config)) { // TODO introduce a new IpManagerEvent constant to distinguish this error case. doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING); enqueueJumpToStoppingState(); enqueueJumpToStoppingState(DisconnectCode.DC_INVALID_PROVISIONING); return; } if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) { doImmediateProvisioningFailure( IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR); enqueueJumpToStoppingState(); enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPREACHABILITYMONITOR); return; } } Loading Loading @@ -1965,8 +1991,8 @@ public class IpClient extends StateMachine { resetLinkProperties(); } private void enqueueJumpToStoppingState() { deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING)); private void enqueueJumpToStoppingState(final DisconnectCode code) { deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING, code.getNumber())); } private ConnectivityPacketTracker createPacketTracker() { Loading Loading @@ -2001,7 +2027,7 @@ public class IpClient extends StateMachine { switch (msg.what) { case CMD_JUMP_RUNNING_TO_STOPPING: case CMD_STOP: transitionTo(mStoppingState); transitionToStoppingState(DisconnectCode.forNumber(msg.arg1)); break; case CMD_START: Loading @@ -2028,8 +2054,14 @@ public class IpClient extends StateMachine { break; case EVENT_NETLINK_LINKPROPERTIES_CHANGED: // EVENT_NETLINK_LINKPROPERTIES_CHANGED message will be received in both of // provisioning loss and normal user termination case (e.g. turn off wifi or // switch to another wifi ssid), hence, checking current interface change // status (down or up) would help distinguish. final boolean ifUp = (msg.arg1 != 0); if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) { transitionTo(mStoppingState); transitionToStoppingState(ifUp ? DisconnectCode.DC_PROVISIONING_FAIL : DisconnectCode.DC_NORMAL_TERMINATION); } break; Loading Loading @@ -2109,7 +2141,7 @@ public class IpClient extends StateMachine { } else { logError("Failed to set IPv4 address."); dispatchCallback(PROV_CHANGE_LOST_PROVISIONING, mLinkProperties); transitionTo(mStoppingState); transitionToStoppingState(DisconnectCode.DC_PROVISIONING_FAIL); } break; } Loading src/android/net/util/NetworkStackUtils.java +18 −0 Original line number Diff line number Diff line Loading @@ -436,4 +436,22 @@ public class NetworkStackUtils { return addr instanceof Inet6Address && ((addr.getAddress()[0] & 0xfe) == 0xfc); } /** * Returns the {@code int} nearest in value to {@code value}. * * @param value any {@code long} value * @return the same value cast to {@code int} if it is in the range of the {@code int} * type, {@link Integer#MAX_VALUE} if it is too large, or {@link Integer#MIN_VALUE} if * it is too small */ public static int saturatedCast(long value) { if (value > Integer.MAX_VALUE) { return Integer.MAX_VALUE; } if (value < Integer.MIN_VALUE) { return Integer.MIN_VALUE; } return (int) value; } } src/android/net/util/Stopwatch.java +8 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,14 @@ public class Stopwatch { return this; } /** * Retart the Stopwatch. */ public Stopwatch restart() { mStartTimeNs = SystemClock.elapsedRealtimeNanos(); return this; } /** * Stop the Stopwatch. * @return the total time recorded, in microseconds, or 0 if not started. Loading src/com/android/networkstack/metrics/IpProvisioningMetrics.java 0 → 100644 +163 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.util.NetworkStackUtils; import android.net.util.Stopwatch; import android.stats.connectivity.DhcpErrorCode; import android.stats.connectivity.DhcpFeature; import android.stats.connectivity.DisconnectCode; import android.stats.connectivity.HostnameTransResult; import java.util.HashSet; import java.util.Set; /** * Class to record the network IpProvisioning into statsd. * 1. Fill in NetworkIpProvisioningReported proto. * 2. Write the NetworkIpProvisioningReported proto into statsd. * 3. This class is not thread-safe, and should always be accessed from the same thread. * @hide */ public class IpProvisioningMetrics { private static final String TAG = IpProvisioningMetrics.class.getSimpleName(); private final NetworkIpProvisioningReported.Builder mStatsBuilder = NetworkIpProvisioningReported.newBuilder(); private final DhcpSession.Builder mDhcpSessionBuilder = DhcpSession.newBuilder(); private final Stopwatch mIpv4Watch = new Stopwatch().start(); private final Stopwatch mIpv6Watch = new Stopwatch().start(); private final Stopwatch mWatch = new Stopwatch().start(); private final Set<DhcpFeature> mDhcpFeatures = new HashSet<DhcpFeature>(); // Define a maximum number of the DhcpErrorCode. public static final int MAX_DHCP_ERROR_COUNT = 20; /** * reset this all metrics members */ public void reset() { mStatsBuilder.clear(); mDhcpSessionBuilder.clear(); mDhcpFeatures.clear(); mIpv4Watch.restart(); mIpv6Watch.restart(); mWatch.restart(); } /** * Write the TransportType into mStatsBuilder. * TODO: implement this */ public void setTransportType() {} /** * Write the IPv4Provisioned latency into mStatsBuilder. */ public void setIPv4ProvisionedLatencyOnFirstTime(final boolean isIpv4Provisioned) { if (isIpv4Provisioned && !mStatsBuilder.hasIpv4LatencyMicros()) { mStatsBuilder.setIpv4LatencyMicros(NetworkStackUtils.saturatedCast(mIpv4Watch.stop())); } } /** * Write the IPv6Provisioned latency into mStatsBuilder. */ public void setIPv6ProvisionedLatencyOnFirstTime(final boolean isIpv6Provisioned) { if (isIpv6Provisioned && !mStatsBuilder.hasIpv6LatencyMicros()) { mStatsBuilder.setIpv6LatencyMicros(NetworkStackUtils.saturatedCast(mIpv6Watch.stop())); } } /** * Write the DhcpFeature proto into mStatsBuilder. */ public void setDhcpEnabledFeature(final DhcpFeature feature) { if (feature == DhcpFeature.DF_UNKNOWN) return; mDhcpFeatures.add(feature); } /** * Write the DHCPDISCOVER transmission count into DhcpSession. */ public void incrementCountForDiscover() { mDhcpSessionBuilder.setDiscoverCount(mDhcpSessionBuilder.getDiscoverCount() + 1); } /** * Write the DHCPREQUEST transmission count into DhcpSession. */ public void incrementCountForRequest() { mDhcpSessionBuilder.setRequestCount(mDhcpSessionBuilder.getRequestCount() + 1); } /** * Write the IPv4 address conflict count into DhcpSession. */ public void incrementCountForIpConflict() { mDhcpSessionBuilder.setConflictCount(mDhcpSessionBuilder.getConflictCount() + 1); } /** * Write the hostname transliteration result into DhcpSession. */ public void setHostnameTransinfo(final boolean isOptionEnabled, final boolean transSuccess) { mDhcpSessionBuilder.setHtResult(!isOptionEnabled ? HostnameTransResult.HTR_DISABLE : transSuccess ? HostnameTransResult.HTR_SUCCESS : HostnameTransResult.HTR_FAILURE); } /** * write the DHCP error code into DhcpSession. */ public void addDhcpErrorCode(final int errorCode) { if (mDhcpSessionBuilder.getErrorCodeCount() >= MAX_DHCP_ERROR_COUNT) return; mDhcpSessionBuilder.addErrorCode(DhcpErrorCode.forNumber(errorCode)); } /** * Write the IP provision disconnect code into DhcpSession. */ public void setDisconnectCode(final DisconnectCode disconnectCode) { if (mStatsBuilder.hasDisconnectCode()) return; mStatsBuilder.setDisconnectCode(disconnectCode); } /** * Write the NetworkIpProvisioningReported proto into statsd. */ public NetworkIpProvisioningReported statsWrite() { if (!mWatch.isStarted()) return null; for (DhcpFeature feature : mDhcpFeatures) { mDhcpSessionBuilder.addUsedFeatures(feature); } mStatsBuilder.setDhcpSession(mDhcpSessionBuilder); mStatsBuilder.setProvisioningDurationMicros(mWatch.stop()); mStatsBuilder.setRandomNumber((int) (Math.random() * 1000)); final NetworkIpProvisioningReported Stats = mStatsBuilder.build(); final byte[] DhcpSession = Stats.getDhcpSession().toByteArray(); NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_IP_PROVISIONING_REPORTED, Stats.getTransportType().getNumber(), Stats.getIpv4LatencyMicros(), Stats.getIpv6LatencyMicros(), Stats.getProvisioningDurationMicros(), Stats.getDisconnectCode().getNumber(), DhcpSession, Stats.getRandomNumber()); mWatch.reset(); return Stats; } } Loading
src/android/net/dhcp/DhcpClient.java +30 −1 Original line number Diff line number Diff line Loading @@ -80,6 +80,7 @@ import android.os.Message; import android.os.PowerManager; import android.os.SystemClock; import android.provider.Settings; import android.stats.connectivity.DhcpFeature; import android.system.ErrnoException; import android.system.Os; import android.util.EventLog; Loading @@ -101,6 +102,7 @@ import com.android.networkstack.apishim.CaptivePortalDataShimImpl; import com.android.networkstack.apishim.SocketUtilsShimImpl; import com.android.networkstack.apishim.common.ShimUtils; import com.android.networkstack.arp.ArpPacket; import com.android.networkstack.metrics.IpProvisioningMetrics; import java.io.FileDescriptor; import java.io.IOException; Loading Loading @@ -305,6 +307,8 @@ public class DhcpClient extends StateMachine { private final Context mContext; private final Random mRandom; private final IpConnectivityLog mMetricsLog = new IpConnectivityLog(); @NonNull private final IpProvisioningMetrics mMetrics; // We use a UDP socket to send, so the kernel handles ARP and routing for us (DHCP servers can // be off-link as well as on-link). Loading Loading @@ -378,9 +382,11 @@ public class DhcpClient extends StateMachine { */ public static class Dependencies { private final NetworkStackIpMemoryStore mNetworkStackIpMemoryStore; private final IpProvisioningMetrics mMetrics; public Dependencies(NetworkStackIpMemoryStore store) { public Dependencies(NetworkStackIpMemoryStore store, IpProvisioningMetrics metrics) { mNetworkStackIpMemoryStore = store; mMetrics = metrics; } /** Loading @@ -406,6 +412,13 @@ public class DhcpClient extends StateMachine { return mNetworkStackIpMemoryStore; } /** * Get a IpProvisioningMetrics instance. */ public IpProvisioningMetrics getIpProvisioningMetrics() { return mMetrics; } /** * Return whether a feature guarded by a feature flag is enabled. * @see NetworkStackUtils#isFeatureEnabled(Context, String, String) Loading Loading @@ -444,6 +457,7 @@ public class DhcpClient extends StateMachine { mController = controller; mIfaceName = iface; mIpMemoryStore = deps.getIpMemoryStore(); mMetrics = deps.getIpProvisioningMetrics(); // CHECKSTYLE:OFF IndentationCheck addState(mStoppedState); Loading Loading @@ -484,6 +498,7 @@ public class DhcpClient extends StateMachine { final boolean sendHostname = deps.getSendHostnameOption(context); mHostname = sendHostname ? new HostnameTransliterator().transliterate( deps.getDeviceName(mContext)) : null; mMetrics.setHostnameTransinfo(sendHostname, mHostname != null); } public void registerForPreDhcpNotification() { Loading Loading @@ -529,6 +544,15 @@ public class DhcpClient extends StateMachine { false /* defaultEnabled */); } private void recordMetricEnabledFeatures() { if (isDhcpLeaseCacheEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_INITREBOOT); if (isDhcpRapidCommitEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_RAPIDCOMMIT); if (isDhcpIpConflictDetectEnabled()) mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_DAD); if (mConfiguration.isPreconnectionEnabled) { mMetrics.setDhcpEnabledFeature(DhcpFeature.DF_FILS); } } private void confirmDhcpLease(DhcpPacket packet, DhcpResults results) { setDhcpLeaseExpiry(packet); acceptDhcpResults(results, "Confirmed"); Loading Loading @@ -610,6 +634,7 @@ public class DhcpClient extends StateMachine { EventLog.writeEvent(snetTagId, bugId, uid, data); } mMetricsLog.log(mIfaceName, new DhcpErrorEvent(e.errorCode)); mMetrics.addDhcpErrorCode(e.errorCode); } } Loading Loading @@ -687,6 +712,7 @@ public class DhcpClient extends StateMachine { final ByteBuffer packet = DhcpPacket.buildDiscoverPacket( DhcpPacket.ENCAP_L2, mTransactionId, getSecs(), mHwAddr, DO_UNICAST, getRequestedParams(), isDhcpRapidCommitEnabled(), mHostname); mMetrics.incrementCountForDiscover(); return transmitPacket(packet, "DHCPDISCOVER", DhcpPacket.ENCAP_L2, INADDR_BROADCAST); } Loading @@ -705,6 +731,7 @@ public class DhcpClient extends StateMachine { String description = "DHCPREQUEST ciaddr=" + clientAddress.getHostAddress() + " request=" + requestedAddress.getHostAddress() + " serverid=" + serverStr; mMetrics.incrementCountForRequest(); return transmitPacket(packet, description, encap, to); } Loading Loading @@ -937,6 +964,7 @@ public class DhcpClient extends StateMachine { } else { startInitRebootOrInit(); } recordMetricEnabledFeatures(); return HANDLED; default: return NOT_HANDLED; Loading Loading @@ -1422,6 +1450,7 @@ public class DhcpClient extends StateMachine { try { final ArpPacket packet = ArpPacket.parseArpPacket(recvbuf, length); if (hasIpAddressConflict(packet, mTargetIp)) { mMetrics.incrementCountForIpConflict(); sendMessage(EVENT_IP_CONFLICT); } } catch (ArpPacket.ParseException e) { Loading
src/android/net/ip/IpClient.java +51 −19 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ import android.os.Message; import android.os.RemoteException; import android.os.ServiceSpecificException; import android.os.SystemClock; import android.stats.connectivity.DisconnectCode; import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; Loading @@ -79,6 +80,7 @@ import com.android.internal.util.WakeupMessage; import com.android.networkstack.apishim.NetworkInformationShimImpl; import com.android.networkstack.apishim.common.NetworkInformationShim; import com.android.networkstack.apishim.common.ShimUtils; import com.android.networkstack.metrics.IpProvisioningMetrics; import com.android.server.NetworkObserverRegistry; import com.android.server.NetworkStackService.NetworkStackServiceManager; Loading Loading @@ -129,6 +131,7 @@ public class IpClient extends StateMachine { private static final ConcurrentHashMap<String, LocalLog> sPktLogs = new ConcurrentHashMap<>(); private final NetworkStackIpMemoryStore mIpMemoryStore; private final NetworkInformationShim mShim = NetworkInformationShimImpl.newInstance(); private final IpProvisioningMetrics mIpProvisioningMetrics = new IpProvisioningMetrics(); /** * Dump all state machine and connectivity packet logs to the specified writer. Loading Loading @@ -527,8 +530,8 @@ public class IpClient extends StateMachine { * Get a DhcpClient Dependencies instance. */ public DhcpClient.Dependencies getDhcpClientDependencies( NetworkStackIpMemoryStore ipMemoryStore) { return new DhcpClient.Dependencies(ipMemoryStore); NetworkStackIpMemoryStore ipMemoryStore, IpProvisioningMetrics metrics) { return new DhcpClient.Dependencies(ipMemoryStore, metrics); } /** Loading Loading @@ -818,7 +821,10 @@ public class IpClient extends StateMachine { * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}. */ public void stop() { sendMessage(CMD_STOP); // The message "arg1" parameter is used to record the disconnect code metrics. // Usually this method is called by the peer (e.g. wifi) intentionally to stop IpClient, // consider that's the normal user termination. sendMessage(CMD_STOP, DisconnectCode.DC_NORMAL_TERMINATION.getNumber()); } /** Loading Loading @@ -1072,6 +1078,14 @@ public class IpClient extends StateMachine { mMetricsLog.log(mInterfaceName, new IpManagerEvent(type, duration)); } // Record the DisconnectCode and transition to StoppingState. // When jumping to mStoppingState This function will ensure // that you will not forget to fill in DisconnectCode. private void transitionToStoppingState(final DisconnectCode code) { mIpProvisioningMetrics.setDisconnectCode(code); transitionTo(mStoppingState); } // For now: use WifiStateMachine's historical notion of provisioned. @VisibleForTesting static boolean isProvisioned(LinkProperties lp, InitialConfiguration config) { Loading Loading @@ -1352,6 +1366,12 @@ public class IpClient extends StateMachine { if (Objects.equals(newLp, mLinkProperties)) { return true; } // Either success IPv4 or IPv6 provisioning triggers new LinkProperties update, // wait for the provisioning completion and record the latency. mIpProvisioningMetrics.setIPv4ProvisionedLatencyOnFirstTime(newLp.isIpv4Provisioned()); mIpProvisioningMetrics.setIPv6ProvisionedLatencyOnFirstTime(newLp.isIpv6Provisioned()); final int delta = setLinkProperties(newLp); // Most of the attributes stored in the memory store are deduced from // the link properties, therefore when the properties update the memory Loading Loading @@ -1447,10 +1467,10 @@ public class IpClient extends StateMachine { } mCallback.onNewDhcpResults(null); handleProvisioningFailure(); handleProvisioningFailure(DisconnectCode.DC_PROVISIONING_FAIL); } private void handleProvisioningFailure() { private void handleProvisioningFailure(final DisconnectCode code) { final LinkProperties newLp = assembleLinkProperties(); int delta = setLinkProperties(newLp); // If we've gotten here and we're still not provisioned treat that as Loading @@ -1467,7 +1487,7 @@ public class IpClient extends StateMachine { dispatchCallback(delta, newLp); if (delta == PROV_CHANGE_LOST_PROVISIONING) { transitionTo(mStoppingState); transitionToStoppingState(code); } } Loading Loading @@ -1723,7 +1743,7 @@ public class IpClient extends StateMachine { private void startDhcpClient() { // Start DHCPv4. mDhcpClient = mDependencies.makeDhcpClient(mContext, IpClient.this, mInterfaceParams, mDependencies.getDhcpClientDependencies(mIpMemoryStore)); mDependencies.getDhcpClientDependencies(mIpMemoryStore, mIpProvisioningMetrics)); // If preconnection is enabled, there is no need to ask Wi-Fi to disable powersaving // during DHCP, because the DHCP handshake will happen during association. In order to Loading @@ -1744,7 +1764,8 @@ public class IpClient extends StateMachine { if (mInterfaceParams == null) { logError("Failed to find InterfaceParams for " + mInterfaceName); doImmediateProvisioningFailure(IpManagerEvent.ERROR_INTERFACE_NOT_FOUND); deferMessage(obtainMessage(CMD_STOP)); deferMessage(obtainMessage(CMD_STOP, DisconnectCode.DC_INTERFACE_NOT_FOUND.getNumber())); return; } Loading Loading @@ -1836,6 +1857,7 @@ public class IpClient extends StateMachine { class StartedState extends State { @Override public void enter() { mIpProvisioningMetrics.reset(); mStartTimeMillis = SystemClock.elapsedRealtime(); if (mConfiguration.mProvisioningTimeoutMs > 0) { final long alarmTime = SystemClock.elapsedRealtime() Loading @@ -1847,13 +1869,17 @@ public class IpClient extends StateMachine { @Override public void exit() { mProvisioningTimeoutAlarm.cancel(); // Record metrics information once this provisioning has completed due to certain // reason (normal termination, provisioning timeout, lost provisioning and etc). mIpProvisioningMetrics.statsWrite(); } @Override public boolean processMessage(Message msg) { switch (msg.what) { case CMD_STOP: transitionTo(mStoppingState); transitionToStoppingState(DisconnectCode.forNumber(msg.arg1)); break; case CMD_UPDATE_L2KEY_CLUSTER: { Loading @@ -1875,7 +1901,7 @@ public class IpClient extends StateMachine { break; case EVENT_PROVISIONING_TIMEOUT: handleProvisioningFailure(); handleProvisioningFailure(DisconnectCode.DC_PROVISIONING_TIMEOUT); break; default: Loading Loading @@ -1912,13 +1938,13 @@ public class IpClient extends StateMachine { if (mConfiguration.mEnableIPv6 && !startIPv6()) { doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV6); enqueueJumpToStoppingState(); enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV6); return; } if (mConfiguration.mEnableIPv4 && !isUsingPreconnection() && !startIPv4()) { doImmediateProvisioningFailure(IpManagerEvent.ERROR_STARTING_IPV4); enqueueJumpToStoppingState(); enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPV4); return; } Loading @@ -1926,14 +1952,14 @@ public class IpClient extends StateMachine { if ((config != null) && !applyInitialConfig(config)) { // TODO introduce a new IpManagerEvent constant to distinguish this error case. doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING); enqueueJumpToStoppingState(); enqueueJumpToStoppingState(DisconnectCode.DC_INVALID_PROVISIONING); return; } if (mConfiguration.mUsingIpReachabilityMonitor && !startIpReachabilityMonitor()) { doImmediateProvisioningFailure( IpManagerEvent.ERROR_STARTING_IPREACHABILITYMONITOR); enqueueJumpToStoppingState(); enqueueJumpToStoppingState(DisconnectCode.DC_ERROR_STARTING_IPREACHABILITYMONITOR); return; } } Loading Loading @@ -1965,8 +1991,8 @@ public class IpClient extends StateMachine { resetLinkProperties(); } private void enqueueJumpToStoppingState() { deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING)); private void enqueueJumpToStoppingState(final DisconnectCode code) { deferMessage(obtainMessage(CMD_JUMP_RUNNING_TO_STOPPING, code.getNumber())); } private ConnectivityPacketTracker createPacketTracker() { Loading Loading @@ -2001,7 +2027,7 @@ public class IpClient extends StateMachine { switch (msg.what) { case CMD_JUMP_RUNNING_TO_STOPPING: case CMD_STOP: transitionTo(mStoppingState); transitionToStoppingState(DisconnectCode.forNumber(msg.arg1)); break; case CMD_START: Loading @@ -2028,8 +2054,14 @@ public class IpClient extends StateMachine { break; case EVENT_NETLINK_LINKPROPERTIES_CHANGED: // EVENT_NETLINK_LINKPROPERTIES_CHANGED message will be received in both of // provisioning loss and normal user termination case (e.g. turn off wifi or // switch to another wifi ssid), hence, checking current interface change // status (down or up) would help distinguish. final boolean ifUp = (msg.arg1 != 0); if (!handleLinkPropertiesUpdate(SEND_CALLBACKS)) { transitionTo(mStoppingState); transitionToStoppingState(ifUp ? DisconnectCode.DC_PROVISIONING_FAIL : DisconnectCode.DC_NORMAL_TERMINATION); } break; Loading Loading @@ -2109,7 +2141,7 @@ public class IpClient extends StateMachine { } else { logError("Failed to set IPv4 address."); dispatchCallback(PROV_CHANGE_LOST_PROVISIONING, mLinkProperties); transitionTo(mStoppingState); transitionToStoppingState(DisconnectCode.DC_PROVISIONING_FAIL); } break; } Loading
src/android/net/util/NetworkStackUtils.java +18 −0 Original line number Diff line number Diff line Loading @@ -436,4 +436,22 @@ public class NetworkStackUtils { return addr instanceof Inet6Address && ((addr.getAddress()[0] & 0xfe) == 0xfc); } /** * Returns the {@code int} nearest in value to {@code value}. * * @param value any {@code long} value * @return the same value cast to {@code int} if it is in the range of the {@code int} * type, {@link Integer#MAX_VALUE} if it is too large, or {@link Integer#MIN_VALUE} if * it is too small */ public static int saturatedCast(long value) { if (value > Integer.MAX_VALUE) { return Integer.MAX_VALUE; } if (value < Integer.MIN_VALUE) { return Integer.MIN_VALUE; } return (int) value; } }
src/android/net/util/Stopwatch.java +8 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,14 @@ public class Stopwatch { return this; } /** * Retart the Stopwatch. */ public Stopwatch restart() { mStartTimeNs = SystemClock.elapsedRealtimeNanos(); return this; } /** * Stop the Stopwatch. * @return the total time recorded, in microseconds, or 0 if not started. Loading
src/com/android/networkstack/metrics/IpProvisioningMetrics.java 0 → 100644 +163 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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.util.NetworkStackUtils; import android.net.util.Stopwatch; import android.stats.connectivity.DhcpErrorCode; import android.stats.connectivity.DhcpFeature; import android.stats.connectivity.DisconnectCode; import android.stats.connectivity.HostnameTransResult; import java.util.HashSet; import java.util.Set; /** * Class to record the network IpProvisioning into statsd. * 1. Fill in NetworkIpProvisioningReported proto. * 2. Write the NetworkIpProvisioningReported proto into statsd. * 3. This class is not thread-safe, and should always be accessed from the same thread. * @hide */ public class IpProvisioningMetrics { private static final String TAG = IpProvisioningMetrics.class.getSimpleName(); private final NetworkIpProvisioningReported.Builder mStatsBuilder = NetworkIpProvisioningReported.newBuilder(); private final DhcpSession.Builder mDhcpSessionBuilder = DhcpSession.newBuilder(); private final Stopwatch mIpv4Watch = new Stopwatch().start(); private final Stopwatch mIpv6Watch = new Stopwatch().start(); private final Stopwatch mWatch = new Stopwatch().start(); private final Set<DhcpFeature> mDhcpFeatures = new HashSet<DhcpFeature>(); // Define a maximum number of the DhcpErrorCode. public static final int MAX_DHCP_ERROR_COUNT = 20; /** * reset this all metrics members */ public void reset() { mStatsBuilder.clear(); mDhcpSessionBuilder.clear(); mDhcpFeatures.clear(); mIpv4Watch.restart(); mIpv6Watch.restart(); mWatch.restart(); } /** * Write the TransportType into mStatsBuilder. * TODO: implement this */ public void setTransportType() {} /** * Write the IPv4Provisioned latency into mStatsBuilder. */ public void setIPv4ProvisionedLatencyOnFirstTime(final boolean isIpv4Provisioned) { if (isIpv4Provisioned && !mStatsBuilder.hasIpv4LatencyMicros()) { mStatsBuilder.setIpv4LatencyMicros(NetworkStackUtils.saturatedCast(mIpv4Watch.stop())); } } /** * Write the IPv6Provisioned latency into mStatsBuilder. */ public void setIPv6ProvisionedLatencyOnFirstTime(final boolean isIpv6Provisioned) { if (isIpv6Provisioned && !mStatsBuilder.hasIpv6LatencyMicros()) { mStatsBuilder.setIpv6LatencyMicros(NetworkStackUtils.saturatedCast(mIpv6Watch.stop())); } } /** * Write the DhcpFeature proto into mStatsBuilder. */ public void setDhcpEnabledFeature(final DhcpFeature feature) { if (feature == DhcpFeature.DF_UNKNOWN) return; mDhcpFeatures.add(feature); } /** * Write the DHCPDISCOVER transmission count into DhcpSession. */ public void incrementCountForDiscover() { mDhcpSessionBuilder.setDiscoverCount(mDhcpSessionBuilder.getDiscoverCount() + 1); } /** * Write the DHCPREQUEST transmission count into DhcpSession. */ public void incrementCountForRequest() { mDhcpSessionBuilder.setRequestCount(mDhcpSessionBuilder.getRequestCount() + 1); } /** * Write the IPv4 address conflict count into DhcpSession. */ public void incrementCountForIpConflict() { mDhcpSessionBuilder.setConflictCount(mDhcpSessionBuilder.getConflictCount() + 1); } /** * Write the hostname transliteration result into DhcpSession. */ public void setHostnameTransinfo(final boolean isOptionEnabled, final boolean transSuccess) { mDhcpSessionBuilder.setHtResult(!isOptionEnabled ? HostnameTransResult.HTR_DISABLE : transSuccess ? HostnameTransResult.HTR_SUCCESS : HostnameTransResult.HTR_FAILURE); } /** * write the DHCP error code into DhcpSession. */ public void addDhcpErrorCode(final int errorCode) { if (mDhcpSessionBuilder.getErrorCodeCount() >= MAX_DHCP_ERROR_COUNT) return; mDhcpSessionBuilder.addErrorCode(DhcpErrorCode.forNumber(errorCode)); } /** * Write the IP provision disconnect code into DhcpSession. */ public void setDisconnectCode(final DisconnectCode disconnectCode) { if (mStatsBuilder.hasDisconnectCode()) return; mStatsBuilder.setDisconnectCode(disconnectCode); } /** * Write the NetworkIpProvisioningReported proto into statsd. */ public NetworkIpProvisioningReported statsWrite() { if (!mWatch.isStarted()) return null; for (DhcpFeature feature : mDhcpFeatures) { mDhcpSessionBuilder.addUsedFeatures(feature); } mStatsBuilder.setDhcpSession(mDhcpSessionBuilder); mStatsBuilder.setProvisioningDurationMicros(mWatch.stop()); mStatsBuilder.setRandomNumber((int) (Math.random() * 1000)); final NetworkIpProvisioningReported Stats = mStatsBuilder.build(); final byte[] DhcpSession = Stats.getDhcpSession().toByteArray(); NetworkStackStatsLog.write(NetworkStackStatsLog.NETWORK_IP_PROVISIONING_REPORTED, Stats.getTransportType().getNumber(), Stats.getIpv4LatencyMicros(), Stats.getIpv6LatencyMicros(), Stats.getProvisioningDurationMicros(), Stats.getDisconnectCode().getNumber(), DhcpSession, Stats.getRandomNumber()); mWatch.reset(); return Stats; } }