diff options
65 files changed, 3018 insertions, 993 deletions
diff --git a/Android.bp b/Android.bp index 81a98502d519..a21cd2803267 100644 --- a/Android.bp +++ b/Android.bp @@ -844,6 +844,8 @@ aidl_interface { "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/api/current.txt b/api/current.txt index 19eabbcd90d0..c488d312bc3e 100755 --- a/api/current.txt +++ b/api/current.txt @@ -27553,6 +27553,7 @@ package android.net { 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); diff --git a/api/system-current.txt b/api/system-current.txt index ab45a22730c0..72b8456059d5 100644 --- a/api/system-current.txt +++ b/api/system-current.txt @@ -803,6 +803,7 @@ package android.content { 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.metrics { 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 @@ package android.net { 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,22 +3053,48 @@ package android.net { 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 { ctor public LinkProperties(); + method public boolean addDnsServer(java.net.InetAddress); method public boolean addRoute(android.net.RouteInfo); method public void clear(); + method public String getTcpBufferSizes(); + method public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers(); + method public boolean hasGlobalIPv6Address(); + method public boolean hasIPv4Address(); + method public boolean hasIPv6DefaultRoute(); + method public boolean isIPv4Provisioned(); + method public boolean isIPv6Provisioned(); + method public boolean isProvisioned(); + method public boolean isReachable(java.net.InetAddress); + method public boolean removeDnsServer(java.net.InetAddress); + method public boolean removeRoute(android.net.RouteInfo); method public void setDnsServers(java.util.Collection<java.net.InetAddress>); method public void setDomains(String); method public void setHttpProxy(android.net.ProxyInfo); method public void setInterfaceName(String); method public void setLinkAddresses(java.util.Collection<android.net.LinkAddress>); method public void setMtu(int); + method public void setPrivateDnsServerName(@Nullable String); + method public void setTcpBufferSizes(String); + method public void setUsePrivateDns(boolean); + method public void setValidatedPrivateDnsServers(java.util.Collection<java.net.InetAddress>); + } + + 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(); + method public int[] getTransportTypes(); + method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities); field public static final int NET_CAPABILITY_OEM_PAID = 22; // 0x16 } @@ -3099,6 +3134,13 @@ package android.net { 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 +3197,146 @@ package android.net { } +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 +4402,23 @@ package android.provider { 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 5e7ce650e616..aea3def47ce7 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -598,13 +598,62 @@ package android.media.audiofx { 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 final class LinkProperties implements android.os.Parcelable { + method public boolean addDnsServer(java.net.InetAddress); + method public String getTcpBufferSizes(); + method public java.util.List<java.net.InetAddress> getValidatedPrivateDnsServers(); + method public boolean hasGlobalIPv6Address(); + method public boolean hasIPv4Address(); + method public boolean hasIPv6DefaultRoute(); + method public boolean isIPv4Provisioned(); + method public boolean isIPv6Provisioned(); + method public boolean isProvisioned(); + method public boolean isReachable(java.net.InetAddress); + method public boolean removeDnsServer(java.net.InetAddress); + method public boolean removeRoute(android.net.RouteInfo); + method public void setPrivateDnsServerName(@Nullable String); + method public void setTcpBufferSizes(String); + method public void setUsePrivateDns(boolean); + method public void setValidatedPrivateDnsServers(java.util.Collection<java.net.InetAddress>); + } + + 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(); + method public boolean satisfiedByNetworkCapabilities(android.net.NetworkCapabilities); + } + + 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 { @@ -616,6 +665,146 @@ package android.net { } +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 +1083,21 @@ package android.provider { 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/cmds/bootanimation/bootanim.rc b/cmds/bootanimation/bootanim.rc index 469c9646a4aa..1b3c32b20503 100644 --- a/cmds/bootanimation/bootanim.rc +++ b/cmds/bootanimation/bootanim.rc @@ -2,6 +2,7 @@ service bootanim /system/bin/bootanimation class core animation user graphics group graphics audio + updatable disabled oneshot writepid /dev/stune/top-app/tasks diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java index a510c578fdd1..aeeaa519e2f5 100644 --- a/core/java/android/app/Activity.java +++ b/core/java/android/app/Activity.java @@ -4735,7 +4735,7 @@ public class Activity extends ContextThemeWrapper /** * @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 89cd0643dd20..29161cce2ca0 100644 --- a/core/java/android/content/Context.java +++ b/core/java/android/content/Context.java @@ -1711,7 +1711,7 @@ public abstract class Context { * @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 adc2ebaf30fa..3cdfc6ee4527 100644 --- a/core/java/android/content/ContextWrapper.java +++ b/core/java/android/content/ContextWrapper.java @@ -386,7 +386,6 @@ public class ContextWrapper extends Context { /** @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 ee05f2832a9a..4047068f1c7b 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 @@ import android.os.RemoteException; */ 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 abc00feeb212..cee3a409fc23 100644 --- a/core/java/android/net/ConnectivityManager.java +++ b/core/java/android/net/ConnectivityManager.java @@ -23,6 +23,7 @@ import android.annotation.SdkConstant; 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 @@ public class ConnectivityManager { * 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 @@ public class ConnectivityManager { * 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 b40f15ae17ba..a536d08876d6 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_SITE; 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 @@ public class LinkAddress implements Parcelable { * @return true if the address is IPv6. * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public boolean isIPv6() { return address instanceof Inet6Address; } @@ -126,6 +128,8 @@ public class LinkAddress implements Parcelable { * @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 @@ public class LinkAddress implements Parcelable { * otherwise. * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public boolean isSameAddressAs(LinkAddress other) { return address.equals(other.address) && prefixLength == other.prefixLength; } @@ -310,6 +315,8 @@ public class LinkAddress implements Parcelable { * 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/LinkProperties.java b/core/java/android/net/LinkProperties.java index c2963fd605c0..21b6a8eb1990 100644 --- a/core/java/android/net/LinkProperties.java +++ b/core/java/android/net/LinkProperties.java @@ -19,6 +19,7 @@ package android.net; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.SystemApi; +import android.annotation.TestApi; import android.annotation.UnsupportedAppUsage; import android.os.Build; import android.os.Parcel; @@ -368,7 +369,8 @@ public final class LinkProperties implements Parcelable { * @return true if the DNS server was added, false if it was already present. * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public boolean addDnsServer(InetAddress dnsServer) { if (dnsServer != null && !mDnses.contains(dnsServer)) { mDnses.add(dnsServer); @@ -384,7 +386,8 @@ public final class LinkProperties implements Parcelable { * @return true if the DNS server was removed, false if it did not exist. * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public boolean removeDnsServer(InetAddress dnsServer) { if (dnsServer != null) { return mDnses.remove(dnsServer); @@ -423,6 +426,8 @@ public final class LinkProperties implements Parcelable { * @param usePrivateDns The private DNS state. * @hide */ + @TestApi + @SystemApi public void setUsePrivateDns(boolean usePrivateDns) { mUsePrivateDns = usePrivateDns; } @@ -448,6 +453,8 @@ public final class LinkProperties implements Parcelable { * @param privateDnsServerName The private DNS server name. * @hide */ + @TestApi + @SystemApi public void setPrivateDnsServerName(@Nullable String privateDnsServerName) { mPrivateDnsServerName = privateDnsServerName; } @@ -510,6 +517,8 @@ public final class LinkProperties implements Parcelable { * object. * @hide */ + @TestApi + @SystemApi public void setValidatedPrivateDnsServers(Collection<InetAddress> dnsServers) { mValidatedPrivateDnses.clear(); for (InetAddress dnsServer: dnsServers) { @@ -525,6 +534,8 @@ public final class LinkProperties implements Parcelable { * DNS servers on this link. * @hide */ + @TestApi + @SystemApi public List<InetAddress> getValidatedPrivateDnsServers() { return Collections.unmodifiableList(mValidatedPrivateDnses); } @@ -636,7 +647,8 @@ public final class LinkProperties implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public void setTcpBufferSizes(String tcpBufferSizes) { mTcpBufferSizes = tcpBufferSizes; } @@ -648,7 +660,8 @@ public final class LinkProperties implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public String getTcpBufferSizes() { return mTcpBufferSizes; } @@ -699,7 +712,8 @@ public final class LinkProperties implements Parcelable { * * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public boolean removeRoute(RouteInfo route) { return route != null && Objects.equals(mIfaceName, route.getInterface()) && @@ -960,7 +974,8 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if there is an IPv4 address, {@code false} otherwise. * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public boolean hasIPv4Address() { for (LinkAddress address : mLinkAddresses) { if (address.getAddress() instanceof Inet4Address) { @@ -988,7 +1003,8 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if there is a global preferred IPv6 address, {@code false} otherwise. * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public boolean hasGlobalIPv6Address() { for (LinkAddress address : mLinkAddresses) { if (address.getAddress() instanceof Inet6Address && address.isGlobalPreferred()) { @@ -1020,7 +1036,8 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if there is an IPv6 default route, {@code false} otherwise. * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public boolean hasIPv6DefaultRoute() { for (RouteInfo r : mRoutes) { if (r.isIPv6Default()) { @@ -1099,6 +1116,8 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if the link is provisioned, {@code false} otherwise. * @hide */ + @TestApi + @SystemApi public boolean isIPv4Provisioned() { return (hasIPv4Address() && hasIPv4DefaultRoute() && @@ -1112,7 +1131,8 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if the link is provisioned, {@code false} otherwise. * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public boolean isIPv6Provisioned() { return (hasGlobalIPv6Address() && hasIPv6DefaultRoute() && @@ -1126,7 +1146,8 @@ public final class LinkProperties implements Parcelable { * @return {@code true} if the link is provisioned, {@code false} otherwise. * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public boolean isProvisioned() { return (isIPv4Provisioned() || isIPv6Provisioned()); } @@ -1138,7 +1159,8 @@ public final class LinkProperties implements Parcelable { * {@code false} otherwise. * @hide */ - @UnsupportedAppUsage + @TestApi + @SystemApi public boolean isReachable(InetAddress ip) { final List<RouteInfo> allRoutes = getAllRoutes(); // If we don't have a route to this IP address, it's not reachable. diff --git a/core/java/android/net/LinkPropertiesParcelable.aidl b/core/java/android/net/LinkPropertiesParcelable.aidl index b153dc70e1b8..6b52239b4168 100644 --- a/core/java/android/net/LinkPropertiesParcelable.aidl +++ b/core/java/android/net/LinkPropertiesParcelable.aidl @@ -35,5 +35,4 @@ parcelable LinkPropertiesParcelable { int mtu; String tcpBufferSizes; IpPrefixParcelable nat64Prefix; - LinkPropertiesParcelable[] stackedLinks; }
\ No newline at end of file diff --git a/core/java/android/net/MacAddress.java b/core/java/android/net/MacAddress.java index 058cb941bfbc..c2b7d2c71b32 100644 --- a/core/java/android/net/MacAddress.java +++ b/core/java/android/net/MacAddress.java @@ -52,6 +52,8 @@ public final class MacAddress implements Parcelable { /** * 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 bf2344d4a9f6..2c831de72051 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 @@ public class Network implements Parcelable { * * @hide */ + @TestApi + @SystemApi public Network getPrivateDnsBypassingCopy() { return new Network(netId, true); } diff --git a/core/java/android/net/NetworkCapabilities.java b/core/java/android/net/NetworkCapabilities.java index 1b44c920a205..7e9bda14b199 100644 --- a/core/java/android/net/NetworkCapabilities.java +++ b/core/java/android/net/NetworkCapabilities.java @@ -712,6 +712,7 @@ public final class NetworkCapabilities implements Parcelable { * @hide */ @TestApi + @SystemApi public @Transport int[] getTransportTypes() { return BitUtils.unpackBits(mTransportTypes); } @@ -1312,6 +1313,8 @@ public final class NetworkCapabilities implements Parcelable { * * @hide */ + @TestApi + @SystemApi public boolean satisfiedByNetworkCapabilities(NetworkCapabilities nc) { return satisfiedByNetworkCapabilities(nc, false); } diff --git a/core/java/android/net/NetworkStack.java b/core/java/android/net/NetworkStack.java index 2eac6de56346..af043eeccde2 100644 --- a/core/java/android/net/NetworkStack.java +++ b/core/java/android/net/NetworkStack.java @@ -50,6 +50,8 @@ public class NetworkStack { 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 @@ public class NetworkStack { @GuardedBy("mPendingNetStackRequests") private INetworkStackConnector mConnector; + private volatile boolean mNetworkStackStartRequested = false; + private interface NetworkStackCallback { void onNetworkStackConnected(INetworkStackConnector connector); } @@ -134,6 +138,7 @@ public class NetworkStack { * 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 @@ public class NetworkStack { } } - // 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 37ab9ffb1f5b..6bf2c67da990 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 @@ public final class RouteInfo implements Parcelable { /** 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 @@ public final class RouteInfo implements Parcelable { * * @hide */ + @TestApi + @SystemApi public int getType() { return mType; } @@ -362,9 +371,7 @@ public final class RouteInfo implements Parcelable { * ({@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 000000000000..7769ec2b65ac --- /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 000000000000..f077e3b77ac7 --- /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 000000000000..db01ae4d4d9c --- /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 5dabf35c4867..8601005c8af4 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 @@ import java.util.List; * 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 @@ public final class ApfProgramEvent implements Parcelable { @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 - - @UnsupportedAppUsage - public ApfProgramEvent() { + public final int flags; // Bitfield compound of FLAG_* constants + + 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 @@ public final class ApfProgramEvent implements Parcelable { 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 @@ public final class ApfProgramEvent implements Parcelable { out.writeInt(flags); } + /** @hide */ @Override public int describeContents() { return 0; @@ -96,6 +184,7 @@ public final class ApfProgramEvent implements Parcelable { 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 @@ public final class ApfProgramEvent implements Parcelable { } }; + /** @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 bb2a35c58667..844a1342a7fa 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 @@ import android.os.Parcelable; * 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 @@ public final class ApfStats implements Parcelable { 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 @@ public final class ApfStats implements Parcelable { out.writeInt(maxProgramSize); } + /** @hide */ @Override public int describeContents() { return 0; @@ -108,6 +263,7 @@ public final class ApfStats implements Parcelable { .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 98a7d7e8de9a..2a942eedcb47 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 @@ import android.os.Parcelable; * 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 @@ public final class DhcpClientEvent implements Parcelable { /** {@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 @@ public final class DhcpClientEvent implements Parcelable { 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 @@ public final class DhcpClientEvent implements Parcelable { 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 c6c06f0e59e9..18fde80b69b0 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 @@ import com.android.internal.util.MessageUtils; * 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 @@ public final class DhcpErrorEvent implements Parcelable { // 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 @@ public final class DhcpErrorEvent implements Parcelable { 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 @@ public final class DhcpErrorEvent implements Parcelable { } }; - @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 998f4ba40daa..c04fcdcadb10 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.Parcelable; 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 @@ import com.android.internal.util.BitUtils; * 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 @@ public class IpConnectivityLog { * @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 @@ public class IpConnectivityLog { * @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 @@ public class IpConnectivityLog { * @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 @@ public class IpConnectivityLog { * @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 @@ public class IpConnectivityLog { * @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 c47650f87544..013e3530a941 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.Retention; 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 final class IpManagerEvent implements Parcelable { 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 @@ public final class IpManagerEvent implements Parcelable { @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 @@ public final class IpManagerEvent implements Parcelable { 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 715c95e7270b..c7362979af61 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 @@ import com.android.internal.util.MessageUtils; * 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 @@ public final class IpReachabilityEvent implements Parcelable { // 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 @@ public final class IpReachabilityEvent implements Parcelable { 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 @@ public final class IpReachabilityEvent implements Parcelable { } }; - /** - * 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 cb82fbe71f6b..f5b2ff130494 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 @@ import java.lang.annotation.RetentionPolicy; /** * {@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 final class NetworkEvent implements Parcelable { public static final int NETWORK_CONSECUTIVE_DNS_TIMEOUT_FOUND = 12; + /** @hide */ @IntDef(value = { NETWORK_CONNECTED, NETWORK_VALIDATED, @@ -63,7 +68,9 @@ public final class NetworkEvent implements Parcelable { @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 @@ public final class NetworkEvent implements Parcelable { 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 c41881ca34fb..d308246f4d77 100644 --- a/core/java/android/net/metrics/RaEvent.java +++ b/core/java/android/net/metrics/RaEvent.java @@ -24,7 +24,7 @@ import android.os.Parcelable; * 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 12c2305dd78e..42e8aa6dc248 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 @@ import java.lang.annotation.RetentionPolicy; * 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 @@ public final class ValidationProbeEvent implements Parcelable { 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; - - public ValidationProbeEvent() { + /** @hide */ + public final int probeType; + /** @hide */ + public final @ReturnCode int returnCode; + + 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 @@ public final class ValidationProbeEvent implements Parcelable { 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 @@ public final class ValidationProbeEvent implements Parcelable { 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 final class ValidationProbeEvent implements Parcelable { } }; - 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 @@ public final class ValidationProbeEvent implements Parcelable { 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 8301286dca47..bbd76d2a1f12 100644 --- a/core/java/android/provider/Settings.java +++ b/core/java/android/provider/Settings.java @@ -10329,6 +10329,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final int CAPTIVE_PORTAL_MODE_IGNORE = 0; /** @@ -10337,6 +10339,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final int CAPTIVE_PORTAL_MODE_PROMPT = 1; /** @@ -10345,6 +10349,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final int CAPTIVE_PORTAL_MODE_AVOID = 2; /** @@ -10354,6 +10360,8 @@ public final class Settings { * 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 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final String CAPTIVE_PORTAL_HTTPS_URL = "captive_portal_https_url"; /** @@ -10390,6 +10400,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final String CAPTIVE_PORTAL_HTTP_URL = "captive_portal_http_url"; /** @@ -10398,6 +10410,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final String CAPTIVE_PORTAL_FALLBACK_URL = "captive_portal_fallback_url"; /** @@ -10406,6 +10420,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final String CAPTIVE_PORTAL_OTHER_FALLBACK_URLS = "captive_portal_other_fallback_urls"; @@ -10415,6 +10431,8 @@ public final class Settings { * by "@@,@@". * @hide */ + @SystemApi + @TestApi public static final String CAPTIVE_PORTAL_FALLBACK_PROBE_SPECS = "captive_portal_fallback_probe_specs"; @@ -10425,6 +10443,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final String CAPTIVE_PORTAL_USE_HTTPS = "captive_portal_use_https"; /** @@ -10433,6 +10453,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final String CAPTIVE_PORTAL_USER_AGENT = "captive_portal_user_agent"; /** @@ -10442,6 +10464,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final String DATA_STALL_CONSECUTIVE_DNS_TIMEOUT_THRESHOLD = "data_stall_consecutive_dns_timeout_threshold"; @@ -10450,6 +10474,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final String DATA_STALL_MIN_EVALUATE_INTERVAL = "data_stall_min_evaluate_interval"; @@ -10459,6 +10485,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final String DATA_STALL_VALID_DNS_TIME_THRESHOLD = "data_stall_valid_dns_time_threshold"; @@ -10468,6 +10496,8 @@ public final class Settings { * * @hide */ + @SystemApi + @TestApi public static final String DATA_STALL_EVALUATION_TYPE = "data_stall_evaluation_type"; /** diff --git a/packages/NetworkStack/src/com/android/server/NetworkStackService.java b/packages/NetworkStack/src/com/android/server/NetworkStackService.java index 057012de433a..cca71e759bdd 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_INVALID_ARGUMENT; 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 @@ public class NetworkStackService extends Service { @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 4077d93d700b..c8a8e1f8e3db 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 @@ public class NetworkMonitor extends StateMachine { 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 733f87393c32..82bf038073c7 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 final class PermissionUtil { */ 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 600a6ae0babd..5ef3fe49da5d 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.TAG_NONE; 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 f0379059a5f7..3351b25d0eec 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.LinkProperties; 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.system.PacketSocketAddress; 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.BufferUnderflowException; 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 @@ public class ApfFilter { 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 @@ public class ApfFilter { } 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 @@ public class ApfFilter { 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 @@ public class ApfFilter { 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 @@ public class ApfFilter { @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 @@ public class ApfFilter { @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 @@ public class ApfFilter { } 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 @@ public class ApfFilter { 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 @@ public class ApfFilter { * 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 a956cefd1235..15acc0ede8b2 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.Log; 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 @@ public class DhcpClient extends StateMachine { } 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 e6ddbbc95469..bef425a37da4 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 02e4f875230a..55dfcef81890 100644 --- a/services/net/java/android/net/ip/InterfaceController.java +++ b/services/net/java/android/net/ip/InterfaceController.java @@ -19,7 +19,6 @@ package android.net.ip; 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 0176dd496a33..b0c095756525 100644 --- a/services/net/java/android/net/ip/IpClient.java +++ b/services/net/java/android/net/ip/IpClient.java @@ -16,19 +16,18 @@ 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.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 +35,10 @@ import android.net.apf.ApfFilter; 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 +51,24 @@ import android.util.LocalLog; 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 +128,17 @@ public class IpClient extends StateMachine { } /** - * 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() {} - - // 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; - } + public static class Callback extends IpClientCallbacks {} - @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 +160,12 @@ public class IpClient extends StateMachine { // 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,289 +235,103 @@ public class IpClient extends StateMachine { } /** - * 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 static class ProvisioningConfiguration + extends android.net.shared.ProvisioningConfiguration { + public ProvisioningConfiguration(android.net.shared.ProvisioningConfiguration other) { + super(other); + } + /** + * @see android.net.shared.ProvisioningConfiguration.Builder + */ + 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() { - mConfig.mEnableIPv4 = false; + super.withoutIPv4(); return this; } + @Override public Builder withoutIPv6() { - mConfig.mEnableIPv6 = false; + super.withoutIPv6(); return this; } + @Override public Builder withoutMultinetworkPolicyTracker() { - mConfig.mUsingMultinetworkPolicyTracker = false; + super.withoutMultinetworkPolicyTracker(); return this; } + @Override public Builder withoutIpReachabilityMonitor() { - mConfig.mUsingIpReachabilityMonitor = false; + super.withoutIpReachabilityMonitor(); return this; } + @Override public Builder withPreDhcpAction() { - mConfig.mRequestedPreDhcpActionMs = DEFAULT_TIMEOUT_MS; + super.withPreDhcpAction(); return this; } + @Override public Builder withPreDhcpAction(int dhcpActionTimeoutMs) { - mConfig.mRequestedPreDhcpActionMs = dhcpActionTimeoutMs; - return this; - } - - public Builder withInitialConfiguration(InitialConfiguration initialConfig) { - mConfig.mInitialConfig = initialConfig; + super.withPreDhcpAction(dhcpActionTimeoutMs); return this; } + @Override public Builder withStaticConfiguration(StaticIpConfiguration staticConfig) { - mConfig.mStaticIpConfig = staticConfig; + super.withStaticConfiguration(staticConfig); return this; } + @Override public Builder withApfCapabilities(ApfCapabilities apfCapabilities) { - mConfig.mApfCapabilities = apfCapabilities; + super.withApfCapabilities(apfCapabilities); return this; } + @Override public Builder withProvisioningTimeoutMs(int timeoutMs) { - mConfig.mProvisioningTimeoutMs = timeoutMs; + super.withProvisioningTimeoutMs(timeoutMs); return this; } + @Override public Builder withRandomMacAddress() { - mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_EUI64; + super.withRandomMacAddress(); return this; } + @Override public Builder withStableMacAddress() { - mConfig.mIPv6AddrGenMode = INetd.IPV6_ADDR_GEN_MODE_STABLE_PRIVACY; + super.withStableMacAddress(); return this; } + @Override public Builder withNetwork(Network network) { - mConfig.mNetwork = network; + super.withNetwork(network); return this; } + @Override public Builder withDisplayName(String displayName) { - mConfig.mDisplayName = displayName; + super.withDisplayName(displayName); return this; } + @Override 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; - } - - /** - * @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. - */ - public boolean isProvisionedBy(List<LinkAddress> addresses, List<RouteInfo> routes) { - if (ipAddresses.isEmpty()) { - return false; - } - - for (LinkAddress addr : ipAddresses) { - if (!any(addresses, (addrSeen) -> addr.isSameAddressAs(addrSeen))) { - return false; - } - } - - if (routes != null) { - for (IpPrefix prefix : directlyConnectedRoutes) { - if (!any(routes, (routeSeen) -> isDirectlyConnectedRoute(routeSeen, prefix))) { - return false; - } - } - } - - return true; - } - - private static boolean isDirectlyConnectedRoute(RouteInfo route, IpPrefix prefix) { - return !route.hasGateway() && prefix.equals(route.getDestination()); - } - - private static boolean isPrefixLengthCompliant(LinkAddress addr) { - return addr.isIPv4() || isCompliantIPv6PrefixLength(addr.getPrefixLength()); - } - - private static boolean isPrefixLengthCompliant(IpPrefix prefix) { - return prefix.isIPv4() || isCompliantIPv6PrefixLength(prefix.getPrefixLength()); - } - - private static boolean isCompliantIPv6PrefixLength(int prefixLength) { - return (NetworkConstants.RFC6177_MIN_PREFIX_LENGTH <= prefixLength) - && (prefixLength <= NetworkConstants.RFC7421_PREFIX_LENGTH); - } - - private static boolean isIPv6DefaultRoute(IpPrefix prefix) { - return prefix.getAddress().equals(Inet6Address.ANY); - } - - private static boolean isIPv6GUA(LinkAddress addr) { - return addr.isIPv6() && addr.isGlobalPreferred(); - } } public static final String DUMP_ARG = "ipclient"; @@ -600,7 +342,7 @@ public class IpClient extends StateMachine { 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; @@ -628,6 +370,11 @@ public class IpClient extends StateMachine { private static final int IMMEDIATE_FAILURE_DURATION = 0; + private static final int PROV_CHANGE_STILL_NOT_PROVISIONED = 1; + private static final int PROV_CHANGE_LOST_PROVISIONING = 2; + private static final int PROV_CHANGE_GAINED_PROVISIONING = 3; + private static final int PROV_CHANGE_STILL_PROVISIONED = 4; + private final State mStoppedState = new StoppedState(); private final State mStoppingState = new StoppingState(); private final State mStartedState = new StartedState(); @@ -638,7 +385,7 @@ public class IpClient extends StateMachine { 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 +404,7 @@ public class IpClient extends StateMachine { * 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 +433,15 @@ public class IpClient extends StateMachine { 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 +449,18 @@ public class IpClient extends StateMachine { * 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 +502,7 @@ public class IpClient extends StateMachine { return; } - final String msg = "interfaceAdded(" + iface +")"; + final String msg = "interfaceAdded(" + iface + ")"; logMsg(msg); } @@ -768,13 +520,13 @@ public class IpClient extends StateMachine { 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 +547,64 @@ public class IpClient extends StateMachine { 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 +633,9 @@ public class IpClient extends StateMachine { 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 +656,10 @@ public class IpClient extends StateMachine { 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 +673,7 @@ public class IpClient extends StateMachine { } mCallback.setNeighborDiscoveryOffload(true); - sendMessage(CMD_START, new ProvisioningConfiguration(req)); + sendMessage(CMD_START, new android.net.shared.ProvisioningConfiguration(req)); } // TODO: Delete this. @@ -874,21 +684,37 @@ public class IpClient extends StateMachine { } 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 +747,9 @@ public class IpClient extends StateMachine { 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 +759,7 @@ public class IpClient extends StateMachine { // 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; @@ -1086,18 +915,18 @@ public class IpClient extends StateMachine { // object that is a correct and complete assessment of what changed, taking // account of the asymmetries described in the comments in this function. // Then switch to using it everywhere (IpReachabilityMonitor, etc.). - private ProvisioningChange compareProvisioning(LinkProperties oldLp, LinkProperties newLp) { - ProvisioningChange delta; + private int compareProvisioning(LinkProperties oldLp, LinkProperties newLp) { + int delta; InitialConfiguration config = mConfiguration != null ? mConfiguration.mInitialConfig : null; final boolean wasProvisioned = isProvisioned(oldLp, config); final boolean isProvisioned = isProvisioned(newLp, config); if (!wasProvisioned && isProvisioned) { - delta = ProvisioningChange.GAINED_PROVISIONING; + delta = PROV_CHANGE_GAINED_PROVISIONING; } else if (wasProvisioned && isProvisioned) { - delta = ProvisioningChange.STILL_PROVISIONED; + delta = PROV_CHANGE_STILL_PROVISIONED; } else if (!wasProvisioned && !isProvisioned) { - delta = ProvisioningChange.STILL_NOT_PROVISIONED; + delta = PROV_CHANGE_STILL_NOT_PROVISIONED; } else { // (wasProvisioned && !isProvisioned) // @@ -1109,7 +938,7 @@ public class IpClient extends StateMachine { // that to be a network without DNS servers and connect anyway. // // See the comment below. - delta = ProvisioningChange.LOST_PROVISIONING; + delta = PROV_CHANGE_LOST_PROVISIONING; } final boolean lostIPv6 = oldLp.isIPv6Provisioned() && !newLp.isIPv6Provisioned(); @@ -1145,7 +974,7 @@ public class IpClient extends StateMachine { // delta will never be LOST_PROVISIONING. So check for loss of // provisioning here too. if (lostIPv4Address || (lostIPv6 && !ignoreIPv6ProvisioningLoss)) { - delta = ProvisioningChange.LOST_PROVISIONING; + delta = PROV_CHANGE_LOST_PROVISIONING; } // Additionally: @@ -1154,28 +983,34 @@ public class IpClient extends StateMachine { // IPv6 default route then also consider the loss of that default route // to be a loss of provisioning. See b/27962810. if (oldLp.hasGlobalIPv6Address() && (lostIPv6Router && !ignoreIPv6ProvisioningLoss)) { - delta = ProvisioningChange.LOST_PROVISIONING; + delta = PROV_CHANGE_LOST_PROVISIONING; } return delta; } - private void dispatchCallback(ProvisioningChange delta, LinkProperties newLp) { + private void dispatchCallback(int delta, LinkProperties newLp) { switch (delta) { - case GAINED_PROVISIONING: - if (DBG) { Log.d(mTag, "onProvisioningSuccess()"); } + case PROV_CHANGE_GAINED_PROVISIONING: + if (DBG) { + Log.d(mTag, "onProvisioningSuccess()"); + } recordMetric(IpManagerEvent.PROVISIONING_OK); mCallback.onProvisioningSuccess(newLp); break; - case LOST_PROVISIONING: - if (DBG) { Log.d(mTag, "onProvisioningFailure()"); } + case PROV_CHANGE_LOST_PROVISIONING: + 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; } @@ -1184,7 +1019,7 @@ public class IpClient extends StateMachine { // Updates all IpClient-related state concerned with LinkProperties. // Returns a ProvisioningChange for possibly notifying other interested // parties that are not fronted by IpClient. - private ProvisioningChange setLinkProperties(LinkProperties newLp) { + private int setLinkProperties(LinkProperties newLp) { if (mApfFilter != null) { mApfFilter.setLinkProperties(newLp); } @@ -1192,10 +1027,10 @@ public class IpClient extends StateMachine { mIpReachabilityMonitor.updateLinkProperties(newLp); } - ProvisioningChange delta = compareProvisioning(mLinkProperties, newLp); + int delta = compareProvisioning(mLinkProperties, newLp); mLinkProperties = new LinkProperties(newLp); - if (delta == ProvisioningChange.GAINED_PROVISIONING) { + if (delta == PROV_CHANGE_GAINED_PROVISIONING) { // TODO: Add a proper ProvisionedState and cancel the alarm in // its enter() method. mProvisioningTimeoutAlarm.cancel(); @@ -1291,17 +1126,17 @@ public class IpClient extends StateMachine { if (Objects.equals(newLp, mLinkProperties)) { return true; } - final ProvisioningChange delta = setLinkProperties(newLp); + final int delta = setLinkProperties(newLp); if (sendCallbacks) { dispatchCallback(delta, newLp); } - return (delta != ProvisioningChange.LOST_PROVISIONING); + return (delta != PROV_CHANGE_LOST_PROVISIONING); } private void handleIPv4Success(DhcpResults dhcpResults) { mDhcpResults = new DhcpResults(dhcpResults); final LinkProperties newLp = assembleLinkProperties(); - final ProvisioningChange delta = setLinkProperties(newLp); + final int delta = setLinkProperties(newLp); if (DBG) { Log.d(mTag, "onNewDhcpResults(" + Objects.toString(dhcpResults) + ")"); @@ -1319,7 +1154,9 @@ public class IpClient extends StateMachine { // 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(); @@ -1327,7 +1164,7 @@ public class IpClient extends StateMachine { private void handleProvisioningFailure() { final LinkProperties newLp = assembleLinkProperties(); - ProvisioningChange delta = setLinkProperties(newLp); + int delta = setLinkProperties(newLp); // If we've gotten here and we're still not provisioned treat that as // a total loss of provisioning. // @@ -1336,12 +1173,12 @@ public class IpClient extends StateMachine { // timeout expired. // // Regardless: GAME OVER. - if (delta == ProvisioningChange.STILL_NOT_PROVISIONED) { - delta = ProvisioningChange.LOST_PROVISIONING; + if (delta == PROV_CHANGE_STILL_NOT_PROVISIONED) { + delta = PROV_CHANGE_LOST_PROVISIONING; } dispatchCallback(delta, newLp); - if (delta == ProvisioningChange.LOST_PROVISIONING) { + if (delta == PROV_CHANGE_LOST_PROVISIONING) { transitionTo(mStoppingState); } } @@ -1372,9 +1209,9 @@ public class IpClient extends StateMachine { } 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 +1229,10 @@ public class IpClient extends StateMachine { // 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 +1300,7 @@ public class IpClient extends StateMachine { break; case CMD_START: - mConfiguration = (ProvisioningConfiguration) msg.obj; + mConfiguration = (android.net.shared.ProvisioningConfiguration) msg.obj; transitionTo(mStartedState); break; @@ -1542,8 +1379,8 @@ public class IpClient extends StateMachine { 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 +1435,7 @@ public class IpClient extends StateMachine { } private boolean readyToProceed() { - return (!mLinkProperties.hasIPv4Address() && - !mLinkProperties.hasGlobalIPv6Address()); + return (!mLinkProperties.hasIPv4Address() && !mLinkProperties.hasGlobalIPv6Address()); } } @@ -1650,7 +1486,7 @@ public class IpClient extends StateMachine { if (mConfiguration.mUsingMultinetworkPolicyTracker) { mMultinetworkPolicyTracker = new MultinetworkPolicyTracker( mContext, getHandler(), - () -> { mLog.log("OBSERVED AvoidBadWifi changed"); }); + () -> mLog.log("OBSERVED AvoidBadWifi changed")); mMultinetworkPolicyTracker.start(); } @@ -1711,8 +1547,8 @@ public class IpClient extends StateMachine { if (!mDhcpActionInFlight) { mCallback.onPreDhcpAction(); mDhcpActionInFlight = true; - final long alarmTime = SystemClock.elapsedRealtime() + - mConfiguration.mRequestedPreDhcpActionMs; + final long alarmTime = SystemClock.elapsedRealtime() + + mConfiguration.mRequestedPreDhcpActionMs; mDhcpActionTimeoutAlarm.schedule(alarmTime); } } @@ -1814,7 +1650,7 @@ public class IpClient extends StateMachine { mDhcpClient.sendMessage(DhcpClient.EVENT_LINKADDRESS_CONFIGURED); } else { logError("Failed to set IPv4 address."); - dispatchCallback(ProvisioningChange.LOST_PROVISIONING, + dispatchCallback(PROV_CHANGE_LOST_PROVISIONING, new LinkProperties(mLinkProperties)); transitionTo(mStoppingState); } @@ -1881,16 +1717,18 @@ public class IpClient extends StateMachine { } 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 +1757,7 @@ public class IpClient extends StateMachine { 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 000000000000..0aec10149b23 --- /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 2eb36a22de55..000000000000 --- 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 9512f1be66ef..34bf4b63a883 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.NetlinkMessage; 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 7e02a2881da8..29e2f0c308d5 100644 --- a/services/net/java/android/net/ip/IpReachabilityMonitor.java +++ b/services/net/java/android/net/ip/IpReachabilityMonitor.java @@ -16,11 +16,13 @@ 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 +35,19 @@ import android.os.Handler; 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; /** @@ -314,10 +307,11 @@ public class IpReachabilityMonitor { } } - final ProvisioningChange delta = LinkProperties.compareProvisioning( - mLinkProperties, whatIfLp); + final boolean lostProvisioning = + (mLinkProperties.isIPv4Provisioned() && !whatIfLp.isIPv4Provisioned()) + || (mLinkProperties.isIPv6Provisioned() && !whatIfLp.isIPv6Provisioned()); - if (delta == ProvisioningChange.LOST_PROVISIONING) { + if (lostProvisioning) { final String logMsg = "FAILURE: LOST_PROVISIONING, " + event; Log.w(TAG, logMsg); if (mCallback != null) { @@ -326,7 +320,7 @@ public class IpReachabilityMonitor { mCallback.notifyLost(ip, logMsg); } } - logNudFailed(delta); + logNudFailed(lostProvisioning); } private boolean avoidingBadLinks() { @@ -376,11 +370,21 @@ public class IpReachabilityMonitor { mMetricsLog.log(mInterfaceParams.name, new IpReachabilityEvent(eventType)); } - private void logNudFailed(ProvisioningChange delta) { + private void logNudFailed(boolean lostProvisioning) { 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, lostProvisioning); 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 d197d017acce..8e3023bc08d4 100644 --- a/services/net/java/android/net/ip/RouterAdvertisementDaemon.java +++ b/services/net/java/android/net/ip/RouterAdvertisementDaemon.java @@ -18,11 +18,15 @@ package android.net.ip; 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 android.util.Log; 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.net.UnknownHostException; 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 4ee64321f262..6978739886c3 100644 --- a/services/net/java/android/net/netlink/ConntrackMessage.java +++ b/services/net/java/android/net/netlink/ConntrackMessage.java @@ -16,22 +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 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/shared/LinkPropertiesParcelableUtil.java b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java index d5213dfcebf8..51d955d5c2ad 100644 --- a/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java +++ b/services/net/java/android/net/shared/LinkPropertiesParcelableUtil.java @@ -182,9 +182,6 @@ public final class LinkPropertiesParcelableUtil { parcel.mtu = lp.getMtu(); parcel.tcpBufferSizes = lp.getTcpBufferSizes(); parcel.nat64Prefix = toStableParcelable(lp.getNat64Prefix()); - parcel.stackedLinks = toParcelableArray( - lp.getStackedLinks(), LinkPropertiesParcelableUtil::toStableParcelable, - LinkPropertiesParcelable.class); return parcel; } @@ -216,9 +213,6 @@ public final class LinkPropertiesParcelableUtil { lp.setMtu(parcel.mtu); lp.setTcpBufferSizes(parcel.tcpBufferSizes); lp.setNat64Prefix(fromStableParcelable(parcel.nat64Prefix)); - for (LinkPropertiesParcelable stackedLink : parcel.stackedLinks) { - lp.addStackedLink(fromStableParcelable(stackedLink)); - } return lp; } } diff --git a/services/net/java/android/net/shared/ProvisioningConfiguration.java b/services/net/java/android/net/shared/ProvisioningConfiguration.java index d995d1b1e622..f9370653411e 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.Network; 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 4951400eed84..ec833b07e806 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 a4b2fbb6d963..7b060da01a89 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 @@ public class InterfaceParams { 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 6e69ff56c723..80b2c2705038 100644 --- a/services/net/java/android/net/util/NetdService.java +++ b/services/net/java/android/net/util/NetdService.java @@ -19,7 +19,6 @@ package android.net.util; 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 564effe05e38..27a5121a148c 100644 --- a/telephony/java/android/telephony/ModemInfo.java +++ b/telephony/java/android/telephony/ModemInfo.java @@ -32,6 +32,11 @@ public class ModemInfo implements Parcelable { 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 c9d0eb11316d..6724c034fc23 100644 --- a/telephony/java/android/telephony/SubscriptionManager.java +++ b/telephony/java/android/telephony/SubscriptionManager.java @@ -1173,17 +1173,33 @@ public class SubscriptionManager { @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 @@ public class SubscriptionManager { 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 2ebe87067be3..8e58b02de23d 100644 --- a/telephony/java/com/android/internal/telephony/RILConstants.java +++ b/telephony/java/com/android/internal/telephony/RILConstants.java @@ -422,6 +422,7 @@ public interface RILConstants { 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 151b5594216a..3c3e7ce3b12a 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.LinkProperties; 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.ErrnoException; 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.net.InetAddress; 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 @@ public class ApfTest { 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 @@ public class ApfTest { 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 @@ public class ApfTest { 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 @@ public class ApfTest { } 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 @@ public class ApfTest { } } - 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 cba3c6572c46..a2dcfef50a49 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.junit.Assert.fail; 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.LinkAddress; 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.filters.SmallTest; 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 org.mockito.MockitoAnnotations; 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 @@ public class IpClientTest { @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 class IpClientTest { 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/shared/LinkPropertiesParcelableUtilTest.java b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java index 6f711c0b5743..b6d01dbc1cac 100644 --- a/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java +++ b/tests/net/java/android/net/shared/LinkPropertiesParcelableUtilTest.java @@ -48,61 +48,52 @@ public class LinkPropertiesParcelableUtilTest { private LinkProperties mLinkProperties; private static final String TEST_LINKPROPS_IFACE = "TEST_IFACE"; - private static final String TEST_STACKED_LINK_1_IFACE = "TEST_STACKED_IFACE_1"; - private static final String TEST_STACKED_LINK_2_IFACE = "TEST_STACKED_IFACE_2"; @Before public void setUp() { - mLinkProperties = makeLinkProperties(TEST_LINKPROPS_IFACE); - mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_1_IFACE)); - mLinkProperties.addStackedLink(makeLinkProperties(TEST_STACKED_LINK_2_IFACE)); - } - - private static LinkProperties makeLinkProperties(String iface) { - final LinkProperties lp = new LinkProperties(); - lp.setInterfaceName(iface); - lp.setLinkAddresses(Arrays.asList( + mLinkProperties = new LinkProperties(); + mLinkProperties.setInterfaceName(TEST_LINKPROPS_IFACE); + mLinkProperties.setLinkAddresses(Arrays.asList( new LinkAddress(InetAddresses.parseNumericAddress("192.168.0.42"), 16), new LinkAddress(InetAddresses.parseNumericAddress("2001:db8::7"), 42))); - lp.setDnsServers(Arrays.asList( + mLinkProperties.setDnsServers(Arrays.asList( InetAddresses.parseNumericAddress("2001:db8::42"), InetAddresses.parseNumericAddress("192.168.1.1") )); - lp.setValidatedPrivateDnsServers(Arrays.asList( + mLinkProperties.setValidatedPrivateDnsServers(Arrays.asList( InetAddresses.parseNumericAddress("2001:db8::43"), InetAddresses.parseNumericAddress("192.168.42.43") )); - lp.setPcscfServers(Arrays.asList( + mLinkProperties.setPcscfServers(Arrays.asList( InetAddresses.parseNumericAddress("2001:db8::47"), InetAddresses.parseNumericAddress("192.168.42.47") )); - lp.setUsePrivateDns(true); - lp.setPrivateDnsServerName("test.example.com"); - lp.setDomains("test1.example.com,test2.example.com"); - lp.addRoute(new RouteInfo( + mLinkProperties.setUsePrivateDns(true); + mLinkProperties.setPrivateDnsServerName("test.example.com"); + mLinkProperties.setDomains("test1.example.com,test2.example.com"); + mLinkProperties.addRoute(new RouteInfo( new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::44"), 45), InetAddresses.parseNumericAddress("2001:db8::45"), - iface, + TEST_LINKPROPS_IFACE, RouteInfo.RTN_UNICAST )); - lp.addRoute(new RouteInfo( + mLinkProperties.addRoute(new RouteInfo( new IpPrefix(InetAddresses.parseNumericAddress("192.168.44.45"), 16), InetAddresses.parseNumericAddress("192.168.45.1"), - iface, + TEST_LINKPROPS_IFACE, RouteInfo.RTN_THROW )); - lp.setHttpProxy(new ProxyInfo("test3.example.com", 8000, + mLinkProperties.setHttpProxy(new ProxyInfo("test3.example.com", 8000, "excl1.example.com,excl2.example.com")); - lp.setMtu(5000); - lp.setTcpBufferSizes("1,2,3,4,5,6"); - lp.setNat64Prefix(new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::48"), 96)); + mLinkProperties.setMtu(5000); + mLinkProperties.setTcpBufferSizes("1,2,3,4,5,6"); + mLinkProperties.setNat64Prefix( + new IpPrefix(InetAddresses.parseNumericAddress("2001:db8::48"), 96)); // Verify that this test does not miss any new field added later. // If any added field is not included in LinkProperties#equals, assertLinkPropertiesEquals // must also be updated. assertFieldCountEquals(14, LinkProperties.class); - - return lp; } @Test @@ -186,7 +177,7 @@ public class LinkPropertiesParcelableUtilTest { private static void assertLinkPropertiesEquals(LinkProperties expected, LinkProperties actual) { assertEquals(expected, actual); - // LinkProperties equals() does not include stacked links - assertEquals(expected.getStackedLinks(), actual.getStackedLinks()); + // Equality on stacked links is not tested as they should not be passed to processes using + // LinkPropertiesParcelable. } } diff --git a/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java b/tests/net/java/android/net/util/ConnectivityPacketSummaryTest.java index f9b7ec8f0322..dfaf52a953c7 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 882babff4aee..1c264184db4c 100644 --- a/tests/net/java/com/android/server/ConnectivityServiceTest.java +++ b/tests/net/java/com/android/server/ConnectivityServiceTest.java @@ -4775,7 +4775,7 @@ public class ConnectivityServiceTest { // 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 @@ public class ConnectivityServiceTest { // 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 @@ public class ConnectivityServiceTest { 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 8359fe2a8fb4..1a0cb745ff0c 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 @@ package com.android.server.connectivity; 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.IIpConnectivityMetrics; 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 android.util.Base64; 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.Comparator; 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 @@ public class IpConnectivityMetricsTest { @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 @@ public class IpConnectivityMetricsTest { 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; - - ValidationProbeEvent validationEv = new ValidationProbeEvent(); - validationEv.durationMs = 40730; - validationEv.probeType = ValidationProbeEvent.PROBE_HTTP; - validationEv.returnCode = 204; - + 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(); + + 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 @@ public class IpConnectivityMetricsTest { " validation_probe_event <", " latency_ms: 40730", " probe_result: 204", - " probe_type: 1", + " probe_type: 257", " >", ">", "events <", diff --git a/tools/apilint/apilint.py b/tools/apilint/apilint.py index 70a47cf42755..75c3eba7765b 100644 --- a/tools/apilint/apilint.py +++ b/tools/apilint/apilint.py @@ -26,7 +26,7 @@ $ git blame api/current.txt -t -e > /tmp/currentblame.txt $ apilint.py /tmp/currentblame.txt previous.txt --no-color """ -import re, sys, collections, traceback, argparse +import re, sys, collections, traceback, argparse, itertools BLACK, RED, GREEN, YELLOW, BLUE, MAGENTA, CYAN, WHITE = range(8) @@ -50,38 +50,37 @@ def format(fg=None, bg=None, bright=False, bold=False, dim=False, reset=False): return "\033[%sm" % (";".join(codes)) -def ident(raw): - """Strips superficial signature changes, giving us a strong key that - can be used to identify members across API levels.""" - raw = raw.replace(" deprecated ", " ") - raw = raw.replace(" synchronized ", " ") - raw = raw.replace(" final ", " ") - raw = re.sub("<.+?>", "", raw) - if " throws " in raw: - raw = raw[:raw.index(" throws ")] - return raw - - class Field(): - def __init__(self, clazz, line, raw, blame): + def __init__(self, clazz, line, raw, blame, sig_format = 1): self.clazz = clazz self.line = line self.raw = raw.strip(" {;") self.blame = blame - raw = raw.split() - self.split = list(raw) + if sig_format == 2: + V2LineParser(raw).parse_into_field(self) + elif sig_format == 1: + # drop generics for now; may need multiple passes + raw = re.sub("<[^<]+?>", "", raw) + raw = re.sub("<[^<]+?>", "", raw) - for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]: - while r in raw: raw.remove(r) + raw = raw.split() + self.split = list(raw) - self.typ = raw[0] - self.name = raw[1].strip(";") - if len(raw) >= 4 and raw[2] == "=": - self.value = raw[3].strip(';"') - else: - self.value = None - self.ident = ident(self.raw) + for r in ["field", "volatile", "transient", "public", "protected", "static", "final", "deprecated"]: + while r in raw: raw.remove(r) + + # ignore annotations for now + raw = [ r for r in raw if not r.startswith("@") ] + + self.typ = raw[0] + self.name = raw[1].strip(";") + if len(raw) >= 4 and raw[2] == "=": + self.value = raw[3].strip(';"') + else: + self.value = None + + self.ident = "-".join((self.typ, self.name, self.value or "")) def __hash__(self): return hash(self.raw) @@ -89,34 +88,55 @@ class Field(): def __repr__(self): return self.raw - class Method(): - def __init__(self, clazz, line, raw, blame): + def __init__(self, clazz, line, raw, blame, sig_format = 1): self.clazz = clazz self.line = line self.raw = raw.strip(" {;") self.blame = blame - # drop generics for now - raw = re.sub("<.+?>", "", raw) - - raw = re.split("[\s(),;]+", raw) - for r in ["", ";"]: - while r in raw: raw.remove(r) - self.split = list(raw) + if sig_format == 2: + V2LineParser(raw).parse_into_method(self) + elif sig_format == 1: + # drop generics for now; may need multiple passes + raw = re.sub("<[^<]+?>", "", raw) + raw = re.sub("<[^<]+?>", "", raw) + + # handle each clause differently + raw_prefix, raw_args, _, raw_throws = re.match(r"(.*?)\((.*?)\)( throws )?(.*?);$", raw).groups() + + # parse prefixes + raw = re.split("[\s]+", raw_prefix) + for r in ["", ";"]: + while r in raw: raw.remove(r) + self.split = list(raw) + + for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default", "operator", "synchronized"]: + while r in raw: raw.remove(r) + + self.typ = raw[0] + self.name = raw[1] + + # parse args + self.args = [] + for arg in re.split(",\s*", raw_args): + arg = re.split("\s", arg) + # ignore annotations for now + arg = [ a for a in arg if not a.startswith("@") ] + if len(arg[0]) > 0: + self.args.append(arg[0]) + + # parse throws + self.throws = [] + for throw in re.split(",\s*", raw_throws): + self.throws.append(throw) + else: + raise ValueError("Unknown signature format: " + sig_format) - for r in ["method", "public", "protected", "static", "final", "deprecated", "abstract", "default"]: - while r in raw: raw.remove(r) + self.ident = "-".join((self.typ, self.name, "-".join(self.args))) - self.typ = raw[0] - self.name = raw[1] - self.args = [] - self.throws = [] - target = self.args - for r in raw[2:]: - if r == "throws": target = self.throws - else: target.append(r) - self.ident = ident(self.raw) + def sig_matches(self, typ, name, args): + return typ == self.typ and name == self.name and args == self.args def __hash__(self): return hash(self.raw) @@ -126,7 +146,7 @@ class Method(): class Class(): - def __init__(self, pkg, line, raw, blame): + def __init__(self, pkg, line, raw, blame, sig_format = 1): self.pkg = pkg self.line = line self.raw = raw.strip(" {;") @@ -135,27 +155,51 @@ class Class(): self.fields = [] self.methods = [] - raw = raw.split() - self.split = list(raw) - if "class" in raw: - self.fullname = raw[raw.index("class")+1] - elif "interface" in raw: - self.fullname = raw[raw.index("interface")+1] - else: - raise ValueError("Funky class type %s" % (self.raw)) + if sig_format == 2: + V2LineParser(raw).parse_into_class(self) + elif sig_format == 1: + # drop generics for now; may need multiple passes + raw = re.sub("<[^<]+?>", "", raw) + raw = re.sub("<[^<]+?>", "", raw) + + raw = raw.split() + self.split = list(raw) + if "class" in raw: + self.fullname = raw[raw.index("class")+1] + elif "interface" in raw: + self.fullname = raw[raw.index("interface")+1] + elif "@interface" in raw: + self.fullname = raw[raw.index("@interface")+1] + else: + raise ValueError("Funky class type %s" % (self.raw)) - if "extends" in raw: - self.extends = raw[raw.index("extends")+1] - self.extends_path = self.extends.split(".") + if "extends" in raw: + self.extends = raw[raw.index("extends")+1] + else: + self.extends = None + + if "implements" in raw: + self.implements = raw[raw.index("implements")+1] + else: + self.implements = None else: - self.extends = None - self.extends_path = [] + raise ValueError("Unknown signature format: " + sig_format) self.fullname = self.pkg.name + "." + self.fullname self.fullname_path = self.fullname.split(".") + if self.extends is not None: + self.extends_path = self.extends.split(".") + else: + self.extends_path = [] + self.name = self.fullname[self.fullname.rindex(".")+1:] + def merge_from(self, other): + self.ctors.extend(other.ctors) + self.fields.extend(other.fields) + self.methods.extend(other.methods) + def __hash__(self): return hash((self.raw, tuple(self.ctors), tuple(self.fields), tuple(self.methods))) @@ -176,15 +220,386 @@ class Package(): def __repr__(self): return self.raw +class V2Tokenizer(object): + __slots__ = ["raw"] + + DELIMITER = re.compile(r'\s+|[()@<>;,={}/"!?]|\[\]|\.\.\.') + STRING_SPECIAL = re.compile(r'["\\]') + + def __init__(self, raw): + self.raw = raw + + def tokenize(self): + tokens = [] + current = 0 + raw = self.raw + length = len(raw) + + while current < length: + while current < length: + start = current + match = V2Tokenizer.DELIMITER.search(raw, start) + if match is not None: + match_start = match.start() + if match_start == current: + end = match.end() + else: + end = match_start + else: + end = length -def _parse_stream(f, clazz_cb=None): - line = 0 + token = raw[start:end] + current = end + + if token == "" or token[0] == " ": + continue + else: + break + + if token == "@": + if raw[start:start+11] == "@interface ": + current = start + 11 + tokens.append("@interface") + continue + elif token == '/': + if raw[start:start+2] == "//": + current = length + continue + elif token == '"': + current, string_token = self.tokenize_string(raw, length, current) + tokens.append(token + string_token) + continue + + tokens.append(token) + + return tokens + + def tokenize_string(self, raw, length, current): + start = current + end = length + while start < end: + match = V2Tokenizer.STRING_SPECIAL.search(raw, start) + if match: + if match.group() == '"': + end = match.end() + break + elif match.group() == '\\': + # ignore whatever is after the slash + start += 2 + else: + raise ValueError("Unexpected match: `%s`" % (match.group())) + else: + raise ValueError("Unexpected EOF tokenizing string: `%s`" % (raw[current - 1:],)) + + token = raw[current:end] + return end, token + +class V2LineParser(object): + __slots__ = ["tokenized", "current", "len"] + + FIELD_KINDS = ("field", "property", "enum_constant") + MODIFIERS = set("public protected internal private abstract default static final transient volatile synchronized native operator sealed strictfp infix inline suspend vararg".split()) + JAVA_LANG_TYPES = set("AbstractMethodError AbstractStringBuilder Appendable ArithmeticException ArrayIndexOutOfBoundsException ArrayStoreException AssertionError AutoCloseable Boolean BootstrapMethodError Byte Character CharSequence Class ClassCastException ClassCircularityError ClassFormatError ClassLoader ClassNotFoundException Cloneable CloneNotSupportedException Comparable Compiler Deprecated Double Enum EnumConstantNotPresentException Error Exception ExceptionInInitializerError Float FunctionalInterface IllegalAccessError IllegalAccessException IllegalArgumentException IllegalMonitorStateException IllegalStateException IllegalThreadStateException IncompatibleClassChangeError IndexOutOfBoundsException InheritableThreadLocal InstantiationError InstantiationException Integer InternalError InterruptedException Iterable LinkageError Long Math NegativeArraySizeException NoClassDefFoundError NoSuchFieldError NoSuchFieldException NoSuchMethodError NoSuchMethodException NullPointerException Number NumberFormatException Object OutOfMemoryError Override Package package-info.java Process ProcessBuilder ProcessEnvironment ProcessImpl Readable ReflectiveOperationException Runnable Runtime RuntimeException RuntimePermission SafeVarargs SecurityException SecurityManager Short StackOverflowError StackTraceElement StrictMath String StringBuffer StringBuilder StringIndexOutOfBoundsException SuppressWarnings System Thread ThreadDeath ThreadGroup ThreadLocal Throwable TypeNotPresentException UNIXProcess UnknownError UnsatisfiedLinkError UnsupportedClassVersionError UnsupportedOperationException VerifyError VirtualMachineError Void".split()) + + def __init__(self, raw): + self.tokenized = V2Tokenizer(raw).tokenize() + self.current = 0 + self.len = len(self.tokenized) + + def parse_into_method(self, method): + method.split = [] + kind = self.parse_one_of("ctor", "method") + method.split.append(kind) + annotations = self.parse_annotations() + method.split.extend(self.parse_modifiers()) + self.parse_matching_paren("<", ">") + if "@Deprecated" in annotations: + method.split.append("deprecated") + if kind == "ctor": + method.typ = "ctor" + else: + method.typ = self.parse_type() + method.split.append(method.typ) + method.name = self.parse_name() + method.split.append(method.name) + self.parse_token("(") + method.args = self.parse_args() + self.parse_token(")") + method.throws = self.parse_throws() + if "@interface" in method.clazz.split: + self.parse_annotation_default() + self.parse_token(";") + self.parse_eof() + + def parse_into_class(self, clazz): + clazz.split = [] + annotations = self.parse_annotations() + if "@Deprecated" in annotations: + clazz.split.append("deprecated") + clazz.split.extend(self.parse_modifiers()) + kind = self.parse_one_of("class", "interface", "@interface", "enum") + if kind == "enum": + # enums are implicitly final + clazz.split.append("final") + clazz.split.append(kind) + clazz.fullname = self.parse_name() + self.parse_matching_paren("<", ">") + extends = self.parse_extends() + clazz.extends = extends[0] if extends else None + implements = self.parse_implements() + clazz.implements = implements[0] if implements else None + # The checks assume that interfaces are always found in implements, which isn't true for + # subinterfaces. + if not implements and "interface" in clazz.split: + clazz.implements = clazz.extends + self.parse_token("{") + self.parse_eof() + + def parse_into_field(self, field): + kind = self.parse_one_of(*V2LineParser.FIELD_KINDS) + field.split = [kind] + annotations = self.parse_annotations() + if "@Deprecated" in annotations: + field.split.append("deprecated") + field.split.extend(self.parse_modifiers()) + field.typ = self.parse_type() + field.split.append(field.typ) + field.name = self.parse_name() + field.split.append(field.name) + if self.parse_if("="): + field.value = self.parse_value_stripped() + else: + field.value = None + + self.parse_token(";") + self.parse_eof() + + def lookahead(self): + return self.tokenized[self.current] + + def parse_one_of(self, *options): + found = self.lookahead() + if found not in options: + raise ValueError("Parsing failed, expected one of `%s` but found `%s` in %s" % (options, found, repr(self.tokenized))) + return self.parse_token() + + def parse_token(self, tok = None): + found = self.lookahead() + if tok is not None and found != tok: + raise ValueError("Parsing failed, expected `%s` but found `%s` in %s" % (tok, found, repr(self.tokenized))) + self.current += 1 + return found + + def eof(self): + return self.current == self.len + + def parse_eof(self): + if not self.eof(): + raise ValueError("Parsing failed, expected EOF, but %s has not been parsed in %s" % (self.tokenized[self.current:], self.tokenized)) + + def parse_if(self, tok): + if not self.eof() and self.lookahead() == tok: + self.parse_token() + return True + return False + + def parse_annotations(self): + ret = [] + while self.lookahead() == "@": + ret.append(self.parse_annotation()) + return ret + + def parse_annotation(self): + ret = self.parse_token("@") + self.parse_token() + self.parse_matching_paren("(", ")") + return ret + + def parse_matching_paren(self, open, close): + start = self.current + if not self.parse_if(open): + return + length = len(self.tokenized) + count = 1 + while count > 0: + if self.current == length: + raise ValueError("Unexpected EOF looking for closing paren: `%s`" % (self.tokenized[start:],)) + t = self.parse_token() + if t == open: + count += 1 + elif t == close: + count -= 1 + return self.tokenized[start:self.current] + + def parse_modifiers(self): + ret = [] + while self.lookahead() in V2LineParser.MODIFIERS: + ret.append(self.parse_token()) + return ret + + def parse_kotlin_nullability(self): + t = self.lookahead() + if t == "?" or t == "!": + return self.parse_token() + return None + + def parse_type(self): + self.parse_annotations() + type = self.parse_token() + if type[-1] == '.': + self.parse_annotations() + type += self.parse_token() + if type in V2LineParser.JAVA_LANG_TYPES: + type = "java.lang." + type + self.parse_matching_paren("<", ">") + while True: + t = self.lookahead() + if t == "@": + self.parse_annotation() + elif t == "[]": + type += self.parse_token() + elif self.parse_kotlin_nullability() is not None: + pass # discard nullability for now + else: + break + return type + + def parse_arg_type(self): + type = self.parse_type() + if self.parse_if("..."): + type += "..." + self.parse_kotlin_nullability() # discard nullability for now + return type + + def parse_name(self): + return self.parse_token() + + def parse_args(self): + args = [] + if self.lookahead() == ")": + return args + + while True: + args.append(self.parse_arg()) + if self.lookahead() == ")": + return args + self.parse_token(",") + + def parse_arg(self): + self.parse_if("vararg") # kotlin vararg + self.parse_annotations() + type = self.parse_arg_type() + l = self.lookahead() + if l != "," and l != ")": + if self.lookahead() != '=': + self.parse_token() # kotlin argument name + if self.parse_if('='): # kotlin default value + self.parse_expression() + return type + + def parse_expression(self): + while not self.lookahead() in [')', ',', ';']: + (self.parse_matching_paren('(', ')') or + self.parse_matching_paren('{', '}') or + self.parse_token()) + + def parse_throws(self): + ret = [] + if self.parse_if("throws"): + ret.append(self.parse_type()) + while self.parse_if(","): + ret.append(self.parse_type()) + return ret + + def parse_extends(self): + if self.parse_if("extends"): + return self.parse_space_delimited_type_list() + return [] + + def parse_implements(self): + if self.parse_if("implements"): + return self.parse_space_delimited_type_list() + return [] + + def parse_space_delimited_type_list(self, terminals = ["implements", "{"]): + types = [] + while True: + types.append(self.parse_type()) + if self.lookahead() in terminals: + return types + + def parse_annotation_default(self): + if self.parse_if("default"): + self.parse_expression() + + def parse_value(self): + if self.lookahead() == "{": + return " ".join(self.parse_matching_paren("{", "}")) + elif self.lookahead() == "(": + return " ".join(self.parse_matching_paren("(", ")")) + else: + return self.parse_token() + + def parse_value_stripped(self): + value = self.parse_value() + if value[0] == '"': + return value[1:-1] + return value + + +def _parse_stream(f, clazz_cb=None, base_f=None, out_classes_with_base=None, + in_classes_with_base=[]): api = {} + in_classes_with_base = _retry_iterator(in_classes_with_base) + + if base_f: + base_classes = _retry_iterator(_parse_stream_to_generator(base_f)) + else: + base_classes = [] + + def handle_class(clazz): + if clazz_cb: + clazz_cb(clazz) + else: # In callback mode, don't keep track of the full API + api[clazz.fullname] = clazz + + def handle_missed_classes_with_base(clazz): + for c in _yield_until_matching_class(in_classes_with_base, clazz): + base_class = _skip_to_matching_class(base_classes, c) + if base_class: + handle_class(base_class) + + for clazz in _parse_stream_to_generator(f): + # Before looking at clazz, let's see if there's some classes that were not present, but + # may have an entry in the base stream. + handle_missed_classes_with_base(clazz) + + base_class = _skip_to_matching_class(base_classes, clazz) + if base_class: + clazz.merge_from(base_class) + if out_classes_with_base is not None: + out_classes_with_base.append(clazz) + handle_class(clazz) + + handle_missed_classes_with_base(None) + + return api + +def _parse_stream_to_generator(f): + line = 0 pkg = None clazz = None blame = None + sig_format = 1 re_blame = re.compile("^([a-z0-9]{7,}) \(<([^>]+)>.+?\) (.+?)$") + + field_prefixes = map(lambda kind: " %s" % (kind,), V2LineParser.FIELD_KINDS) + def startsWithFieldPrefix(raw): + for prefix in field_prefixes: + if raw.startswith(prefix): + return True + return False + for raw in f: line += 1 raw = raw.rstrip() @@ -195,29 +610,72 @@ def _parse_stream(f, clazz_cb=None): else: blame = None - if raw.startswith("package"): + if line == 1 and raw == "// Signature format: 2.0": + sig_format = 2 + elif raw.startswith("package"): pkg = Package(line, raw, blame) elif raw.startswith(" ") and raw.endswith("{"): - # When provided with class callback, we treat as incremental - # parse and don't build up entire API - if clazz and clazz_cb: - clazz_cb(clazz) - clazz = Class(pkg, line, raw, blame) - if not clazz_cb: - api[clazz.fullname] = clazz + clazz = Class(pkg, line, raw, blame, sig_format=sig_format) elif raw.startswith(" ctor"): - clazz.ctors.append(Method(clazz, line, raw, blame)) + clazz.ctors.append(Method(clazz, line, raw, blame, sig_format=sig_format)) elif raw.startswith(" method"): - clazz.methods.append(Method(clazz, line, raw, blame)) - elif raw.startswith(" field"): - clazz.fields.append(Field(clazz, line, raw, blame)) - - # Handle last trailing class - if clazz and clazz_cb: - clazz_cb(clazz) - - return api - + clazz.methods.append(Method(clazz, line, raw, blame, sig_format=sig_format)) + elif startsWithFieldPrefix(raw): + clazz.fields.append(Field(clazz, line, raw, blame, sig_format=sig_format)) + elif raw.startswith(" }") and clazz: + yield clazz + +def _retry_iterator(it): + """Wraps an iterator, such that calling send(True) on it will redeliver the same element""" + for e in it: + while True: + retry = yield e + if not retry: + break + # send() was called, asking us to redeliver clazz on next(). Still need to yield + # a dummy value to the send() first though. + if (yield "Returning clazz on next()"): + raise TypeError("send() must be followed by next(), not send()") + +def _skip_to_matching_class(classes, needle): + """Takes a classes iterator and consumes entries until it returns the class we're looking for + + This relies on classes being sorted by package and class name.""" + + for clazz in classes: + if clazz.pkg.name < needle.pkg.name: + # We haven't reached the right package yet + continue + if clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname: + # We're in the right package, but not the right class yet + continue + if clazz.fullname == needle.fullname: + return clazz + # We ran past the right class. Send it back into the generator, then report failure. + classes.send(clazz) + return None + +def _yield_until_matching_class(classes, needle): + """Takes a class iterator and yields entries it until it reaches the class we're looking for. + + This relies on classes being sorted by package and class name.""" + + for clazz in classes: + if needle is None: + yield clazz + elif clazz.pkg.name < needle.pkg.name: + # We haven't reached the right package yet + yield clazz + elif clazz.pkg.name == needle.pkg.name and clazz.fullname < needle.fullname: + # We're in the right package, but not the right class yet + yield clazz + elif clazz.fullname == needle.fullname: + # Class found, abort. + return + else: + # We ran past the right class. Send it back into the iterator, then abort. + classes.send(clazz) + return class Failure(): def __init__(self, sig, clazz, detail, error, rule, msg): @@ -257,7 +715,7 @@ def _fail(clazz, detail, error, rule, msg): """Records an API failure to be processed later.""" global failures - sig = "%s-%s-%s" % (clazz.fullname, repr(detail), msg) + sig = "%s-%s-%s" % (clazz.fullname, detail.ident if detail else None, msg) sig = sig.replace(" deprecated ", " ") failures[sig] = Failure(sig, clazz, detail, error, rule, msg) @@ -298,7 +756,7 @@ def verify_constants(clazz): def verify_enums(clazz): """Enums are bad, mmkay?""" - if "extends java.lang.Enum" in clazz.raw: + if clazz.extends == "java.lang.Enum" or "enum" in clazz.split: error(clazz, None, "F5", "Enums are not allowed") @@ -357,7 +815,7 @@ def verify_listeners(clazz): interface OnFooListener { void onFoo() }""" if clazz.name.endswith("Listener"): - if " abstract class " in clazz.raw: + if "abstract" in clazz.split and "class" in clazz.split: error(clazz, None, "L1", "Listeners should be an interface, or otherwise renamed Callback") for m in clazz.methods: @@ -436,16 +894,16 @@ def verify_equals(clazz): eq = False hc = False for m in clazz.methods: - if " static " in m.raw: continue - if "boolean equals(java.lang.Object)" in m.raw: eq = True - if "int hashCode()" in m.raw: hc = True + if "static" in m.split: continue + if m.sig_matches("boolean", "equals", ["java.lang.Object"]): eq = True + if m.sig_matches("int", "hashCode", []): hc = True if eq != hc: error(clazz, None, "M8", "Must override both equals and hashCode; missing one") def verify_parcelable(clazz): """Verify that Parcelable objects aren't hiding required bits.""" - if "implements android.os.Parcelable" in clazz.raw: + if clazz.implements == "android.os.Parcelable": creator = [ i for i in clazz.fields if i.name == "CREATOR" ] write = [ i for i in clazz.methods if i.name == "writeToParcel" ] describe = [ i for i in clazz.methods if i.name == "describeContents" ] @@ -453,8 +911,7 @@ def verify_parcelable(clazz): if len(creator) == 0 or len(write) == 0 or len(describe) == 0: error(clazz, None, "FW3", "Parcelable requires CREATOR, writeToParcel, and describeContents; missing one") - if ((" final class " not in clazz.raw) and - (" final deprecated class " not in clazz.raw)): + if "final" not in clazz.split: error(clazz, None, "FW8", "Parcelable classes must be final") for c in clazz.ctors: @@ -465,6 +922,7 @@ def verify_parcelable(clazz): def verify_protected(clazz): """Verify that no protected methods or fields are allowed.""" for m in clazz.methods: + if m.name == "finalize": continue if "protected" in m.split: error(clazz, m, "M7", "Protected methods not allowed; must be public") for f in clazz.fields: @@ -505,7 +963,7 @@ def verify_fields(clazz): else: error(clazz, f, "F2", "Bare fields must be marked final, or add accessors if mutable") - if not "static" in f.split: + if "static" not in f.split and "property" not in f.split: if not re.match("[a-z]([a-zA-Z]+)?", f.name): error(clazz, f, "S1", "Non-static fields must be named using myField style") @@ -573,7 +1031,7 @@ def verify_helper_classes(clazz): """Verify that helper classes are named consistently with what they extend. All developer extendable methods should be named onFoo().""" test_methods = False - if "extends android.app.Service" in clazz.raw: + if clazz.extends == "android.app.Service": test_methods = True if not clazz.name.endswith("Service"): error(clazz, None, "CL4", "Inconsistent class name; should be FooService") @@ -585,7 +1043,7 @@ def verify_helper_classes(clazz): if f.value != clazz.fullname: error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname)) - if "extends android.content.ContentProvider" in clazz.raw: + if clazz.extends == "android.content.ContentProvider": test_methods = True if not clazz.name.endswith("Provider"): error(clazz, None, "CL4", "Inconsistent class name; should be FooProvider") @@ -597,12 +1055,12 @@ def verify_helper_classes(clazz): if f.value != clazz.fullname: error(clazz, f, "C4", "Inconsistent interface constant; expected '%s'" % (clazz.fullname)) - if "extends android.content.BroadcastReceiver" in clazz.raw: + if clazz.extends == "android.content.BroadcastReceiver": test_methods = True if not clazz.name.endswith("Receiver"): error(clazz, None, "CL4", "Inconsistent class name; should be FooReceiver") - if "extends android.app.Activity" in clazz.raw: + if clazz.extends == "android.app.Activity": test_methods = True if not clazz.name.endswith("Activity"): error(clazz, None, "CL4", "Inconsistent class name; should be FooActivity") @@ -620,7 +1078,7 @@ def verify_helper_classes(clazz): def verify_builder(clazz): """Verify builder classes. Methods should return the builder to enable chaining.""" - if " extends " in clazz.raw: return + if clazz.extends: return if not clazz.name.endswith("Builder"): return if clazz.name != "Builder": @@ -648,7 +1106,7 @@ def verify_builder(clazz): def verify_aidl(clazz): """Catch people exposing raw AIDL.""" - if "extends android.os.Binder" in clazz.raw or "implements android.os.IInterface" in clazz.raw: + if clazz.extends == "android.os.Binder" or clazz.implements == "android.os.IInterface": error(clazz, None, None, "Raw AIDL interfaces must not be exposed") @@ -657,48 +1115,66 @@ def verify_internal(clazz): if clazz.pkg.name.startswith("com.android"): error(clazz, None, None, "Internal classes must not be exposed") +def layering_build_ranking(ranking_list): + r = {} + for rank, ps in enumerate(ranking_list): + if not isinstance(ps, list): + ps = [ps] + for p in ps: + rs = r + for n in p.split('.'): + if n not in rs: + rs[n] = {} + rs = rs[n] + rs['-rank'] = rank + return r + +LAYERING_PACKAGE_RANKING = layering_build_ranking([ + ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"], + "android.app", + "android.widget", + "android.view", + "android.animation", + "android.provider", + ["android.content","android.graphics.drawable"], + "android.database", + "android.text", + "android.graphics", + "android.os", + "android.util" +]) def verify_layering(clazz): """Catch package layering violations. For example, something in android.os depending on android.app.""" - ranking = [ - ["android.service","android.accessibilityservice","android.inputmethodservice","android.printservice","android.appwidget","android.webkit","android.preference","android.gesture","android.print"], - "android.app", - "android.widget", - "android.view", - "android.animation", - "android.provider", - ["android.content","android.graphics.drawable"], - "android.database", - "android.graphics", - "android.text", - "android.os", - "android.util" - ] def rank(p): - for i in range(len(ranking)): - if isinstance(ranking[i], list): - for j in ranking[i]: - if p.startswith(j): return i + r = None + l = LAYERING_PACKAGE_RANKING + for n in p.split('.'): + if n in l: + l = l[n] + if '-rank' in l: + r = l['-rank'] else: - if p.startswith(ranking[i]): return i + break + return r cr = rank(clazz.pkg.name) if cr is None: return for f in clazz.fields: ir = rank(f.typ) - if ir and ir < cr: + if ir is not None and ir < cr: warn(clazz, f, "FW6", "Field type violates package layering") - for m in clazz.methods: + for m in itertools.chain(clazz.methods, clazz.ctors): ir = rank(m.typ) - if ir and ir < cr: + if ir is not None and ir < cr: warn(clazz, m, "FW6", "Method return type violates package layering") for arg in m.args: ir = rank(arg) - if ir and ir < cr: + if ir is not None and ir < cr: warn(clazz, m, "FW6", "Method argument type violates package layering") @@ -789,21 +1265,18 @@ def verify_exception(clazz): if len(m.args) == 0 and t in ["java.lang.IllegalArgumentException", "java.lang.NullPointerException"]: warn(clazz, m, "S1", "Methods taking no arguments should throw IllegalStateException") +GOOGLE_IGNORECASE = re.compile("google", re.IGNORECASE) def verify_google(clazz): """Verifies that APIs never reference Google.""" - if re.search("google", clazz.raw, re.IGNORECASE): + if GOOGLE_IGNORECASE.search(clazz.raw) is not None: error(clazz, None, None, "Must never reference Google") - test = [] - test.extend(clazz.ctors) - test.extend(clazz.fields) - test.extend(clazz.methods) - - for t in test: - if re.search("google", t.raw, re.IGNORECASE): - error(clazz, t, None, "Must never reference Google") + for test in clazz.ctors, clazz.fields, clazz.methods: + for t in test: + if GOOGLE_IGNORECASE.search(t.raw) is not None: + error(clazz, t, None, "Must never reference Google") def verify_bitset(clazz): @@ -998,6 +1471,10 @@ def verify_resource_names(clazz): # Resources defined by files are foo_bar_baz if clazz.name in ["anim","animator","color","dimen","drawable","interpolator","layout","transition","menu","mipmap","string","plurals","raw","xml"]: for f in clazz.fields: + if re.match("config_[a-z][a-zA-Z1-9]*$", f.name): continue + if f.name.startswith("config_"): + error(clazz, f, None, "Expected config name to be config_fooBarBaz style") + if re.match("[a-z1-9_]+$", f.name): continue error(clazz, f, None, "Expected resource name in this class to be foo_bar_baz style") @@ -1053,7 +1530,7 @@ def verify_abstract_inner(clazz): """Verifies that abstract inner classes are static.""" if re.match(".+?\.[A-Z][^\.]+\.[A-Z]", clazz.fullname): - if " abstract " in clazz.raw and " static " not in clazz.raw: + if "abstract" in clazz.split and "static" not in clazz.split: warn(clazz, None, None, "Abstract inner classes should be static to improve testability") @@ -1148,8 +1625,8 @@ def verify_units(clazz): def verify_closable(clazz): """Verifies that classes are AutoClosable.""" - if "implements java.lang.AutoCloseable" in clazz.raw: return - if "implements java.io.Closeable" in clazz.raw: return + if clazz.implements == "java.lang.AutoCloseable": return + if clazz.implements == "java.io.Closeable": return for m in clazz.methods: if len(m.args) > 0: continue @@ -1194,7 +1671,7 @@ def verify_method_name_not_kotlin_operator(clazz): binary.add(op) for m in clazz.methods: - if 'static' in m.split: + if 'static' in m.split or 'operator' in m.split: continue # https://kotlinlang.org/docs/reference/operator-overloading.html#unary-prefix-operators @@ -1235,6 +1712,9 @@ def verify_method_name_not_kotlin_operator(clazz): def verify_collections_over_arrays(clazz): """Warn that [] should be Collections.""" + if "@interface" in clazz.split: + return + safe = ["java.lang.String[]","byte[]","short[]","int[]","long[]","float[]","double[]","boolean[]","char[]"] for m in clazz.methods: if m.typ.endswith("[]") and m.typ not in safe: @@ -1253,10 +1733,19 @@ def verify_user_handle(clazz): if clazz.fullname == "android.os.UserManager": return for m in clazz.methods: - if m.name.endswith("AsUser") or m.name.endswith("ForUser"): continue if re.match("on[A-Z]+", m.name): continue - if "android.os.UserHandle" in m.args: - warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' or 'queryFooForUser'") + + has_arg = "android.os.UserHandle" in m.args + has_name = m.name.endswith("AsUser") or m.name.endswith("ForUser") + + if clazz.fullname.endswith("Manager") and has_arg: + warn(clazz, m, None, "When a method overload is needed to target a specific " + "UserHandle, callers should be directed to use " + "Context.createPackageContextAsUser() and re-obtain the relevant " + "Manager, and no new API should be added") + elif has_arg and not has_name: + warn(clazz, m, None, "Method taking UserHandle should be named 'doFooAsUser' " + "or 'queryFooForUser'") def verify_params(clazz): @@ -1334,18 +1823,79 @@ def verify_clone(clazz): error(clazz, m, None, "Provide an explicit copy constructor instead of implementing clone()") +def verify_pfd(clazz): + """Verify that android APIs use PFD over FD.""" + examine = clazz.ctors + clazz.methods + for m in examine: + if m.typ == "java.io.FileDescriptor": + error(clazz, m, "FW11", "Must use ParcelFileDescriptor") + if m.typ == "int": + if "Fd" in m.name or "FD" in m.name or "FileDescriptor" in m.name: + error(clazz, m, "FW11", "Must use ParcelFileDescriptor") + for arg in m.args: + if arg == "java.io.FileDescriptor": + error(clazz, m, "FW11", "Must use ParcelFileDescriptor") + + for f in clazz.fields: + if f.typ == "java.io.FileDescriptor": + error(clazz, f, "FW11", "Must use ParcelFileDescriptor") + + +def verify_numbers(clazz): + """Discourage small numbers types like short and byte.""" + + discouraged = ["short","byte"] + + for c in clazz.ctors: + for arg in c.args: + if arg in discouraged: + warn(clazz, c, "FW12", "Should avoid odd sized primitives; use int instead") + + for f in clazz.fields: + if f.typ in discouraged: + warn(clazz, f, "FW12", "Should avoid odd sized primitives; use int instead") + + for m in clazz.methods: + if m.typ in discouraged: + warn(clazz, m, "FW12", "Should avoid odd sized primitives; use int instead") + for arg in m.args: + if arg in discouraged: + warn(clazz, m, "FW12", "Should avoid odd sized primitives; use int instead") + + +def verify_singleton(clazz): + """Catch singleton objects with constructors.""" + + singleton = False + for m in clazz.methods: + if m.name.startswith("get") and m.name.endswith("Instance") and " static " in m.raw: + singleton = True + + if singleton: + for c in clazz.ctors: + error(clazz, c, None, "Singleton classes should use getInstance() methods") + + + +def is_interesting(clazz): + """Test if given class is interesting from an Android PoV.""" + + if clazz.pkg.name.startswith("java"): return False + if clazz.pkg.name.startswith("junit"): return False + if clazz.pkg.name.startswith("org.apache"): return False + if clazz.pkg.name.startswith("org.xml"): return False + if clazz.pkg.name.startswith("org.json"): return False + if clazz.pkg.name.startswith("org.w3c"): return False + if clazz.pkg.name.startswith("android.icu."): return False + return True + + def examine_clazz(clazz): """Find all style issues in the given class.""" notice(clazz) - if clazz.pkg.name.startswith("java"): return - if clazz.pkg.name.startswith("junit"): return - if clazz.pkg.name.startswith("org.apache"): return - if clazz.pkg.name.startswith("org.xml"): return - if clazz.pkg.name.startswith("org.json"): return - if clazz.pkg.name.startswith("org.w3c"): return - if clazz.pkg.name.startswith("android.icu."): return + if not is_interesting(clazz): return verify_constants(clazz) verify_enums(clazz) @@ -1397,14 +1947,19 @@ def examine_clazz(clazz): verify_tense(clazz) verify_icu(clazz) verify_clone(clazz) + verify_pfd(clazz) + verify_numbers(clazz) + verify_singleton(clazz) -def examine_stream(stream): +def examine_stream(stream, base_stream=None, in_classes_with_base=[], out_classes_with_base=None): """Find all style issues in the given API stream.""" global failures, noticed failures = {} noticed = {} - _parse_stream(stream, examine_clazz) + _parse_stream(stream, examine_clazz, base_f=base_stream, + in_classes_with_base=in_classes_with_base, + out_classes_with_base=out_classes_with_base) return (failures, noticed) @@ -1479,6 +2034,7 @@ def show_deprecations_at_birth(cur, prev): # Remove all existing things so we're left with new for prev_clazz in prev.values(): cur_clazz = cur[prev_clazz.fullname] + if not is_interesting(cur_clazz): continue sigs = { i.ident: i for i in prev_clazz.ctors } cur_clazz.ctors = [ i for i in cur_clazz.ctors if i.ident not in sigs ] @@ -1492,11 +2048,11 @@ def show_deprecations_at_birth(cur, prev): del cur[prev_clazz.fullname] for clazz in cur.values(): - if " deprecated " in clazz.raw and not clazz.fullname in prev: + if "deprecated" in clazz.split and not clazz.fullname in prev: error(clazz, None, None, "Found API deprecation at birth") for i in clazz.ctors + clazz.methods + clazz.fields: - if " deprecated " in i.raw: + if "deprecated" in i.split: error(clazz, i, None, "Found API deprecation at birth") print "%s Deprecated at birth %s\n" % ((format(fg=WHITE, bg=BLUE, bold=True), @@ -1506,12 +2062,50 @@ def show_deprecations_at_birth(cur, prev): print +def show_stats(cur, prev): + """Show API stats.""" + + stats = collections.defaultdict(int) + for cur_clazz in cur.values(): + if not is_interesting(cur_clazz): continue + + if cur_clazz.fullname not in prev: + stats['new_classes'] += 1 + stats['new_ctors'] += len(cur_clazz.ctors) + stats['new_methods'] += len(cur_clazz.methods) + stats['new_fields'] += len(cur_clazz.fields) + else: + prev_clazz = prev[cur_clazz.fullname] + + sigs = { i.ident: i for i in prev_clazz.ctors } + ctors = len([ i for i in cur_clazz.ctors if i.ident not in sigs ]) + sigs = { i.ident: i for i in prev_clazz.methods } + methods = len([ i for i in cur_clazz.methods if i.ident not in sigs ]) + sigs = { i.ident: i for i in prev_clazz.fields } + fields = len([ i for i in cur_clazz.fields if i.ident not in sigs ]) + + if ctors + methods + fields > 0: + stats['extend_classes'] += 1 + stats['extend_ctors'] += ctors + stats['extend_methods'] += methods + stats['extend_fields'] += fields + + print "#", "".join([ k.ljust(20) for k in sorted(stats.keys()) ]) + print " ", "".join([ str(stats[k]).ljust(20) for k in sorted(stats.keys()) ]) + + if __name__ == "__main__": parser = argparse.ArgumentParser(description="Enforces common Android public API design \ patterns. It ignores lint messages from a previous API level, if provided.") parser.add_argument("current.txt", type=argparse.FileType('r'), help="current.txt") parser.add_argument("previous.txt", nargs='?', type=argparse.FileType('r'), default=None, help="previous.txt") + parser.add_argument("--base-current", nargs='?', type=argparse.FileType('r'), default=None, + help="The base current.txt to use when examining system-current.txt or" + " test-current.txt") + parser.add_argument("--base-previous", nargs='?', type=argparse.FileType('r'), default=None, + help="The base previous.txt to use when examining system-previous.txt or" + " test-previous.txt") parser.add_argument("--no-color", action='store_const', const=True, help="Disable terminal colors") parser.add_argument("--allow-google", action='store_const', const=True, @@ -1520,6 +2114,8 @@ if __name__ == "__main__": help="Show API changes noticed") parser.add_argument("--show-deprecations-at-birth", action='store_const', const=True, help="Show API deprecations at birth") + parser.add_argument("--show-stats", action='store_const', const=True, + help="Show API stats") args = vars(parser.parse_args()) if args['no_color']: @@ -1529,7 +2125,9 @@ if __name__ == "__main__": ALLOW_GOOGLE = True current_file = args['current.txt'] + base_current_file = args['base_current'] previous_file = args['previous.txt'] + base_previous_file = args['base_previous'] if args['show_deprecations_at_birth']: with current_file as f: @@ -1539,11 +2137,32 @@ if __name__ == "__main__": show_deprecations_at_birth(cur, prev) sys.exit() + if args['show_stats']: + with current_file as f: + cur = _parse_stream(f) + with previous_file as f: + prev = _parse_stream(f) + show_stats(cur, prev) + sys.exit() + + classes_with_base = [] + with current_file as f: - cur_fail, cur_noticed = examine_stream(f) + if base_current_file: + with base_current_file as base_f: + cur_fail, cur_noticed = examine_stream(f, base_f, + out_classes_with_base=classes_with_base) + else: + cur_fail, cur_noticed = examine_stream(f, out_classes_with_base=classes_with_base) + if not previous_file is None: with previous_file as f: - prev_fail, prev_noticed = examine_stream(f) + if base_previous_file: + with base_previous_file as base_f: + prev_fail, prev_noticed = examine_stream(f, base_f, + in_classes_with_base=classes_with_base) + else: + prev_fail, prev_noticed = examine_stream(f, in_classes_with_base=classes_with_base) # ignore errors from previous API level for p in prev_fail: diff --git a/tools/apilint/apilint_sha_system.sh b/tools/apilint/apilint_sha_system.sh new file mode 100755 index 000000000000..8538a3d904f5 --- /dev/null +++ b/tools/apilint/apilint_sha_system.sh @@ -0,0 +1,23 @@ +#!/bin/bash + +# Copyright (C) 2018 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. + +if git show --name-only --pretty=format: $1 | grep api/ > /dev/null; then + python tools/apilint/apilint.py \ + --base-current <(git show $1:api/current.txt) \ + --base-previous <(git show $1^:api/current.txt) \ + <(git show $1:api/system-current.txt) \ + <(git show $1^:api/system-current.txt) +fi diff --git a/tools/apilint/apilint_stats.sh b/tools/apilint/apilint_stats.sh new file mode 100755 index 000000000000..052d9a5265fe --- /dev/null +++ b/tools/apilint/apilint_stats.sh @@ -0,0 +1,7 @@ +#!/bin/bash +API=28 +while [ $API -gt 14 ]; do + echo "# Changes in API $((API))" + python tools/apilint/apilint.py --show-stats ../../prebuilts/sdk/$((API))/public/api/android.txt ../../prebuilts/sdk/$((API-1))/public/api/android.txt + let API=API-1 +done diff --git a/tools/apilint/apilint_test.py b/tools/apilint/apilint_test.py new file mode 100644 index 000000000000..9c261d506dac --- /dev/null +++ b/tools/apilint/apilint_test.py @@ -0,0 +1,356 @@ +#!/usr/bin/env python + +# Copyright (C) 2018 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. + +import unittest + +import apilint + +def cls(pkg, name): + return apilint.Class(apilint.Package(999, "package %s {" % pkg, None), 999, + "public final class %s {" % name, None) + +_ri = apilint._retry_iterator + +c1 = cls("android.app", "ActivityManager") +c2 = cls("android.app", "Notification") +c3 = cls("android.app", "Notification.Action") +c4 = cls("android.graphics", "Bitmap") + +class UtilTests(unittest.TestCase): + def test_retry_iterator(self): + it = apilint._retry_iterator([1, 2, 3, 4]) + self.assertEqual(it.next(), 1) + self.assertEqual(it.next(), 2) + self.assertEqual(it.next(), 3) + it.send("retry") + self.assertEqual(it.next(), 3) + self.assertEqual(it.next(), 4) + with self.assertRaises(StopIteration): + it.next() + + def test_retry_iterator_one(self): + it = apilint._retry_iterator([1]) + self.assertEqual(it.next(), 1) + it.send("retry") + self.assertEqual(it.next(), 1) + with self.assertRaises(StopIteration): + it.next() + + def test_retry_iterator_one(self): + it = apilint._retry_iterator([1]) + self.assertEqual(it.next(), 1) + it.send("retry") + self.assertEqual(it.next(), 1) + with self.assertRaises(StopIteration): + it.next() + + def test_skip_to_matching_class_found(self): + it = _ri([c1, c2, c3, c4]) + self.assertEquals(apilint._skip_to_matching_class(it, c3), + c3) + self.assertEqual(it.next(), c4) + + def test_skip_to_matching_class_not_found(self): + it = _ri([c1, c2, c3, c4]) + self.assertEquals(apilint._skip_to_matching_class(it, cls("android.content", "ContentProvider")), + None) + self.assertEqual(it.next(), c4) + + def test_yield_until_matching_class_found(self): + it = _ri([c1, c2, c3, c4]) + self.assertEquals(list(apilint._yield_until_matching_class(it, c3)), + [c1, c2]) + self.assertEqual(it.next(), c4) + + def test_yield_until_matching_class_not_found(self): + it = _ri([c1, c2, c3, c4]) + self.assertEquals(list(apilint._yield_until_matching_class(it, cls("android.content", "ContentProvider"))), + [c1, c2, c3]) + self.assertEqual(it.next(), c4) + + def test_yield_until_matching_class_None(self): + it = _ri([c1, c2, c3, c4]) + self.assertEquals(list(apilint._yield_until_matching_class(it, None)), + [c1, c2, c3, c4]) + + +faulty_current_txt = """ +package android.app { + public final class Activity { + } + + public final class WallpaperColors implements android.os.Parcelable { + ctor public WallpaperColors(android.os.Parcel); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR; + } +} +""".split('\n') + +ok_current_txt = """ +package android.app { + public final class Activity { + } + + public final class WallpaperColors implements android.os.Parcelable { + ctor public WallpaperColors(); + method public int describeContents(); + method public void writeToParcel(android.os.Parcel, int); + field public static final android.os.Parcelable.Creator<android.app.WallpaperColors> CREATOR; + } +} +""".split('\n') + +system_current_txt = """ +package android.app { + public final class WallpaperColors implements android.os.Parcelable { + method public int getSomething(); + } +} +""".split('\n') + + + +class BaseFileTests(unittest.TestCase): + def test_base_file_avoids_errors(self): + failures, _ = apilint.examine_stream(system_current_txt, ok_current_txt) + self.assertEquals(failures, {}) + + def test_class_with_base_finds_same_errors(self): + failures_with_classes_with_base, _ = apilint.examine_stream("", faulty_current_txt, + in_classes_with_base=[cls("android.app", "WallpaperColors")]) + failures_with_system_txt, _ = apilint.examine_stream(system_current_txt, faulty_current_txt) + + self.assertEquals(failures_with_classes_with_base.keys(), failures_with_system_txt.keys()) + + def test_classes_with_base_is_emited(self): + classes_with_base = [] + _, _ = apilint.examine_stream(system_current_txt, faulty_current_txt, + out_classes_with_base=classes_with_base) + self.assertEquals(map(lambda x: x.fullname, classes_with_base), ["android.app.WallpaperColors"]) + +class ParseV2Stream(unittest.TestCase): + def test_field_kinds(self): + api = apilint._parse_stream(""" +// Signature format: 2.0 +package android { + public enum SomeEnum { + enum_constant public static final android.SomeEnum ENUM_CONST; + field public static final int FIELD_CONST; + property public final int someProperty; + ctor public SomeEnum(); + method public Object? getObject(); + } +} + """.strip().split('\n')) + + self.assertEquals(api['android.SomeEnum'].fields[0].split[0], 'enum_constant') + self.assertEquals(api['android.SomeEnum'].fields[1].split[0], 'field') + self.assertEquals(api['android.SomeEnum'].fields[2].split[0], 'property') + self.assertEquals(api['android.SomeEnum'].ctors[0].split[0], 'ctor') + self.assertEquals(api['android.SomeEnum'].methods[0].split[0], 'method') + +class V2TokenizerTests(unittest.TestCase): + def _test(self, raw, expected): + self.assertEquals(apilint.V2Tokenizer(raw).tokenize(), expected) + + def test_simple(self): + self._test(" method public some.Type someName(some.Argument arg, int arg);", + ['method', 'public', 'some.Type', 'someName', '(', 'some.Argument', + 'arg', ',', 'int', 'arg', ')', ';']) + self._test("class Some.Class extends SomeOther {", + ['class', 'Some.Class', 'extends', 'SomeOther', '{']) + + def test_varargs(self): + self._test("name(String...)", + ['name', '(', 'String', '...', ')']) + + def test_kotlin(self): + self._test("String? name(String!...)", + ['String', '?', 'name', '(', 'String', '!', '...', ')']) + + def test_annotation(self): + self._test("method @Nullable public void name();", + ['method', '@', 'Nullable', 'public', 'void', 'name', '(', ')', ';']) + + def test_annotation_args(self): + self._test("@Some(val=1, other=2) class Class {", + ['@', 'Some', '(', 'val', '=', '1', ',', 'other', '=', '2', ')', + 'class', 'Class', '{']) + def test_comment(self): + self._test("some //comment", ['some']) + + def test_strings(self): + self._test(r'"" "foo" "\"" "\\"', ['""', '"foo"', r'"\""', r'"\\"']) + + def test_at_interface(self): + self._test("public @interface Annotation {", + ['public', '@interface', 'Annotation', '{']) + + def test_array_type(self): + self._test("int[][]", ['int', '[]', '[]']) + + def test_generics(self): + self._test("<>foobar<A extends Object>", + ['<', '>', 'foobar', '<', 'A', 'extends', 'Object', '>']) + +class V2ParserTests(unittest.TestCase): + def _cls(self, raw): + pkg = apilint.Package(999, "package pkg {", None) + return apilint.Class(pkg, 1, raw, '', sig_format=2) + + def _method(self, raw, cls=None): + if not cls: + cls = self._cls("class Class {") + return apilint.Method(cls, 1, raw, '', sig_format=2) + + def _field(self, raw): + cls = self._cls("class Class {") + return apilint.Field(cls, 1, raw, '', sig_format=2) + + def test_class(self): + cls = self._cls("@Deprecated @IntRange(from=1, to=2) public static abstract class Some.Name extends Super<Class> implements Interface<Class> {") + self.assertTrue('deprecated' in cls.split) + self.assertTrue('static' in cls.split) + self.assertTrue('abstract' in cls.split) + self.assertTrue('class' in cls.split) + self.assertEquals('Super', cls.extends) + self.assertEquals('Interface', cls.implements) + self.assertEquals('pkg.Some.Name', cls.fullname) + + def test_enum(self): + cls = self._cls("public enum Some.Name {") + self._field("enum_constant public static final android.ValueType COLOR;") + + def test_interface(self): + cls = self._cls("@Deprecated @IntRange(from=1, to=2) public interface Some.Name extends Interface<Class> {") + self.assertTrue('deprecated' in cls.split) + self.assertTrue('interface' in cls.split) + self.assertEquals('Interface', cls.extends) + self.assertEquals('Interface', cls.implements) + self.assertEquals('pkg.Some.Name', cls.fullname) + + def test_at_interface(self): + cls = self._cls("@java.lang.annotation.Target({java.lang.annotation.ElementType.TYPE, java.lang.annotation.ElementType.FIELD, java.lang.annotation.ElementType.METHOD, java.lang.annotation.ElementType.PARAMETER, java.lang.annotation.ElementType.CONSTRUCTOR, java.lang.annotation.ElementType.LOCAL_VARIABLE}) @java.lang.annotation.Retention(java.lang.annotation.RetentionPolicy.CLASS) public @interface SuppressLint {") + self.assertTrue('@interface' in cls.split) + self.assertEquals('pkg.SuppressLint', cls.fullname) + + def test_parse_method(self): + m = self._method("method @Deprecated public static native <T> Class<T>[][] name(" + + "Class<T[]>[][], Class<T[][][]>[][]...) throws Exception, T;") + self.assertTrue('static' in m.split) + self.assertTrue('public' in m.split) + self.assertTrue('method' in m.split) + self.assertTrue('native' in m.split) + self.assertTrue('deprecated' in m.split) + self.assertEquals('java.lang.Class[][]', m.typ) + self.assertEquals('name', m.name) + self.assertEquals(['java.lang.Class[][]', 'java.lang.Class[][]...'], m.args) + self.assertEquals(['java.lang.Exception', 'T'], m.throws) + + def test_ctor(self): + m = self._method("ctor @Deprecated <T> ClassName();") + self.assertTrue('ctor' in m.split) + self.assertTrue('deprecated' in m.split) + self.assertEquals('ctor', m.typ) + self.assertEquals('ClassName', m.name) + + def test_parse_annotation_method(self): + cls = self._cls("@interface Annotation {") + self._method('method abstract String category() default "";', cls=cls) + self._method('method abstract boolean deepExport() default false;', cls=cls) + self._method('method abstract ViewDebug.FlagToString[] flagMapping() default {};', cls=cls) + self._method('method abstract ViewDebug.FlagToString[] flagMapping() default (double)java.lang.Float.NEGATIVE_INFINITY;', cls=cls) + + def test_parse_string_field(self): + f = self._field('field @Deprecated public final String SOME_NAME = "value";') + self.assertTrue('field' in f.split) + self.assertTrue('deprecated' in f.split) + self.assertTrue('final' in f.split) + self.assertEquals('java.lang.String', f.typ) + self.assertEquals('SOME_NAME', f.name) + self.assertEquals('value', f.value) + + def test_parse_field(self): + f = self._field('field public Object SOME_NAME;') + self.assertTrue('field' in f.split) + self.assertEquals('java.lang.Object', f.typ) + self.assertEquals('SOME_NAME', f.name) + self.assertEquals(None, f.value) + + def test_parse_int_field(self): + f = self._field('field public int NAME = 123;') + self.assertTrue('field' in f.split) + self.assertEquals('int', f.typ) + self.assertEquals('NAME', f.name) + self.assertEquals('123', f.value) + + def test_parse_quotient_field(self): + f = self._field('field public int NAME = (0.0/0.0);') + self.assertTrue('field' in f.split) + self.assertEquals('int', f.typ) + self.assertEquals('NAME', f.name) + self.assertEquals('( 0.0 / 0.0 )', f.value) + + def test_kotlin_types(self): + self._field('field public List<Integer[]?[]!>?[]![]? NAME;') + self._method("method <T?> Class<T!>?[]![][]? name(Type!, Type argname," + + "Class<T?>[][]?[]!...!) throws Exception, T;") + self._method("method <T> T name(T a = 1, T b = A(1), Lambda f = { false }, N? n = null, " + + """double c = (1/0), float d = 1.0f, String s = "heyo", char c = 'a');""") + + def test_kotlin_operator(self): + self._method('method public operator void unaryPlus(androidx.navigation.NavDestination);') + self._method('method public static operator androidx.navigation.NavDestination get(androidx.navigation.NavGraph, @IdRes int id);') + self._method('method public static operator <T> T get(androidx.navigation.NavigatorProvider, kotlin.reflect.KClass<T> clazz);') + + def test_kotlin_property(self): + self._field('property public VM value;') + self._field('property public final String? action;') + + def test_kotlin_varargs(self): + self._method('method public void error(int p = "42", Integer int2 = "null", int p1 = "42", vararg String args);') + + def test_kotlin_default_values(self): + self._method('method public void foo(String! = null, String! = "Hello World", int = 42);') + self._method('method void method(String, String firstArg = "hello", int secondArg = "42", String thirdArg = "world");') + self._method('method void method(String, String firstArg = "hello", int secondArg = "42");') + self._method('method void method(String, String firstArg = "hello");') + self._method('method void edit(android.Type, boolean commit = false, Function1<? super Editor,kotlin.Unit> action);') + self._method('method <K, V> LruCache<K,V> lruCache(int maxSize, Function2<? super K,? super V,java.lang.Integer> sizeOf = { _, _ -> 1 }, Function1<? extends V> create = { (V)null }, Function4<kotlin.Unit> onEntryRemoved = { _, _, _, _ -> });') + self._method('method android.Bitmap? drawToBitmap(android.View, android.Config config = android.graphics.Bitmap.Config.ARGB_8888);') + self._method('method void emptyLambda(Function0<kotlin.Unit> sizeOf = {});') + self._method('method void method1(int p = 42, Integer? int2 = null, int p1 = 42, String str = "hello world", java.lang.String... args);') + self._method('method void method2(int p, int int2 = (2 * int) * some.other.pkg.Constants.Misc.SIZE);') + self._method('method void method3(String str, int p, int int2 = double(int) + str.length);') + self._method('method void print(test.pkg.Foo foo = test.pkg.Foo());') + + def test_type_use_annotation(self): + self._method('method public static int codePointAt(char @NonNull [], int);') + self._method('method @NonNull public java.util.Set<java.util.Map.@NonNull Entry<K,V>> entrySet();') + + m = self._method('method @NonNull public java.lang.annotation.@NonNull Annotation @NonNull [] getAnnotations();') + self.assertEquals('java.lang.annotation.Annotation[]', m.typ) + + m = self._method('method @NonNull public abstract java.lang.annotation.@NonNull Annotation @NonNull [] @NonNull [] getParameterAnnotations();') + self.assertEquals('java.lang.annotation.Annotation[][]', m.typ) + + m = self._method('method @NonNull public @NonNull String @NonNull [] split(@NonNull String, int);') + self.assertEquals('java.lang.String[]', m.typ) + +if __name__ == "__main__": + unittest.main() |