Merge "Delay start of bootanimation until after the Runtime APEX is mounted."
diff --git a/Android.bp b/Android.bp
index 81a9850..a21cd28 100644
--- a/Android.bp
+++ b/Android.bp
@@ -844,6 +844,8 @@
         "core/java/android/net/dhcp/DhcpServingParamsParcel.aidl",
         "core/java/android/net/dhcp/IDhcpServer.aidl",
         "core/java/android/net/dhcp/IDhcpServerCallbacks.aidl",
+        "core/java/android/net/ip/IIpClient.aidl",
+        "core/java/android/net/ip/IIpClientCallbacks.aidl",
         "core/java/android/net/ipmemorystore/**/*.aidl",
     ],
     api_dir: "aidl/networkstack",
diff --git a/Android.mk b/Android.mk
index 9f7bf99..e405345 100644
--- a/Android.mk
+++ b/Android.mk
@@ -87,14 +87,11 @@
     frameworks/base/config/hiddenapi-greylist-max-p.txt \
     frameworks/base/config/hiddenapi-greylist-max-o.txt \
     frameworks/base/config/hiddenapi-force-blacklist.txt \
-    $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
-    $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
+    $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) \
     $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
     $(SOONG_HIDDENAPI_FLAGS)
 	frameworks/base/tools/hiddenapi/generate_hiddenapi_lists.py \
-	    --public $(INTERNAL_PLATFORM_HIDDENAPI_PUBLIC_LIST) \
-	    --private $(INTERNAL_PLATFORM_HIDDENAPI_PRIVATE_LIST) \
-	    --csv $(PRIVATE_FLAGS_INPUTS) \
+	    --csv $(INTERNAL_PLATFORM_HIDDENAPI_STUB_FLAGS) $(PRIVATE_FLAGS_INPUTS) \
 	    --greylist frameworks/base/config/hiddenapi-greylist.txt \
 	    --greylist-ignore-conflicts $(INTERNAL_PLATFORM_REMOVED_DEX_API_FILE) \
 	    --greylist-max-p frameworks/base/config/hiddenapi-greylist-max-p.txt \
diff --git a/api/current.txt b/api/current.txt
index a7cc380..bd769e0 100755
--- a/api/current.txt
+++ b/api/current.txt
@@ -27553,6 +27553,7 @@
     method public android.net.IpPrefix getDestination();
     method public java.net.InetAddress getGateway();
     method public String getInterface();
+    method public boolean hasGateway();
     method public boolean isDefaultRoute();
     method public boolean matches(java.net.InetAddress);
     method public void writeToParcel(android.os.Parcel, int);
@@ -40455,6 +40456,8 @@
     method public static String gaiName(int);
     field public static final int AF_INET;
     field public static final int AF_INET6;
+    field public static final int AF_NETLINK;
+    field public static final int AF_PACKET;
     field public static final int AF_UNIX;
     field public static final int AF_UNSPEC;
     field public static final int AI_ADDRCONFIG;
@@ -40464,6 +40467,7 @@
     field public static final int AI_NUMERICSERV;
     field public static final int AI_PASSIVE;
     field public static final int AI_V4MAPPED;
+    field public static final int ARPHRD_ETHER;
     field public static final int CAP_AUDIT_CONTROL;
     field public static final int CAP_AUDIT_WRITE;
     field public static final int CAP_BLOCK_SUSPEND;
@@ -40587,6 +40591,10 @@
     field public static final int ESPIPE;
     field public static final int ESRCH;
     field public static final int ESTALE;
+    field public static final int ETH_P_ALL;
+    field public static final int ETH_P_ARP;
+    field public static final int ETH_P_IP;
+    field public static final int ETH_P_IPV6;
     field public static final int ETIME;
     field public static final int ETIMEDOUT;
     field public static final int ETXTBSY;
@@ -40684,6 +40692,8 @@
     field public static final int MS_ASYNC;
     field public static final int MS_INVALIDATE;
     field public static final int MS_SYNC;
+    field public static final int NETLINK_INET_DIAG;
+    field public static final int NETLINK_ROUTE;
     field public static final int NI_DGRAM;
     field public static final int NI_NAMEREQD;
     field public static final int NI_NOFQDN;
@@ -40720,6 +40730,7 @@
     field public static final int PR_GET_DUMPABLE;
     field public static final int PR_SET_DUMPABLE;
     field public static final int PR_SET_NO_NEW_PRIVS;
+    field public static final int RTMGRP_NEIGH;
     field public static final int RT_SCOPE_HOST;
     field public static final int RT_SCOPE_LINK;
     field public static final int RT_SCOPE_NOWHERE;
diff --git a/api/system-current.txt b/api/system-current.txt
index ab45a22..1320d14 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -803,6 +803,7 @@
     method public abstract void sendBroadcast(android.content.Intent, @Nullable String, @Nullable android.os.Bundle);
     method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS) public abstract void sendBroadcastAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle, @Nullable String, @Nullable android.os.Bundle);
     method public abstract void sendOrderedBroadcast(@NonNull android.content.Intent, @Nullable String, @Nullable android.os.Bundle, @Nullable android.content.BroadcastReceiver, @Nullable android.os.Handler, int, @Nullable String, @Nullable android.os.Bundle);
+    method @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) public void startActivityAsUser(@RequiresPermission android.content.Intent, android.os.UserHandle);
     field public static final String BACKUP_SERVICE = "backup";
     field public static final String CONTEXTHUB_SERVICE = "contexthub";
     field public static final String EUICC_CARD_SERVICE = "euicc_card";
@@ -3023,6 +3024,12 @@
 
 package android.net {
 
+  public class CaptivePortal implements android.os.Parcelable {
+    field public static final int APP_RETURN_DISMISSED = 0; // 0x0
+    field public static final int APP_RETURN_UNWANTED = 1; // 0x1
+    field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
+  }
+
   public class ConnectivityManager {
     method @RequiresPermission(android.Manifest.permission.LOCAL_MAC_ADDRESS) public String getCaptivePortalServerUrl();
     method @RequiresPermission(anyOf={android.Manifest.permission.TETHER_PRIVILEGED, android.Manifest.permission.WRITE_SETTINGS}) public boolean isTetheringSupported();
@@ -3030,6 +3037,8 @@
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void startTethering(int, boolean, android.net.ConnectivityManager.OnStartTetheringCallback, android.os.Handler);
     method @RequiresPermission(android.Manifest.permission.TETHER_PRIVILEGED) public void stopTethering(int);
+    field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
+    field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
     field public static final int TETHERING_BLUETOOTH = 2; // 0x2
     field public static final int TETHERING_USB = 1; // 0x1
     field public static final int TETHERING_WIFI = 0; // 0x0
@@ -3044,6 +3053,10 @@
   public class LinkAddress implements android.os.Parcelable {
     ctor public LinkAddress(java.net.InetAddress, int);
     ctor public LinkAddress(String);
+    method public boolean isGlobalPreferred();
+    method public boolean isIPv4();
+    method public boolean isIPv6();
+    method public boolean isSameAddressAs(android.net.LinkAddress);
   }
 
   public final class LinkProperties implements android.os.Parcelable {
@@ -3058,6 +3071,10 @@
     method public void setMtu(int);
   }
 
+  public class Network implements android.os.Parcelable {
+    method public android.net.Network getPrivateDnsBypassingCopy();
+  }
+
   public final class NetworkCapabilities implements android.os.Parcelable {
     method public int getSignalStrength();
     field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16
@@ -3099,6 +3116,13 @@
     field public static final String EXTRA_PACKAGE_NAME = "packageName";
   }
 
+  public final class RouteInfo implements android.os.Parcelable {
+    method public int getType();
+    field public static final int RTN_THROW = 9; // 0x9
+    field public static final int RTN_UNICAST = 1; // 0x1
+    field public static final int RTN_UNREACHABLE = 7; // 0x7
+  }
+
   public class RssiCurve implements android.os.Parcelable {
     ctor public RssiCurve(int, int, byte[]);
     ctor public RssiCurve(int, int, byte[], int);
@@ -3155,6 +3179,146 @@
 
 }
 
+package android.net.metrics {
+
+  public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
+  }
+
+  public static class ApfProgramEvent.Builder {
+    ctor public ApfProgramEvent.Builder();
+    method public android.net.metrics.ApfProgramEvent build();
+    method public android.net.metrics.ApfProgramEvent.Builder setActualLifetime(long);
+    method public android.net.metrics.ApfProgramEvent.Builder setCurrentRas(int);
+    method public android.net.metrics.ApfProgramEvent.Builder setFilteredRas(int);
+    method public android.net.metrics.ApfProgramEvent.Builder setFlags(boolean, boolean);
+    method public android.net.metrics.ApfProgramEvent.Builder setLifetime(long);
+    method public android.net.metrics.ApfProgramEvent.Builder setProgramLength(int);
+  }
+
+  public final class ApfStats implements android.net.metrics.IpConnectivityLog.Event {
+  }
+
+  public static class ApfStats.Builder {
+    ctor public ApfStats.Builder();
+    method public android.net.metrics.ApfStats build();
+    method public android.net.metrics.ApfStats.Builder setDroppedRas(int);
+    method public android.net.metrics.ApfStats.Builder setDurationMs(long);
+    method public android.net.metrics.ApfStats.Builder setMatchingRas(int);
+    method public android.net.metrics.ApfStats.Builder setMaxProgramSize(int);
+    method public android.net.metrics.ApfStats.Builder setParseErrors(int);
+    method public android.net.metrics.ApfStats.Builder setProgramUpdates(int);
+    method public android.net.metrics.ApfStats.Builder setProgramUpdatesAll(int);
+    method public android.net.metrics.ApfStats.Builder setProgramUpdatesAllowingMulticast(int);
+    method public android.net.metrics.ApfStats.Builder setReceivedRas(int);
+    method public android.net.metrics.ApfStats.Builder setZeroLifetimeRas(int);
+  }
+
+  public final class DhcpClientEvent implements android.net.metrics.IpConnectivityLog.Event {
+  }
+
+  public static class DhcpClientEvent.Builder {
+    ctor public DhcpClientEvent.Builder();
+    method public android.net.metrics.DhcpClientEvent build();
+    method public android.net.metrics.DhcpClientEvent.Builder setDurationMs(int);
+    method public android.net.metrics.DhcpClientEvent.Builder setMsg(String);
+  }
+
+  public final class DhcpErrorEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor public DhcpErrorEvent(int);
+    method public static int errorCodeWithOption(int, int);
+    field public static final int BOOTP_TOO_SHORT;
+    field public static final int BUFFER_UNDERFLOW;
+    field public static final int DHCP_BAD_MAGIC_COOKIE;
+    field public static final int DHCP_ERROR = 4; // 0x4
+    field public static final int DHCP_INVALID_OPTION_LENGTH;
+    field public static final int DHCP_NO_COOKIE;
+    field public static final int DHCP_NO_MSG_TYPE;
+    field public static final int DHCP_UNKNOWN_MSG_TYPE;
+    field public static final int L2_ERROR = 1; // 0x1
+    field public static final int L2_TOO_SHORT;
+    field public static final int L2_WRONG_ETH_TYPE;
+    field public static final int L3_ERROR = 2; // 0x2
+    field public static final int L3_INVALID_IP;
+    field public static final int L3_NOT_IPV4;
+    field public static final int L3_TOO_SHORT;
+    field public static final int L4_ERROR = 3; // 0x3
+    field public static final int L4_NOT_UDP;
+    field public static final int L4_WRONG_PORT;
+    field public static final int MISC_ERROR = 5; // 0x5
+    field public static final int PARSING_ERROR;
+    field public static final int RECEIVE_ERROR;
+  }
+
+  public class IpConnectivityLog {
+    method public boolean log(long, android.net.metrics.IpConnectivityLog.Event);
+    method public boolean log(String, android.net.metrics.IpConnectivityLog.Event);
+    method public boolean log(int, int[], android.net.metrics.IpConnectivityLog.Event);
+    method public boolean log(android.net.metrics.IpConnectivityLog.Event);
+  }
+
+  public static interface IpConnectivityLog.Event extends android.os.Parcelable {
+  }
+
+  public final class IpManagerEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor public IpManagerEvent(int, long);
+    field public static final int COMPLETE_LIFECYCLE = 3; // 0x3
+    field public static final int ERROR_INTERFACE_NOT_FOUND = 8; // 0x8
+    field public static final int ERROR_INVALID_PROVISIONING = 7; // 0x7
+    field public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6; // 0x6
+    field public static final int ERROR_STARTING_IPV4 = 4; // 0x4
+    field public static final int ERROR_STARTING_IPV6 = 5; // 0x5
+    field public static final int PROVISIONING_FAIL = 2; // 0x2
+    field public static final int PROVISIONING_OK = 1; // 0x1
+  }
+
+  public final class IpReachabilityEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor public IpReachabilityEvent(int);
+    field public static final int NUD_FAILED = 512; // 0x200
+    field public static final int NUD_FAILED_ORGANIC = 1024; // 0x400
+    field public static final int PROBE = 256; // 0x100
+    field public static final int PROVISIONING_LOST = 768; // 0x300
+    field public static final int PROVISIONING_LOST_ORGANIC = 1280; // 0x500
+  }
+
+  public final class NetworkEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor public NetworkEvent(int, long);
+    ctor public NetworkEvent(int);
+    field public static final int NETWORK_CAPTIVE_PORTAL_FOUND = 4; // 0x4
+    field public static final int NETWORK_CONNECTED = 1; // 0x1
+    field public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12; // 0xc
+    field public static final int NETWORK_DISCONNECTED = 7; // 0x7
+    field public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10; // 0xa
+    field public static final int NETWORK_FIRST_VALIDATION_SUCCESS = 8; // 0x8
+    field public static final int NETWORK_LINGER = 5; // 0x5
+    field public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11; // 0xb
+    field public static final int NETWORK_REVALIDATION_SUCCESS = 9; // 0x9
+    field public static final int NETWORK_UNLINGER = 6; // 0x6
+    field public static final int NETWORK_VALIDATED = 2; // 0x2
+    field public static final int NETWORK_VALIDATION_FAILED = 3; // 0x3
+  }
+
+  public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event {
+    method public static String getProbeName(int);
+    field public static final int DNS_FAILURE = 0; // 0x0
+    field public static final int DNS_SUCCESS = 1; // 0x1
+    field public static final int PROBE_DNS = 0; // 0x0
+    field public static final int PROBE_FALLBACK = 4; // 0x4
+    field public static final int PROBE_HTTP = 1; // 0x1
+    field public static final int PROBE_HTTPS = 2; // 0x2
+    field public static final int PROBE_PAC = 3; // 0x3
+    field public static final int PROBE_PRIVDNS = 5; // 0x5
+  }
+
+  public static class ValidationProbeEvent.Builder {
+    ctor public ValidationProbeEvent.Builder();
+    method public android.net.metrics.ValidationProbeEvent build();
+    method public android.net.metrics.ValidationProbeEvent.Builder setDurationMs(long);
+    method public android.net.metrics.ValidationProbeEvent.Builder setProbeType(int, boolean);
+    method public android.net.metrics.ValidationProbeEvent.Builder setReturnCode(int);
+  }
+
+}
+
 package android.net.wifi {
 
   @Deprecated public class RttManager {
@@ -4220,8 +4384,23 @@
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static boolean putString(@NonNull android.content.ContentResolver, @NonNull String, @Nullable String, @Nullable String, boolean);
     method @RequiresPermission(android.Manifest.permission.WRITE_SECURE_SETTINGS) public static void resetToDefaults(@NonNull android.content.ContentResolver, @Nullable String);
     field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
+    field public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = "captive_portal_fallback_probe_specs";
+    field public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url";
+    field public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url";
+    field public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
+    field public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
+    field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2
+    field public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; // 0x0
+    field public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; // 0x1
+    field public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS = "captive_portal_other_fallback_urls";
+    field public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
+    field public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https";
     field public static final String CARRIER_APP_NAMES = "carrier_app_names";
     field public static final String CARRIER_APP_WHITELIST = "carrier_app_whitelist";
+    field public static final String DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = "data_stall_consecutive_dns_timeout_threshold";
+    field public static final String DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type";
+    field public static final String DATA_STALL_MIN_EVALUATE_INTERVAL = "data_stall_min_evaluate_interval";
+    field public static final String DATA_STALL_VALID_DNS_TIME_THRESHOLD = "data_stall_valid_dns_time_threshold";
     field public static final String DEFAULT_SM_DP_PLUS = "default_sm_dp_plus";
     field public static final String EUICC_PROVISIONED = "euicc_provisioned";
     field public static final String INSTALL_CARRIER_APP_NOTIFICATION_PERSISTENT = "install_carrier_app_notification_persistent";
diff --git a/api/test-current.txt b/api/test-current.txt
index 5e7ce65..46f98e5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -598,15 +598,44 @@
 
 package android.net {
 
+  public class CaptivePortal implements android.os.Parcelable {
+    field public static final int APP_RETURN_DISMISSED = 0; // 0x0
+    field public static final int APP_RETURN_UNWANTED = 1; // 0x1
+    field public static final int APP_RETURN_WANTED_AS_IS = 2; // 0x2
+  }
+
+  public class ConnectivityManager {
+    field public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC = "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
+    field public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT = "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
+  }
+
   public final class IpSecManager {
     field public static final int INVALID_SECURITY_PARAMETER_INDEX = 0; // 0x0
   }
 
+  public class LinkAddress implements android.os.Parcelable {
+    method public boolean isGlobalPreferred();
+    method public boolean isIPv4();
+    method public boolean isIPv6();
+    method public boolean isSameAddressAs(android.net.LinkAddress);
+  }
+
+  public class Network implements android.os.Parcelable {
+    method public android.net.Network getPrivateDnsBypassingCopy();
+  }
+
   public final class NetworkCapabilities implements android.os.Parcelable {
     method public int[] getCapabilities();
     method public int[] getTransportTypes();
   }
 
+  public final class RouteInfo implements android.os.Parcelable {
+    method public int getType();
+    field public static final int RTN_THROW = 9; // 0x9
+    field public static final int RTN_UNICAST = 1; // 0x1
+    field public static final int RTN_UNREACHABLE = 7; // 0x7
+  }
+
   public class TrafficStats {
     method public static long getLoopbackRxBytes();
     method public static long getLoopbackRxPackets();
@@ -616,6 +645,146 @@
 
 }
 
+package android.net.metrics {
+
+  public final class ApfProgramEvent implements android.net.metrics.IpConnectivityLog.Event {
+  }
+
+  public static class ApfProgramEvent.Builder {
+    ctor public ApfProgramEvent.Builder();
+    method public android.net.metrics.ApfProgramEvent build();
+    method public android.net.metrics.ApfProgramEvent.Builder setActualLifetime(long);
+    method public android.net.metrics.ApfProgramEvent.Builder setCurrentRas(int);
+    method public android.net.metrics.ApfProgramEvent.Builder setFilteredRas(int);
+    method public android.net.metrics.ApfProgramEvent.Builder setFlags(boolean, boolean);
+    method public android.net.metrics.ApfProgramEvent.Builder setLifetime(long);
+    method public android.net.metrics.ApfProgramEvent.Builder setProgramLength(int);
+  }
+
+  public final class ApfStats implements android.net.metrics.IpConnectivityLog.Event {
+  }
+
+  public static class ApfStats.Builder {
+    ctor public ApfStats.Builder();
+    method public android.net.metrics.ApfStats build();
+    method public android.net.metrics.ApfStats.Builder setDroppedRas(int);
+    method public android.net.metrics.ApfStats.Builder setDurationMs(long);
+    method public android.net.metrics.ApfStats.Builder setMatchingRas(int);
+    method public android.net.metrics.ApfStats.Builder setMaxProgramSize(int);
+    method public android.net.metrics.ApfStats.Builder setParseErrors(int);
+    method public android.net.metrics.ApfStats.Builder setProgramUpdates(int);
+    method public android.net.metrics.ApfStats.Builder setProgramUpdatesAll(int);
+    method public android.net.metrics.ApfStats.Builder setProgramUpdatesAllowingMulticast(int);
+    method public android.net.metrics.ApfStats.Builder setReceivedRas(int);
+    method public android.net.metrics.ApfStats.Builder setZeroLifetimeRas(int);
+  }
+
+  public final class DhcpClientEvent implements android.net.metrics.IpConnectivityLog.Event {
+  }
+
+  public static class DhcpClientEvent.Builder {
+    ctor public DhcpClientEvent.Builder();
+    method public android.net.metrics.DhcpClientEvent build();
+    method public android.net.metrics.DhcpClientEvent.Builder setDurationMs(int);
+    method public android.net.metrics.DhcpClientEvent.Builder setMsg(String);
+  }
+
+  public final class DhcpErrorEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor public DhcpErrorEvent(int);
+    method public static int errorCodeWithOption(int, int);
+    field public static final int BOOTP_TOO_SHORT;
+    field public static final int BUFFER_UNDERFLOW;
+    field public static final int DHCP_BAD_MAGIC_COOKIE;
+    field public static final int DHCP_ERROR = 4; // 0x4
+    field public static final int DHCP_INVALID_OPTION_LENGTH;
+    field public static final int DHCP_NO_COOKIE;
+    field public static final int DHCP_NO_MSG_TYPE;
+    field public static final int DHCP_UNKNOWN_MSG_TYPE;
+    field public static final int L2_ERROR = 1; // 0x1
+    field public static final int L2_TOO_SHORT;
+    field public static final int L2_WRONG_ETH_TYPE;
+    field public static final int L3_ERROR = 2; // 0x2
+    field public static final int L3_INVALID_IP;
+    field public static final int L3_NOT_IPV4;
+    field public static final int L3_TOO_SHORT;
+    field public static final int L4_ERROR = 3; // 0x3
+    field public static final int L4_NOT_UDP;
+    field public static final int L4_WRONG_PORT;
+    field public static final int MISC_ERROR = 5; // 0x5
+    field public static final int PARSING_ERROR;
+    field public static final int RECEIVE_ERROR;
+  }
+
+  public class IpConnectivityLog {
+    method public boolean log(long, android.net.metrics.IpConnectivityLog.Event);
+    method public boolean log(String, android.net.metrics.IpConnectivityLog.Event);
+    method public boolean log(int, int[], android.net.metrics.IpConnectivityLog.Event);
+    method public boolean log(android.net.metrics.IpConnectivityLog.Event);
+  }
+
+  public static interface IpConnectivityLog.Event extends android.os.Parcelable {
+  }
+
+  public final class IpManagerEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor public IpManagerEvent(int, long);
+    field public static final int COMPLETE_LIFECYCLE = 3; // 0x3
+    field public static final int ERROR_INTERFACE_NOT_FOUND = 8; // 0x8
+    field public static final int ERROR_INVALID_PROVISIONING = 7; // 0x7
+    field public static final int ERROR_STARTING_IPREACHABILITYMONITOR = 6; // 0x6
+    field public static final int ERROR_STARTING_IPV4 = 4; // 0x4
+    field public static final int ERROR_STARTING_IPV6 = 5; // 0x5
+    field public static final int PROVISIONING_FAIL = 2; // 0x2
+    field public static final int PROVISIONING_OK = 1; // 0x1
+  }
+
+  public final class IpReachabilityEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor public IpReachabilityEvent(int);
+    field public static final int NUD_FAILED = 512; // 0x200
+    field public static final int NUD_FAILED_ORGANIC = 1024; // 0x400
+    field public static final int PROBE = 256; // 0x100
+    field public static final int PROVISIONING_LOST = 768; // 0x300
+    field public static final int PROVISIONING_LOST_ORGANIC = 1280; // 0x500
+  }
+
+  public final class NetworkEvent implements android.net.metrics.IpConnectivityLog.Event {
+    ctor public NetworkEvent(int, long);
+    ctor public NetworkEvent(int);
+    field public static final int NETWORK_CAPTIVE_PORTAL_FOUND = 4; // 0x4
+    field public static final int NETWORK_CONNECTED = 1; // 0x1
+    field public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12; // 0xc
+    field public static final int NETWORK_DISCONNECTED = 7; // 0x7
+    field public static final int NETWORK_FIRST_VALIDATION_PORTAL_FOUND = 10; // 0xa
+    field public static final int NETWORK_FIRST_VALIDATION_SUCCESS = 8; // 0x8
+    field public static final int NETWORK_LINGER = 5; // 0x5
+    field public static final int NETWORK_REVALIDATION_PORTAL_FOUND = 11; // 0xb
+    field public static final int NETWORK_REVALIDATION_SUCCESS = 9; // 0x9
+    field public static final int NETWORK_UNLINGER = 6; // 0x6
+    field public static final int NETWORK_VALIDATED = 2; // 0x2
+    field public static final int NETWORK_VALIDATION_FAILED = 3; // 0x3
+  }
+
+  public final class ValidationProbeEvent implements android.net.metrics.IpConnectivityLog.Event {
+    method public static String getProbeName(int);
+    field public static final int DNS_FAILURE = 0; // 0x0
+    field public static final int DNS_SUCCESS = 1; // 0x1
+    field public static final int PROBE_DNS = 0; // 0x0
+    field public static final int PROBE_FALLBACK = 4; // 0x4
+    field public static final int PROBE_HTTP = 1; // 0x1
+    field public static final int PROBE_HTTPS = 2; // 0x2
+    field public static final int PROBE_PAC = 3; // 0x3
+    field public static final int PROBE_PRIVDNS = 5; // 0x5
+  }
+
+  public static class ValidationProbeEvent.Builder {
+    ctor public ValidationProbeEvent.Builder();
+    method public android.net.metrics.ValidationProbeEvent build();
+    method public android.net.metrics.ValidationProbeEvent.Builder setDurationMs(long);
+    method public android.net.metrics.ValidationProbeEvent.Builder setProbeType(int, boolean);
+    method public android.net.metrics.ValidationProbeEvent.Builder setReturnCode(int);
+  }
+
+}
+
 package android.os {
 
   public static class Build.VERSION {
@@ -894,6 +1063,21 @@
 
   public static final class Settings.Global extends android.provider.Settings.NameValueTable {
     field public static final String AUTOFILL_COMPAT_MODE_ALLOWED_PACKAGES = "autofill_compat_mode_allowed_packages";
+    field public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = "captive_portal_fallback_probe_specs";
+    field public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url";
+    field public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url";
+    field public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
+    field public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
+    field public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; // 0x2
+    field public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; // 0x0
+    field public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; // 0x1
+    field public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS = "captive_portal_other_fallback_urls";
+    field public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
+    field public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https";
+    field public static final String DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = "data_stall_consecutive_dns_timeout_threshold";
+    field public static final String DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type";
+    field public static final String DATA_STALL_MIN_EVALUATE_INTERVAL = "data_stall_min_evaluate_interval";
+    field public static final String DATA_STALL_VALID_DNS_TIME_THRESHOLD = "data_stall_valid_dns_time_threshold";
     field public static final String HIDDEN_API_BLACKLIST_EXEMPTIONS = "hidden_api_blacklist_exemptions";
     field public static final String LOCATION_GLOBAL_KILL_SWITCH = "location_global_kill_switch";
     field public static final String LOW_POWER_MODE = "low_power";
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index a510c57..aeeaa51 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -4735,7 +4735,7 @@
     /**
      * @hide Implement to provide correct calling token.
      */
-    @UnsupportedAppUsage
+    @Override
     public void startActivityAsUser(Intent intent, UserHandle user) {
         startActivityAsUser(intent, null, user);
     }
diff --git a/core/java/android/content/Context.java b/core/java/android/content/Context.java
index 89cd064..29161cc 100644
--- a/core/java/android/content/Context.java
+++ b/core/java/android/content/Context.java
@@ -1711,7 +1711,7 @@
      * @hide
      */
     @RequiresPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL)
-    @UnsupportedAppUsage
+    @SystemApi
     public void startActivityAsUser(@RequiresPermission Intent intent, UserHandle user) {
         throw new RuntimeException("Not implemented. Must override in a subclass.");
     }
diff --git a/core/java/android/content/ContextWrapper.java b/core/java/android/content/ContextWrapper.java
index adc2eba..3cdfc6ee 100644
--- a/core/java/android/content/ContextWrapper.java
+++ b/core/java/android/content/ContextWrapper.java
@@ -386,7 +386,6 @@
 
     /** @hide */
     @Override
-    @UnsupportedAppUsage
     public void startActivityAsUser(Intent intent, UserHandle user) {
         mBase.startActivityAsUser(intent, user);
     }
diff --git a/core/java/android/net/CaptivePortal.java b/core/java/android/net/CaptivePortal.java
index ee05f28..4047068 100644
--- a/core/java/android/net/CaptivePortal.java
+++ b/core/java/android/net/CaptivePortal.java
@@ -15,6 +15,8 @@
  */
 package android.net;
 
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.IBinder;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -28,10 +30,16 @@
  */
 public class CaptivePortal implements Parcelable {
     /** @hide */
+    @SystemApi
+    @TestApi
     public static final int APP_RETURN_DISMISSED    = 0;
     /** @hide */
+    @SystemApi
+    @TestApi
     public static final int APP_RETURN_UNWANTED     = 1;
     /** @hide */
+    @SystemApi
+    @TestApi
     public static final int APP_RETURN_WANTED_AS_IS = 2;
 
     private final IBinder mBinder;
diff --git a/core/java/android/net/ConnectivityManager.java b/core/java/android/net/ConnectivityManager.java
index abc00fe..cee3a40 100644
--- a/core/java/android/net/ConnectivityManager.java
+++ b/core/java/android/net/ConnectivityManager.java
@@ -23,6 +23,7 @@
 import android.annotation.SdkConstant.SdkConstantType;
 import android.annotation.SystemApi;
 import android.annotation.SystemService;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.app.PendingIntent;
 import android.content.Context;
@@ -255,6 +256,8 @@
      * portal login activity.
      * {@hide}
      */
+    @SystemApi
+    @TestApi
     public static final String EXTRA_CAPTIVE_PORTAL_PROBE_SPEC =
             "android.net.extra.CAPTIVE_PORTAL_PROBE_SPEC";
 
@@ -262,6 +265,8 @@
      * Key for passing a user agent string to the captive portal login activity.
      * {@hide}
      */
+    @SystemApi
+    @TestApi
     public static final String EXTRA_CAPTIVE_PORTAL_USER_AGENT =
             "android.net.extra.CAPTIVE_PORTAL_USER_AGENT";
 
diff --git a/core/java/android/net/LinkAddress.java b/core/java/android/net/LinkAddress.java
index b40f15a..a536d08 100644
--- a/core/java/android/net/LinkAddress.java
+++ b/core/java/android/net/LinkAddress.java
@@ -26,6 +26,7 @@
 import static android.system.OsConstants.RT_SCOPE_UNIVERSE;
 
 import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
@@ -117,7 +118,8 @@
      * @return true if the address is IPv6.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public boolean isIPv6() {
         return address instanceof Inet6Address;
     }
@@ -126,6 +128,8 @@
      * @return true if the address is IPv4 or is a mapped IPv4 address.
      * @hide
      */
+    @TestApi
+    @SystemApi
     public boolean isIPv4() {
         return address instanceof Inet4Address;
     }
@@ -263,7 +267,8 @@
      * otherwise.
      * @hide
      */
-    @UnsupportedAppUsage
+    @TestApi
+    @SystemApi
     public boolean isSameAddressAs(LinkAddress other) {
         return address.equals(other.address) && prefixLength == other.prefixLength;
     }
@@ -310,6 +315,8 @@
      * Returns true if this {@code LinkAddress} is global scope and preferred.
      * @hide
      */
+    @TestApi
+    @SystemApi
     public boolean isGlobalPreferred() {
         /**
          * Note that addresses flagged as IFA_F_OPTIMISTIC are
diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java
index 058cb94..c2b7d2c 100644
--- a/core/java/android/net/MacAddress.java
+++ b/core/java/android/net/MacAddress.java
@@ -52,6 +52,8 @@
 
     /**
      * The MacAddress zero MAC address.
+     *
+     * <p>Not publicly exposed or treated specially since the OUI 00:00:00 is registered.
      * @hide
      */
     @UnsupportedAppUsage
diff --git a/core/java/android/net/Network.java b/core/java/android/net/Network.java
index bf2344d..2c831de 100644
--- a/core/java/android/net/Network.java
+++ b/core/java/android/net/Network.java
@@ -16,6 +16,8 @@
 
 package android.net;
 
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -157,6 +159,8 @@
      *
      * @hide
      */
+    @TestApi
+    @SystemApi
     public Network getPrivateDnsBypassingCopy() {
         return new Network(netId, true);
     }
diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java
index 2eac6de..af043ee 100644
--- a/core/java/android/net/NetworkStack.java
+++ b/core/java/android/net/NetworkStack.java
@@ -50,6 +50,8 @@
 
     public static final String NETWORKSTACK_PACKAGE_NAME = "com.android.mainline.networkstack";
 
+    private static final int NETWORKSTACK_TIMEOUT_MS = 10_000;
+
     @NonNull
     @GuardedBy("mPendingNetStackRequests")
     private final ArrayList<NetworkStackCallback> mPendingNetStackRequests = new ArrayList<>();
@@ -57,6 +59,8 @@
     @GuardedBy("mPendingNetStackRequests")
     private INetworkStackConnector mConnector;
 
+    private volatile boolean mNetworkStackStartRequested = false;
+
     private interface NetworkStackCallback {
         void onNetworkStackConnected(INetworkStackConnector connector);
     }
@@ -134,6 +138,7 @@
      * started.
      */
     public void start(Context context) {
+        mNetworkStackStartRequested = true;
         // Try to bind in-process if the library is available
         IBinder connector = null;
         try {
@@ -170,15 +175,54 @@
         }
     }
 
-    // TODO: use this method to obtain the connector when implementing network stack operations
+    /**
+     * For non-system server clients, get the connector registered by the system server.
+     */
+    private INetworkStackConnector getRemoteConnector() {
+        // Block until the NetworkStack connector is registered in ServiceManager.
+        // <p>This is only useful for non-system processes that do not have a way to be notified of
+        // registration completion. Adding a callback system would be too heavy weight considering
+        // that the connector is registered on boot, so it is unlikely that a client would request
+        // it before it is registered.
+        // TODO: consider blocking boot on registration and simplify much of the logic in this class
+        IBinder connector;
+        try {
+            final long before = System.currentTimeMillis();
+            while ((connector = ServiceManager.getService(Context.NETWORK_STACK_SERVICE)) == null) {
+                Thread.sleep(20);
+                if (System.currentTimeMillis() - before > NETWORKSTACK_TIMEOUT_MS) {
+                    Slog.e(TAG, "Timeout waiting for NetworkStack connector");
+                    return null;
+                }
+            }
+        } catch (InterruptedException e) {
+            Slog.e(TAG, "Error waiting for NetworkStack connector", e);
+            return null;
+        }
+
+        return INetworkStackConnector.Stub.asInterface(connector);
+    }
+
     private void requestConnector(@NonNull NetworkStackCallback request) {
         // TODO: PID check.
-        if (Binder.getCallingUid() != Process.SYSTEM_UID) {
+        final int caller = Binder.getCallingUid();
+        if (caller != Process.SYSTEM_UID && caller != Process.BLUETOOTH_UID) {
             // Don't even attempt to obtain the connector and give a nice error message
             throw new SecurityException(
                     "Only the system server should try to bind to the network stack.");
         }
 
+        if (!mNetworkStackStartRequested) {
+            // The network stack is not being started in this process, e.g. this process is not
+            // the system server. Get a remote connector registered by the system server.
+            final INetworkStackConnector connector = getRemoteConnector();
+            synchronized (mPendingNetStackRequests) {
+                mConnector = connector;
+            }
+            request.onNetworkStackConnected(connector);
+            return;
+        }
+
         final INetworkStackConnector connector;
         synchronized (mPendingNetStackRequests) {
             connector = mConnector;
diff --git a/core/java/android/net/RouteInfo.java b/core/java/android/net/RouteInfo.java
index 37ab9ff..6bf2c67 100644
--- a/core/java/android/net/RouteInfo.java
+++ b/core/java/android/net/RouteInfo.java
@@ -16,16 +16,17 @@
 
 package android.net;
 
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Build;
 import android.os.Parcel;
 import android.os.Parcelable;
 
-import java.net.UnknownHostException;
-import java.net.InetAddress;
 import java.net.Inet4Address;
 import java.net.Inet6Address;
-
+import java.net.InetAddress;
+import java.net.UnknownHostException;
 import java.util.Collection;
 import java.util.Objects;
 
@@ -67,12 +68,18 @@
 
 
     /** Unicast route. @hide */
+    @SystemApi
+    @TestApi
     public static final int RTN_UNICAST = 1;
 
     /** Unreachable route. @hide */
+    @SystemApi
+    @TestApi
     public static final int RTN_UNREACHABLE = 7;
 
     /** Throw route. @hide */
+    @SystemApi
+    @TestApi
     public static final int RTN_THROW = 9;
 
     /**
@@ -317,6 +324,8 @@
      *
      * @hide
      */
+    @TestApi
+    @SystemApi
     public int getType() {
         return mType;
     }
@@ -362,9 +371,7 @@
      * ({@code false}).
      *
      * @return {@code true} if a gateway is specified
-     * @hide
      */
-    @UnsupportedAppUsage
     public boolean hasGateway() {
         return mHasGateway;
     }
diff --git a/core/java/android/net/ip/IIpClient.aidl b/core/java/android/net/ip/IIpClient.aidl
new file mode 100644
index 0000000..7769ec2
--- /dev/null
+++ b/core/java/android/net/ip/IIpClient.aidl
@@ -0,0 +1,32 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+package android.net.ip;
+
+import android.net.ProxyInfoParcelable;
+import android.net.ProvisioningConfigurationParcelable;
+
+/** @hide */
+oneway interface IIpClient {
+    void completedPreDhcpAction();
+    void confirmConfiguration();
+    void readPacketFilterComplete(in byte[] data);
+    void shutdown();
+    void startProvisioning(in ProvisioningConfigurationParcelable req);
+    void stop();
+    void setTcpBufferSizes(in String tcpBufferSizes);
+    void setHttpProxy(in ProxyInfoParcelable proxyInfo);
+    void setMulticastFilter(boolean enabled);
+}
diff --git a/core/java/android/net/ip/IIpClientCallbacks.aidl b/core/java/android/net/ip/IIpClientCallbacks.aidl
new file mode 100644
index 0000000..f077e3b
--- /dev/null
+++ b/core/java/android/net/ip/IIpClientCallbacks.aidl
@@ -0,0 +1,66 @@
+/**
+ * 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 perNmissions and
+ * limitations under the License.
+ */
+package android.net.ip;
+
+import android.net.LinkPropertiesParcelable;
+import android.net.ip.IIpClient;
+import android.net.DhcpResultsParcelable;
+
+/** @hide */
+oneway interface IIpClientCallbacks {
+    void onIpClientCreated(in IIpClient ipClient);
+
+    void onPreDhcpAction();
+    void onPostDhcpAction();
+
+    // This is purely advisory and not an indication of provisioning
+    // success or failure.  This is only here for callers that want to
+    // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
+    // DHCPv4 or static IPv4 configuration failure or success can be
+    // determined by whether or not the passed-in DhcpResults object is
+    // null or not.
+    void onNewDhcpResults(in DhcpResultsParcelable dhcpResults);
+
+    void onProvisioningSuccess(in LinkPropertiesParcelable newLp);
+    void onProvisioningFailure(in LinkPropertiesParcelable newLp);
+
+    // Invoked on LinkProperties changes.
+    void onLinkPropertiesChange(in LinkPropertiesParcelable newLp);
+
+    // Called when the internal IpReachabilityMonitor (if enabled) has
+    // detected the loss of a critical number of required neighbors.
+    void onReachabilityLost(in String logMsg);
+
+    // Called when the IpClient state machine terminates.
+    void onQuit();
+
+    // Install an APF program to filter incoming packets.
+    void installPacketFilter(in byte[] filter);
+
+    // Asynchronously read back the APF program & data buffer from the wifi driver.
+    // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
+    // buffer. In response to this request, the driver returns the data buffer asynchronously
+    // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
+    void startReadPacketFilter();
+
+    // If multicast filtering cannot be accomplished with APF, this function will be called to
+    // actuate multicast filtering using another means.
+    void setFallbackMulticastFilter(boolean enabled);
+
+    // Enabled/disable Neighbor Discover offload functionality. This is
+    // called, for example, whenever 464xlat is being started or stopped.
+    void setNeighborDiscoveryOffload(boolean enable);
+}
\ No newline at end of file
diff --git a/core/java/android/net/ip/IpClientCallbacks.java b/core/java/android/net/ip/IpClientCallbacks.java
new file mode 100644
index 0000000..db01ae4
--- /dev/null
+++ b/core/java/android/net/ip/IpClientCallbacks.java
@@ -0,0 +1,119 @@
+/*
+ * 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 android.net.ip;
+
+import android.net.DhcpResults;
+import android.net.LinkProperties;
+
+/**
+ * Callbacks for handling IpClient events.
+ *
+ * This is a convenience class to allow clients not to override all methods of IIpClientCallbacks,
+ * and avoid unparceling arguments.
+ * These methods are called asynchronously on a Binder thread, as IpClient lives in a different
+ * process.
+ * @hide
+ */
+public class IpClientCallbacks {
+
+    /**
+     * Callback called upon IpClient creation.
+     *
+     * @param ipClient The Binder token to communicate with IpClient.
+     */
+    public void onIpClientCreated(IIpClient ipClient) {}
+
+    /**
+     * Callback called prior to DHCP discovery/renewal.
+     *
+     * <p>In order to receive onPreDhcpAction(), call #withPreDhcpAction() when constructing a
+     * ProvisioningConfiguration.
+     *
+     * <p>Implementations of onPreDhcpAction() must call IpClient#completedPreDhcpAction() to
+     * indicate that DHCP is clear to proceed.
+      */
+    public void onPreDhcpAction() {}
+
+    /**
+     * Callback called after DHCP discovery/renewal.
+     */
+    public void onPostDhcpAction() {}
+
+    /**
+     * Callback called when new DHCP results are available.
+     *
+     * <p>This is purely advisory and not an indication of provisioning success or failure.  This is
+     * only here for callers that want to expose DHCPv4 results to other APIs
+     * (e.g., WifiInfo#setInetAddress).
+     *
+     * <p>DHCPv4 or static IPv4 configuration failure or success can be determined by whether or not
+     * the passed-in DhcpResults object is null.
+     */
+    public void onNewDhcpResults(DhcpResults dhcpResults) {}
+
+    /**
+     * Indicates that provisioning was successful.
+     */
+    public void onProvisioningSuccess(LinkProperties newLp) {}
+
+    /**
+     * Indicates that provisioning failed.
+     */
+    public void onProvisioningFailure(LinkProperties newLp) {}
+
+    /**
+     * Invoked on LinkProperties changes.
+     */
+    public void onLinkPropertiesChange(LinkProperties newLp) {}
+
+    /**Called when the internal IpReachabilityMonitor (if enabled) has
+     * detected the loss of a critical number of required neighbors.
+     */
+    public void onReachabilityLost(String logMsg) {}
+
+    /**
+     * Called when the IpClient state machine terminates.
+     */
+    public void onQuit() {}
+
+    /**
+     * Called to indicate that a new APF program must be installed to filter incoming packets.
+     */
+    public void installPacketFilter(byte[] filter) {}
+
+    /**
+     * Called to indicate that the APF Program & data buffer must be read asynchronously from the
+     * wifi driver.
+     *
+     * <p>Due to Wifi HAL limitations, the current implementation only supports dumping the entire
+     * buffer. In response to this request, the driver returns the data buffer asynchronously
+     * by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
+     */
+    public void startReadPacketFilter() {}
+
+    /**
+     * If multicast filtering cannot be accomplished with APF, this function will be called to
+     * actuate multicast filtering using another means.
+     */
+    public void setFallbackMulticastFilter(boolean enabled) {}
+
+    /**
+     * Enabled/disable Neighbor Discover offload functionality. This is called, for example,
+     * whenever 464xlat is being started or stopped.
+     */
+    public void setNeighborDiscoveryOffload(boolean enable) {}
+}
diff --git a/core/java/android/net/metrics/ApfProgramEvent.java b/core/java/android/net/metrics/ApfProgramEvent.java
index 5dabf35..8601005 100644
--- a/core/java/android/net/metrics/ApfProgramEvent.java
+++ b/core/java/android/net/metrics/ApfProgramEvent.java
@@ -17,6 +17,8 @@
 package android.net.metrics;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -36,11 +38,15 @@
  * the APF program in place with a new APF program.
  * {@hide}
  */
-public final class ApfProgramEvent implements Parcelable {
+@TestApi
+@SystemApi
+public final class ApfProgramEvent implements IpConnectivityLog.Event {
 
     // Bitflag constants describing what an Apf program filters.
     // Bits are indexeds from LSB to MSB, starting at index 0.
+    /** @hide */
     public static final int FLAG_MULTICAST_FILTER_ON = 0;
+    /** @hide */
     public static final int FLAG_HAS_IPV4_ADDRESS    = 1;
 
     /** {@hide} */
@@ -48,21 +54,33 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface Flags {}
 
+    /** @hide */
     @UnsupportedAppUsage
-    public long lifetime;       // Maximum computed lifetime of the program in seconds
+    public final long lifetime;       // Maximum computed lifetime of the program in seconds
+    /** @hide */
     @UnsupportedAppUsage
-    public long actualLifetime; // Effective program lifetime in seconds
+    public final long actualLifetime; // Effective program lifetime in seconds
+    /** @hide */
     @UnsupportedAppUsage
-    public int filteredRas;     // Number of RAs filtered by the APF program
+    public final int filteredRas;     // Number of RAs filtered by the APF program
+    /** @hide */
     @UnsupportedAppUsage
-    public int currentRas;      // Total number of current RAs at generation time
+    public final int currentRas;      // Total number of current RAs at generation time
+    /** @hide */
     @UnsupportedAppUsage
-    public int programLength;   // Length of the APF program in bytes
+    public final int programLength;   // Length of the APF program in bytes
+    /** @hide */
     @UnsupportedAppUsage
-    public int flags;           // Bitfield compound of FLAG_* constants
+    public final int flags;           // Bitfield compound of FLAG_* constants
 
-    @UnsupportedAppUsage
-    public ApfProgramEvent() {
+    private ApfProgramEvent(long lifetime, long actualLifetime, int filteredRas, int currentRas,
+            int programLength, int flags) {
+        this.lifetime = lifetime;
+        this.actualLifetime = actualLifetime;
+        this.filteredRas = filteredRas;
+        this.currentRas = currentRas;
+        this.programLength = programLength;
+        this.flags = flags;
     }
 
     private ApfProgramEvent(Parcel in) {
@@ -74,6 +92,75 @@
         this.flags = in.readInt();
     }
 
+    /**
+     * Utility to create an instance of {@link ApfProgramEvent}.
+     */
+    public static class Builder {
+        private long mLifetime;
+        private long mActualLifetime;
+        private int mFilteredRas;
+        private int mCurrentRas;
+        private int mProgramLength;
+        private int mFlags;
+
+        /**
+         * Set the maximum computed lifetime of the program in seconds.
+         */
+        public Builder setLifetime(long lifetime) {
+            mLifetime = lifetime;
+            return this;
+        }
+
+        /**
+         * Set the effective program lifetime in seconds.
+         */
+        public Builder setActualLifetime(long lifetime) {
+            mActualLifetime = lifetime;
+            return this;
+        }
+
+        /**
+         * Set the number of RAs filtered by the APF program.
+         */
+        public Builder setFilteredRas(int filteredRas) {
+            mFilteredRas = filteredRas;
+            return this;
+        }
+
+        /**
+         * Set the total number of current RAs at generation time.
+         */
+        public Builder setCurrentRas(int currentRas) {
+            mCurrentRas = currentRas;
+            return this;
+        }
+
+        /**
+         * Set the length of the APF program in bytes.
+         */
+        public Builder setProgramLength(int programLength) {
+            mProgramLength = programLength;
+            return this;
+        }
+
+        /**
+         * Set the flags describing what an Apf program filters.
+         */
+        public Builder setFlags(boolean hasIPv4, boolean multicastFilterOn) {
+            mFlags = flagsFor(hasIPv4, multicastFilterOn);
+            return this;
+        }
+
+        /**
+         * Build a new {@link ApfProgramEvent}.
+         */
+        public ApfProgramEvent build() {
+            return new ApfProgramEvent(mLifetime, mActualLifetime, mFilteredRas, mCurrentRas,
+                    mProgramLength, mFlags);
+        }
+    }
+
+    /** @hide */
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeLong(lifetime);
@@ -84,6 +171,7 @@
         out.writeInt(flags);
     }
 
+    /** @hide */
     @Override
     public int describeContents() {
         return 0;
@@ -96,6 +184,7 @@
                 programLength, actualLifetime, lifetimeString, namesOf(flags));
     }
 
+    /** @hide */
     public static final Parcelable.Creator<ApfProgramEvent> CREATOR
             = new Parcelable.Creator<ApfProgramEvent>() {
         public ApfProgramEvent createFromParcel(Parcel in) {
@@ -107,6 +196,7 @@
         }
     };
 
+    /** @hide */
     @UnsupportedAppUsage
     public static @Flags int flagsFor(boolean hasIPv4, boolean multicastFilterOn) {
         int bitfield = 0;
diff --git a/core/java/android/net/metrics/ApfStats.java b/core/java/android/net/metrics/ApfStats.java
index bb2a35c..844a134 100644
--- a/core/java/android/net/metrics/ApfStats.java
+++ b/core/java/android/net/metrics/ApfStats.java
@@ -16,6 +16,8 @@
 
 package android.net.metrics;
 
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -24,42 +26,70 @@
  * An event logged for an interface with APF capabilities when its IpClient state machine exits.
  * {@hide}
  */
-public final class ApfStats implements Parcelable {
+@SystemApi
+@TestApi
+public final class ApfStats implements IpConnectivityLog.Event {
 
-    /** time interval in milliseconds these stastistics covers. */
+    /**
+     * time interval in milliseconds these stastistics covers.
+     * @hide
+     */
     @UnsupportedAppUsage
-    public long durationMs;
-    /** number of received RAs. */
+    public final long durationMs;
+    /**
+     * number of received RAs.
+     * @hide
+     */
     @UnsupportedAppUsage
-    public int receivedRas;
-    /** number of received RAs matching a known RA. */
+    public final int receivedRas;
+    /**
+     * number of received RAs matching a known RA.
+     * @hide
+     */
     @UnsupportedAppUsage
-    public int matchingRas;
-    /** number of received RAs ignored due to the MAX_RAS limit. */
+    public final int matchingRas;
+    /**
+     * number of received RAs ignored due to the MAX_RAS limit.
+     * @hide
+     */
     @UnsupportedAppUsage
-    public int droppedRas;
-    /** number of received RAs with a minimum lifetime of 0. */
+    public final int droppedRas;
+    /**
+     * number of received RAs with a minimum lifetime of 0.
+     * @hide
+     */
     @UnsupportedAppUsage
-    public int zeroLifetimeRas;
-    /** number of received RAs that could not be parsed. */
+    public final int zeroLifetimeRas;
+    /**
+     * number of received RAs that could not be parsed.
+     * @hide
+     */
     @UnsupportedAppUsage
-    public int parseErrors;
-    /** number of APF program updates from receiving RAs.. */
+    public final int parseErrors;
+    /**
+     * number of APF program updates from receiving RAs.
+     * @hide
+     */
     @UnsupportedAppUsage
-    public int programUpdates;
-    /** total number of APF program updates. */
+    public final int programUpdates;
+    /**
+     * total number of APF program updates.
+     * @hide
+     */
     @UnsupportedAppUsage
-    public int programUpdatesAll;
-    /** number of APF program updates from allowing multicast traffic. */
+    public final int programUpdatesAll;
+    /**
+     * number of APF program updates from allowing multicast traffic.
+     * @hide
+     */
     @UnsupportedAppUsage
-    public int programUpdatesAllowingMulticast;
-    /** maximum APF program size advertised by hardware. */
+    public final int programUpdatesAllowingMulticast;
+    /**
+     * maximum APF program size advertised by hardware.
+     * @hide
+     */
     @UnsupportedAppUsage
-    public int maxProgramSize;
-
-    @UnsupportedAppUsage
-    public ApfStats() {
-    }
+    public final int maxProgramSize;
 
     private ApfStats(Parcel in) {
         this.durationMs = in.readLong();
@@ -74,6 +104,130 @@
         this.maxProgramSize = in.readInt();
     }
 
+    private ApfStats(long durationMs, int receivedRas, int matchingRas, int droppedRas,
+            int zeroLifetimeRas, int parseErrors, int programUpdates, int programUpdatesAll,
+            int programUpdatesAllowingMulticast, int maxProgramSize) {
+        this.durationMs = durationMs;
+        this.receivedRas = receivedRas;
+        this.matchingRas = matchingRas;
+        this.droppedRas = droppedRas;
+        this.zeroLifetimeRas = zeroLifetimeRas;
+        this.parseErrors = parseErrors;
+        this.programUpdates = programUpdates;
+        this.programUpdatesAll = programUpdatesAll;
+        this.programUpdatesAllowingMulticast = programUpdatesAllowingMulticast;
+        this.maxProgramSize = maxProgramSize;
+    }
+
+    /**
+     * Utility to create an instance of {@link ApfStats}.
+     * @hide
+     */
+    @SystemApi
+    @TestApi
+    public static class Builder {
+        private long mDurationMs;
+        private int mReceivedRas;
+        private int mMatchingRas;
+        private int mDroppedRas;
+        private int mZeroLifetimeRas;
+        private int mParseErrors;
+        private int mProgramUpdates;
+        private int mProgramUpdatesAll;
+        private int mProgramUpdatesAllowingMulticast;
+        private int mMaxProgramSize;
+
+        /**
+         * Set the time interval in milliseconds these statistics covers.
+         */
+        public Builder setDurationMs(long durationMs) {
+            mDurationMs = durationMs;
+            return this;
+        }
+
+        /**
+         * Set the number of received RAs.
+         */
+        public Builder setReceivedRas(int receivedRas) {
+            mReceivedRas = receivedRas;
+            return this;
+        }
+
+        /**
+         * Set the number of received RAs matching a known RA.
+         */
+        public Builder setMatchingRas(int matchingRas) {
+            mMatchingRas = matchingRas;
+            return this;
+        }
+
+        /**
+         * Set the number of received RAs ignored due to the MAX_RAS limit.
+         */
+        public Builder setDroppedRas(int droppedRas) {
+            mDroppedRas = droppedRas;
+            return this;
+        }
+
+        /**
+         * Set the number of received RAs with a minimum lifetime of 0.
+         */
+        public Builder setZeroLifetimeRas(int zeroLifetimeRas) {
+            mZeroLifetimeRas = zeroLifetimeRas;
+            return this;
+        }
+
+        /**
+         * Set the number of received RAs that could not be parsed.
+         */
+        public Builder setParseErrors(int parseErrors) {
+            mParseErrors = parseErrors;
+            return this;
+        }
+
+        /**
+         * Set the number of APF program updates from receiving RAs.
+         */
+        public Builder setProgramUpdates(int programUpdates) {
+            mProgramUpdates = programUpdates;
+            return this;
+        }
+
+        /**
+         * Set the total number of APF program updates.
+         */
+        public Builder setProgramUpdatesAll(int programUpdatesAll) {
+            mProgramUpdatesAll = programUpdatesAll;
+            return this;
+        }
+
+        /**
+         * Set the number of APF program updates from allowing multicast traffic.
+         */
+        public Builder setProgramUpdatesAllowingMulticast(int programUpdatesAllowingMulticast) {
+            mProgramUpdatesAllowingMulticast = programUpdatesAllowingMulticast;
+            return this;
+        }
+
+        /**
+         * Set the maximum APF program size advertised by hardware.
+         */
+        public Builder setMaxProgramSize(int maxProgramSize) {
+            mMaxProgramSize = maxProgramSize;
+            return this;
+        }
+
+        /**
+         * Create a new {@link ApfStats}.
+         */
+        public ApfStats build() {
+            return new ApfStats(mDurationMs, mReceivedRas, mMatchingRas, mDroppedRas,
+                    mZeroLifetimeRas, mParseErrors, mProgramUpdates, mProgramUpdatesAll,
+                    mProgramUpdatesAllowingMulticast, mMaxProgramSize);
+        }
+    }
+
+    /** @hide */
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeLong(durationMs);
@@ -88,6 +242,7 @@
         out.writeInt(maxProgramSize);
     }
 
+    /** @hide */
     @Override
     public int describeContents() {
         return 0;
@@ -108,6 +263,7 @@
                 .toString();
     }
 
+    /** @hide */
     public static final Parcelable.Creator<ApfStats> CREATOR = new Parcelable.Creator<ApfStats>() {
         public ApfStats createFromParcel(Parcel in) {
             return new ApfStats(in);
diff --git a/core/java/android/net/metrics/DhcpClientEvent.java b/core/java/android/net/metrics/DhcpClientEvent.java
index 98a7d7e..2a942ee 100644
--- a/core/java/android/net/metrics/DhcpClientEvent.java
+++ b/core/java/android/net/metrics/DhcpClientEvent.java
@@ -16,6 +16,8 @@
 
 package android.net.metrics;
 
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.os.Parcel;
 import android.os.Parcelable;
@@ -24,7 +26,9 @@
  * An event recorded when a DhcpClient state machine transitions to a new state.
  * {@hide}
  */
-public final class DhcpClientEvent implements Parcelable {
+@SystemApi
+@TestApi
+public final class DhcpClientEvent implements IpConnectivityLog.Event {
 
     // Names for recording DhcpClient pseudo-state transitions.
     /** {@hide} Represents transitions from DhcpInitState to DhcpBoundState */
@@ -32,11 +36,13 @@
     /** {@hide} Represents transitions from and to DhcpBoundState via DhcpRenewingState */
     public static final String RENEWING_BOUND = "RenewingBoundState";
 
+    /** @hide */
     public final String msg;
+    /** @hide */
     public final int durationMs;
 
     @UnsupportedAppUsage
-    public DhcpClientEvent(String msg, int durationMs) {
+    private DhcpClientEvent(String msg, int durationMs) {
         this.msg = msg;
         this.durationMs = durationMs;
     }
@@ -46,12 +52,45 @@
         this.durationMs = in.readInt();
     }
 
+    /**
+     * Utility to create an instance of {@link ApfProgramEvent}.
+     */
+    public static class Builder {
+        private String mMsg;
+        private int mDurationMs;
+
+        /**
+         * Set the message of the event.
+         */
+        public Builder setMsg(String msg) {
+            mMsg = msg;
+            return this;
+        }
+
+        /**
+         * Set the duration of the event in milliseconds.
+         */
+        public Builder setDurationMs(int durationMs) {
+            mDurationMs = durationMs;
+            return this;
+        }
+
+        /**
+         * Create a new {@link DhcpClientEvent}.
+         */
+        public DhcpClientEvent build() {
+            return new DhcpClientEvent(mMsg, mDurationMs);
+        }
+    }
+
+    /** @hide */
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeString(msg);
         out.writeInt(durationMs);
     }
 
+    /** @hide */
     @Override
     public int describeContents() {
         return 0;
@@ -62,6 +101,7 @@
         return String.format("DhcpClientEvent(%s, %dms)", msg, durationMs);
     }
 
+    /** @hide */
     public static final Parcelable.Creator<DhcpClientEvent> CREATOR
         = new Parcelable.Creator<DhcpClientEvent>() {
         public DhcpClientEvent createFromParcel(Parcel in) {
diff --git a/core/java/android/net/metrics/DhcpErrorEvent.java b/core/java/android/net/metrics/DhcpErrorEvent.java
index c6c06f0..18fde80 100644
--- a/core/java/android/net/metrics/DhcpErrorEvent.java
+++ b/core/java/android/net/metrics/DhcpErrorEvent.java
@@ -16,7 +16,8 @@
 
 package android.net.metrics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -27,48 +28,34 @@
  * Event class used to record error events when parsing DHCP response packets.
  * {@hide}
  */
-public final class DhcpErrorEvent implements Parcelable {
+@SystemApi
+@TestApi
+public final class DhcpErrorEvent implements IpConnectivityLog.Event {
     public static final int L2_ERROR   = 1;
     public static final int L3_ERROR   = 2;
     public static final int L4_ERROR   = 3;
     public static final int DHCP_ERROR = 4;
     public static final int MISC_ERROR = 5;
 
-    @UnsupportedAppUsage
     public static final int L2_TOO_SHORT               = makeErrorCode(L2_ERROR, 1);
-    @UnsupportedAppUsage
     public static final int L2_WRONG_ETH_TYPE          = makeErrorCode(L2_ERROR, 2);
 
-    @UnsupportedAppUsage
     public static final int L3_TOO_SHORT               = makeErrorCode(L3_ERROR, 1);
-    @UnsupportedAppUsage
     public static final int L3_NOT_IPV4                = makeErrorCode(L3_ERROR, 2);
-    @UnsupportedAppUsage
     public static final int L3_INVALID_IP              = makeErrorCode(L3_ERROR, 3);
 
-    @UnsupportedAppUsage
     public static final int L4_NOT_UDP                 = makeErrorCode(L4_ERROR, 1);
-    @UnsupportedAppUsage
     public static final int L4_WRONG_PORT              = makeErrorCode(L4_ERROR, 2);
 
-    @UnsupportedAppUsage
     public static final int BOOTP_TOO_SHORT            = makeErrorCode(DHCP_ERROR, 1);
-    @UnsupportedAppUsage
     public static final int DHCP_BAD_MAGIC_COOKIE      = makeErrorCode(DHCP_ERROR, 2);
-    @UnsupportedAppUsage
     public static final int DHCP_INVALID_OPTION_LENGTH = makeErrorCode(DHCP_ERROR, 3);
-    @UnsupportedAppUsage
     public static final int DHCP_NO_MSG_TYPE           = makeErrorCode(DHCP_ERROR, 4);
-    @UnsupportedAppUsage
     public static final int DHCP_UNKNOWN_MSG_TYPE      = makeErrorCode(DHCP_ERROR, 5);
-    @UnsupportedAppUsage
     public static final int DHCP_NO_COOKIE             = makeErrorCode(DHCP_ERROR, 6);
 
-    @UnsupportedAppUsage
     public static final int BUFFER_UNDERFLOW           = makeErrorCode(MISC_ERROR, 1);
-    @UnsupportedAppUsage
     public static final int RECEIVE_ERROR              = makeErrorCode(MISC_ERROR, 2);
-    @UnsupportedAppUsage
     public static final int PARSING_ERROR              = makeErrorCode(MISC_ERROR, 3);
 
     // error code byte format (MSB to LSB):
@@ -76,9 +63,9 @@
     // byte 1: error subtype
     // byte 2: unused
     // byte 3: optional code
+    /** @hide */
     public final int errorCode;
 
-    @UnsupportedAppUsage
     public DhcpErrorEvent(int errorCode) {
         this.errorCode = errorCode;
     }
@@ -87,16 +74,19 @@
         this.errorCode = in.readInt();
     }
 
+    /** @hide */
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(errorCode);
     }
 
+    /** @hide */
     @Override
     public int describeContents() {
         return 0;
     }
 
+    /** @hide */
     public static final Parcelable.Creator<DhcpErrorEvent> CREATOR
         = new Parcelable.Creator<DhcpErrorEvent>() {
         public DhcpErrorEvent createFromParcel(Parcel in) {
@@ -108,7 +98,6 @@
         }
     };
 
-    @UnsupportedAppUsage
     public static int errorCodeWithOption(int errorCode, int option) {
         return (0xFFFF0000 & errorCode) | (0xFF & option);
     }
diff --git a/core/java/android/net/metrics/IpConnectivityLog.java b/core/java/android/net/metrics/IpConnectivityLog.java
index 998f4ba..c04fcdc 100644
--- a/core/java/android/net/metrics/IpConnectivityLog.java
+++ b/core/java/android/net/metrics/IpConnectivityLog.java
@@ -16,6 +16,8 @@
 
 package android.net.metrics;
 
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.annotation.UnsupportedAppUsage;
 import android.net.ConnectivityMetricsEvent;
 import android.net.IIpConnectivityMetrics;
@@ -23,6 +25,7 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.util.Log;
+
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.BitUtils;
 
@@ -30,18 +33,28 @@
  * Class for logging IpConnectvity events with IpConnectivityMetrics
  * {@hide}
  */
+@SystemApi
+@TestApi
 public class IpConnectivityLog {
     private static final String TAG = IpConnectivityLog.class.getSimpleName();
     private static final boolean DBG = false;
 
+    /** @hide */
     public static final String SERVICE_NAME = "connmetrics";
 
     private IIpConnectivityMetrics mService;
 
+    /**
+     * An event to be logged.
+     */
+    public interface Event extends Parcelable {}
+
+    /** @hide */
     @UnsupportedAppUsage
     public IpConnectivityLog() {
     }
 
+    /** @hide */
     @VisibleForTesting
     public IpConnectivityLog(IIpConnectivityMetrics service) {
         mService = service;
@@ -67,6 +80,7 @@
      * @param ev the event to log. If the event timestamp is 0,
      * the timestamp is set to the current time in milliseconds.
      * @return true if the event was successfully logged.
+     * @hide
      */
     public boolean log(ConnectivityMetricsEvent ev) {
         if (!checkLoggerService()) {
@@ -94,7 +108,7 @@
      * @param data is a Parcelable instance representing the event.
      * @return true if the event was successfully logged.
      */
-    public boolean log(long timestamp, Parcelable data) {
+    public boolean log(long timestamp, Event data) {
         ConnectivityMetricsEvent ev = makeEv(data);
         ev.timestamp = timestamp;
         return log(ev);
@@ -106,8 +120,7 @@
      * @param data is a Parcelable instance representing the event.
      * @return true if the event was successfully logged.
      */
-    @UnsupportedAppUsage
-    public boolean log(String ifname, Parcelable data) {
+    public boolean log(String ifname, Event data) {
         ConnectivityMetricsEvent ev = makeEv(data);
         ev.ifname = ifname;
         return log(ev);
@@ -121,7 +134,7 @@
      * @param data is a Parcelable instance representing the event.
      * @return true if the event was successfully logged.
      */
-    public boolean log(int netid, int[] transports, Parcelable data) {
+    public boolean log(int netid, int[] transports, Event data) {
         ConnectivityMetricsEvent ev = makeEv(data);
         ev.netId = netid;
         ev.transports = BitUtils.packBits(transports);
@@ -133,12 +146,11 @@
      * @param data is a Parcelable instance representing the event.
      * @return true if the event was successfully logged.
      */
-    @UnsupportedAppUsage
-    public boolean log(Parcelable data) {
+    public boolean log(Event data) {
         return log(makeEv(data));
     }
 
-    private static ConnectivityMetricsEvent makeEv(Parcelable data) {
+    private static ConnectivityMetricsEvent makeEv(Event data) {
         ConnectivityMetricsEvent ev = new ConnectivityMetricsEvent();
         ev.data = data;
         return ev;
diff --git a/core/java/android/net/metrics/IpManagerEvent.java b/core/java/android/net/metrics/IpManagerEvent.java
index c47650f..013e353 100644
--- a/core/java/android/net/metrics/IpManagerEvent.java
+++ b/core/java/android/net/metrics/IpManagerEvent.java
@@ -17,7 +17,8 @@
 package android.net.metrics;
 
 import android.annotation.IntDef;
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -28,11 +29,13 @@
 import java.lang.annotation.RetentionPolicy;
 
 /**
- * An event recorded by IpManager when IP provisioning completes for a network or
+ * An event recorded by IpClient when IP provisioning completes for a network or
  * when a network disconnects.
  * {@hide}
  */
-public final class IpManagerEvent implements Parcelable {
+@SystemApi
+@TestApi
+public final class IpManagerEvent implements IpConnectivityLog.Event {
 
     public static final int PROVISIONING_OK                       = 1;
     public static final int PROVISIONING_FAIL                     = 2;
@@ -43,6 +46,7 @@
     public static final int ERROR_INVALID_PROVISIONING            = 7;
     public static final int ERROR_INTERFACE_NOT_FOUND             = 8;
 
+    /** @hide */
     @IntDef(value = {
             PROVISIONING_OK, PROVISIONING_FAIL, COMPLETE_LIFECYCLE,
             ERROR_STARTING_IPV4, ERROR_STARTING_IPV6, ERROR_STARTING_IPREACHABILITYMONITOR,
@@ -51,10 +55,11 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
 
+    /** @hide */
     public final @EventType int eventType;
+    /** @hide */
     public final long durationMs;
 
-    @UnsupportedAppUsage
     public IpManagerEvent(@EventType int eventType, long duration) {
         this.eventType = eventType;
         this.durationMs = duration;
@@ -65,17 +70,20 @@
         this.durationMs = in.readLong();
     }
 
+    /** @hide */
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(eventType);
         out.writeLong(durationMs);
     }
 
+    /** @hide */
     @Override
     public int describeContents() {
         return 0;
     }
 
+    /** @hide */
     public static final Parcelable.Creator<IpManagerEvent> CREATOR
         = new Parcelable.Creator<IpManagerEvent>() {
         public IpManagerEvent createFromParcel(Parcel in) {
diff --git a/core/java/android/net/metrics/IpReachabilityEvent.java b/core/java/android/net/metrics/IpReachabilityEvent.java
index 715c95e..c736297 100644
--- a/core/java/android/net/metrics/IpReachabilityEvent.java
+++ b/core/java/android/net/metrics/IpReachabilityEvent.java
@@ -16,7 +16,8 @@
 
 package android.net.metrics;
 
-import android.annotation.UnsupportedAppUsage;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -28,7 +29,9 @@
  * a neighbor probe result.
  * {@hide}
  */
-public final class IpReachabilityEvent implements Parcelable {
+@SystemApi
+@TestApi
+public final class IpReachabilityEvent implements IpConnectivityLog.Event {
 
     // Event types.
     /** A probe forced by IpReachabilityMonitor. */
@@ -47,9 +50,9 @@
     // byte 1: unused
     // byte 2: type of event: PROBE, NUD_FAILED, PROVISIONING_LOST
     // byte 3: when byte 2 == PROBE, errno code from RTNetlink or IpReachabilityMonitor.
+    /** @hide */
     public final int eventType;
 
-    @UnsupportedAppUsage
     public IpReachabilityEvent(int eventType) {
         this.eventType = eventType;
     }
@@ -58,16 +61,19 @@
         this.eventType = in.readInt();
     }
 
+    /** @hide */
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(eventType);
     }
 
+    /** @hide */
     @Override
     public int describeContents() {
         return 0;
     }
 
+    /** @hide */
     public static final Parcelable.Creator<IpReachabilityEvent> CREATOR
         = new Parcelable.Creator<IpReachabilityEvent>() {
         public IpReachabilityEvent createFromParcel(Parcel in) {
@@ -79,18 +85,6 @@
         }
     };
 
-    /**
-     * Returns the NUD failure event type code corresponding to the given conditions.
-     */
-    @UnsupportedAppUsage
-    public static int nudFailureEventType(boolean isFromProbe, boolean isProvisioningLost) {
-        if (isFromProbe) {
-            return isProvisioningLost ? PROVISIONING_LOST : NUD_FAILED;
-        } else {
-            return isProvisioningLost ? PROVISIONING_LOST_ORGANIC : NUD_FAILED_ORGANIC;
-        }
-    }
-
     @Override
     public String toString() {
         int hi = eventType & 0xff00;
diff --git a/core/java/android/net/metrics/NetworkEvent.java b/core/java/android/net/metrics/NetworkEvent.java
index cb82fbe..f5b2ff1 100644
--- a/core/java/android/net/metrics/NetworkEvent.java
+++ b/core/java/android/net/metrics/NetworkEvent.java
@@ -17,6 +17,8 @@
 package android.net.metrics;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -29,7 +31,9 @@
 /**
  * {@hide}
  */
-public final class NetworkEvent implements Parcelable {
+@SystemApi
+@TestApi
+public final class NetworkEvent implements IpConnectivityLog.Event {
 
     public static final int NETWORK_CONNECTED            = 1;
     public static final int NETWORK_VALIDATED            = 2;
@@ -46,6 +50,7 @@
 
     public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12;
 
+    /** @hide */
     @IntDef(value = {
             NETWORK_CONNECTED,
             NETWORK_VALIDATED,
@@ -63,7 +68,9 @@
     @Retention(RetentionPolicy.SOURCE)
     public @interface EventType {}
 
+    /** @hide */
     public final @EventType int eventType;
+    /** @hide */
     public final long durationMs;
 
     public NetworkEvent(@EventType int eventType, long durationMs) {
@@ -80,17 +87,20 @@
         durationMs = in.readLong();
     }
 
+    /** @hide */
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeInt(eventType);
         out.writeLong(durationMs);
     }
 
+    /** @hide */
     @Override
     public int describeContents() {
         return 0;
     }
 
+    /** @hide */
     public static final Parcelable.Creator<NetworkEvent> CREATOR
         = new Parcelable.Creator<NetworkEvent>() {
         public NetworkEvent createFromParcel(Parcel in) {
diff --git a/core/java/android/net/metrics/RaEvent.java b/core/java/android/net/metrics/RaEvent.java
index c41881c..d308246 100644
--- a/core/java/android/net/metrics/RaEvent.java
+++ b/core/java/android/net/metrics/RaEvent.java
@@ -24,7 +24,7 @@
  * An event logged when the APF packet socket receives an RA packet.
  * {@hide}
  */
-public final class RaEvent implements Parcelable {
+public final class RaEvent implements IpConnectivityLog.Event {
 
     public static final long NO_LIFETIME = -1L;
 
diff --git a/core/java/android/net/metrics/ValidationProbeEvent.java b/core/java/android/net/metrics/ValidationProbeEvent.java
index 12c2305..42e8aa6 100644
--- a/core/java/android/net/metrics/ValidationProbeEvent.java
+++ b/core/java/android/net/metrics/ValidationProbeEvent.java
@@ -17,6 +17,8 @@
 package android.net.metrics;
 
 import android.annotation.IntDef;
+import android.annotation.SystemApi;
+import android.annotation.TestApi;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.util.SparseArray;
@@ -30,7 +32,9 @@
  * An event recorded by NetworkMonitor when sending a probe for finding captive portals.
  * {@hide}
  */
-public final class ValidationProbeEvent implements Parcelable {
+@SystemApi
+@TestApi
+public final class ValidationProbeEvent implements IpConnectivityLog.Event {
 
     public static final int PROBE_DNS       = 0;
     public static final int PROBE_HTTP      = 1;
@@ -45,20 +49,27 @@
     private static final int FIRST_VALIDATION  = 1 << 8;
     private static final int REVALIDATION      = 2 << 8;
 
+    /** @hide */
     @IntDef(value = {DNS_FAILURE, DNS_SUCCESS})
     @Retention(RetentionPolicy.SOURCE)
     public @interface ReturnCode {}
 
-    public long durationMs;
+    /** @hide */
+    public final long durationMs;
     // probeType byte format (MSB to LSB):
     // byte 0: unused
     // byte 1: unused
     // byte 2: 0 = UNKNOWN, 1 = FIRST_VALIDATION, 2 = REVALIDATION
     // byte 3: PROBE_* constant
-    public int probeType;
-    public @ReturnCode int returnCode;
+    /** @hide */
+    public final int probeType;
+    /** @hide */
+    public final @ReturnCode int returnCode;
 
-    public ValidationProbeEvent() {
+    private ValidationProbeEvent(long durationMs, int probeType, int returnCode) {
+        this.durationMs = durationMs;
+        this.probeType = probeType;
+        this.returnCode = returnCode;
     }
 
     private ValidationProbeEvent(Parcel in) {
@@ -67,6 +78,47 @@
         returnCode = in.readInt();
     }
 
+    /**
+     * Utility to create an instance of {@link ApfProgramEvent}.
+     */
+    public static class Builder {
+        private long mDurationMs;
+        private int mProbeType;
+        private int mReturnCode;
+
+        /**
+         * Set the duration of the probe in milliseconds.
+         */
+        public Builder setDurationMs(long durationMs) {
+            mDurationMs = durationMs;
+            return this;
+        }
+
+        /**
+         * Set the probe type based on whether it was the first validation.
+         */
+        public Builder setProbeType(int probeType, boolean firstValidation) {
+            mProbeType = makeProbeType(probeType, firstValidation);
+            return this;
+        }
+
+        /**
+         * Set the return code of the probe.
+         */
+        public Builder setReturnCode(int returnCode) {
+            mReturnCode = returnCode;
+            return this;
+        }
+
+        /**
+         * Create a new {@link ValidationProbeEvent}.
+         */
+        public ValidationProbeEvent build() {
+            return new ValidationProbeEvent(mDurationMs, mProbeType, mReturnCode);
+        }
+    }
+
+    /** @hide */
     @Override
     public void writeToParcel(Parcel out, int flags) {
         out.writeLong(durationMs);
@@ -74,11 +126,13 @@
         out.writeInt(returnCode);
     }
 
+    /** @hide */
     @Override
     public int describeContents() {
         return 0;
     }
 
+    /** @hide */
     public static final Parcelable.Creator<ValidationProbeEvent> CREATOR
         = new Parcelable.Creator<ValidationProbeEvent>() {
         public ValidationProbeEvent createFromParcel(Parcel in) {
@@ -90,7 +144,7 @@
         }
     };
 
-    public static int makeProbeType(int probeType, boolean firstValidation) {
+    private static int makeProbeType(int probeType, boolean firstValidation) {
         return (probeType & 0xff) | (firstValidation ? FIRST_VALIDATION : REVALIDATION);
     }
 
@@ -98,7 +152,7 @@
         return Decoder.constants.get(probeType & 0xff, "PROBE_???");
     }
 
-    public static String getValidationStage(int probeType) {
+    private static String getValidationStage(int probeType) {
         return Decoder.constants.get(probeType & 0xff00, "UNKNOWN");
     }
 
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index 8301286..bbd76d2 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -10329,6 +10329,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0;
 
         /**
@@ -10337,6 +10339,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1;
 
         /**
@@ -10345,6 +10349,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final int CAPTIVE_PORTAL_MODE_AVOID = 2;
 
         /**
@@ -10354,6 +10360,8 @@
          * The default for this setting is CAPTIVE_PORTAL_MODE_PROMPT.
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String CAPTIVE_PORTAL_MODE = "captive_portal_mode";
 
         /**
@@ -10382,6 +10390,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url";
 
         /**
@@ -10390,6 +10400,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url";
 
         /**
@@ -10398,6 +10410,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url";
 
         /**
@@ -10406,6 +10420,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS =
                 "captive_portal_other_fallback_urls";
 
@@ -10415,6 +10431,8 @@
          * by "@@,@@".
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS =
                 "captive_portal_fallback_probe_specs";
 
@@ -10425,6 +10443,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https";
 
         /**
@@ -10433,6 +10453,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent";
 
         /**
@@ -10442,6 +10464,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD =
                 "data_stall_consecutive_dns_timeout_threshold";
 
@@ -10450,6 +10474,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String DATA_STALL_MIN_EVALUATE_INTERVAL =
                 "data_stall_min_evaluate_interval";
 
@@ -10459,6 +10485,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String DATA_STALL_VALID_DNS_TIME_THRESHOLD =
                 "data_stall_valid_dns_time_threshold";
 
@@ -10468,6 +10496,8 @@
          *
          * @hide
          */
+        @SystemApi
+        @TestApi
         public static final String DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type";
 
         /**
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index 31e799d..0985ac3 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -378,8 +378,8 @@
                    Values must be from NetworkCapabilities#NET_CAPABILITIES_* constants.
                [IP config] Optional. If empty or not specified - DHCP will be used, otherwise
                    use the following format to specify static IP configuration:
-		       ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
-                       domains=<comma-sep-domains> 
+                       ip=<ip-address/mask> gateway=<ip-address> dns=<comma-sep-ip-addresses>
+                       domains=<comma-sep-domains>
          -->
     <string-array translatable="false" name="config_ethernet_interfaces">
         <!--
diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
index 057012d..cca71e7 100644
--- a/packages/NetworkStack/src/com/android/server/NetworkStackService.java
+++ b/packages/NetworkStack/src/com/android/server/NetworkStackService.java
@@ -20,6 +20,7 @@
 import static android.net.dhcp.IDhcpServer.STATUS_SUCCESS;
 import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR;
 
+import static com.android.server.util.PermissionUtil.checkDumpPermission;
 import static com.android.server.util.PermissionUtil.checkNetworkStackCallingPermission;
 
 import android.annotation.NonNull;
@@ -139,7 +140,7 @@
         @Override
         protected void dump(@NonNull FileDescriptor fd, @NonNull PrintWriter fout,
                 @Nullable String[] args) {
-            checkNetworkStackCallingPermission();
+            checkDumpPermission();
             final IndentingPrintWriter pw = new IndentingPrintWriter(fout, "  ");
             pw.println("NetworkStack logs:");
             mLog.dump(fd, pw, args);
diff --git a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
index 4077d93..c8a8e1f 100644
--- a/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
+++ b/packages/NetworkStack/src/com/android/server/connectivity/NetworkMonitor.java
@@ -1598,10 +1598,11 @@
     private void logValidationProbe(long durationMs, int probeType, int probeResult) {
         int[] transports = mNetworkCapabilities.getTransportTypes();
         boolean isFirstValidation = validationStage().mIsFirstValidation;
-        ValidationProbeEvent ev = new ValidationProbeEvent();
-        ev.probeType = ValidationProbeEvent.makeProbeType(probeType, isFirstValidation);
-        ev.returnCode = probeResult;
-        ev.durationMs = durationMs;
+        ValidationProbeEvent ev = new ValidationProbeEvent.Builder()
+                .setProbeType(probeType, isFirstValidation)
+                .setReturnCode(probeResult)
+                .setDurationMs(durationMs)
+                .build();
         mMetricsLog.log(mNetId, transports, ev);
     }
 
diff --git a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
index 733f873..82bf038 100644
--- a/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
+++ b/packages/NetworkStack/src/com/android/server/util/PermissionUtil.java
@@ -31,8 +31,21 @@
      */
     public static void checkNetworkStackCallingPermission() {
         // TODO: check that the calling PID is the system server.
-        if (getCallingUid() != Process.SYSTEM_UID && getCallingUid() != Process.ROOT_UID) {
-            throw new SecurityException("Invalid caller: " + getCallingUid());
+        final int caller = getCallingUid();
+        if (caller != Process.SYSTEM_UID && caller != Process.BLUETOOTH_UID) {
+            throw new SecurityException("Invalid caller: " + caller);
+        }
+    }
+
+    /**
+     * Check that the caller is allowed to dump the network stack, e.g. dumpsys.
+     * @throws SecurityException The caller is not allowed to dump the network stack.
+     */
+    public static void checkDumpPermission() {
+        final int caller = getCallingUid();
+        if (caller != Process.SYSTEM_UID && caller != Process.ROOT_UID
+                && caller != Process.SHELL_UID) {
+            throw new SecurityException("No dump permissions for caller: " + caller);
         }
     }
 
diff --git a/services/core/java/com/android/server/NetworkManagementService.java b/services/core/java/com/android/server/NetworkManagementService.java
index 600a6ae..5ef3fe49 100644
--- a/services/core/java/com/android/server/NetworkManagementService.java
+++ b/services/core/java/com/android/server/NetworkManagementService.java
@@ -39,14 +39,6 @@
 import static android.net.NetworkStats.UID_ALL;
 import static android.net.TrafficStats.UID_TETHERING;
 
-import static com.android.server.NetworkManagementService.NetdResponseCode.ClatdStatusResult;
-import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceGetCfgResult;
-import static com.android.server.NetworkManagementService.NetdResponseCode.InterfaceListResult;
-import static com.android.server.NetworkManagementService.NetdResponseCode.IpFwdStatusResult;
-import static com.android.server.NetworkManagementService.NetdResponseCode.TetherDnsFwdTgtListResult;
-import static com.android.server.NetworkManagementService.NetdResponseCode.TetherInterfaceListResult;
-import static com.android.server.NetworkManagementService.NetdResponseCode.TetherStatusResult;
-import static com.android.server.NetworkManagementService.NetdResponseCode.TetheringStatsListResult;
 import static com.android.server.NetworkManagementService.NetdResponseCode.TtyListResult;
 import static com.android.server.NetworkManagementSocketTagger.PROP_QTAGUID_ENABLED;
 
diff --git a/services/net/java/android/net/apf/ApfFilter.java b/services/net/java/android/net/apf/ApfFilter.java
index f037905..3351b25 100644
--- a/services/net/java/android/net/apf/ApfFilter.java
+++ b/services/net/java/android/net/apf/ApfFilter.java
@@ -16,8 +16,19 @@
 
 package android.net.apf;
 
-import static android.net.util.NetworkConstants.*;
-import static android.system.OsConstants.*;
+import static android.net.util.NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
+import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
+import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static android.net.util.NetworkConstants.ICMPV6_ROUTER_SOLICITATION;
+import static android.system.OsConstants.AF_PACKET;
+import static android.system.OsConstants.ARPHRD_ETHER;
+import static android.system.OsConstants.ETH_P_ARP;
+import static android.system.OsConstants.ETH_P_IP;
+import static android.system.OsConstants.ETH_P_IPV6;
+import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_RAW;
+
 import static com.android.internal.util.BitUtils.bytesToBEInt;
 import static com.android.internal.util.BitUtils.getUint16;
 import static com.android.internal.util.BitUtils.getUint32;
@@ -34,7 +45,7 @@
 import android.net.NetworkUtils;
 import android.net.apf.ApfGenerator.IllegalInstructionException;
 import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpClient;
+import android.net.ip.IpClientCallbacks;
 import android.net.metrics.ApfProgramEvent;
 import android.net.metrics.ApfStats;
 import android.net.metrics.IpConnectivityLog;
@@ -48,10 +59,14 @@
 import android.text.format.DateUtils;
 import android.util.Log;
 import android.util.Pair;
+
 import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.HexDump;
 import com.android.internal.util.IndentingPrintWriter;
+
+import libcore.io.IoBridge;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
 import java.net.Inet4Address;
@@ -63,7 +78,6 @@
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.Arrays;
-import libcore.io.IoBridge;
 
 /**
  * For networks that support packet filtering via APF programs, {@code ApfFilter}
@@ -174,7 +188,13 @@
         private final byte[] mPacket = new byte[1514];
         private final FileDescriptor mSocket;
         private final long mStart = SystemClock.elapsedRealtime();
-        private final ApfStats mStats = new ApfStats();
+
+        private int mReceivedRas = 0;
+        private int mMatchingRas = 0;
+        private int mDroppedRas = 0;
+        private int mParseErrors = 0;
+        private int mZeroLifetimeRas = 0;
+        private int mProgramUpdates = 0;
 
         private volatile boolean mStopped;
 
@@ -207,26 +227,26 @@
         }
 
         private void updateStats(ProcessRaResult result) {
-            mStats.receivedRas++;
+            mReceivedRas++;
             switch(result) {
                 case MATCH:
-                    mStats.matchingRas++;
+                    mMatchingRas++;
                     return;
                 case DROPPED:
-                    mStats.droppedRas++;
+                    mDroppedRas++;
                     return;
                 case PARSE_ERROR:
-                    mStats.parseErrors++;
+                    mParseErrors++;
                     return;
                 case ZERO_LIFETIME:
-                    mStats.zeroLifetimeRas++;
+                    mZeroLifetimeRas++;
                     return;
                 case UPDATE_EXPIRY:
-                    mStats.matchingRas++;
-                    mStats.programUpdates++;
+                    mMatchingRas++;
+                    mProgramUpdates++;
                     return;
                 case UPDATE_NEW_RA:
-                    mStats.programUpdates++;
+                    mProgramUpdates++;
                     return;
             }
         }
@@ -234,11 +254,19 @@
         private void logStats() {
             final long nowMs = SystemClock.elapsedRealtime();
             synchronized (this) {
-                mStats.durationMs = nowMs - mStart;
-                mStats.maxProgramSize = mApfCapabilities.maximumApfProgramSize;
-                mStats.programUpdatesAll = mNumProgramUpdates;
-                mStats.programUpdatesAllowingMulticast = mNumProgramUpdatesAllowingMulticast;
-                mMetricsLog.log(mStats);
+                final ApfStats stats = new ApfStats.Builder()
+                        .setReceivedRas(mReceivedRas)
+                        .setMatchingRas(mMatchingRas)
+                        .setDroppedRas(mDroppedRas)
+                        .setParseErrors(mParseErrors)
+                        .setZeroLifetimeRas(mZeroLifetimeRas)
+                        .setProgramUpdates(mProgramUpdates)
+                        .setDurationMs(nowMs - mStart)
+                        .setMaxProgramSize(mApfCapabilities.maximumApfProgramSize)
+                        .setProgramUpdatesAll(mNumProgramUpdates)
+                        .setProgramUpdatesAllowingMulticast(mNumProgramUpdatesAllowingMulticast)
+                        .build();
+                mMetricsLog.log(stats);
                 logApfProgramEventLocked(nowMs / DateUtils.SECOND_IN_MILLIS);
             }
         }
@@ -308,7 +336,7 @@
     private static final int APF_MAX_ETH_TYPE_BLACK_LIST_LEN = 20;
 
     private final ApfCapabilities mApfCapabilities;
-    private final IpClient.Callback mIpClientCallback;
+    private final IpClientCallbacks mIpClientCallback;
     private final InterfaceParams mInterfaceParams;
     private final IpConnectivityLog mMetricsLog;
 
@@ -349,7 +377,7 @@
 
     @VisibleForTesting
     ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams,
-            IpClient.Callback ipClientCallback, IpConnectivityLog log) {
+            IpClientCallbacks ipClientCallback, IpConnectivityLog log) {
         mApfCapabilities = config.apfCapabilities;
         mIpClientCallback = ipClientCallback;
         mInterfaceParams = ifParams;
@@ -849,7 +877,7 @@
     @GuardedBy("this")
     private long mLastInstalledProgramMinLifetime;
     @GuardedBy("this")
-    private ApfProgramEvent mLastInstallEvent;
+    private ApfProgramEvent.Builder mLastInstallEvent;
 
     // For debugging only. The last program installed.
     @GuardedBy("this")
@@ -1281,12 +1309,12 @@
         }
         mIpClientCallback.installPacketFilter(program);
         logApfProgramEventLocked(now);
-        mLastInstallEvent = new ApfProgramEvent();
-        mLastInstallEvent.lifetime = programMinLifetime;
-        mLastInstallEvent.filteredRas = rasToFilter.size();
-        mLastInstallEvent.currentRas = mRas.size();
-        mLastInstallEvent.programLength = program.length;
-        mLastInstallEvent.flags = ApfProgramEvent.flagsFor(mIPv4Address != null, mMulticastFilter);
+        mLastInstallEvent = new ApfProgramEvent.Builder()
+                .setLifetime(programMinLifetime)
+                .setFilteredRas(rasToFilter.size())
+                .setCurrentRas(mRas.size())
+                .setProgramLength(program.length)
+                .setFlags(mIPv4Address != null, mMulticastFilter);
     }
 
     @GuardedBy("this")
@@ -1294,13 +1322,14 @@
         if (mLastInstallEvent == null) {
             return;
         }
-        ApfProgramEvent ev = mLastInstallEvent;
+        ApfProgramEvent.Builder ev = mLastInstallEvent;
         mLastInstallEvent = null;
-        ev.actualLifetime = now - mLastTimeInstalledProgram;
-        if (ev.actualLifetime < APF_PROGRAM_EVENT_LIFETIME_THRESHOLD) {
+        final long actualLifetime = now - mLastTimeInstalledProgram;
+        ev.setActualLifetime(actualLifetime);
+        if (actualLifetime < APF_PROGRAM_EVENT_LIFETIME_THRESHOLD) {
             return;
         }
-        mMetricsLog.log(ev);
+        mMetricsLog.log(ev.build());
     }
 
     /**
@@ -1390,7 +1419,7 @@
      * filtering using APF programs.
      */
     public static ApfFilter maybeCreate(Context context, ApfConfiguration config,
-            InterfaceParams ifParams, IpClient.Callback ipClientCallback) {
+            InterfaceParams ifParams, IpClientCallbacks ipClientCallback) {
         if (context == null || config == null || ifParams == null) return null;
         ApfCapabilities apfCapabilities =  config.apfCapabilities;
         if (apfCapabilities == null) return null;
diff --git a/services/net/java/android/net/dhcp/DhcpClient.java b/services/net/java/android/net/dhcp/DhcpClient.java
index a956cef..15acc0e 100644
--- a/services/net/java/android/net/dhcp/DhcpClient.java
+++ b/services/net/java/android/net/dhcp/DhcpClient.java
@@ -16,28 +16,39 @@
 
 package android.net.dhcp;
 
-import com.android.internal.util.HexDump;
-import com.android.internal.util.Protocol;
-import com.android.internal.util.State;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.StateMachine;
-import com.android.internal.util.WakeupMessage;
+import static android.net.dhcp.DhcpPacket.DHCP_BROADCAST_ADDRESS;
+import static android.net.dhcp.DhcpPacket.DHCP_DNS_SERVER;
+import static android.net.dhcp.DhcpPacket.DHCP_DOMAIN_NAME;
+import static android.net.dhcp.DhcpPacket.DHCP_LEASE_TIME;
+import static android.net.dhcp.DhcpPacket.DHCP_MTU;
+import static android.net.dhcp.DhcpPacket.DHCP_REBINDING_TIME;
+import static android.net.dhcp.DhcpPacket.DHCP_RENEWAL_TIME;
+import static android.net.dhcp.DhcpPacket.DHCP_ROUTER;
+import static android.net.dhcp.DhcpPacket.DHCP_SUBNET_MASK;
+import static android.net.dhcp.DhcpPacket.DHCP_VENDOR_INFO;
+import static android.net.dhcp.DhcpPacket.INADDR_ANY;
+import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST;
+import static android.system.OsConstants.AF_INET;
+import static android.system.OsConstants.AF_PACKET;
+import static android.system.OsConstants.ETH_P_IP;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_DGRAM;
+import static android.system.OsConstants.SOCK_RAW;
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_BINDTODEVICE;
+import static android.system.OsConstants.SO_BROADCAST;
+import static android.system.OsConstants.SO_RCVBUF;
+import static android.system.OsConstants.SO_REUSEADDR;
 
 import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
 import android.net.DhcpResults;
-import android.net.InterfaceConfiguration;
-import android.net.LinkAddress;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
-import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.DhcpClientEvent;
 import android.net.metrics.DhcpErrorEvent;
+import android.net.metrics.IpConnectivityLog;
 import android.net.util.InterfaceParams;
 import android.os.Message;
-import android.os.RemoteException;
-import android.os.ServiceManager;
 import android.os.SystemClock;
 import android.system.ErrnoException;
 import android.system.Os;
@@ -47,20 +58,23 @@
 import android.util.SparseArray;
 import android.util.TimeUtils;
 
+import com.android.internal.util.HexDump;
+import com.android.internal.util.MessageUtils;
+import com.android.internal.util.Protocol;
+import com.android.internal.util.State;
+import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
+
+import libcore.io.IoBridge;
+
 import java.io.FileDescriptor;
 import java.io.IOException;
-import java.lang.Thread;
 import java.net.Inet4Address;
 import java.net.SocketException;
 import java.nio.ByteBuffer;
 import java.util.Arrays;
 import java.util.Random;
 
-import libcore.io.IoBridge;
-
-import static android.system.OsConstants.*;
-import static android.net.dhcp.DhcpPacket.*;
-
 /**
  * A DHCPv4 client.
  *
@@ -1029,6 +1043,10 @@
     }
 
     private void logState(String name, int durationMs) {
-        mMetricsLog.log(mIfaceName, new DhcpClientEvent(name, durationMs));
+        final DhcpClientEvent event = new DhcpClientEvent.Builder()
+                .setMsg(name)
+                .setDurationMs(durationMs)
+                .build();
+        mMetricsLog.log(mIfaceName, event);
     }
 }
diff --git a/services/net/java/android/net/ip/ConnectivityPacketTracker.java b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
index e6ddbbc..bef425a 100644
--- a/services/net/java/android/net/ip/ConnectivityPacketTracker.java
+++ b/services/net/java/android/net/ip/ConnectivityPacketTracker.java
@@ -16,27 +16,27 @@
 
 package android.net.ip;
 
-import static android.system.OsConstants.*;
+import static android.system.OsConstants.AF_PACKET;
+import static android.system.OsConstants.ARPHRD_ETHER;
+import static android.system.OsConstants.ETH_P_ALL;
+import static android.system.OsConstants.SOCK_RAW;
 
 import android.net.NetworkUtils;
-import android.net.util.PacketReader;
 import android.net.util.ConnectivityPacketSummary;
 import android.net.util.InterfaceParams;
+import android.net.util.PacketReader;
 import android.os.Handler;
 import android.system.ErrnoException;
 import android.system.Os;
 import android.system.PacketSocketAddress;
 import android.text.TextUtils;
-import android.util.Log;
 import android.util.LocalLog;
+import android.util.Log;
 
-import libcore.io.IoBridge;
 import libcore.util.HexEncoding;
 
 import java.io.FileDescriptor;
-import java.io.InterruptedIOException;
 import java.io.IOException;
-import java.net.SocketException;
 
 
 /**
diff --git a/services/net/java/android/net/ip/InterfaceController.java b/services/net/java/android/net/ip/InterfaceController.java
index 02e4f87..55dfcef 100644
--- a/services/net/java/android/net/ip/InterfaceController.java
+++ b/services/net/java/android/net/ip/InterfaceController.java
@@ -19,7 +19,6 @@
 import android.net.INetd;
 import android.net.InterfaceConfiguration;
 import android.net.LinkAddress;
-import android.net.util.NetdService;
 import android.net.util.SharedLog;
 import android.os.INetworkManagementService;
 import android.os.RemoteException;
diff --git a/services/net/java/android/net/ip/IpClient.java b/services/net/java/android/net/ip/IpClient.java
index 0176dd4..9f15573 100644
--- a/services/net/java/android/net/ip/IpClient.java
+++ b/services/net/java/android/net/ip/IpClient.java
@@ -16,19 +16,19 @@
 
 package android.net.ip;
 
-import com.android.internal.util.HexDump;
-import com.android.internal.util.MessageUtils;
-import com.android.internal.util.WakeupMessage;
+import static android.net.shared.LinkPropertiesParcelableUtil.fromStableParcelable;
 
 import android.content.Context;
 import android.net.DhcpResults;
 import android.net.INetd;
 import android.net.IpPrefix;
 import android.net.LinkAddress;
-import android.net.LinkProperties.ProvisioningChange;
 import android.net.LinkProperties;
+import android.net.LinkProperties.ProvisioningChange;
 import android.net.Network;
+import android.net.ProvisioningConfigurationParcelable;
 import android.net.ProxyInfo;
+import android.net.ProxyInfoParcelable;
 import android.net.RouteInfo;
 import android.net.StaticIpConfiguration;
 import android.net.apf.ApfCapabilities;
@@ -36,10 +36,10 @@
 import android.net.dhcp.DhcpClient;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.IpManagerEvent;
+import android.net.shared.InitialConfiguration;
 import android.net.util.InterfaceParams;
 import android.net.util.MultinetworkPolicyTracker;
 import android.net.util.NetdService;
-import android.net.util.NetworkConstants;
 import android.net.util.SharedLog;
 import android.os.ConditionVariable;
 import android.os.INetworkManagementService;
@@ -52,29 +52,24 @@
 import android.util.Log;
 import android.util.SparseArray;
 
-import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.R;
+import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
-import com.android.internal.util.IndentingPrintWriter;
 import com.android.internal.util.IState;
+import com.android.internal.util.IndentingPrintWriter;
+import com.android.internal.util.MessageUtils;
 import com.android.internal.util.Preconditions;
 import com.android.internal.util.State;
 import com.android.internal.util.StateMachine;
+import com.android.internal.util.WakeupMessage;
 import com.android.server.net.NetlinkTracker;
 
 import java.io.FileDescriptor;
 import java.io.PrintWriter;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.SocketException;
-import java.util.ArrayList;
 import java.util.Collection;
-import java.util.HashSet;
-import java.util.Objects;
 import java.util.List;
-import java.util.Set;
-import java.util.StringJoiner;
+import java.util.Objects;
 import java.util.concurrent.ConcurrentHashMap;
 import java.util.concurrent.CountDownLatch;
 import java.util.function.Predicate;
@@ -134,83 +129,17 @@
     }
 
     /**
-     * Callbacks for handling IpClient events.
-     *
-     * These methods are called by IpClient on its own thread. Implementations
-     * of this class MUST NOT carry out long-running computations or hold locks
-     * for which there might be contention with other code calling public
-     * methods of the same IpClient instance.
+     * TODO: remove after migrating clients to use IpClientCallbacks directly
+     * @see IpClientCallbacks
      */
-    public static class Callback {
-        // In order to receive onPreDhcpAction(), call #withPreDhcpAction()
-        // when constructing a ProvisioningConfiguration.
-        //
-        // Implementations of onPreDhcpAction() must call
-        // IpClient#completedPreDhcpAction() to indicate that DHCP is clear
-        // to proceed.
-        public void onPreDhcpAction() {}
-        public void onPostDhcpAction() {}
+    public static class Callback extends IpClientCallbacks {}
 
-        // This is purely advisory and not an indication of provisioning
-        // success or failure.  This is only here for callers that want to
-        // expose DHCPv4 results to other APIs (e.g., WifiInfo#setInetAddress).
-        // DHCPv4 or static IPv4 configuration failure or success can be
-        // determined by whether or not the passed-in DhcpResults object is
-        // null or not.
-        public void onNewDhcpResults(DhcpResults dhcpResults) {}
-
-        public void onProvisioningSuccess(LinkProperties newLp) {}
-        public void onProvisioningFailure(LinkProperties newLp) {}
-
-        // Invoked on LinkProperties changes.
-        public void onLinkPropertiesChange(LinkProperties newLp) {}
-
-        // Called when the internal IpReachabilityMonitor (if enabled) has
-        // detected the loss of a critical number of required neighbors.
-        public void onReachabilityLost(String logMsg) {}
-
-        // Called when the IpClient state machine terminates.
-        public void onQuit() {}
-
-        // Install an APF program to filter incoming packets.
-        public void installPacketFilter(byte[] filter) {}
-
-        // Asynchronously read back the APF program & data buffer from the wifi driver.
-        // Due to Wifi HAL limitations, the current implementation only supports dumping the entire
-        // buffer. In response to this request, the driver returns the data buffer asynchronously
-        // by sending an IpClient#EVENT_READ_PACKET_FILTER_COMPLETE message.
-        public void startReadPacketFilter() {}
-
-        // If multicast filtering cannot be accomplished with APF, this function will be called to
-        // actuate multicast filtering using another means.
-        public void setFallbackMulticastFilter(boolean enabled) {}
-
-        // Enabled/disable Neighbor Discover offload functionality. This is
-        // called, for example, whenever 464xlat is being started or stopped.
-        public void setNeighborDiscoveryOffload(boolean enable) {}
-    }
-
-    public static class WaitForProvisioningCallback extends Callback {
-        private final ConditionVariable mCV = new ConditionVariable();
-        private LinkProperties mCallbackLinkProperties;
-
-        public LinkProperties waitForProvisioning() {
-            mCV.block();
-            return mCallbackLinkProperties;
-        }
-
-        @Override
-        public void onProvisioningSuccess(LinkProperties newLp) {
-            mCallbackLinkProperties = newLp;
-            mCV.open();
-        }
-
-        @Override
-        public void onProvisioningFailure(LinkProperties newLp) {
-            mCallbackLinkProperties = null;
-            mCV.open();
-        }
-    }
+    /**
+     * TODO: remove once clients are migrated to IpClientUtil.WaitForProvisioningCallback
+     * @see IpClientUtil.WaitForProvisioningCallbacks
+     */
+    public static class WaitForProvisioningCallback
+            extends IpClientUtil.WaitForProvisioningCallbacks {}
 
     // Use a wrapper class to log in order to ensure complete and detailed
     // logging. This method is lighter weight than annotations/reflection
@@ -232,12 +161,12 @@
     // once passed on to the callback they may be modified by another thread.
     //
     // TODO: Find an lighter weight approach.
-    private class LoggingCallbackWrapper extends Callback {
+    private class LoggingCallbackWrapper extends IpClientCallbacks {
         private static final String PREFIX = "INVOKE ";
-        private final Callback mCallback;
+        private final IpClientCallbacks mCallback;
 
-        public LoggingCallbackWrapper(Callback callback) {
-            mCallback = (callback != null) ? callback : new Callback();
+        LoggingCallbackWrapper(IpClientCallbacks callback) {
+            mCallback = (callback != null) ? callback : new IpClientCallbacks();
         }
 
         private void log(String msg) {
@@ -307,288 +236,102 @@
     }
 
     /**
-     * This class encapsulates parameters to be passed to
-     * IpClient#startProvisioning(). A defensive copy is made by IpClient
-     * and the values specified herein are in force until IpClient#stop()
-     * is called.
-     *
-     * Example use:
-     *
-     *     final ProvisioningConfiguration config =
-     *             mIpClient.buildProvisioningConfiguration()
-     *                     .withPreDhcpAction()
-     *                     .withProvisioningTimeoutMs(36 * 1000)
-     *                     .build();
-     *     mIpClient.startProvisioning(config);
-     *     ...
-     *     mIpClient.stop();
-     *
-     * The specified provisioning configuration will only be active until
-     * IpClient#stop() is called. Future calls to IpClient#startProvisioning()
-     * must specify the configuration again.
+     * TODO: remove after migrating clients to use the shared configuration class directly.
+     * @see android.net.shared.ProvisioningConfiguration
      */
-    public static class ProvisioningConfiguration {
-        // TODO: Delete this default timeout once those callers that care are
-        // fixed to pass in their preferred timeout.
-        //
-        // We pick 36 seconds so we can send DHCP requests at
-        //
-        //     t=0, t=2, t=6, t=14, t=30
-        //
-        // allowing for 10% jitter.
-        private static final int DEFAULT_TIMEOUT_MS = 36 * 1000;
-
-        public static class Builder {
-            private ProvisioningConfiguration mConfig = new ProvisioningConfiguration();
-
-            public Builder withoutIPv4() {
-                mConfig.mEnableIPv4 = false;
-                return this;
-            }
-
-            public Builder withoutIPv6() {
-                mConfig.mEnableIPv6 = false;
-                return this;
-            }
-
-            public Builder withoutMultinetworkPolicyTracker() {
-                mConfig.mUsingMultinetworkPolicyTracker = false;
-                return this;
-            }
-
-            public Builder withoutIpReachabilityMonitor() {
-                mConfig.mUsingIpReachabilityMonitor = false;
-                return this;
-            }
-
-            public Builder withPreDhcpAction() {
-                mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS;
-                return this;
-            }
-
-            public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
-                mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs;
-                return this;
-            }
-
-            public Builder withInitialConfiguration(InitialConfiguration initialConfig) {
-                mConfig.mInitialConfig = initialConfig;
-                return this;
-            }
-
-            public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
-                mConfig.mStaticIpConfig = staticConfig;
-                return this;
-            }
-
-            public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
-                mConfig.mApfCapabilities = apfCapabilities;
-                return this;
-            }
-
-            public Builder withProvisioningTimeoutMs(int timeoutMs) {
-                mConfig.mProvisioningTimeoutMs = timeoutMs;
-                return this;
-            }
-
-            public Builder withRandomMacAddress() {
-                mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64;
-                return this;
-            }
-
-            public Builder withStableMacAddress() {
-                mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
-                return this;
-            }
-
-            public Builder withNetwork(Network network) {
-                mConfig.mNetwork = network;
-                return this;
-            }
-
-            public Builder withDisplayName(String displayName) {
-                mConfig.mDisplayName = displayName;
-                return this;
-            }
-
-            public ProvisioningConfiguration build() {
-                return new ProvisioningConfiguration(mConfig);
-            }
-        }
-
-        /* package */ boolean mEnableIPv4 = true;
-        /* package */ boolean mEnableIPv6 = true;
-        /* package */ boolean mUsingMultinetworkPolicyTracker = true;
-        /* package */ boolean mUsingIpReachabilityMonitor = true;
-        /* package */ int mRequestedPreDhcpActionMs;
-        /* package */ InitialConfiguration mInitialConfig;
-        /* package */ StaticIpConfiguration mStaticIpConfig;
-        /* package */ ApfCapabilities mApfCapabilities;
-        /* package */ int mProvisioningTimeoutMs = DEFAULT_TIMEOUT_MS;
-        /* package */ int mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY;
-        /* package */ Network mNetwork = null;
-        /* package */ String mDisplayName = null;
-
-        public ProvisioningConfiguration() {} // used by Builder
-
-        public ProvisioningConfiguration(ProvisioningConfiguration other) {
-            mEnableIPv4 = other.mEnableIPv4;
-            mEnableIPv6 = other.mEnableIPv6;
-            mUsingMultinetworkPolicyTracker = other.mUsingMultinetworkPolicyTracker;
-            mUsingIpReachabilityMonitor = other.mUsingIpReachabilityMonitor;
-            mRequestedPreDhcpActionMs = other.mRequestedPreDhcpActionMs;
-            mInitialConfig = InitialConfiguration.copy(other.mInitialConfig);
-            mStaticIpConfig = other.mStaticIpConfig;
-            mApfCapabilities = other.mApfCapabilities;
-            mProvisioningTimeoutMs = other.mProvisioningTimeoutMs;
-            mIPv6AddrGenMode = other.mIPv6AddrGenMode;
-            mNetwork = other.mNetwork;
-            mDisplayName = other.mDisplayName;
-        }
-
-        @Override
-        public String toString() {
-            return new StringJoiner(", ", getClass().getSimpleName() + "{", "}")
-                    .add("mEnableIPv4: " + mEnableIPv4)
-                    .add("mEnableIPv6: " + mEnableIPv6)
-                    .add("mUsingMultinetworkPolicyTracker: " + mUsingMultinetworkPolicyTracker)
-                    .add("mUsingIpReachabilityMonitor: " + mUsingIpReachabilityMonitor)
-                    .add("mRequestedPreDhcpActionMs: " + mRequestedPreDhcpActionMs)
-                    .add("mInitialConfig: " + mInitialConfig)
-                    .add("mStaticIpConfig: " + mStaticIpConfig)
-                    .add("mApfCapabilities: " + mApfCapabilities)
-                    .add("mProvisioningTimeoutMs: " + mProvisioningTimeoutMs)
-                    .add("mIPv6AddrGenMode: " + mIPv6AddrGenMode)
-                    .add("mNetwork: " + mNetwork)
-                    .add("mDisplayName: " + mDisplayName)
-                    .toString();
-        }
-
-        public boolean isValid() {
-            return (mInitialConfig == null) || mInitialConfig.isValid();
-        }
-    }
-
-    public static class InitialConfiguration {
-        public final Set<LinkAddress> ipAddresses = new HashSet<>();
-        public final Set<IpPrefix> directlyConnectedRoutes = new HashSet<>();
-        public final Set<InetAddress> dnsServers = new HashSet<>();
-        public Inet4Address gateway; // WiFi legacy behavior with static ipv4 config
-
-        public static InitialConfiguration copy(InitialConfiguration config) {
-            if (config == null) {
-                return null;
-            }
-            InitialConfiguration configCopy = new InitialConfiguration();
-            configCopy.ipAddresses.addAll(config.ipAddresses);
-            configCopy.directlyConnectedRoutes.addAll(config.directlyConnectedRoutes);
-            configCopy.dnsServers.addAll(config.dnsServers);
-            return configCopy;
-        }
-
-        @Override
-        public String toString() {
-            return String.format(
-                    "InitialConfiguration(IPs: {%s}, prefixes: {%s}, DNS: {%s}, v4 gateway: %s)",
-                    join(", ", ipAddresses), join(", ", directlyConnectedRoutes),
-                    join(", ", dnsServers), gateway);
-        }
-
-        public boolean isValid() {
-            if (ipAddresses.isEmpty()) {
-                return false;
-            }
-
-            // For every IP address, there must be at least one prefix containing that address.
-            for (LinkAddress addr : ipAddresses) {
-                if (!any(directlyConnectedRoutes, (p) -> p.contains(addr.getAddress()))) {
-                    return false;
-                }
-            }
-            // For every dns server, there must be at least one prefix containing that address.
-            for (InetAddress addr : dnsServers) {
-                if (!any(directlyConnectedRoutes, (p) -> p.contains(addr))) {
-                    return false;
-                }
-            }
-            // All IPv6 LinkAddresses have an RFC7421-suitable prefix length
-            // (read: compliant with RFC4291#section2.5.4).
-            if (any(ipAddresses, not(InitialConfiguration::isPrefixLengthCompliant))) {
-                return false;
-            }
-            // If directlyConnectedRoutes contains an IPv6 default route
-            // then ipAddresses MUST contain at least one non-ULA GUA.
-            if (any(directlyConnectedRoutes, InitialConfiguration::isIPv6DefaultRoute)
-                    && all(ipAddresses, not(InitialConfiguration::isIPv6GUA))) {
-                return false;
-            }
-            // The prefix length of routes in directlyConnectedRoutes be within reasonable
-            // bounds for IPv6: /48-/64 just as we’d accept in RIOs.
-            if (any(directlyConnectedRoutes, not(InitialConfiguration::isPrefixLengthCompliant))) {
-                return false;
-            }
-            // There no more than one IPv4 address
-            if (ipAddresses.stream().filter(LinkAddress::isIPv4).count() > 1) {
-                return false;
-            }
-
-            return true;
+    public static class ProvisioningConfiguration
+            extends android.net.shared.ProvisioningConfiguration {
+        public ProvisioningConfiguration(android.net.shared.ProvisioningConfiguration other) {
+            super(other);
         }
 
         /**
-         * @return true if the given list of addressess and routes satisfies provisioning for this
-         * InitialConfiguration. LinkAddresses and RouteInfo objects are not compared with equality
-         * because addresses and routes seen by Netlink will contain additional fields like flags,
-         * interfaces, and so on. If this InitialConfiguration has no IP address specified, the
-         * provisioning check always fails.
-         *
-         * If the given list of routes is null, only addresses are taken into considerations.
+         * @see android.net.shared.ProvisioningConfiguration.Builder
          */
-        public boolean isProvisionedBy(List<LinkAddress> addresses, List<RouteInfo> routes) {
-            if (ipAddresses.isEmpty()) {
-                return false;
+        public static class Builder extends android.net.shared.ProvisioningConfiguration.Builder {
+            // Override all methods to have a return type matching this Builder
+            @Override
+            public Builder withoutIPv4() {
+                super.withoutIPv4();
+                return this;
             }
 
-            for (LinkAddress addr : ipAddresses) {
-                if (!any(addresses, (addrSeen) -> addr.isSameAddressAs(addrSeen))) {
-                    return false;
-                }
+            @Override
+            public Builder withoutIPv6() {
+                super.withoutIPv6();
+                return this;
             }
 
-            if (routes != null) {
-                for (IpPrefix prefix : directlyConnectedRoutes) {
-                    if (!any(routes, (routeSeen) -> isDirectlyConnectedRoute(routeSeen, prefix))) {
-                        return false;
-                    }
-                }
+            @Override
+            public Builder withoutMultinetworkPolicyTracker() {
+                super.withoutMultinetworkPolicyTracker();
+                return this;
             }
 
-            return true;
-        }
+            @Override
+            public Builder withoutIpReachabilityMonitor() {
+                super.withoutIpReachabilityMonitor();
+                return this;
+            }
 
-        private static boolean isDirectlyConnectedRoute(RouteInfo route, IpPrefix prefix) {
-            return !route.hasGateway() && prefix.equals(route.getDestination());
-        }
+            @Override
+            public Builder withPreDhcpAction() {
+                super.withPreDhcpAction();
+                return this;
+            }
 
-        private static boolean isPrefixLengthCompliant(LinkAddress addr) {
-            return addr.isIPv4() || isCompliantIPv6PrefixLength(addr.getPrefixLength());
-        }
+            @Override
+            public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
+                super.withPreDhcpAction(dhcpActionTimeoutMs);
+                return this;
+            }
 
-        private static boolean isPrefixLengthCompliant(IpPrefix prefix) {
-            return prefix.isIPv4() || isCompliantIPv6PrefixLength(prefix.getPrefixLength());
-        }
+            @Override
+            public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
+                super.withStaticConfiguration(staticConfig);
+                return this;
+            }
 
-        private static boolean isCompliantIPv6PrefixLength(int prefixLength) {
-            return (NetworkConstants.RFC6177_MIN_PREFIX_LENGTH <= prefixLength)
-                    && (prefixLength <= NetworkConstants.RFC7421_PREFIX_LENGTH);
-        }
+            @Override
+            public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
+                super.withApfCapabilities(apfCapabilities);
+                return this;
+            }
 
-        private static boolean isIPv6DefaultRoute(IpPrefix prefix) {
-            return prefix.getAddress().equals(Inet6Address.ANY);
-        }
+            @Override
+            public Builder withProvisioningTimeoutMs(int timeoutMs) {
+                super.withProvisioningTimeoutMs(timeoutMs);
+                return this;
+            }
 
-        private static boolean isIPv6GUA(LinkAddress addr) {
-            return addr.isIPv6() && addr.isGlobalPreferred();
+            @Override
+            public Builder withRandomMacAddress() {
+                super.withRandomMacAddress();
+                return this;
+            }
+
+            @Override
+            public Builder withStableMacAddress() {
+                super.withStableMacAddress();
+                return this;
+            }
+
+            @Override
+            public Builder withNetwork(Network network) {
+                super.withNetwork(network);
+                return this;
+            }
+
+            @Override
+            public Builder withDisplayName(String displayName) {
+                super.withDisplayName(displayName);
+                return this;
+            }
+
+            @Override
+            public ProvisioningConfiguration build() {
+                return new ProvisioningConfiguration(mConfig);
+            }
         }
     }
 
@@ -600,7 +343,7 @@
     private static final int CMD_START                            = 3;
     private static final int CMD_CONFIRM                          = 4;
     private static final int EVENT_PRE_DHCP_ACTION_COMPLETE       = 5;
-    // Sent by NetlinkTracker to communicate netlink events.
+    // Triggered by NetlinkTracker to communicate netlink events.
     private static final int EVENT_NETLINK_LINKPROPERTIES_CHANGED = 6;
     private static final int CMD_UPDATE_TCP_BUFFER_SIZES          = 7;
     private static final int CMD_UPDATE_HTTP_PROXY                = 8;
@@ -638,7 +381,7 @@
     private final String mInterfaceName;
     private final String mClatInterfaceName;
     @VisibleForTesting
-    protected final Callback mCallback;
+    protected final IpClientCallbacks mCallback;
     private final Dependencies mDependencies;
     private final CountDownLatch mShutdownLatch;
     private final INetworkManagementService mNwService;
@@ -657,7 +400,7 @@
      * Non-final member variables accessed only from within our StateMachine.
      */
     private LinkProperties mLinkProperties;
-    private ProvisioningConfiguration mConfiguration;
+    private android.net.shared.ProvisioningConfiguration mConfiguration;
     private MultinetworkPolicyTracker mMultinetworkPolicyTracker;
     private IpReachabilityMonitor mIpReachabilityMonitor;
     private DhcpClient mDhcpClient;
@@ -686,12 +429,15 @@
             return NetdService.getInstance();
         }
 
+        /**
+         * Get interface parameters for the specified interface.
+         */
         public InterfaceParams getInterfaceParams(String ifname) {
             return InterfaceParams.getByName(ifname);
         }
     }
 
-    public IpClient(Context context, String ifName, Callback callback) {
+    public IpClient(Context context, String ifName, IpClientCallbacks callback) {
         this(context, ifName, callback, new Dependencies());
     }
 
@@ -699,16 +445,18 @@
      * An expanded constructor, useful for dependency injection.
      * TODO: migrate all test users to mock IpClient directly and remove this ctor.
      */
-    public IpClient(Context context, String ifName, Callback callback,
+    public IpClient(Context context, String ifName, IpClientCallbacks callback,
             INetworkManagementService nwService) {
         this(context, ifName, callback, new Dependencies() {
             @Override
-            public INetworkManagementService getNMS() { return nwService; }
+            public INetworkManagementService getNMS() {
+                return nwService;
+            }
         });
     }
 
     @VisibleForTesting
-    IpClient(Context context, String ifName, Callback callback, Dependencies deps) {
+    IpClient(Context context, String ifName, IpClientCallbacks callback, Dependencies deps) {
         super(IpClient.class.getSimpleName() + "." + ifName);
         Preconditions.checkNotNull(ifName);
         Preconditions.checkNotNull(callback);
@@ -750,7 +498,7 @@
                     return;
                 }
 
-                final String msg = "interfaceAdded(" + iface +")";
+                final String msg = "interfaceAdded(" + iface + ")";
                 logMsg(msg);
             }
 
@@ -768,13 +516,13 @@
                     return;
                 }
 
-                final String msg = "interfaceRemoved(" + iface +")";
+                final String msg = "interfaceRemoved(" + iface + ")";
                 logMsg(msg);
             }
 
             private void logMsg(String msg) {
                 Log.d(mTag, msg);
-                getHandler().post(() -> { mLog.log("OBSERVED " + msg); });
+                getHandler().post(() -> mLog.log("OBSERVED " + msg));
             }
         };
 
@@ -795,11 +543,64 @@
         startStateMachineUpdaters();
     }
 
+    /**
+     * Make a IIpClient connector to communicate with this IpClient.
+     */
+    public IIpClient makeConnector() {
+        return new IpClientConnector();
+    }
+
+    class IpClientConnector extends IIpClient.Stub {
+        @Override
+        public void completedPreDhcpAction() {
+            IpClient.this.completedPreDhcpAction();
+        }
+        @Override
+        public void confirmConfiguration() {
+            IpClient.this.confirmConfiguration();
+        }
+        @Override
+        public void readPacketFilterComplete(byte[] data) {
+            IpClient.this.readPacketFilterComplete(data);
+        }
+        @Override
+        public void shutdown() {
+            IpClient.this.shutdown();
+        }
+        @Override
+        public void startProvisioning(ProvisioningConfigurationParcelable req) {
+            IpClient.this.startProvisioning(
+                    android.net.shared.ProvisioningConfiguration.fromStableParcelable(req));
+        }
+        @Override
+        public void stop() {
+            IpClient.this.stop();
+        }
+        @Override
+        public void setTcpBufferSizes(String tcpBufferSizes) {
+            IpClient.this.setTcpBufferSizes(tcpBufferSizes);
+        }
+        @Override
+        public void setHttpProxy(ProxyInfoParcelable proxyInfo) {
+            IpClient.this.setHttpProxy(fromStableParcelable(proxyInfo));
+        }
+        @Override
+        public void setMulticastFilter(boolean enabled) {
+            IpClient.this.setMulticastFilter(enabled);
+        }
+        // TODO: remove and have IpClient logs dumped in NetworkStack dumpsys
+        public void dumpIpClientLogs(FileDescriptor fd, PrintWriter pw, String[] args) {
+            IpClient.this.dump(fd, pw, args);
+        }
+    }
+
     private void configureAndStartStateMachine() {
+        // CHECKSTYLE:OFF IndentationCheck
         addState(mStoppedState);
         addState(mStartedState);
             addState(mRunningState, mStartedState);
         addState(mStoppingState);
+        // CHECKSTYLE:ON IndentationCheck
 
         setInitialState(mStoppedState);
 
@@ -828,7 +629,9 @@
         mShutdownLatch.countDown();
     }
 
-    // Shut down this IpClient instance altogether.
+    /**
+     * Shut down this IpClient instance altogether.
+     */
     public void shutdown() {
         stop();
         sendMessage(CMD_TERMINATE_AFTER_STOP);
@@ -849,7 +652,10 @@
         return new ProvisioningConfiguration.Builder();
     }
 
-    public void startProvisioning(ProvisioningConfiguration req) {
+    /**
+     * Start provisioning with the provided parameters.
+     */
+    public void startProvisioning(android.net.shared.ProvisioningConfiguration req) {
         if (!req.isValid()) {
             doImmediateProvisioningFailure(IpManagerEvent.ERROR_INVALID_PROVISIONING);
             return;
@@ -863,7 +669,7 @@
         }
 
         mCallback.setNeighborDiscoveryOffload(true);
-        sendMessage(CMD_START, new ProvisioningConfiguration(req));
+        sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req));
     }
 
     // TODO: Delete this.
@@ -874,21 +680,37 @@
     }
 
     public void startProvisioning() {
-        startProvisioning(new ProvisioningConfiguration());
+        startProvisioning(new android.net.shared.ProvisioningConfiguration());
     }
 
+    /**
+     * Stop this IpClient.
+     *
+     * <p>This does not shut down the StateMachine itself, which is handled by {@link #shutdown()}.
+     */
     public void stop() {
         sendMessage(CMD_STOP);
     }
 
+    /**
+     * Confirm the provisioning configuration.
+     */
     public void confirmConfiguration() {
         sendMessage(CMD_CONFIRM);
     }
 
+    /**
+     * For clients using {@link ProvisioningConfiguration.Builder#withPreDhcpAction()}, must be
+     * called after {@link IIpClientCallbacks#onPreDhcpAction} to indicate that DHCP is clear to
+     * proceed.
+     */
     public void completedPreDhcpAction() {
         sendMessage(EVENT_PRE_DHCP_ACTION_COMPLETE);
     }
 
+    /**
+     * Indicate that packet filter read is complete.
+     */
     public void readPacketFilterComplete(byte[] data) {
         sendMessage(EVENT_READ_PACKET_FILTER_COMPLETE, data);
     }
@@ -921,6 +743,9 @@
         sendMessage(CMD_SET_MULTICAST_FILTER, enabled);
     }
 
+    /**
+     * Dump logs of this IpClient.
+     */
     public void dump(FileDescriptor fd, PrintWriter writer, String[] args) {
         if (args != null && args.length > 0 && DUMP_ARG_CONFIRM.equals(args[0])) {
             // Execute confirmConfiguration() and take no further action.
@@ -930,7 +755,7 @@
 
         // Thread-unsafe access to mApfFilter but just used for debugging.
         final ApfFilter apfFilter = mApfFilter;
-        final ProvisioningConfiguration provisioningConfig = mConfiguration;
+        final android.net.shared.ProvisioningConfiguration provisioningConfig = mConfiguration;
         final ApfCapabilities apfCapabilities = (provisioningConfig != null)
                 ? provisioningConfig.mApfCapabilities : null;
 
@@ -1163,19 +988,25 @@
     private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) {
         switch (delta) {
             case GAINED_PROVISIONING:
-                if (DBG) { Log.d(mTag, "onProvisioningSuccess()"); }
+                if (DBG) {
+                    Log.d(mTag, "onProvisioningSuccess()");
+                }
                 recordMetric(IpManagerEvent.PROVISIONING_OK);
                 mCallback.onProvisioningSuccess(newLp);
                 break;
 
             case LOST_PROVISIONING:
-                if (DBG) { Log.d(mTag, "onProvisioningFailure()"); }
+                if (DBG) {
+                    Log.d(mTag, "onProvisioningFailure()");
+                }
                 recordMetric(IpManagerEvent.PROVISIONING_FAIL);
                 mCallback.onProvisioningFailure(newLp);
                 break;
 
             default:
-                if (DBG) { Log.d(mTag, "onLinkPropertiesChange()"); }
+                if (DBG) {
+                    Log.d(mTag, "onLinkPropertiesChange()");
+                }
                 mCallback.onLinkPropertiesChange(newLp);
                 break;
         }
@@ -1319,7 +1150,9 @@
         // any addresses upon entry to StoppedState.
         mInterfaceCtrl.clearIPv4Address();
         mDhcpResults = null;
-        if (DBG) { Log.d(mTag, "onNewDhcpResults(null)"); }
+        if (DBG) {
+            Log.d(mTag, "onNewDhcpResults(null)");
+        }
         mCallback.onNewDhcpResults(null);
 
         handleProvisioningFailure();
@@ -1372,9 +1205,9 @@
     }
 
     private boolean startIPv6() {
-        return mInterfaceCtrl.setIPv6PrivacyExtensions(true) &&
-               mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode) &&
-               mInterfaceCtrl.enableIPv6();
+        return mInterfaceCtrl.setIPv6PrivacyExtensions(true)
+                && mInterfaceCtrl.setIPv6AddrGenModeIfSupported(mConfiguration.mIPv6AddrGenMode)
+                && mInterfaceCtrl.enableIPv6();
     }
 
     private boolean applyInitialConfig(InitialConfiguration config) {
@@ -1392,10 +1225,10 @@
             // settings observer to watch for update and re-program these
             // parameters (Q: is this level of dynamic updatability really
             // necessary or does reading from settings at startup suffice?).
-            final int NUM_SOLICITS = 5;
-            final int INTER_SOLICIT_INTERVAL_MS = 750;
+            final int numSolicits = 5;
+            final int interSolicitIntervalMs = 750;
             setNeighborParameters(mDependencies.getNetd(), mInterfaceName,
-                    NUM_SOLICITS, INTER_SOLICIT_INTERVAL_MS);
+                    numSolicits, interSolicitIntervalMs);
         } catch (Exception e) {
             mLog.e("Failed to adjust neighbor parameters", e);
             // Carry on using the system defaults (currently: 3, 1000);
@@ -1463,7 +1296,7 @@
                     break;
 
                 case CMD_START:
-                    mConfiguration = (ProvisioningConfiguration) msg.obj;
+                    mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj;
                     transitionTo(mStartedState);
                     break;
 
@@ -1542,8 +1375,8 @@
             mStartTimeMillis = SystemClock.elapsedRealtime();
 
             if (mConfiguration.mProvisioningTimeoutMs > 0) {
-                final long alarmTime = SystemClock.elapsedRealtime() +
-                        mConfiguration.mProvisioningTimeoutMs;
+                final long alarmTime = SystemClock.elapsedRealtime()
+                        + mConfiguration.mProvisioningTimeoutMs;
                 mProvisioningTimeoutAlarm.schedule(alarmTime);
             }
 
@@ -1598,8 +1431,7 @@
         }
 
         private boolean readyToProceed() {
-            return (!mLinkProperties.hasIPv4Address() &&
-                    !mLinkProperties.hasGlobalIPv6Address());
+            return (!mLinkProperties.hasIPv4Address() && !mLinkProperties.hasGlobalIPv6Address());
         }
     }
 
@@ -1650,7 +1482,7 @@
             if (mConfiguration.mUsingMultinetworkPolicyTracker) {
                 mMultinetworkPolicyTracker = new MultinetworkPolicyTracker(
                         mContext, getHandler(),
-                        () -> { mLog.log("OBSERVED AvoidBadWifi changed"); });
+                        () -> mLog.log("OBSERVED AvoidBadWifi changed"));
                 mMultinetworkPolicyTracker.start();
             }
 
@@ -1711,8 +1543,8 @@
             if (!mDhcpActionInFlight) {
                 mCallback.onPreDhcpAction();
                 mDhcpActionInFlight = true;
-                final long alarmTime = SystemClock.elapsedRealtime() +
-                        mConfiguration.mRequestedPreDhcpActionMs;
+                final long alarmTime = SystemClock.elapsedRealtime()
+                        + mConfiguration.mRequestedPreDhcpActionMs;
                 mDhcpActionTimeoutAlarm.schedule(alarmTime);
             }
         }
@@ -1881,16 +1713,18 @@
     }
 
     private static void setNeighborParameters(
-            INetd netd, String ifName, int num_solicits, int inter_solicit_interval_ms)
+            INetd netd, String ifName, int numSolicits, int interSolicitIntervalMs)
             throws RemoteException, IllegalArgumentException {
         Preconditions.checkNotNull(netd);
         Preconditions.checkArgument(!TextUtils.isEmpty(ifName));
-        Preconditions.checkArgument(num_solicits > 0);
-        Preconditions.checkArgument(inter_solicit_interval_ms > 0);
+        Preconditions.checkArgument(numSolicits > 0);
+        Preconditions.checkArgument(interSolicitIntervalMs > 0);
 
         for (int family : new Integer[]{INetd.IPV4, INetd.IPV6}) {
-            netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms", Integer.toString(inter_solicit_interval_ms));
-            netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit", Integer.toString(num_solicits));
+            netd.setProcSysNet(family, INetd.NEIGH, ifName, "retrans_time_ms",
+                    Integer.toString(interSolicitIntervalMs));
+            netd.setProcSysNet(family, INetd.NEIGH, ifName, "ucast_solicit",
+                    Integer.toString(numSolicits));
         }
     }
 
@@ -1919,7 +1753,7 @@
     static <T> T find(Iterable<T> coll, Predicate<T> fn) {
         for (T t: coll) {
             if (fn.test(t)) {
-              return t;
+                return t;
             }
         }
         return null;
diff --git a/services/net/java/android/net/ip/IpClientUtil.java b/services/net/java/android/net/ip/IpClientUtil.java
new file mode 100644
index 0000000..0aec101
--- /dev/null
+++ b/services/net/java/android/net/ip/IpClientUtil.java
@@ -0,0 +1,92 @@
+/*
+ * 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 android.net.ip;
+
+import android.content.Context;
+import android.net.LinkProperties;
+import android.os.ConditionVariable;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+
+
+/**
+ * Utilities and wrappers to simplify communication with IpClient, which lives in the NetworkStack
+ * process.
+ *
+ * @hide
+ */
+public class IpClientUtil {
+    // TODO: remove once IpClient dumps are moved to NetworkStack and callers don't need this arg
+    public static final String DUMP_ARG = IpClient.DUMP_ARG;
+
+    /**
+     * Subclass of {@link IpClientCallbacks} allowing clients to block until provisioning is
+     * complete with {@link WaitForProvisioningCallbacks#waitForProvisioning()}.
+     */
+    public static class WaitForProvisioningCallbacks extends IpClientCallbacks {
+        private final ConditionVariable mCV = new ConditionVariable();
+        private LinkProperties mCallbackLinkProperties;
+
+        /**
+         * Block until either {@link #onProvisioningSuccess(LinkProperties)} or
+         * {@link #onProvisioningFailure(LinkProperties)} is called.
+         */
+        public LinkProperties waitForProvisioning() {
+            mCV.block();
+            return mCallbackLinkProperties;
+        }
+
+        @Override
+        public void onProvisioningSuccess(LinkProperties newLp) {
+            mCallbackLinkProperties = newLp;
+            mCV.open();
+        }
+
+        @Override
+        public void onProvisioningFailure(LinkProperties newLp) {
+            mCallbackLinkProperties = null;
+            mCV.open();
+        }
+    }
+
+    /**
+     * Create a new IpClient.
+     *
+     * <p>This is a convenience method to allow clients to use {@link IpClientCallbacks} instead of
+     * {@link IIpClientCallbacks}.
+     */
+    public static void makeIpClient(Context context, String ifName, IpClientCallbacks callback) {
+        // TODO: request IpClient asynchronously from NetworkStack.
+        final IpClient ipClient = new IpClient(context, ifName, callback);
+        callback.onIpClientCreated(ipClient.makeConnector());
+    }
+
+    /**
+     * Dump logs for the specified IpClient.
+     * TODO: remove logging from this method once IpClient logs are dumped in NetworkStack dumpsys,
+     * then remove callers and delete.
+     */
+    public static void dumpIpClient(
+            IIpClient connector, FileDescriptor fd, PrintWriter pw, String[] args) {
+        if (!(connector instanceof IpClient.IpClientConnector)) {
+            pw.println("Invalid connector");
+            return;
+        }
+        ((IpClient.IpClientConnector) connector).dumpIpClientLogs(fd, pw, args);
+    }
+}
diff --git a/services/net/java/android/net/ip/IpManager.java b/services/net/java/android/net/ip/IpManager.java
deleted file mode 100644
index 2eb36a2..0000000
--- a/services/net/java/android/net/ip/IpManager.java
+++ /dev/null
@@ -1,124 +0,0 @@
-/*
- * Copyright (C) 2017 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 android.net.ip;
-
-import android.content.Context;
-import android.net.INetd;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.net.StaticIpConfiguration;
-import android.net.apf.ApfCapabilities;
-import android.net.util.NetdService;
-import android.os.INetworkManagementService;
-import android.os.ServiceManager;
-import android.net.apf.ApfCapabilities;
-
-import com.android.internal.annotations.VisibleForTesting;
-
-
-/*
- * TODO: Delete this altogether in favor of its renamed successor: IpClient.
- *
- * @hide
- */
-public class IpManager extends IpClient {
-    public static class ProvisioningConfiguration extends IpClient.ProvisioningConfiguration {
-        public ProvisioningConfiguration(IpClient.ProvisioningConfiguration ipcConfig) {
-            super(ipcConfig);
-        }
-
-        public static class Builder extends IpClient.ProvisioningConfiguration.Builder {
-            @Override
-            public Builder withoutIPv4() {
-                super.withoutIPv4();
-                return this;
-            }
-            @Override
-            public Builder withoutIPv6() {
-                super.withoutIPv6();
-                return this;
-            }
-            @Override
-            public Builder withoutIpReachabilityMonitor() {
-                super.withoutIpReachabilityMonitor();
-                return this;
-            }
-            @Override
-            public Builder withPreDhcpAction() {
-                super.withPreDhcpAction();
-                return this;
-            }
-            @Override
-            public Builder withPreDhcpAction(int dhcpActionTimeoutMs) {
-                super.withPreDhcpAction(dhcpActionTimeoutMs);
-                return this;
-            }
-            // No Override; locally defined type.
-            public Builder withInitialConfiguration(InitialConfiguration initialConfig) {
-                super.withInitialConfiguration((IpClient.InitialConfiguration) initialConfig);
-                return this;
-            }
-            @Override
-            public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) {
-                super.withStaticConfiguration(staticConfig);
-                return this;
-            }
-            @Override
-            public Builder withApfCapabilities(ApfCapabilities apfCapabilities) {
-                super.withApfCapabilities(apfCapabilities);
-                return this;
-            }
-            @Override
-            public Builder withProvisioningTimeoutMs(int timeoutMs) {
-                super.withProvisioningTimeoutMs(timeoutMs);
-                return this;
-            }
-            @Override
-            public Builder withNetwork(Network network) {
-                super.withNetwork(network);
-                return this;
-            }
-            @Override
-            public Builder withDisplayName(String displayName) {
-                super.withDisplayName(displayName);
-                return this;
-            }
-            @Override
-            public ProvisioningConfiguration build() {
-                return new ProvisioningConfiguration(super.build());
-            }
-        }
-    }
-
-    public static ProvisioningConfiguration.Builder buildProvisioningConfiguration() {
-        return new ProvisioningConfiguration.Builder();
-    }
-
-    public static class InitialConfiguration extends IpClient.InitialConfiguration {
-    }
-
-    public static class Callback extends IpClient.Callback {
-    }
-
-    public IpManager(Context context, String ifName, Callback callback) {
-        super(context, ifName, callback);
-    }
-
-    public void startProvisioning(ProvisioningConfiguration req) {
-        super.startProvisioning((IpClient.ProvisioningConfiguration) req);
-    }
-}
diff --git a/services/net/java/android/net/ip/IpNeighborMonitor.java b/services/net/java/android/net/ip/IpNeighborMonitor.java
index 9512f1b..34bf4b6 100644
--- a/services/net/java/android/net/ip/IpNeighborMonitor.java
+++ b/services/net/java/android/net/ip/IpNeighborMonitor.java
@@ -16,8 +16,8 @@
 
 package android.net.ip;
 
-import static android.net.netlink.NetlinkConstants.hexify;
 import static android.net.netlink.NetlinkConstants.RTM_DELNEIGH;
+import static android.net.netlink.NetlinkConstants.hexify;
 import static android.net.netlink.NetlinkConstants.stringForNlMsgType;
 
 import android.net.MacAddress;
@@ -26,7 +26,6 @@
 import android.net.netlink.NetlinkSocket;
 import android.net.netlink.RtNetlinkNeighborMessage;
 import android.net.netlink.StructNdMsg;
-import android.net.netlink.StructNlMsgHdr;
 import android.net.util.PacketReader;
 import android.net.util.SharedLog;
 import android.os.Handler;
diff --git a/services/net/java/android/net/ip/IpReachabilityMonitor.java b/services/net/java/android/net/ip/IpReachabilityMonitor.java
index 7e02a28..8b02156 100644
--- a/services/net/java/android/net/ip/IpReachabilityMonitor.java
+++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java
@@ -16,11 +16,14 @@
 
 package android.net.ip;
 
+import static android.net.metrics.IpReachabilityEvent.NUD_FAILED;
+import static android.net.metrics.IpReachabilityEvent.NUD_FAILED_ORGANIC;
+import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST;
+import static android.net.metrics.IpReachabilityEvent.PROVISIONING_LOST_ORGANIC;
+
 import android.content.Context;
-import android.net.LinkAddress;
 import android.net.LinkProperties;
 import android.net.LinkProperties.ProvisioningChange;
-import android.net.ProxyInfo;
 import android.net.RouteInfo;
 import android.net.ip.IpNeighborMonitor.NeighborEvent;
 import android.net.metrics.IpConnectivityLog;
@@ -33,28 +36,19 @@
 import android.os.PowerManager;
 import android.os.PowerManager.WakeLock;
 import android.os.SystemClock;
-import android.system.ErrnoException;
-import android.system.OsConstants;
 import android.util.Log;
 
-import com.android.internal.annotations.GuardedBy;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.DumpUtils;
 import com.android.internal.util.DumpUtils.Dump;
 
-import java.io.InterruptedIOException;
 import java.io.PrintWriter;
 import java.net.Inet6Address;
 import java.net.InetAddress;
-import java.net.InetSocketAddress;
-import java.net.SocketAddress;
-import java.nio.ByteBuffer;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 
 /**
@@ -380,7 +374,18 @@
         long duration = SystemClock.elapsedRealtime() - mLastProbeTimeMs;
         boolean isFromProbe = (duration < getProbeWakeLockDuration());
         boolean isProvisioningLost = (delta == ProvisioningChange.LOST_PROVISIONING);
-        int eventType = IpReachabilityEvent.nudFailureEventType(isFromProbe, isProvisioningLost);
+        int eventType = nudFailureEventType(isFromProbe, isProvisioningLost);
         mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType));
     }
+
+    /**
+     * Returns the NUD failure event type code corresponding to the given conditions.
+     */
+    private static int nudFailureEventType(boolean isFromProbe, boolean isProvisioningLost) {
+        if (isFromProbe) {
+            return isProvisioningLost ? PROVISIONING_LOST : NUD_FAILED;
+        } else {
+            return isProvisioningLost ? PROVISIONING_LOST_ORGANIC : NUD_FAILED_ORGANIC;
+        }
+    }
 }
diff --git a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
index d197d01..8e3023b 100644
--- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
+++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java
@@ -18,11 +18,15 @@
 
 import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
 import static android.net.util.NetworkConstants.RFC7421_PREFIX_LENGTH;
-import static android.system.OsConstants.*;
+import static android.system.OsConstants.AF_INET6;
+import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.SOCK_RAW;
+import static android.system.OsConstants.SOL_SOCKET;
+import static android.system.OsConstants.SO_BINDTODEVICE;
+import static android.system.OsConstants.SO_SNDTIMEO;
 
 import android.net.IpPrefix;
 import android.net.LinkAddress;
-import android.net.LinkProperties;
 import android.net.NetworkUtils;
 import android.net.TrafficStats;
 import android.net.util.InterfaceParams;
@@ -34,10 +38,8 @@
 import com.android.internal.annotations.GuardedBy;
 
 import libcore.io.IoBridge;
-import libcore.util.HexEncoding;
 
 import java.io.FileDescriptor;
-import java.io.InterruptedIOException;
 import java.io.IOException;
 import java.net.Inet6Address;
 import java.net.InetAddress;
@@ -47,7 +49,6 @@
 import java.nio.BufferOverflowException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
-import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.HashSet;
 import java.util.Iterator;
diff --git a/services/net/java/android/net/netlink/ConntrackMessage.java b/services/net/java/android/net/netlink/ConntrackMessage.java
index 605c46b..6978739 100644
--- a/services/net/java/android/net/netlink/ConntrackMessage.java
+++ b/services/net/java/android/net/netlink/ConntrackMessage.java
@@ -16,23 +16,15 @@
 
 package android.net.netlink;
 
-import static android.net.netlink.NetlinkConstants.alignedLengthOf;
-import static android.net.netlink.StructNlAttr.makeNestedType;
-import static android.net.netlink.StructNlAttr.NLA_HEADERLEN;
 import static android.net.netlink.StructNlMsgHdr.NLM_F_ACK;
-import static android.net.netlink.StructNlMsgHdr.NLM_F_DUMP;
 import static android.net.netlink.StructNlMsgHdr.NLM_F_REPLACE;
 import static android.net.netlink.StructNlMsgHdr.NLM_F_REQUEST;
-import static android.net.util.NetworkConstants.IPV4_ADDR_LEN;
+
 import static java.nio.ByteOrder.BIG_ENDIAN;
 
 import android.system.OsConstants;
-import android.util.Log;
-import libcore.io.SizeOf;
 
 import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
 
diff --git a/services/net/java/android/net/netlink/StructNfGenMsg.java b/services/net/java/android/net/netlink/StructNfGenMsg.java
index 99695e2..8155977 100644
--- a/services/net/java/android/net/netlink/StructNfGenMsg.java
+++ b/services/net/java/android/net/netlink/StructNfGenMsg.java
@@ -16,8 +16,6 @@
 
 package android.net.netlink;
 
-import libcore.io.SizeOf;
-
 import java.nio.ByteBuffer;
 
 
@@ -29,7 +27,7 @@
  * @hide
  */
 public class StructNfGenMsg {
-    public static final int STRUCT_SIZE = 2 + SizeOf.SHORT;
+    public static final int STRUCT_SIZE = 2 + Short.BYTES;
 
     public static final int NFNETLINK_V0 = 0;
 
diff --git a/services/net/java/android/net/netlink/StructNlAttr.java b/services/net/java/android/net/netlink/StructNlAttr.java
index 811bdbb..28a4e88 100644
--- a/services/net/java/android/net/netlink/StructNlAttr.java
+++ b/services/net/java/android/net/netlink/StructNlAttr.java
@@ -17,7 +17,6 @@
 package android.net.netlink;
 
 import android.net.netlink.NetlinkConstants;
-import libcore.io.SizeOf;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
@@ -117,7 +116,7 @@
     public StructNlAttr(short type, short value, ByteOrder order) {
         this(order);
         nla_type = type;
-        setValue(new byte[SizeOf.SHORT]);
+        setValue(new byte[Short.BYTES]);
         getValueAsByteBuffer().putShort(value);
     }
 
@@ -128,7 +127,7 @@
     public StructNlAttr(short type, int value, ByteOrder order) {
         this(order);
         nla_type = type;
-        setValue(new byte[SizeOf.INT]);
+        setValue(new byte[Integer.BYTES]);
         getValueAsByteBuffer().putInt(value);
     }
 
@@ -164,7 +163,7 @@
 
     public int getValueAsInt(int defaultValue) {
         final ByteBuffer byteBuffer = getValueAsByteBuffer();
-        if (byteBuffer == null || byteBuffer.remaining() != SizeOf.INT) {
+        if (byteBuffer == null || byteBuffer.remaining() != Integer.BYTES) {
             return defaultValue;
         }
         return getValueAsByteBuffer().getInt();
diff --git a/services/net/java/android/net/netlink/StructNlMsgErr.java b/services/net/java/android/net/netlink/StructNlMsgErr.java
index f095af4..6fcc6e6 100644
--- a/services/net/java/android/net/netlink/StructNlMsgErr.java
+++ b/services/net/java/android/net/netlink/StructNlMsgErr.java
@@ -18,7 +18,6 @@
 
 import android.net.netlink.NetlinkConstants;
 import android.net.netlink.StructNlMsgHdr;
-import libcore.io.SizeOf;
 
 import java.nio.ByteBuffer;
 
@@ -31,7 +30,7 @@
  * @hide
  */
 public class StructNlMsgErr {
-    public static final int STRUCT_SIZE = SizeOf.INT + StructNlMsgHdr.STRUCT_SIZE;
+    public static final int STRUCT_SIZE = Integer.BYTES + StructNlMsgHdr.STRUCT_SIZE;
 
     public static boolean hasAvailableSpace(ByteBuffer byteBuffer) {
         return byteBuffer != null && byteBuffer.remaining() >= STRUCT_SIZE;
diff --git a/services/net/java/android/net/shared/ProvisioningConfiguration.java b/services/net/java/android/net/shared/ProvisioningConfiguration.java
index d995d1b..f937065 100644
--- a/services/net/java/android/net/shared/ProvisioningConfiguration.java
+++ b/services/net/java/android/net/shared/ProvisioningConfiguration.java
@@ -22,6 +22,7 @@
 import android.net.ProvisioningConfigurationParcelable;
 import android.net.StaticIpConfiguration;
 import android.net.apf.ApfCapabilities;
+import android.net.ip.IIpClient;
 
 import java.util.Objects;
 import java.util.StringJoiner;
diff --git a/services/net/java/android/net/util/ConnectivityPacketSummary.java b/services/net/java/android/net/util/ConnectivityPacketSummary.java
index 4951400..ec833b0 100644
--- a/services/net/java/android/net/util/ConnectivityPacketSummary.java
+++ b/services/net/java/android/net/util/ConnectivityPacketSummary.java
@@ -16,19 +16,56 @@
 
 package android.net.util;
 
-import android.net.dhcp.DhcpPacket;
+import static android.net.util.NetworkConstants.ARP_HWTYPE_ETHER;
+import static android.net.util.NetworkConstants.ARP_PAYLOAD_LEN;
+import static android.net.util.NetworkConstants.ARP_REPLY;
+import static android.net.util.NetworkConstants.ARP_REQUEST;
+import static android.net.util.NetworkConstants.DHCP4_CLIENT_PORT;
+import static android.net.util.NetworkConstants.ETHER_ADDR_LEN;
+import static android.net.util.NetworkConstants.ETHER_DST_ADDR_OFFSET;
+import static android.net.util.NetworkConstants.ETHER_HEADER_LEN;
+import static android.net.util.NetworkConstants.ETHER_SRC_ADDR_OFFSET;
+import static android.net.util.NetworkConstants.ETHER_TYPE_ARP;
+import static android.net.util.NetworkConstants.ETHER_TYPE_IPV4;
+import static android.net.util.NetworkConstants.ETHER_TYPE_IPV6;
+import static android.net.util.NetworkConstants.ETHER_TYPE_OFFSET;
+import static android.net.util.NetworkConstants.ICMPV6_HEADER_MIN_LEN;
+import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_LENGTH_SCALING_FACTOR;
+import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_MIN_LENGTH;
+import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_MTU;
+import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_SLLA;
+import static android.net.util.NetworkConstants.ICMPV6_ND_OPTION_TLLA;
+import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_ADVERTISEMENT;
+import static android.net.util.NetworkConstants.ICMPV6_NEIGHBOR_SOLICITATION;
+import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static android.net.util.NetworkConstants.ICMPV6_ROUTER_SOLICITATION;
+import static android.net.util.NetworkConstants.IPV4_ADDR_LEN;
+import static android.net.util.NetworkConstants.IPV4_DST_ADDR_OFFSET;
+import static android.net.util.NetworkConstants.IPV4_FLAGS_OFFSET;
+import static android.net.util.NetworkConstants.IPV4_FRAGMENT_MASK;
+import static android.net.util.NetworkConstants.IPV4_HEADER_MIN_LEN;
+import static android.net.util.NetworkConstants.IPV4_IHL_MASK;
+import static android.net.util.NetworkConstants.IPV4_PROTOCOL_OFFSET;
+import static android.net.util.NetworkConstants.IPV4_SRC_ADDR_OFFSET;
+import static android.net.util.NetworkConstants.IPV6_ADDR_LEN;
+import static android.net.util.NetworkConstants.IPV6_HEADER_LEN;
+import static android.net.util.NetworkConstants.IPV6_PROTOCOL_OFFSET;
+import static android.net.util.NetworkConstants.IPV6_SRC_ADDR_OFFSET;
+import static android.net.util.NetworkConstants.UDP_HEADER_LEN;
+import static android.net.util.NetworkConstants.asString;
+import static android.net.util.NetworkConstants.asUint;
+import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_UDP;
+
 import android.net.MacAddress;
+import android.net.dhcp.DhcpPacket;
 
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.nio.ByteBuffer;
 import java.nio.ByteOrder;
-import java.util.Arrays;
 import java.util.StringJoiner;
 
-import static android.system.OsConstants.*;
-import static android.net.util.NetworkConstants.*;
-
 
 /**
  * Critical connectivity packet summarizing class.
diff --git a/services/net/java/android/net/util/InterfaceParams.java b/services/net/java/android/net/util/InterfaceParams.java
index a4b2fbb..7b060da 100644
--- a/services/net/java/android/net/util/InterfaceParams.java
+++ b/services/net/java/android/net/util/InterfaceParams.java
@@ -16,9 +16,9 @@
 
 package android.net.util;
 
-import static android.net.MacAddress.ALL_ZEROS_ADDRESS;
 import static android.net.util.NetworkConstants.ETHER_MTU;
 import static android.net.util.NetworkConstants.IPV6_MIN_MTU;
+
 import static com.android.internal.util.Preconditions.checkArgument;
 
 import android.net.MacAddress;
@@ -67,7 +67,8 @@
         checkArgument((index > 0), "invalid interface index");
         this.name = name;
         this.index = index;
-        this.macAddr = (macAddr != null) ? macAddr : ALL_ZEROS_ADDRESS;
+        this.macAddr = (macAddr != null) ? macAddr : MacAddress.fromBytes(new byte[] {
+                0x02, 0x00, 0x00, 0x00, 0x00, 0x00 });
         this.defaultMtu = (defaultMtu > IPV6_MIN_MTU) ? defaultMtu : IPV6_MIN_MTU;
     }
 
diff --git a/services/net/java/android/net/util/NetdService.java b/services/net/java/android/net/util/NetdService.java
index 6e69ff5..80b2c27 100644
--- a/services/net/java/android/net/util/NetdService.java
+++ b/services/net/java/android/net/util/NetdService.java
@@ -19,7 +19,6 @@
 import android.net.INetd;
 import android.os.RemoteException;
 import android.os.ServiceManager;
-import android.os.ServiceSpecificException;
 import android.os.SystemClock;
 import android.util.Log;
 
diff --git a/telephony/java/android/telephony/ModemInfo.java b/telephony/java/android/telephony/ModemInfo.java
index 564effe..27a5121 100644
--- a/telephony/java/android/telephony/ModemInfo.java
+++ b/telephony/java/android/telephony/ModemInfo.java
@@ -32,6 +32,11 @@
     public final boolean isVoiceSupported;
     public final boolean isDataSupported;
 
+    // TODO b/121394331: Clean up this class after V1_1.PhoneCapability cleanup.
+    public ModemInfo(int modemId) {
+        this(modemId, 0, true, true);
+    }
+
     public ModemInfo(int modemId, int rat, boolean isVoiceSupported, boolean isDataSupported) {
         this.modemId = modemId;
         this.rat = rat;
diff --git a/telephony/java/android/telephony/SubscriptionManager.java b/telephony/java/android/telephony/SubscriptionManager.java
index c9d0eb1..6724c034 100644
--- a/telephony/java/android/telephony/SubscriptionManager.java
+++ b/telephony/java/android/telephony/SubscriptionManager.java
@@ -1173,17 +1173,33 @@
     @SuppressAutoDoc // Blocked by b/72967236 - no support for carrier privileges
     @RequiresPermission(android.Manifest.permission.READ_PHONE_STATE)
     public List<SubscriptionInfo> getActiveSubscriptionInfoList() {
-        List<SubscriptionInfo> result = null;
+        return getActiveSubscriptionInfoList(false);
+    }
+
+    /**
+     * This is similar to {@link #getActiveSubscriptionInfoList()}, but if userVisibleOnly
+     * is true, it will filter out the hidden subscriptions.
+     *
+     * @hide
+     */
+    public List<SubscriptionInfo> getActiveSubscriptionInfoList(boolean userVisibleOnly) {
+        List<SubscriptionInfo> activeList = null;
 
         try {
             ISub iSub = ISub.Stub.asInterface(ServiceManager.getService("isub"));
             if (iSub != null) {
-                result = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
+                activeList = iSub.getActiveSubscriptionInfoList(mContext.getOpPackageName());
             }
         } catch (RemoteException ex) {
             // ignore it
         }
-        return result;
+
+        if (!userVisibleOnly || activeList == null) {
+            return activeList;
+        } else {
+            return activeList.stream().filter(subInfo -> !shouldHideSubscription(subInfo))
+                    .collect(Collectors.toList());
+        }
     }
 
     /**
@@ -2717,8 +2733,7 @@
         if (availableList == null) {
             return null;
         } else {
-            return getAvailableSubscriptionInfoList().stream()
-                    .filter(subInfo -> !shouldHideSubscription(subInfo))
+            return availableList.stream().filter(subInfo -> !shouldHideSubscription(subInfo))
                     .collect(Collectors.toList());
         }
     }
diff --git a/telephony/java/com/android/internal/telephony/RILConstants.java b/telephony/java/com/android/internal/telephony/RILConstants.java
index 2ebe870..8e58b02 100644
--- a/telephony/java/com/android/internal/telephony/RILConstants.java
+++ b/telephony/java/com/android/internal/telephony/RILConstants.java
@@ -422,6 +422,7 @@
     int RIL_REQUEST_SET_LINK_CAPACITY_REPORTING_CRITERIA = 203;
     int RIL_REQUEST_SET_PREFERRED_DATA_MODEM = 204;
     int RIL_REQUEST_EMERGENCY_DIAL = 205;
+    int RIL_REQUEST_GET_PHONE_CAPABILITY = 206;
 
     /* Responses begin */
     int RIL_RESPONSE_ACKNOWLEDGEMENT = 800;
diff --git a/tests/net/java/android/net/apf/ApfTest.java b/tests/net/java/android/net/apf/ApfTest.java
index 151b559..3c3e7ce 100644
--- a/tests/net/java/android/net/apf/ApfTest.java
+++ b/tests/net/java/android/net/apf/ApfTest.java
@@ -16,10 +16,19 @@
 
 package android.net.apf;
 
-import static android.net.util.NetworkConstants.*;
-import static android.system.OsConstants.*;
+import static android.net.util.NetworkConstants.ICMPV6_ECHO_REQUEST_TYPE;
+import static android.net.util.NetworkConstants.ICMPV6_ROUTER_ADVERTISEMENT;
+import static android.system.OsConstants.AF_UNIX;
+import static android.system.OsConstants.ARPHRD_ETHER;
+import static android.system.OsConstants.ETH_P_ARP;
+import static android.system.OsConstants.ETH_P_IP;
+import static android.system.OsConstants.ETH_P_IPV6;
+import static android.system.OsConstants.IPPROTO_ICMPV6;
+import static android.system.OsConstants.IPPROTO_UDP;
+import static android.system.OsConstants.SOCK_STREAM;
+
 import static com.android.internal.util.BitUtils.bytesToBEInt;
-import static com.android.internal.util.BitUtils.put;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
@@ -33,7 +42,7 @@
 import android.net.apf.ApfFilter.ApfConfiguration;
 import android.net.apf.ApfGenerator.IllegalInstructionException;
 import android.net.apf.ApfGenerator.Register;
-import android.net.ip.IpClient;
+import android.net.ip.IpClientCallbacks;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.RaEvent;
 import android.net.util.InterfaceParams;
@@ -47,8 +56,20 @@
 import android.system.Os;
 import android.text.format.DateUtils;
 import android.util.Log;
+
 import com.android.frameworks.tests.net.R;
 import com.android.internal.util.HexDump;
+
+import libcore.io.IoUtils;
+import libcore.io.Streams;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
 import java.io.File;
 import java.io.FileDescriptor;
 import java.io.FileOutputStream;
@@ -59,14 +80,6 @@
 import java.nio.ByteBuffer;
 import java.util.List;
 import java.util.Random;
-import libcore.io.IoUtils;
-import libcore.io.Streams;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
 
 /**
  * Tests for APF program generator and interpreter.
@@ -902,7 +915,7 @@
             HexDump.toHexString(data, false), result);
     }
 
-    private class MockIpClientCallback extends IpClient.Callback {
+    private class MockIpClientCallback extends IpClientCallbacks {
         private final ConditionVariable mGotApfProgram = new ConditionVariable();
         private byte[] mLastApfProgram;
 
@@ -933,7 +946,7 @@
         private final long mFixedTimeMs = SystemClock.elapsedRealtime();
 
         public TestApfFilter(Context context, ApfConfiguration config,
-                IpClient.Callback ipClientCallback, IpConnectivityLog log) throws Exception {
+                IpClientCallbacks ipClientCallback, IpConnectivityLog log) throws Exception {
             super(context, config, InterfaceParams.getByName("lo"), ipClientCallback, log);
         }
 
@@ -1062,7 +1075,7 @@
     private static final byte[] IPV4_ANY_HOST_ADDR       = {0, 0, 0, 0};
 
     // Helper to initialize a default apfFilter.
-    private ApfFilter setupApfFilter(IpClient.Callback ipClientCallback, ApfConfiguration config)
+    private ApfFilter setupApfFilter(IpClientCallbacks ipClientCallback, ApfConfiguration config)
             throws Exception {
         LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19);
         LinkProperties lp = new LinkProperties();
@@ -1509,7 +1522,8 @@
     }
 
     private void verifyRaEvent(RaEvent expected) {
-        ArgumentCaptor<Parcelable> captor = ArgumentCaptor.forClass(Parcelable.class);
+        ArgumentCaptor<IpConnectivityLog.Event> captor =
+                ArgumentCaptor.forClass(IpConnectivityLog.Event.class);
         verify(mLog, atLeastOnce()).log(captor.capture());
         RaEvent got = lastRaEvent(captor.getAllValues());
         if (!raEventEquals(expected, got)) {
@@ -1517,7 +1531,7 @@
         }
     }
 
-    private RaEvent lastRaEvent(List<Parcelable> events) {
+    private RaEvent lastRaEvent(List<IpConnectivityLog.Event> events) {
         RaEvent got = null;
         for (Parcelable ev : events) {
             if (ev instanceof RaEvent) {
diff --git a/tests/net/java/android/net/ip/IpClientTest.java b/tests/net/java/android/net/ip/IpClientTest.java
index cba3c65..a2dcfef 100644
--- a/tests/net/java/android/net/ip/IpClientTest.java
+++ b/tests/net/java/android/net/ip/IpClientTest.java
@@ -23,7 +23,6 @@
 import static org.mockito.Mockito.any;
 import static org.mockito.Mockito.anyString;
 import static org.mockito.Mockito.eq;
-import static org.mockito.Mockito.mock;
 import static org.mockito.Mockito.never;
 import static org.mockito.Mockito.reset;
 import static org.mockito.Mockito.timeout;
@@ -40,9 +39,8 @@
 import android.net.LinkProperties;
 import android.net.MacAddress;
 import android.net.RouteInfo;
-import android.net.ip.IpClient.Callback;
-import android.net.ip.IpClient.InitialConfiguration;
-import android.net.ip.IpClient.ProvisioningConfiguration;
+import android.net.shared.InitialConfiguration;
+import android.net.shared.ProvisioningConfiguration;
 import android.net.util.InterfaceParams;
 import android.os.INetworkManagementService;
 import android.provider.Settings;
@@ -50,8 +48,8 @@
 import android.support.test.runner.AndroidJUnit4;
 import android.test.mock.MockContentResolver;
 
-import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.internal.R;
+import com.android.internal.util.test.FakeSettingsProvider;
 import com.android.server.net.BaseNetworkObserver;
 
 import org.junit.Before;
@@ -63,8 +61,8 @@
 
 import java.net.InetAddress;
 import java.util.Arrays;
-import java.util.List;
 import java.util.HashSet;
+import java.util.List;
 import java.util.Set;
 
 /**
@@ -87,7 +85,7 @@
     @Mock private INetworkManagementService mNMService;
     @Mock private INetd mNetd;
     @Mock private Resources mResources;
-    @Mock private Callback mCb;
+    @Mock private IpClientCallbacks mCb;
     @Mock private AlarmManager mAlarm;
     @Mock private IpClient.Dependencies mDependecies;
     private MockContentResolver mContentResolver;
@@ -179,7 +177,7 @@
     public void testInterfaceNotFoundFailsImmediately() throws Exception {
         setTestInterfaceParams(null);
         final IpClient ipc = new IpClient(mContext, TEST_IFNAME, mCb, mDependecies);
-        ipc.startProvisioning(new IpClient.ProvisioningConfiguration());
+        ipc.startProvisioning(new ProvisioningConfiguration());
         verify(mCb, times(1)).onProvisioningFailure(any());
         ipc.shutdown();
     }
diff --git a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
index f9b7ec8..dfaf52a 100644
--- a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
+++ b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java
@@ -16,19 +16,18 @@
 
 package android.net.util;
 
-import static android.net.util.NetworkConstants.*;
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertTrue;
 
 import android.net.MacAddress;
-import android.support.test.runner.AndroidJUnit4;
 import android.support.test.filters.SmallTest;
-
-import org.junit.runner.RunWith;
-import org.junit.Test;
+import android.support.test.runner.AndroidJUnit4;
 
 import libcore.util.HexEncoding;
 
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
 /**
  * Tests for ConnectivityPacketSummary.
  *
diff --git a/tests/net/java/com/android/server/ConnectivityServiceTest.java b/tests/net/java/com/android/server/ConnectivityServiceTest.java
index 882babf..1c26418 100644
--- a/tests/net/java/com/android/server/ConnectivityServiceTest.java
+++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java
@@ -4775,7 +4775,7 @@
 
         // Clat iface up, expect stack link updated.
         clat.interfaceLinkStateChanged(CLAT_PREFIX + MOBILE_IFNAME, true);
-        waitForIdle();
+        networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
         List<LinkProperties> stackedLps = mCm.getLinkProperties(mCellNetworkAgent.getNetwork())
                 .getStackedLinks();
         assertEquals(makeClatLinkProperties(myIpv4), stackedLps.get(0));
@@ -4783,7 +4783,6 @@
         // Change trivial linkproperties and see if stacked link is preserved.
         cellLp.addDnsServer(InetAddress.getByName("8.8.8.8"));
         mCellNetworkAgent.sendLinkProperties(cellLp);
-        waitForIdle();
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
 
         List<LinkProperties> stackedLpsAfterChange =
@@ -4795,19 +4794,19 @@
         cellLp.addLinkAddress(myIpv4);
         cellLp.addRoute(new RouteInfo(myIpv4, null, MOBILE_IFNAME));
         mCellNetworkAgent.sendLinkProperties(cellLp);
-        waitForIdle();
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
         verify(mMockNetd, times(1)).clatdStop(MOBILE_IFNAME);
 
         // Clat iface removed, expect linkproperties revert to original one
         clat.interfaceRemoved(CLAT_PREFIX + MOBILE_IFNAME);
-        waitForIdle();
         networkCallback.expectCallback(CallbackState.LINK_PROPERTIES, mCellNetworkAgent);
         LinkProperties actualLpAfterIpv4 = mCm.getLinkProperties(mCellNetworkAgent.getNetwork());
         assertEquals(cellLp, actualLpAfterIpv4);
 
         // Clean up
         mCellNetworkAgent.disconnect();
+        networkCallback.expectCallback(CallbackState.LOST, mCellNetworkAgent);
+        networkCallback.assertNoCallback();
         mCm.unregisterNetworkCallback(networkCallback);
     }
 
diff --git a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
index 8359fe2..1a0cb74 100644
--- a/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
+++ b/tests/net/java/com/android/server/connectivity/IpConnectivityMetricsTest.java
@@ -18,14 +18,15 @@
 
 import static android.net.metrics.INetdEventListener.EVENT_GETADDRINFO;
 import static android.net.metrics.INetdEventListener.EVENT_GETHOSTBYNAME;
-import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.timeout;
-import static org.mockito.Mockito.verify;
-import static org.mockito.Mockito.when;
+
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
 import static org.junit.Assert.fail;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.timeout;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
 
 import android.content.Context;
 import android.net.ConnectivityManager;
@@ -34,12 +35,11 @@
 import android.net.IpPrefix;
 import android.net.LinkAddress;
 import android.net.LinkProperties;
-import android.net.RouteInfo;
 import android.net.Network;
 import android.net.NetworkCapabilities;
+import android.net.RouteInfo;
 import android.net.metrics.ApfProgramEvent;
 import android.net.metrics.ApfStats;
-import android.net.metrics.DefaultNetworkEvent;
 import android.net.metrics.DhcpClientEvent;
 import android.net.metrics.IpConnectivityLog;
 import android.net.metrics.IpManagerEvent;
@@ -55,6 +55,13 @@
 import com.android.internal.util.BitUtils;
 import com.android.server.connectivity.metrics.nano.IpConnectivityLogClass;
 
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.util.Collections;
@@ -62,13 +69,6 @@
 import java.util.Iterator;
 import java.util.List;
 
-import org.mockito.ArgumentCaptor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
 @RunWith(AndroidJUnit4.class)
 @SmallTest
 public class IpConnectivityMetricsTest {
@@ -154,7 +154,7 @@
     @Test
     public void testRateLimiting() {
         final IpConnectivityLog logger = new IpConnectivityLog(mService.impl);
-        final ApfProgramEvent ev = new ApfProgramEvent();
+        final ApfProgramEvent ev = new ApfProgramEvent.Builder().build();
         final long fakeTimestamp = 1;
 
         int attempt = 100; // More than burst quota, but less than buffer size.
@@ -304,26 +304,31 @@
         when(mCm.getNetworkCapabilities(new Network(100))).thenReturn(ncWifi);
         when(mCm.getNetworkCapabilities(new Network(101))).thenReturn(ncCell);
 
-        ApfStats apfStats = new ApfStats();
-        apfStats.durationMs = 45000;
-        apfStats.receivedRas = 10;
-        apfStats.matchingRas = 2;
-        apfStats.droppedRas = 2;
-        apfStats.parseErrors = 2;
-        apfStats.zeroLifetimeRas = 1;
-        apfStats.programUpdates = 4;
-        apfStats.programUpdatesAll = 7;
-        apfStats.programUpdatesAllowingMulticast = 3;
-        apfStats.maxProgramSize = 2048;
+        ApfStats apfStats = new ApfStats.Builder()
+                .setDurationMs(45000)
+                .setReceivedRas(10)
+                .setMatchingRas(2)
+                .setDroppedRas(2)
+                .setParseErrors(2)
+                .setZeroLifetimeRas(1)
+                .setProgramUpdates(4)
+                .setProgramUpdatesAll(7)
+                .setProgramUpdatesAllowingMulticast(3)
+                .setMaxProgramSize(2048)
+                .build();
 
-        ValidationProbeEvent validationEv = new ValidationProbeEvent();
-        validationEv.durationMs = 40730;
-        validationEv.probeType = ValidationProbeEvent.PROBE_HTTP;
-        validationEv.returnCode = 204;
+        final ValidationProbeEvent validationEv = new ValidationProbeEvent.Builder()
+                .setDurationMs(40730)
+                .setProbeType(ValidationProbeEvent.PROBE_HTTP, true)
+                .setReturnCode(204)
+                .build();
 
+        final DhcpClientEvent event = new DhcpClientEvent.Builder()
+                .setMsg("SomeState")
+                .setDurationMs(192)
+                .build();
         Parcelable[] events = {
-            new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED),
-            new DhcpClientEvent("SomeState", 192),
+            new IpReachabilityEvent(IpReachabilityEvent.NUD_FAILED), event,
             new IpManagerEvent(IpManagerEvent.PROVISIONING_OK, 5678),
             validationEv,
             apfStats,
@@ -424,7 +429,7 @@
                 "  validation_probe_event <",
                 "    latency_ms: 40730",
                 "    probe_result: 204",
-                "    probe_type: 1",
+                "    probe_type: 257",
                 "  >",
                 ">",
                 "events <",
diff --git a/tools/hiddenapi/generate_hiddenapi_lists.py b/tools/hiddenapi/generate_hiddenapi_lists.py
index 01728fa1..2a8f695 100755
--- a/tools/hiddenapi/generate_hiddenapi_lists.py
+++ b/tools/hiddenapi/generate_hiddenapi_lists.py
@@ -17,6 +17,7 @@
 Generate API lists for non-SDK API enforcement.
 """
 import argparse
+from collections import defaultdict
 import os
 import sys
 import re
@@ -27,16 +28,20 @@
 FLAG_BLACKLIST = "blacklist"
 FLAG_GREYLIST_MAX_O = "greylist-max-o"
 FLAG_GREYLIST_MAX_P = "greylist-max-p"
+FLAG_CORE_PLATFORM_API = "core-platform-api"
 
 # List of all known flags.
-FLAGS = [
+FLAGS_API_LIST = [
     FLAG_WHITELIST,
     FLAG_GREYLIST,
     FLAG_BLACKLIST,
     FLAG_GREYLIST_MAX_O,
     FLAG_GREYLIST_MAX_P,
 ]
-FLAGS_SET = set(FLAGS)
+ALL_FLAGS = FLAGS_API_LIST + [ FLAG_CORE_PLATFORM_API ]
+
+FLAGS_API_LIST_SET = set(FLAGS_API_LIST)
+ALL_FLAGS_SET = set(ALL_FLAGS)
 
 # Suffix used in command line args to express that only known and
 # otherwise unassigned entries should be assign the given flag.
@@ -62,7 +67,7 @@
 SERIALIZATION_REGEX = re.compile(r'.*->(' + '|'.join(SERIALIZATION_PATTERNS) + r')$')
 
 # Predicates to be used with filter_apis.
-IS_UNASSIGNED = lambda api, flags: not flags
+HAS_NO_API_LIST_ASSIGNED = lambda api, flags: not FLAGS_API_LIST_SET.intersection(flags)
 IS_SERIALIZATION = lambda api, flags: SERIALIZATION_REGEX.match(api)
 
 def get_args():
@@ -73,12 +78,10 @@
     """
     parser = argparse.ArgumentParser()
     parser.add_argument('--output', required=True)
-    parser.add_argument('--public', required=True, help='list of all public entries')
-    parser.add_argument('--private', required=True, help='list of all private entries')
     parser.add_argument('--csv', nargs='*', default=[], metavar='CSV_FILE',
         help='CSV files to be merged into output')
 
-    for flag in FLAGS:
+    for flag in ALL_FLAGS:
         ignore_conflicts_flag = flag + FLAG_IGNORE_CONFLICTS_SUFFIX
         parser.add_argument('--' + flag, dest=flag, nargs='*', default=[], metavar='TXT_FILE',
             help='lists of entries with flag "' + flag + '"')
@@ -118,26 +121,9 @@
         f.writelines(lines)
 
 class FlagsDict:
-    def __init__(self, public_api, private_api):
-        # Bootstrap the entries dictionary.
-
-        # Check that the two sets do not overlap.
-        public_api_set = set(public_api)
-        private_api_set = set(private_api)
-        assert public_api_set.isdisjoint(private_api_set), (
-            "Lists of public and private API overlap. " +
-            "This suggests an issue with the `hiddenapi` build tool.")
-
-        # Compute the whole key set
-        self._dict_keyset = public_api_set.union(private_api_set)
-
-        # Create a dict that creates entries for both public and private API,
-        # and assigns public API to the whitelist.
-        self._dict = {}
-        for api in public_api:
-            self._dict[api] = set([ FLAG_WHITELIST ])
-        for api in private_api:
-            self._dict[api] = set()
+    def __init__(self):
+        self._dict_keyset = set()
+        self._dict = defaultdict(set)
 
     def _check_entries_set(self, keys_subset, source):
         assert isinstance(keys_subset, set)
@@ -150,12 +136,12 @@
 
     def _check_flags_set(self, flags_subset, source):
         assert isinstance(flags_subset, set)
-        assert flags_subset.issubset(FLAGS_SET), (
+        assert flags_subset.issubset(ALL_FLAGS_SET), (
             "Error processing: {}\n"
             "The following flags were not recognized: \n"
             "{}\n"
             "Please visit go/hiddenapi for more information.").format(
-                source, "\n".join(flags_subset - FLAGS_SET))
+                source, "\n".join(flags_subset - ALL_FLAGS_SET))
 
     def filter_apis(self, filter_fn):
         """Returns APIs which match a given predicate.
@@ -173,7 +159,7 @@
 
     def get_valid_subset_of_unassigned_apis(self, api_subset):
         """Sanitizes a key set input to only include keys which exist in the dictionary
-        and have not been assigned any flags.
+        and have not been assigned any API list flags.
 
         Args:
             entries_subset (set/list): Key set to be sanitized.
@@ -182,7 +168,7 @@
             Sanitized key set.
         """
         assert isinstance(api_subset, set)
-        return api_subset.intersection(self.filter_apis(IS_UNASSIGNED))
+        return api_subset.intersection(self.filter_apis(HAS_NO_API_LIST_ASSIGNED))
 
     def generate_csv(self):
         """Constructs CSV entries from a dictionary.
@@ -203,14 +189,13 @@
             source (string): Origin of `csv_lines`. Will be printed in error messages.
 
         Throws:
-            AssertionError if parsed API signatures of flags are invalid.
+            AssertionError if parsed flags are invalid.
         """
         # Split CSV lines into arrays of values.
         csv_values = [ line.split(',') for line in csv_lines ]
 
-        # Check that all entries exist in the dict.
-        csv_keys = set([ csv[0] for csv in csv_values ])
-        self._check_entries_set(csv_keys, source)
+        # Update the full set of API signatures.
+        self._dict_keyset.update([ csv[0] for csv in csv_values ])
 
         # Check that all flags are known.
         csv_flags = set(reduce(lambda x, y: set(x).union(y), [ csv[1:] for csv in csv_values ], []))
@@ -224,7 +209,7 @@
         """Assigns a flag to given subset of entries.
 
         Args:
-            flag (string): One of FLAGS.
+            flag (string): One of ALL_FLAGS.
             apis (set): Subset of APIs to recieve the flag.
             source (string): Origin of `entries_subset`. Will be printed in error messages.
 
@@ -245,18 +230,23 @@
     # Parse arguments.
     args = vars(get_args())
 
-    flags = FlagsDict(read_lines(args["public"]), read_lines(args["private"]))
+    # Initialize API->flags dictionary.
+    flags = FlagsDict()
+
+    # Merge input CSV files into the dictionary.
+    # Do this first because CSV files produced by parsing API stubs will
+    # contain the full set of APIs. Subsequent additions from text files
+    # will be able to detect invalid entries, and/or filter all as-yet
+    # unassigned entries.
+    for filename in args["csv"]:
+        flags.parse_and_merge_csv(read_lines(filename), filename)
 
     # Combine inputs which do not require any particular order.
     # (1) Assign serialization API to whitelist.
     flags.assign_flag(FLAG_WHITELIST, flags.filter_apis(IS_SERIALIZATION))
 
-    # (2) Merge input CSV files into the dictionary.
-    for filename in args["csv"]:
-        flags.parse_and_merge_csv(read_lines(filename), filename)
-
-    # (3) Merge text files with a known flag into the dictionary.
-    for flag in FLAGS:
+    # (2) Merge text files with a known flag into the dictionary.
+    for flag in ALL_FLAGS:
         for filename in args[flag]:
             flags.assign_flag(flag, read_lines(filename), filename)
 
@@ -265,13 +255,13 @@
     # (a) the entry exists, and
     # (b) it has not been assigned any other flag.
     # Because of (b), this must run after all strict assignments have been performed.
-    for flag in FLAGS:
+    for flag in ALL_FLAGS:
         for filename in args[flag + FLAG_IGNORE_CONFLICTS_SUFFIX]:
             valid_entries = flags.get_valid_subset_of_unassigned_apis(read_lines(filename))
             flags.assign_flag(flag, valid_entries, filename)
 
     # Assign all remaining entries to the blacklist.
-    flags.assign_flag(FLAG_BLACKLIST, flags.filter_apis(IS_UNASSIGNED))
+    flags.assign_flag(FLAG_BLACKLIST, flags.filter_apis(HAS_NO_API_LIST_ASSIGNED))
 
     # Write output.
     write_lines(args["output"], flags.generate_csv())