diff --git a/compatibility_matrices/compatibility_matrix.current.xml b/compatibility_matrices/compatibility_matrix.current.xml index 886f7f5b39..e30dad749a 100644 --- a/compatibility_matrices/compatibility_matrix.current.xml +++ b/compatibility_matrices/compatibility_matrix.current.xml @@ -686,6 +686,14 @@ default + + android.hardware.tetheroffload + 1 + + IOffload + default + + android.hardware.thermal 2.0 diff --git a/tetheroffload/aidl/Android.bp b/tetheroffload/aidl/Android.bp new file mode 100644 index 0000000000..1d8058685c --- /dev/null +++ b/tetheroffload/aidl/Android.bp @@ -0,0 +1,27 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +aidl_interface { + name: "android.hardware.tetheroffload", + vendor_available: true, + srcs: ["android/hardware/tetheroffload/*.aidl"], + stability: "vintf", + backend: { + cpp: { + enabled: false, + }, + java: { + sdk_version: "module_current", + apex_available: [ + "com.android.tethering", + ], + min_sdk_version: "30", + enabled: true, + }, + ndk: { + apps_enabled: false, + }, + }, + frozen: false, +} diff --git a/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ForwardedStats.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ForwardedStats.aidl new file mode 100644 index 0000000000..493a69868f --- /dev/null +++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ForwardedStats.aidl @@ -0,0 +1,39 @@ +/* + * 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 -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.hardware.tetheroffload; +@VintfStability +parcelable ForwardedStats { + long rxBytes; + long txBytes; +} diff --git a/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IOffload.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IOffload.aidl new file mode 100644 index 0000000000..9a58b1fbba --- /dev/null +++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IOffload.aidl @@ -0,0 +1,46 @@ +/* + * 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 -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.hardware.tetheroffload; +@VintfStability +interface IOffload { + void initOffload(in ParcelFileDescriptor fd1, in ParcelFileDescriptor fd2, in android.hardware.tetheroffload.ITetheringOffloadCallback cb); + void stopOffload(); + void setLocalPrefixes(in String[] prefixes); + android.hardware.tetheroffload.ForwardedStats getForwardedStats(in String upstream); + void setDataWarningAndLimit(in String upstream, in long warningBytes, in long limitBytes); + void setUpstreamParameters(in String iface, in String v4Addr, in String v4Gw, in String[] v6Gws); + void addDownstream(in String iface, in String prefix); + void removeDownstream(in String iface, in String prefix); + const int ERROR_CODE_UNUSED = 0; +} diff --git a/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IPv4AddrPortPair.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IPv4AddrPortPair.aidl new file mode 100644 index 0000000000..2b42f0c056 --- /dev/null +++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/IPv4AddrPortPair.aidl @@ -0,0 +1,39 @@ +/* + * 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 -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.hardware.tetheroffload; +@VintfStability +parcelable IPv4AddrPortPair { + String addr; + int port; +} diff --git a/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl new file mode 100644 index 0000000000..4eb7d04d00 --- /dev/null +++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl @@ -0,0 +1,39 @@ +/* + * 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 -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.hardware.tetheroffload; +@VintfStability +interface ITetheringOffloadCallback { + oneway void onEvent(in android.hardware.tetheroffload.OffloadCallbackEvent event); + oneway void updateTimeout(in android.hardware.tetheroffload.NatTimeoutUpdate params); +} diff --git a/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NatTimeoutUpdate.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NatTimeoutUpdate.aidl new file mode 100644 index 0000000000..9eddaa205f --- /dev/null +++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NatTimeoutUpdate.aidl @@ -0,0 +1,40 @@ +/* + * 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 -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.hardware.tetheroffload; +@VintfStability +parcelable NatTimeoutUpdate { + android.hardware.tetheroffload.IPv4AddrPortPair src; + android.hardware.tetheroffload.IPv4AddrPortPair dst; + android.hardware.tetheroffload.NetworkProtocol proto; +} diff --git a/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NetworkProtocol.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NetworkProtocol.aidl new file mode 100644 index 0000000000..52bd2a6c50 --- /dev/null +++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/NetworkProtocol.aidl @@ -0,0 +1,39 @@ +/* + * 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 -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.hardware.tetheroffload; +@Backing(type="int") @VintfStability +enum NetworkProtocol { + TCP = 6, + UDP = 17, +} diff --git a/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/OffloadCallbackEvent.aidl b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/OffloadCallbackEvent.aidl new file mode 100644 index 0000000000..026e18ed1d --- /dev/null +++ b/tetheroffload/aidl/aidl_api/android.hardware.tetheroffload/current/android/hardware/tetheroffload/OffloadCallbackEvent.aidl @@ -0,0 +1,43 @@ +/* + * 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 -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.hardware.tetheroffload; +@Backing(type="int") @VintfStability +enum OffloadCallbackEvent { + OFFLOAD_STARTED = 1, + OFFLOAD_STOPPED_ERROR = 2, + OFFLOAD_STOPPED_UNSUPPORTED = 3, + OFFLOAD_SUPPORT_AVAILABLE = 4, + OFFLOAD_STOPPED_LIMIT_REACHED = 5, + OFFLOAD_WARNING_REACHED = 6, +} diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/ForwardedStats.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/ForwardedStats.aidl new file mode 100644 index 0000000000..d2fe49b494 --- /dev/null +++ b/tetheroffload/aidl/android/hardware/tetheroffload/ForwardedStats.aidl @@ -0,0 +1,26 @@ +/* + * 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. + */ + +package android.hardware.tetheroffload; + +@VintfStability +parcelable ForwardedStats { + /** + * Tx/Rx forwarded bytes + */ + long rxBytes; + long txBytes; +} diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/IOffload.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/IOffload.aidl new file mode 100644 index 0000000000..30b2c8d8aa --- /dev/null +++ b/tetheroffload/aidl/android/hardware/tetheroffload/IOffload.aidl @@ -0,0 +1,284 @@ +/* + * 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. + */ + +package android.hardware.tetheroffload; + +import android.hardware.tetheroffload.ForwardedStats; +import android.hardware.tetheroffload.ITetheringOffloadCallback; + +/** + * Interface used to control tethering offload. + */ +@VintfStability +interface IOffload { + /** + * Error code for all {@code ServiceSpecificException}s thrown by this interface. + */ + const int ERROR_CODE_UNUSED = 0; + + /** + * Indicates intent to start offload for tethering in immediate future. + * + * This API must be called exactly once the first time that Tethering is requested by + * the user. + * + * If this API is called multiple times without first calling stopOffload, then the subsequent + * calls must fail without changing the state of the server. + * + * If for some reason, the hardware is currently unable to support offload, this call must fail. + * + * @param fd1 A file descriptor bound to the following netlink groups + * (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY). + * @param fd2 A file descriptor bound to the following netlink groups + * (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY). + * @param cb Assuming success, this callback must provide unsolicited updates of offload status. + * It is assumed to be valid until stopOffload is called. + * + * @throws: + * - EX_ILLEGAL_ARGUMENT if any file descriptors are invalid. + * - EX_ILLEGAL_STATE if this method previously succeeded and stopOffload() was not + * later called. + * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the + * error. + * + * Remarks: Initializing offload does not imply that any upstreams or downstreams have yet been, + * or even will be, chosen. This API is symmetrical with stopOffload. + */ + void initOffload(in ParcelFileDescriptor fd1, in ParcelFileDescriptor fd2, + in ITetheringOffloadCallback cb); + + /** + * Indicate desire to tear down all tethering offload. + * + * Called after tethering is no longer requested by the user. Any remaining offload must + * be subsequently torn down by the management process. Upon success, the callback registered + * in initOffload must be released, and offload must be stopped. + * + * @throws: + * - EX_ILLEGAL_STATE if initOffload() was not called, or if stopOffload() was already + * called. + * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the + * error. + * + * Remarks: Statistics must be reset by this API. + */ + void stopOffload(); + + /** + * Instruct management process not to forward traffic destined to or from the specified + * prefixes. + * + * This API may only be called after initOffload and before stopOffload. + * + * @param prefixes List containing fully specified prefixes. For e.g. 192.168.1.0/24 + * or 2001:4860:684::/64 + * + * @throws: + * - EX_ILLEGAL_ARGUMENT if the IP prefixes are invalid. + * - EX_ILLEGAL_STATE if this method is called before initOffload(), or if this method + * is called after stopOffload(). + * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the + * error. + * + * Remarks: This list overrides any previously specified list + */ + void setLocalPrefixes(in String[] prefixes); + + /** + * Query offloaded traffic statistics forwarded to an upstream address. + * + * Return statistics that have transpired since the last query. This would include + * statistics from all offloaded downstream tether interfaces that have been forwarded to this + * upstream interface. After returning the statistics, the counters are reset to zero. + * + * Only offloaded statistics must be returned by this API, software stats must not be + * returned. + * + * @param upstream Upstream interface on which traffic exited/entered + * + * @return ForwardedStats depicting the received and transmitted bytes + * + * @throws: + * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the + * error. + */ + ForwardedStats getForwardedStats(in String upstream); + + /** + * Instruct hardware to send callbacks, and possibly stop offload, after certain number of bytes + * have been transferred in either direction on this upstream interface. + * + * The specified quota bytes must be applied to all traffic on the given upstream interface. + * This includes hardware forwarded traffic, software forwarded traffic, and AP-originated + * traffic. IPv4 and IPv6 traffic both count towards the same quota. IP headers are included + * in the byte count quota, but, link-layer headers are not. + * + * This API may only be called while offload is occurring on this upstream. The hardware + * management process MUST NOT store the values when offload is not started and apply them + * once offload is started. This is because the quota values would likely become stale over + * time and would not reflect any new traffic that has occurred. + * + * The specified quota bytes MUST replace any previous quotas set by + * {@code setDataWarningAndLimit} specified on the same interface. It may be interpreted as + * "tell me when either or bytes have been transferred + * (in either direction), and stop offload when bytes have been transferred, + * starting now and counting from zero on ." + * + * Once the {@code warningBytes} is reached, the callback registered in initOffload must be + * called with {@code OFFLOAD_WARNING_REACHED} to indicate this event. Once the event fires + * for this upstream, no further {@code OFFLOAD_WARNING_REACHED} event will be fired for this + * upstream unless this method is called again with the same interface. Note that there is + * no need to call initOffload again to resume offload if stopOffload was not called by the + * client. + * + * Similarly, Once the {@code limitBytes} is reached, the callback registered in initOffload + * must be called with {@code OFFLOAD_STOPPED_LIMIT_REACHED} to indicate this event. Once + * the event fires for this upstream, no further {@code OFFLOAD_STOPPED_LIMIT_REACHED} + * event will be fired for this upstream unless this method is called again with the same + * interface. However, unlike {@code warningBytes}, when {@code limitBytes} is reached, + * all offload must be stopped. If offload is desired again, the hardware management + * process must be completely reprogrammed by calling setUpstreamParameters and + * addDownstream again. + * + * Note that {@code warningBytes} must always be less than or equal to {@code limitBytes}, + * when {@code warningBytes} is reached, {@code limitBytes} may still valid unless this method + * is called again with the same interface. + * + * @param upstream Upstream interface name that quota must apply to. + * @param warningBytes The quota of warning, defined as the number of bytes, starting from + * zero and counting from now. + * @param limitBytes The quota of limit, defined as the number of bytes, starting from zero + * and counting from now. + * + * @throws: + * - EX_ILLEGAL_ARGUMENT if any parameters are invalid (such as invalid upstream + * or negative number of bytes). + * - EX_ILLEGAL_STATE if this method is called before initOffload(), or if this method + * is called after stopOffload(). + * - EX_UNSUPPORTED_OPERATION if it is not supported. + * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the + * error. + */ + void setDataWarningAndLimit(in String upstream, in long warningBytes, in long limitBytes); + + /** + * Instruct hardware to start forwarding traffic to the specified upstream. + * + * When iface, v4Addr, and v4Gw are all non-null, the management process may begin forwarding + * any currently configured or future configured IPv4 downstreams to this upstream interface. + * + * If any of the previously three mentioned parameters are null, then any current IPv4 offload + * must be stopped. + * + * When iface and v6Gws are both non-null, and in the case of v6Gws, are not empty, the + * management process may begin forwarding any currently configured or future configured IPv6 + * downstreams to this upstream interface. + * + * If either of the two above parameters are null, or no V6 Gateways are provided, then IPv6 + * offload must be stopped. + * + * This API may only be called after initOffload and before stopOffload. + * + * @param iface Upstream interface name. Note that only one is needed because IPv4 and IPv6 + * interfaces cannot be different (only known that this can occur during software + * xlat, which cannot be offloaded through hardware anyways). If the iface is + * null, offload must be stopped. + * @param v4Addr The local IPv4 address assigned to the provided upstream interface, i.e. the + * IPv4 address the packets are NATed to. For e.g. 192.168.0.12. + * @param v4Gw The IPv4 address of the IPv4 gateway on the upstream interface. + * For e.g. 192.168.1.1 + * @param v6Gws A list of IPv6 addresses (for e.g. fe80::97be:9de7:b24b:9194) for possible IPv6 + * gateways on the upstream interface. + * + * @throws: + * - EX_ILLEGAL_ARGUMENT if any parameters are invalid (such as invalid upstream + * or IP addresses). + * - EX_ILLEGAL_STATE if this method is called before initOffload(), or if this method + * is called after stopOffload(). + * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the + * error. + * + * Remarks: This overrides any previously configured parameters. + */ + void setUpstreamParameters( + in String iface, in String v4Addr, in String v4Gw, in String[] v6Gws); + + /** + * Configure a downstream interface and prefix in the hardware management process that may be + * forwarded. + * + * The prefix may be an IPv4 or an IPv6 prefix to signify which family can be offloaded from + * the specified tether interface. The list of IPv4 and IPv6 downstreams that are configured + * may differ. + * + * If the given protocol, as determined by the prefix, has an upstream set, + * the hardware may begin forwarding traffic between the upstream and any devices on the + * downstream interface that have IP addresses within the specified prefix. Other traffic from + * the same downstream interfaces is unaffected and must be forwarded if and only if it was + * already being forwarded. + * + * If no upstream is currently configured, then these downstream interface and prefixes must be + * preserved so that offload may begin in the future when an upstream is set. + * + * This API does not replace any previously configured downstreams and any such downstreams must + * be explicitly removed by calling removeDownstream. + * + * This API may only be called after initOffload and before stopOffload. + * + * @param iface Downstream interface + * @param prefix Downstream prefix depicting addresses that may be offloaded. + * For e.g. 192.168.1.0/24 or 2001:4860:684::/64) + * + * @throws: + * - EX_ILLEGAL_ARGUMENT if any parameters are invalid (such as invalid downstream + * or IP prefix). + * - EX_ILLEGAL_STATE if this method is called before initOffload(), or if this method + * is called after stopOffload(). + * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the + * error. + * + * Remarks: The hardware management process may fail this call in a normal situation. This can + * happen because the hardware cannot support the current number of prefixes, the + * hardware cannot support concurrent offload on multiple interfaces, the hardware + * cannot currently support offload on the tether interface for some reason, or any + * other dynamic configuration issues which may occur. In this case, + * traffic must remain unaffected and must be forwarded if and only if it was already + * being forwarded. + */ + void addDownstream(in String iface, in String prefix); + + /** + * Remove a downstream prefix that may be forwarded from the hardware management process. + * + * The prefix may be an IPv4 or an IPv6 prefix. If it was not previously configured using + * addDownstream, then this must be a no-op. + * + * This API may only be called after initOffload and before stopOffload. + * + * @param iface Downstream interface + * @param prefix Downstream prefix depicting address that must no longer be offloaded + * For e.g. 192.168.1.0/24 or 2001:4860:684::/64) + * + * @throws: + * - EX_ILLEGAL_ARGUMENT if any parameters are invalid (such as invalid downstream + * or IP prefix). + * - EX_ILLEGAL_STATE if this method is called before initOffload(), or if this method + * is called after stopOffload(). + * - EX_SERVICE_SPECIFIC with the error message set to a human-readable reason for the + * error. + */ + void removeDownstream(in String iface, in String prefix); +} diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/IPv4AddrPortPair.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/IPv4AddrPortPair.aidl new file mode 100644 index 0000000000..16d7d85b80 --- /dev/null +++ b/tetheroffload/aidl/android/hardware/tetheroffload/IPv4AddrPortPair.aidl @@ -0,0 +1,26 @@ +/* + * 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. + */ + +package android.hardware.tetheroffload; + +@VintfStability +parcelable IPv4AddrPortPair { + /** + * IPv4 Address and Port + */ + String addr; + int port; +} diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl new file mode 100644 index 0000000000..5ee819b7cd --- /dev/null +++ b/tetheroffload/aidl/android/hardware/tetheroffload/ITetheringOffloadCallback.aidl @@ -0,0 +1,42 @@ +/* + * 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. + */ + +package android.hardware.tetheroffload; + +import android.hardware.tetheroffload.NatTimeoutUpdate; +import android.hardware.tetheroffload.OffloadCallbackEvent; + +/** + * Callback providing information about status of hardware management process + * as well as providing a way to keep offloaded connections from timing out. + */ +@VintfStability +oneway interface ITetheringOffloadCallback { + /** + * Called when an asynchronous event is generated by the hardware + * management process. + */ + void onEvent(in OffloadCallbackEvent event); + + /** + * Provide a way for the management process to request that a connections + * timeout be updated in kernel. + * + * This is necessary to ensure that offloaded traffic is not cleaned up + * by the kernel connection tracking module for IPv4. + */ + void updateTimeout(in NatTimeoutUpdate params); +} diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/NatTimeoutUpdate.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/NatTimeoutUpdate.aidl new file mode 100644 index 0000000000..50805ef25e --- /dev/null +++ b/tetheroffload/aidl/android/hardware/tetheroffload/NatTimeoutUpdate.aidl @@ -0,0 +1,27 @@ +/* + * 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. + */ + +package android.hardware.tetheroffload; + +import android.hardware.tetheroffload.IPv4AddrPortPair; +import android.hardware.tetheroffload.NetworkProtocol; + +@VintfStability +parcelable NatTimeoutUpdate { + IPv4AddrPortPair src; + IPv4AddrPortPair dst; + NetworkProtocol proto; +} diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/NetworkProtocol.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/NetworkProtocol.aidl new file mode 100644 index 0000000000..cc4f7acbc0 --- /dev/null +++ b/tetheroffload/aidl/android/hardware/tetheroffload/NetworkProtocol.aidl @@ -0,0 +1,24 @@ +/* + * 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. + */ + +package android.hardware.tetheroffload; + +@VintfStability +@Backing(type="int") +enum NetworkProtocol { + TCP = 6, + UDP = 17, +} diff --git a/tetheroffload/aidl/android/hardware/tetheroffload/OffloadCallbackEvent.aidl b/tetheroffload/aidl/android/hardware/tetheroffload/OffloadCallbackEvent.aidl new file mode 100644 index 0000000000..a95f67495f --- /dev/null +++ b/tetheroffload/aidl/android/hardware/tetheroffload/OffloadCallbackEvent.aidl @@ -0,0 +1,64 @@ +/* + * 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. + */ + +package android.hardware.tetheroffload; + +@VintfStability +@Backing(type="int") +enum OffloadCallbackEvent { + /** + * Indicate that a working configuration has been programmed and the + * hardware management process has begun forwarding traffic. + */ + OFFLOAD_STARTED = 1, + /** + * Indicate that an error has occurred which has disrupted hardware + * acceleration. Software routing may still be attempted; however, + * statistics may be temporarily unavailable. Statistics may be recovered + * after OFFLOAD_SUPPORT_AVAILABLE event is fired. + */ + OFFLOAD_STOPPED_ERROR = 2, + /** + * Indicate that the device has moved to a RAT on which hardware + * acceleration is not supported. Subsequent calls to setUpstreamParameters + * and add/removeDownstream will likely fail and cannot be presumed to be + * saved inside of the hardware management process. Upon receiving + * OFFLOAD_SUPPORT_AVAILABLE, the client may reprogram the hardware + * management process to begin offload again. + */ + OFFLOAD_STOPPED_UNSUPPORTED = 3, + /** + * Indicate that the hardware management process is willing and able to + * provide support for hardware acceleration at this time. If applicable, + * the client may query for statistics. If offload is desired, the client + * must reprogram the hardware management process. + */ + OFFLOAD_SUPPORT_AVAILABLE = 4, + /** + * Hardware acceleration is no longer in effect and must be reprogrammed + * in order to resume. This event is fired when the limit, applied in + * setDataLimit, has expired. It is recommended that the client query for + * statistics immediately after receiving this event. + */ + OFFLOAD_STOPPED_LIMIT_REACHED = 5, + /** + * This event is fired when the quota, applied in setDataWarning, has expired. It is + * recommended that the client query for statistics immediately after receiving this event. + * Any offloaded traffic will continue to be offloaded until offload is stopped or + * OFFLOAD_STOPPED_LIMIT_REACHED is sent. + */ + OFFLOAD_WARNING_REACHED = 6, +} diff --git a/tetheroffload/aidl/default/Android.bp b/tetheroffload/aidl/default/Android.bp new file mode 100644 index 0000000000..8f0739c3d9 --- /dev/null +++ b/tetheroffload/aidl/default/Android.bp @@ -0,0 +1,36 @@ +// 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. + +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_binary { + name: "android.hardware.tetheroffload-service.example", + relative_install_path: "hw", + init_rc: ["tetheroffload-example.rc"], + vintf_fragments: ["tetheroffload-example.xml"], + vendor: true, + shared_libs: [ + "android.hardware.tetheroffload-V1-ndk", + "libbase", + "libbinder_ndk", + "libcutils", + "libutils", + ], + srcs: [ + "main.cpp", + "Offload.cpp", + ], +} diff --git a/tetheroffload/aidl/default/Offload.cpp b/tetheroffload/aidl/default/Offload.cpp new file mode 100644 index 0000000000..8aa6916fc0 --- /dev/null +++ b/tetheroffload/aidl/default/Offload.cpp @@ -0,0 +1,246 @@ +/* + * 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. + */ + +#include +#include + +#include +#include +#include + +#include "Offload.h" + +namespace aidl::android::hardware::tetheroffload::impl::example { + +using ::android::base::Join; + +ndk::ScopedAStatus Offload::addDownstream(const std::string& in_iface, + const std::string& in_prefix) { + LOG(VERBOSE) << __func__ << " Interface: " << in_iface << ", Prefix: " << in_prefix; + if (!isInitialized()) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage( + EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized"); + } + if (!isValidInterface(in_iface)) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid interface name"); + } + if (!isValidIpPrefix(in_prefix)) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid IP prefix"); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Offload::getForwardedStats(const std::string& in_upstream, + ForwardedStats* _aidl_return) { + LOG(VERBOSE) << __func__ << " Upstream: " << in_upstream; + ForwardedStats stats; + stats.rxBytes = 0; + stats.txBytes = 0; + *_aidl_return = std::move(stats); + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Offload::initOffload(const ndk::ScopedFileDescriptor& in_fd1, + const ndk::ScopedFileDescriptor& in_fd2, + const std::shared_ptr& in_cb) { + LOG(VERBOSE) << __func__ << " FileDescriptor1: " << std::to_string(in_fd1.get()) + << ", FileDescriptor2: " << std::to_string(in_fd2.get()) + << ", ITetheringOffloadCallback: " << in_cb; + if (isInitialized()) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage( + EX_ILLEGAL_STATE, "Tetheroffload HAL already initialized"); + } + int fd1 = in_fd1.get(); + int fd2 = in_fd2.get(); + if (fd1 < 0 || fd2 < 0) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid file descriptors"); + } + mFd1 = ndk::ScopedFileDescriptor(dup(fd1)); + mFd2 = ndk::ScopedFileDescriptor(dup(fd2)); + mInitialized = true; + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Offload::removeDownstream(const std::string& in_iface, + const std::string& in_prefix) { + LOG(VERBOSE) << __func__ << " Interface: " << in_iface << ", Prefix: " << in_prefix; + if (!isInitialized()) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage( + EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized"); + } + if (!isValidIpPrefix(in_prefix)) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid IP prefix"); + } + if (!isValidInterface(in_iface)) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid interface name"); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Offload::setDataWarningAndLimit(const std::string& in_upstream, + int64_t in_warningBytes, int64_t in_limitBytes) { + LOG(VERBOSE) << __func__ << " Upstream: " << in_upstream + << ", WarningBytes: " << in_warningBytes << ", LimitBytes: " << in_limitBytes; + if (!isInitialized()) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage( + EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized"); + } + if (!isValidInterface(in_upstream)) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid interface name"); + } + if (in_warningBytes < 0 || in_limitBytes < 0) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Threshold must be non-negative"); + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Offload::setLocalPrefixes(const std::vector& in_prefixes) { + LOG(VERBOSE) << __func__ << " Prefixes: " << Join(in_prefixes, ','); + if (!isInitialized()) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage( + EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized"); + } + if (in_prefixes.empty()) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "No IP prefix"); + } + for (std::string prefix : in_prefixes) { + if (!isValidIpPrefix(prefix)) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid IP prefix"); + } + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Offload::setUpstreamParameters(const std::string& in_iface, + const std::string& in_v4Addr, + const std::string& in_v4Gw, + const std::vector& in_v6Gws) { + LOG(VERBOSE) << __func__ << " Interface: " << in_iface << ", IPv4Address: " << in_v4Addr + << ", IPv4Gateway: " << in_v4Gw << ", IPv6Gateways: " << Join(in_v6Gws, ','); + if (!isInitialized()) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage( + EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized"); + } + if (!isValidInterface(in_iface)) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid interface name"); + } + if (in_v4Addr.empty() && in_v4Gw.empty() && in_v6Gws.empty()) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "No upstream IP address"); + } + if (!in_v4Addr.empty() && !in_v4Gw.empty()) { + if (!isValidIpv4Address(in_v4Addr) || !isValidIpv4Address(in_v4Gw)) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid IP address"); + } + } + for (std::string ip : in_v6Gws) { + if (!isValidIpv6Address(ip)) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage(EX_ILLEGAL_ARGUMENT, + "Invalid IP address"); + } + } + return ndk::ScopedAStatus::ok(); +} + +ndk::ScopedAStatus Offload::stopOffload() { + LOG(VERBOSE) << __func__; + if (!isInitialized()) { + return ndk::ScopedAStatus::fromExceptionCodeWithMessage( + EX_ILLEGAL_STATE, "Tetheroffload HAL not initialized"); + } + mInitialized = false; + return ndk::ScopedAStatus::ok(); +}; + +bool Offload::isInitialized() { + return (mInitialized == true); +} + +bool Offload::isValidInterface(const std::string& iface) { + return !iface.empty() && iface != "invalid"; +} + +bool Offload::isValidIpv4Address(const std::string& repr) { + return validateIpAddressOrPrefix(repr, AF_INET, false); +} + +bool Offload::isValidIpv4Prefix(const std::string& repr) { + return validateIpAddressOrPrefix(repr, AF_INET, true); +} + +bool Offload::isValidIpv6Address(const std::string& repr) { + return validateIpAddressOrPrefix(repr, AF_INET6, false); +} + +bool Offload::isValidIpv6Prefix(const std::string& repr) { + return validateIpAddressOrPrefix(repr, AF_INET6, true); +} + +bool Offload::isValidIpAddress(const std::string& repr) { + return isValidIpv4Address(repr) || isValidIpv6Address(repr); +} + +bool Offload::isValidIpPrefix(const std::string& repr) { + return isValidIpv4Prefix(repr) || isValidIpv6Prefix(repr); +} + +// Refer to libnetdutils's IPAddress and IPPrefix classes. +// Can't use them directly because libnetdutils is not "vendor_available". +bool Offload::validateIpAddressOrPrefix(const std::string& repr, const int expectedFamily, + const bool isPrefix) { + const addrinfo hints = { + .ai_flags = AI_NUMERICHOST | AI_NUMERICSERV, + }; + addrinfo* res; + size_t index = repr.find('/'); + if (isPrefix && index == std::string::npos) return false; + + // Parse the IP address. + const std::string ipAddress = isPrefix ? repr.substr(0, index) : repr; + const int ret = getaddrinfo(ipAddress.c_str(), nullptr, &hints, &res); + if (ret != 0) return false; + + // Check the address family. + int family = res[0].ai_family; + freeaddrinfo(res); + if (family != expectedFamily) return false; + if (!isPrefix) return true; + + // Parse the prefix length. + const char* prefixString = repr.c_str() + index + 1; + if (!isdigit(*prefixString)) return false; + char* endptr; + unsigned long prefixlen = strtoul(prefixString, &endptr, 10); + if (*endptr != '\0') return false; + + uint8_t maxlen = (family == AF_INET) ? 32 : 128; + if (prefixlen > maxlen) return false; + + return true; +} + +} // namespace aidl::android::hardware::tetheroffload::impl::example diff --git a/tetheroffload/aidl/default/Offload.h b/tetheroffload/aidl/default/Offload.h new file mode 100644 index 0000000000..a1f6bce51f --- /dev/null +++ b/tetheroffload/aidl/default/Offload.h @@ -0,0 +1,74 @@ +/* + * 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. + */ + +#pragma once + +#include + +namespace aidl { +namespace android { +namespace hardware { +namespace tetheroffload { +namespace impl { +namespace example { + +using aidl::android::hardware::tetheroffload::ForwardedStats; +using aidl::android::hardware::tetheroffload::ITetheringOffloadCallback; + +class Offload : public BnOffload { + public: + ndk::ScopedAStatus addDownstream(const std::string& in_iface, + const std::string& in_prefix) override; + ndk::ScopedAStatus getForwardedStats(const std::string& in_upstream, + ForwardedStats* _aidl_return) override; + ndk::ScopedAStatus initOffload( + const ndk::ScopedFileDescriptor& in_fd1, const ndk::ScopedFileDescriptor& in_fd2, + const std::shared_ptr& in_cb) override; + ndk::ScopedAStatus removeDownstream(const std::string& in_iface, + const std::string& in_prefix) override; + ndk::ScopedAStatus setDataWarningAndLimit(const std::string& in_upstream, + int64_t in_warningBytes, + int64_t in_limitBytes) override; + ndk::ScopedAStatus setLocalPrefixes(const std::vector& in_prefixes) override; + ndk::ScopedAStatus setUpstreamParameters(const std::string& in_iface, + const std::string& in_v4Addr, + const std::string& in_v4Gw, + const std::vector& in_v6Gws) override; + ndk::ScopedAStatus stopOffload() override; + + private: + bool isInitialized(); + bool isValidInterface(const std::string& iface); + bool isValidIpv4Address(const std::string& repr); + bool isValidIpv4Prefix(const std::string& repr); + bool isValidIpv6Address(const std::string& repr); + bool isValidIpv6Prefix(const std::string& repr); + bool isValidIpAddress(const std::string& repr); + bool isValidIpPrefix(const std::string& repr); + bool validateIpAddressOrPrefix(const std::string& repr, const int expectedFamily, + const bool isPrefix); + + bool mInitialized = false; + ndk::ScopedFileDescriptor mFd1; + ndk::ScopedFileDescriptor mFd2; +}; + +} // namespace example +} // namespace impl +} // namespace tetheroffload +} // namespace hardware +} // namespace android +} // namespace aidl diff --git a/tetheroffload/aidl/default/main.cpp b/tetheroffload/aidl/default/main.cpp new file mode 100644 index 0000000000..663363038f --- /dev/null +++ b/tetheroffload/aidl/default/main.cpp @@ -0,0 +1,35 @@ +/* + * 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. + */ + +#include "Offload.h" + +#include +#include +#include + +using aidl::android::hardware::tetheroffload::impl::example::Offload; + +int main() { + ABinderProcess_setThreadPoolMaxThreadCount(0); + std::shared_ptr offload = ndk::SharedRefBase::make(); + + binder_status_t status = AServiceManager_addService( + offload->asBinder().get(), Offload::makeServiceName("default").c_str()); + CHECK_EQ(status, STATUS_OK); + + ABinderProcess_joinThreadPool(); + return EXIT_FAILURE; // should not reach +} diff --git a/tetheroffload/aidl/default/tetheroffload-example.rc b/tetheroffload/aidl/default/tetheroffload-example.rc new file mode 100644 index 0000000000..46cda61d02 --- /dev/null +++ b/tetheroffload/aidl/default/tetheroffload-example.rc @@ -0,0 +1,4 @@ +service vendor.tetheroffload-example /vendor/bin/hw/android.hardware.tetheroffload-service.example + class hal + user nobody + group nobody diff --git a/tetheroffload/aidl/default/tetheroffload-example.xml b/tetheroffload/aidl/default/tetheroffload-example.xml new file mode 100644 index 0000000000..9fe83f6c6e --- /dev/null +++ b/tetheroffload/aidl/default/tetheroffload-example.xml @@ -0,0 +1,7 @@ + + + android.hardware.tetheroffload + 1 + IOffload/default + + diff --git a/tetheroffload/aidl/vts/functional/Android.bp b/tetheroffload/aidl/vts/functional/Android.bp new file mode 100644 index 0000000000..c9c184568d --- /dev/null +++ b/tetheroffload/aidl/vts/functional/Android.bp @@ -0,0 +1,24 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_test { + name: "VtsHalTetheroffloadTargetTest", + defaults: [ + "VtsHalTargetTestDefaults", + "use_libaidlvintf_gtest_helper_static", + ], + srcs: [ + "VtsHalTetheroffloadTargetTest.cpp", + ], + shared_libs: [ + "libbinder_ndk", + ], + static_libs: [ + "android.hardware.tetheroffload-V1-ndk", + "libgmock_ndk", + ], + test_suites: [ + "vts", + ], +} diff --git a/tetheroffload/aidl/vts/functional/VtsHalTetheroffloadTargetTest.cpp b/tetheroffload/aidl/vts/functional/VtsHalTetheroffloadTargetTest.cpp new file mode 100644 index 0000000000..fc8abbd2f6 --- /dev/null +++ b/tetheroffload/aidl/vts/functional/VtsHalTetheroffloadTargetTest.cpp @@ -0,0 +1,689 @@ +/* + * 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. + */ + +#define LOG_TAG "tetheroffload_aidl_hal_test" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace aidl::android::hardware::tetheroffload { + +namespace { + +using ::android::base::unique_fd; +using android::hardware::tetheroffload::ForwardedStats; +using android::hardware::tetheroffload::IOffload; +using android::hardware::tetheroffload::NatTimeoutUpdate; +using android::hardware::tetheroffload::OffloadCallbackEvent; +using ::testing::AnyOf; +using ::testing::Eq; + +const std::string TEST_IFACE = "rmnet_data0"; +const unsigned kFd1Groups = NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY; +const unsigned kFd2Groups = NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY; + +enum class ExpectBoolean { + Ignored = -1, + False = 0, + True = 1, +}; + +inline const sockaddr* asSockaddr(const sockaddr_nl* nladdr) { + return reinterpret_cast(nladdr); +} + +int netlinkSocket(int protocol, unsigned groups) { + unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, protocol)); + if (s.get() < 0) { + return -errno; + } + + const struct sockaddr_nl bind_addr = { + .nl_family = AF_NETLINK, + .nl_pad = 0, + .nl_pid = 0, + .nl_groups = groups, + }; + if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) { + return -errno; + } + + const struct sockaddr_nl kernel_addr = { + .nl_family = AF_NETLINK, + .nl_pad = 0, + .nl_pid = 0, + .nl_groups = groups, + }; + if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) { + return -errno; + } + + return s.release(); +} + +int netlinkSocket(unsigned groups) { + return netlinkSocket(NETLINK_NETFILTER, groups); +} + +// Check whether the specified interface is up. +bool interfaceIsUp(const std::string name) { + struct ifreq ifr = {}; + strlcpy(ifr.ifr_name, name.c_str(), sizeof(ifr.ifr_name)); + int sock = socket(AF_INET6, SOCK_DGRAM, 0); + if (sock == -1) return false; + int ret = ioctl(sock, SIOCGIFFLAGS, &ifr, sizeof(ifr)); + close(sock); + return (ret == 0) && (ifr.ifr_flags & IFF_UP); +} + +// Callback class for both events and NAT timeout updates. +class TetheringOffloadCallback : public BnTetheringOffloadCallback { + public: + ndk::ScopedAStatus onEvent(OffloadCallbackEvent in_event) override { + auto lock = std::lock_guard{mMutex}; + mOnEventInvoked = true; + mLastEvent = in_event; + mNotifyCv.notify_all(); + return ndk::ScopedAStatus::ok(); + } + + ndk::ScopedAStatus updateTimeout(const NatTimeoutUpdate& in_params) override { + auto lock = std::lock_guard{mMutex}; + mOnUpdateTimeoutInvoked = true; + mNatTimeout = in_params; + mNotifyCv.notify_all(); + return ndk::ScopedAStatus::ok(); + } + + private: + std::mutex mMutex; + std::condition_variable mNotifyCv; + OffloadCallbackEvent mLastEvent; + NatTimeoutUpdate mNatTimeout; + bool mOnEventInvoked = false; + bool mOnUpdateTimeoutInvoked = false; +}; + +// The common base class for tetheroffload AIDL HAL tests. +class TetheroffloadAidlTestBase : public testing::TestWithParam { + public: + virtual void SetUp() override { getService(); } + virtual void TearDown() override { + // For good measure, the teardown should try stopOffload() once more, since + // different HAL call test cycles might enter this function. Also the + // return code cannot be actually expected for all cases, hence ignore it. + stopOffload(ExpectBoolean::Ignored); + }; + + protected: + void getService() { + AIBinder* binder = AServiceManager_waitForService(GetParam().c_str()); + ASSERT_NE(binder, nullptr); + mOffload = IOffload::fromBinder(ndk::SpAIBinder(binder)); + } + + void initOffload(const bool expectedResult) { + unique_fd ufd1(netlinkSocket(kFd1Groups)); + if (ufd1.get() < 0) { + ALOGE("Unable to create conntrack sockets: %d/%s", errno, strerror(errno)); + FAIL(); + } + ndk::ScopedFileDescriptor fd1 = ndk::ScopedFileDescriptor(ufd1.release()); + + unique_fd ufd2(netlinkSocket(kFd2Groups)); + if (ufd2.get() < 0) { + ALOGE("Unable to create conntrack sockets: %d/%s", errno, strerror(errno)); + FAIL(); + } + ndk::ScopedFileDescriptor fd2 = ndk::ScopedFileDescriptor(ufd2.release()); + + mTetheringOffloadCallback = ndk::SharedRefBase::make(); + ASSERT_NE(mTetheringOffloadCallback, nullptr) << "Could not get offload callback"; + + ASSERT_EQ(mOffload->initOffload(fd1, fd2, mTetheringOffloadCallback).getExceptionCode(), + expectedResult ? EX_NONE : EX_ILLEGAL_STATE); + } + + void stopOffload(const ExpectBoolean expectedResult) { + ndk::ScopedAStatus status = mOffload->stopOffload(); + if (expectedResult == ExpectBoolean::Ignored) return; + ASSERT_EQ(status.getExceptionCode(), + expectedResult == ExpectBoolean::True ? EX_NONE : EX_ILLEGAL_STATE); + } + + std::shared_ptr mOffload; + std::shared_ptr mTetheringOffloadCallback; +}; + +// The test class for tetheroffload before initialization. +class TetheroffloadAidlPreInitTest : public TetheroffloadAidlTestBase { + public: + virtual void SetUp() override { getService(); } +}; + +// The main test class for tetheroffload AIDL HAL. +class TetheroffloadAidlGeneralTest : public TetheroffloadAidlTestBase { + public: + virtual void SetUp() override { + getService(); + initOffload(true); + } +}; + +// Passing invalid file descriptor to initOffload() should return an error. +// Check that this occurs when both FDs are empty. +TEST_P(TetheroffloadAidlPreInitTest, TestInitOffloadInvalidFdsReturnsError) { + ndk::ScopedFileDescriptor fd1 = ndk::ScopedFileDescriptor(-1); + ndk::ScopedFileDescriptor fd2 = ndk::ScopedFileDescriptor(-1); + mTetheringOffloadCallback = ndk::SharedRefBase::make(); + ASSERT_NE(mTetheringOffloadCallback, nullptr) << "Could not get offload callback"; + EXPECT_THAT(mOffload->initOffload(fd1, fd2, mTetheringOffloadCallback).getExceptionCode(), + AnyOf(Eq(EX_ILLEGAL_ARGUMENT), Eq(EX_TRANSACTION_FAILED))); +} + +// Passing invalid file descriptor to initOffload() should return an error. +// Check that this occurs when FD1 is empty. +TEST_P(TetheroffloadAidlPreInitTest, TestInitOffloadInvalidFd1ReturnsError) { + ndk::ScopedFileDescriptor fd1 = ndk::ScopedFileDescriptor(-1); + unique_fd ufd2(netlinkSocket(kFd2Groups)); + if (ufd2.get() < 0) { + ALOGE("Unable to create conntrack sockets: %d/%s", errno, strerror(errno)); + FAIL(); + } + ndk::ScopedFileDescriptor fd2 = ndk::ScopedFileDescriptor(ufd2.release()); + mTetheringOffloadCallback = ndk::SharedRefBase::make(); + ASSERT_NE(mTetheringOffloadCallback, nullptr) << "Could not get offload callback"; + EXPECT_THAT(mOffload->initOffload(fd1, fd2, mTetheringOffloadCallback).getExceptionCode(), + AnyOf(Eq(EX_ILLEGAL_ARGUMENT), Eq(EX_TRANSACTION_FAILED))); +} + +// Passing invalid file descriptor to initOffload() should return an error. +// Check that this occurs when FD2 is empty. +TEST_P(TetheroffloadAidlPreInitTest, TestInitOffloadInvalidFd2ReturnsError) { + unique_fd ufd1(netlinkSocket(kFd1Groups)); + if (ufd1.get() < 0) { + ALOGE("Unable to create conntrack sockets: %d/%s", errno, strerror(errno)); + FAIL(); + } + ndk::ScopedFileDescriptor fd1 = ndk::ScopedFileDescriptor(ufd1.release()); + ndk::ScopedFileDescriptor fd2 = ndk::ScopedFileDescriptor(-1); + mTetheringOffloadCallback = ndk::SharedRefBase::make(); + ASSERT_NE(mTetheringOffloadCallback, nullptr) << "Could not get offload callback"; + EXPECT_THAT(mOffload->initOffload(fd1, fd2, mTetheringOffloadCallback).getExceptionCode(), + AnyOf(Eq(EX_ILLEGAL_ARGUMENT), Eq(EX_TRANSACTION_FAILED))); +} + +// Call initOffload() multiple times. Check that non-first initOffload() calls return error. +TEST_P(TetheroffloadAidlPreInitTest, AdditionalInitsWithoutStopReturnError) { + initOffload(true); + initOffload(false); + initOffload(false); + initOffload(false); +} + +// Check that calling stopOffload() without first having called initOffload() returns error. +TEST_P(TetheroffloadAidlPreInitTest, MultipleStopsWithoutInitReturnError) { + stopOffload(ExpectBoolean::False); + stopOffload(ExpectBoolean::False); + stopOffload(ExpectBoolean::False); +} + +// Check that calling stopOffload() after a complete init/stop cycle returns error. +TEST_P(TetheroffloadAidlPreInitTest, AdditionalStopsWithInitReturnError) { + initOffload(true); + // Call setUpstreamParameters() so that "offload" can be reasonably said + // to be both requested and operational. + const std::string iface(TEST_IFACE); + const std::string v4Addr("192.0.0.2"); + const std::string v4Gw("192.0.0.1"); + const std::vector v6Gws{std::string("fe80::db8:1"), std::string("fe80::db8:2")}; + EXPECT_TRUE(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).isOk()); + if (!interfaceIsUp(TEST_IFACE)) { + return; + } + SCOPED_TRACE("Expecting stopOffload to succeed"); + stopOffload(ExpectBoolean::True); // balance out initOffload(true) + SCOPED_TRACE("Expecting stopOffload to fail the first time"); + stopOffload(ExpectBoolean::False); + SCOPED_TRACE("Expecting stopOffload to fail the second time"); + stopOffload(ExpectBoolean::False); +} + +// Check that calling setLocalPrefixes() without first having called initOffload() returns error. +TEST_P(TetheroffloadAidlPreInitTest, SetLocalPrefixesWithoutInitReturnsError) { + const std::vector prefixes{std::string("2001:db8::/64")}; + EXPECT_EQ(mOffload->setLocalPrefixes(prefixes).getExceptionCode(), EX_ILLEGAL_STATE); +} + +// Check that calling getForwardedStats() without first having called initOffload() +// returns zero bytes statistics. +TEST_P(TetheroffloadAidlPreInitTest, GetForwardedStatsWithoutInitReturnsZeroValues) { + const std::string upstream(TEST_IFACE); + ForwardedStats stats; + EXPECT_TRUE(mOffload->getForwardedStats(upstream, &stats).isOk()); + EXPECT_EQ(stats.rxBytes, 0ULL); + EXPECT_EQ(stats.txBytes, 0ULL); +} + +// Check that calling setDataWarningAndLimit() without first having called initOffload() returns +// error. +TEST_P(TetheroffloadAidlPreInitTest, SetDataWarningAndLimitWithoutInitReturnsError) { + const std::string upstream(TEST_IFACE); + const int64_t warning = 5000LL; + const int64_t limit = 5000LL; + EXPECT_EQ(mOffload->setDataWarningAndLimit(upstream, warning, limit).getExceptionCode(), + EX_ILLEGAL_STATE); +} + +// Check that calling setUpstreamParameters() without first having called initOffload() +// returns error. +TEST_P(TetheroffloadAidlPreInitTest, SetUpstreamParametersWithoutInitReturnsError) { + const std::string iface(TEST_IFACE); + const std::string v4Addr("192.0.2.0/24"); + const std::string v4Gw("192.0.2.1"); + const std::vector v6Gws{std::string("fe80::db8:1")}; + EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(), + EX_ILLEGAL_STATE); +} + +// Check that calling addDownstream() with an IPv4 prefix without first having called +// initOffload() returns error. +TEST_P(TetheroffloadAidlPreInitTest, AddIPv4DownstreamWithoutInitReturnsError) { + const std::string iface(TEST_IFACE); + const std::string prefix("192.0.2.0/24"); + EXPECT_EQ(mOffload->addDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_STATE); +} + +// Check that calling addDownstream() with an IPv6 prefix without first having called +// initOffload() returns error. +TEST_P(TetheroffloadAidlPreInitTest, AddIPv6DownstreamWithoutInitReturnsError) { + const std::string iface(TEST_IFACE); + const std::string prefix("2001:db8::/64"); + EXPECT_EQ(mOffload->addDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_STATE); +} + +// Check that calling removeDownstream() with an IPv4 prefix without first having called +// initOffload() returns error. +TEST_P(TetheroffloadAidlPreInitTest, RemoveIPv4DownstreamWithoutInitReturnsError) { + const std::string iface(TEST_IFACE); + const std::string prefix("192.0.2.0/24"); + EXPECT_EQ(mOffload->removeDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_STATE); +} + +// Check that calling removeDownstream() with an IPv6 prefix without first having called +// initOffload() returns error. +TEST_P(TetheroffloadAidlPreInitTest, RemoveIPv6DownstreamWithoutInitReturnsError) { + const std::string iface(TEST_IFACE); + const std::string prefix("2001:db8::/64"); + EXPECT_EQ(mOffload->removeDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_STATE); +} + +/* + * Tests for IOffload::setLocalPrefixes(). + */ + +// Test setLocalPrefixes() rejects an IPv4 address. +TEST_P(TetheroffloadAidlGeneralTest, SetLocalPrefixesIPv4AddressFails) { + const std::vector prefixes{std::string("192.0.2.1")}; + EXPECT_EQ(mOffload->setLocalPrefixes(prefixes).getExceptionCode(), EX_ILLEGAL_ARGUMENT); +} + +// Test setLocalPrefixes() rejects an IPv6 address. +TEST_P(TetheroffloadAidlGeneralTest, SetLocalPrefixesIPv6AddressFails) { + const std::vector prefixes{std::string("fe80::1")}; + EXPECT_EQ(mOffload->setLocalPrefixes(prefixes).getExceptionCode(), EX_ILLEGAL_ARGUMENT); +} + +// Test setLocalPrefixes() accepts both IPv4 and IPv6 prefixes. +TEST_P(TetheroffloadAidlGeneralTest, SetLocalPrefixesIPv4v6PrefixesOk) { + const std::vector prefixes{std::string("192.0.2.0/24"), std::string("fe80::/64")}; + EXPECT_TRUE(mOffload->setLocalPrefixes(prefixes).isOk()); +} + +// Test that setLocalPrefixes() fails given empty input. There is always +// a non-empty set of local prefixes; when all networking interfaces are down +// we still apply {127.0.0.0/8, ::1/128, fe80::/64} here. +TEST_P(TetheroffloadAidlGeneralTest, SetLocalPrefixesEmptyFails) { + const std::vector prefixes{}; + EXPECT_EQ(mOffload->setLocalPrefixes(prefixes).getExceptionCode(), EX_ILLEGAL_ARGUMENT); +} + +// Test setLocalPrefixes() fails on incorrectly formed input strings. +TEST_P(TetheroffloadAidlGeneralTest, SetLocalPrefixesInvalidFails) { + const std::vector prefixes{std::string("192.0.2.0/24"), std::string("invalid")}; + EXPECT_EQ(mOffload->setLocalPrefixes(prefixes).getExceptionCode(), EX_ILLEGAL_ARGUMENT); +} + +/* + * Tests for IOffload::getForwardedStats(). + */ + +// Test that getForwardedStats() for a non-existent upstream yields zero bytes statistics. +TEST_P(TetheroffloadAidlGeneralTest, GetForwardedStatsInvalidUpstreamIface) { + const std::string upstream("invalid"); + ForwardedStats stats; + EXPECT_TRUE(mOffload->getForwardedStats(upstream, &stats).isOk()); + EXPECT_EQ(stats.rxBytes, 0ULL); + EXPECT_EQ(stats.txBytes, 0ULL); +} + +// TEST_IFACE is presumed to exist on the device and be up. No packets +// are ever actually caused to be forwarded. +TEST_P(TetheroffloadAidlGeneralTest, GetForwardedStatsDummyIface) { + const std::string upstream(TEST_IFACE); + ForwardedStats stats; + EXPECT_TRUE(mOffload->getForwardedStats(upstream, &stats).isOk()); + EXPECT_EQ(stats.rxBytes, 0ULL); + EXPECT_EQ(stats.txBytes, 0ULL); +} + +/* + * Tests for IOffload::setDataWarningAndLimit(). + */ + +// Test that setDataWarningAndLimit() for an empty interface name fails. +TEST_P(TetheroffloadAidlGeneralTest, SetDataWarningAndLimitEmptyUpstreamIfaceFails) { + const std::string upstream(""); + const int64_t warning = 12345LL; + const int64_t limit = 67890LL; + EXPECT_THAT(mOffload->setDataWarningAndLimit(upstream, warning, limit).getExceptionCode(), + AnyOf(Eq(EX_ILLEGAL_ARGUMENT), Eq(EX_UNSUPPORTED_OPERATION))); +} + +// TEST_IFACE is presumed to exist on the device and be up. No packets +// are ever actually caused to be forwarded. +TEST_P(TetheroffloadAidlGeneralTest, SetDataWarningAndLimitNonZeroOk) { + const std::string upstream(TEST_IFACE); + const int64_t warning = 4000LL; + const int64_t limit = 5000LL; + EXPECT_THAT(mOffload->setDataWarningAndLimit(upstream, warning, limit).getExceptionCode(), + AnyOf(Eq(EX_NONE), Eq(EX_UNSUPPORTED_OPERATION))); +} + +// TEST_IFACE is presumed to exist on the device and be up. No packets +// are ever actually caused to be forwarded. +TEST_P(TetheroffloadAidlGeneralTest, SetDataWarningAndLimitZeroOk) { + const std::string upstream(TEST_IFACE); + const int64_t warning = 0LL; + const int64_t limit = 0LL; + EXPECT_THAT(mOffload->setDataWarningAndLimit(upstream, warning, limit).getExceptionCode(), + AnyOf(Eq(EX_NONE), Eq(EX_UNSUPPORTED_OPERATION))); +} + +// TEST_IFACE is presumed to exist on the device and be up. No packets +// are ever actually caused to be forwarded. +TEST_P(TetheroffloadAidlGeneralTest, SetDataWarningAndLimitUnlimitedWarningOk) { + const std::string upstream(TEST_IFACE); + const int64_t warning = LLONG_MAX; + const int64_t limit = 5000LL; + EXPECT_TRUE(mOffload->setDataWarningAndLimit(upstream, warning, limit).isOk()); +} + +// Test that setDataWarningAndLimit() with negative thresholds fails. +TEST_P(TetheroffloadAidlGeneralTest, SetDataWarningAndLimitNegativeFails) { + const std::string upstream(TEST_IFACE); + const int64_t warning = -1LL; + const int64_t limit = -1LL; + EXPECT_THAT(mOffload->setDataWarningAndLimit(upstream, warning, limit).getExceptionCode(), + AnyOf(Eq(EX_ILLEGAL_ARGUMENT), Eq(EX_UNSUPPORTED_OPERATION))); +} + +/* + * Tests for IOffload::setUpstreamParameters(). + */ + +// TEST_IFACE is presumed to exist on the device and be up. No packets +// are ever actually caused to be forwarded. +TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersIPv6OnlyOk) { + const std::string iface(TEST_IFACE); + const std::string v4Addr(""); + const std::string v4Gw(""); + const std::vector v6Gws{std::string("fe80::db8:1"), std::string("fe80::db8:2")}; + EXPECT_TRUE(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).isOk()); +} + +// TEST_IFACE is presumed to exist on the device and be up. No packets +// are ever actually caused to be forwarded. +TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersAlternateIPv6OnlyOk) { + const std::string iface(TEST_IFACE); + const std::string v4Addr(""); + const std::string v4Gw(""); + const std::vector v6Gws{std::string("fe80::db8:1"), std::string("fe80::db8:3")}; + EXPECT_TRUE(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).isOk()); +} + +// TEST_IFACE is presumed to exist on the device and be up. No packets +// are ever actually caused to be forwarded. +TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersIPv4OnlyOk) { + const std::string iface(TEST_IFACE); + const std::string v4Addr("192.0.2.2"); + const std::string v4Gw("192.0.2.1"); + const std::vector v6Gws{}; + EXPECT_TRUE(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).isOk()); +} + +// TEST_IFACE is presumed to exist on the device and be up. No packets +// are ever actually caused to be forwarded. +TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersIPv4v6Ok) { + const std::string iface(TEST_IFACE); + const std::string v4Addr("192.0.2.2"); + const std::string v4Gw("192.0.2.1"); + const std::vector v6Gws{std::string("fe80::db8:1"), std::string("fe80::db8:2")}; + EXPECT_TRUE(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).isOk()); +} + +// Test that setUpstreamParameters() fails when all parameters are empty. +TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersEmptyFails) { + const std::string iface(""); + const std::string v4Addr(""); + const std::string v4Gw(""); + const std::vector v6Gws{}; + EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(), + EX_ILLEGAL_ARGUMENT); +} + +// Test that setUpstreamParameters() fails when given empty or non-existent interface names. +TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersBogusIfaceFails) { + const std::string v4Addr("192.0.2.2"); + const std::string v4Gw("192.0.2.1"); + const std::vector v6Gws{std::string("fe80::db8:1")}; + for (const auto& bogus : {"", "invalid"}) { + SCOPED_TRACE(testing::Message() << "upstream: " << bogus); + const std::string iface(bogus); + EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(), + EX_ILLEGAL_ARGUMENT); + } +} + +// Test that setUpstreamParameters() fails when given unparseable IPv4 addresses. +TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersInvalidIPv4AddrFails) { + const std::string iface(TEST_IFACE); + const std::string v4Gw("192.0.2.1"); + const std::vector v6Gws{std::string("fe80::db8:1")}; + for (const auto& bogus : {"invalid", "192.0.2"}) { + SCOPED_TRACE(testing::Message() << "v4addr: " << bogus); + const std::string v4Addr(bogus); + EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(), + EX_ILLEGAL_ARGUMENT); + } +} + +// Test that setUpstreamParameters() fails when given unparseable IPv4 gateways. +TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersInvalidIPv4GatewayFails) { + const std::string iface(TEST_IFACE); + const std::string v4Addr("192.0.2.2"); + const std::vector v6Gws{std::string("fe80::db8:1")}; + for (const auto& bogus : {"invalid", "192.0.2"}) { + SCOPED_TRACE(testing::Message() << "v4gateway: " << bogus); + const std::string v4Gw(bogus); + EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(), + EX_ILLEGAL_ARGUMENT); + } +} + +// Test that setUpstreamParameters() fails when given unparseable IPv6 gateways. +TEST_P(TetheroffloadAidlGeneralTest, SetUpstreamParametersBadIPv6GatewaysFail) { + const std::string iface(TEST_IFACE); + const std::string v4Addr("192.0.2.2"); + const std::string v4Gw("192.0.2.1"); + for (const auto& bogus : {"", "invalid", "fe80::bogus", "192.0.2.66"}) { + SCOPED_TRACE(testing::Message() << "v6gateway: " << bogus); + const std::vector v6Gws{std::string("fe80::1"), std::string(bogus)}; + EXPECT_EQ(mOffload->setUpstreamParameters(iface, v4Addr, v4Gw, v6Gws).getExceptionCode(), + EX_ILLEGAL_ARGUMENT); + } +} + +/* + * Tests for IOffload::addDownstream(). + */ + +// Test addDownstream() works given an IPv4 prefix. +TEST_P(TetheroffloadAidlGeneralTest, AddDownstreamIPv4) { + const std::string iface("dummy0"); + const std::string prefix("192.0.2.0/24"); + EXPECT_TRUE(mOffload->addDownstream(iface, prefix).isOk()); +} + +// Test addDownstream() works given an IPv6 prefix. +TEST_P(TetheroffloadAidlGeneralTest, AddDownstreamIPv6) { + const std::string iface("dummy0"); + const std::string prefix("2001:db8::/64"); + EXPECT_TRUE(mOffload->addDownstream(iface, prefix).isOk()); +} + +// Test addDownstream() fails given all empty parameters. +TEST_P(TetheroffloadAidlGeneralTest, AddDownstreamEmptyFails) { + const std::string iface(""); + const std::string prefix(""); + EXPECT_EQ(mOffload->addDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_ARGUMENT); +} + +// Test addDownstream() fails given empty or non-existent interface names. +TEST_P(TetheroffloadAidlGeneralTest, AddDownstreamInvalidIfaceFails) { + const std::string prefix("192.0.2.0/24"); + for (const auto& bogus : {"", "invalid"}) { + SCOPED_TRACE(testing::Message() << "iface: " << bogus); + const std::string iface(bogus); + EXPECT_EQ(mOffload->addDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_ARGUMENT); + } +} + +// Test addDownstream() fails given unparseable prefix arguments. +TEST_P(TetheroffloadAidlGeneralTest, AddDownstreamBogusPrefixFails) { + const std::string iface("dummy0"); + for (const auto& bogus : {"", "192.0.2/24", "2001:db8/64"}) { + SCOPED_TRACE(testing::Message() << "prefix: " << bogus); + const std::string prefix(bogus); + EXPECT_EQ(mOffload->addDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_ARGUMENT); + } +} + +/* + * Tests for IOffload::removeDownstream(). + */ + +// Test removeDownstream() works given an IPv4 prefix. +TEST_P(TetheroffloadAidlGeneralTest, RemoveDownstreamIPv4) { + const std::string iface("dummy0"); + const std::string prefix("192.0.2.0/24"); + // First add the downstream, otherwise removeDownstream logic can reasonably + // return error for downstreams not previously added. + EXPECT_TRUE(mOffload->addDownstream(iface, prefix).isOk()); + EXPECT_TRUE(mOffload->removeDownstream(iface, prefix).isOk()); +} + +// Test removeDownstream() works given an IPv6 prefix. +TEST_P(TetheroffloadAidlGeneralTest, RemoveDownstreamIPv6) { + const std::string iface("dummy0"); + const std::string prefix("2001:db8::/64"); + // First add the downstream, otherwise removeDownstream logic can reasonably + // return error for downstreams not previously added. + EXPECT_TRUE(mOffload->addDownstream(iface, prefix).isOk()); + EXPECT_TRUE(mOffload->removeDownstream(iface, prefix).isOk()); +} + +// Test removeDownstream() fails given all empty parameters. +TEST_P(TetheroffloadAidlGeneralTest, RemoveDownstreamEmptyFails) { + const std::string iface(""); + const std::string prefix(""); + EXPECT_EQ(mOffload->removeDownstream(iface, prefix).getExceptionCode(), EX_ILLEGAL_ARGUMENT); +} + +// Test removeDownstream() fails given empty or non-existent interface names. +TEST_P(TetheroffloadAidlGeneralTest, RemoveDownstreamBogusIfaceFails) { + const std::string prefix("192.0.2.0/24"); + for (const auto& bogus : {"", "invalid"}) { + SCOPED_TRACE(testing::Message() << "iface: " << bogus); + const std::string iface(bogus); + EXPECT_EQ(mOffload->removeDownstream(iface, prefix).getExceptionCode(), + EX_ILLEGAL_ARGUMENT); + } +} + +// Test removeDownstream() fails given unparseable prefix arguments. +TEST_P(TetheroffloadAidlGeneralTest, RemoveDownstreamBogusPrefixFails) { + const std::string iface("dummy0"); + for (const auto& bogus : {"", "192.0.2/24", "2001:db8/64"}) { + SCOPED_TRACE(testing::Message() << "prefix: " << bogus); + const std::string prefix(bogus); + EXPECT_EQ(mOffload->removeDownstream(iface, prefix).getExceptionCode(), + EX_ILLEGAL_ARGUMENT); + } +} + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TetheroffloadAidlTestBase); +INSTANTIATE_TEST_SUITE_P( + IOffload, TetheroffloadAidlTestBase, + testing::ValuesIn(::android::getAidlHalInstanceNames(IOffload::descriptor)), + ::android::PrintInstanceNameToString); + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TetheroffloadAidlPreInitTest); +INSTANTIATE_TEST_SUITE_P( + IOffload, TetheroffloadAidlPreInitTest, + testing::ValuesIn(::android::getAidlHalInstanceNames(IOffload::descriptor)), + ::android::PrintInstanceNameToString); + +GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(TetheroffloadAidlGeneralTest); +INSTANTIATE_TEST_SUITE_P( + IOffload, TetheroffloadAidlGeneralTest, + testing::ValuesIn(::android::getAidlHalInstanceNames(IOffload::descriptor)), + ::android::PrintInstanceNameToString); + +} // namespace + +int main(int argc, char** argv) { + ::testing::InitGoogleTest(&argc, argv); + ABinderProcess_setThreadPoolMaxThreadCount(1); + ABinderProcess_startThreadPool(); + return RUN_ALL_TESTS(); +} + +} // namespace aidl::android::hardware::tetheroffload