Snap for 11174750 from 3255d59c03ace580cdfd13a4046a026c23b16cad to mainline-extservices-release Change-Id: I1017d700b055b00725af9b8277f95f7e0db31f31
diff --git a/Android.bp b/Android.bp index 61b4a1e..8ee5d6f 100644 --- a/Android.bp +++ b/Android.bp
@@ -89,7 +89,7 @@ sdk_version: module_34_version, libs: [ "framework-configinfrastructure", - "framework-connectivity", + "framework-connectivity.stubs.module_lib", "framework-connectivity-t", "framework-statsd", "framework-wifi", @@ -311,6 +311,7 @@ "androidx.annotation_annotation", "modules-utils-build_system", "modules-utils-preconditions", + "modules-utils-shell-command-handler", "modules-utils-statemachine", "netd_aidl_interface-lateststable-java", "networkstack-client",
diff --git a/OWNERS b/OWNERS index c24680e..b0e134e 100644 --- a/OWNERS +++ b/OWNERS
@@ -1,2 +1,3 @@ +# Bug component: 31808 set noparent file:platform/packages/modules/Connectivity:main:/OWNERS_core_networking
diff --git a/apishim/common/com/android/networkstack/apishim/common/NetworkInformationShim.java b/apishim/common/com/android/networkstack/apishim/common/NetworkInformationShim.java index e7f7b3d..2fcd8c6 100644 --- a/apishim/common/com/android/networkstack/apishim/common/NetworkInformationShim.java +++ b/apishim/common/com/android/networkstack/apishim/common/NetworkInformationShim.java
@@ -100,7 +100,7 @@ } /** - * @see NetworkCapabilites#getUnderlyingNetworks() + * @see NetworkCapabilities#getUnderlyingNetworks() */ @Nullable default List<Network> getUnderlyingNetworks(@NonNull NetworkCapabilities nc) {
diff --git a/common/networkstackclient/Android.bp b/common/networkstackclient/Android.bp index 0571042..e056e3b 100644 --- a/common/networkstackclient/Android.bp +++ b/common/networkstackclient/Android.bp
@@ -160,6 +160,10 @@ version: "18", imports: ["ipmemorystore-aidl-interfaces-V10"], }, + { + version: "19", + imports: ["ipmemorystore-aidl-interfaces-V10"], + }, ], @@ -174,7 +178,7 @@ min_sdk_version: "30", static_libs: [ "ipmemorystore-aidl-interfaces-V10-java", - "networkstack-aidl-interfaces-V18-java", + "networkstack-aidl-interfaces-V19-java", ], visibility: ["//packages/modules/NetworkStack:__subpackages__"], apex_available: [
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/.hash b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/.hash new file mode 100644 index 0000000..5e94944 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/.hash
@@ -0,0 +1 @@ +ffc74fbac5dcfe825bc16b9a3a91b43251476361
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/DataStallReportParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/DataStallReportParcelable.aidl new file mode 100644 index 0000000..771deda --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/DataStallReportParcelable.aidl
@@ -0,0 +1,42 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable DataStallReportParcelable { + long timestampMillis = 0; + int detectionMethod = 1; + int tcpPacketFailRate = 2; + int tcpMetricsCollectionPeriodMillis = 3; + int dnsConsecutiveTimeouts = 4; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/DhcpResultsParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/DhcpResultsParcelable.aidl new file mode 100644 index 0000000..31f2194 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/DhcpResultsParcelable.aidl
@@ -0,0 +1,44 @@ +/** + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable DhcpResultsParcelable { + android.net.StaticIpConfiguration baseConfiguration; + int leaseDuration; + int mtu; + String serverAddress; + String vendorInfo; + @nullable String serverHostName; + @nullable String captivePortalApiUrl; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkMonitor.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkMonitor.aidl new file mode 100644 index 0000000..fb13c0c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkMonitor.aidl
@@ -0,0 +1,60 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetworkMonitor { + oneway void start(); + oneway void launchCaptivePortalApp(); + oneway void notifyCaptivePortalAppFinished(int response); + oneway void setAcceptPartialConnectivity(); + oneway void forceReevaluation(int uid); + oneway void notifyPrivateDnsChanged(in android.net.PrivateDnsConfigParcel config); + oneway void notifyDnsResponse(int returnCode); + oneway void notifyNetworkConnected(in android.net.LinkProperties lp, in android.net.NetworkCapabilities nc); + oneway void notifyNetworkDisconnected(); + oneway void notifyLinkPropertiesChanged(in android.net.LinkProperties lp); + oneway void notifyNetworkCapabilitiesChanged(in android.net.NetworkCapabilities nc); + oneway void notifyNetworkConnectedParcel(in android.net.networkstack.aidl.NetworkMonitorParameters params); + const int NETWORK_TEST_RESULT_VALID = 0; + const int NETWORK_TEST_RESULT_INVALID = 1; + const int NETWORK_TEST_RESULT_PARTIAL_CONNECTIVITY = 2; + const int NETWORK_VALIDATION_RESULT_VALID = 0x01; + const int NETWORK_VALIDATION_RESULT_PARTIAL = 0x02; + const int NETWORK_VALIDATION_RESULT_SKIPPED = 0x04; + const int NETWORK_VALIDATION_PROBE_DNS = 0x04; + const int NETWORK_VALIDATION_PROBE_HTTP = 0x08; + const int NETWORK_VALIDATION_PROBE_HTTPS = 0x10; + const int NETWORK_VALIDATION_PROBE_FALLBACK = 0x20; + const int NETWORK_VALIDATION_PROBE_PRIVDNS = 0x40; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkMonitorCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkMonitorCallbacks.aidl new file mode 100644 index 0000000..36eda8e --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkMonitorCallbacks.aidl
@@ -0,0 +1,46 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetworkMonitorCallbacks { + oneway void onNetworkMonitorCreated(in android.net.INetworkMonitor networkMonitor) = 0; + oneway void notifyNetworkTested(int testResult, @nullable String redirectUrl) = 1; + oneway void notifyPrivateDnsConfigResolved(in android.net.PrivateDnsConfigParcel config) = 2; + oneway void showProvisioningNotification(String action, String packageName) = 3; + oneway void hideProvisioningNotification() = 4; + oneway void notifyProbeStatusChanged(int probesCompleted, int probesSucceeded) = 5; + oneway void notifyNetworkTestedWithExtras(in android.net.NetworkTestResultParcelable result) = 6; + oneway void notifyDataStallSuspected(in android.net.DataStallReportParcelable report) = 7; + oneway void notifyCaptivePortalDataChanged(in android.net.CaptivePortalData data) = 8; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkStackConnector.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkStackConnector.aidl new file mode 100644 index 0000000..8120ffc --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkStackConnector.aidl
@@ -0,0 +1,42 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetworkStackConnector { + oneway void makeDhcpServer(in String ifName, in android.net.dhcp.DhcpServingParamsParcel params, in android.net.dhcp.IDhcpServerCallbacks cb); + oneway void makeNetworkMonitor(in android.net.Network network, String name, in android.net.INetworkMonitorCallbacks cb); + oneway void makeIpClient(in String ifName, in android.net.ip.IIpClientCallbacks callbacks); + oneway void fetchIpMemoryStore(in android.net.IIpMemoryStoreCallbacks cb); + oneway void allowTestUid(int uid, in android.net.INetworkStackStatusCallback cb); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkStackStatusCallback.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkStackStatusCallback.aidl new file mode 100644 index 0000000..0b6b778 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/INetworkStackStatusCallback.aidl
@@ -0,0 +1,38 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +/* @hide */ +interface INetworkStackStatusCallback { + oneway void onStatusAvailable(int statusCode); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/InformationElementParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/InformationElementParcelable.aidl new file mode 100644 index 0000000..6103774 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/InformationElementParcelable.aidl
@@ -0,0 +1,39 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable InformationElementParcelable { + int id; + byte[] payload; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/InitialConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/InitialConfigurationParcelable.aidl new file mode 100644 index 0000000..6a597e6 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/InitialConfigurationParcelable.aidl
@@ -0,0 +1,41 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable InitialConfigurationParcelable { + android.net.LinkAddress[] ipAddresses; + android.net.IpPrefix[] directlyConnectedRoutes; + String[] dnsServers; + String gateway; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/Layer2InformationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/Layer2InformationParcelable.aidl new file mode 100644 index 0000000..83796ee --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/Layer2InformationParcelable.aidl
@@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable Layer2InformationParcelable { + String l2Key; + String cluster; + android.net.MacAddress bssid; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/Layer2PacketParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/Layer2PacketParcelable.aidl new file mode 100644 index 0000000..4b3fff5 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/Layer2PacketParcelable.aidl
@@ -0,0 +1,39 @@ +/** + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable Layer2PacketParcelable { + android.net.MacAddress dstMacAddress; + byte[] payload; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/NattKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/NattKeepalivePacketDataParcelable.aidl new file mode 100644 index 0000000..18cf954 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/NattKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,41 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable NattKeepalivePacketDataParcelable { + byte[] srcAddress; + int srcPort; + byte[] dstAddress; + int dstPort; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/NetworkTestResultParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/NetworkTestResultParcelable.aidl new file mode 100644 index 0000000..4d6d5a2 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/NetworkTestResultParcelable.aidl
@@ -0,0 +1,42 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable NetworkTestResultParcelable { + long timestampMillis; + int result; + int probesSucceeded; + int probesAttempted; + String redirectUrl; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/PrivateDnsConfigParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/PrivateDnsConfigParcel.aidl new file mode 100644 index 0000000..ab62fe7 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/PrivateDnsConfigParcel.aidl
@@ -0,0 +1,44 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(equals=true, toString=true) +parcelable PrivateDnsConfigParcel { + String hostname; + String[] ips; + int privateDnsMode = (-1) /* -1 */; + String dohName = ""; + String[] dohIps = {}; + String dohPath = ""; + int dohPort = (-1) /* -1 */; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ProvisioningConfigurationParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ProvisioningConfigurationParcelable.aidl new file mode 100644 index 0000000..fba524b --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ProvisioningConfigurationParcelable.aidl
@@ -0,0 +1,63 @@ +/* +** +** 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. +*/ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable ProvisioningConfigurationParcelable { + /** + * @deprecated use ipv4ProvisioningMode instead. + */ + boolean enableIPv4; + /** + * @deprecated use ipv6ProvisioningMode instead. + */ + boolean enableIPv6; + boolean usingMultinetworkPolicyTracker; + boolean usingIpReachabilityMonitor; + int requestedPreDhcpActionMs; + android.net.InitialConfigurationParcelable initialConfig; + android.net.StaticIpConfiguration staticIpConfig; + android.net.apf.ApfCapabilities apfCapabilities; + int provisioningTimeoutMs; + int ipv6AddrGenMode; + android.net.Network network; + String displayName; + boolean enablePreconnection; + @nullable android.net.ScanResultInfoParcelable scanResultInfo; + @nullable android.net.Layer2InformationParcelable layer2Info; + @nullable List<android.net.networkstack.aidl.dhcp.DhcpOption> options; + int ipv4ProvisioningMode; + int ipv6ProvisioningMode; + boolean uniqueEui64AddressesOnly; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ScanResultInfoParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ScanResultInfoParcelable.aidl new file mode 100644 index 0000000..94fc27f --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ScanResultInfoParcelable.aidl
@@ -0,0 +1,40 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable ScanResultInfoParcelable { + String ssid; + String bssid; + android.net.InformationElementParcelable[] informationElements; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/TcpKeepalivePacketDataParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/TcpKeepalivePacketDataParcelable.aidl new file mode 100644 index 0000000..0e1c21c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/TcpKeepalivePacketDataParcelable.aidl
@@ -0,0 +1,47 @@ +/* + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net; +@JavaDerive(toString=true) +parcelable TcpKeepalivePacketDataParcelable { + byte[] srcAddress; + int srcPort; + byte[] dstAddress; + int dstPort; + int seq; + int ack; + int rcvWnd; + int rcvWndScale; + int tos; + int ttl; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/DhcpLeaseParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/DhcpLeaseParcelable.aidl new file mode 100644 index 0000000..3cd8860 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/DhcpLeaseParcelable.aidl
@@ -0,0 +1,43 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.dhcp; +@JavaDerive(toString=true) +parcelable DhcpLeaseParcelable { + byte[] clientId; + byte[] hwAddr; + int netAddr; + int prefixLength; + long expTime; + String hostname; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/DhcpServingParamsParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/DhcpServingParamsParcel.aidl new file mode 100644 index 0000000..7997936 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/DhcpServingParamsParcel.aidl
@@ -0,0 +1,49 @@ +/** + * + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.dhcp; +@JavaDerive(toString=true) +parcelable DhcpServingParamsParcel { + int serverAddr; + int serverAddrPrefixLength; + int[] defaultRouters; + int[] dnsServers; + int[] excludedAddrs; + long dhcpLeaseTimeSecs; + int linkMtu; + boolean metered; + int singleClientAddr = 0; + boolean changePrefixOnDecline = false; + int leasesSubnetPrefixLength = 0; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/IDhcpEventCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/IDhcpEventCallbacks.aidl new file mode 100644 index 0000000..9312f47 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/IDhcpEventCallbacks.aidl
@@ -0,0 +1,38 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.dhcp; +interface IDhcpEventCallbacks { + oneway void onLeasesChanged(in List<android.net.dhcp.DhcpLeaseParcelable> newLeases); + oneway void onNewPrefixRequest(in android.net.IpPrefix currentPrefix); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/IDhcpServer.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/IDhcpServer.aidl new file mode 100644 index 0000000..1109f35 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/IDhcpServer.aidl
@@ -0,0 +1,45 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.dhcp; +/* @hide */ +interface IDhcpServer { + oneway void start(in android.net.INetworkStackStatusCallback cb) = 0; + oneway void startWithCallbacks(in android.net.INetworkStackStatusCallback statusCb, in android.net.dhcp.IDhcpEventCallbacks eventCb) = 3; + oneway void updateParams(in android.net.dhcp.DhcpServingParamsParcel params, in android.net.INetworkStackStatusCallback cb) = 1; + oneway void stop(in android.net.INetworkStackStatusCallback cb) = 2; + const int STATUS_UNKNOWN = 0; + const int STATUS_SUCCESS = 1; + const int STATUS_INVALID_ARGUMENT = 2; + const int STATUS_UNKNOWN_ERROR = 3; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/IDhcpServerCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/IDhcpServerCallbacks.aidl new file mode 100644 index 0000000..ab8577c --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/dhcp/IDhcpServerCallbacks.aidl
@@ -0,0 +1,38 @@ +/** + * 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 perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.dhcp; +/* @hide */ +interface IDhcpServerCallbacks { + oneway void onDhcpServerCreated(int statusCode, in android.net.dhcp.IDhcpServer server); +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ip/IIpClient.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ip/IIpClient.aidl new file mode 100644 index 0000000..b81ec20 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ip/IIpClient.aidl
@@ -0,0 +1,59 @@ +/** + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.ip; +/* @hide */ +interface IIpClient { + oneway void completedPreDhcpAction(); + oneway void confirmConfiguration(); + oneway void readPacketFilterComplete(in byte[] data); + oneway void shutdown(); + oneway void startProvisioning(in android.net.ProvisioningConfigurationParcelable req); + oneway void stop(); + oneway void setTcpBufferSizes(in String tcpBufferSizes); + oneway void setHttpProxy(in android.net.ProxyInfo proxyInfo); + oneway void setMulticastFilter(boolean enabled); + oneway void addKeepalivePacketFilter(int slot, in android.net.TcpKeepalivePacketDataParcelable pkt); + oneway void removeKeepalivePacketFilter(int slot); + oneway void setL2KeyAndGroupHint(in String l2Key, in String cluster); + oneway void addNattKeepalivePacketFilter(int slot, in android.net.NattKeepalivePacketDataParcelable pkt); + oneway void notifyPreconnectionComplete(boolean success); + oneway void updateLayer2Information(in android.net.Layer2InformationParcelable info); + oneway void updateApfCapabilities(in android.net.apf.ApfCapabilities apfCapabilities); + const int PROV_IPV4_DISABLED = 0x00; + const int PROV_IPV4_STATIC = 0x01; + const int PROV_IPV4_DHCP = 0x02; + const int PROV_IPV6_DISABLED = 0x00; + const int PROV_IPV6_SLAAC = 0x01; + const int PROV_IPV6_LINKLOCAL = 0x02; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ip/IIpClientCallbacks.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ip/IIpClientCallbacks.aidl new file mode 100644 index 0000000..9d36419 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/ip/IIpClientCallbacks.aidl
@@ -0,0 +1,54 @@ +/** + * 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.ip; +/* @hide */ +interface IIpClientCallbacks { + oneway void onIpClientCreated(in android.net.ip.IIpClient ipClient); + oneway void onPreDhcpAction(); + oneway void onPostDhcpAction(); + oneway void onNewDhcpResults(in android.net.DhcpResultsParcelable dhcpResults); + oneway void onProvisioningSuccess(in android.net.LinkProperties newLp); + oneway void onProvisioningFailure(in android.net.LinkProperties newLp); + oneway void onLinkPropertiesChange(in android.net.LinkProperties newLp); + oneway void onReachabilityLost(in String logMsg); + oneway void onQuit(); + oneway void installPacketFilter(in byte[] filter); + oneway void startReadPacketFilter(); + oneway void setFallbackMulticastFilter(boolean enabled); + oneway void setNeighborDiscoveryOffload(boolean enable); + oneway void onPreconnectionStart(in List<android.net.Layer2PacketParcelable> packets); + oneway void onReachabilityFailure(in android.net.networkstack.aidl.ip.ReachabilityLossInfoParcelable lossInfo); + oneway void setMaxDtimMultiplier(int multiplier); + const int DTIM_MULTIPLIER_RESET = 0; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/NetworkMonitorParameters.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/NetworkMonitorParameters.aidl new file mode 100644 index 0000000..2ab9db0 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/NetworkMonitorParameters.aidl
@@ -0,0 +1,41 @@ +/** + * + * Copyright (C) 2022 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.networkstack.aidl; +@JavaDerive(equals=true, toString=true) +parcelable NetworkMonitorParameters { + android.net.NetworkAgentConfig networkAgentConfig; + android.net.NetworkCapabilities networkCapabilities; + android.net.LinkProperties linkProperties; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/dhcp/DhcpOption.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/dhcp/DhcpOption.aidl new file mode 100644 index 0000000..eea3e0d --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/dhcp/DhcpOption.aidl
@@ -0,0 +1,39 @@ +/** + * Copyright (c) 2020, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing perNmissions and + * limitations under the License. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.networkstack.aidl.dhcp; +@JavaDerive(toString=true) +parcelable DhcpOption { + byte type; + @nullable byte[] value; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable.aidl new file mode 100644 index 0000000..bb88434 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/ip/ReachabilityLossInfoParcelable.aidl
@@ -0,0 +1,39 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.networkstack.aidl.ip; +@JavaDerive(equals=true, toString=true) @JavaOnlyImmutable +parcelable ReachabilityLossInfoParcelable { + String message; + android.net.networkstack.aidl.ip.ReachabilityLossReason reason; +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl new file mode 100644 index 0000000..f9bb3c4 --- /dev/null +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/19/android/net/networkstack/aidl/ip/ReachabilityLossReason.aidl
@@ -0,0 +1,40 @@ +/* + * Copyright (C) 2021 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. + */ +/////////////////////////////////////////////////////////////////////////////// +// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. // +/////////////////////////////////////////////////////////////////////////////// + +// This file is a snapshot of an AIDL file. Do not edit it manually. There are +// two cases: +// 1). this is a frozen version file - do not edit this in any case. +// 2). this is a 'current' file. If you make a backwards compatible change to +// the interface (from the latest frozen version), the build system will +// prompt you to update this file with `m <name>-update-api`. +// +// You must not make a backward incompatible change to any AIDL file built +// with the aidl_interface module type with versions property set. The module +// type is used to build AIDL files in a way that they can be used across +// independently updatable components of the system. If a device is shipped +// with such a backward incompatible change, it has a high risk of breaking +// later when a module using the interface is updated, e.g., Mainline modules. + +package android.net.networkstack.aidl.ip; +@Backing(type="int") +enum ReachabilityLossReason { + ROAM, + CONFIRM, + ORGANIC, +}
diff --git a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/PrivateDnsConfigParcel.aidl b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/PrivateDnsConfigParcel.aidl index 1457caf..ab62fe7 100644 --- a/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/PrivateDnsConfigParcel.aidl +++ b/common/networkstackclient/aidl_api/networkstack-aidl-interfaces/current/android/net/PrivateDnsConfigParcel.aidl
@@ -32,8 +32,13 @@ // later when a module using the interface is updated, e.g., Mainline modules. package android.net; -@JavaDerive(toString=true) +@JavaDerive(equals=true, toString=true) parcelable PrivateDnsConfigParcel { String hostname; String[] ips; + int privateDnsMode = (-1) /* -1 */; + String dohName = ""; + String[] dohIps = {}; + String dohPath = ""; + int dohPort = (-1) /* -1 */; }
diff --git a/common/networkstackclient/src/android/net/PrivateDnsConfigParcel.aidl b/common/networkstackclient/src/android/net/PrivateDnsConfigParcel.aidl index 97bb697..e747d61 100644 --- a/common/networkstackclient/src/android/net/PrivateDnsConfigParcel.aidl +++ b/common/networkstackclient/src/android/net/PrivateDnsConfigParcel.aidl
@@ -16,8 +16,60 @@ package android.net; -@JavaDerive(toString=true) +@JavaDerive(equals=true, toString=true) parcelable PrivateDnsConfigParcel { + /** + * The hostname of private DNS provider. + */ String hostname; + + /** + * The DoT server IP addresses of `hostname`. They are not sorted. + */ String[] ips; + + /** + * The private DNS mode associated with this PrivateDnsConfigParcel. + * If it's set, the value must be one of the following constants defined in + * ConnectivitySettingsManager. + * - PRIVATE_DNS_MODE_OFF (1) + * - PRIVATE_DNS_MODE_OPPORTUNISTIC (2) + * - PRIVATE_DNS_MODE_PROVIDER_HOSTNAME (3) + * + * For compatibility with old PrivateDnsConfigParcel, set the default value to -1 to indicate + * that the sender is using an old version of PrivateDnsConfigParcel and that the receiver + * cannot determine the private DNS mode by reading this field. + */ + int privateDnsMode = -1; + + /** + * The following fields with the prefix "doh" store the DoH3 information discovered from + * DDR. The similar fields are defined in DnsResolver as well. Although duplicating code + * is not a good idea, it avoids the complexity and confusion of having a parcelable + * containing a nested parcelable where the client and server could have a different version + * of the nested parcelable. + */ + + /** + * The DoH server hostname derived from TargetName field of a DNS SVCB response. + */ + String dohName = ""; + + /** + * The DoH server IP addresses of `dohName`. They are not sorted. + */ + String[] dohIps = {}; + + /** + * A part of the URI template used to construct the URL for DNS resolution. + * It's derived only from DNS SVCB SvcParamKey "dohpath". + * The URI template for DNS resolution is as follows: + * https://<dohName>/<dohPath> + */ + String dohPath = ""; + + /** + * The port used to reach the DoH servers. + */ + int dohPort = -1; }
diff --git a/common/networkstackclient/src/android/net/shared/PrivateDnsConfig.java b/common/networkstackclient/src/android/net/shared/PrivateDnsConfig.java index 106ca1c..632d1d6 100644 --- a/common/networkstackclient/src/android/net/shared/PrivateDnsConfig.java +++ b/common/networkstackclient/src/android/net/shared/PrivateDnsConfig.java
@@ -16,9 +16,14 @@ package android.net.shared; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; import static android.net.shared.ParcelableUtil.fromParcelableArray; import static android.net.shared.ParcelableUtil.toParcelableArray; +import android.annotation.NonNull; +import android.annotation.Nullable; import android.net.PrivateDnsConfigParcel; import android.text.TextUtils; @@ -27,50 +32,116 @@ /** @hide */ public class PrivateDnsConfig { - public final boolean useTls; + // These fields store the private DNS configuration from setting. + public final int mode; + @NonNull public final String hostname; + + // Stores the DoT server IP addresses resolved from A/AAAA lookups. + @NonNull public final InetAddress[] ips; + // These fields store the DoH information discovered from SVCB lookups. + @NonNull + public final String dohName; + @NonNull + public final InetAddress[] dohIps; + @NonNull + public final String dohPath; + public final int dohPort; + + /** + * A constructor for off mode private DNS configuration. + * TODO(b/261404136): Consider simplifying the constructors. One possible way is to + * use constants to represent private DNS modes: + * public static PrivateDnsConfig OFF = new PrivateDnsConfig(false); + * public static PrivateDnsConfig OPPORTUNISTIC = new PrivateDnsConfig(true); + * public static PrivateDnsConfig STRICT = new PrivateDnsConfig(String hostname); + */ public PrivateDnsConfig() { this(false); } + /** + * A constructor for off/opportunistic mode private DNS configuration depending on `useTls`. + */ public PrivateDnsConfig(boolean useTls) { - this.useTls = useTls; - this.hostname = ""; - this.ips = new InetAddress[0]; + this(useTls ? PRIVATE_DNS_MODE_OPPORTUNISTIC : PRIVATE_DNS_MODE_OFF, null /* hostname */, + null /* ips */, null /* dohName */, null /* dohIps */, null /* dohPath */, + -1 /* dohPort */); } - public PrivateDnsConfig(String hostname, InetAddress[] ips) { - this.useTls = !TextUtils.isEmpty(hostname); - this.hostname = useTls ? hostname : ""; - this.ips = (ips != null) ? ips : new InetAddress[0]; + /** + * A constructor for off/strict mode private DNS configuration depending on `hostname`. + * If `hostname` is empty or null, this constructor creates a PrivateDnsConfig for off mode; + * otherwise, it creates a PrivateDnsConfig for strict mode. + */ + public PrivateDnsConfig(@Nullable String hostname, @Nullable InetAddress[] ips) { + this(TextUtils.isEmpty(hostname) ? PRIVATE_DNS_MODE_OFF : + PRIVATE_DNS_MODE_PROVIDER_HOSTNAME, hostname, ips, null /* dohName */, + null /* dohIps */, null /* dohPath */, -1 /* dohPort */); + } + + /** + * A constructor for all kinds of private DNS configuration with given DoH information. + * It treats both null values and empty strings as equivalent. Similarly, treats null values + * and empty arrays as equivalent. + */ + public PrivateDnsConfig(int mode, @Nullable String hostname, @Nullable InetAddress[] ips, + @Nullable String dohName, @Nullable InetAddress[] dohIps, @Nullable String dohPath, + int dohPort) { + this.mode = mode; + this.hostname = (hostname != null) ? hostname : ""; + this.ips = (ips != null) ? ips.clone() : new InetAddress[0]; + this.dohName = (dohName != null) ? dohName : ""; + this.dohIps = (dohIps != null) ? dohIps.clone() : new InetAddress[0]; + this.dohPath = (dohPath != null) ? dohPath : ""; + this.dohPort = dohPort; } public PrivateDnsConfig(PrivateDnsConfig cfg) { - useTls = cfg.useTls; + mode = cfg.mode; hostname = cfg.hostname; ips = cfg.ips; + dohName = cfg.dohName; + dohIps = cfg.dohIps; + dohPath = cfg.dohPath; + dohPort = cfg.dohPort; } /** * Indicates whether this is a strict mode private DNS configuration. */ public boolean inStrictMode() { - return useTls && !TextUtils.isEmpty(hostname); + return mode == PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; } /** * Indicates whether this is an opportunistic mode private DNS configuration. */ public boolean inOpportunisticMode() { - return useTls && TextUtils.isEmpty(hostname); + return mode == PRIVATE_DNS_MODE_OPPORTUNISTIC; } @Override public String toString() { return PrivateDnsConfig.class.getSimpleName() - + "{" + useTls + ":" + hostname + "/" + Arrays.toString(ips) + "}"; + + "{" + modeAsString(mode) + ":" + hostname + "/" + Arrays.toString(ips) + + ", dohName=" + dohName + + ", dohIps=" + Arrays.toString(dohIps) + + ", dohPath=" + dohPath + + ", dohPort=" + dohPort + + "}"; + } + + @NonNull + private static String modeAsString(int mode) { + switch (mode) { + case PRIVATE_DNS_MODE_OFF: return "off"; + case PRIVATE_DNS_MODE_OPPORTUNISTIC: return "opportunistic"; + case PRIVATE_DNS_MODE_PROVIDER_HOSTNAME: return "strict"; + default: return "unknown"; + } } /** @@ -81,7 +152,12 @@ parcel.hostname = hostname; parcel.ips = toParcelableArray( Arrays.asList(ips), IpConfigurationParcelableUtil::parcelAddress, String.class); - + parcel.privateDnsMode = mode; + parcel.dohName = dohName; + parcel.dohIps = toParcelableArray( + Arrays.asList(dohIps), IpConfigurationParcelableUtil::parcelAddress, String.class); + parcel.dohPath = dohPath; + parcel.dohPort = dohPort; return parcel; } @@ -92,6 +168,26 @@ InetAddress[] ips = new InetAddress[parcel.ips.length]; ips = fromParcelableArray(parcel.ips, IpConfigurationParcelableUtil::unparcelAddress) .toArray(ips); - return new PrivateDnsConfig(parcel.hostname, ips); + + // For compatibility. If the sender (Tethering module) is using an old version (< 19) of + // NetworkStack AIDL that `privateDnsMode` field is not present, `privateDnsMode` will be + // assigned from the default value -1. Let `privateDnsMode` assigned based on the hostname. + // In this case, there is a harmless bug that the receiver (NetworkStack module) can't + // convert the parcel to a PrivateDnsConfig that indicates opportunistic mode. + // The bug is harmless because 1) the bug exists for years without any problems and + // 2) NetworkMonitor cares PrivateDnsConfig that indicates strict/off mode only. + // If the sender is using new version (>=19) while the receiver is using an old version, + // the above mentioned harmless bug will persist. Except for that harmless bug, there + // should be no other issues. New version's toParcel() doesn't change how the pre-existing + // fields `hostname` and `ips` are assigned. + if (parcel.privateDnsMode == -1) { + return new PrivateDnsConfig(parcel.hostname, ips); + } + + InetAddress[] dohIps = new InetAddress[parcel.dohIps.length]; + dohIps = fromParcelableArray(parcel.dohIps, + IpConfigurationParcelableUtil::unparcelAddress).toArray(dohIps); + return new PrivateDnsConfig(parcel.privateDnsMode, parcel.hostname, ips, parcel.dohName, + dohIps, parcel.dohPath, parcel.dohPort); } }
diff --git a/src/android/net/apf/ApfFilter.java b/src/android/net/apf/ApfFilter.java index 41c6726..3ab5e0e 100644 --- a/src/android/net/apf/ApfFilter.java +++ b/src/android/net/apf/ApfFilter.java
@@ -17,6 +17,8 @@ package android.net.apf; import static android.net.util.SocketUtils.makePacketSocketAddress; +import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED; +import static android.os.PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED; import static android.system.OsConstants.AF_PACKET; import static android.system.OsConstants.ARPHRD_ETHER; import static android.system.OsConstants.ETH_P_ARP; @@ -54,6 +56,7 @@ import android.util.Log; import android.util.SparseArray; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.internal.annotations.GuardedBy; @@ -61,6 +64,7 @@ import com.android.internal.util.HexDump; import com.android.internal.util.IndentingPrintWriter; import com.android.internal.util.TokenBucket; +import com.android.modules.utils.build.SdkLevel; import com.android.net.module.util.CollectionUtils; import com.android.net.module.util.ConnectivityUtils; import com.android.net.module.util.InterfaceParams; @@ -112,6 +116,7 @@ public int[] ethTypeBlackList; public int minRdnssLifetimeSec; public int acceptRaMinLft; + public boolean shouldHandleLightDoze; } /** @@ -132,8 +137,6 @@ PASSED_IPV4_UNICAST, PASSED_IPV6_ICMP, PASSED_IPV6_UNICAST_NON_ICMP, - PASSED_ARP_NON_IPV4, - PASSED_ARP_UNKNOWN, PASSED_ARP_UNICAST_REPLY, PASSED_NON_IP_UNICAST, PASSED_MDNS, @@ -156,7 +159,9 @@ DROPPED_IPV4_KEEPALIVE_ACK, DROPPED_IPV6_KEEPALIVE_ACK, DROPPED_IPV4_NATT_KEEPALIVE, - DROPPED_MDNS; + DROPPED_MDNS, + DROPPED_ARP_NON_IPV4, + DROPPED_ARP_UNKNOWN; // Returns the negative byte offset from the end of the APF data segment for // a given counter. @@ -323,16 +328,48 @@ // Tracks the value of /proc/sys/ipv6/conf/$iface/accept_ra_min_lft which affects router, RIO, // and PIO valid lifetimes. private final int mAcceptRaMinLft; + private final boolean mShouldHandleLightDoze; + + private static boolean isDeviceIdleModeChangedAction(Intent intent) { + return ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction()); + } + + private boolean isDeviceLightIdleModeChangedAction(Intent intent) { + // The ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED only exist since T. For lower platform version, + // the check should return false. The explicit SDK check is needed to make linter happy + // about accessing ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED in this function. + if (!SdkLevel.isAtLeastT()) { + return false; + } + if (!mShouldHandleLightDoze) { + return false; + } + return ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED.equals(intent.getAction()); + } + + private boolean isDeviceLightIdleMode(@NonNull PowerManager powerManager) { + // The powerManager.isDeviceLightIdleMode() only exist since T. For lower platform version, + // the check should return false. The explicit SDK check is needed to make linter happy + // about accessing powerManager.isDeviceLightIdleMode() in this function. + if (!SdkLevel.isAtLeastT()) { + return false; + } + if (!mShouldHandleLightDoze) { + return false; + } + + return powerManager.isDeviceLightIdleMode(); + } // Detects doze mode state transitions. private final BroadcastReceiver mDeviceIdleReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { - String action = intent.getAction(); - if (action.equals(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)) { - PowerManager powerManager = - (PowerManager) context.getSystemService(Context.POWER_SERVICE); - final boolean deviceIdle = powerManager.isDeviceIdleMode(); + final PowerManager powerManager = context.getSystemService(PowerManager.class); + if (isDeviceIdleModeChangedAction(intent) + || isDeviceLightIdleModeChangedAction(intent)) { + final boolean deviceIdle = powerManager.isDeviceIdleMode() + || isDeviceLightIdleMode(powerManager); setDozeMode(deviceIdle); } } @@ -346,9 +383,16 @@ @GuardedBy("this") private int mIPv4PrefixLength; - @VisibleForTesting + private final Dependencies mDependencies; + public ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams, IpClientCallbacksWrapper ipClientCallback) { + this(context, config, ifParams, ipClientCallback, new Dependencies(context)); + } + + @VisibleForTesting + public ApfFilter(Context context, ApfConfiguration config, InterfaceParams ifParams, + IpClientCallbacksWrapper ipClientCallback, Dependencies dependencies) { mApfCapabilities = config.apfCapabilities; mIpClientCallback = ipClientCallback; mInterfaceParams = ifParams; @@ -357,6 +401,8 @@ mMinRdnssLifetimeSec = config.minRdnssLifetimeSec; mAcceptRaMinLft = config.acceptRaMinLft; mContext = context; + mShouldHandleLightDoze = config.shouldHandleLightDoze; + mDependencies = dependencies; if (mApfCapabilities.hasDataAccess()) { mCountAndPassLabel = "countAndPass"; @@ -384,8 +430,33 @@ maybeStartFilter(); // Listen for doze-mode transition changes to enable/disable the IPv6 multicast filter. - mContext.registerReceiver(mDeviceIdleReceiver, - new IntentFilter(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)); + mDependencies.addDeviceIdleReceiver(mDeviceIdleReceiver, mShouldHandleLightDoze); + } + + /** + * Dependencies class for testing. + */ + @VisibleForTesting + public static class Dependencies { + private final Context mContext; + public Dependencies(final Context context) { + mContext = context; + } + + /** Add receiver for detecting doze mode change */ + public void addDeviceIdleReceiver(@NonNull final BroadcastReceiver receiver, + boolean shouldHandleLightDoze) { + final IntentFilter intentFilter = new IntentFilter(ACTION_DEVICE_IDLE_MODE_CHANGED); + if (SdkLevel.isAtLeastT() && shouldHandleLightDoze) { + intentFilter.addAction(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED); + } + mContext.registerReceiver(receiver, intentFilter); + } + + /** Remove broadcast receiver. */ + public void removeBroadcastReceiver(@NonNull final BroadcastReceiver receiver) { + mContext.unregisterReceiver(receiver); + } } public synchronized void setDataSnapshot(byte[] data) { @@ -1360,9 +1431,9 @@ // Here's a basic summary of what the ARP filter program does: // // if not ARP IPv4 - // pass + // drop // if not ARP IPv4 reply or request - // pass + // drop // if ARP reply source ip is 0.0.0.0 // drop // if unicast ARP reply @@ -1377,28 +1448,28 @@ final String checkTargetIPv4 = "checkTargetIPv4"; - // Pass if not ARP IPv4. + // Drop if not ARP IPv4. gen.addLoadImmediate(Register.R0, ARP_HEADER_OFFSET); - maybeSetupCounter(gen, Counter.PASSED_ARP_NON_IPV4); - gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, mCountAndPassLabel); + maybeSetupCounter(gen, Counter.DROPPED_ARP_NON_IPV4); + gen.addJumpIfBytesNotEqual(Register.R0, ARP_IPV4_HEADER, mCountAndDropLabel); - // Pass if unknown ARP opcode. + // Drop if unknown ARP opcode. gen.addLoad16(Register.R0, ARP_OPCODE_OFFSET); gen.addJumpIfR0Equals(ARP_OPCODE_REQUEST, checkTargetIPv4); // Skip to unicast check - maybeSetupCounter(gen, Counter.PASSED_ARP_UNKNOWN); - gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndPassLabel); + maybeSetupCounter(gen, Counter.DROPPED_ARP_UNKNOWN); + gen.addJumpIfR0NotEquals(ARP_OPCODE_REPLY, mCountAndDropLabel); // Drop if ARP reply source IP is 0.0.0.0 gen.addLoad32(Register.R0, ARP_SOURCE_IP_ADDRESS_OFFSET); maybeSetupCounter(gen, Counter.DROPPED_ARP_REPLY_SPA_NO_HOST); gen.addJumpIfR0Equals(IPV4_ANY_HOST_ADDRESS, mCountAndDropLabel); - // Pass if unicast reply. + // Pass if non-broadcast reply. gen.addLoadImmediate(Register.R0, ETH_DEST_ADDR_OFFSET); maybeSetupCounter(gen, Counter.PASSED_ARP_UNICAST_REPLY); gen.addJumpIfBytesNotEqual(Register.R0, ETHER_BROADCAST, mCountAndPassLabel); - // Either a unicast request, a unicast reply, or a broadcast reply. + // Either a request, or a broadcast reply. gen.defineLabel(checkTargetIPv4); if (mIPv4Address == null) { // When there is no IPv4 address, drop GARP replies (b/29404209). @@ -2052,7 +2123,7 @@ mReceiveThread = null; } mRas.clear(); - mContext.unregisterReceiver(mDeviceIdleReceiver); + mDependencies.removeBroadcastReceiver(mDeviceIdleReceiver); } public synchronized void setMulticastFilter(boolean isEnabled) { @@ -2087,6 +2158,11 @@ installNewProgramLocked(); } + @VisibleForTesting + public synchronized boolean isInDozeMode() { + return mInDozeMode; + } + /** Find the single IPv4 LinkAddress if there is one, otherwise return null. */ private static LinkAddress findIPv4LinkAddress(LinkProperties lp) { LinkAddress ipv4Address = null;
diff --git a/src/android/net/apf/ApfGenerator.java b/src/android/net/apf/ApfGenerator.java index 0460c83..e14364e 100644 --- a/src/android/net/apf/ApfGenerator.java +++ b/src/android/net/apf/ApfGenerator.java
@@ -64,7 +64,12 @@ EXT(21), // Followed by immediate indicating ExtendedOpcodes. LDDW(22), // Load 4 bytes from data memory address (register + immediate): "lddw R0, [5]R1" STDW(23), // Store 4 bytes to data memory address (register + immediate): "stdw R0, [5]R1" - WRITE(24); // Write 1, 2 or 4 bytes imm to the output buffer, e.g. "WRITE 5" + WRITE(24), // Write 1, 2 or 4 bytes imm to the output buffer, e.g. "WRITE 5" + // Copy the data from input packet or APF data region to output buffer. Register bit is + // used to specify the source of data copy: R=0 means copy from packet, R=1 means copy + // from APF data region. The source offset is encoded in the first imm and the copy length + // is encoded in the second imm. "e.g. MEMCOPY(R=0), 5, 5" + MEMCOPY(25); final int value; @@ -85,7 +90,13 @@ TRANS(37), // Transmit buffer, "e.g. TRANS R0" EWRITE1(38), // Write 1 byte from register to the output buffer, e.g. "EWRITE1 R0" EWRITE2(39), // Write 2 bytes from register to the output buffer, e.g. "EWRITE2 R0" - EWRITE4(40); // Write 4 bytes from register to the output buffer, e.g. "EWRITE4 R0" + EWRITE4(40), // Write 4 bytes from register to the output buffer, e.g. "EWRITE4 R0" + // Copy the data from input packet to output buffer. The source offset is encoded as [Rx + // + second imm]. The copy length is encoded in the third imm. "e.g. EPKTCOPY [R0 + 5], 5" + EPKTCOPY(41), + // Copy the data from APF data region to output buffer. The source offset is encoded as [Rx + // + second imm]. The copy length is encoded in the third imm. "e.g. EDATACOPY [R0 + 5], 5" + EDATACOPY(42); final int value; @@ -118,12 +129,18 @@ mSigned = signed; mImmSize = size; } + + @Override + public String toString() { + return "Immediate{" + "mSigned=" + mSigned + ", mImmSize=" + mImmSize + ", mValue=" + + mValue + '}'; + } } private class Instruction { private final byte mOpcode; // A "Opcode" value. private final byte mRegister; // A "Register" value. - private final int mMaxSupportedImmediates; + private final int mMaxSupportedImms; public final List<Immediate> mImms = new ArrayList<>(); // When mOpcode is a jump: private byte mTargetLabelSize; @@ -136,13 +153,13 @@ int offset; Instruction(Opcodes opcode, Register register) { - this(opcode, register, 1 /* mMaxSupportedImmediates */); + this(opcode, register, 1 /* maxSupportedImm */); } - Instruction(Opcodes opcode, Register register, int maxSupportedImm) { + Instruction(Opcodes opcode, Register register, int maxSupportedImms) { mOpcode = (byte) opcode.value; mRegister = (byte) register.value; - mMaxSupportedImmediates = maxSupportedImm; + mMaxSupportedImms = maxSupportedImms; } Instruction(Opcodes opcode) { @@ -162,10 +179,10 @@ } void addImm(Immediate imm) { - if (mImms.size() == mMaxSupportedImmediates) { + if (mImms.size() == mMaxSupportedImms) { throw new IllegalArgumentException( String.format("Opcode: %d only support at max: %d imms", mOpcode, - mMaxSupportedImmediates)); + mMaxSupportedImms)); } mImms.add(imm); } @@ -201,9 +218,20 @@ return 0; } int size = 1; - size += mImms.size() * generatedImmSize(); + byte maxImmSize = getMaxImmSize(); + // For the copy opcode, the last imm is the length field is always 1 byte + if (isCopyOpCode()) { + if (mMaxSupportedImms != mImms.size()) { + throw new IllegalStateException( + "mImm size: " + mImms.size() + " doesn't match the mMaxSupportedImms: " + + mMaxSupportedImms); + } + size += (mImms.size() - 1) * maxImmSize + mImms.get(mImms.size() - 1).mImmSize; + } else { + size += mImms.size() * maxImmSize; + } if (mTargetLabel != null) { - size += generatedImmSize(); + size += maxImmSize; } if (mCompareBytes != null) { size += mCompareBytes.length; @@ -233,7 +261,7 @@ * Assemble value for instruction size field. */ private byte generateImmSizeField() { - byte immSize = generatedImmSize(); + byte immSize = getMaxImmSize(); // Encode size field to fit in 2 bits: 0->0, 1->1, 2->2, 3->4. return immSize == 4 ? 3 : immSize; } @@ -248,15 +276,15 @@ /** * Write {@code value} at offset {@code writingOffset} into {@code bytecode}. - * {@link generatedImmSize} bytes are written. {@code value} is truncated to - * {@code generatedImmSize} bytes. {@code value} is treated simply as a + * {@code immSize} bytes are written. {@code value} is truncated to + * {@code immSize} bytes. {@code value} is treated simply as a * 32-bit value, so unsigned values should be zero extended and the truncation * should simply throw away their zero-ed upper bits, and signed values should * be sign extended and the truncation should simply throw away their signed * upper bits. */ - private int writeValue(int value, byte[] bytecode, int writingOffset) { - for (int i = generatedImmSize() - 1; i >= 0; i--) { + private int writeValue(int value, byte[] bytecode, int writingOffset, byte immSize) { + for (int i = immSize - 1; i >= 0; i--) { bytecode[writingOffset++] = (byte)((value >> (i * 8)) & 255); } return writingOffset; @@ -271,11 +299,29 @@ } int writingOffset = offset; bytecode[writingOffset++] = generateInstructionByte(); + byte maxImmSize = getMaxImmSize(); if (mTargetLabel != null) { - writingOffset = writeValue(calculateTargetLabelOffset(), bytecode, writingOffset); + writingOffset = writeValue(calculateTargetLabelOffset(), bytecode, writingOffset, + maxImmSize); } - for (int i = 0; i < mImms.size(); ++i) { - writingOffset = writeValue(mImms.get(i).mValue, bytecode, writingOffset); + // For the copy opcode, the last imm is the length field is always 1 byte + if (isCopyOpCode()) { + if (mMaxSupportedImms != mImms.size()) { + throw new IllegalStateException( + "mImm size: " + mImms.size() + " doesn't match the mMaxSupportedImms: " + + mMaxSupportedImms); + } + int i; + for (i = 0; i < mImms.size() - 1; ++i) { + writingOffset = writeValue(mImms.get(i).mValue, bytecode, writingOffset, + maxImmSize); + } + writingOffset = writeValue(mImms.get(i).mValue, bytecode, writingOffset, + mImms.get(i).mImmSize); + } else { + for (Immediate imm : mImms) { + writingOffset = writeValue(imm.mValue, bytecode, writingOffset, maxImmSize); + } } if (mCompareBytes != null) { System.arraycopy(mCompareBytes, 0, bytecode, writingOffset, mCompareBytes.length); @@ -287,6 +333,20 @@ } } + private boolean isCopyOpCode() { + if (mOpcode == Opcodes.MEMCOPY.value) { + return true; + } + if (mOpcode == Opcodes.EXT.value) { + int realOpcode = mImms.get(0).mValue; + if (realOpcode == ExtendedOpcodes.EPKTCOPY.value + || realOpcode == ExtendedOpcodes.EDATACOPY.value) { + return true; + } + } + return false; + } + /** * Calculate the size of either the immediate fields or the target label field, if either is * present. Most instructions have either immediates or a target label field, but for the @@ -295,7 +355,7 @@ * byte, hence why this function simply takes the maximum of those sizes, so neither is * truncated. */ - private byte generatedImmSize() { + private byte getMaxImmSize() { byte maxSize = mTargetLabelSize; for (int i = 0; i < mImms.size(); ++i) { maxSize = (byte) Math.max(maxSize, mImms.get(i).mImmSize); @@ -936,6 +996,116 @@ } /** + * Add an instruction to the end of the program to copy data from APF data region to output + * buffer. + * + * @param srcOffset the offset inside the APF data region for where to start copy + * @param length the length of bytes needed to be copied, only <= 255 bytes can be copied at + * one time. + * @return the ApfGenerator object + * @throws IllegalInstructionException throws when imm size is incorrectly set. + */ + public ApfGenerator addDataCopy(int srcOffset, int length) + throws IllegalInstructionException { + return addMemCopy(srcOffset, length, Register.R1); + } + + /** + * Add an instruction to the end of the program to copy data from input packet to output buffer. + * + * @param srcOffset the offset inside the input packet for where to start copy + * @param length the length of bytes needed to be copied, only <= 255 bytes can be copied at + * one time. + * @return the ApfGenerator object + * @throws IllegalInstructionException throws when imm size is incorrectly set. + */ + public ApfGenerator addPacketCopy(int srcOffset, int length) + throws IllegalInstructionException { + return addMemCopy(srcOffset, length, Register.R0); + } + + private ApfGenerator addMemCopy(int srcOffset, int length, Register register) + throws IllegalInstructionException { + requireApfVersion(5); + checkCopyLength(length); + checkCopyOffset(srcOffset); + Instruction instruction = new Instruction(Opcodes.MEMCOPY, + register, 2 /* maxSupportedImms */); + // if the offset == 0, it should still be encoded with 1 byte size. + if (srcOffset == 0) { + instruction.addUnsignedImm(srcOffset, (byte) 1 /* size */); + } else { + instruction.addUnsignedImm(srcOffset); + } + instruction.addUnsignedImm(length, (byte) 1 /* size */); + addInstruction(instruction); + return this; + } + + /** + * Add an instruction to the end of the program to copy data from APF data region to output + * buffer. + * + * @param register the register that stored the base offset value. + * @param relativeOffset the offset inside the APF data region for where to start copy + * @param length the length of bytes needed to be copied, only <= 255 bytes can be copied at + * one time. + * @return the ApfGenerator object + * @throws IllegalInstructionException throws when imm size is incorrectly set. + */ + public ApfGenerator addDataCopy(Register register, int relativeOffset, int length) + throws IllegalInstructionException { + return addMemcopy(register, relativeOffset, length, ExtendedOpcodes.EDATACOPY.value); + } + + /** + * Add an instruction to the end of the program to copy data from input packet to output buffer. + * + * @param register the register that stored the base offset value. + * @param relativeOffset the offset inside the input packet for where to start copy + * @param length the length of bytes needed to be copied, only <= 255 bytes can be copied at + * one time. + * @return the ApfGenerator object + * @throws IllegalInstructionException throws when imm size is incorrectly set. + */ + public ApfGenerator addPacketCopy(Register register, int relativeOffset, int length) + throws IllegalInstructionException { + return addMemcopy(register, relativeOffset, length, ExtendedOpcodes.EPKTCOPY.value); + } + + private ApfGenerator addMemcopy(Register register, int relativeOffset, int length, int opcode) + throws IllegalInstructionException { + requireApfVersion(5); + checkCopyLength(length); + checkCopyOffset(relativeOffset); + Instruction instruction = new Instruction(Opcodes.EXT, register, 3 /* maxSupportedImms */); + instruction.addUnsignedImm(opcode); + // if the offset == 0, it should still be encoded with 1 byte size. + if (relativeOffset == 0) { + instruction.addUnsignedImm(relativeOffset, (byte) 1 /* size */); + } else { + instruction.addUnsignedImm(relativeOffset); + } + instruction.addUnsignedImm(length, (byte) 1 /* size */); + addInstruction(instruction); + return this; + } + + private void checkCopyLength(int length) { + if (length < 0 || length > 255) { + throw new IllegalArgumentException( + "copy length must between 0 to 255, length: " + length); + } + } + + private void checkCopyOffset(int offset) { + if (offset < 0) { + throw new IllegalArgumentException( + "offset must be non less than zero, offset: " + offset); + } + } + + /** * Add an instruction to the end of the program to load 32 bits from the data memory into * {@code register}. The source address is computed by adding the signed immediate * @{code offset} to the other register.
diff --git a/src/android/net/dhcp/DhcpClient.java b/src/android/net/dhcp/DhcpClient.java index b09a2b1..4353746 100644 --- a/src/android/net/dhcp/DhcpClient.java +++ b/src/android/net/dhcp/DhcpClient.java
@@ -53,7 +53,6 @@ import static com.android.net.module.util.NetworkStackConstants.IPV4_CONFLICT_PROBE_NUM; import static com.android.net.module.util.SocketUtils.closeSocketQuietly; import static com.android.networkstack.util.NetworkStackUtils.DHCP_INIT_REBOOT_VERSION; -import static com.android.networkstack.util.NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION; import static com.android.networkstack.util.NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION; import static com.android.networkstack.util.NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION; import static com.android.networkstack.util.NetworkStackUtils.DHCP_SLOW_RETRANSMISSION_VERSION; @@ -302,9 +301,7 @@ if (isCapportApiEnabled()) { params.write(DHCP_CAPTIVE_PORTAL); } - if (isIPv6OnlyPreferredModeEnabled()) { - params.write(DHCP_IPV6_ONLY_PREFERRED); - } + params.write(DHCP_IPV6_ONLY_PREFERRED); // Customized DHCP options to be put in PRL. for (DhcpOption option : mConfiguration.options) { if (option.value == null) params.write(option.type); @@ -567,16 +564,6 @@ } /** - * check whether or not to support IPv6-only preferred option. - * - * IPv6-only preferred option is enabled by default if there is no experiment flag set to - * disable this feature explicitly. - */ - public boolean isIPv6OnlyPreferredModeEnabled() { - return mDependencies.isFeatureNotChickenedOut(mContext, DHCP_IPV6_ONLY_PREFERRED_VERSION); - } - - /** * Check whether to adopt slow DHCPREQUEST retransmission approach in Renewing/Rebinding state * suggested in RFC2131 section 4.4.5. */ @@ -648,7 +635,6 @@ private byte[] getOptionsToSkip() { final ByteArrayOutputStream optionsToSkip = new ByteArrayOutputStream(2); if (!isCapportApiEnabled()) optionsToSkip.write(DHCP_CAPTIVE_PORTAL); - if (!isIPv6OnlyPreferredModeEnabled()) optionsToSkip.write(DHCP_IPV6_ONLY_PREFERRED); return optionsToSkip.toByteArray(); } @@ -1292,7 +1278,6 @@ } private boolean maybeTransitionToIpv6OnlyWaitState(@NonNull final DhcpPacket packet) { - if (!isIPv6OnlyPreferredModeEnabled()) return false; if (packet.getIpv6OnlyWaitTimeMillis() == DhcpPacket.V6ONLY_PREFERRED_ABSENCE) return false; mIpv6OnlyWaitTimeMs = packet.getIpv6OnlyWaitTimeMillis();
diff --git a/src/android/net/dhcp6/Dhcp6Client.java b/src/android/net/dhcp6/Dhcp6Client.java index 7359c7c..a107b9c 100644 --- a/src/android/net/dhcp6/Dhcp6Client.java +++ b/src/android/net/dhcp6/Dhcp6Client.java
@@ -59,6 +59,7 @@ import com.android.net.module.util.InterfaceParams; import com.android.net.module.util.PacketReader; import com.android.net.module.util.netlink.NetlinkUtils; +import com.android.net.module.util.structs.IaPrefixOption; import java.io.FileDescriptor; import java.io.IOException; @@ -66,6 +67,7 @@ import java.net.SocketException; import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.Collections; import java.util.Random; import java.util.function.IntSupplier; @@ -282,17 +284,27 @@ scheduleKick(); } - private void handleReceivedPacket(Dhcp6Packet packet) { + private void handleReceivedPacket(@NonNull final Dhcp6Packet packet) { // Technically it is valid for the server to not include a prefix in an IA in certain // scenarios (specifically in a reply to Renew / Rebind, which means: do not extend the - // prefix). However, while only supporting a single prefix, this never works well, so if - // the server decides to do so, ignore it. - // TODO: revisit this when adding multi-prefix support. - final boolean validIpo = packet.mPrefixDelegation.ipo != null - && packet.mPrefixDelegation.ipo.isValid(); - if (packet.isValid(mTransId, mClientDuid) && validIpo) { - receivePacket(packet); + // prefix, e.g. the list of prefix is empty). However, if prefix(es) do exist and all + // prefixes are invalid, then we should just ignore this packet. + if (!packet.isValid(mTransId, mClientDuid)) return; + if (!packet.mPrefixDelegation.ipos.isEmpty()) { + boolean allInvalidPrefixes = true; + for (IaPrefixOption ipo : packet.mPrefixDelegation.ipos) { + if (ipo != null && ipo.isValid()) { + allInvalidPrefixes = false; + break; + } + } + if (allInvalidPrefixes) { + Log.w(TAG, "All IA_Prefix options included in the " + + packet.getClass().getSimpleName() + " are invalid, ignore it."); + return; + } } + receivePacket(packet); } @Override @@ -377,17 +389,18 @@ // prevent packet storms due to low timeouts. int renewTimeout = mReply.t1; int rebindTimeout = mReply.t2; - final long expirationTimeout = mReply.ipo.valid; + final long preferredTimeout = mReply.getMinimalPreferredLifetime(); + final long expirationTimeout = mReply.getMinimalValidLifetime(); // rfc8415#section-14.2: if t1 and / or t2 are 0, the client chooses an appropriate value. // rfc8415#section-21.21: Recommended values for T1 and T2 are 0.5 and 0.8 times the // shortest preferred lifetime of the prefixes in the IA_PD that the server is willing to // extend, respectively. if (renewTimeout == 0) { - renewTimeout = (int) (mReply.ipo.preferred * 0.5); + renewTimeout = (int) (preferredTimeout * 0.5); } if (rebindTimeout == 0) { - rebindTimeout = (int) (mReply.ipo.preferred * 0.8); + rebindTimeout = (int) (preferredTimeout * 0.8); } // Note: message validation asserts that the received t1 <= t2 if both t1 > 0 and t2 > 0. @@ -461,16 +474,6 @@ return transmitPacket(packet, "rebind"); } - private ByteBuffer buildEmptyIaPdOption() { - return Dhcp6Packet.buildIaPdOption(IAID, 0 /* t1 */, 0 /* t2 */, 0 /* preferred */, - 0 /* valid */, new byte[16] /* empty prefix */, (byte) RFC7421_PREFIX_LENGTH); - } - - private ByteBuffer buildIaPdOption(@NonNull final PrefixDelegation pd) { - return Dhcp6Packet.buildIaPdOption(pd.iaid, pd.t1, pd.t2, pd.ipo.preferred, pd.ipo.valid, - pd.ipo.prefix, pd.ipo.prefixLen); - } - /** * Parent state at which client does initialization of interface and packet handler, also * processes the CMD_STOP_DHCP6 command in this state which child states don't handle. @@ -545,7 +548,12 @@ @Override protected boolean sendPacket(int transId, long elapsedTimeMs) { - return sendSolicitPacket(transId, elapsedTimeMs, buildEmptyIaPdOption()); + final IaPrefixOption hintOption = new IaPrefixOption((short) IaPrefixOption.LENGTH, + 0 /* preferred */, 0 /* valid */, (byte) RFC7421_PREFIX_LENGTH, + new byte[16] /* empty prefix */); + final PrefixDelegation pd = new PrefixDelegation(IAID, 0 /* t1 */, 0 /* t2 */, + Collections.singletonList(hintOption)); + return sendSolicitPacket(transId, elapsedTimeMs, pd.build()); } // TODO: support multiple prefixes. @@ -585,7 +593,7 @@ @Override protected boolean sendPacket(int transId, long elapsedTimeMs) { - return sendRequestPacket(transId, elapsedTimeMs, buildIaPdOption(mAdvertise)); + return sendRequestPacket(transId, elapsedTimeMs, mAdvertise.build()); } @Override @@ -630,6 +638,33 @@ } } + // Create an IPv6 address from the interface mac address with IFA_F_MANAGETEMPADDR + // flag, kernel will create another privacy IPv6 address on behalf of user space. + // We don't need to remember IPv6 addresses that need to extend the lifetime every + // time it enters BoundState. + private boolean addInterfaceAddress(@NonNull final Inet6Address address, + @NonNull final IaPrefixOption ipo) { + final int flags = IFA_F_NOPREFIXROUTE | IFA_F_MANAGETEMPADDR | IFA_F_NODAD; + final long now = SystemClock.elapsedRealtime(); + final long deprecationTime = now + ipo.preferred; + final long expirationTime = now + ipo.valid; + final LinkAddress la = new LinkAddress(address, RFC7421_PREFIX_LENGTH, flags, + RT_SCOPE_UNIVERSE /* scope */, deprecationTime, expirationTime); + if (!la.isGlobalPreferred()) { + Log.e(TAG, la + " is not a global preferred IPv6 address"); + return false; + } + if (!NetlinkUtils.sendRtmNewAddressRequest(mIface.index, address, + (short) RFC7421_PREFIX_LENGTH, + flags, (byte) RT_SCOPE_UNIVERSE /* scope */, + ipo.preferred, ipo.valid)) { + Log.e(TAG, "Failed to set IPv6 address " + address.getHostAddress() + + "%" + mIface.index); + return false; + } + return true; + } + /** * Client has already obtained the lease(e.g. IA_PD option) from server and stays in Bound * state until T1 expires, and then transition to Renew state to extend the lease duration. @@ -642,34 +677,21 @@ // TODO: roll back to SOLICIT state after a delay if something wrong happens // instead of returning directly. - // The server may assign a prefix with length less than 64. To support automatic address - // generation (with IFA_F_MANAGETEMPADDR), we always set the address prefix length to - // 64, even if the delegated prefix length is less than 64. However, the unreachable - // route should still use the assigned prefix length. - final IpPrefix routePrefix = mReply.ipo.getIpPrefix(); - final IpPrefix addressPrefix = new IpPrefix(routePrefix.getAddress(), - RFC7421_PREFIX_LENGTH); - // Create EUI-64, so we don't need to remember IPv6 addresses that need to extend the - // lifetime every time it enters BoundState. - final Inet6Address address = createInet6AddressFromEui64(addressPrefix, - macAddressToEui64(mIface.macAddr)); - final int flags = IFA_F_NOPREFIXROUTE | IFA_F_MANAGETEMPADDR | IFA_F_NODAD; - final long now = SystemClock.elapsedRealtime(); - final long deprecationTime = now + mReply.ipo.preferred; - final long expirationTime = now + mReply.ipo.valid; - final LinkAddress la = new LinkAddress(address, RFC7421_PREFIX_LENGTH, flags, - RT_SCOPE_UNIVERSE /* scope */, deprecationTime, expirationTime); - if (!la.isGlobalPreferred()) { - Log.e(TAG, la + " is not a global IPv6 address, ignoring"); - return; - } - if (!NetlinkUtils.sendRtmNewAddressRequest(mIface.index, address, - (short) RFC7421_PREFIX_LENGTH, - flags, (byte) RT_SCOPE_UNIVERSE /* scope */, - mReply.ipo.preferred, mReply.ipo.valid)) { - Log.e(TAG, "Failed to set IPv6 address " + address.getHostAddress() - + "%" + mIface.index); - return; + for (IaPrefixOption ipo : mReply.getValidIaPrefixes()) { + // TODO: The prefix with preferred/valid lifetime of 0 is valid, but client + // should stop using the prefix immediately. Actually kernel doesn't accept + // the address with valid lifetime of 0 and returns EINVAL when it sees that. + // We should send RTM_DELADDR netlink message to kernel to delete these addresses + // from the interface if any. + // Configure IPv6 addresses based on the delegated prefix(es) on the interface. + // We've checked that delegated prefix is valid upon receiving the response from + // DHCPv6 server, and the server may assign a prefix with length less than 64. So + // for SLAAC use case we always set the prefix length to 64 even if the delegated + // prefix length is less than 64. + final IpPrefix prefix = ipo.getIpPrefix(); + final Inet6Address address = createInet6AddressFromEui64(prefix, + macAddressToEui64(mIface.macAddr)); + if (!addInterfaceAddress(address, ipo)) continue; } notifyPrefixDelegation(DHCP6_PD_SUCCESS, mReply); } @@ -701,11 +723,13 @@ protected void receivePacket(Dhcp6Packet packet) { if (!(packet instanceof Dhcp6ReplyPacket)) return; final PrefixDelegation pd = packet.mPrefixDelegation; - if (!(Arrays.equals(pd.ipo.prefix, mReply.ipo.prefix) - && pd.ipo.prefixLen == mReply.ipo.prefixLen)) { - Log.i(TAG, "Renewal prefix " + HexDump.toHexString(pd.ipo.prefix) + final IaPrefixOption request = mReply.ipos.get(0); + final IaPrefixOption response = pd.ipos.get(0); + if (!(Arrays.equals(request.prefix, response.prefix) + && request.prefixLen == response.prefixLen)) { + Log.i(TAG, "Renewal prefix " + HexDump.toHexString(response.prefix) + " does not match current prefix " - + HexDump.toHexString(mReply.ipo.prefix)); + + HexDump.toHexString(request.prefix)); notifyPrefixDelegation(DHCP6_PD_PREFIX_CHANGED, null); transitionTo(mSolicitState); return; @@ -748,7 +772,7 @@ @Override protected boolean sendPacket(int transId, long elapsedTimeMs) { - return sendRenewPacket(transId, elapsedTimeMs, buildIaPdOption(mReply)); + return sendRenewPacket(transId, elapsedTimeMs, mReply.build()); } } @@ -764,7 +788,7 @@ @Override protected boolean sendPacket(int transId, long elapsedTimeMs) { - return sendRebindPacket(transId, elapsedTimeMs, buildIaPdOption(mReply)); + return sendRebindPacket(transId, elapsedTimeMs, mReply.build()); } }
diff --git a/src/android/net/dhcp6/Dhcp6Packet.java b/src/android/net/dhcp6/Dhcp6Packet.java index 540a670..7a977e5 100644 --- a/src/android/net/dhcp6/Dhcp6Packet.java +++ b/src/android/net/dhcp6/Dhcp6Packet.java
@@ -22,8 +22,8 @@ import android.util.Log; import androidx.annotation.NonNull; -import androidx.annotation.VisibleForTesting; +import com.android.internal.annotations.VisibleForTesting; import com.android.internal.util.HexDump; import com.android.net.module.util.Struct; import com.android.net.module.util.structs.IaPdOption; @@ -33,7 +33,11 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Objects; import java.util.OptionalInt; /** @@ -120,6 +124,11 @@ protected PrefixDelegation mPrefixDelegation; /** + * DHCPv6 Optional Type: IA Prefix Option. + */ + public static final byte DHCP6_IAPREFIX = 26; + + /** * DHCPv6 Optional Type: SOL_MAX_RT. */ public static final byte DHCP6_SOL_MAX_RT = 82; @@ -155,6 +164,14 @@ } /** + * Returns decoded IA_PD options associated with IA_ID. + */ + @VisibleForTesting + public PrefixDelegation getPrefixDelegation() { + return mPrefixDelegation; + } + + /** * Returns IA_ID associated to IA_PD. */ public int getIaId() { @@ -188,20 +205,23 @@ * https://www.rfc-editor.org/rfc/rfc8415.html#section-21.21 */ public static class PrefixDelegation { - public int iaid; - public int t1; - public int t2; - public final IaPrefixOption ipo; + public final int iaid; + public final int t1; + public final int t2; + @NonNull + public final List<IaPrefixOption> ipos; - PrefixDelegation(int iaid, int t1, int t2, final IaPrefixOption ipo) { + public PrefixDelegation(int iaid, int t1, int t2, + @NonNull final List<IaPrefixOption> ipos) { + Objects.requireNonNull(ipos); this.iaid = iaid; this.t1 = t1; this.t2 = t2; - this.ipo = ipo; + this.ipos = ipos; } /** - * Check whether or not the delegated prefix in DHCPv6 packet is valid. + * Check whether or not the IA_PD option in DHCPv6 message is valid. * * TODO: ensure that the prefix has a reasonable lifetime, and the timers aren't too short. */ @@ -214,7 +234,6 @@ Log.e(TAG, "IA_PD option with invalid T1 " + t1 + " or T2 " + t2); return false; } - // Generally, t1 must be smaller or equal to t2 (except when t2 is 0). if (t2 != 0 && t1 > t2) { Log.e(TAG, "IA_PD option with T1 " + t1 + " greater than T2 " + t2); @@ -223,10 +242,95 @@ return true; } + /** + * Decode an IA_PD option from the byte buffer. + */ + public static PrefixDelegation decode(@NonNull final ByteBuffer buffer) + throws ParseException { + try { + final int iaid = buffer.getInt(); + final int t1 = buffer.getInt(); + final int t2 = buffer.getInt(); + final List<IaPrefixOption> ipos = new ArrayList<IaPrefixOption>(); + while (buffer.remaining() > 0) { + final int original = buffer.position(); + final short optionType = buffer.getShort(); + final int optionLen = buffer.getShort() & 0xFFFF; + switch (optionType) { + case DHCP6_IAPREFIX: + buffer.position(original); + final IaPrefixOption ipo = Struct.parse(IaPrefixOption.class, buffer); + Log.d(TAG, "IA Prefix Option: " + ipo); + ipos.add(ipo); + break; + // TODO: support DHCP6_STATUS_CODE option + default: + skipOption(buffer, optionLen); + } + } + return new PrefixDelegation(iaid, t1, t2, ipos); + } catch (BufferUnderflowException e) { + throw new ParseException(e.getMessage()); + } + } + + /** + * Build an IA_PD option from given specific parameters, including IA_PREFIX options. + */ + public ByteBuffer build() { + final ByteBuffer iapd = ByteBuffer.allocate(IaPdOption.LENGTH + + Struct.getSize(IaPrefixOption.class) * ipos.size()); + iapd.putInt(iaid); + iapd.putInt(t1); + iapd.putInt(t2); + for (IaPrefixOption ipo : ipos) { + ipo.writeToByteBuffer(iapd); + } + iapd.flip(); + return iapd; + } + + /** + * Return valid IA prefix options to be used and extended in the Reply message. It may + * return empty list if there isn't any valid IA prefix option in the Reply message. + * + * TODO: ensure that the prefix has a reasonable lifetime, and the timers aren't too short. + * and handle status code such as NoPrefixAvail. + */ + public List<IaPrefixOption> getValidIaPrefixes() { + final List<IaPrefixOption> validIpos = new ArrayList<IaPrefixOption>(); + for (IaPrefixOption ipo : ipos) { + if (!ipo.isValid()) continue; + validIpos.add(ipo); + } + return validIpos; + } + @Override public String toString() { return "Prefix Delegation: iaid " + iaid + ", t1 " + t1 + ", t2 " + t2 - + ", prefix " + ipo; + + ", IA prefix options: " + ipos; + } + + /** + * Compare the preferred lifetime in the IA prefix optin list and return the minimum one. + * TODO: exclude 0 preferred lifetime. + */ + public long getMinimalPreferredLifetime() { + final IaPrefixOption ipo = Collections.min(ipos, + (IaPrefixOption lhs, IaPrefixOption rhs) -> Long.compare(lhs.preferred, + rhs.preferred)); + return ipo.preferred; + } + + /** + * Compare the valid lifetime in the IA prefix optin list and return the minimum one. + * TODO: exclude 0 valid lifetime. + */ + public long getMinimalValidLifetime() { + final IaPrefixOption ipo = Collections.min(ipos, + (IaPrefixOption lhs, IaPrefixOption rhs) -> Long.compare(lhs.valid, rhs.valid)); + return ipo.valid; } } @@ -293,8 +397,7 @@ * | | * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ - @VisibleForTesting - static Dhcp6Packet decode(@NonNull final ByteBuffer packet) throws ParseException { + private static Dhcp6Packet decode(@NonNull final ByteBuffer packet) throws ParseException { int elapsedTime = 0; byte[] iapd = null; byte[] serverDuid = null; @@ -303,6 +406,7 @@ String statusMsg = null; boolean rapidCommit = false; int solMaxRt = 0; + PrefixDelegation pd = null; packet.order(ByteOrder.BIG_ENDIAN); @@ -347,6 +451,7 @@ final byte[] bytes = new byte[expectedLen]; packet.get(bytes, 0 /* offset */, expectedLen); iapd = bytes; + pd = PrefixDelegation.decode(ByteBuffer.wrap(iapd)); break; case DHCP6_RAPID_COMMIT: expectedLen = 0; @@ -410,14 +515,9 @@ throw new ParseException("Unimplemented DHCP6 message type %d" + messageType); } - if (iapd != null) { - final ByteBuffer buffer = ByteBuffer.wrap(iapd); - final int iaid = buffer.getInt(); - final int t1 = buffer.getInt(); - final int t2 = buffer.getInt(); - final IaPrefixOption ipo = Struct.parse(IaPrefixOption.class, buffer); - newPacket.mPrefixDelegation = new PrefixDelegation(iaid, t1, t2, ipo); - newPacket.mIaId = iaid; + if (pd != null) { + newPacket.mPrefixDelegation = pd; + newPacket.mIaId = pd.iaid; } newPacket.mStatusCode = statusCode; newPacket.mStatusMsg = statusMsg; @@ -528,23 +628,6 @@ } /** - * Build an IA_PD option from given specific parameters, including IA_PREFIX option. - */ - public static ByteBuffer buildIaPdOption(int iaid, int t1, int t2, long preferred, long valid, - final byte[] prefix, byte prefixLen) { - final ByteBuffer iapd = ByteBuffer.allocate(IaPdOption.LENGTH - + Struct.getSize(IaPrefixOption.class)); - iapd.putInt(iaid); - iapd.putInt(t1); - iapd.putInt(t2); - final ByteBuffer prefixOption = IaPrefixOption.build((short) IaPrefixOption.LENGTH, - preferred, valid, prefixLen, prefix); - iapd.put(prefixOption); - iapd.flip(); - return iapd; - } - - /** * Builds a DHCPv6 SOLICIT packet from the required specified parameters. */ public static ByteBuffer buildSolicitPacket(int transId, long millisecs,
diff --git a/src/android/net/ip/IpClient.java b/src/android/net/ip/IpClient.java index 11cdd20..9747c2c 100644 --- a/src/android/net/ip/IpClient.java +++ b/src/android/net/ip/IpClient.java
@@ -40,10 +40,12 @@ import static com.android.net.module.util.NetworkStackConstants.IPV6_ADDR_ALL_ROUTERS_MULTICAST; import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH; import static com.android.net.module.util.NetworkStackConstants.VENDOR_SPECIFIC_IE_ID; +import static com.android.networkstack.util.NetworkStackUtils.APF_HANDLE_LIGHT_DOZE_FORCE_DISABLE; +import static com.android.networkstack.util.NetworkStackUtils.APF_NEW_RA_FILTER_VERSION; import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_DHCPV6_PREFIX_DELEGATION_VERSION; import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_GARP_NA_ROAMING_VERSION; import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_GRATUITOUS_NA_VERSION; -import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_IGNORE_LOW_RA_LIFETIME_FORCE_DISABLE; +import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_IGNORE_LOW_RA_LIFETIME_VERSION; import static com.android.networkstack.util.NetworkStackUtils.IPCLIENT_MULTICAST_NS_VERSION; import static com.android.server.util.PermissionUtil.enforceNetworkStackCallingPermission; @@ -122,6 +124,7 @@ import com.android.net.module.util.arp.ArpPacket; import com.android.net.module.util.ip.InterfaceController; import com.android.net.module.util.netlink.NetlinkUtils; +import com.android.net.module.util.structs.IaPrefixOption; import com.android.networkstack.R; import com.android.networkstack.apishim.NetworkInformationShimImpl; import com.android.networkstack.apishim.SocketUtilsShimImpl; @@ -668,6 +671,9 @@ // Experiment flag read from device config. private final boolean mDhcp6PrefixDelegationEnabled; + private final boolean mUseNewApfFilter; + private final boolean mEnableIpClientIgnoreLowRaLifetime; + private final boolean mApfShouldHandleLightDoze; private InterfaceParams mInterfaceParams; @@ -822,8 +828,8 @@ */ public AndroidPacketFilter maybeCreateApfFilter(Context context, ApfFilter.ApfConfiguration config, InterfaceParams ifParams, - IpClientCallbacksWrapper cb) { - if (isFeatureEnabled(context, NetworkStackUtils.APF_NEW_RA_FILTER_VERSION)) { + IpClientCallbacksWrapper cb, boolean useNewApfFilter) { + if (useNewApfFilter) { return ApfFilter.maybeCreate(context, config, ifParams, cb); } else { return LegacyApfFilter.maybeCreate(context, config, ifParams, cb); @@ -884,6 +890,12 @@ CONFIG_MIN_RDNSS_LIFETIME, DEFAULT_MIN_RDNSS_LIFETIME); mAcceptRaMinLft = mDependencies.getDeviceConfigPropertyInt(CONFIG_ACCEPT_RA_MIN_LFT, DEFAULT_ACCEPT_RA_MIN_LFT); + mUseNewApfFilter = mDependencies.isFeatureEnabled(context, APF_NEW_RA_FILTER_VERSION); + mEnableIpClientIgnoreLowRaLifetime = mDependencies.isFeatureEnabled(context, + IPCLIENT_IGNORE_LOW_RA_LIFETIME_VERSION); + // Light doze mode status checking API is only available at T or later releases. + mApfShouldHandleLightDoze = SdkLevel.isAtLeastT() && mDependencies.isFeatureNotChickenedOut( + mContext, APF_HANDLE_LIGHT_DOZE_FORCE_DISABLE); IpClientLinkObserver.Configuration config = new IpClientLinkObserver.Configuration( mMinRdnssLifetimeSec); @@ -1293,7 +1305,8 @@ IndentingPrintWriter pw = new IndentingPrintWriter(writer, " "); pw.println(mTag + " APF dump:"); pw.increaseIndent(); - if (apfFilter != null) { + if (apfFilter != null && apfCapabilities != null + && apfCapabilities.apfVersionSupported > 0) { if (apfCapabilities.hasDataAccess()) { // Request a new snapshot, then wait for it. mApfDataSnapshotComplete.close(); @@ -1721,24 +1734,24 @@ // [4] Add in data from DHCPv6 Prefix Delegation, if available. if (mPrefixDelegation != null) { - try { - final IpPrefix destination = - new IpPrefix(Inet6Address.getByAddress(mPrefixDelegation.ipo.prefix), - mPrefixDelegation.ipo.prefixLen); - // Direct-connected route to delegated prefix. Add RTN_UNREACHABLE to this route - // based on the delegated prefix. To prevent the traffic loop between host and - // upstream delegated router. Because we specify the IFA_F_NOPREFIXROUTE when adding - // the IPv6 address, the kernel does not create a delegated prefix route, as a - // result, the user space won't receive any RTM_NEWROUTE message about the delegated - // prefix, we still need to install an unreachable route for the delegated prefix - // manually in LinkProperties to notify the caller this update. - // TODO: support RTN_BLACKHOLE in netd and use that on newer Android versions. - final RouteInfo route = new RouteInfo(destination, null /* gateway */, - mInterfaceName, RTN_UNREACHABLE); - newLp.addRoute(route); - } catch (UnknownHostException e) { - Log.wtf(mTag, "Invalid delegated prefix " - + HexDump.toHexString(mPrefixDelegation.ipo.prefix)); + for (IaPrefixOption ipo : mPrefixDelegation.ipos) { + try { + final IpPrefix destination = + new IpPrefix(Inet6Address.getByAddress(ipo.prefix), ipo.prefixLen); + // Direct-connected route to delegated prefix. Add RTN_UNREACHABLE to this route + // based on the delegated prefix. To prevent the traffic loop between host and + // upstream delegated router. Because we specify the IFA_F_NOPREFIXROUTE when + // adding the IPv6 address, the kernel does not create a delegated prefix route, + // as a result, the user space won't receive any RTM_NEWROUTE message about the + // delegated prefix, we still need to install an unreachable route for the + // delegated prefix manually in LinkProperties to notify the caller this update. + // TODO: support RTN_BLACKHOLE in netd and use that on newer Android versions. + final RouteInfo route = new RouteInfo(destination, null /* gateway */, + mInterfaceName, RTN_UNREACHABLE); + newLp.addRoute(route); + } catch (UnknownHostException e) { + Log.wtf(mTag, "Invalid delegated prefix " + HexDump.toHexString(ipo.prefix)); + } } } @@ -2160,10 +2173,10 @@ setIpv6Sysctl(DAD_TRANSMITS, 0 /* dad_transmits */); } } - // Check chickened out flag first before reading IPv6 sysctl, which can prevent from + // Check the feature flag first before reading IPv6 sysctl, which can prevent from // triggering a potential kernel bug about the sysctl. - if (mDependencies.isFeatureNotChickenedOut(mContext, - IPCLIENT_IGNORE_LOW_RA_LIFETIME_FORCE_DISABLE) + // TODO: add unit test to check if the setIpv6Sysctl() is called or not. + if (mEnableIpClientIgnoreLowRaLifetime && mUseNewApfFilter && mDependencies.hasIpv6Sysctl(mInterfaceName, ACCEPT_RA_MIN_LFT)) { setIpv6Sysctl(ACCEPT_RA_MIN_LFT, mAcceptRaMinLft); } @@ -2244,8 +2257,7 @@ setIpv6Sysctl(ACCEPT_RA, 2); setIpv6Sysctl(ACCEPT_RA_DEFRTR, 1); maybeRestoreDadTransmits(); - if (mDependencies.isFeatureNotChickenedOut(mContext, - IPCLIENT_IGNORE_LOW_RA_LIFETIME_FORCE_DISABLE) + if (mUseNewApfFilter && mEnableIpClientIgnoreLowRaLifetime && mDependencies.hasIpv6Sysctl(mInterfaceName, ACCEPT_RA_MIN_LFT)) { setIpv6Sysctl(ACCEPT_RA_MIN_LFT, 0 /* sysctl default */); } @@ -2353,8 +2365,9 @@ apfConfig.minRdnssLifetimeSec = mMinRdnssLifetimeSec; apfConfig.acceptRaMinLft = mAcceptRaMinLft; + apfConfig.shouldHandleLightDoze = mApfShouldHandleLightDoze; return mDependencies.maybeCreateApfFilter(mContext, apfConfig, mInterfaceParams, - mCallback); + mCallback, mUseNewApfFilter); } private boolean handleUpdateApfCapabilities(@NonNull final ApfCapabilities apfCapabilities) { @@ -2855,13 +2868,12 @@ Log.wtf(mTag, "PrefixDelegation shouldn't be null when DHCPv6 PD fails."); return; } + final IaPrefixOption ipo = mPrefixDelegation.ipos.get(0); final IpPrefix prefix; try { - prefix = new IpPrefix(Inet6Address.getByAddress(mPrefixDelegation.ipo.prefix), - RFC7421_PREFIX_LENGTH); + prefix = new IpPrefix(Inet6Address.getByAddress(ipo.prefix), RFC7421_PREFIX_LENGTH); } catch (UnknownHostException e) { - Log.wtf(TAG, "Invalid delegated prefix " - + HexDump.toHexString(mPrefixDelegation.ipo.prefix)); + Log.wtf(TAG, "Invalid delegated prefix " + HexDump.toHexString(ipo.prefix)); return; }
diff --git a/src/com/android/networkstack/metrics/stats.proto b/src/com/android/networkstack/metrics/stats.proto index c09f082..bd5e62b 100644 --- a/src/com/android/networkstack/metrics/stats.proto +++ b/src/com/android/networkstack/metrics/stats.proto
@@ -188,3 +188,73 @@ // NUD neighbor type, default gateway, DNS server or both. optional .android.stats.connectivity.NudNeighborType neighbor_type = 3; } + +/** + * Logs Ip client RA(Router Advertisement) info + * Logged from: + * packages/modules/NetworkStack/src/android/net/ip/IpClient.java + */ +message IpClientRaInfoReported { + // The maximum number of distinct RAs (Router Advertisements). + optional int32 max_number_of_distinct_ras = 1; + + // The number of zero lifetime RAs (Router Advertisements). + optional int32 number_of_zero_lifetime_ras = 2; + + // The number of parsing error for RAs (Router Advertisements). + optional int32 number_of_parsing_error_ras = 3; + + // The lowest router lifetime in seconds. + optional int32 lowest_router_lifetime_seconds = 4; + + // The lowest valid lifetime of PIO (Prefix Information Option) in seconds. + optional int32 lowest_pio_valid_lifetime_seconds = 5; + + // The lowest route lifetime of RIO (Route Information Option) in seconds. + optional int32 lowest_rio_route_lifetime_seconds = 6; + + // The lowest lifetime of RDNSS (Recursive DNS Server Option) in seconds. + optional int32 lowest_rdnss_lifetime_seconds = 7; +} + +/** + * Logs value of the APF counter. + */ +message ApfCounter { + // The name of APF counter. + optional .android.stats.connectivity.CounterName counter_name = 1; + + // The value of APF counter. + optional int32 counter_value = 2; +} + + +message ApfCounterList { + repeated ApfCounter apf_counter = 1; +} + +/** + * Logs APF session information event. + * Logged from: + * packages/modules/NetworkStack/src/android/net/apf/ApfFilter.java or + * packages/modules/NetworkStack/src/android/net/apf/LegacyApfFilter.java + */ +message ApfSessionInfoReported { + // The version of APF, where version = -1 equals APF disable. + optional int32 version = 1; + + // The memory size of APF module. + optional int32 memory_size = 2; + + // The values of all APF counters. + optional ApfCounterList apf_counter_list = 3; + + // The duration of ip client in milliseconds. + optional int32 ip_client_session_duration_ms = 4; + + // Number of times APF program updated. + optional int32 num_of_times_apf_program_updated = 5; + + // Record the maximum of program size. + optional int32 max_program_size = 6; +}
diff --git a/src/com/android/networkstack/netlink/TcpInfo.java b/src/com/android/networkstack/netlink/TcpInfo.java index 73d206e..de450e9 100644 --- a/src/com/android/networkstack/netlink/TcpInfo.java +++ b/src/com/android/networkstack/netlink/TcpInfo.java
@@ -170,7 +170,8 @@ } } - private static String decodeWscale(byte num) { + @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE) + static String decodeWscale(byte num) { return String.valueOf((num >> 4) & 0x0f) + ":" + String.valueOf(num & 0x0f); }
diff --git a/src/com/android/networkstack/netlink/TcpSocketTracker.java b/src/com/android/networkstack/netlink/TcpSocketTracker.java index d28f4b5..658fe8a 100644 --- a/src/com/android/networkstack/netlink/TcpSocketTracker.java +++ b/src/com/android/networkstack/netlink/TcpSocketTracker.java
@@ -27,12 +27,14 @@ import static android.system.OsConstants.SOL_SOCKET; import static android.system.OsConstants.SO_SNDTIMEO; +import static com.android.net.module.util.FeatureVersions.FEATURE_IS_UID_NETWORKING_BLOCKED; import static com.android.net.module.util.NetworkStackConstants.DNS_OVER_TLS_PORT; import static com.android.net.module.util.netlink.NetlinkConstants.NLMSG_DONE; import static com.android.net.module.util.netlink.NetlinkConstants.SOCKDIAG_MSG_HEADER_SIZE; import static com.android.net.module.util.netlink.NetlinkConstants.SOCK_DIAG_BY_FAMILY; import static com.android.net.module.util.netlink.NetlinkUtils.DEFAULT_RECV_BUFSIZE; import static com.android.net.module.util.netlink.NetlinkUtils.IO_TIMEOUT_MS; +import static com.android.networkstack.util.NetworkStackUtils.IGNORE_TCP_INFO_FOR_BLOCKED_UIDS; import static com.android.networkstack.util.NetworkStackUtils.SKIP_TCP_POLL_IN_LIGHT_DOZE; import android.annotation.TargetApi; @@ -40,10 +42,12 @@ import android.content.Context; import android.content.Intent; import android.content.IntentFilter; +import android.net.ConnectivityManager; import android.net.INetd; import android.net.LinkProperties; import android.net.MarkMaskParcel; import android.net.Network; +import android.net.NetworkCapabilities; import android.os.AsyncTask; import android.os.Build; import android.os.IBinder; @@ -68,9 +72,9 @@ import com.android.net.module.util.DeviceConfigUtils; import com.android.net.module.util.SocketUtils; import com.android.net.module.util.netlink.InetDiagMessage; -import com.android.net.module.util.netlink.NetlinkConstants; import com.android.net.module.util.netlink.NetlinkUtils; import com.android.net.module.util.netlink.StructInetDiagMsg; +import com.android.net.module.util.netlink.StructNlAttr; import com.android.net.module.util.netlink.StructNlMsgHdr; import com.android.networkstack.apishim.NetworkShimImpl; import com.android.networkstack.apishim.common.UnsupportedApiLevelException; @@ -129,6 +133,8 @@ private int mMinPacketsThreshold = DEFAULT_DATA_STALL_MIN_PACKETS_THRESHOLD; private int mTcpPacketsFailRateThreshold = DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE; + // TODO: Remove doze mode solution since uid networking blocked traffic is filtered out by + // the info provided by bpf maps. private final Object mDozeModeLock = new Object(); @GuardedBy("mDozeModeLock") private boolean mInDozeMode = false; @@ -139,8 +145,13 @@ private boolean mInOpportunisticMode; @NonNull private LinkProperties mLinkProperties; + @NonNull + private NetworkCapabilities mNetworkCapabilities; + private final boolean mShouldDisableInDeepDoze; private final boolean mShouldDisableInLightDoze; + private final boolean mShouldIgnoreTcpInfoForBlockedUids; + private final ConnectivityManager mCm; @VisibleForTesting protected final DeviceConfig.OnPropertiesChangedListener mConfigListener = @@ -158,8 +169,9 @@ } }; - private static boolean isDeviceIdleModeChangedAction(Intent intent) { - return ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction()); + private boolean isDeviceIdleModeChangedAction(Intent intent) { + return mShouldDisableInDeepDoze + && ACTION_DEVICE_IDLE_MODE_CHANGED.equals(intent.getAction()); } @TargetApi(Build.VERSION_CODES.TIRAMISU) @@ -180,7 +192,8 @@ // For tcp polling mechanism, there is no difference between deep doze mode and // light doze mode. The deep doze mode and light doze mode block networking // for uids in the same way, use single variable to control. - final boolean deviceIdle = powerManager.isDeviceIdleMode() + final boolean deviceIdle = (mShouldDisableInDeepDoze + && powerManager.isDeviceIdleMode()) || (mShouldDisableInLightDoze && powerManager.isDeviceLightIdleMode()); setDozeMode(deviceIdle); } @@ -191,7 +204,15 @@ mDependencies = dps; mNetwork = network; mNetd = mDependencies.getNetd(); - mShouldDisableInLightDoze = mDependencies.shouldDisableInLightDoze(); + mShouldIgnoreTcpInfoForBlockedUids = mDependencies.shouldIgnoreTcpInfoForBlockedUids(); + + // Previous workarounds can be disabled if the device supports ignore blocked uids feature. + // To prevent inconsistencies and issues like broadcast receiver leaks, the feature flags + // are fixed after being read. + // TODO: Remove these workarounds when pre-T devices are no longer supported. + mShouldDisableInLightDoze = mDependencies.shouldDisableInLightDoze( + mShouldIgnoreTcpInfoForBlockedUids); + mShouldDisableInDeepDoze = !mShouldIgnoreTcpInfoForBlockedUids; // If the parcel is null, nothing should be matched which is achieved by the combination of // {@code NetlinkUtils#NULL_MASK} and {@code NetlinkUtils#UNKNOWN_MARK}. @@ -205,7 +226,9 @@ family, InetDiagMessage.buildInetDiagReqForAliveTcpSockets(family)); } mDependencies.addDeviceConfigChangedListener(mConfigListener); - mDependencies.addDeviceIdleReceiver(mDeviceIdleReceiver, mShouldDisableInLightDoze); + mDependencies.addDeviceIdleReceiver(mDeviceIdleReceiver, mShouldDisableInDeepDoze, + mShouldDisableInLightDoze); + mCm = mDependencies.getContext().getSystemService(ConnectivityManager.class); } @Nullable @@ -214,9 +237,9 @@ final int netId = NetworkShimImpl.newInstance(mNetwork).getNetId(); return mNetd.getFwmarkForNetwork(netId); } catch (UnsupportedApiLevelException e) { - log("Get netId is not available in this API level."); + logd("Get netId is not available in this API level."); } catch (RemoteException e) { - Log.e(TAG, "Error getting fwmark for network, ", e); + loge("Error getting fwmark for network, ", e); } return null; } @@ -246,12 +269,13 @@ mDependencies.sendPollingRequest(fd, mSockDiagMsg.get(family)); while (parseMessage(mDependencies.recvMessage(fd), family, newSocketInfoList, time)) { - log("Pending info exist. Attempt to read more"); + logd("Pending info exist. Attempt to read more"); } } // Append TcpStats based on previous and current socket info. final TcpStat stat = new TcpStat(); + final ArrayList<Integer> skippedBlockedUids = new ArrayList<>(); mLatestReportedUids.clear(); for (final SocketInfo newInfo : newSocketInfoList) { final TcpStat diff = calculateLatestPacketsStat(newInfo, @@ -271,11 +295,28 @@ continue; } + if (mShouldIgnoreTcpInfoForBlockedUids) { + // For backward-compatibility, NET_CAPABILITY_TEMPORARILY_NOT_METERED + // is not referenced when deciding meteredness in NetworkPolicyManagerService. + // Thus, whether to block metered networking should only be judged with + // NET_CAPABILITY_NOT_METERED. + final boolean metered = !mNetworkCapabilities.hasCapability( + NetworkCapabilities.NET_CAPABILITY_NOT_METERED); + final boolean uidBlocked = mCm.isUidNetworkingBlocked(newInfo.uid, metered); + if (uidBlocked) { + skippedBlockedUids.add(newInfo.uid); + continue; + } + } + if (diff != null) { mLatestReportedUids.add(newInfo.uid); stat.accumulate(diff); } } + if (!skippedBlockedUids.isEmpty()) { + logd("Skip blocked uids: " + skippedBlockedUids); + } // Calculate mLatestReceiveCount, mSentSinceLastRecv and mLatestPacketFailPercentage. mSentSinceLastRecv = (stat.receivedCount == 0) @@ -288,7 +329,7 @@ cleanupSocketInfo(time); return true; } catch (ErrnoException | SocketException | InterruptedIOException e) { - Log.e(TAG, "Fail to get TCP info via netlink.", e); + loge("Fail to get TCP info via netlink.", e); } finally { SocketUtils.closeSocketQuietly(fd); } @@ -302,11 +343,11 @@ // Return true if there are more pending messages to read @VisibleForTesting - static boolean parseMessage(ByteBuffer bytes, int family, + boolean parseMessage(ByteBuffer bytes, int family, ArrayList<SocketInfo> outputSocketInfoList, long time) { if (!NetlinkUtils.enoughBytesRemainForValidNlMsg(bytes)) { // This is unlikely to happen in real cases. Check this first for testing. - Log.e(TAG, "Size is less than header size. Ignored."); + loge("Size is less than header size. Ignored."); return false; } @@ -340,12 +381,12 @@ outputSocketInfoList.add(info); } while (NetlinkUtils.enoughBytesRemainForValidNlMsg(bytes)); } catch (IllegalArgumentException | BufferUnderflowException e) { - Log.wtf(TAG, "Unexpected socket info parsing, family " + family + logwtf("Unexpected socket info parsing, family " + family + " buffer:" + bytes + " " + Base64.getEncoder().encodeToString(bytes.array()), e); return false; } catch (IllegalStateException e) { - Log.e(TAG, "Unexpected socket info parsing, family " + family + loge("Unexpected socket info parsing, family " + family + " buffer:" + bytes + " " + Base64.getEncoder().encodeToString(bytes.array()), e); return false; @@ -354,21 +395,21 @@ return true; } - private static int getLengthAndVerifyMsgHeader(@NonNull ByteBuffer bytes, int family) { + private int getLengthAndVerifyMsgHeader(@NonNull ByteBuffer bytes, int family) { final StructNlMsgHdr nlmsghdr = StructNlMsgHdr.parse(bytes); if (nlmsghdr == null) { - Log.e(TAG, "Badly formatted data."); + loge("Badly formatted data."); return END_OF_PARSING; } - log("pollSocketsInfo: nlmsghdr=" + nlmsghdr + ", limit=" + bytes.limit()); + logd("pollSocketsInfo: nlmsghdr=" + nlmsghdr + ", limit=" + bytes.limit()); // End of the message. Stop parsing. if (nlmsghdr.nlmsg_type == NLMSG_DONE) { return END_OF_PARSING; } if (nlmsghdr.nlmsg_type != SOCK_DIAG_BY_FAMILY) { - Log.e(TAG, "Expect to get family " + family + loge("Expect to get family " + family + " SOCK_DIAG_BY_FAMILY message but get " + nlmsghdr.nlmsg_type); return END_OF_PARSING; @@ -393,7 +434,7 @@ /** Parse a {@code SocketInfo} from the given position of the given byte buffer. */ @NonNull - private static SocketInfo parseSockInfo(@NonNull final ByteBuffer bytes, final int family, + private SocketInfo parseSockInfo(@NonNull final ByteBuffer bytes, final int family, final int nlmsgLen, final long time, final int uid, final long cookie, final int dstPort) { final int remainingDataSize = bytes.position() + nlmsgLen - SOCKDIAG_MSG_HEADER_SIZE; @@ -401,22 +442,17 @@ int mark = NetlinkUtils.INIT_MARK_VALUE; // Get a tcp_info. while (bytes.position() < remainingDataSize) { - final RoutingAttribute rtattr = - new RoutingAttribute(bytes.getShort(), bytes.getShort()); - final short dataLen = rtattr.getDataLength(); - if (rtattr.rtaType == NetlinkUtils.INET_DIAG_INFO) { - tcpInfo = TcpInfo.parse(bytes, dataLen); - } else if (rtattr.rtaType == NetlinkUtils.INET_DIAG_MARK) { - mark = bytes.getInt(); - } else { - // Data provided by kernel will include both valid data and padding data. The data - // len provided from kernel indicates the valid data size. Readers must deduce the - // alignment by themselves. - skipRemainingAttributesBytesAligned(bytes, dataLen); + final StructNlAttr nlattr = StructNlAttr.parse(bytes); + if (nlattr == null) break; + + if (nlattr.nla_type == NetlinkUtils.INET_DIAG_MARK) { + mark = nlattr.getValueAsInteger(); + } else if (nlattr.nla_type == NetlinkUtils.INET_DIAG_INFO) { + tcpInfo = TcpInfo.parse(nlattr.getValueAsByteBuffer(), nlattr.getAlignedLength()); } } final SocketInfo info = new SocketInfo(tcpInfo, family, mark, time, uid, cookie, dstPort); - log("parseSockInfo, " + info); + logd("parseSockInfo, " + info); return info; } @@ -434,7 +470,7 @@ } final boolean ret = (getLatestPacketFailPercentage() >= getTcpPacketsFailRateThreshold()); if (ret) { - Log.d(TAG, "data stall suspected, uids: " + mLatestReportedUids.toString()); + log("data stall suspected, uids: " + mLatestReportedUids.toString()); } return ret; } @@ -450,7 +486,7 @@ } if (current.tcpInfo == null) { - log("Current tcpInfo is null."); + logd("Current tcpInfo is null."); return null; } @@ -463,7 +499,7 @@ stat.receivedCount -= previous.tcpInfo.mSegsIn; stat.retransCount -= previous.tcpInfo.mTotalRetrans; } - log("calculateLatestPacketsStat, stat:" + stat); + logd("calculateLatestPacketsStat, stat:" + stat); return stat; } @@ -503,63 +539,31 @@ return mTcpPacketsFailRateThreshold; } - /** - * Method to skip the remaining attributes bytes. - * Corresponds to NLMSG_NEXT in bionic/libc/kernel/uapi/linux/netlink.h. - * - * @param buffer the target ByteBuffer - * @param len the remaining length to skip. - */ - private static void skipRemainingAttributesBytesAligned(@NonNull final ByteBuffer buffer, - final short len) { - // Data in {@Code RoutingAttribute} is followed after header with size {@Code NLA_ALIGNTO} - // bytes long for each block. Next attribute will start after the padding bytes if any. - // If all remaining bytes after header are valid in a data block, next attr will just start - // after valid bytes. - // - // E.g. With NLA_ALIGNTO(4), an attr struct with length 5 means 1 byte valid data remains - // after header and 3(4-1) padding bytes. Next attr with length 8 will start after the - // padding bytes and contain 4(8-4) valid bytes of data. The next attr start after the - // valid bytes, like: - // - // [HEADER(L=5)][ 4-Bytes DATA ][ HEADER(L=8) ][4 bytes DATA][Next attr] - // [ 5 valid bytes ][3 padding bytes ][ 8 valid bytes ] ... - final int cur = buffer.position(); - buffer.position(cur + NetlinkConstants.alignedLengthOf(len)); + private void logd(final String str) { + if (DBG) log(str); } - private static void log(final String str) { - if (DBG) Log.d(TAG, str); + private void log(final String s) { + Log.d(TAG + "/" + mNetwork.toString(), s); + } + + private void loge(final String str) { + loge(str, null /* tr */); + } + + private void loge(final String str, @Nullable Throwable tr) { + Log.e(TAG + "/" + mNetwork.toString(), str, tr); + } + + private void logwtf(final String str, @Nullable Throwable tr) { + Log.wtf(TAG + "/" + mNetwork.toString(), str, tr); } /** Stops monitoring and releases resources. */ public void quit() { mDependencies.removeDeviceConfigChangedListener(mConfigListener); - mDependencies.removeBroadcastReceiver(mDeviceIdleReceiver); - } - - /** - * Corresponds to {@code struct rtattr} from bionic/libc/kernel/uapi/linux/rtnetlink.h - * - * struct rtattr { - * unsigned short rta_len; // Length of option - * unsigned short rta_type; // Type of option - * // Data follows - * }; - */ - static class RoutingAttribute { - public static final int HEADER_LENGTH = 4; - - public final short rtaLen; // The whole valid size of the struct. - public final short rtaType; - - RoutingAttribute(final short len, final short type) { - rtaLen = len; - rtaType = type; - } - public short getDataLength() { - return (short) (rtaLen - HEADER_LENGTH); - } + mDependencies.removeBroadcastReceiver(mDeviceIdleReceiver, + mShouldDisableInDeepDoze, mShouldDisableInLightDoze); } /** @@ -639,7 +643,7 @@ synchronized (mDozeModeLock) { if (mInDozeMode == isEnabled) return; mInDozeMode = isEnabled; - log("Doze mode enabled=" + mInDozeMode); + logd("Doze mode enabled=" + mInDozeMode); } } @@ -647,13 +651,17 @@ if (mInOpportunisticMode == isEnabled) return; mInOpportunisticMode = isEnabled; - log("Private DNS Opportunistic mode enabled=" + mInOpportunisticMode); + logd("Private DNS Opportunistic mode enabled=" + mInOpportunisticMode); } public void setLinkProperties(@NonNull LinkProperties lp) { mLinkProperties = lp; } + public void setNetworkCapabilities(@NonNull NetworkCapabilities caps) { + mNetworkCapabilities = caps; + } + /** * Dependencies class for testing. */ @@ -673,7 +681,7 @@ */ public FileDescriptor connectToKernel() throws ErrnoException, SocketException { final FileDescriptor fd = NetlinkUtils.createNetLinkInetDiagSocket(); - NetlinkUtils.connectSocketToNetlink(fd); + NetlinkUtils.connectToKernel(fd); Os.setsockoptTimeval(fd, SOL_SOCKET, SO_SNDTIMEO, StructTimeval.fromMillis(IO_TIMEOUT_MS)); return fd; @@ -740,8 +748,14 @@ /** Add receiver for detecting doze mode change to control TCP detection. */ @TargetApi(Build.VERSION_CODES.TIRAMISU) public void addDeviceIdleReceiver(@NonNull final BroadcastReceiver receiver, - boolean shouldDisableInLightDoze) { - final IntentFilter intentFilter = new IntentFilter(ACTION_DEVICE_IDLE_MODE_CHANGED); + boolean shouldDisableInDeepDoze, boolean shouldDisableInLightDoze) { + // No need to register receiver if no related feature is enabled. + if (!shouldDisableInDeepDoze && !shouldDisableInLightDoze) return; + + final IntentFilter intentFilter = new IntentFilter(); + if (shouldDisableInDeepDoze) { + intentFilter.addAction(ACTION_DEVICE_IDLE_MODE_CHANGED); + } if (shouldDisableInLightDoze) { intentFilter.addAction(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED); } @@ -749,7 +763,9 @@ } /** Remove broadcast receiver. */ - public void removeBroadcastReceiver(@NonNull final BroadcastReceiver receiver) { + public void removeBroadcastReceiver(@NonNull final BroadcastReceiver receiver, + boolean shouldDisableInDeepDoze, boolean shouldDisableInLightDoze) { + if (!shouldDisableInDeepDoze && !shouldDisableInLightDoze) return; mContext.unregisterReceiver(receiver); } @@ -759,10 +775,27 @@ * to deal with flag values changing at runtime. */ @TargetApi(Build.VERSION_CODES.TIRAMISU) - public boolean shouldDisableInLightDoze() { + public boolean shouldDisableInLightDoze(boolean ignoreBlockedUidsSupported) { // Light doze mode status checking API is only available at T or later releases. - return SdkLevel.isAtLeastT() && DeviceConfigUtils.isNetworkStackFeatureNotChickenedOut( + if (!SdkLevel.isAtLeastT()) return false; + + // Disable light doze mode design is replaced by ignoring blocked uids design. + if (ignoreBlockedUidsSupported) return false; + + return DeviceConfigUtils.isNetworkStackFeatureNotChickenedOut( mContext, SKIP_TCP_POLL_IN_LIGHT_DOZE); } + + /** + * Get whether the ignore Tcp info for blocked uids is supported. This method should + * only be called once in the constructor, to ensure that the code does not need + * to deal with flag values changing at runtime. + */ + public boolean shouldIgnoreTcpInfoForBlockedUids() { + return SdkLevel.isAtLeastT() && DeviceConfigUtils.isFeatureSupported( + mContext, FEATURE_IS_UID_NETWORKING_BLOCKED) + && DeviceConfigUtils.isNetworkStackFeatureNotChickenedOut(mContext, + IGNORE_TCP_INFO_FOR_BLOCKED_UIDS); + } } }
diff --git a/src/com/android/networkstack/util/NetworkStackUtils.java b/src/com/android/networkstack/util/NetworkStackUtils.java index c9589e3..2dbf86e 100755 --- a/src/com/android/networkstack/util/NetworkStackUtils.java +++ b/src/com/android/networkstack/util/NetworkStackUtils.java
@@ -34,7 +34,6 @@ import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; -import java.net.SocketException; import java.net.UnknownHostException; /** @@ -152,21 +151,6 @@ new String [] {"https://www.google.com/generate_204"}; /** - * @deprecated Considering boolean experiment flag is likely to cause misconfiguration - * particularly when NetworkStack module rolls back to previous version. It's - * much safer to determine whether or not to enable one specific experimental - * feature by comparing flag version with module version. - */ - @Deprecated - public static final String DHCP_INIT_REBOOT_ENABLED = "dhcp_init_reboot_enabled"; - - /** - * @deprecated See above explanation. - */ - @Deprecated - public static final String DHCP_RAPID_COMMIT_ENABLED = "dhcp_rapid_commit_enabled"; - - /** * Minimum module version at which to enable the DHCP INIT-REBOOT state. */ public static final String DHCP_INIT_REBOOT_VERSION = "dhcp_init_reboot_version"; @@ -182,12 +166,6 @@ public static final String DHCP_IP_CONFLICT_DETECT_VERSION = "dhcp_ip_conflict_detect_version"; /** - * Minimum module version at which to enable the IPv6-Only preferred option. - */ - public static final String DHCP_IPV6_ONLY_PREFERRED_VERSION = - "dhcp_ipv6_only_preferred_version"; - - /** * Minimum module version at which to enable slow DHCP retransmission approach in renew/rebind * state suggested in RFC2131 section 4.4.5. */ @@ -271,6 +249,12 @@ * Experiment flag to enable new ra filter. */ public static final String APF_NEW_RA_FILTER_VERSION = "apf_new_ra_filter_version"; + /** + * Experiment flag to enable the feature of ignoring any individual RA section with lifetime + * below accept_ra_min_lft sysctl. + */ + public static final String IPCLIENT_IGNORE_LOW_RA_LIFETIME_VERSION = + "ipclient_ignore_low_ra_lifetime_version"; /**** BEGIN Feature Kill Switch Flags ****/ @@ -282,11 +266,10 @@ "ipclient_parse_netlink_events_force_disable"; /** - * Kill switch flag to disable the feature of ignoring any individual RA section with lifetime - * below accept_ra_min_lft sysctl. + * Kill switch flag to disable the feature of handle light doze mode in Apf. */ - public static final String IPCLIENT_IGNORE_LOW_RA_LIFETIME_FORCE_DISABLE = - "ipclient_ignore_low_ra_lifetime_force_disable"; + public static final String APF_HANDLE_LIGHT_DOZE_FORCE_DISABLE = + "apf_handle_light_doze_force_disable"; /** * Kill switch flag to disable the feature of skipping Tcp socket info polling when light @@ -294,6 +277,18 @@ */ public static final String SKIP_TCP_POLL_IN_LIGHT_DOZE = "skip_tcp_poll_in_light_doze_mode"; + /** + * Kill switch flag to disable the feature of re-evaluate when network resumes. + */ + public static final String REEVALUATE_WHEN_RESUME = "reevaluate_when_resume"; + + /** + * Kill switch flag to disable the feature of ignoring Tcp socket info for uids which + * networking are blocked. + */ + public static final String IGNORE_TCP_INFO_FOR_BLOCKED_UIDS = + "ignore_tcp_info_for_blocked_uids"; + static { System.loadLibrary("networkstackutilsjni"); }
diff --git a/src/com/android/server/NetworkStackService.java b/src/com/android/server/NetworkStackService.java index 368a6d4..40aee28 100644 --- a/src/com/android/server/NetworkStackService.java +++ b/src/com/android/server/NetworkStackService.java
@@ -21,11 +21,15 @@ import static android.net.dhcp.IDhcpServer.STATUS_UNKNOWN_ERROR; import static com.android.net.module.util.DeviceConfigUtils.getResBooleanConfig; +import static com.android.net.module.util.FeatureVersions.FEATURE_IS_UID_NETWORKING_BLOCKED; +import static com.android.networkstack.util.NetworkStackUtils.IGNORE_TCP_INFO_FOR_BLOCKED_UIDS; +import static com.android.networkstack.util.NetworkStackUtils.SKIP_TCP_POLL_IN_LIGHT_DOZE; import static com.android.server.util.PermissionUtil.checkDumpPermission; import android.app.Service; import android.content.Context; import android.content.Intent; +import android.net.ConnectivityManager; import android.net.IIpMemoryStore; import android.net.IIpMemoryStoreCallbacks; import android.net.INetd; @@ -49,6 +53,7 @@ import android.os.HandlerThread; import android.os.IBinder; import android.os.Looper; +import android.os.ParcelFileDescriptor; import android.os.RemoteException; import android.text.TextUtils; import android.util.ArraySet; @@ -59,6 +64,8 @@ import com.android.internal.annotations.GuardedBy; import com.android.internal.util.IndentingPrintWriter; +import com.android.modules.utils.BasicShellCommandHandler; +import com.android.net.module.util.DeviceConfigUtils; import com.android.net.module.util.SharedLog; import com.android.networkstack.NetworkStackNotifier; import com.android.networkstack.R; @@ -435,6 +442,20 @@ return; } + pw.println("Device Configs:"); + pw.increaseIndent(); + pw.println("SKIP_TCP_POLL_IN_LIGHT_DOZE=" + + DeviceConfigUtils.isNetworkStackFeatureNotChickenedOut( + mContext, SKIP_TCP_POLL_IN_LIGHT_DOZE)); + pw.println("FEATURE_IS_UID_NETWORKING_BLOCKED=" + DeviceConfigUtils.isFeatureSupported( + mContext, FEATURE_IS_UID_NETWORKING_BLOCKED)); + pw.println("IGNORE_TCP_INFO_FOR_BLOCKED_UIDS=" + + DeviceConfigUtils.isNetworkStackFeatureNotChickenedOut(mContext, + IGNORE_TCP_INFO_FOR_BLOCKED_UIDS)); + pw.decreaseIndent(); + pw.println(); + + pw.println("NetworkStack logs:"); mLog.dump(fd, pw, args); @@ -482,6 +503,69 @@ R.bool.config_no_sim_card_uses_neighbor_mcc, false)); } + @Override + public int handleShellCommand(@NonNull ParcelFileDescriptor in, + @NonNull ParcelFileDescriptor out, @NonNull ParcelFileDescriptor err, + @NonNull String[] args) { + return new ShellCmd().exec(this, in.getFileDescriptor(), out.getFileDescriptor(), + err.getFileDescriptor(), args); + } + + private class ShellCmd extends BasicShellCommandHandler { + @Override + public int onCommand(String cmd) { + if (cmd == null) { + return handleDefaultCommands(cmd); + } + final PrintWriter pw = getOutPrintWriter(); + try { + switch (cmd) { + case "is-uid-networking-blocked": + if (!DeviceConfigUtils.isFeatureSupported(mContext, + FEATURE_IS_UID_NETWORKING_BLOCKED)) { + pw.println("API is unsupported"); + return -1; + } + + // Usage : cmd network_stack is-uid-networking-blocked <uid> <metered> + // If no argument, get and display the usage help. + if (getRemainingArgsCount() != 2) { + onHelp(); + return -1; + } + final int uid; + final boolean metered; + // If any fail, throws and output to the stdout. + // Let the caller handle it. + uid = Integer.parseInt(getNextArg()); + metered = Boolean.parseBoolean(getNextArg()); + final ConnectivityManager cm = + mContext.getSystemService(ConnectivityManager.class); + pw.println(cm.isUidNetworkingBlocked( + uid, metered /* isNetworkMetered */)); + return 0; + default: + return handleDefaultCommands(cmd); + } + } catch (Exception e) { + pw.println(e); + } + return -1; + } + + @Override + public void onHelp() { + PrintWriter pw = getOutPrintWriter(); + pw.println("NetworkStack service commands:"); + pw.println(" help"); + pw.println(" Print this help text."); + pw.println(" is-uid-networking-blocked <uid> <metered>"); + pw.println(" Get whether the networking is blocked for given uid and metered."); + pw.println(" <uid>: The target uid."); + pw.println(" <metered>: [true|false], Whether the target network is metered."); + } + } + /** * Dump version information of the module and detected system version. */
diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java index 5b551a0..8c10138 100755 --- a/src/com/android/server/connectivity/NetworkMonitor.java +++ b/src/com/android/server/connectivity/NetworkMonitor.java
@@ -36,6 +36,7 @@ import static android.net.INetworkMonitor.NETWORK_VALIDATION_RESULT_VALID; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.net.captiveportal.CaptivePortalProbeSpec.parseCaptivePortalProbeSpecs; import static android.net.metrics.ValidationProbeEvent.DNS_FAILURE; import static android.net.metrics.ValidationProbeEvent.DNS_SUCCESS; @@ -542,6 +543,7 @@ private final boolean mPrivateIpNoInternetEnabled; private final boolean mMetricsEnabled; + private final boolean mReevaluateWhenResumeEnabled; @NonNull private final NetworkInformationShim mInfoShim = NetworkInformationShimImpl.newInstance(); @@ -628,6 +630,8 @@ mPrivateIpNoInternetEnabled = getIsPrivateIpNoInternetEnabled(); mMetricsEnabled = deps.isFeatureNotChickenedOut(context, NetworkStackUtils.VALIDATION_METRICS_VERSION); + mReevaluateWhenResumeEnabled = deps.isFeatureNotChickenedOut(context, + NetworkStackUtils.REEVALUATE_WHEN_RESUME); mUseHttps = getUseHttpsValidation(); mCaptivePortalUserAgent = getCaptivePortalUserAgent(); mCaptivePortalFallbackSpecs = @@ -938,6 +942,7 @@ // Initialization. tst.setOpportunisticMode(false); tst.setLinkProperties(mLinkProperties); + tst.setNetworkCapabilities(mNetworkCapabilities); } Log.d(TAG, "Starting on network " + mNetwork + " with capport HTTPS URL " + Arrays.toString(mCaptivePortalHttpsUrls) @@ -1101,16 +1106,8 @@ } break; case EVENT_NETWORK_CAPABILITIES_CHANGED: - final NetworkCapabilities newCap = (NetworkCapabilities) message.obj; - // Reevaluate network if underlying network changes on the validation required - // VPN. - if (isVpnUnderlyingNetworkChangeReevaluationRequired( - newCap, mNetworkCapabilities)) { - sendMessage(CMD_FORCE_REEVALUATION, NO_UID, 0); - } - - mNetworkCapabilities = newCap; - suppressNotificationIfNetworkRestricted(); + handleCapabilitiesChanged((NetworkCapabilities) message.obj, + true /* reevaluateOnResume */); break; case EVENT_RESOURCE_CONFIG_CHANGED: // RRO generation does not happen during package installation and instead after @@ -1129,20 +1126,53 @@ return HANDLED; } - private boolean isVpnUnderlyingNetworkChangeReevaluationRequired( - final NetworkCapabilities newCap, final NetworkCapabilities oldCap) { - return !newCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) - && isValidationRequired() - && !Objects.equals(mInfoShim.getUnderlyingNetworks(newCap), - mInfoShim.getUnderlyingNetworks(oldCap)); - } - @Override public void exit() { mContext.unregisterReceiver(mConfigurationReceiver); } } + private void handleCapabilitiesChanged(@NonNull final NetworkCapabilities newCap, + boolean reevaluateOnResume) { + // Go to EvaluatingState to reset the network re-evaluation timer when + // the network resumes from suspended. + // This is because the network is expected to be down + // when the device is suspended, and if the delay timer falls back to + // the maximum interval, re-evaluation will be triggered slowly after + // the network resumes. + // Suppress re-evaluation in validated state, if the network has been validated, + // then it's in the expected state. + // TODO(b/287183389): Evaluate once but do not re-evaluate when suspended, to make + // exclamation mark visible by user but doesn't cause too much network traffic. + if (mReevaluateWhenResumeEnabled && reevaluateOnResume + && !mNetworkCapabilities.hasCapability(NET_CAPABILITY_NOT_SUSPENDED) + && newCap.hasCapability(NET_CAPABILITY_NOT_SUSPENDED)) { + // Interrupt if waiting for next probe. + sendMessage(CMD_FORCE_REEVALUATION, NO_UID, 1 /* forceAccept */); + } else if (isVpnUnderlyingNetworkChangeReevaluationRequired(newCap, mNetworkCapabilities)) { + // If no re-evaluation is needed from the previous check, fall-through for lower + // priority checks. + // Reevaluate network if underlying network changes on the validation required + // VPN. + sendMessage(CMD_FORCE_REEVALUATION, NO_UID, 0 /* forceAccept */); + } + final TcpSocketTracker tst = getTcpSocketTracker(); + if (tst != null) { + tst.setNetworkCapabilities(newCap); + } + + mNetworkCapabilities = newCap; + suppressNotificationIfNetworkRestricted(); + } + + private boolean isVpnUnderlyingNetworkChangeReevaluationRequired( + final NetworkCapabilities newCap, final NetworkCapabilities oldCap) { + return !newCap.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN) + && isValidationRequired() + && !Objects.equals(mInfoShim.getUnderlyingNetworks(newCap), + mInfoShim.getUnderlyingNetworks(oldCap)); + } + // Being in the ValidatedState State indicates a Network is: // - Successfully validated, or // - Wanted "as is" by the user, or @@ -1208,6 +1238,12 @@ sendTcpPollingEvent(); } break; + case EVENT_NETWORK_CAPABILITIES_CHANGED: + // The timer does not need to reset, and it won't need to re-evaluate if + // the network is already validated when resumes. + handleCapabilitiesChanged((NetworkCapabilities) message.obj, + false /* reevaluateOnResume */); + break; default: return NOT_HANDLED; } @@ -3456,6 +3492,11 @@ } @VisibleForTesting + public int getReevaluationDelayMs() { + return mReevaluateDelayMs; + } + + @VisibleForTesting protected boolean isDataStall() { if (!isDataStallDetectionRequired()) { return false;
diff --git a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java index c54906d..d239379 100644 --- a/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java +++ b/tests/integration/common/android/net/ip/IpClientIntegrationTestCommon.java
@@ -38,6 +38,7 @@ import static android.net.dhcp.DhcpPacket.INADDR_BROADCAST; import static android.net.dhcp.DhcpPacket.INFINITE_LEASE; import static android.net.dhcp.DhcpPacket.MIN_V6ONLY_WAIT_MS; +import static android.net.dhcp6.Dhcp6Packet.PrefixDelegation; import static android.net.ip.IIpClientCallbacks.DTIM_MULTIPLIER_RESET; import static android.net.ip.IpClient.CONFIG_IPV6_AUTOCONF_TIMEOUT; import static android.net.ip.IpClient.CONFIG_ACCEPT_RA_MIN_LFT; @@ -78,6 +79,7 @@ import static com.android.net.module.util.NetworkStackConstants.NEIGHBOR_ADVERTISEMENT_FLAG_SOLICITED; import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_AUTONOMOUS; import static com.android.net.module.util.NetworkStackConstants.PIO_FLAG_ON_LINK; +import static com.android.net.module.util.NetworkStackConstants.RFC7421_PREFIX_LENGTH; import static com.android.testutils.MiscAsserts.assertThrows; import static com.android.testutils.ParcelUtils.parcelingRoundTrip; import static com.android.testutils.TestPermissionUtil.runAsShell; @@ -151,6 +153,7 @@ import android.net.dhcp.DhcpRequestPacket; import android.net.dhcp6.Dhcp6Client; import android.net.dhcp6.Dhcp6Packet; +import android.net.dhcp6.Dhcp6Packet.PrefixDelegation; import android.net.dhcp6.Dhcp6RebindPacket; import android.net.dhcp6.Dhcp6RenewPacket; import android.net.dhcp6.Dhcp6RequestPacket; @@ -199,6 +202,7 @@ import com.android.net.module.util.netlink.NetlinkUtils; import com.android.net.module.util.netlink.StructNdOptPref64; import com.android.net.module.util.structs.EthernetHeader; +import com.android.net.module.util.structs.IaPrefixOption; import com.android.net.module.util.structs.Ipv6Header; import com.android.net.module.util.structs.LlaOption; import com.android.net.module.util.structs.PrefixInformationOption; @@ -693,14 +697,11 @@ } protected void setDhcpFeatures(final boolean isDhcpLeaseCacheEnabled, - final boolean isRapidCommitEnabled, final boolean isDhcpIpConflictDetectEnabled, - final boolean isIPv6OnlyPreferredEnabled) { + final boolean isRapidCommitEnabled, final boolean isDhcpIpConflictDetectEnabled) { setFeatureEnabled(NetworkStackUtils.DHCP_INIT_REBOOT_VERSION, isDhcpLeaseCacheEnabled); setFeatureEnabled(NetworkStackUtils.DHCP_RAPID_COMMIT_VERSION, isRapidCommitEnabled); setFeatureEnabled(NetworkStackUtils.DHCP_IP_CONFLICT_DETECT_VERSION, isDhcpIpConflictDetectEnabled); - setFeatureEnabled(NetworkStackUtils.DHCP_IPV6_ONLY_PREFERRED_VERSION, - isIPv6OnlyPreferredEnabled); } private void setDeviceConfigForMaxDtimMultiplier() { @@ -1201,9 +1202,9 @@ private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled, final boolean shouldReplyRapidCommitAck, final boolean isPreconnectionEnabled, - final boolean isDhcpIpConflictDetectEnabled, final boolean isIPv6OnlyPreferredEnabled, - final String displayName, final ScanResultInfo scanResultInfo, - final Layer2Information layer2Info) throws Exception { + final boolean isDhcpIpConflictDetectEnabled, final String displayName, + final ScanResultInfo scanResultInfo, final Layer2Information layer2Info) + throws Exception { ProvisioningConfiguration.Builder prov = new ProvisioningConfiguration.Builder() .withoutIpReachabilityMonitor() .withLayer2Information(layer2Info == null @@ -1216,7 +1217,7 @@ if (scanResultInfo != null) prov.withScanResultInfo(scanResultInfo); setDhcpFeatures(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck, - isDhcpIpConflictDetectEnabled, isIPv6OnlyPreferredEnabled); + isDhcpIpConflictDetectEnabled); startIpClientProvisioning(prov.build()); if (!isPreconnectionEnabled) { @@ -1227,10 +1228,9 @@ private void startIpClientProvisioning(final boolean isDhcpLeaseCacheEnabled, final boolean isDhcpRapidCommitEnabled, final boolean isPreconnectionEnabled, - final boolean isDhcpIpConflictDetectEnabled, final boolean isIPv6OnlyPreferredEnabled) - throws Exception { + final boolean isDhcpIpConflictDetectEnabled) throws Exception { startIpClientProvisioning(isDhcpLeaseCacheEnabled, isDhcpRapidCommitEnabled, - isPreconnectionEnabled, isDhcpIpConflictDetectEnabled, isIPv6OnlyPreferredEnabled, + isPreconnectionEnabled, isDhcpIpConflictDetectEnabled, null /* displayName */, null /* ScanResultInfo */, null /* layer2Info */); } @@ -1284,13 +1284,12 @@ final Integer leaseTimeSec, final boolean isDhcpLeaseCacheEnabled, final boolean shouldReplyRapidCommitAck, final int mtu, final boolean isDhcpIpConflictDetectEnabled, - final boolean isIPv6OnlyPreferredEnabled, final String captivePortalApiUrl, final String displayName, final ScanResultInfo scanResultInfo, final Layer2Information layer2Info) throws Exception { startIpClientProvisioning(isDhcpLeaseCacheEnabled, shouldReplyRapidCommitAck, false /* isPreconnectionEnabled */, isDhcpIpConflictDetectEnabled, - isIPv6OnlyPreferredEnabled, displayName, scanResultInfo, layer2Info); + displayName, scanResultInfo, layer2Info); return handleDhcpPackets(isSuccessLease, leaseTimeSec, shouldReplyRapidCommitAck, mtu, captivePortalApiUrl); } @@ -1335,7 +1334,6 @@ final boolean isDhcpIpConflictDetectEnabled) throws Exception { return performDhcpHandshake(isSuccessLease, leaseTimeSec, isDhcpLeaseCacheEnabled, isDhcpRapidCommitEnabled, mtu, isDhcpIpConflictDetectEnabled, - false /* isIPv6OnlyPreferredEnabled */, null /* captivePortalApiUrl */, null /* displayName */, null /* scanResultInfo */, null /* layer2Info */); } @@ -1392,7 +1390,7 @@ }).when(mIpMemoryStore).retrieveNetworkAttributes(eq(TEST_L2KEY), any()); startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); return getNextDhcpPacket(); } @@ -1494,7 +1492,7 @@ startIpClientProvisioning(true /* isDhcpLeaseCacheEnabled */, shouldReplyRapidCommitAck, true /* isDhcpPreConnectionEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart(); final int preconnDiscoverTransId = packet.getTransactionId(); @@ -1674,7 +1672,7 @@ public void testDhcpInit() throws Exception { startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); final DhcpPacket packet = getNextDhcpPacket(); assertTrue(packet instanceof DhcpDiscoverPacket); } @@ -1742,7 +1740,7 @@ public void testRollbackFromRapidCommitOption() throws Exception { startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, true /* isDhcpRapidCommitEnabled */, false /* isPreConnectionEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); final List<DhcpPacket> discoverList = new ArrayList<DhcpPacket>(); DhcpPacket packet; @@ -1819,7 +1817,7 @@ public void testDhcpClientRapidCommitEnabled() throws Exception { startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, true /* shouldReplyRapidCommitAck */, false /* isPreconnectionEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); final DhcpPacket packet = getNextDhcpPacket(); assertTrue(packet instanceof DhcpDiscoverPacket); } @@ -2545,7 +2543,7 @@ // PreconnectionState instead of RunningState. startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, true /* isDhcpPreConnectionEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); assertDiscoverPacketOnPreconnectionStart(); // Force to enter RunningState. @@ -2671,7 +2669,7 @@ .withPreconnection() .build(); setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); startIpClientProvisioning(config); assertDiscoverPacketOnPreconnectionStart(); @@ -2703,7 +2701,7 @@ // StoppedState. startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, false /* isPreConnectionEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); final DhcpPacket discover = getNextDhcpPacket(); assertTrue(discover instanceof DhcpDiscoverPacket); } @@ -2820,7 +2818,7 @@ boolean serverSendsOption) throws Exception { startIpClientProvisioning(false /* isDhcpLeaseCacheEnabled */, false /* shouldReplyRapidCommitAck */, false /* isPreConnectionEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); final DhcpPacket discover = getNextDhcpPacket(); assertTrue(discover instanceof DhcpDiscoverPacket); assertEquals(featureEnabled, discover.hasRequestedParam(DhcpPacket.DHCP_CAPTIVE_PORTAL)); @@ -2931,7 +2929,6 @@ TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */, - false /* isIPv6OnlyPreferredEnabled */, null /* captivePortalApiUrl */, displayName, info /* scanResultInfo */, null /* layer2Info */); assertEquals(2, sentPackets.size()); @@ -3046,7 +3043,6 @@ performDhcpHandshake(true /* isSuccessLease */, TEST_LEASE_DURATION_S, true /* isDhcpLeaseCacheEnabled */, false /* isDhcpRapidCommitEnabled */, TEST_DEFAULT_MTU, false /* isDhcpIpConflictDetectEnabled */, - false /* isIPv6OnlyPreferredEnabled */, null /* captivePortalApiUrl */, displayName, null /* scanResultInfo */, layer2Info); verifyIPv4OnlyProvisioningSuccess(Collections.singletonList(CLIENT_ADDR)); @@ -3199,7 +3195,7 @@ // Enable rapid commit to accelerate DHCP handshake to shorten test duration, // not strictly necessary. setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); // Both signature and root tests can use this function to do dual-stack provisioning. if (useNetworkStackSignature()) { mIpc.startProvisioning(config); @@ -3227,7 +3223,7 @@ for (LinkAddress la : lp.getLinkAddresses()) { final InetAddress addr = la.getAddress(); if ((addr instanceof Inet6Address) && !addr.isLinkLocalAddress()) { - return prefix.contains(addr); + if (prefix.contains(addr)) return true; } } return false; @@ -3303,7 +3299,7 @@ .withoutIpReachabilityMonitor() .build(); setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, false /* isRapidCommitEnabled */, - false /* isDhcpIpConflictDetectEnabled */, true /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); startIpClientProvisioning(config); final DhcpPacket packet = @@ -3367,7 +3363,7 @@ .build(); setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, - false /* isDhcpIpConflictDetectEnabled */, true /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); startIpClientProvisioning(config); final DhcpPacket packet = assertDiscoverPacketOnPreconnectionStart(); @@ -3422,7 +3418,7 @@ .build(); setDhcpFeatures(true /* isDhcpLeaseCacheEnabled */, false /* isRapidCommitEnabled */, - false /* isDhcpIpConflictDetectEnabled */, true /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); startIpClientProvisioning(config); final DhcpPacket packet = @@ -3556,7 +3552,7 @@ .withoutIPv6(); setDhcpFeatures(isDhcpLeaseCacheEnabled, false /* isRapidCommitEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); startIpClientProvisioning(prov.build()); verify(mCb, timeout(TEST_TIMEOUT_MS)).setFallbackMulticastFilter(true); @@ -3927,7 +3923,7 @@ // Enable rapid commit to accelerate DHCP handshake to shorten test duration, // not strictly necessary. setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); // Disable gratuitious neighbor discovery feature manually, if the feature is enabled on // the DUT during experiment launch, that will send another two duplicate NA packets and @@ -4294,8 +4290,7 @@ mNetworkAgentThread.start(); setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, - false /* isDhcpIpConflictDetectEnabled */, - false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); setFeatureEnabled( NetworkStackUtils.IP_REACHABILITY_IGNORE_INCOMPLETE_IPV6_DNS_SERVER_VERSION, isIgnoreIncompleteIpv6DnsServerEnabled); @@ -4471,7 +4466,7 @@ // Speed up provisioning by enabling rapid commit. TODO: why is this necessary? setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); config = new ProvisioningConfiguration.Builder() .build(); startIpClientProvisioning(config); @@ -4812,18 +4807,27 @@ inOrder.verify(mCb, timeout(TEST_TIMEOUT_MS)).setMaxDtimMultiplier(DTIM_MULTIPLIER_RESET); } - private void handleDhcp6Packets(final IpPrefix prefix, boolean shouldReplyRapidCommit) - throws Exception { - handleDhcp6Packets(prefix, 3600 /* t1 */, 4500 /* t2 */, 4500 /* preferred */, - 7200 /* valid */, shouldReplyRapidCommit); + private IaPrefixOption buildIaPrefixOption(final IpPrefix prefix, int preferred, + int valid) { + return new IaPrefixOption((short) IaPrefixOption.LENGTH, preferred, valid, + (byte) RFC7421_PREFIX_LENGTH, prefix.getRawAddress() /* prefix */); } - private void handleDhcp6Packets(final IpPrefix prefix, int t1, int t2, int preferred, int valid, + private void handleDhcp6Packets(final IpPrefix prefix, boolean shouldReplyRapidCommit) + throws Exception { + final IaPrefixOption ipo = buildIaPrefixOption(prefix, 4500 /* preferred */, + 7200 /* valid */); + handleDhcp6Packets(Collections.singletonList(ipo), 3600 /* t1 */, 4500 /* t2 */, + shouldReplyRapidCommit); + } + + private void handleDhcp6Packets(final List<IaPrefixOption> ipos, int t1, int t2, boolean shouldReplyRapidCommit) throws Exception { + ByteBuffer iapd; Dhcp6Packet packet; while ((packet = getNextDhcp6Packet()) != null) { - final ByteBuffer iapd = Dhcp6Packet.buildIaPdOption(packet.getIaId(), t1, t2, - preferred, valid, prefix.getRawAddress(), (byte) prefix.getPrefixLength()); + final PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), t1, t2, ipos); + iapd = pd.build(); if (packet instanceof Dhcp6SolicitPacket) { if (shouldReplyRapidCommit) { mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac, @@ -4883,7 +4887,11 @@ @Test public void testDhcp6Pd_longPrefixLength() throws Exception { prepareDhcp6PdTest(); - handleDhcp6Packets(new IpPrefix("2001:db8:1::/80"), true /* shouldReplyRapidCommit */); + final IpPrefix prefix = new IpPrefix("2001:db8:1::/80"); + final IaPrefixOption ipo = buildIaPrefixOption(prefix, 3600 /* preferred */, + 4000 /* valid */); + handleDhcp6Packets(Collections.singletonList(ipo), 3600 /* t1 */, 4500 /* t2 */, + true /* shouldReplyRapidCommit */); verify(mCb, never()).onProvisioningSuccess(any()); } @@ -4900,24 +4908,33 @@ @Test public void testDhcp6Pd_T1GreaterThanT2() throws Exception { prepareDhcp6PdTest(); - handleDhcp6Packets(new IpPrefix("2001:db8:1::/80"), 4500 /* t1 */, 3600 /* t2 */, - 4500 /* preferred */, 7200 /* valid */, true /* shouldReplyRapidCommit */); + final IpPrefix prefix = new IpPrefix("2001:db8:1::/64"); + final IaPrefixOption ipo = buildIaPrefixOption(prefix, 3600 /* preferred */, + 4000 /* valid */); + handleDhcp6Packets(Collections.singletonList(ipo), 4500 /* t1 */, 3600 /* t2 */, + true /* shouldReplyRapidCommit */); verify(mCb, never()).onProvisioningSuccess(any()); } @Test public void testDhcp6Pd_preferredLifetimeGreaterThanValidLifetime() throws Exception { prepareDhcp6PdTest(); - handleDhcp6Packets(new IpPrefix("2001:db8:1::/80"), 3600 /* t1 */, 4500 /* t2 */, - 7200 /* preferred */, 4500 /* valid */, true /* shouldReplyRapidCommit */); + final IpPrefix prefix = new IpPrefix("2001:db8:1::/64"); + final IaPrefixOption ipo = buildIaPrefixOption(prefix, 7200 /* preferred */, + 4500 /* valid */); + handleDhcp6Packets(Collections.singletonList(ipo), 3600 /* t1 */, 4500 /* t2 */, + true /* shouldReplyRapidCommit */); verify(mCb, never()).onProvisioningSuccess(any()); } @Test public void testDhcp6Pd_preferredLifetimeLessThanT2() throws Exception { prepareDhcp6PdTest(); - handleDhcp6Packets(new IpPrefix("2001:db8:1::/80"), 3600 /* t1 */, 4500 /* t2 */, - 3600 /* preferred */, 4000 /* valid */, true /* shouldReplyRapidCommit */); + final IpPrefix prefix = new IpPrefix("2001:db8:1::/64"); + final IaPrefixOption ipo = buildIaPrefixOption(prefix, 3600 /* preferred */, + 4000 /* valid */); + handleDhcp6Packets(Collections.singletonList(ipo), 3600 /* t1 */, 4500 /* t2 */, + true /* shouldReplyRapidCommit */); verify(mCb, never()).onProvisioningSuccess(any()); } @@ -4936,7 +4953,7 @@ ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() .build(); setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); startIpClientProvisioning(config); waitForRouterSolicitation(); @@ -4994,7 +5011,7 @@ ProvisioningConfiguration config = new ProvisioningConfiguration.Builder() .build(); setDhcpFeatures(false /* isDhcpLeaseCacheEnabled */, true /* isRapidCommitEnabled */, - false /* isDhcpIpConflictDetectEnabled */, false /* isIPv6OnlyPreferredEnabled */); + false /* isDhcpIpConflictDetectEnabled */); startIpClientProvisioning(config); waitForRouterSolicitation(); @@ -5017,6 +5034,44 @@ )); } + @Test + public void testDhcp6Pd_multiplePrefixesWithInvalidPrefix() throws Exception { + final IpPrefix valid = new IpPrefix("2001:db8:1::/64"); + final IpPrefix invalid = new IpPrefix("2001:db8:2::/64"); // preferred lft > valid lft + final IaPrefixOption validIpo = buildIaPrefixOption(valid, 4500 /* preferred */, + 7200 /* valid */); + final IaPrefixOption invalidIpo = buildIaPrefixOption(invalid, 4500 /* preferred */, + 3000 /* valid */); + + prepareDhcp6PdTest(); + handleDhcp6Packets(Arrays.asList(invalidIpo, validIpo), 3600 /* t1 */, 4500 /* t2 */, + true /* shouldReplyRapidCommit */); + final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); + verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); + final LinkProperties lp = captor.getValue(); + assertTrue(hasIpv6AddressPrefixedWith(lp, valid)); + assertFalse(hasIpv6AddressPrefixedWith(lp, invalid)); + } + + @Test + public void testDhcp6Pd_multiplePrefixesWithPrefixValidLifetimeOfZero() throws Exception { + final IpPrefix valid = new IpPrefix("2001:db8:1::/64"); + final IpPrefix invalid = new IpPrefix("2001:db8:2::/64"); // preferred/valid lft 0 + final IaPrefixOption validIpo = buildIaPrefixOption(valid, 4500 /* preferred */, + 7200 /* valid */); + final IaPrefixOption invalidIpo = buildIaPrefixOption(invalid, 0 /* preferred */, + 0 /* valid */); + + prepareDhcp6PdTest(); + handleDhcp6Packets(Arrays.asList(invalidIpo, validIpo), 3600 /* t1 */, 4500 /* t2 */, + true /* shouldReplyRapidCommit */); + final ArgumentCaptor<LinkProperties> captor = ArgumentCaptor.forClass(LinkProperties.class); + verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningSuccess(captor.capture()); + final LinkProperties lp = captor.getValue(); + assertTrue(hasIpv6AddressPrefixedWith(lp, valid)); + assertFalse(hasIpv6AddressPrefixedWith(lp, invalid)); + } + private void prepareDhcp6PdRenewTest() throws Exception { final IpPrefix prefix = new IpPrefix("2001:db8:1::/64"); prepareDhcp6PdTest(); @@ -5065,12 +5120,14 @@ Dhcp6Packet packet = getNextDhcp6Packet(); assertTrue(packet instanceof Dhcp6RenewPacket); - // Reply with a different prefix with requested one, check if all global IPv6 addresses - // will be deleted and loss the IPv6 provisioning. + // Reply with a different prefix with requested one, per RFC8415#section-18.2.10.1 + // any new prefix should be added. final IpPrefix prefix1 = new IpPrefix("2001:db8:2::/64"); - final ByteBuffer iapd = Dhcp6Packet.buildIaPdOption(packet.getIaId(), 3600 /* t1*/, - 4500 /* t2 */, 4500 /* preferred */, 7200 /* valid */, prefix1.getRawAddress(), - (byte) 64 /* prefix length */); + final IaPrefixOption ipo = buildIaPrefixOption(prefix1, 4500 /* preferred */, + 7200 /* valid */); + final PrefixDelegation pd = new PrefixDelegation(packet.getIaId(), 3600 /* t1 */, + 4500 /* t2 */, Collections.singletonList(ipo)); + final ByteBuffer iapd = pd.build(); mPacketReader.sendResponse(buildDhcp6Reply(packet, iapd.array(), mClientMac, (Inet6Address) mClientIpAddress, false /* rapidCommit */)); verify(mCb, timeout(TEST_TIMEOUT_MS)).onProvisioningFailure(any());
diff --git a/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt b/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt index 8b1b046..b10e6e1 100644 --- a/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt +++ b/tests/integration/signature/android/net/NetworkStatsIntegrationTest.kt
@@ -28,7 +28,9 @@ import android.net.NetworkStatsIntegrationTest.Direction.UPLOAD import android.net.NetworkTemplate.MATCH_TEST import android.os.Build +import android.os.ParcelFileDescriptor.AutoCloseInputStream import android.os.Process +import android.util.Log import androidx.test.platform.app.InstrumentationRegistry import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo import com.android.testutils.DevSdkIgnoreRunner @@ -42,6 +44,7 @@ import fi.iki.elonen.NanoHTTPD import java.io.BufferedInputStream import java.io.BufferedOutputStream +import java.io.BufferedReader import java.net.HttpURLConnection import java.net.HttpURLConnection.HTTP_OK import java.net.InetSocketAddress @@ -49,6 +52,7 @@ import java.nio.charset.Charset import kotlin.math.ceil import kotlin.test.assertEquals +import kotlin.test.assertNotNull import kotlin.test.assertTrue import org.junit.After import org.junit.Assume.assumeTrue @@ -62,6 +66,7 @@ @TargetApi(Build.VERSION_CODES.S) @IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) class NetworkStatsIntegrationTest { + private val TAG = NetworkStatsIntegrationTest::class.java.simpleName private val INTERNAL_V6ADDR = LinkAddress(InetAddresses.parseNumericAddress("2001:db8::1234"), 64) private val EXTERNAL_V6ADDR = @@ -381,6 +386,7 @@ val taggedUid = getUidDetail(iface, TEST_TAG) val trafficStatsIface = getTrafficStatsIface(iface) val trafficStatsUid = getTrafficStatsUid(Process.myUid()) + val xtBpfStats = getXtBpfStatsInternal() private fun getUidDetail(iface: String, tag: Int): BareStats { return getNetworkStatsThat(iface, tag) { nsm, template -> @@ -447,6 +453,40 @@ TrafficStats.getUidTxBytes(uid), TrafficStats.getUidTxPackets(uid) ) + + private fun getXtBpfStatsInternal(): BareStats { + // The following pattern matches ip(6)tables-save -c output like below: + // [119:37802] -A bw_raw_PREROUTING -m bpf --object-pinned + // /sys/fs/bpf/netd_shared/prog_netd_skfilter_ingress_xtbpf + // [141:26439] -A bw_mangle_POSTROUTING -m bpf --object-pinned + // /sys/fs/bpf/netd_shared/prog_netd_skfilter_egress_xtbpf + val ingressRegex = Regex("""\[(?<rxPackets>\d+):(?<rxBytes>\d+)\]""" + + """.*prog_netd_skfilter_ingress_xtbpf""") + val egressRegex = Regex("""\[(?<txPackets>\d+):(?<txBytes>\d+)\]""" + + """.*prog_netd_skfilter_egress_xtbpf""") + val (v4Stats, v6Stats) = listOf("iptables-save -c", "ip6tables-save -c").map { + val output = runShellCommand(it) + val rxMatches = ingressRegex.find(output) + val txMatches = egressRegex.find(output) + assertNotNull(rxMatches) + assertNotNull(txMatches) + + BareStats( + rxBytes = rxMatches.groups["rxBytes"]!!.value.toLong(), + rxPackets = rxMatches.groups["rxPackets"]!!.value.toLong(), + txBytes = txMatches.groups["txBytes"]!!.value.toLong(), + txPackets = txMatches.groups["txPackets"]!!.value.toLong() + ) + } + return v4Stats.plus(v6Stats) + } + + private fun runShellCommand(cmd: String): String { + return InstrumentationRegistry.getInstrumentation().getUiAutomation() + .executeShellCommand(cmd).use { pfd -> + AutoCloseInputStream(pfd).bufferedReader().use(BufferedReader::readText) + } + } } private fun assertAllStatsIncreases( @@ -475,6 +515,15 @@ lower: BareStats, upper: BareStats ) { + // XtBpf iptables hook counted traffic on all interfaces. Thus, this might see traffic + // on other interfaces as well. Also, other thread/process could reload the relevant + // iptables table. Thus, instead of asserting the readings, print logs when it is + // unexpected to provide more debug information when failing other items. + if (!checkInRange(before.xtBpfStats, after.xtBpfStats, + lower + lower.reverse(), upper + upper.reverse())) { + Log.d(TAG, "Unexpected xtbpf stats: ${after.xtBpfStats} - ${before.xtBpfStats} " + + "is not within range [$lower, $upper]") + } assertInRange( "Unexpected iface traffic stats", after.iface, @@ -550,16 +599,24 @@ ) { // Passing the value after operation and the value before operation to dump the actual // numbers if it fails. - val value = after - before - assertTrue( - value.rxBytes in lower.rxBytes..upper.rxBytes && - value.rxPackets in lower.rxPackets..upper.rxPackets && - value.txBytes in lower.txBytes..upper.txBytes && - value.txPackets in lower.txPackets..upper.txPackets, + assertTrue(checkInRange(before, after, lower, upper), "$tag on $iface: $after - $before is not within range [$lower, $upper]" ) } + private fun checkInRange( + before: BareStats, + after: BareStats, + lower: BareStats, + upper: BareStats + ): Boolean { + val value = after - before + return value.rxBytes in lower.rxBytes..upper.rxBytes && + value.rxPackets in lower.rxPackets..upper.rxPackets && + value.txBytes in lower.txBytes..upper.txBytes && + value.txPackets in lower.txPackets..upper.txPackets + } + fun getRandomString(length: Long): String { val allowedChars = ('A'..'Z') + ('a'..'z') + ('0'..'9') return (1..length)
diff --git a/tests/unit/jni/apf_jni.cpp b/tests/unit/jni/apf_jni.cpp index 5ae3ada..84c5c1a 100644 --- a/tests/unit/jni/apf_jni.cpp +++ b/tests/unit/jni/apf_jni.cpp
@@ -41,7 +41,7 @@ filter_age); } else { return apf_run(program, program_len, ram_len, packet, packet_len, - filter_age); + filter_age << 14); } }
diff --git a/tests/unit/src/android/net/apf/ApfTest.java b/tests/unit/src/android/net/apf/ApfTest.java index 69c8917..6a54e1c 100644 --- a/tests/unit/src/android/net/apf/ApfTest.java +++ b/tests/unit/src/android/net/apf/ApfTest.java
@@ -25,7 +25,8 @@ import static android.net.apf.ApfTestUtils.MIN_PKT_SIZE; import static android.net.apf.ApfTestUtils.PASS; import static android.net.apf.ApfTestUtils.assertProgramEquals; -import static android.system.OsConstants.AF_UNIX; +import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED; +import static android.os.PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED; import static android.system.OsConstants.ARPHRD_ETHER; import static android.system.OsConstants.ETH_P_ARP; import static android.system.OsConstants.ETH_P_IP; @@ -34,7 +35,6 @@ import static android.system.OsConstants.IPPROTO_IPV6; import static android.system.OsConstants.IPPROTO_TCP; import static android.system.OsConstants.IPPROTO_UDP; -import static android.system.OsConstants.SOCK_STREAM; import static com.android.net.module.util.NetworkStackConstants.ICMPV6_ECHO_REQUEST_TYPE; @@ -42,9 +42,13 @@ 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.ArgumentMatchers.anyBoolean; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.verify; +import android.content.BroadcastReceiver; import android.content.Context; +import android.content.Intent; import android.net.InetAddresses; import android.net.IpPrefix; import android.net.LinkAddress; @@ -54,14 +58,12 @@ import android.net.TcpKeepalivePacketDataParcelable; import android.net.apf.ApfFilter.ApfConfiguration; import android.net.apf.ApfGenerator.IllegalInstructionException; -import android.net.ip.IIpClientCallbacks; -import android.net.ip.IpClient.IpClientCallbacksWrapper; -import android.os.ConditionVariable; -import android.os.SystemClock; +import android.net.apf.ApfTestUtils.MockIpClientCallback; +import android.net.apf.ApfTestUtils.TestApfFilter; +import android.os.Build; +import android.os.PowerManager; import android.system.ErrnoException; -import android.system.Os; import android.text.TextUtils; -import android.text.format.DateUtils; import android.util.Log; import android.util.Pair; @@ -71,27 +73,26 @@ import com.android.internal.util.HexDump; import com.android.net.module.util.DnsPacket; import com.android.net.module.util.Inet4AddressUtils; -import com.android.net.module.util.InterfaceParams; import com.android.net.module.util.NetworkStackConstants; import com.android.net.module.util.PacketBuilder; -import com.android.net.module.util.SharedLog; -import com.android.networkstack.apishim.NetworkInformationShimImpl; import com.android.server.networkstack.tests.R; +import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRunner; -import libcore.io.IoUtils; import libcore.io.Streams; import org.junit.Assert; import org.junit.Before; +import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; +import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import java.io.ByteArrayOutputStream; import java.io.File; -import java.io.FileDescriptor; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; @@ -112,12 +113,13 @@ * * The test cases will be executed by both APFv4 and APFv6 interpreter. */ -@RunWith(Parameterized.class) +@RunWith(DevSdkIgnoreRunner.class) @SmallTest public class ApfTest { - private static final int TIMEOUT_MS = 500; private static final int MIN_APF_VERSION = 2; + @Rule + public DevSdkIgnoreRule mDevSdkIgnoreRule = new DevSdkIgnoreRule(); // Indicates which apf interpreter to run. @Parameterized.Parameter() public int mApfVersion; @@ -127,10 +129,14 @@ return Arrays.asList(4, 6); } - @Mock Context mContext; + @Mock private Context mContext; + @Mock + private ApfFilter.Dependencies mDependencies; + @Mock private PowerManager mPowerManager; @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); + doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class); } private static final String TAG = "ApfTest"; @@ -546,8 +552,8 @@ // Test filter age pre-filled memory. gen = new ApfGenerator(MIN_APF_VERSION); gen.addLoadFromMemory(R0, gen.FILTER_AGE_MEMORY_SLOT); - gen.addJumpIfR0Equals(1234567890, gen.DROP_LABEL); - assertDrop(gen, new byte[MIN_PKT_SIZE], 1234567890); + gen.addJumpIfR0Equals(123, gen.DROP_LABEL); + assertDrop(gen, new byte[MIN_PKT_SIZE], 123); // Test packet size pre-filled memory. gen = new ApfGenerator(MIN_APF_VERSION); @@ -915,96 +921,6 @@ apfFilter.shutdown(); } - private class MockIpClientCallback extends IpClientCallbacksWrapper { - private final ConditionVariable mGotApfProgram = new ConditionVariable(); - private byte[] mLastApfProgram; - - MockIpClientCallback() { - super(mock(IIpClientCallbacks.class), mock(SharedLog.class), - NetworkInformationShimImpl.newInstance()); - } - - @Override - public void installPacketFilter(byte[] filter) { - mLastApfProgram = filter; - mGotApfProgram.open(); - } - - public void resetApfProgramWait() { - mGotApfProgram.close(); - } - - public byte[] assertProgramUpdateAndGet() { - assertTrue(mGotApfProgram.block(TIMEOUT_MS)); - return mLastApfProgram; - } - - public void assertNoProgramUpdate() { - assertFalse(mGotApfProgram.block(TIMEOUT_MS)); - } - } - - private static class TestApfFilter extends ApfFilter { - public static final byte[] MOCK_MAC_ADDR = {1,2,3,4,5,6}; - - private FileDescriptor mWriteSocket; - private long mCurrentTimeMs = SystemClock.elapsedRealtime(); - private final MockIpClientCallback mMockIpClientCb; - - public TestApfFilter(Context context, ApfConfiguration config, - MockIpClientCallback ipClientCallback) throws Exception { - super(context, config, InterfaceParams.getByName("lo"), ipClientCallback); - mMockIpClientCb = ipClientCallback; - } - - // Pretend an RA packet has been received and show it to ApfFilter. - public void pretendPacketReceived(byte[] packet) throws IOException, ErrnoException { - mMockIpClientCb.resetApfProgramWait(); - // ApfFilter's ReceiveThread will be waiting to read this. - Os.write(mWriteSocket, packet, 0, packet.length); - } - - // Simulate current time changes - public void increaseCurrentTimeSeconds(int delta) { - mCurrentTimeMs += delta * DateUtils.SECOND_IN_MILLIS; - } - - @Override - protected int secondsSinceBoot() { - return (int) (mCurrentTimeMs / DateUtils.SECOND_IN_MILLIS); - } - - @Override - public synchronized void maybeStartFilter() { - mHardwareAddress = MOCK_MAC_ADDR; - installNewProgramLocked(); - - // Create two sockets, "readSocket" and "mWriteSocket" and connect them together. - FileDescriptor readSocket = new FileDescriptor(); - mWriteSocket = new FileDescriptor(); - try { - Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mWriteSocket, readSocket); - } catch (ErrnoException e) { - fail(); - return; - } - // Now pass readSocket to ReceiveThread as if it was setup to read raw RAs. - // This allows us to pretend RA packets have been recieved via pretendPacketReceived(). - mReceiveThread = new ReceiveThread(readSocket); - mReceiveThread.start(); - } - - @Override - public void shutdown() { - super.shutdown(); - if (mReceiveThread != null) { - mReceiveThread.halt(); - mReceiveThread = null; - } - IoUtils.closeQuietly(mWriteSocket); - } - } - private static final int ETH_HEADER_LEN = 14; private static final int ETH_DEST_ADDR_OFFSET = 0; private static final int ETH_ETHERTYPE_OFFSET = 12; @@ -1134,17 +1050,6 @@ private static final int IPV6_UDP_DEST_PORT_OFFSET = IPV6_PAYLOAD_OFFSET + 2; private static final int MDNS_UDP_PORT = 5353; - // Helper to initialize a default apfFilter. - private ApfFilter setupApfFilter( - MockIpClientCallback ipClientCallback, ApfConfiguration config) throws Exception { - LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19); - LinkProperties lp = new LinkProperties(); - lp.addLinkAddress(link); - TestApfFilter apfFilter = new TestApfFilter(mContext, config, ipClientCallback); - apfFilter.setLinkProperties(lp); - return apfFilter; - } - private static void setIpv4VersionFields(ByteBuffer packet) { packet.putShort(ETH_ETHERTYPE_OFFSET, (short) ETH_P_IP); packet.put(IP_HEADER_OFFSET, (byte) 0x45); @@ -1800,20 +1705,60 @@ @Test public void testApfFilterMulticastPingWhileDozing() throws Exception { - MockIpClientCallback ipClientCallback = new MockIpClientCallback(); - ApfFilter apfFilter = setupApfFilter(ipClientCallback, getDefaultConfig()); + doTestApfFilterMulticastPingWhileDozing(false /* isLightDozing */); + } + + @Test + @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + public void testApfFilterMulticastPingWhileLightDozing() throws Exception { + doTestApfFilterMulticastPingWhileDozing(true /* isLightDozing */); + } + + @Test + @DevSdkIgnoreRule.IgnoreUpTo(Build.VERSION_CODES.TIRAMISU) + public void testShouldHandleLightDozeKillSwitch() throws Exception { + final MockIpClientCallback ipClientCallback = new MockIpClientCallback(); + final ApfConfiguration configuration = getDefaultConfig(); + configuration.shouldHandleLightDoze = false; + final ApfFilter apfFilter = TestApfFilter.createTestApfFilter(mContext, ipClientCallback, + configuration, mDependencies); + final ArgumentCaptor<BroadcastReceiver> receiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mDependencies).addDeviceIdleReceiver(receiverCaptor.capture(), anyBoolean()); + final BroadcastReceiver receiver = receiverCaptor.getValue(); + doReturn(true).when(mPowerManager).isDeviceLightIdleMode(); + receiver.onReceive(mContext, new Intent(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED)); + assertFalse(apfFilter.isInDozeMode()); + } + + private void doTestApfFilterMulticastPingWhileDozing(boolean isLightDozing) throws Exception { + final MockIpClientCallback ipClientCallback = new MockIpClientCallback(); + final ApfConfiguration configuration = getDefaultConfig(); + configuration.shouldHandleLightDoze = true; + final ApfFilter apfFilter = TestApfFilter.createTestApfFilter(mContext, ipClientCallback, + configuration, mDependencies); + final ArgumentCaptor<BroadcastReceiver> receiverCaptor = + ArgumentCaptor.forClass(BroadcastReceiver.class); + verify(mDependencies).addDeviceIdleReceiver(receiverCaptor.capture(), anyBoolean()); + final BroadcastReceiver receiver = receiverCaptor.getValue(); // Construct a multicast ICMPv6 ECHO request. final byte[] multicastIpv6Addr = {(byte)0xff,2,0,0,0,0,0,0,0,0,0,0,0,0,0,(byte)0xfb}; - ByteBuffer packet = makeIpv6Packet(IPPROTO_ICMPV6); + final ByteBuffer packet = makeIpv6Packet(IPPROTO_ICMPV6); packet.put(ICMP6_TYPE_OFFSET, (byte)ICMPV6_ECHO_REQUEST_TYPE); put(packet, IPV6_DEST_ADDR_OFFSET, multicastIpv6Addr); // Normally, we let multicast pings alone... assertPass(ipClientCallback.assertProgramUpdateAndGet(), packet.array()); + if (isLightDozing) { + doReturn(true).when(mPowerManager).isDeviceLightIdleMode(); + receiver.onReceive(mContext, new Intent(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED)); + } else { + doReturn(true).when(mPowerManager).isDeviceIdleMode(); + receiver.onReceive(mContext, new Intent(ACTION_DEVICE_IDLE_MODE_CHANGED)); + } // ...and even while dozing... - apfFilter.setDozeMode(true); assertPass(ipClientCallback.assertProgramUpdateAndGet(), packet.array()); // ...but when the multicast filter is also enabled, drop the multicast pings to save power. @@ -1829,7 +1774,13 @@ // Now wake up from doze mode to ensure that we no longer drop the packets. // (The multicast filter is still enabled at this point). - apfFilter.setDozeMode(false); + if (isLightDozing) { + doReturn(false).when(mPowerManager).isDeviceLightIdleMode(); + receiver.onReceive(mContext, new Intent(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED)); + } else { + doReturn(false).when(mPowerManager).isDeviceIdleMode(); + receiver.onReceive(mContext, new Intent(ACTION_DEVICE_IDLE_MODE_CHANGED)); + } assertPass(ipClientCallback.assertProgramUpdateAndGet(), packet.array()); apfFilter.shutdown(); @@ -1839,7 +1790,8 @@ public void testApfFilter802_3() throws Exception { MockIpClientCallback ipClientCallback = new MockIpClientCallback(); ApfConfiguration config = getDefaultConfig(); - ApfFilter apfFilter = setupApfFilter(ipClientCallback, config); + ApfFilter apfFilter = TestApfFilter.createTestApfFilter(mContext, ipClientCallback, config, + mDependencies); byte[] program = ipClientCallback.assertProgramUpdateAndGet(); // Verify empty packet of 100 zero bytes is passed @@ -1859,7 +1811,8 @@ ipClientCallback.resetApfProgramWait(); apfFilter.shutdown(); config.ieee802_3Filter = DROP_802_3_FRAMES; - apfFilter = setupApfFilter(ipClientCallback, config); + apfFilter = TestApfFilter.createTestApfFilter(mContext, ipClientCallback, config, + mDependencies); program = ipClientCallback.assertProgramUpdateAndGet(); // Verify that IEEE802.3 frame is dropped @@ -1886,7 +1839,8 @@ MockIpClientCallback ipClientCallback = new MockIpClientCallback(); ApfConfiguration config = getDefaultConfig(); - ApfFilter apfFilter = setupApfFilter(ipClientCallback, config); + ApfFilter apfFilter = TestApfFilter.createTestApfFilter(mContext, ipClientCallback, config, + mDependencies); byte[] program = ipClientCallback.assertProgramUpdateAndGet(); // Verify empty packet of 100 zero bytes is passed @@ -1906,7 +1860,8 @@ ipClientCallback.resetApfProgramWait(); apfFilter.shutdown(); config.ethTypeBlackList = ipv4BlackList; - apfFilter = setupApfFilter(ipClientCallback, config); + apfFilter = TestApfFilter.createTestApfFilter(mContext, ipClientCallback, config, + mDependencies); program = ipClientCallback.assertProgramUpdateAndGet(); // Verify that IPv4 frame will be dropped @@ -1921,7 +1876,8 @@ ipClientCallback.resetApfProgramWait(); apfFilter.shutdown(); config.ethTypeBlackList = ipv4Ipv6BlackList; - apfFilter = setupApfFilter(ipClientCallback, config); + apfFilter = TestApfFilter.createTestApfFilter(mContext, ipClientCallback, config, + mDependencies); program = ipClientCallback.assertProgramUpdateAndGet(); // Verify that IPv4 frame will be dropped
diff --git a/tests/unit/src/android/net/apf/ApfTestUtils.java b/tests/unit/src/android/net/apf/ApfTestUtils.java index 555b92e..69c4079 100644 --- a/tests/unit/src/android/net/apf/ApfTestUtils.java +++ b/tests/unit/src/android/net/apf/ApfTestUtils.java
@@ -16,17 +16,43 @@ package android.net.apf; import static android.net.apf.ApfJniUtils.apfSimulate; +import static android.system.OsConstants.AF_UNIX; +import static android.system.OsConstants.SOCK_STREAM; 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 android.content.Context; +import android.net.LinkAddress; +import android.net.LinkProperties; +import android.net.ip.IIpClientCallbacks; +import android.net.ip.IpClient; +import android.os.ConditionVariable; +import android.os.SystemClock; +import android.system.ErrnoException; +import android.system.Os; +import android.text.format.DateUtils; import com.android.internal.util.HexDump; +import com.android.net.module.util.InterfaceParams; +import com.android.net.module.util.SharedLog; +import com.android.networkstack.apishim.NetworkInformationShimImpl; +import libcore.io.IoUtils; + +import java.io.FileDescriptor; +import java.io.IOException; +import java.net.InetAddress; import java.util.Arrays; /** * The util class for calling the APF interpreter and check the return value */ public class ApfTestUtils { + public static final int TIMEOUT_MS = 500; public static final int PASS = 1; public static final int DROP = 0; // Interpreter will just accept packets without link layer headers, so pad fake packet to at @@ -137,6 +163,15 @@ } } + /** + * Runs the APF program with customized data region and checks the return code. + */ + public static void assertVerdict(int apfVersion, int expected, byte[] program, byte[] packet, + byte[] data) { + assertReturnCodesEqual(expected, + apfSimulate(apfVersion, program, packet, data, 0 /* filterAge */)); + } + private static void assertVerdict(int apfVersion, int expected, ApfGenerator gen, byte[] packet, int filterAge) throws ApfGenerator.IllegalInstructionException { assertReturnCodesEqual(expected, @@ -174,4 +209,134 @@ throws ApfGenerator.IllegalInstructionException { assertVerdict(apfVersion, DROP, gen, new byte[MIN_PKT_SIZE], 0); } + + /** + * The Mock ip client callback class. + */ + public static class MockIpClientCallback extends IpClient.IpClientCallbacksWrapper { + private final ConditionVariable mGotApfProgram = new ConditionVariable(); + private byte[] mLastApfProgram; + + MockIpClientCallback() { + super(mock(IIpClientCallbacks.class), mock(SharedLog.class), + NetworkInformationShimImpl.newInstance()); + } + + @Override + public void installPacketFilter(byte[] filter) { + mLastApfProgram = filter; + mGotApfProgram.open(); + } + + /** + * Reset the apf program and wait for the next update. + */ + public void resetApfProgramWait() { + mGotApfProgram.close(); + } + + /** + * Assert the program is update within TIMEOUT_MS and return the program. + */ + public byte[] assertProgramUpdateAndGet() { + assertTrue(mGotApfProgram.block(TIMEOUT_MS)); + return mLastApfProgram; + } + + /** + * Assert the program is not update within TIMEOUT_MS. + */ + public void assertNoProgramUpdate() { + assertFalse(mGotApfProgram.block(TIMEOUT_MS)); + } + } + + /** + * The test apf filter class. + */ + public static class TestApfFilter extends ApfFilter { + public static final byte[] MOCK_MAC_ADDR = {1, 2, 3, 4, 5, 6}; + private static final byte[] MOCK_IPV4_ADDR = {10, 0, 0, 1}; + + private FileDescriptor mWriteSocket; + private long mCurrentTimeMs = SystemClock.elapsedRealtime(); + private final MockIpClientCallback mMockIpClientCb; + + public TestApfFilter(Context context, ApfConfiguration config, + MockIpClientCallback ipClientCallback) throws Exception { + this(context, config, ipClientCallback, new Dependencies(context)); + } + + public TestApfFilter(Context context, ApfConfiguration config, + MockIpClientCallback ipClientCallback, Dependencies dependencies) { + super(context, config, InterfaceParams.getByName("lo"), ipClientCallback, dependencies); + mMockIpClientCb = ipClientCallback; + } + + /** + * Create a new test ApfFiler. + */ + public static ApfFilter createTestApfFilter(Context context, + MockIpClientCallback ipClientCallback, ApfConfiguration config, + ApfFilter.Dependencies dependencies) throws Exception { + LinkAddress link = new LinkAddress(InetAddress.getByAddress(MOCK_IPV4_ADDR), 19); + LinkProperties lp = new LinkProperties(); + lp.addLinkAddress(link); + TestApfFilter apfFilter = new TestApfFilter(context, config, ipClientCallback, + dependencies); + apfFilter.setLinkProperties(lp); + return apfFilter; + } + + /** + * Pretend an RA packet has been received and show it to ApfFilter. + */ + public void pretendPacketReceived(byte[] packet) throws IOException, ErrnoException { + mMockIpClientCb.resetApfProgramWait(); + // ApfFilter's ReceiveThread will be waiting to read this. + Os.write(mWriteSocket, packet, 0, packet.length); + } + + /** + * Simulate current time changes. + */ + public void increaseCurrentTimeSeconds(int delta) { + mCurrentTimeMs += delta * DateUtils.SECOND_IN_MILLIS; + } + + @Override + protected int secondsSinceBoot() { + return (int) (mCurrentTimeMs / DateUtils.SECOND_IN_MILLIS); + } + + @Override + public synchronized void maybeStartFilter() { + mHardwareAddress = MOCK_MAC_ADDR; + installNewProgramLocked(); + + // Create two sockets, "readSocket" and "mWriteSocket" and connect them together. + FileDescriptor readSocket = new FileDescriptor(); + mWriteSocket = new FileDescriptor(); + try { + Os.socketpair(AF_UNIX, SOCK_STREAM, 0, mWriteSocket, readSocket); + } catch (ErrnoException e) { + fail(); + return; + } + // Now pass readSocket to ReceiveThread as if it was setup to read raw RAs. + // This allows us to pretend RA packets have been received via pretendPacketReceived(). + mReceiveThread = new ReceiveThread(readSocket); + mReceiveThread.start(); + } + + @Override + public void shutdown() { + super.shutdown(); + if (mReceiveThread != null) { + mReceiveThread.halt(); + mReceiveThread = null; + } + IoUtils.closeQuietly(mWriteSocket); + } + } }
diff --git a/tests/unit/src/android/net/apf/ApfV5Test.kt b/tests/unit/src/android/net/apf/ApfV5Test.kt index d42396a..8cfa316 100644 --- a/tests/unit/src/android/net/apf/ApfV5Test.kt +++ b/tests/unit/src/android/net/apf/ApfV5Test.kt
@@ -71,6 +71,32 @@ " 0: write r0, 1", " 2: write r0, 2", " 4: write r0, 4"), ApfJniUtils.disassembleApf(program)) + + gen = ApfGenerator(MIN_APF_VERSION) + gen.addDataCopy(1, 5) + gen.addPacketCopy(1000, 255) + program = gen.generate() + assertContentEquals(byteArrayOf( + encodeInstruction(25, 1, 1), 1, 5, + encodeInstruction(25, 2, 0), + 0x03.toByte(), 0xe8.toByte(), 0xff.toByte(), + ), program) + assertContentEquals(arrayOf( + " 0: dcopy 1, 5", + " 3: pcopy 1000, 255"), ApfJniUtils.disassembleApf(program)) + + gen = ApfGenerator(MIN_APF_VERSION) + gen.addDataCopy(ApfGenerator.Register.R1, 0, 5) + gen.addPacketCopy(ApfGenerator.Register.R0, 1000, 255) + program = gen.generate() + assertContentEquals(byteArrayOf( + encodeInstruction(21, 1, 1), 42, 0, 5, + encodeInstruction(21, 2, 0), + 0, 41, 0x03.toByte(), 0xe8.toByte(), 0xff.toByte() + ), program) + assertContentEquals(arrayOf( + " 0: dcopy [r1+0], 5", + " 4: pcopy [r0+1000], 255"), ApfJniUtils.disassembleApf(program)) } private fun encodeInstruction(opcode: Int, immLength: Int, register: Int): Byte {
diff --git a/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt b/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt index de97ed4..00e480b 100644 --- a/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt +++ b/tests/unit/src/android/net/dhcp6/Dhcp6PacketTest.kt
@@ -20,7 +20,7 @@ import androidx.test.runner.AndroidJUnit4 import com.android.net.module.util.HexDump import com.android.testutils.assertThrows -import java.nio.ByteBuffer +import kotlin.test.assertEquals import kotlin.test.assertTrue import org.junit.Test import org.junit.runner.RunWith @@ -42,7 +42,7 @@ // IA prefix option(option_len=25) "001A001900000000000000004000000000000000000000000000000000" val bytes = HexDump.hexStringToByteArray(solicitHex) - val packet = Dhcp6Packet.decode(ByteBuffer.wrap(bytes)) + val packet = Dhcp6Packet.decode(bytes, bytes.size) assertTrue(packet is Dhcp6SolicitPacket) } @@ -61,7 +61,7 @@ "001A001900000000000000004000000000000000000000000000000000" val bytes = HexDump.hexStringToByteArray(solicitHex) assertThrows(Dhcp6Packet.ParseException::class.java) { - Dhcp6Packet.decode(ByteBuffer.wrap(bytes)) + Dhcp6Packet.decode(bytes, bytes.size) } } @@ -80,7 +80,7 @@ "001A0019000000000000000040000000000000000000000000000000" val bytes = HexDump.hexStringToByteArray(solicitHex) assertThrows(Dhcp6Packet.ParseException::class.java) { - Dhcp6Packet.decode(ByteBuffer.wrap(bytes)) + Dhcp6Packet.decode(bytes, bytes.size) } } @@ -99,7 +99,7 @@ "001A001900000000000000004000000000000000000000000000000000" val bytes = HexDump.hexStringToByteArray(solicitHex) assertThrows(Dhcp6Packet.ParseException::class.java) { - Dhcp6Packet.decode(ByteBuffer.wrap(bytes)) + Dhcp6Packet.decode(bytes, bytes.size) } } @@ -119,7 +119,7 @@ // IA prefix option(option_len=25, prefix="fdfd:9ed6:7950:2::/64") "001A00190000019F0000A8C040FDFD9ED6795000010000000000000000" val bytes = HexDump.hexStringToByteArray(advertiseHex) - val packet = Dhcp6Packet.decode(ByteBuffer.wrap(bytes)) + val packet = Dhcp6Packet.decode(bytes, bytes.size) assertTrue(packet is Dhcp6AdvertisePacket) } @@ -142,7 +142,37 @@ "001A00190000019F0000A8C040FDFD9ED6795000010000000000000000" val bytes = HexDump.hexStringToByteArray(advertiseHex) // The unsupported option will be skipped normally and won't throw ParseException. - val packet = Dhcp6Packet.decode(ByteBuffer.wrap(bytes)) + val packet = Dhcp6Packet.decode(bytes, bytes.size) assertTrue(packet is Dhcp6AdvertisePacket) } + + @Test + fun testDecodeDhcp6ReplyPacket() { + val replyHex = + // Reply, Transaction ID + "07000A47" + + // server identifier option(option_len=10) + "0002000A0003000186C9B26AED4D" + + // client identifier option(option_len=12) + "0001000C0003001B02FBBAFFFEB7BC71" + + // SOL_MAX_RT (don't support this option yet) + "005200040000003c" + + // Rapid Commit + "000e0000" + + // DNS recursive server (don't support this opton yet) + "00170010fdfd9ed6795000000000000000000001" + + // IA_PD option(option_len=70, including IA prefix option) + "0019004629cc56c7000000d300000152" + + // IA prefix option(option_len=25, prefix="2401:fa00:49c:412::/64", preferred=400, + // valid=1623) + "001a00190000019000000657402401fa00049c04120000000000000000" + + // IA prefix option(option_len=25, prefix="fdfd:9ed6:7950:2::/64", preferred=423, + // valid=43200) + "001a0019000001a70000a8c040fdfd9ed6795000020000000000000000" + val bytes = HexDump.hexStringToByteArray(replyHex) + val packet = Dhcp6Packet.decode(bytes, bytes.size) + assertTrue(packet is Dhcp6ReplyPacket) + assertEquals(400, packet.prefixDelegation.minimalPreferredLifetime) + assertEquals(1623, packet.prefixDelegation.minimalValidLifetime) + } }
diff --git a/tests/unit/src/android/net/ip/IpClientTest.java b/tests/unit/src/android/net/ip/IpClientTest.java index d8b2cc2..4e40fa2 100644 --- a/tests/unit/src/android/net/ip/IpClientTest.java +++ b/tests/unit/src/android/net/ip/IpClientTest.java
@@ -25,6 +25,7 @@ import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.anyBoolean; import static org.mockito.Mockito.any; import static org.mockito.Mockito.anyString; import static org.mockito.Mockito.clearInvocations; @@ -86,6 +87,8 @@ import org.mockito.Mock; import org.mockito.MockitoAnnotations; +import java.io.FileDescriptor; +import java.io.PrintWriter; import java.net.Inet4Address; import java.net.Inet6Address; import java.net.InetAddress; @@ -147,6 +150,8 @@ @Mock private IpMemoryStoreService mIpMemoryStoreService; @Mock private InterfaceParams mInterfaceParams; @Mock private IpConnectivityLog mMetricsLog; + @Mock private FileDescriptor mFd; + @Mock private PrintWriter mWriter; private NetworkObserver mObserver; private InterfaceParams mIfParams; @@ -702,7 +707,7 @@ final ArgumentCaptor<ApfConfiguration> configCaptor = ArgumentCaptor.forClass( ApfConfiguration.class); verify(mDependencies, timeout(TEST_TIMEOUT_MS)).maybeCreateApfFilter( - any(), configCaptor.capture(), any(), any()); + any(), configCaptor.capture(), any(), any(), anyBoolean()); return configCaptor.getValue(); } @@ -771,7 +776,7 @@ final ArgumentCaptor<ApfConfiguration> configCaptor = ArgumentCaptor.forClass( ApfConfiguration.class); verify(mDependencies, timeout(TEST_TIMEOUT_MS)).maybeCreateApfFilter( - any(), configCaptor.capture(), any(), any()); + any(), configCaptor.capture(), any(), any(), anyBoolean()); final ApfConfiguration actual = configCaptor.getValue(); assertNotNull(actual); assertEquals(4, actual.apfCapabilities.apfVersionSupported); @@ -782,6 +787,17 @@ } @Test + public void testDumpApfFilter_withNoException() throws Exception { + final IpClient ipc = makeIpClient(TEST_IFNAME); + final ApfConfiguration config = verifyApfFilterCreatedOnStart(ipc, + false /* isApfSupported */); + assertNull(config.apfCapabilities); + clearInvocations(mDependencies); + ipc.dump(mFd, mWriter, null /* args */); + verifyShutdown(ipc); + } + + @Test public void testApfUpdateCapabilities_nonNullInitialApfCapabilities() throws Exception { final IpClient ipc = makeIpClient(TEST_IFNAME); final ApfConfiguration config = verifyApfFilterCreatedOnStart(ipc, @@ -793,7 +809,8 @@ 8192 /* maxProgramSize */, 4 /* format */); ipc.updateApfCapabilities(newApfCapabilities); HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS); - verify(mDependencies, never()).maybeCreateApfFilter(any(), any(), any(), any()); + verify(mDependencies, never()).maybeCreateApfFilter(any(), any(), any(), any(), + anyBoolean()); verifyShutdown(ipc); } @@ -807,7 +824,8 @@ ipc.updateApfCapabilities(null /* apfCapabilities */); HandlerUtils.waitForIdle(ipc.getHandler(), TEST_TIMEOUT_MS); - verify(mDependencies, never()).maybeCreateApfFilter(any(), any(), any(), any()); + verify(mDependencies, never()).maybeCreateApfFilter(any(), any(), any(), any(), + anyBoolean()); verifyShutdown(ipc); }
diff --git a/tests/unit/src/android/net/shared/PrivateDnsConfigTest.java b/tests/unit/src/android/net/shared/PrivateDnsConfigTest.java new file mode 100644 index 0000000..94f04d5 --- /dev/null +++ b/tests/unit/src/android/net/shared/PrivateDnsConfigTest.java
@@ -0,0 +1,133 @@ +/* + * Copyright (C) 2023 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.shared; + +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OFF; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_OPPORTUNISTIC; +import static android.net.ConnectivitySettingsManager.PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; + +import android.net.PrivateDnsConfigParcel; + +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.net.InetAddress; + +@RunWith(JUnit4.class) +public final class PrivateDnsConfigTest { + private static final int OFF_MODE = PRIVATE_DNS_MODE_OFF; + private static final int OPPORTUNISTIC_MODE = PRIVATE_DNS_MODE_OPPORTUNISTIC; + private static final int STRICT_MODE = PRIVATE_DNS_MODE_PROVIDER_HOSTNAME; + + private static final InetAddress[] TEST_ADDRS = new InetAddress[] { + InetAddress.parseNumericAddress("1.2.3.4"), + InetAddress.parseNumericAddress("2001:db8::2"), + }; + + private String[] toStringArray(InetAddress[] ips) { + String[] out = new String[ips.length]; + int i = 0; + for (InetAddress ip : ips) { + out[i++] = ip.getHostAddress(); + } + return out; + } + + private void assertPrivateDnsConfigEquals(PrivateDnsConfig a, PrivateDnsConfig b) { + assertEquals(a.mode, b.mode); + assertEquals(a.hostname, b.hostname); + assertArrayEquals(a.ips, b.ips); + assertEquals(a.dohName, b.dohName); + assertArrayEquals(a.dohIps, b.dohIps); + assertEquals(a.dohPath, b.dohPath); + assertEquals(a.dohPort, b.dohPort); + } + + private void assertParcelEquals(PrivateDnsConfig cfg, PrivateDnsConfigParcel parcel) { + assertEquals(parcel.privateDnsMode, cfg.mode); + assertEquals(parcel.hostname, cfg.hostname); + assertArrayEquals(parcel.ips, toStringArray(cfg.ips)); + assertEquals(parcel.dohName, cfg.dohName); + assertEquals(parcel.dohPath, cfg.dohPath); + assertEquals(parcel.dohPort, cfg.dohPort); + assertArrayEquals(parcel.dohIps, toStringArray(cfg.dohIps)); + } + + // Tests both toParcel() and fromParcel() together. + private void testPrivateDnsConfigConversion(PrivateDnsConfig cfg) { + final PrivateDnsConfigParcel parcel = cfg.toParcel(); + assertParcelEquals(cfg, parcel); + + final PrivateDnsConfig convertedCfg = PrivateDnsConfig.fromParcel(parcel); + assertPrivateDnsConfigEquals(cfg, convertedCfg); + } + + // Tests that a PrivateDnsConfig and a PrivateDnsConfig that is converted from + // PrivateDnsConfigParcel are equal. + @Test + public void testParcelableConversion() { + // Test the constructor: PrivateDnsConfig() + testPrivateDnsConfigConversion(new PrivateDnsConfig()); + + // Test the constructor: PrivateDnsConfig(boolean useTls) + testPrivateDnsConfigConversion(new PrivateDnsConfig(true)); + testPrivateDnsConfigConversion(new PrivateDnsConfig(false)); + + // Test the constructor: PrivateDnsConfig(String hostname, InetAddress[] ips) + testPrivateDnsConfigConversion(new PrivateDnsConfig(null, null)); + testPrivateDnsConfigConversion(new PrivateDnsConfig(null, TEST_ADDRS)); + testPrivateDnsConfigConversion(new PrivateDnsConfig("dns.com", null)); + testPrivateDnsConfigConversion(new PrivateDnsConfig("dns.com", TEST_ADDRS)); + + // Test the constructor: + // PrivateDnsConfig(int mode, String hostname, InetAddress[] ips, + // String dohName, InetAddress[] dohIps, String dohPath, int dohPort) + for (int mode : new int[] { OFF_MODE, OPPORTUNISTIC_MODE, STRICT_MODE }) { + testPrivateDnsConfigConversion(new PrivateDnsConfig(mode, null, null, + null, null, null, -1)); + testPrivateDnsConfigConversion(new PrivateDnsConfig(mode, "dns.com", null, + null, null, null, -1)); + testPrivateDnsConfigConversion(new PrivateDnsConfig(mode, "dns.com", TEST_ADDRS, + null, null, null, -1)); + testPrivateDnsConfigConversion(new PrivateDnsConfig(mode, "dns.com", TEST_ADDRS, + "doh.com", null, null, -1)); + testPrivateDnsConfigConversion(new PrivateDnsConfig(mode, "dns.com", TEST_ADDRS, + "doh.com", TEST_ADDRS, null, -1)); + testPrivateDnsConfigConversion(new PrivateDnsConfig(mode, "dns.com", TEST_ADDRS, + "doh.com", TEST_ADDRS, "dohpath=/some-path{?dns}", -1)); + testPrivateDnsConfigConversion(new PrivateDnsConfig(mode, "dns.com", TEST_ADDRS, + "doh.com", TEST_ADDRS, "dohpath=/some-path{?dns}", 443)); + } + } + + @Test + public void testIpAddressArrayIsCopied() { + final InetAddress ip = InetAddress.parseNumericAddress("1.2.3.4"); + final InetAddress[] ipArray = new InetAddress[] { ip }; + final PrivateDnsConfig cfg = new PrivateDnsConfig(OPPORTUNISTIC_MODE, null /* hostname */, + ipArray /* ips */, null /* dohName */, ipArray /* dohIps */, null /* dohPath */, + -1 /* dohPort */); + + ipArray[0] = InetAddress.parseNumericAddress("2001:db8::2"); + assertArrayEquals(new InetAddress[] { ip }, cfg.ips); + assertArrayEquals(new InetAddress[] { ip }, cfg.dohIps); + } +}
diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java index 380f4ec..ff56f5f 100644 --- a/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java +++ b/tests/unit/src/com/android/networkstack/netlink/TcpInfoTest.java
@@ -242,4 +242,20 @@ return info; } + + @Test + public void testHashCode() { + final TcpInfo info = new TcpInfo(2, 1, 5); + final TcpInfo info2 = new TcpInfo(2, 1, 5); + + assertEquals(info, info2); + assertEquals(info.hashCode(), info2.hashCode()); + } + + @Test + public void testDecodeWscale() { + assertEquals("0:0", TcpInfo.decodeWscale((byte) 0)); + assertEquals("15:15", TcpInfo.decodeWscale((byte) 255)); + assertEquals("15:0", TcpInfo.decodeWscale((byte) 240)); + } }
diff --git a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java index a697d8d..3857b04 100644 --- a/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java +++ b/tests/unit/src/com/android/networkstack/netlink/TcpSocketTrackerTest.java
@@ -16,8 +16,12 @@ package com.android.networkstack.netlink; +import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; +import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; import static android.net.util.DataStallUtils.CONFIG_TCP_PACKETS_FAIL_PERCENTAGE; import static android.net.util.DataStallUtils.DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE; +import static android.os.PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED; import static android.os.PowerManager.ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED; import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; import static android.system.OsConstants.AF_INET; @@ -33,18 +37,22 @@ import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.any; import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.never; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; +import android.annotation.IntDef; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.net.ConnectivityManager; import android.net.INetd; import android.net.InetAddresses; import android.net.LinkProperties; import android.net.MarkMaskParcel; import android.net.Network; +import android.net.NetworkCapabilities; import android.os.Build; import android.os.PowerManager; import android.util.Log; @@ -57,6 +65,7 @@ import com.android.net.module.util.netlink.NetlinkUtils; import com.android.net.module.util.netlink.StructNlMsgHdr; import com.android.testutils.DevSdkIgnoreRule; +import com.android.testutils.DevSdkIgnoreRule.IgnoreAfter; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; import libcore.util.HexEncoding; @@ -71,6 +80,8 @@ import org.mockito.MockitoAnnotations; import java.io.FileDescriptor; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; import java.net.InetAddress; import java.nio.ByteBuffer; import java.nio.ByteOrder; @@ -118,10 +129,22 @@ private static final int TEST_NETID2_FWMARK = 0x1A85; private static final int NETID_MASK = 0xffff; private static final int TEST_UID1 = 1234; + private static final int TEST_UID2 = TEST_UID1 + 1; private static final short TEST_DST_PORT = 29113; private static final long TEST_COOKIE1 = 43387759684916L; private static final long TEST_COOKIE2 = TEST_COOKIE1 + 1; private static final InetAddress TEST_DNS1 = InetAddresses.parseNumericAddress("8.8.8.8"); + + private static final NetworkCapabilities CELL_METERED_CAPABILITIES = + new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET); + + private static final NetworkCapabilities CELL_NOT_METERED_CAPABILITIES = + new NetworkCapabilities() + .addTransportType(TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_METERED); @Mock private TcpSocketTracker.Dependencies mDependencies; @Mock private INetd mNetd; private final Network mNetwork = new Network(TEST_NETID1); @@ -129,6 +152,7 @@ private TerribleFailureHandler mOldWtfHandler; @Mock private Context mContext; @Mock private PowerManager mPowerManager; + @Mock private ConnectivityManager mCm; @Rule public final DevSdkIgnoreRule mIgnoreRule = new DevSdkIgnoreRule(); @@ -146,11 +170,13 @@ eq(NAMESPACE_CONNECTIVITY), eq(CONFIG_TCP_PACKETS_FAIL_PERCENTAGE), anyInt())).thenReturn(DEFAULT_TCP_PACKETS_FAIL_PERCENTAGE); - when(mDependencies.shouldDisableInLightDoze()).thenReturn(true); + when(mDependencies.shouldDisableInLightDoze(anyBoolean())).thenReturn(true); when(mNetd.getFwmarkForNetwork(eq(TEST_NETID1))) .thenReturn(makeMarkMaskParcel(NETID_MASK, TEST_NETID1_FWMARK)); + doReturn(mContext).when(mDependencies).getContext(); doReturn(mPowerManager).when(mContext).getSystemService(PowerManager.class); + doReturn(mCm).when(mContext).getSystemService(ConnectivityManager.class); } @After @@ -180,7 +206,8 @@ public void testParseSockInfo() { final ByteBuffer buffer = getByteBuffer(SOCK_DIAG_TCP_INET_TEST_BYTES); final ArrayList<TcpSocketTracker.SocketInfo> infoList = new ArrayList<>(); - TcpSocketTracker.parseMessage(buffer, AF_INET, infoList, 100L); + final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mNetwork); + tst.parseMessage(buffer, AF_INET, infoList, 100L); assertEquals(1, infoList.size()); final TcpSocketTracker.SocketInfo parsed = infoList.get(0); @@ -263,7 +290,7 @@ testLp.addDnsServer(TEST_DNS1); tst.setLinkProperties(testLp); doReturn(getByteBufferFromHexString(composeSockDiagTcpHex(9, 10) - + composeSockDiagTcpHex(9, 10, DNS_OVER_TLS_PORT, TEST_COOKIE2) + + composeSockDiagTcpHex(9, 10, DNS_OVER_TLS_PORT, TEST_COOKIE2, TEST_UID1) + NLMSG_DONE_HEX)) .when(mDependencies).recvMessage(any()); assertTrue(tst.pollSocketsInfo()); @@ -281,7 +308,7 @@ testLp.addValidatedPrivateDnsServer(TEST_DNS1); tst.setLinkProperties(testLp); doReturn(getByteBufferFromHexString(composeSockDiagTcpHex(10, 12) - + composeSockDiagTcpHex(11, 12, DNS_OVER_TLS_PORT, TEST_COOKIE2) + + composeSockDiagTcpHex(11, 12, DNS_OVER_TLS_PORT, TEST_COOKIE2, TEST_UID1) + NLMSG_DONE_HEX)) .when(mDependencies).recvMessage(any()); assertTrue(tst.pollSocketsInfo()); @@ -295,7 +322,7 @@ // polling cycle. tst.setOpportunisticMode(false); doReturn(getByteBufferFromHexString(composeSockDiagTcpHex(11, 14) - + composeSockDiagTcpHex(13, 14, DNS_OVER_TLS_PORT, TEST_COOKIE2) + + composeSockDiagTcpHex(13, 14, DNS_OVER_TLS_PORT, TEST_COOKIE2, TEST_UID1) + NLMSG_DONE_HEX)) .when(mDependencies).recvMessage(any()); assertTrue(tst.pollSocketsInfo()); @@ -305,6 +332,101 @@ assertFalse(tst.isDataStallSuspected()); } + @IgnoreAfter(Build.VERSION_CODES.S_V2) + @Test + public void testPollSocketsInfo_ignoreBlockedUid_featureDisabled_beforeT() throws Exception { + doTestPollSocketsInfo_ignoreBlockedUid_featureDisabled(); + } + + @IgnoreUpTo(Build.VERSION_CODES.S_V2) + @Test + public void testPollSocketsInfo_ignoreBlockedUid_featureDisabled_TOrAbove() throws Exception { + doTestPollSocketsInfo_ignoreBlockedUid_featureDisabled(); + verify(mCm, never()).isUidNetworkingBlocked(anyInt(), anyBoolean()); + } + + private void doTestPollSocketsInfo_ignoreBlockedUid_featureDisabled() throws Exception { + doReturn(false).when(mDependencies).shouldIgnoreTcpInfoForBlockedUids(); + final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mNetwork); + // Simulate 1 message with data stall happened. + doReturn(getByteBufferFromHexString( + composeSockDiagTcpHex(4, 10) + NLMSG_DONE_HEX)) + .when(mDependencies).recvMessage(any()); + assertTrue(tst.pollSocketsInfo()); + // 4 retran / 10 sent = 40 percent. + assertEquals(40, tst.getLatestPacketFailPercentage()); + assertEquals(10, tst.getSentSinceLastRecv()); + assertFalse(tst.isDataStallSuspected()); + + // With the feature disabled, append another message with blocked uid, verify the + // traffic of networking-blocked uid is not filtered. + doReturn(getByteBufferFromHexString(composeSockDiagTcpHex(9, 10) + + composeSockDiagTcpHex(5, 10, DNS_OVER_TLS_PORT, TEST_COOKIE2, TEST_UID2) + + NLMSG_DONE_HEX)) + .when(mDependencies).recvMessage(any()); + assertTrue(tst.pollSocketsInfo()); + // 5 + 5 retrans / 10 sent = 100 percent. + assertEquals(100, tst.getLatestPacketFailPercentage()); + assertEquals(20, tst.getSentSinceLastRecv()); + assertTrue(tst.isDataStallSuspected()); + } + + // The feature is not enabled on pre-T device, because it needs bpf support. + @IgnoreUpTo(Build.VERSION_CODES.S_V2) + @Test + public void testPollSocketsInfo_ignoreBlockedUid_featureEnabled() throws Exception { + doReturn(true).when(mDependencies).shouldIgnoreTcpInfoForBlockedUids(); + final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mNetwork); + tst.setNetworkCapabilities(CELL_NOT_METERED_CAPABILITIES); + doReturn(true).when(mCm).isUidNetworkingBlocked(TEST_UID2, false /* metered */); + // With the feature enabled, append another message with blocked uid, verify the + // traffic of networking-blocked uid is filtered out. + doReturn(getByteBufferFromHexString(composeSockDiagTcpHex(4, 10) + + composeSockDiagTcpHex(6, 12, DNS_OVER_TLS_PORT, TEST_COOKIE2, TEST_UID2) + + NLMSG_DONE_HEX)) + .when(mDependencies).recvMessage(any()); + assertTrue(tst.pollSocketsInfo()); + assertEquals(40, tst.getLatestPacketFailPercentage()); + assertEquals(10, tst.getSentSinceLastRecv()); + assertFalse(tst.isDataStallSuspected()); + + // Unblock traffic of the uid, verify the traffic of the uid is not filtered. + doReturn(false).when(mCm).isUidNetworkingBlocked(TEST_UID2, false /* metered */); + doReturn(getByteBufferFromHexString(composeSockDiagTcpHex(4, 10) + + composeSockDiagTcpHex(8, 14, DNS_OVER_TLS_PORT, TEST_COOKIE2, TEST_UID2) + + NLMSG_DONE_HEX)) + .when(mDependencies).recvMessage(any()); + assertTrue(tst.pollSocketsInfo()); + // Lost 2 / 2 sent = 100 percent. + assertEquals(100, tst.getLatestPacketFailPercentage()); + assertEquals(12, tst.getSentSinceLastRecv()); + assertTrue(tst.isDataStallSuspected()); + } + + // The feature is not enabled on pre-T device, because it needs bpf support. + @IgnoreUpTo(Build.VERSION_CODES.S_V2) + @Test + public void testPollSocketsInfo_ignoreBlockedUid_featureEnabled_dataSaver() throws Exception { + doReturn(true).when(mDependencies).shouldIgnoreTcpInfoForBlockedUids(); + final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mNetwork); + + tst.setNetworkCapabilities(CELL_NOT_METERED_CAPABILITIES); + final ByteBuffer mockMessage = getByteBufferFromHexString(composeSockDiagTcpHex(4, 10) + + NLMSG_DONE_HEX); + doReturn(mockMessage).when(mDependencies).recvMessage(any()); + assertTrue(tst.pollSocketsInfo()); + verify(mCm).isUidNetworkingBlocked(TEST_UID1, false /* metered */); + + // Verify the metered parameter will be correctly passed to ConnectivityManager. + tst.setNetworkCapabilities(CELL_METERED_CAPABILITIES); + mockMessage.rewind(); // Reset read position to 0 since the same buffer is used. + assertTrue(tst.pollSocketsInfo()); + verify(mCm).isUidNetworkingBlocked(TEST_UID1, true /* metered */); + + // Correctness of the logic which handling different blocked status is + // verified in other tests, see {@code testPollSocketsInfo_ignoreBlockedUid_featureEnabled}. + } + @Test public void testTcpInfoParsingWithMultipleMsgs() throws Exception { final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mNetwork); @@ -469,133 +591,160 @@ } private static String composeSockDiagTcpHex(int retrans, int sent) { - return composeSockDiagTcpHex(retrans, sent, TEST_DST_PORT, TEST_COOKIE1); + return composeSockDiagTcpHex(retrans, sent, TEST_DST_PORT, TEST_COOKIE1, TEST_UID1); } - private static String composeSockDiagTcpHex(int retrans, int sent, short dstPort, long cookie) { + private static String composeSockDiagTcpHex(int retrans, int sent, short dstPort, + long cookie, int uid) { return // struct nlmsghdr. - "14010000" + // length = 276 - "1400" + // type = SOCK_DIAG_BY_FAMILY - "0301" + // flags = NLM_F_REQUEST | NLM_F_DUMP - "00000000" + // seqno - "00000000" + // pid (0 == kernel) + "14010000" // length = 276 + + "1400" // type = SOCK_DIAG_BY_FAMILY + + "0301" // flags = NLM_F_REQUEST | NLM_F_DUMP + + "00000000" // seqno + + "00000000" // pid (0 == kernel) // struct inet_diag_req_v2 - "02" + // family = AF_INET - "06" + // state - "00" + // timer - "00" + // retrans + + "02" // family = AF_INET + + "06" // state + + "00" // timer + + "00" // retrans // inet_diag_sockid: ports and addresses are always in big endian, // see StructInetDiagSockId. - "DEA5" + // idiag_sport = 56997 - getHexStringFromShort(dstPort, ByteOrder.BIG_ENDIAN) + // idiag_dport - "0a006402000000000000000000000000" + // idiag_src = 10.0.100.2 - "08080808000000000000000000000000" + // idiag_dst = 8.8.8.8 - "00000000" + // idiag_if - getHexStringFromLong(cookie) + // idiag_cookie - "00000000" + // idiag_expires - "00000000" + // idiag_rqueue - "00000000" + // idiag_wqueue - getHexStringFromInt(TEST_UID1) + // idiag_uid - "00000000" + // idiag_inode + + "DEA5" // idiag_sport = 56997 + + getHexStringFromShort(dstPort, ByteOrder.BIG_ENDIAN) // idiag_dport + + "0a006402000000000000000000000000" // idiag_src = 10.0.100.2 + + "08080808000000000000000000000000" // idiag_dst = 8.8.8.8 + + "00000000" // idiag_if + + getHexStringFromLong(cookie) // idiag_cookie + + "00000000" // idiag_expires + + "00000000" // idiag_rqueue + + "00000000" // idiag_wqueue + + getHexStringFromInt(uid) // idiag_uid + + "00000000" // idiag_inode // rtattr - "0500" + // len = 5 - "0800" + // type = 8 - "00000000" + // data - "0800" + // len = 8 - "0F00" + // type = 15(INET_DIAG_MARK) - "850A0C00" + // data, socket mark=789125 - "AC00" + // len = 172 - "0200" + // type = 2(INET_DIAG_INFO) + + "0500" // len = 5 + + "0800" // type = 8 + + "00000000" // data + + "0800" // len = 8 + + "0F00" // type = 15(INET_DIAG_MARK) + + "850A0C00" // data, socket mark=789125 + + "AC00" // len = 172 + + "0200" // type = 2(INET_DIAG_INFO) // tcp_info - "01" + // state = TCP_ESTABLISHED - "00" + // ca_state = TCP_CA_OPEN - "05" + // retransmits = 5 - "00" + // probes = 0 - "00" + // backoff = 0 - "07" + // option = TCPI_OPT_WSCALE|TCPI_OPT_SACK|TCPI_OPT_TIMESTAMPS - "88" + // wscale = 8 - "00" + // delivery_rate_app_limited = 0 - "4A911B00" + // rto = 1806666 - "00000000" + // ato = 0 - "2E050000" + // sndMss = 1326 - "18020000" + // rcvMss = 536 - "00000000" + // unsacked = 0 - "00000000" + // acked = 0 - "00000000" + // lost - "00000000" + // retrans = 0 - "00000000" + // fackets = 0 - "BB000000" + // lastDataSent = 187 - "00000000" + // lastAckSent = 0 - "BB000000" + // lastDataRecv = 187 - "BB000000" + // lastDataAckRecv = 187 - "DC050000" + // pmtu = 1500 - "30560100" + // rcvSsthresh = 87600 - "3E2C0900" + // rttt = 601150 - "1F960400" + // rttvar = 300575 - "78050000" + // sndSsthresh = 1400 - "0A000000" + // sndCwnd = 10 - "A8050000" + // advmss = 1448 - "03000000" + // reordering = 3 - "00000000" + // rcvrtt = 0 - "30560100" + // rcvspace = 87600 - getHexStringFromInt(retrans) + // totalRetrans - "53AC000000000000" + // pacingRate = 44115 - "FFFFFFFFFFFFFFFF" + // maxPacingRate = 18446744073709551615 - "0100000000000000" + // bytesAcked = 1 - "0000000000000000" + // bytesReceived = 0 - getHexStringFromInt(sent) + // SegsOut - "00000000" + // SegsIn = 0 - "00000000" + // NotSentBytes = 0 - "3E2C0900" + // minRtt = 601150 - "00000000" + // DataSegsIn = 0 - "00000000" + // DataSegsOut = 0 - "0000000000000000"; // deliverRate = 0 + + "01" // state = TCP_ESTABLISHED + + "00" // ca_state = TCP_CA_OPEN + + "05" // retransmits = 5 + + "00" // probes = 0 + + "00" // backoff = 0 + + "07" // option = TCPI_OPT_WSCALE|TCPI_OPT_SACK|TCPI_OPT_TIMESTAMPS + + "88" // wscale = 8 + + "00" // delivery_rate_app_limited = 0 + + "4A911B00" // rto = 1806666 + + "00000000" // ato = 0 + + "2E050000" // sndMss = 1326 + + "18020000" // rcvMss = 536 + + "00000000" // unsacked = 0 + + "00000000" // acked = 0 + + "00000000" // lost + + "00000000" // retrans = 0 + + "00000000" // fackets = 0 + + "BB000000" // lastDataSent = 187 + + "00000000" // lastAckSent = 0 + + "BB000000" // lastDataRecv = 187 + + "BB000000" // lastDataAckRecv = 187 + + "DC050000" // pmtu = 1500 + + "30560100" // rcvSsthresh = 87600 + + "3E2C0900" // rttt = 601150 + + "1F960400" // rttvar = 300575 + + "78050000" // sndSsthresh = 1400 + + "0A000000" // sndCwnd = 10 + + "A8050000" // advmss = 1448 + + "03000000" // reordering = 3 + + "00000000" // rcvrtt = 0 + + "30560100" // rcvspace = 87600 + + getHexStringFromInt(retrans) // totalRetrans + + "53AC000000000000" // pacingRate = 44115 + + "FFFFFFFFFFFFFFFF" // maxPacingRate = 18446744073709551615 + + "0100000000000000" // bytesAcked = 1 + + "0000000000000000" // bytesReceived = 0 + + getHexStringFromInt(sent) // SegsOut + + "00000000" // SegsIn = 0 + + "00000000" // NotSentBytes = 0 + + "3E2C0900" // minRtt = 601150 + + "00000000" // DataSegsIn = 0 + + "00000000" // DataSegsOut = 0 + + "0000000000000000"; // deliverRate = 0 } + private static final int DEEP_DOZE = 0; + private static final int LIGHT_DOZE = 1; + + @Retention(RetentionPolicy.SOURCE) + @IntDef(value = { + DEEP_DOZE, + LIGHT_DOZE + }) + private @interface DozeModeType {} + @Test - public void testTcpInfoParsingWithDozeMode() throws Exception { - final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mNetwork); - final ArgumentCaptor<BroadcastReceiver> receiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); + public void testTcpInfoParsingWithDozeMode_enabled() throws Exception { + doReturn(false).when(mDependencies).shouldIgnoreTcpInfoForBlockedUids(); + doReturn(false).when(mDependencies).shouldDisableInLightDoze(anyBoolean()); + doTestTcpInfoDisableParsingWithDozeMode(DEEP_DOZE, true /* featureEnabled */); + } - verify(mDependencies).addDeviceIdleReceiver(receiverCaptor.capture(), anyBoolean()); - setupNormalTestTcpInfo(); - assertTrue(tst.pollSocketsInfo()); - - // Lower the threshold. - when(mDependencies.getDeviceConfigPropertyInt(any(), eq(CONFIG_TCP_PACKETS_FAIL_PERCENTAGE), - anyInt())).thenReturn(40); - - // Trigger a config update. - tst.mConfigListener.onPropertiesChanged(null /* properties */); - assertEquals(10, tst.getSentSinceLastRecv()); - assertEquals(50, tst.getLatestPacketFailPercentage()); - assertTrue(tst.isDataStallSuspected()); - - // Enable doze mode, verify counters are not updated. - doReturn(true).when(mPowerManager).isDeviceIdleMode(); - final BroadcastReceiver receiver = receiverCaptor.getValue(); - receiver.onReceive(mContext, new Intent(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)); - assertFalse(tst.pollSocketsInfo()); - assertEquals(10, tst.getSentSinceLastRecv()); - assertEquals(50, tst.getLatestPacketFailPercentage()); - assertFalse(tst.isDataStallSuspected()); + // Ignore blocked uids is supported on T. Thus, for pre-T device this feature is always + // needed since there is no replacement. + @IgnoreUpTo(Build.VERSION_CODES.S_V2) + @Test + public void testTcpInfoParsingWithDozeMode_disabled() throws Exception { + doReturn(true).when(mDependencies).shouldIgnoreTcpInfoForBlockedUids(); + doReturn(false).when(mDependencies).shouldDisableInLightDoze(anyBoolean()); + doTestTcpInfoDisableParsingWithDozeMode(DEEP_DOZE, false /* featureEnabled */); } @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2) public void testTcpInfoDisableParsingWithLightDozeMode_enabled() throws Exception { + doReturn(true).when(mDependencies).shouldDisableInLightDoze(anyBoolean()); + doTestTcpInfoDisableParsingWithDozeMode(LIGHT_DOZE, true /* featureEnabled */); + } + + @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2) + public void testTcpInfoDisableParsingWithLightDozeMode_disabled() throws Exception { + doReturn(false).when(mDependencies).shouldDisableInLightDoze(anyBoolean()); + doTestTcpInfoDisableParsingWithDozeMode(LIGHT_DOZE, false /* featureEnabled */); + } + + private void doTestTcpInfoDisableParsingWithDozeMode(@DozeModeType int dozeModeType, + boolean featureEnabled) throws Exception { final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mNetwork); + tst.setNetworkCapabilities(CELL_NOT_METERED_CAPABILITIES); final ArgumentCaptor<BroadcastReceiver> receiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class); - // Enable light doze mode with 1 netlink message. - verify(mDependencies).addDeviceIdleReceiver(receiverCaptor.capture(), anyBoolean()); + // Enable doze mode with 1 netlink message. + verify(mDependencies).addDeviceIdleReceiver(receiverCaptor.capture(), + anyBoolean(), anyBoolean()); final BroadcastReceiver receiver = receiverCaptor.getValue(); - doReturn(true).when(mPowerManager).isDeviceLightIdleMode(); - receiver.onReceive(mContext, new Intent(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED)); + if (dozeModeType == DEEP_DOZE) { + doReturn(true).when(mPowerManager).isDeviceIdleMode(); + receiver.onReceive(mContext, new Intent(ACTION_DEVICE_IDLE_MODE_CHANGED)); + } else { + doReturn(true).when(mPowerManager).isDeviceLightIdleMode(); + receiver.onReceive(mContext, new Intent(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED)); + } doReturn(getByteBufferFromHexString(composeSockDiagTcpHex(9, 10) + NLMSG_DONE_HEX)).when(mDependencies).recvMessage(any()); + if (!featureEnabled) { + // Verify TcpInfo is still processed. + assertTrue(tst.pollSocketsInfo()); + assertEquals(10, tst.getSentSinceLastRecv()); + // Lost 4 + default 5 retrans / 10 sent. + assertEquals(90, tst.getLatestPacketFailPercentage()); + assertTrue(tst.isDataStallSuspected()); + return; + } + // Verify counters are not updated. assertFalse(tst.pollSocketsInfo()); assertEquals(0, tst.getSentSinceLastRecv()); @@ -603,9 +752,14 @@ assertEquals(-1, tst.getLatestPacketFailPercentage()); assertFalse(tst.isDataStallSuspected()); - // Disable light doze mode, verify polling are processed and counters are updated. - doReturn(false).when(mPowerManager).isDeviceLightIdleMode(); - receiver.onReceive(mContext, new Intent(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED)); + // Disable deep/light doze mode, verify polling are processed and counters are updated. + if (dozeModeType == DEEP_DOZE) { + doReturn(false).when(mPowerManager).isDeviceIdleMode(); + receiver.onReceive(mContext, new Intent(ACTION_DEVICE_IDLE_MODE_CHANGED)); + } else { + doReturn(false).when(mPowerManager).isDeviceLightIdleMode(); + receiver.onReceive(mContext, new Intent(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED)); + } assertTrue(tst.pollSocketsInfo()); assertEquals(10, tst.getSentSinceLastRecv()); // Lost 4 + default 5 retrans / 10 sent. @@ -613,28 +767,6 @@ assertTrue(tst.isDataStallSuspected()); } - @Test @IgnoreUpTo(Build.VERSION_CODES.S_V2) - public void testTcpInfoDisableParsingWithLightDozeMode_disabled() throws Exception { - when(mDependencies.shouldDisableInLightDoze()).thenReturn(false); - final TcpSocketTracker tst = new TcpSocketTracker(mDependencies, mNetwork); - final ArgumentCaptor<BroadcastReceiver> receiverCaptor = - ArgumentCaptor.forClass(BroadcastReceiver.class); - - // Enable light doze mode with 1 netlink message. - verify(mDependencies).addDeviceIdleReceiver(receiverCaptor.capture(), anyBoolean()); - final BroadcastReceiver receiver = receiverCaptor.getValue(); - doReturn(true).when(mPowerManager).isDeviceLightIdleMode(); - receiver.onReceive(mContext, new Intent(ACTION_DEVICE_LIGHT_IDLE_MODE_CHANGED)); - doReturn(getByteBufferFromHexString(composeSockDiagTcpHex(9, 10) - + NLMSG_DONE_HEX)).when(mDependencies).recvMessage(any()); - - // Verify TcpInfo is still processed. - assertTrue(tst.pollSocketsInfo()); - assertEquals(10, tst.getSentSinceLastRecv()); - assertEquals(90, tst.getLatestPacketFailPercentage()); - assertTrue(tst.isDataStallSuspected()); - } - private void setupNormalTestTcpInfo() throws Exception { final ByteBuffer tcpBufferV6 = getByteBuffer(TEST_RESPONSE_BYTES); final ByteBuffer tcpBufferV4 = getByteBuffer(TEST_RESPONSE_BYTES); @@ -643,28 +775,28 @@ private static final String BAD_DIAG_MSG_HEX = // struct nlmsghdr. - "00000058" + // length = 1476395008 - "1400" + // type = SOCK_DIAG_BY_FAMILY - "0301" + // flags = NLM_F_REQUEST | NLM_F_DUMP - "00000000" + // seqno - "00000000" + // pid (0 == kernel) + "00000058" // length = 1476395008 + + "1400" // type = SOCK_DIAG_BY_FAMILY + + "0301" // flags = NLM_F_REQUEST | NLM_F_DUMP + + "00000000" // seqno + + "00000000" // pid (0 == kernel) // struct inet_diag_req_v2 - "02" + // family = AF_INET - "06" + // state - "00" + // timer - "00" + // retrans + + "02" // family = AF_INET + + "06" // state + + "00" // timer + + "00" // retrans // inet_diag_sockid - "DEA5" + // idiag_sport = 42462 - "71B9" + // idiag_dport = 47473 - "0a006402000000000000000000000000" + // idiag_src = 10.0.100.2 - "08080808000000000000000000000000" + // idiag_dst = 8.8.8.8 - "00000000" + // idiag_if - "34ED000076270000" + // idiag_cookie = 43387759684916 - "00000000" + // idiag_expires - "00000000" + // idiag_rqueue - "00000000" + // idiag_wqueue - "00000000" + // idiag_uid - "00000000"; // idiag_inode + + "DEA5" // idiag_sport = 42462 + + "71B9" // idiag_dport = 47473 + + "0a006402000000000000000000000000" // idiag_src = 10.0.100.2 + + "08080808000000000000000000000000" // idiag_dst = 8.8.8.8 + + "00000000" // idiag_if + + "34ED000076270000" // idiag_cookie = 43387759684916 + + "00000000" // idiag_expires + + "00000000" // idiag_rqueue + + "00000000" // idiag_wqueue + + "00000000" // idiag_uid + + "00000000"; // idiag_inode private static final byte[] BAD_SOCK_DIAG_MSG_BYTES = HexEncoding.decode(BAD_DIAG_MSG_HEX.toCharArray(), false);
diff --git a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java index 3f2ff31..5be2573 100644 --- a/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java +++ b/tests/unit/src/com/android/server/connectivity/NetworkMonitorTest.java
@@ -32,6 +32,7 @@ import static android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_METERED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED; +import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_SUSPENDED; import static android.net.NetworkCapabilities.NET_CAPABILITY_NOT_VCN_MANAGED; import static android.net.NetworkCapabilities.NET_CAPABILITY_OEM_PAID; import static android.net.NetworkCapabilities.NET_CAPABILITY_TRUSTED; @@ -62,6 +63,7 @@ import static com.android.networkstack.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS; import static com.android.networkstack.util.NetworkStackUtils.DEFAULT_CAPTIVE_PORTAL_DNS_PROBE_TIMEOUT; import static com.android.networkstack.util.NetworkStackUtils.DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION; +import static com.android.networkstack.util.NetworkStackUtils.REEVALUATE_WHEN_RESUME; import static com.android.server.connectivity.NetworkMonitor.INITIAL_REEVALUATE_DELAY_MS; import static com.android.server.connectivity.NetworkMonitor.extractCharset; @@ -319,10 +321,17 @@ return lp; } - private static final NetworkCapabilities CELL_METERED_CAPABILITIES = new NetworkCapabilities() + private static final NetworkCapabilities CELL_SUSPENDED_METERED_CAPABILITIES = + new NetworkCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) .addCapability(NET_CAPABILITY_INTERNET); + private static final NetworkCapabilities CELL_METERED_CAPABILITIES = + new NetworkCapabilities() + .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) + .addCapability(NET_CAPABILITY_INTERNET) + .addCapability(NET_CAPABILITY_NOT_SUSPENDED); + private static final NetworkCapabilities CELL_NOT_METERED_CAPABILITIES = new NetworkCapabilities() .addTransportType(NetworkCapabilities.TRANSPORT_CELLULAR) @@ -554,6 +563,7 @@ initHttpConnection(mHttpConnection); initHttpConnection(mHttpsConnection); initHttpConnection(mFallbackConnection); + initHttpConnection(mOtherFallbackConnection); mFakeDns = new FakeDns(); mFakeDns.startMocking(); @@ -2290,6 +2300,64 @@ } @Test + public void testReevaluationInterval_networkResume() throws Exception { + // Setup nothing and expect validation to fail. + doReturn(true).when(mDependencies) + .isFeatureNotChickenedOut(any(), eq(REEVALUATE_WHEN_RESUME)); + final NetworkMonitor nm = runFailedNetworkTest(); + verifyNetworkTested(VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, + 1 /* interactions */); + // Reevaluation delay doubled right after 1st validation failure. + assertEquals(INITIAL_REEVALUATE_DELAY_MS * 2, nm.getReevaluationDelayMs()); + + // Suspend the network. Verify re-evaluation count does not increase. + setNetworkCapabilities(nm, CELL_SUSPENDED_METERED_CAPABILITIES); + verifyNetworkTested(VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, + 1 /* interactions */); + // Verify the count does not increase. + assertEquals(INITIAL_REEVALUATE_DELAY_MS * 2, nm.getReevaluationDelayMs()); + + // Resume the network, verify re-evaluation runs immediately and the timer resets. + setNetworkCapabilities(nm, CELL_METERED_CAPABILITIES); + // Wait for another idle to prevent from flaky because the handler fires another message + // to re-evaluate. + HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); + assertEquals(INITIAL_REEVALUATE_DELAY_MS, nm.getReevaluationDelayMs()); + verifyNetworkTested(VALIDATION_RESULT_INVALID, 0 /* probesSucceeded */, + 2 /* interactions */); + } + + @Test + public void testReevaluationInterval_verifiedNetwork() throws Exception { + final WrappedNetworkMonitor wnm = prepareValidatedStateNetworkMonitor( + CELL_METERED_CAPABILITIES); + assertEquals(INITIAL_REEVALUATE_DELAY_MS, wnm.getReevaluationDelayMs()); + + // Suspend the network. Verify re-evaluation count does not increase. + setNetworkCapabilities(wnm, CELL_SUSPENDED_METERED_CAPABILITIES); + verifyNetworkTestedValidFromHttps(1 /* interactions */); + assertEquals(INITIAL_REEVALUATE_DELAY_MS, wnm.getReevaluationDelayMs()); + + // Resume the network. Verify re-evaluation count does not increase. + setNetworkCapabilities(wnm, CELL_METERED_CAPABILITIES); + verifyNetworkTestedValidFromHttps(1 /* interactions */); + assertEquals(INITIAL_REEVALUATE_DELAY_MS, wnm.getReevaluationDelayMs()); + } + + @Test + public void testTcpSocketTracker_setCapabilities() throws Exception { + setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_TCP); + final InOrder inOrder = inOrder(mTst); + final WrappedNetworkMonitor wnm = prepareValidatedStateNetworkMonitor( + CELL_METERED_CAPABILITIES); + inOrder.verify(mTst).setNetworkCapabilities(eq(CELL_METERED_CAPABILITIES)); + + // Suspend the network. Verify the capabilities would be passed to TcpSocketTracker. + setNetworkCapabilities(wnm, CELL_SUSPENDED_METERED_CAPABILITIES); + inOrder.verify(mTst).setNetworkCapabilities(eq(CELL_SUSPENDED_METERED_CAPABILITIES)); + } + + @Test public void testDataStall_setOpportunisticMode() { setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_TCP); WrappedNetworkMonitor wnm = makeCellNotMeteredNetworkMonitor(); @@ -2328,7 +2396,7 @@ private void testDataStall_StallDnsSuspectedAndSendMetrics(int transport, NetworkCapabilities nc) throws Exception { // NM suspects data stall from DNS signal and sends data stall metrics. - final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall(nc); + final WrappedNetworkMonitor nm = prepareValidatedStateNetworkMonitor(nc); makeDnsTimeoutEvent(nm, 5); // Trigger a dns signal to start evaluate data stall and upload metrics. nm.notifyDnsResponse(RETURN_CODE_DNS_TIMEOUT); @@ -2338,7 +2406,7 @@ @Test public void testDataStall_NoStallSuspectedAndSendMetrics() throws Exception { - final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall( + final WrappedNetworkMonitor nm = prepareValidatedStateNetworkMonitor( CELL_METERED_CAPABILITIES); // Setup no data stall dns signal. makeDnsTimeoutEvent(nm, 3); @@ -2360,12 +2428,11 @@ private void testDataStall_StallTcpSuspectedAndSendMetrics(NetworkCapabilities nc) throws Exception { - assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)); setupTcpDataStall(); - setTcpPollingInterval(0); + setTcpPollingInterval(1); // NM suspects data stall from TCP signal and sends data stall metrics. setDataStallEvaluationType(DATA_STALL_EVALUATION_TYPE_TCP); - final WrappedNetworkMonitor nm = prepareNetworkMonitorForVerifyDataStall(nc); + final WrappedNetworkMonitor nm = prepareValidatedStateNetworkMonitor(nc); // Trigger a tcp event immediately. nm.sendTcpPollingEvent(); // Allow only one transport type in the context of this test for simplification. @@ -2374,7 +2441,7 @@ verifySendDataStallDetectionStats(nm, DATA_STALL_EVALUATION_TYPE_TCP, transports[0]); } - private WrappedNetworkMonitor prepareNetworkMonitorForVerifyDataStall(NetworkCapabilities nc) + private WrappedNetworkMonitor prepareValidatedStateNetworkMonitor(NetworkCapabilities nc) throws Exception { // Connect a VALID network to simulate the data stall detection because data stall // evaluation will only start from validated state. @@ -2411,7 +2478,9 @@ ArgumentCaptor.forClass(CaptivePortalProbeResult.class); final ArgumentCaptor<DataStallDetectionStats> statsCaptor = ArgumentCaptor.forClass(DataStallDetectionStats.class); - verify(mDependencies, timeout(HANDLER_TIMEOUT_MS).times(1)) + // TCP data stall detection may be triggered more than once because NM stays in the + // ValidatedState and polling timer is set to 0. + verify(mDependencies, timeout(HANDLER_TIMEOUT_MS).atLeast(1)) .writeDataStallDetectionStats(statsCaptor.capture(), probeResultCaptor.capture()); // Ensure probe will not stop due to rate-limiting mechanism. nm.setLastProbeTime(SystemClock.elapsedRealtime() - STALL_EXPECTED_LAST_PROBE_TIME_MS); @@ -2566,26 +2635,22 @@ @Test public void testCollectDataStallMetrics_TcpWithCellular() { - assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)); testDataStallMetricsWithCellular(DATA_STALL_EVALUATION_TYPE_TCP); } @Test public void testCollectDataStallMetrics_TcpWithWiFi() { - assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)); testDataStallMetricsWithWiFi(DATA_STALL_EVALUATION_TYPE_TCP); } @Test public void testCollectDataStallMetrics_TcpAndDnsWithWifi() { - assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)); testDataStallMetricsWithWiFi( DATA_STALL_EVALUATION_TYPE_TCP | DATA_STALL_EVALUATION_TYPE_DNS); } @Test public void testCollectDataStallMetrics_TcpAndDnsWithCellular() { - assumeTrue(ShimUtils.isReleaseOrDevelopmentApiAbove(Build.VERSION_CODES.Q)); testDataStallMetricsWithCellular( DATA_STALL_EVALUATION_TYPE_TCP | DATA_STALL_EVALUATION_TYPE_DNS); } @@ -2672,19 +2737,17 @@ @Test public void testNotifyNetwork_WithforceReevaluation() throws Exception { + // Set validated result for both HTTP and HTTPS probes. setValidProbes(); final NetworkMonitor nm = runValidatedNetworkTest(); // Verify forceReevaluation will not reset the validation result but only probe result until // getting the validation result. setSslException(mHttpsConnection); - setStatus(mHttpConnection, 500); - setStatus(mFallbackConnection, 204); nm.forceReevaluation(Process.myUid()); // Expect to send HTTP, HTTPs, FALLBACK and evaluation results. - verifyNetworkTested(VALIDATION_RESULT_INVALID, - NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_FALLBACK, + verifyNetworkTested(NETWORK_VALIDATION_RESULT_PARTIAL, + NETWORK_VALIDATION_PROBE_DNS | NETWORK_VALIDATION_PROBE_HTTP, 1 /* interactions */); - HandlerUtils.waitForIdle(nm.getHandler(), HANDLER_TIMEOUT_MS); } @Test