diff --git a/Tethering/Android.bp b/Tethering/Android.bp index d2f6d154c7..8810a8c0ff 100644 --- a/Tethering/Android.bp +++ b/Tethering/Android.bp @@ -61,9 +61,13 @@ java_defaults { "modules-utils-build", "modules-utils-statemachine", "networkstack-client", + // AIDL tetheroffload implementation + "android.hardware.tetheroffload-V1-java", + // HIDL tetheroffload implementation "android.hardware.tetheroffload.config-V1.0-java", "android.hardware.tetheroffload.control-V1.0-java", "android.hardware.tetheroffload.control-V1.1-java", + "android.hidl.manager-V1.2-java", "net-utils-framework-common", "net-utils-device-common", "net-utils-device-common-bpf", diff --git a/Tethering/src/com/android/networkstack/tethering/IOffloadHal.java b/Tethering/src/com/android/networkstack/tethering/IOffloadHal.java new file mode 100644 index 0000000000..e66e7aee4b --- /dev/null +++ b/Tethering/src/com/android/networkstack/tethering/IOffloadHal.java @@ -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 com.android.networkstack.tethering; + +import android.annotation.NonNull; +import android.os.NativeHandle; + +import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; +import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback; + +import java.util.ArrayList; + +/** Abstraction of Tetheroffload HAL interface */ +interface IOffloadHal { + /* + * Initialize the Tetheroffload HAL. Offload management process need to know conntrack rules to + * support NAT, but it may not have permission to create netlink netfilter sockets. Create two + * netlink netfilter sockets and share them with offload management process. + */ + boolean initOffload(@NonNull NativeHandle handle1, @NonNull NativeHandle handle2, + @NonNull OffloadHalCallback callback); + + /** Stop the Tetheroffload HAL. */ + boolean stopOffload(); + + /** Get HAL interface version number. */ + int getVersion(); + + /** Get Tx/Rx usage from last query. */ + ForwardedStats getForwardedStats(@NonNull String upstream); + + /** Set local prefixes to offload management process. */ + boolean setLocalPrefixes(@NonNull ArrayList localPrefixes); + + /** Set data limit value to offload management process. */ + boolean setDataLimit(@NonNull String iface, long limit); + + /** Set data warning and limit value to offload management process. */ + boolean setDataWarningAndLimit(@NonNull String iface, long warning, long limit); + + /** Set upstream parameters to offload management process. */ + boolean setUpstreamParameters(@NonNull String iface, @NonNull String v4addr, + @NonNull String v4gateway, @NonNull ArrayList v6gws); + + /** Add downstream prefix to offload management process. */ + boolean addDownstream(@NonNull String ifname, @NonNull String prefix); + + /** Remove downstream prefix from offload management process. */ + boolean removeDownstream(@NonNull String ifname, @NonNull String prefix); +} diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadController.java b/Tethering/src/com/android/networkstack/tethering/OffloadController.java index d2f177d5e7..5fa6b2de6d 100644 --- a/Tethering/src/com/android/networkstack/tethering/OffloadController.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadController.java @@ -26,8 +26,8 @@ import static android.net.NetworkStats.UID_TETHERING; import static android.net.netstats.provider.NetworkStatsProvider.QUOTA_UNLIMITED; import static android.provider.Settings.Global.TETHER_OFFLOAD_DISABLED; -import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0; -import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1; import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE; import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; @@ -98,9 +98,8 @@ public class OffloadController { private final OffloadTetheringStatsProvider mStatsProvider; private final SharedLog mLog; private final HashMap mDownstreams; - private boolean mConfigInitialized; @OffloadHardwareInterface.OffloadHalVersion - private int mControlHalVersion; + private int mOffloadHalVersion; private LinkProperties mUpstreamLinkProperties; // The complete set of offload-exempt prefixes passed in via Tethering from // all upstream and downstream sources. @@ -205,20 +204,11 @@ public class OffloadController { return false; } - if (!mConfigInitialized) { - mConfigInitialized = mHwInterface.initOffloadConfig(); - if (!mConfigInitialized) { - mLog.i("tethering offload config not supported"); - stop(); - return false; - } - } - - mControlHalVersion = mHwInterface.initOffloadControl( + mOffloadHalVersion = mHwInterface.initOffload( // OffloadHardwareInterface guarantees that these callback // methods are called on the handler passed to it, which is the // same as mHandler, as coordinated by the setup in Tethering. - new OffloadHardwareInterface.ControlCallback() { + new OffloadHardwareInterface.OffloadHalCallback() { @Override public void onStarted() { if (!started()) return; @@ -305,11 +295,11 @@ public class OffloadController { final boolean isStarted = started(); if (!isStarted) { - mLog.i("tethering offload control not supported"); + mLog.i("tethering offload not supported"); stop(); } else { mLog.log("tethering offload started, version: " - + OffloadHardwareInterface.halVerToString(mControlHalVersion)); + + OffloadHardwareInterface.halVerToString(mOffloadHalVersion)); mNatUpdateCallbacksReceived = 0; mNatUpdateNetlinkErrors = 0; maybeSchedulePollingStats(); @@ -325,9 +315,8 @@ public class OffloadController { final boolean wasStarted = started(); updateStatsForCurrentUpstream(); mUpstreamLinkProperties = null; - mHwInterface.stopOffloadControl(); - mControlHalVersion = OFFLOAD_HAL_VERSION_NONE; - mConfigInitialized = false; + mHwInterface.stopOffload(); + mOffloadHalVersion = OFFLOAD_HAL_VERSION_NONE; if (mHandler.hasCallbacks(mScheduledPollingTask)) { mHandler.removeCallbacks(mScheduledPollingTask); } @@ -335,7 +324,7 @@ public class OffloadController { } private boolean started() { - return mConfigInitialized && mControlHalVersion != OFFLOAD_HAL_VERSION_NONE; + return mOffloadHalVersion != OFFLOAD_HAL_VERSION_NONE; } @VisibleForTesting @@ -528,7 +517,7 @@ public class OffloadController { } private boolean useStatsPolling() { - return mControlHalVersion == OFFLOAD_HAL_VERSION_1_0; + return mOffloadHalVersion == OFFLOAD_HAL_VERSION_HIDL_1_0; } private boolean maybeUpdateDataWarningAndLimit(String iface) { @@ -540,7 +529,7 @@ public class OffloadController { final InterfaceQuota quota = mInterfaceQuotas.getOrDefault(iface, InterfaceQuota.MAX_VALUE); final boolean ret; - if (mControlHalVersion >= OFFLOAD_HAL_VERSION_1_1) { + if (mOffloadHalVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) { ret = mHwInterface.setDataWarningAndLimit(iface, quota.warningBytes, quota.limitBytes); } else { ret = mHwInterface.setDataLimit(iface, quota.limitBytes); @@ -611,7 +600,7 @@ public class OffloadController { for (RouteInfo ri : oldRoutes) { if (shouldIgnoreDownstreamRoute(ri)) continue; if (!newRoutes.contains(ri)) { - mHwInterface.removeDownstreamPrefix(ifname, ri.getDestination().toString()); + mHwInterface.removeDownstream(ifname, ri.getDestination().toString()); } } @@ -619,7 +608,7 @@ public class OffloadController { for (RouteInfo ri : newRoutes) { if (shouldIgnoreDownstreamRoute(ri)) continue; if (!oldRoutes.contains(ri)) { - mHwInterface.addDownstreamPrefix(ifname, ri.getDestination().toString()); + mHwInterface.addDownstream(ifname, ri.getDestination().toString()); } } } @@ -639,7 +628,7 @@ public class OffloadController { for (RouteInfo route : lp.getRoutes()) { if (shouldIgnoreDownstreamRoute(route)) continue; - mHwInterface.removeDownstreamPrefix(ifname, route.getDestination().toString()); + mHwInterface.removeDownstream(ifname, route.getDestination().toString()); } } @@ -768,7 +757,7 @@ public class OffloadController { final boolean isStarted = started(); pw.println("Offload HALs " + (isStarted ? "started" : "not started")); pw.println("Offload Control HAL version: " - + OffloadHardwareInterface.halVerToString(mControlHalVersion)); + + OffloadHardwareInterface.halVerToString(mOffloadHalVersion)); LinkProperties lp = mUpstreamLinkProperties; String upstream = (lp != null) ? lp.getInterfaceName() : null; pw.println("Current upstream: " + upstream); diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHalAidlImpl.java b/Tethering/src/com/android/networkstack/tethering/OffloadHalAidlImpl.java new file mode 100644 index 0000000000..e7dc7575af --- /dev/null +++ b/Tethering/src/com/android/networkstack/tethering/OffloadHalAidlImpl.java @@ -0,0 +1,304 @@ +/* + * 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 com.android.networkstack.tethering; + +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_AIDL; + +import android.annotation.NonNull; +import android.hardware.tetheroffload.ForwardedStats; +import android.hardware.tetheroffload.IOffload; +import android.hardware.tetheroffload.ITetheringOffloadCallback; +import android.hardware.tetheroffload.NatTimeoutUpdate; +import android.hardware.tetheroffload.NetworkProtocol; +import android.hardware.tetheroffload.OffloadCallbackEvent; +import android.os.Handler; +import android.os.NativeHandle; +import android.os.ParcelFileDescriptor; +import android.os.ServiceManager; +import android.system.OsConstants; + +import com.android.modules.utils.build.SdkLevel; +import com.android.net.module.util.SharedLog; +import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback; + +import java.util.ArrayList; + +/** + * The implementation of IOffloadHal which based on Stable AIDL interface + */ +public class OffloadHalAidlImpl implements IOffloadHal { + private static final String TAG = OffloadHalAidlImpl.class.getSimpleName(); + private static final String HAL_INSTANCE_NAME = IOffload.DESCRIPTOR + "/default"; + + private final Handler mHandler; + private final SharedLog mLog; + private final IOffload mIOffload; + @OffloadHardwareInterface.OffloadHalVersion + private final int mOffloadVersion; + + private TetheringOffloadCallback mTetheringOffloadCallback; + + public OffloadHalAidlImpl(int version, @NonNull IOffload offload, @NonNull Handler handler, + @NonNull SharedLog log) { + mOffloadVersion = version; + mIOffload = offload; + mHandler = handler; + mLog = log.forSubComponent(TAG); + } + + /** + * Initialize the Tetheroffload HAL. Provides bound netlink file descriptors for use in the + * management process. + */ + public boolean initOffload(@NonNull NativeHandle handle1, @NonNull NativeHandle handle2, + @NonNull OffloadHalCallback callback) { + final String methodStr = String.format("initOffload(%d, %d, %s)", + handle1.getFileDescriptor().getInt$(), handle2.getFileDescriptor().getInt$(), + (callback == null) ? "null" + : "0x" + Integer.toHexString(System.identityHashCode(callback))); + mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, callback, mLog); + try { + mIOffload.initOffload( + ParcelFileDescriptor.adoptFd(handle1.getFileDescriptor().getInt$()), + ParcelFileDescriptor.adoptFd(handle2.getFileDescriptor().getInt$()), + mTetheringOffloadCallback); + } catch (Exception e) { + logAndIgnoreException(e, methodStr); + return false; + } + mLog.i(methodStr); + return true; + } + + /** Stop the Tetheroffload HAL. */ + public boolean stopOffload() { + final String methodStr = "stopOffload()"; + try { + mIOffload.stopOffload(); + } catch (Exception e) { + logAndIgnoreException(e, methodStr); + return false; + } + + mTetheringOffloadCallback = null; + mLog.i(methodStr); + return true; + } + + /** Get HAL interface version number. */ + public int getVersion() { + return mOffloadVersion; + } + + /** Get Tx/Rx usage from last query. */ + public OffloadHardwareInterface.ForwardedStats getForwardedStats(@NonNull String upstream) { + ForwardedStats stats = new ForwardedStats(); + final String methodStr = String.format("getForwardedStats(%s)", upstream); + try { + stats = mIOffload.getForwardedStats(upstream); + } catch (Exception e) { + logAndIgnoreException(e, methodStr); + } + mLog.i(methodStr); + return new OffloadHardwareInterface.ForwardedStats(stats.rxBytes, stats.txBytes); + } + + /** Set local prefixes to offload management process. */ + public boolean setLocalPrefixes(@NonNull ArrayList localPrefixes) { + final String methodStr = String.format("setLocalPrefixes([%s])", + String.join(",", localPrefixes)); + try { + mIOffload.setLocalPrefixes(localPrefixes.toArray(new String[localPrefixes.size()])); + } catch (Exception e) { + logAndIgnoreException(e, methodStr); + return false; + } + mLog.i(methodStr); + return true; + } + + /** + * Set data limit value to offload management process. + * Method setDataLimit is deprecated in AIDL, so call setDataWarningAndLimit instead, + * with warningBytes set to its MAX_VALUE. + */ + public boolean setDataLimit(@NonNull String iface, long limit) { + final long warning = Long.MAX_VALUE; + final String methodStr = String.format("setDataLimit(%s, %d)", iface, limit); + try { + mIOffload.setDataWarningAndLimit(iface, warning, limit); + } catch (Exception e) { + logAndIgnoreException(e, methodStr); + return false; + } + mLog.i(methodStr); + return true; + } + + /** Set data warning and limit value to offload management process. */ + public boolean setDataWarningAndLimit(@NonNull String iface, long warning, long limit) { + final String methodStr = + String.format("setDataWarningAndLimit(%s, %d, %d)", iface, warning, limit); + try { + mIOffload.setDataWarningAndLimit(iface, warning, limit); + } catch (Exception e) { + logAndIgnoreException(e, methodStr); + return false; + } + mLog.i(methodStr); + return true; + } + + /** Set upstream parameters to offload management process. */ + public boolean setUpstreamParameters(@NonNull String iface, @NonNull String v4addr, + @NonNull String v4gateway, @NonNull ArrayList v6gws) { + final String methodStr = String.format("setUpstreamParameters(%s, %s, %s, [%s])", + iface, v4addr, v4gateway, String.join(",", v6gws)); + try { + mIOffload.setUpstreamParameters(iface, v4addr, v4gateway, + v6gws.toArray(new String[v6gws.size()])); + } catch (Exception e) { + logAndIgnoreException(e, methodStr); + return false; + } + mLog.i(methodStr); + return true; + } + + /** Add downstream prefix to offload management process. */ + public boolean addDownstream(@NonNull String ifname, @NonNull String prefix) { + final String methodStr = String.format("addDownstream(%s, %s)", ifname, prefix); + try { + mIOffload.addDownstream(ifname, prefix); + } catch (Exception e) { + logAndIgnoreException(e, methodStr); + return false; + } + mLog.i(methodStr); + return true; + } + + /** Remove downstream prefix from offload management process. */ + public boolean removeDownstream(@NonNull String ifname, @NonNull String prefix) { + final String methodStr = String.format("removeDownstream(%s, %s)", ifname, prefix); + try { + mIOffload.removeDownstream(ifname, prefix); + } catch (Exception e) { + logAndIgnoreException(e, methodStr); + return false; + } + mLog.i(methodStr); + return true; + } + + /** + * Get {@link IOffloadHal} object from the AIDL service. + * + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @param log Log to be used by the repository. + */ + public static IOffloadHal getIOffloadHal(Handler handler, SharedLog log) { + // Tetheroffload AIDL interface is only supported after U. + if (!SdkLevel.isAtLeastU() || !ServiceManager.isDeclared(HAL_INSTANCE_NAME)) return null; + + IOffload offload = IOffload.Stub.asInterface( + ServiceManager.waitForDeclaredService(HAL_INSTANCE_NAME)); + if (offload == null) return null; + + return new OffloadHalAidlImpl(OFFLOAD_HAL_VERSION_AIDL, offload, handler, log); + } + + private void logAndIgnoreException(Exception e, final String methodStr) { + mLog.e(methodStr + " failed with " + e.getClass().getSimpleName() + ": ", e); + } + + private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub { + public final Handler handler; + public final OffloadHalCallback callback; + public final SharedLog log; + + TetheringOffloadCallback( + Handler h, OffloadHalCallback cb, SharedLog sharedLog) { + handler = h; + callback = cb; + log = sharedLog; + } + + private void handleOnEvent(int event) { + switch (event) { + case OffloadCallbackEvent.OFFLOAD_STARTED: + callback.onStarted(); + break; + case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR: + callback.onStoppedError(); + break; + case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED: + callback.onStoppedUnsupported(); + break; + case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE: + callback.onSupportAvailable(); + break; + case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED: + callback.onStoppedLimitReached(); + break; + case OffloadCallbackEvent.OFFLOAD_WARNING_REACHED: + callback.onWarningReached(); + break; + default: + log.e("Unsupported OffloadCallbackEvent: " + event); + } + } + + @Override + public void onEvent(int event) { + handler.post(() -> { + handleOnEvent(event); + }); + } + + @Override + public void updateTimeout(NatTimeoutUpdate params) { + handler.post(() -> { + callback.onNatTimeoutUpdate( + networkProtocolToOsConstant(params.proto), + params.src.addr, params.src.port, + params.dst.addr, params.dst.port); + }); + } + + @Override + public String getInterfaceHash() { + return ITetheringOffloadCallback.HASH; + } + + @Override + public int getInterfaceVersion() { + return ITetheringOffloadCallback.VERSION; + } + } + + private static int networkProtocolToOsConstant(int proto) { + switch (proto) { + case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP; + case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP; + default: + // The caller checks this value and will log an error. Just make + // sure it won't collide with valid OsConstants.IPPROTO_* values. + return -Math.abs(proto); + } + } +} diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHalHidlImpl.java b/Tethering/src/com/android/networkstack/tethering/OffloadHalHidlImpl.java new file mode 100644 index 0000000000..3e02543feb --- /dev/null +++ b/Tethering/src/com/android/networkstack/tethering/OffloadHalHidlImpl.java @@ -0,0 +1,436 @@ +/* + * 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 com.android.networkstack.tethering; + +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE; +import static com.android.networkstack.tethering.OffloadHardwareInterface.halVerToString; +import static com.android.networkstack.tethering.util.TetheringUtils.uint16; + +import android.annotation.NonNull; +import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; +import android.hardware.tetheroffload.control.V1_0.IOffloadControl; +import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; +import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; +import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; +import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback; +import android.os.Handler; +import android.os.NativeHandle; +import android.os.RemoteException; +import android.system.OsConstants; +import android.util.Log; + +import com.android.net.module.util.SharedLog; +import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; +import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback; + +import java.util.ArrayList; +import java.util.NoSuchElementException; + +/** + * The implementation of IOffloadHal which based on HIDL interfaces + */ +public class OffloadHalHidlImpl implements IOffloadHal { + private static final String TAG = OffloadHalHidlImpl.class.getSimpleName(); + private static final String YIELDS = " -> "; + + private final Handler mHandler; + private final SharedLog mLog; + private final IOffloadConfig mIOffloadConfig; + private final IOffloadControl mIOffloadControl; + @OffloadHardwareInterface.OffloadHalVersion + private final int mOffloadControlVersion; + + private OffloadHalCallback mOffloadHalCallback; + private TetheringOffloadCallback mTetheringOffloadCallback; + + public OffloadHalHidlImpl(int version, @NonNull IOffloadConfig config, + @NonNull IOffloadControl control, @NonNull Handler handler, @NonNull SharedLog log) { + mOffloadControlVersion = version; + mIOffloadConfig = config; + mIOffloadControl = control; + mHandler = handler; + mLog = log.forSubComponent(TAG); + } + + /** + * Initialize the Tetheroffload HAL. Provides bound netlink file descriptors for use in the + * management process. + */ + public boolean initOffload(@NonNull NativeHandle handle1, @NonNull NativeHandle handle2, + @NonNull OffloadHalCallback callback) { + final String logmsg = String.format("initOffload(%d, %d, %s)", + handle1.getFileDescriptor().getInt$(), handle2.getFileDescriptor().getInt$(), + (callback == null) ? "null" + : "0x" + Integer.toHexString(System.identityHashCode(callback))); + + mOffloadHalCallback = callback; + mTetheringOffloadCallback = new TetheringOffloadCallback( + mHandler, mOffloadHalCallback, mLog, mOffloadControlVersion); + final CbResults results = new CbResults(); + try { + mIOffloadConfig.setHandles(handle1, handle2, + (boolean success, String errMsg) -> { + results.mSuccess = success; + results.mErrMsg = errMsg; + }); + mIOffloadControl.initOffload( + mTetheringOffloadCallback, + (boolean success, String errMsg) -> { + results.mSuccess = success; + results.mErrMsg = errMsg; + }); + } catch (RemoteException e) { + record(logmsg, e); + return false; + } + + record(logmsg, results); + return results.mSuccess; + } + + /** Stop the Tetheroffload HAL. */ + public boolean stopOffload() { + try { + mIOffloadControl.stopOffload( + (boolean success, String errMsg) -> { + if (!success) mLog.e("stopOffload failed: " + errMsg); + }); + } catch (RemoteException e) { + mLog.e("failed to stopOffload: " + e); + } + mOffloadHalCallback = null; + mTetheringOffloadCallback = null; + mLog.log("stopOffload()"); + return true; + } + + /** Get HAL interface version number. */ + public int getVersion() { + return mOffloadControlVersion; + } + + /** Get Tx/Rx usage from last query. */ + public ForwardedStats getForwardedStats(@NonNull String upstream) { + final String logmsg = String.format("getForwardedStats(%s)", upstream); + + final ForwardedStats stats = new ForwardedStats(); + try { + mIOffloadControl.getForwardedStats( + upstream, + (long rxBytes, long txBytes) -> { + stats.rxBytes = (rxBytes > 0) ? rxBytes : 0; + stats.txBytes = (txBytes > 0) ? txBytes : 0; + }); + } catch (RemoteException e) { + record(logmsg, e); + return stats; + } + + return stats; + } + + /** Set local prefixes to offload management process. */ + public boolean setLocalPrefixes(@NonNull ArrayList localPrefixes) { + final String logmsg = String.format("setLocalPrefixes([%s])", + String.join(",", localPrefixes)); + + final CbResults results = new CbResults(); + try { + mIOffloadControl.setLocalPrefixes(localPrefixes, + (boolean success, String errMsg) -> { + results.mSuccess = success; + results.mErrMsg = errMsg; + }); + } catch (RemoteException e) { + record(logmsg, e); + return false; + } + + record(logmsg, results); + return results.mSuccess; + } + + /** Set data limit value to offload management process. */ + public boolean setDataLimit(@NonNull String iface, long limit) { + + final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit); + + final CbResults results = new CbResults(); + try { + mIOffloadControl.setDataLimit( + iface, limit, + (boolean success, String errMsg) -> { + results.mSuccess = success; + results.mErrMsg = errMsg; + }); + } catch (RemoteException e) { + record(logmsg, e); + return false; + } + + record(logmsg, results); + return results.mSuccess; + } + + /** Set data warning and limit value to offload management process. */ + public boolean setDataWarningAndLimit(@NonNull String iface, long warning, long limit) { + if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_HIDL_1_1) { + throw new UnsupportedOperationException( + "setDataWarningAndLimit is not supported below HAL V1.1"); + } + final String logmsg = + String.format("setDataWarningAndLimit(%s, %d, %d)", iface, warning, limit); + + final CbResults results = new CbResults(); + try { + ((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mIOffloadControl) + .setDataWarningAndLimit( + iface, warning, limit, + (boolean success, String errMsg) -> { + results.mSuccess = success; + results.mErrMsg = errMsg; + }); + } catch (RemoteException e) { + record(logmsg, e); + return false; + } + + record(logmsg, results); + return results.mSuccess; + } + + /** Set upstream parameters to offload management process. */ + public boolean setUpstreamParameters(@NonNull String iface, @NonNull String v4addr, + @NonNull String v4gateway, @NonNull ArrayList v6gws) { + final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])", + iface, v4addr, v4gateway, String.join(",", v6gws)); + + final CbResults results = new CbResults(); + try { + mIOffloadControl.setUpstreamParameters( + iface, v4addr, v4gateway, v6gws, + (boolean success, String errMsg) -> { + results.mSuccess = success; + results.mErrMsg = errMsg; + }); + } catch (RemoteException e) { + record(logmsg, e); + return false; + } + + record(logmsg, results); + return results.mSuccess; + } + + /** Add downstream prefix to offload management process. */ + public boolean addDownstream(@NonNull String ifname, @NonNull String prefix) { + final String logmsg = String.format("addDownstream(%s, %s)", ifname, prefix); + + final CbResults results = new CbResults(); + try { + mIOffloadControl.addDownstream(ifname, prefix, + (boolean success, String errMsg) -> { + results.mSuccess = success; + results.mErrMsg = errMsg; + }); + } catch (RemoteException e) { + record(logmsg, e); + return false; + } + + record(logmsg, results); + return results.mSuccess; + } + + /** Remove downstream prefix from offload management process. */ + public boolean removeDownstream(@NonNull String ifname, @NonNull String prefix) { + final String logmsg = String.format("removeDownstream(%s, %s)", ifname, prefix); + + final CbResults results = new CbResults(); + try { + mIOffloadControl.removeDownstream(ifname, prefix, + (boolean success, String errMsg) -> { + results.mSuccess = success; + results.mErrMsg = errMsg; + }); + } catch (RemoteException e) { + record(logmsg, e); + return false; + } + + record(logmsg, results); + return results.mSuccess; + } + + /** + * Get {@link IOffloadHal} object from the HIDL service. + * + * @param handler {@link Handler} to specify the thread upon which the callback will be invoked. + * @param log Log to be used by the repository. + */ + public static IOffloadHal getIOffloadHal(Handler handler, SharedLog log) { + IOffloadConfig config = null; + try { + config = IOffloadConfig.getService(true /*retry*/); + } catch (RemoteException | NoSuchElementException e) { + log.e("getIOffloadConfig error " + e); + return null; + } + + IOffloadControl control = null; + int version = OFFLOAD_HAL_VERSION_NONE; + try { + control = android.hardware.tetheroffload.control + .V1_1.IOffloadControl.getService(true /*retry*/); + version = OFFLOAD_HAL_VERSION_HIDL_1_1; + } catch (NoSuchElementException e) { + // Unsupported by device. + } catch (RemoteException e) { + log.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_HIDL_1_1); + } + if (control == null) { + try { + control = IOffloadControl.getService(true /*retry*/); + version = OFFLOAD_HAL_VERSION_HIDL_1_0; + } catch (NoSuchElementException e) { + // Unsupported by device. + } catch (RemoteException e) { + log.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_HIDL_1_0); + } + } + + if (config == null || control == null) return null; + + return new OffloadHalHidlImpl(version, config, control, handler, log); + } + + private void record(String msg, Throwable t) { + mLog.e(msg + YIELDS + "exception: " + t); + } + + private void record(String msg, CbResults results) { + final String logmsg = msg + YIELDS + results; + if (!results.mSuccess) { + mLog.e(logmsg); + } else { + mLog.log(logmsg); + } + } + + private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub { + public final Handler handler; + public final OffloadHalCallback callback; + public final SharedLog log; + private final int mOffloadControlVersion; + + TetheringOffloadCallback( + Handler h, OffloadHalCallback cb, SharedLog sharedLog, int offloadControlVersion) { + handler = h; + callback = cb; + log = sharedLog; + this.mOffloadControlVersion = offloadControlVersion; + } + + private void handleOnEvent(int event) { + switch (event) { + case OffloadCallbackEvent.OFFLOAD_STARTED: + callback.onStarted(); + break; + case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR: + callback.onStoppedError(); + break; + case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED: + callback.onStoppedUnsupported(); + break; + case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE: + callback.onSupportAvailable(); + break; + case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED: + callback.onStoppedLimitReached(); + break; + case android.hardware.tetheroffload.control + .V1_1.OffloadCallbackEvent.OFFLOAD_WARNING_REACHED: + callback.onWarningReached(); + break; + default: + log.e("Unsupported OffloadCallbackEvent: " + event); + } + } + + @Override + public void onEvent(int event) { + // The implementation should never call onEvent()) if the event is already reported + // through newer callback. + if (mOffloadControlVersion > OFFLOAD_HAL_VERSION_HIDL_1_0) { + Log.wtf(TAG, "onEvent(" + event + ") fired on HAL " + + halVerToString(mOffloadControlVersion)); + } + handler.post(() -> { + handleOnEvent(event); + }); + } + + @Override + public void onEvent_1_1(int event) { + if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_HIDL_1_1) { + Log.wtf(TAG, "onEvent_1_1(" + event + ") fired on HAL " + + halVerToString(mOffloadControlVersion)); + return; + } + handler.post(() -> { + handleOnEvent(event); + }); + } + + @Override + public void updateTimeout(NatTimeoutUpdate params) { + handler.post(() -> { + callback.onNatTimeoutUpdate( + networkProtocolToOsConstant(params.proto), + params.src.addr, uint16(params.src.port), + params.dst.addr, uint16(params.dst.port)); + }); + } + } + + private static int networkProtocolToOsConstant(int proto) { + switch (proto) { + case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP; + case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP; + default: + // The caller checks this value and will log an error. Just make + // sure it won't collide with valid OsConstants.IPPROTO_* values. + return -Math.abs(proto); + } + } + + private static class CbResults { + boolean mSuccess; + String mErrMsg; + + @Override + public String toString() { + if (mSuccess) { + return "ok"; + } else { + return "fail: " + mErrMsg; + } + } + } +} diff --git a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java index 76ddfe5fa8..ea20063cf5 100644 --- a/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java +++ b/Tethering/src/com/android/networkstack/tethering/OffloadHardwareInterface.java @@ -18,25 +18,15 @@ package com.android.networkstack.tethering; import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_DUMP; import static com.android.net.module.util.netlink.StructNlMsgHdr.NLM_F_REQUEST; -import static com.android.networkstack.tethering.util.TetheringUtils.uint16; import android.annotation.IntDef; import android.annotation.NonNull; -import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; -import android.hardware.tetheroffload.control.V1_0.IOffloadControl; -import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; -import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; -import android.hardware.tetheroffload.control.V1_0.OffloadCallbackEvent; -import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback; import android.net.util.SocketUtils; import android.os.Handler; import android.os.NativeHandle; -import android.os.RemoteException; import android.system.ErrnoException; import android.system.Os; import android.system.OsConstants; -import android.util.Log; -import android.util.Pair; import com.android.internal.annotations.VisibleForTesting; import com.android.net.module.util.SharedLog; @@ -54,8 +44,6 @@ import java.net.SocketException; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.util.ArrayList; -import java.util.NoSuchElementException; - /** * Capture tethering dependencies, for injection. @@ -86,43 +74,43 @@ public class OffloadHardwareInterface { private final Handler mHandler; private final SharedLog mLog; private final Dependencies mDeps; - private IOffloadControl mOffloadControl; + private IOffloadHal mIOffload; // TODO: Use major-minor version control to prevent from defining new constants. static final int OFFLOAD_HAL_VERSION_NONE = 0; - static final int OFFLOAD_HAL_VERSION_1_0 = 1; - static final int OFFLOAD_HAL_VERSION_1_1 = 2; + static final int OFFLOAD_HAL_VERSION_HIDL_1_0 = 1; + static final int OFFLOAD_HAL_VERSION_HIDL_1_1 = 2; + static final int OFFLOAD_HAL_VERSION_AIDL = 3; /** @hide */ @Retention(RetentionPolicy.SOURCE) @IntDef(prefix = "OFFLOAD_HAL_VERSION_", value = { OFFLOAD_HAL_VERSION_NONE, - OFFLOAD_HAL_VERSION_1_0, - OFFLOAD_HAL_VERSION_1_1 + OFFLOAD_HAL_VERSION_HIDL_1_0, + OFFLOAD_HAL_VERSION_HIDL_1_1, + OFFLOAD_HAL_VERSION_AIDL, }) public @interface OffloadHalVersion {} - @OffloadHalVersion - private int mOffloadControlVersion = OFFLOAD_HAL_VERSION_NONE; @NonNull static String halVerToString(int version) { switch(version) { - case OFFLOAD_HAL_VERSION_1_0: - return "1.0"; - case OFFLOAD_HAL_VERSION_1_1: - return "1.1"; + case OFFLOAD_HAL_VERSION_HIDL_1_0: + return "HIDL 1.0"; + case OFFLOAD_HAL_VERSION_HIDL_1_1: + return "HIDL 1.1"; + case OFFLOAD_HAL_VERSION_AIDL: + return "AIDL"; case OFFLOAD_HAL_VERSION_NONE: return "None"; default: throw new IllegalArgumentException("Unsupported version int " + version); } - } - private TetheringOffloadCallback mTetheringOffloadCallback; - private ControlCallback mControlCallback; + private OffloadHalCallback mOffloadHalCallback; /** The callback to notify status of offload management process. */ - public static class ControlCallback { + public static class OffloadHalCallback { /** Offload started. */ public void onStarted() {} /** @@ -179,7 +167,7 @@ public class OffloadHardwareInterface { } public OffloadHardwareInterface(Handler h, SharedLog log) { - this(h, log, new Dependencies(log)); + this(h, log, new Dependencies(h, log)); } OffloadHardwareInterface(Handler h, SharedLog log, Dependencies deps) { @@ -190,45 +178,21 @@ public class OffloadHardwareInterface { /** Capture OffloadHardwareInterface dependencies, for injection. */ static class Dependencies { + private final Handler mHandler; private final SharedLog mLog; - Dependencies(SharedLog log) { + Dependencies(Handler handler, SharedLog log) { + mHandler = handler; mLog = log; } - public IOffloadConfig getOffloadConfig() { - try { - return IOffloadConfig.getService(true /*retry*/); - } catch (RemoteException | NoSuchElementException e) { - mLog.e("getIOffloadConfig error " + e); - return null; - } - } - - @NonNull - public Pair getOffloadControl() { - IOffloadControl hal = null; - int version = OFFLOAD_HAL_VERSION_NONE; - try { - hal = android.hardware.tetheroffload.control - .V1_1.IOffloadControl.getService(true /*retry*/); - version = OFFLOAD_HAL_VERSION_1_1; - } catch (NoSuchElementException e) { - // Unsupported by device. - } catch (RemoteException e) { - mLog.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_1_1); - } + public IOffloadHal getOffload() { + // Prefer AIDL implementation if its service is declared. + IOffloadHal hal = OffloadHalAidlImpl.getIOffloadHal(mHandler, mLog); if (hal == null) { - try { - hal = IOffloadControl.getService(true /*retry*/); - version = OFFLOAD_HAL_VERSION_1_0; - } catch (NoSuchElementException e) { - // Unsupported by device. - } catch (RemoteException e) { - mLog.e("Unable to get offload control " + OFFLOAD_HAL_VERSION_1_0); - } + hal = OffloadHalHidlImpl.getIOffloadHal(mHandler, mLog); } - return new Pair(hal, version); + return hal; } public NativeHandle createConntrackSocket(final int groups) { @@ -273,56 +237,6 @@ public class OffloadHardwareInterface { return DEFAULT_TETHER_OFFLOAD_DISABLED; } - /** - * Offload management process need to know conntrack rules to support NAT, but it may not have - * permission to create netlink netfilter sockets. Create two netlink netfilter sockets and - * share them with offload management process. - */ - public boolean initOffloadConfig() { - final IOffloadConfig offloadConfig = mDeps.getOffloadConfig(); - if (offloadConfig == null) { - mLog.e("Could not find IOffloadConfig service"); - return false; - } - // Per the IConfigOffload definition: - // - // h1 provides a file descriptor bound to the following netlink groups - // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY). - // - // h2 provides a file descriptor bound to the following netlink groups - // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY). - final NativeHandle h1 = mDeps.createConntrackSocket( - NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); - if (h1 == null) return false; - - requestSocketDump(h1); - - final NativeHandle h2 = mDeps.createConntrackSocket( - NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); - if (h2 == null) { - closeFdInNativeHandle(h1); - return false; - } - - final CbResults results = new CbResults(); - try { - offloadConfig.setHandles(h1, h2, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record("initOffloadConfig, setHandles fail", e); - return false; - } - // Explicitly close FDs. - closeFdInNativeHandle(h1); - closeFdInNativeHandle(h2); - - record("initOffloadConfig, setHandles results:", results); - return results.mSuccess; - } - @VisibleForTesting void sendIpv4NfGenMsg(@NonNull NativeHandle handle, short type, short flags) { final int length = StructNlMsgHdr.STRUCT_SIZE + StructNfGenMsg.STRUCT_SIZE; @@ -355,165 +269,107 @@ public class OffloadHardwareInterface { (short) (NLM_F_REQUEST | NLM_F_DUMP)); } - private void closeFdInNativeHandle(final NativeHandle h) { - try { - h.close(); - } catch (IOException | IllegalStateException e) { - // IllegalStateException means fd is already closed, do nothing here. - // Also nothing we can do if IOException. + private void maybeCloseFdInNativeHandles(final NativeHandle... handles) { + for (NativeHandle h : handles) { + if (h == null) continue; + try { + h.close(); + } catch (IOException | IllegalStateException e) { + // IllegalStateException means fd is already closed, do nothing here. + // Also nothing we can do if IOException. + } } } + private int initWithHandles(NativeHandle h1, NativeHandle h2) { + if (h1 == null || h2 == null) { + mLog.e("Failed to create socket."); + return OFFLOAD_HAL_VERSION_NONE; + } + + requestSocketDump(h1); + if (!mIOffload.initOffload(h1, h2, mOffloadHalCallback)) { + mIOffload.stopOffload(); + mLog.e("Failed to initialize offload."); + return OFFLOAD_HAL_VERSION_NONE; + } + + return mIOffload.getVersion(); + } + /** * Initialize the tethering offload HAL. * * @return one of {@code OFFLOAD_HAL_VERSION_*} represents the HAL version, or * {@link #OFFLOAD_HAL_VERSION_NONE} if failed. */ - public int initOffloadControl(ControlCallback controlCb) { - mControlCallback = controlCb; - - if (mOffloadControl == null) { - final Pair halAndVersion = mDeps.getOffloadControl(); - mOffloadControl = halAndVersion.first; - mOffloadControlVersion = halAndVersion.second; - if (mOffloadControl == null) { - mLog.e("tethering IOffloadControl.getService() returned null"); + public int initOffload(OffloadHalCallback offloadCb) { + if (mIOffload == null) { + mIOffload = mDeps.getOffload(); + if (mIOffload == null) { + mLog.e("No tethering offload HAL service found."); return OFFLOAD_HAL_VERSION_NONE; } - mLog.i("tethering offload control version " - + halVerToString(mOffloadControlVersion) + " is supported."); + mLog.i("Tethering offload version " + + halVerToString(mIOffload.getVersion()) + " is supported."); } - final String logmsg = String.format("initOffloadControl(%s)", - (controlCb == null) ? "null" - : "0x" + Integer.toHexString(System.identityHashCode(controlCb))); + // Per the IOffload definition: + // + // h1 provides a file descriptor bound to the following netlink groups + // (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY). + // + // h2 provides a file descriptor bound to the following netlink groups + // (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY). + final NativeHandle h1 = mDeps.createConntrackSocket( + NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY); + final NativeHandle h2 = mDeps.createConntrackSocket( + NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY); - mTetheringOffloadCallback = new TetheringOffloadCallback( - mHandler, mControlCallback, mLog, mOffloadControlVersion); - final CbResults results = new CbResults(); - try { - mOffloadControl.initOffload( - mTetheringOffloadCallback, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return OFFLOAD_HAL_VERSION_NONE; + mOffloadHalCallback = offloadCb; + final int version = initWithHandles(h1, h2); + + // Explicitly close FDs for HIDL. AIDL will pass the original FDs to the service, + // they shouldn't be closed here. + if (version < OFFLOAD_HAL_VERSION_AIDL) { + maybeCloseFdInNativeHandles(h1, h2); } - - record(logmsg, results); - return results.mSuccess ? mOffloadControlVersion : OFFLOAD_HAL_VERSION_NONE; + return version; } - /** Stop IOffloadControl. */ - public void stopOffloadControl() { - if (mOffloadControl != null) { - try { - mOffloadControl.stopOffload( - (boolean success, String errMsg) -> { - if (!success) mLog.e("stopOffload failed: " + errMsg); - }); - } catch (RemoteException e) { - mLog.e("failed to stopOffload: " + e); + /** Stop the tethering offload HAL. */ + public void stopOffload() { + if (mIOffload != null) { + if (!mIOffload.stopOffload()) { + mLog.e("Failed to stop offload."); } } - mOffloadControl = null; - mTetheringOffloadCallback = null; - mControlCallback = null; - mLog.log("stopOffloadControl()"); + mIOffload = null; + mOffloadHalCallback = null; } /** Get Tx/Rx usage from last query. */ public ForwardedStats getForwardedStats(String upstream) { - final String logmsg = String.format("getForwardedStats(%s)", upstream); - - final ForwardedStats stats = new ForwardedStats(); - try { - mOffloadControl.getForwardedStats( - upstream, - (long rxBytes, long txBytes) -> { - stats.rxBytes = (rxBytes > 0) ? rxBytes : 0; - stats.txBytes = (txBytes > 0) ? txBytes : 0; - }); - } catch (RemoteException e) { - record(logmsg, e); - return stats; - } - - return stats; + return mIOffload.getForwardedStats(upstream); } /** Set local prefixes to offload management process. */ public boolean setLocalPrefixes(ArrayList localPrefixes) { - final String logmsg = String.format("setLocalPrefixes([%s])", - String.join(",", localPrefixes)); - - final CbResults results = new CbResults(); - try { - mOffloadControl.setLocalPrefixes(localPrefixes, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; + return mIOffload.setLocalPrefixes(localPrefixes); } /** Set data limit value to offload management process. */ public boolean setDataLimit(String iface, long limit) { - - final String logmsg = String.format("setDataLimit(%s, %d)", iface, limit); - - final CbResults results = new CbResults(); - try { - mOffloadControl.setDataLimit( - iface, limit, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; + return mIOffload.setDataLimit(iface, limit); } /** Set data warning and limit value to offload management process. */ public boolean setDataWarningAndLimit(String iface, long warning, long limit) { - if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_1_1) { - throw new IllegalArgumentException( + if (mIOffload.getVersion() < OFFLOAD_HAL_VERSION_HIDL_1_1) { + throw new UnsupportedOperationException( "setDataWarningAndLimit is not supported below HAL V1.1"); } - final String logmsg = - String.format("setDataWarningAndLimit(%s, %d, %d)", iface, warning, limit); - - final CbResults results = new CbResults(); - try { - ((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mOffloadControl) - .setDataWarningAndLimit( - iface, warning, limit, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; + return mIOffload.setDataWarningAndLimit(iface, warning, limit); } /** Set upstream parameters to offload management process. */ @@ -523,178 +379,16 @@ public class OffloadHardwareInterface { v4addr = (v4addr != null) ? v4addr : NO_IPV4_ADDRESS; v4gateway = (v4gateway != null) ? v4gateway : NO_IPV4_GATEWAY; v6gws = (v6gws != null) ? v6gws : new ArrayList<>(); - - final String logmsg = String.format("setUpstreamParameters(%s, %s, %s, [%s])", - iface, v4addr, v4gateway, String.join(",", v6gws)); - - final CbResults results = new CbResults(); - try { - mOffloadControl.setUpstreamParameters( - iface, v4addr, v4gateway, v6gws, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; + return mIOffload.setUpstreamParameters(iface, v4addr, v4gateway, v6gws); } /** Add downstream prefix to offload management process. */ - public boolean addDownstreamPrefix(String ifname, String prefix) { - final String logmsg = String.format("addDownstreamPrefix(%s, %s)", ifname, prefix); - - final CbResults results = new CbResults(); - try { - mOffloadControl.addDownstream(ifname, prefix, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; + public boolean addDownstream(String ifname, String prefix) { + return mIOffload.addDownstream(ifname, prefix); } /** Remove downstream prefix from offload management process. */ - public boolean removeDownstreamPrefix(String ifname, String prefix) { - final String logmsg = String.format("removeDownstreamPrefix(%s, %s)", ifname, prefix); - - final CbResults results = new CbResults(); - try { - mOffloadControl.removeDownstream(ifname, prefix, - (boolean success, String errMsg) -> { - results.mSuccess = success; - results.mErrMsg = errMsg; - }); - } catch (RemoteException e) { - record(logmsg, e); - return false; - } - - record(logmsg, results); - return results.mSuccess; - } - - private void record(String msg, Throwable t) { - mLog.e(msg + YIELDS + "exception: " + t); - } - - private void record(String msg, CbResults results) { - final String logmsg = msg + YIELDS + results; - if (!results.mSuccess) { - mLog.e(logmsg); - } else { - mLog.log(logmsg); - } - } - - private static class TetheringOffloadCallback extends ITetheringOffloadCallback.Stub { - public final Handler handler; - public final ControlCallback controlCb; - public final SharedLog log; - private final int mOffloadControlVersion; - - TetheringOffloadCallback( - Handler h, ControlCallback cb, SharedLog sharedLog, int offloadControlVersion) { - handler = h; - controlCb = cb; - log = sharedLog; - this.mOffloadControlVersion = offloadControlVersion; - } - - private void handleOnEvent(int event) { - switch (event) { - case OffloadCallbackEvent.OFFLOAD_STARTED: - controlCb.onStarted(); - break; - case OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR: - controlCb.onStoppedError(); - break; - case OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED: - controlCb.onStoppedUnsupported(); - break; - case OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE: - controlCb.onSupportAvailable(); - break; - case OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED: - controlCb.onStoppedLimitReached(); - break; - case android.hardware.tetheroffload.control - .V1_1.OffloadCallbackEvent.OFFLOAD_WARNING_REACHED: - controlCb.onWarningReached(); - break; - default: - log.e("Unsupported OffloadCallbackEvent: " + event); - } - } - - @Override - public void onEvent(int event) { - // The implementation should never call onEvent()) if the event is already reported - // through newer callback. - if (mOffloadControlVersion > OFFLOAD_HAL_VERSION_1_0) { - Log.wtf(TAG, "onEvent(" + event + ") fired on HAL " - + halVerToString(mOffloadControlVersion)); - } - handler.post(() -> { - handleOnEvent(event); - }); - } - - @Override - public void onEvent_1_1(int event) { - if (mOffloadControlVersion < OFFLOAD_HAL_VERSION_1_1) { - Log.wtf(TAG, "onEvent_1_1(" + event + ") fired on HAL " - + halVerToString(mOffloadControlVersion)); - return; - } - handler.post(() -> { - handleOnEvent(event); - }); - } - - @Override - public void updateTimeout(NatTimeoutUpdate params) { - handler.post(() -> { - controlCb.onNatTimeoutUpdate( - networkProtocolToOsConstant(params.proto), - params.src.addr, uint16(params.src.port), - params.dst.addr, uint16(params.dst.port)); - }); - } - } - - private static int networkProtocolToOsConstant(int proto) { - switch (proto) { - case NetworkProtocol.TCP: return OsConstants.IPPROTO_TCP; - case NetworkProtocol.UDP: return OsConstants.IPPROTO_UDP; - default: - // The caller checks this value and will log an error. Just make - // sure it won't collide with valid OsContants.IPPROTO_* values. - return -Math.abs(proto); - } - } - - private static class CbResults { - boolean mSuccess; - String mErrMsg; - - @Override - public String toString() { - if (mSuccess) { - return "ok"; - } else { - return "fail: " + mErrMsg; - } - } + public boolean removeDownstream(String ifname, String prefix) { + return mIOffload.removeDownstream(ifname, prefix); } } diff --git a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java index 706df4eaec..b3fb3e45f6 100644 --- a/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java +++ b/Tethering/tests/privileged/src/com/android/networkstack/tethering/ConntrackSocketTest.java @@ -80,7 +80,7 @@ public class ConntrackSocketTest { // Looper must be prepared here since AndroidJUnitRunner runs tests on separate threads. if (Looper.myLooper() == null) Looper.prepare(); - mDeps = new OffloadHardwareInterface.Dependencies(mLog); + mDeps = new OffloadHardwareInterface.Dependencies(mHandler, mLog); mOffloadHw = new OffloadHardwareInterface(mHandler, mLog, mDeps); } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java index faca1c890f..36c15a7ca1 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadControllerTest.java @@ -31,8 +31,8 @@ import static com.android.modules.utils.build.SdkLevel.isAtLeastT; import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_IFACE; import static com.android.networkstack.tethering.OffloadController.StatsType.STATS_PER_UID; import static com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; -import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0; -import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1; import static com.android.networkstack.tethering.TetheringConfiguration.DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS; import static com.android.testutils.MiscAsserts.assertContainsAll; import static com.android.testutils.MiscAsserts.assertThrows; @@ -79,6 +79,7 @@ import androidx.test.runner.AndroidJUnit4; import com.android.internal.util.test.FakeSettingsProvider; import com.android.net.module.util.SharedLog; +import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback; import com.android.testutils.DevSdkIgnoreRule; import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; import com.android.testutils.TestableNetworkStatsProviderCbBinder; @@ -125,8 +126,8 @@ public class OffloadControllerTest { private OffloadController.OffloadTetheringStatsProvider mTetherStatsProvider; private final ArgumentCaptor mStringArrayCaptor = ArgumentCaptor.forClass(ArrayList.class); - private final ArgumentCaptor mControlCallbackCaptor = - ArgumentCaptor.forClass(OffloadHardwareInterface.ControlCallback.class); + private final ArgumentCaptor mOffloadHalCallbackCaptor = + ArgumentCaptor.forClass(OffloadHalCallback.class); private MockContentResolver mContentResolver; private final TestLooper mTestLooper = new TestLooper(); private OffloadController.Dependencies mDeps = new OffloadController.Dependencies() { @@ -151,10 +152,9 @@ public class OffloadControllerTest { FakeSettingsProvider.clearSettingsProvider(); } - private void setupFunctioningHardwareInterface(int controlVersion) { - when(mHardware.initOffloadConfig()).thenReturn(true); - when(mHardware.initOffloadControl(mControlCallbackCaptor.capture())) - .thenReturn(controlVersion); + private void setupFunctioningHardwareInterface(int offloadHalVersion) { + when(mHardware.initOffload(mOffloadHalCallbackCaptor.capture())) + .thenReturn(offloadHalVersion); when(mHardware.setUpstreamParameters(anyString(), any(), any(), any())).thenReturn(true); when(mHardware.getForwardedStats(any())).thenReturn(new ForwardedStats()); when(mHardware.setDataLimit(anyString(), anyLong())).thenReturn(true); @@ -192,9 +192,9 @@ public class OffloadControllerTest { @Test public void testStartStop() throws Exception { stopOffloadController( - startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/)); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/)); stopOffloadController( - startOffloadController(OFFLOAD_HAL_VERSION_1_1, true /*expectStart*/)); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_1, true /*expectStart*/)); } @NonNull @@ -206,9 +206,8 @@ public class OffloadControllerTest { final InOrder inOrder = inOrder(mHardware); inOrder.verify(mHardware, times(1)).getDefaultTetherOffloadDisabled(); - inOrder.verify(mHardware, times(expectStart ? 1 : 0)).initOffloadConfig(); - inOrder.verify(mHardware, times(expectStart ? 1 : 0)).initOffloadControl( - any(OffloadHardwareInterface.ControlCallback.class)); + inOrder.verify(mHardware, times(expectStart ? 1 : 0)).initOffload( + any(OffloadHalCallback.class)); inOrder.verifyNoMoreInteractions(); // Clear counters only instead of whole mock to preserve the mocking setup. clearInvocations(mHardware); @@ -218,7 +217,7 @@ public class OffloadControllerTest { private void stopOffloadController(final OffloadController offload) throws Exception { final InOrder inOrder = inOrder(mHardware); offload.stop(); - inOrder.verify(mHardware, times(1)).stopOffloadControl(); + inOrder.verify(mHardware, times(1)).stopOffload(); inOrder.verifyNoMoreInteractions(); reset(mHardware); } @@ -228,7 +227,7 @@ public class OffloadControllerTest { when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(1); assertThrows(SettingNotFoundException.class, () -> Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED)); - startOffloadController(OFFLOAD_HAL_VERSION_1_0, false /*expectStart*/); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, false /*expectStart*/); } @Test @@ -236,26 +235,26 @@ public class OffloadControllerTest { when(mHardware.getDefaultTetherOffloadDisabled()).thenReturn(0); assertThrows(SettingNotFoundException.class, () -> Settings.Global.getInt(mContentResolver, TETHER_OFFLOAD_DISABLED)); - startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/); } @Test public void testSettingsAllowsStart() throws Exception { Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 0); - startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/); } @Test public void testSettingsDisablesStart() throws Exception { Settings.Global.putInt(mContentResolver, TETHER_OFFLOAD_DISABLED, 1); - startOffloadController(OFFLOAD_HAL_VERSION_1_0, false /*expectStart*/); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, false /*expectStart*/); } @Test public void testSetUpstreamLinkPropertiesWorking() throws Exception { enableOffload(); final OffloadController offload = - startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/); // In reality, the UpstreamNetworkMonitor would have passed down to us // a covering set of local prefixes representing a minimum essential @@ -426,7 +425,7 @@ public class OffloadControllerTest { public void testGetForwardedStats() throws Exception { enableOffload(); final OffloadController offload = - startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/); final String ethernetIface = "eth1"; final String mobileIface = "rmnet_data0"; @@ -521,11 +520,11 @@ public class OffloadControllerTest { // Verify the OffloadController is called by R framework, where the framework doesn't send // warning. // R only uses HAL 1.0. - checkSetDataWarningAndLimit(false, OFFLOAD_HAL_VERSION_1_0); + checkSetDataWarningAndLimit(false, OFFLOAD_HAL_VERSION_HIDL_1_0); // Verify the OffloadController is called by S+ framework, where the framework sends // warning along with limit. - checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_1_0); - checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_1_1); + checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_HIDL_1_0); + checkSetDataWarningAndLimit(true, OFFLOAD_HAL_VERSION_HIDL_1_1); } private void checkSetDataWarningAndLimit(boolean isProviderSetWarning, int controlVersion) @@ -550,7 +549,7 @@ public class OffloadControllerTest { when(mHardware.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true); offload.setUpstreamLinkProperties(lp); // Applying an interface sends the initial quota to the hardware. - if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) { + if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) { inOrder.verify(mHardware).setDataWarningAndLimit(ethernetIface, Long.MAX_VALUE, Long.MAX_VALUE); } else { @@ -576,7 +575,7 @@ public class OffloadControllerTest { mTetherStatsProvider.onSetLimit(ethernetIface, ethernetLimit); } waitForIdle(); - if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) { + if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) { inOrder.verify(mHardware).setDataWarningAndLimit(ethernetIface, Long.MAX_VALUE, ethernetLimit); } else { @@ -591,7 +590,7 @@ public class OffloadControllerTest { mTetherStatsProvider.onSetLimit(mobileIface, mobileLimit); } waitForIdle(); - if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) { + if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) { inOrder.verify(mHardware, never()).setDataWarningAndLimit(anyString(), anyLong(), anyLong()); } else { @@ -603,7 +602,7 @@ public class OffloadControllerTest { lp.setInterfaceName(mobileIface); offload.setUpstreamLinkProperties(lp); waitForIdle(); - if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) { + if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) { inOrder.verify(mHardware).setDataWarningAndLimit(mobileIface, isProviderSetWarning ? mobileWarning : Long.MAX_VALUE, mobileLimit); @@ -620,7 +619,7 @@ public class OffloadControllerTest { mTetherStatsProvider.onSetLimit(mobileIface, NetworkStatsProvider.QUOTA_UNLIMITED); } waitForIdle(); - if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) { + if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) { inOrder.verify(mHardware).setDataWarningAndLimit(mobileIface, Long.MAX_VALUE, Long.MAX_VALUE); } else { @@ -655,15 +654,15 @@ public class OffloadControllerTest { } waitForIdle(); inOrder.verify(mHardware).getForwardedStats(ethernetIface); - inOrder.verify(mHardware).stopOffloadControl(); + inOrder.verify(mHardware).stopOffload(); } @Test public void testDataWarningAndLimitCallback_LimitReached() throws Exception { enableOffload(); - startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/); - final OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); + final OffloadHalCallback callback = mOffloadHalCallbackCaptor.getValue(); callback.onStoppedLimitReached(); mTetherStatsProviderCb.expectNotifyStatsUpdated(); @@ -679,8 +678,8 @@ public class OffloadControllerTest { @Test @IgnoreUpTo(Build.VERSION_CODES.R) // HAL 1.1 is only supported from S public void testDataWarningAndLimitCallback_WarningReached() throws Exception { - startOffloadController(OFFLOAD_HAL_VERSION_1_1, true /*expectStart*/); - final OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_1, true /*expectStart*/); + final OffloadHalCallback callback = mOffloadHalCallbackCaptor.getValue(); callback.onWarningReached(); mTetherStatsProviderCb.expectNotifyStatsUpdated(); @@ -695,7 +694,7 @@ public class OffloadControllerTest { public void testAddRemoveDownstreams() throws Exception { enableOffload(); final OffloadController offload = - startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/); final InOrder inOrder = inOrder(mHardware); // Tethering makes several calls to setLocalPrefixes() before add/remove @@ -710,14 +709,14 @@ public class OffloadControllerTest { usbLinkProperties.addRoute( new RouteInfo(new IpPrefix(USB_PREFIX), null, null, RTN_UNICAST)); offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, USB_PREFIX); + inOrder.verify(mHardware, times(1)).addDownstream(RNDIS0, USB_PREFIX); inOrder.verifyNoMoreInteractions(); // [2] Routes for IPv6 link-local prefixes should never be added. usbLinkProperties.addRoute( new RouteInfo(new IpPrefix(IPV6_LINKLOCAL), null, null, RTN_UNICAST)); offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString()); + inOrder.verify(mHardware, never()).addDownstream(eq(RNDIS0), anyString()); inOrder.verifyNoMoreInteractions(); // [3] Add an IPv6 prefix for good measure. Only new offload-able @@ -726,14 +725,14 @@ public class OffloadControllerTest { usbLinkProperties.addRoute( new RouteInfo(new IpPrefix(IPV6_DOC_PREFIX), null, null, RTN_UNICAST)); offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX); + inOrder.verify(mHardware, times(1)).addDownstream(RNDIS0, IPV6_DOC_PREFIX); inOrder.verifyNoMoreInteractions(); // [4] Adding addresses doesn't affect notifyDownstreamLinkProperties(). // The address is passed in by a separate setLocalPrefixes() invocation. usbLinkProperties.addLinkAddress(new LinkAddress("2001:db8::2/64")); offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, never()).addDownstreamPrefix(eq(RNDIS0), anyString()); + inOrder.verify(mHardware, never()).addDownstream(eq(RNDIS0), anyString()); // [5] Differences in local routes are converted into addDownstream() // and removeDownstream() invocations accordingly. @@ -742,8 +741,8 @@ public class OffloadControllerTest { usbLinkProperties.addRoute( new RouteInfo(new IpPrefix(IPV6_DISCARD_PREFIX), null, null, RTN_UNICAST)); offload.notifyDownstreamLinkProperties(usbLinkProperties); - inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DOC_PREFIX); - inOrder.verify(mHardware, times(1)).addDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX); + inOrder.verify(mHardware, times(1)).removeDownstream(RNDIS0, IPV6_DOC_PREFIX); + inOrder.verify(mHardware, times(1)).addDownstream(RNDIS0, IPV6_DISCARD_PREFIX); inOrder.verifyNoMoreInteractions(); // [6] Removing a downstream interface which was never added causes no @@ -753,8 +752,8 @@ public class OffloadControllerTest { // [7] Removing an active downstream removes all remaining prefixes. offload.removeDownstreamInterface(RNDIS0); - inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, USB_PREFIX); - inOrder.verify(mHardware, times(1)).removeDownstreamPrefix(RNDIS0, IPV6_DISCARD_PREFIX); + inOrder.verify(mHardware, times(1)).removeDownstream(RNDIS0, USB_PREFIX); + inOrder.verify(mHardware, times(1)).removeDownstream(RNDIS0, IPV6_DISCARD_PREFIX); inOrder.verifyNoMoreInteractions(); } @@ -762,7 +761,7 @@ public class OffloadControllerTest { public void testControlCallbackOnStoppedUnsupportedFetchesAllStats() throws Exception { enableOffload(); final OffloadController offload = - startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/); // Pretend to set a few different upstreams (only the interface name // matters for this test; we're ignoring IP and route information). @@ -776,7 +775,7 @@ public class OffloadControllerTest { // that happen with setUpstreamParameters(). clearInvocations(mHardware); - OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); + OffloadHalCallback callback = mOffloadHalCallbackCaptor.getValue(); callback.onStoppedUnsupported(); // Verify forwarded stats behaviour. @@ -793,7 +792,7 @@ public class OffloadControllerTest { throws Exception { enableOffload(); final OffloadController offload = - startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/); // Pretend to set a few different upstreams (only the interface name // matters for this test; we're ignoring IP and route information). @@ -840,7 +839,7 @@ public class OffloadControllerTest { // that happen with setUpstreamParameters(). clearInvocations(mHardware); - OffloadHardwareInterface.ControlCallback callback = mControlCallbackCaptor.getValue(); + OffloadHalCallback callback = mOffloadHalCallbackCaptor.getValue(); callback.onSupportAvailable(); // Verify forwarded stats behaviour. @@ -859,8 +858,8 @@ public class OffloadControllerTest { // into OffloadController proper. After this, also check for: // "192.168.43.1/32", "2001:2::1/128", "2001:2::2/128" "127.0.0.0/8", "192.0.2.0/24", "fe80::/64", "2001:db8::/64"); - verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "192.168.43.0/24"); - verify(mHardware, times(1)).addDownstreamPrefix(WLAN0, "2001:2::/64"); + verify(mHardware, times(1)).addDownstream(WLAN0, "192.168.43.0/24"); + verify(mHardware, times(1)).addDownstream(WLAN0, "2001:2::/64"); verify(mHardware, times(1)).setUpstreamParameters(eq(RMNET0), any(), any(), any()); verify(mHardware, times(1)).setDataLimit(eq(RMNET0), anyLong()); verifyNoMoreInteractions(mHardware); @@ -871,7 +870,7 @@ public class OffloadControllerTest { enableOffload(); setOffloadPollInterval(DEFAULT_TETHER_OFFLOAD_POLL_INTERVAL_MS); final OffloadController offload = - startOffloadController(OFFLOAD_HAL_VERSION_1_0, true /*expectStart*/); + startOffloadController(OFFLOAD_HAL_VERSION_HIDL_1_0, true /*expectStart*/); // Initialize with fake eth upstream. final String ethernetIface = "eth1"; @@ -925,7 +924,7 @@ public class OffloadControllerTest { offload.setUpstreamLinkProperties(makeEthernetLinkProperties()); mTetherStatsProvider.onSetAlert(0); waitForIdle(); - if (controlVersion >= OFFLOAD_HAL_VERSION_1_1) { + if (controlVersion >= OFFLOAD_HAL_VERSION_HIDL_1_1) { mTetherStatsProviderCb.assertNoCallback(); } else { mTetherStatsProviderCb.expectNotifyAlertReached(); @@ -935,7 +934,7 @@ public class OffloadControllerTest { @Test public void testSoftwarePollingUsed() throws Exception { - checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_1_0); - checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_1_1); + checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_HIDL_1_0); + checkSoftwarePollingUsed(OFFLOAD_HAL_VERSION_HIDL_1_1); } } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalAidlImplTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalAidlImplTest.java new file mode 100644 index 0000000000..c9ce64fb08 --- /dev/null +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalAidlImplTest.java @@ -0,0 +1,390 @@ +/* + * 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 com.android.networkstack.tethering; + +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_AIDL; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.anyLong; +import static org.mockito.Mockito.anyString; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.doThrow; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import android.hardware.tetheroffload.ForwardedStats; +import android.hardware.tetheroffload.IOffload; +import android.hardware.tetheroffload.IPv4AddrPortPair; +import android.hardware.tetheroffload.ITetheringOffloadCallback; +import android.hardware.tetheroffload.NatTimeoutUpdate; +import android.hardware.tetheroffload.NetworkProtocol; +import android.hardware.tetheroffload.OffloadCallbackEvent; +import android.os.Handler; +import android.os.NativeHandle; +import android.os.ParcelFileDescriptor; +import android.os.ServiceSpecificException; +import android.os.test.TestLooper; +import android.system.OsConstants; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.net.module.util.SharedLog; +import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.MockitoAnnotations; + +import java.io.FileDescriptor; +import java.util.ArrayList; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class OffloadHalAidlImplTest { + private static final String RMNET0 = "test_rmnet_data0"; + + private final SharedLog mLog = new SharedLog("test"); + private final TestLooper mTestLooper = new TestLooper(); + + private IOffload mIOffloadMock; + private OffloadHalAidlImpl mIOffloadHal; + private ITetheringOffloadCallback mTetheringOffloadCallback; + private OffloadHalCallback mOffloadHalCallback; + + private void initAndValidateOffloadHal(boolean initSuccess) + throws Exception { + final FileDescriptor fd1 = new FileDescriptor(); + final FileDescriptor fd2 = new FileDescriptor(); + final NativeHandle handle1 = new NativeHandle(fd1, true); + final NativeHandle handle2 = new NativeHandle(fd2, true); + final ArgumentCaptor fdCaptor1 = + ArgumentCaptor.forClass(ParcelFileDescriptor.class); + final ArgumentCaptor fdCaptor2 = + ArgumentCaptor.forClass(ParcelFileDescriptor.class); + final ArgumentCaptor offloadCallbackCaptor = + ArgumentCaptor.forClass(ITetheringOffloadCallback.class); + if (initSuccess) { + doNothing().when(mIOffloadMock).initOffload(any(), any(), any()); + } else { + doThrow(new IllegalStateException()).when(mIOffloadMock).initOffload(any(), any(), + any()); + } + assertEquals(mIOffloadHal.initOffload(handle1, handle2, mOffloadHalCallback), + initSuccess); + verify(mIOffloadMock).initOffload(fdCaptor1.capture(), fdCaptor2.capture(), + offloadCallbackCaptor.capture()); + assertEquals(fdCaptor1.getValue().getFd(), fd1.getInt$()); + assertEquals(fdCaptor2.getValue().getFd(), fd2.getInt$()); + mTetheringOffloadCallback = offloadCallbackCaptor.getValue(); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mIOffloadMock = mock(IOffload.class); + mIOffloadHal = new OffloadHalAidlImpl(OFFLOAD_HAL_VERSION_AIDL, mIOffloadMock, + new Handler(mTestLooper.getLooper()), mLog); + mOffloadHalCallback = spy(new OffloadHalCallback()); + } + + @Test + public void testInitOffloadSuccess() throws Exception { + initAndValidateOffloadHal(true /* initSuccess */); + } + + @Test + public void testInitOffloadFailure() throws Exception { + initAndValidateOffloadHal(false /* initSuccess */); + } + + @Test + public void testStopOffloadSuccess() throws Exception { + initAndValidateOffloadHal(true); + doNothing().when(mIOffloadMock).stopOffload(); + assertTrue(mIOffloadHal.stopOffload()); + verify(mIOffloadMock).stopOffload(); + } + + @Test + public void testStopOffloadFailure() throws Exception { + initAndValidateOffloadHal(true); + doThrow(new IllegalStateException()).when(mIOffloadMock).stopOffload(); + assertFalse(mIOffloadHal.stopOffload()); + } + + private void doTestGetForwardedStats(boolean expectSuccess) throws Exception { + initAndValidateOffloadHal(true); + final ForwardedStats returnStats = new ForwardedStats(); + if (expectSuccess) { + returnStats.rxBytes = 12345; + returnStats.txBytes = 67890; + when(mIOffloadMock.getForwardedStats(anyString())).thenReturn(returnStats); + } else { + when(mIOffloadMock.getForwardedStats(anyString())) + .thenThrow(new ServiceSpecificException(IOffload.ERROR_CODE_UNUSED)); + } + final OffloadHardwareInterface.ForwardedStats stats = + mIOffloadHal.getForwardedStats(RMNET0); + verify(mIOffloadMock).getForwardedStats(eq(RMNET0)); + assertNotNull(stats); + assertEquals(stats.rxBytes, returnStats.rxBytes); + assertEquals(stats.txBytes, returnStats.txBytes); + } + + @Test + public void testGetForwardedStatsSuccess() throws Exception { + doTestGetForwardedStats(true); + } + + @Test + public void testGetForwardedStatsFailure() throws Exception { + doTestGetForwardedStats(false); + } + + private void doTestSetLocalPrefixes(boolean expectSuccess) throws Exception { + initAndValidateOffloadHal(true); + final ArrayList localPrefixes = new ArrayList<>(); + localPrefixes.add("127.0.0.0/8"); + localPrefixes.add("fe80::/64"); + final String[] localPrefixesArray = + localPrefixes.toArray(new String[localPrefixes.size()]); + if (expectSuccess) { + doNothing().when(mIOffloadMock).setLocalPrefixes(any()); + } else { + doThrow(new IllegalArgumentException()).when(mIOffloadMock).setLocalPrefixes(any()); + } + assertEquals(expectSuccess, mIOffloadHal.setLocalPrefixes(localPrefixes)); + verify(mIOffloadMock).setLocalPrefixes(eq(localPrefixesArray)); + } + + @Test + public void testSetLocalPrefixesSuccess() throws Exception { + doTestSetLocalPrefixes(true); + } + + @Test + public void testSetLocalPrefixesFailure() throws Exception { + doTestSetLocalPrefixes(false); + } + + private void doTestSetDataLimit(boolean expectSuccess) throws Exception { + initAndValidateOffloadHal(true); + final long limit = 12345; + if (expectSuccess) { + doNothing().when(mIOffloadMock).setDataWarningAndLimit(anyString(), anyLong(), + anyLong()); + } else { + doThrow(new IllegalArgumentException()) + .when(mIOffloadMock).setDataWarningAndLimit(anyString(), anyLong(), anyLong()); + } + assertEquals(expectSuccess, mIOffloadHal.setDataLimit(RMNET0, limit)); + verify(mIOffloadMock).setDataWarningAndLimit(eq(RMNET0), eq(Long.MAX_VALUE), eq(limit)); + } + + @Test + public void testSetDataLimitSuccess() throws Exception { + doTestSetDataLimit(true); + } + + @Test + public void testSetDataLimitFailure() throws Exception { + doTestSetDataLimit(false); + } + + private void doTestSetDataWarningAndLimit(boolean expectSuccess) throws Exception { + initAndValidateOffloadHal(true); + final long warning = 12345; + final long limit = 67890; + if (expectSuccess) { + doNothing().when(mIOffloadMock).setDataWarningAndLimit(anyString(), anyLong(), + anyLong()); + } else { + doThrow(new IllegalArgumentException()) + .when(mIOffloadMock).setDataWarningAndLimit(anyString(), anyLong(), anyLong()); + } + assertEquals(expectSuccess, mIOffloadHal.setDataWarningAndLimit(RMNET0, warning, limit)); + verify(mIOffloadMock).setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit)); + } + + @Test + public void testSetDataWarningAndLimitSuccess() throws Exception { + doTestSetDataWarningAndLimit(true); + } + + @Test + public void testSetDataWarningAndLimitFailure() throws Exception { + doTestSetDataWarningAndLimit(false); + } + + private void doTestSetUpstreamParameters(boolean expectSuccess) throws Exception { + initAndValidateOffloadHal(true); + final String v4addr = "192.168.10.1"; + final String v4gateway = "192.168.10.255"; + final ArrayList v6gws = new ArrayList<>(0); + v6gws.add("2001:db8::1"); + String[] v6gwsArray = v6gws.toArray(new String[v6gws.size()]); + if (expectSuccess) { + doNothing().when(mIOffloadMock).setUpstreamParameters(anyString(), anyString(), + anyString(), any()); + } else { + doThrow(new IllegalArgumentException()).when(mIOffloadMock).setUpstreamParameters( + anyString(), anyString(), anyString(), any()); + } + assertEquals(expectSuccess, mIOffloadHal.setUpstreamParameters(RMNET0, v4addr, v4gateway, + v6gws)); + verify(mIOffloadMock).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway), + eq(v6gwsArray)); + } + + @Test + public void testSetUpstreamParametersSuccess() throws Exception { + doTestSetUpstreamParameters(true); + } + + @Test + public void testSetUpstreamParametersFailure() throws Exception { + doTestSetUpstreamParameters(false); + } + + private void doTestAddDownstream(boolean expectSuccess) throws Exception { + initAndValidateOffloadHal(true); + final String ifName = "wlan1"; + final String prefix = "192.168.43.0/24"; + if (expectSuccess) { + doNothing().when(mIOffloadMock).addDownstream(anyString(), anyString()); + } else { + doThrow(new IllegalStateException()).when(mIOffloadMock).addDownstream(anyString(), + anyString()); + } + assertEquals(expectSuccess, mIOffloadHal.addDownstream(ifName, prefix)); + verify(mIOffloadMock).addDownstream(eq(ifName), eq(prefix)); + } + + @Test + public void testAddDownstreamSuccess() throws Exception { + doTestAddDownstream(true); + } + + @Test + public void testAddDownstreamFailure() throws Exception { + doTestAddDownstream(false); + } + + private void doTestRemoveDownstream(boolean expectSuccess) throws Exception { + initAndValidateOffloadHal(true); + final String ifName = "wlan1"; + final String prefix = "192.168.43.0/24"; + if (expectSuccess) { + doNothing().when(mIOffloadMock).removeDownstream(anyString(), anyString()); + } else { + doThrow(new IllegalArgumentException()).when(mIOffloadMock).removeDownstream( + anyString(), anyString()); + } + assertEquals(expectSuccess, mIOffloadHal.removeDownstream(ifName, prefix)); + verify(mIOffloadMock).removeDownstream(eq(ifName), eq(prefix)); + } + + @Test + public void testRemoveDownstreamSuccess() throws Exception { + doTestRemoveDownstream(true); + } + + @Test + public void testRemoveDownstreamFailure() throws Exception { + doTestRemoveDownstream(false); + } + + @Test + public void testTetheringOffloadCallback() throws Exception { + initAndValidateOffloadHal(true); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED); + mTestLooper.dispatchAll(); + final InOrder inOrder = inOrder(mOffloadHalCallback); + inOrder.verify(mOffloadHalCallback).onStarted(); + inOrder.verifyNoMoreInteractions(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR); + mTestLooper.dispatchAll(); + inOrder.verify(mOffloadHalCallback).onStoppedError(); + inOrder.verifyNoMoreInteractions(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED); + mTestLooper.dispatchAll(); + inOrder.verify(mOffloadHalCallback).onStoppedUnsupported(); + inOrder.verifyNoMoreInteractions(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE); + mTestLooper.dispatchAll(); + inOrder.verify(mOffloadHalCallback).onSupportAvailable(); + inOrder.verifyNoMoreInteractions(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED); + mTestLooper.dispatchAll(); + inOrder.verify(mOffloadHalCallback).onStoppedLimitReached(); + inOrder.verifyNoMoreInteractions(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_WARNING_REACHED); + mTestLooper.dispatchAll(); + inOrder.verify(mOffloadHalCallback).onWarningReached(); + inOrder.verifyNoMoreInteractions(); + + final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP); + mTetheringOffloadCallback.updateTimeout(tcpParams); + mTestLooper.dispatchAll(); + inOrder.verify(mOffloadHalCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP), + eq(tcpParams.src.addr), + eq(tcpParams.src.port), + eq(tcpParams.dst.addr), + eq(tcpParams.dst.port)); + inOrder.verifyNoMoreInteractions(); + + final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP); + mTetheringOffloadCallback.updateTimeout(udpParams); + mTestLooper.dispatchAll(); + inOrder.verify(mOffloadHalCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP), + eq(udpParams.src.addr), + eq(udpParams.src.port), + eq(udpParams.dst.addr), + eq(udpParams.dst.port)); + inOrder.verifyNoMoreInteractions(); + } + + private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) { + final NatTimeoutUpdate params = new NatTimeoutUpdate(); + params.proto = proto; + params.src = new IPv4AddrPortPair(); + params.dst = new IPv4AddrPortPair(); + params.src.addr = "192.168.43.200"; + params.src.port = 100; + params.dst.addr = "172.50.46.169"; + params.dst.port = 150; + return params; + } +} diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalHidlImplTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalHidlImplTest.java new file mode 100644 index 0000000000..6fdab5ad87 --- /dev/null +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHalHidlImplTest.java @@ -0,0 +1,359 @@ +/* + * 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 com.android.networkstack.tethering; + +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1; +import static com.android.networkstack.tethering.util.TetheringUtils.uint16; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; +import static org.junit.Assert.fail; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.eq; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.reset; +import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.verify; + +import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; +import android.hardware.tetheroffload.control.V1_0.IOffloadControl; +import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; +import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; +import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback; +import android.hardware.tetheroffload.control.V1_1.OffloadCallbackEvent; +import android.os.Handler; +import android.os.NativeHandle; +import android.os.test.TestLooper; +import android.system.OsConstants; + +import androidx.test.filters.SmallTest; +import androidx.test.runner.AndroidJUnit4; + +import com.android.net.module.util.SharedLog; +import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; +import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback; + +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.InOrder; +import org.mockito.MockitoAnnotations; + +import java.io.FileDescriptor; +import java.util.ArrayList; + +@RunWith(AndroidJUnit4.class) +@SmallTest +public final class OffloadHalHidlImplTest { + private static final String RMNET0 = "test_rmnet_data0"; + + private final SharedLog mLog = new SharedLog("test"); + private final TestLooper mTestLooper = new TestLooper(); + + private OffloadHalHidlImpl mIOffloadHal; + private IOffloadConfig mIOffloadConfigMock; + private IOffloadControl mIOffloadControlMock; + private ITetheringOffloadCallback mTetheringOffloadCallback; + private OffloadHalCallback mOffloadHalCallback; + + private void createAndInitOffloadHal(int version) throws Exception { + final FileDescriptor fd1 = new FileDescriptor(); + final FileDescriptor fd2 = new FileDescriptor(); + final NativeHandle handle1 = new NativeHandle(fd1, true); + final NativeHandle handle2 = new NativeHandle(fd2, true); + mIOffloadConfigMock = mock(IOffloadConfig.class); + switch (version) { + case OFFLOAD_HAL_VERSION_HIDL_1_0: + mIOffloadControlMock = mock(IOffloadControl.class); + break; + case OFFLOAD_HAL_VERSION_HIDL_1_1: + mIOffloadControlMock = mock( + android.hardware.tetheroffload.control.V1_1.IOffloadControl.class); + break; + default: + fail("Nonexistent HAL version"); + return; + } + mIOffloadHal = new OffloadHalHidlImpl(version, mIOffloadConfigMock, + mIOffloadControlMock, new Handler(mTestLooper.getLooper()), mLog); + mIOffloadHal.initOffload(handle1, handle2, mOffloadHalCallback); + + final ArgumentCaptor nativeHandleCaptor1 = + ArgumentCaptor.forClass(NativeHandle.class); + final ArgumentCaptor nativeHandleCaptor2 = + ArgumentCaptor.forClass(NativeHandle.class); + final ArgumentCaptor offloadCallbackCaptor = + ArgumentCaptor.forClass(ITetheringOffloadCallback.class); + verify(mIOffloadConfigMock).setHandles(nativeHandleCaptor1.capture(), + nativeHandleCaptor2.capture(), any()); + verify(mIOffloadControlMock).initOffload(offloadCallbackCaptor.capture(), any()); + assertEquals(nativeHandleCaptor1.getValue().getFileDescriptor().getInt$(), + handle1.getFileDescriptor().getInt$()); + assertEquals(nativeHandleCaptor2.getValue().getFileDescriptor().getInt$(), + handle2.getFileDescriptor().getInt$()); + mTetheringOffloadCallback = offloadCallbackCaptor.getValue(); + } + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + mOffloadHalCallback = spy(new OffloadHalCallback()); + } + + @Test + public void testGetForwardedStats() throws Exception { + createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0); + final long rxBytes = 12345; + final long txBytes = 67890; + doAnswer(invocation -> { + ((IOffloadControl.getForwardedStatsCallback) invocation.getArgument(1)) + .onValues(rxBytes, txBytes); + return null; + }).when(mIOffloadControlMock).getForwardedStats(eq(RMNET0), any()); + final ForwardedStats stats = mIOffloadHal.getForwardedStats(RMNET0); + verify(mIOffloadControlMock).getForwardedStats(eq(RMNET0), any()); + assertNotNull(stats); + assertEquals(rxBytes, stats.rxBytes); + assertEquals(txBytes, stats.txBytes); + } + + private void doTestSetLocalPrefixes(boolean expectSuccess) throws Exception { + createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0); + final ArrayList localPrefixes = new ArrayList<>(); + localPrefixes.add("127.0.0.0/8"); + localPrefixes.add("fe80::/64"); + doAnswer(invocation -> { + ((IOffloadControl.setLocalPrefixesCallback) invocation.getArgument(1)) + .onValues(expectSuccess, ""); + return null; + }).when(mIOffloadControlMock).setLocalPrefixes(eq(localPrefixes), any()); + assertEquals(expectSuccess, mIOffloadHal.setLocalPrefixes(localPrefixes)); + verify(mIOffloadControlMock).setLocalPrefixes(eq(localPrefixes), any()); + } + + @Test + public void testSetLocalPrefixesSuccess() throws Exception { + doTestSetLocalPrefixes(true); + } + + @Test + public void testSetLocalPrefixesFailure() throws Exception { + doTestSetLocalPrefixes(false); + } + + private void doTestSetDataLimit(boolean expectSuccess) throws Exception { + createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0); + final long limit = 12345; + doAnswer(invocation -> { + ((IOffloadControl.setDataLimitCallback) invocation.getArgument(2)) + .onValues(expectSuccess, ""); + return null; + }).when(mIOffloadControlMock).setDataLimit(eq(RMNET0), eq(limit), any()); + assertEquals(expectSuccess, mIOffloadHal.setDataLimit(RMNET0, limit)); + verify(mIOffloadControlMock).setDataLimit(eq(RMNET0), eq(limit), any()); + } + + @Test + public void testSetDataLimitSuccess() throws Exception { + doTestSetDataLimit(true); + } + + @Test + public void testSetDataLimitFailure() throws Exception { + doTestSetDataLimit(false); + } + + private void doTestSetDataWarningAndLimit(boolean expectSuccess) throws Exception { + createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_1); + final long warning = 12345; + final long limit = 67890; + doAnswer(invocation -> { + ((android.hardware.tetheroffload.control.V1_1.IOffloadControl + .setDataWarningAndLimitCallback) invocation.getArgument(3)) + .onValues(expectSuccess, ""); + return null; + }).when((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mIOffloadControlMock) + .setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit), any()); + assertEquals(expectSuccess, mIOffloadHal.setDataWarningAndLimit(RMNET0, warning, limit)); + verify((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mIOffloadControlMock) + .setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit), any()); + } + + @Test + public void testSetDataWarningAndLimitSuccess() throws Exception { + doTestSetDataWarningAndLimit(true); + } + + @Test + public void testSetDataWarningAndLimitFailure() throws Exception { + // Verify that V1.0 control HAL would reject the function call with exception. + createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0); + final long warning = 12345; + final long limit = 67890; + assertThrows(UnsupportedOperationException.class, + () -> mIOffloadHal.setDataWarningAndLimit(RMNET0, warning, limit)); + + doTestSetDataWarningAndLimit(false); + } + + private void doTestSetUpstreamParameters(boolean expectSuccess) throws Exception { + createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0); + final String v4addr = "192.168.10.1"; + final String v4gateway = "192.168.10.255"; + final ArrayList v6gws = new ArrayList<>(0); + v6gws.add("2001:db8::1"); + doAnswer(invocation -> { + ((IOffloadControl.setUpstreamParametersCallback) invocation.getArgument(4)) + .onValues(expectSuccess, ""); + return null; + }).when(mIOffloadControlMock).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway), + eq(v6gws), any()); + assertEquals(expectSuccess, mIOffloadHal.setUpstreamParameters(RMNET0, v4addr, v4gateway, + v6gws)); + verify(mIOffloadControlMock).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway), + eq(v6gws), any()); + } + + @Test + public void testSetUpstreamParametersSuccess() throws Exception { + doTestSetUpstreamParameters(true); + } + + @Test + public void testSetUpstreamParametersFailure() throws Exception { + doTestSetUpstreamParameters(false); + } + + private void doTestAddDownstream(boolean expectSuccess) throws Exception { + createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0); + final String ifName = "wlan1"; + final String prefix = "192.168.43.0/24"; + doAnswer(invocation -> { + ((IOffloadControl.addDownstreamCallback) invocation.getArgument(2)) + .onValues(expectSuccess, ""); + return null; + }).when(mIOffloadControlMock).addDownstream(eq(ifName), eq(prefix), any()); + assertEquals(expectSuccess, mIOffloadHal.addDownstream(ifName, prefix)); + verify(mIOffloadControlMock).addDownstream(eq(ifName), eq(prefix), any()); + } + + @Test + public void testAddDownstreamSuccess() throws Exception { + doTestAddDownstream(true); + } + + @Test + public void testAddDownstreamFailure() throws Exception { + doTestAddDownstream(false); + } + + private void doTestRemoveDownstream(boolean expectSuccess) throws Exception { + createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0); + final String ifName = "wlan1"; + final String prefix = "192.168.43.0/24"; + doAnswer(invocation -> { + ((IOffloadControl.removeDownstreamCallback) invocation.getArgument(2)) + .onValues(expectSuccess, ""); + return null; + }).when(mIOffloadControlMock).removeDownstream(eq(ifName), eq(prefix), any()); + assertEquals(expectSuccess, mIOffloadHal.removeDownstream(ifName, prefix)); + verify(mIOffloadControlMock).removeDownstream(eq(ifName), eq(prefix), any()); + } + + @Test + public void testRemoveDownstreamSuccess() throws Exception { + doTestRemoveDownstream(true); + } + + @Test + public void testRemoveDownstreamFailure() throws Exception { + doTestRemoveDownstream(false); + } + + @Test + public void testTetheringOffloadCallback() throws Exception { + createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_0); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED); + mTestLooper.dispatchAll(); + verify(mOffloadHalCallback).onStarted(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR); + mTestLooper.dispatchAll(); + verify(mOffloadHalCallback).onStoppedError(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED); + mTestLooper.dispatchAll(); + verify(mOffloadHalCallback).onStoppedUnsupported(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE); + mTestLooper.dispatchAll(); + verify(mOffloadHalCallback).onSupportAvailable(); + + mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED); + mTestLooper.dispatchAll(); + verify(mOffloadHalCallback).onStoppedLimitReached(); + + final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP); + mTetheringOffloadCallback.updateTimeout(tcpParams); + mTestLooper.dispatchAll(); + verify(mOffloadHalCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP), + eq(tcpParams.src.addr), + eq(uint16(tcpParams.src.port)), + eq(tcpParams.dst.addr), + eq(uint16(tcpParams.dst.port))); + + final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP); + mTetheringOffloadCallback.updateTimeout(udpParams); + mTestLooper.dispatchAll(); + verify(mOffloadHalCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP), + eq(udpParams.src.addr), + eq(uint16(udpParams.src.port)), + eq(udpParams.dst.addr), + eq(uint16(udpParams.dst.port))); + reset(mOffloadHalCallback); + + createAndInitOffloadHal(OFFLOAD_HAL_VERSION_HIDL_1_1); + + // Verify the interface will process the events that comes from V1.1 HAL. + mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_STARTED); + mTestLooper.dispatchAll(); + final InOrder inOrder = inOrder(mOffloadHalCallback); + inOrder.verify(mOffloadHalCallback).onStarted(); + inOrder.verifyNoMoreInteractions(); + + mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_WARNING_REACHED); + mTestLooper.dispatchAll(); + inOrder.verify(mOffloadHalCallback).onWarningReached(); + inOrder.verifyNoMoreInteractions(); + } + + private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) { + final NatTimeoutUpdate params = new NatTimeoutUpdate(); + params.proto = proto; + params.src.addr = "192.168.43.200"; + params.src.port = 100; + params.dst.addr = "172.50.46.169"; + params.dst.port = 150; + return params; + } +} diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java index 36b439b661..b1f875b615 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/OffloadHardwareInterfaceTest.java @@ -20,36 +20,29 @@ import static android.system.OsConstants.AF_INET; import static android.system.OsConstants.AF_UNIX; import static android.system.OsConstants.SOCK_STREAM; -import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0; -import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_1; -import static com.android.networkstack.tethering.util.TetheringUtils.uint16; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_AIDL; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_1; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE; import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertThrows; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.anyLong; +import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.inOrder; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.reset; -import static org.mockito.Mockito.spy; +import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; -import android.hardware.tetheroffload.config.V1_0.IOffloadConfig; -import android.hardware.tetheroffload.control.V1_0.IOffloadControl; -import android.hardware.tetheroffload.control.V1_0.NatTimeoutUpdate; -import android.hardware.tetheroffload.control.V1_0.NetworkProtocol; -import android.hardware.tetheroffload.control.V1_1.ITetheringOffloadCallback; -import android.hardware.tetheroffload.control.V1_1.OffloadCallbackEvent; import android.os.Handler; import android.os.NativeHandle; import android.os.test.TestLooper; import android.system.ErrnoException; import android.system.Os; -import android.system.OsConstants; -import android.util.Pair; import androidx.test.filters.SmallTest; import androidx.test.runner.AndroidJUnit4; @@ -57,12 +50,13 @@ import androidx.test.runner.AndroidJUnit4; import com.android.net.module.util.SharedLog; import com.android.net.module.util.netlink.StructNfGenMsg; import com.android.net.module.util.netlink.StructNlMsgHdr; +import com.android.networkstack.tethering.OffloadHardwareInterface.ForwardedStats; +import com.android.networkstack.tethering.OffloadHardwareInterface.OffloadHalCallback; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; -import org.mockito.InOrder; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @@ -79,11 +73,9 @@ public final class OffloadHardwareInterfaceTest { private final TestLooper mTestLooper = new TestLooper(); private OffloadHardwareInterface mOffloadHw; - private ITetheringOffloadCallback mTetheringOffloadCallback; - private OffloadHardwareInterface.ControlCallback mControlCallback; + private OffloadHalCallback mOffloadHalCallback; - @Mock private IOffloadConfig mIOffloadConfig; - private IOffloadControl mIOffloadControl; + @Mock private IOffloadHal mIOffload; @Mock private NativeHandle mNativeHandle; // Random values to test Netlink message. @@ -91,32 +83,16 @@ public final class OffloadHardwareInterfaceTest { private static final short TEST_FLAGS = 263; class MyDependencies extends OffloadHardwareInterface.Dependencies { - private final int mMockControlVersion; - MyDependencies(SharedLog log, final int mockControlVersion) { - super(log); - mMockControlVersion = mockControlVersion; + private final int mMockOffloadHalVersion; + MyDependencies(Handler handler, SharedLog log, final int mockOffloadHalVersion) { + super(handler, log); + mMockOffloadHalVersion = mockOffloadHalVersion; + when(mIOffload.getVersion()).thenReturn(mMockOffloadHalVersion); } @Override - public IOffloadConfig getOffloadConfig() { - return mIOffloadConfig; - } - - @Override - public Pair getOffloadControl() { - switch (mMockControlVersion) { - case OFFLOAD_HAL_VERSION_1_0: - mIOffloadControl = mock(IOffloadControl.class); - break; - case OFFLOAD_HAL_VERSION_1_1: - mIOffloadControl = - mock(android.hardware.tetheroffload.control.V1_1.IOffloadControl.class); - break; - default: - throw new IllegalArgumentException("Invalid offload control version " - + mMockControlVersion); - } - return new Pair(mIOffloadControl, mMockControlVersion); + public IOffloadHal getOffload() { + return mMockOffloadHalVersion == OFFLOAD_HAL_VERSION_NONE ? null : mIOffload; } @Override @@ -128,156 +104,140 @@ public final class OffloadHardwareInterfaceTest { @Before public void setUp() { MockitoAnnotations.initMocks(this); - mControlCallback = spy(new OffloadHardwareInterface.ControlCallback()); + mOffloadHalCallback = new OffloadHalCallback(); + when(mIOffload.initOffload(any(NativeHandle.class), any(NativeHandle.class), + any(OffloadHalCallback.class))).thenReturn(true); } - private void startOffloadHardwareInterface(int controlVersion) throws Exception { + private void startOffloadHardwareInterface(int offloadHalVersion) + throws Exception { final SharedLog log = new SharedLog("test"); - mOffloadHw = new OffloadHardwareInterface(new Handler(mTestLooper.getLooper()), log, - new MyDependencies(log, controlVersion)); - mOffloadHw.initOffloadConfig(); - mOffloadHw.initOffloadControl(mControlCallback); - final ArgumentCaptor mOffloadCallbackCaptor = - ArgumentCaptor.forClass(ITetheringOffloadCallback.class); - verify(mIOffloadControl).initOffload(mOffloadCallbackCaptor.capture(), any()); - mTetheringOffloadCallback = mOffloadCallbackCaptor.getValue(); + final Handler handler = new Handler(mTestLooper.getLooper()); + final int num = offloadHalVersion != OFFLOAD_HAL_VERSION_NONE ? 1 : 0; + mOffloadHw = new OffloadHardwareInterface(handler, log, + new MyDependencies(handler, log, offloadHalVersion)); + assertEquals(offloadHalVersion, mOffloadHw.initOffload(mOffloadHalCallback)); + verify(mIOffload, times(num)).initOffload(any(NativeHandle.class), any(NativeHandle.class), + eq(mOffloadHalCallback)); + } + + @Test + public void testInitFailureWithNoHal() throws Exception { + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_NONE); + } + + @Test + public void testInitSuccessWithAidl() throws Exception { + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_AIDL); + } + + @Test + public void testInitSuccessWithHidl_1_0() throws Exception { + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0); + } + + @Test + public void testInitSuccessWithHidl_1_1() throws Exception { + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1); } @Test public void testGetForwardedStats() throws Exception { - startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0); - final OffloadHardwareInterface.ForwardedStats stats = mOffloadHw.getForwardedStats(RMNET0); - verify(mIOffloadControl).getForwardedStats(eq(RMNET0), any()); - assertNotNull(stats); + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0); + ForwardedStats stats = new ForwardedStats(12345, 56780); + when(mIOffload.getForwardedStats(anyString())).thenReturn(stats); + assertEquals(mOffloadHw.getForwardedStats(RMNET0), stats); + verify(mIOffload).getForwardedStats(eq(RMNET0)); } @Test public void testSetLocalPrefixes() throws Exception { - startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0); + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0); final ArrayList localPrefixes = new ArrayList<>(); localPrefixes.add("127.0.0.0/8"); localPrefixes.add("fe80::/64"); - mOffloadHw.setLocalPrefixes(localPrefixes); - verify(mIOffloadControl).setLocalPrefixes(eq(localPrefixes), any()); + when(mIOffload.setLocalPrefixes(any())).thenReturn(true); + assertTrue(mOffloadHw.setLocalPrefixes(localPrefixes)); + verify(mIOffload).setLocalPrefixes(eq(localPrefixes)); + when(mIOffload.setLocalPrefixes(any())).thenReturn(false); + assertFalse(mOffloadHw.setLocalPrefixes(localPrefixes)); } @Test public void testSetDataLimit() throws Exception { - startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0); + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0); final long limit = 12345; - mOffloadHw.setDataLimit(RMNET0, limit); - verify(mIOffloadControl).setDataLimit(eq(RMNET0), eq(limit), any()); + when(mIOffload.setDataLimit(anyString(), anyLong())).thenReturn(true); + assertTrue(mOffloadHw.setDataLimit(RMNET0, limit)); + verify(mIOffload).setDataLimit(eq(RMNET0), eq(limit)); + when(mIOffload.setDataLimit(anyString(), anyLong())).thenReturn(false); + assertFalse(mOffloadHw.setDataLimit(RMNET0, limit)); + } + + @Test + public void testSetDataWarningAndLimitFailureWithHidl_1_0() throws Exception { + // Verify V1.0 control HAL would reject the function call with exception. + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0); + final long warning = 12345; + final long limit = 67890; + assertThrows(UnsupportedOperationException.class, + () -> mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit)); } @Test public void testSetDataWarningAndLimit() throws Exception { - // Verify V1.0 control HAL would reject the function call with exception. - startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0); + // Verify V1.1 control HAL could receive this function call. + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_1); final long warning = 12345; final long limit = 67890; - assertThrows(IllegalArgumentException.class, - () -> mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit)); - reset(mIOffloadControl); - - // Verify V1.1 control HAL could receive this function call. - startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_1); - mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit); - verify((android.hardware.tetheroffload.control.V1_1.IOffloadControl) mIOffloadControl) - .setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit), any()); + when(mIOffload.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(true); + assertTrue(mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit)); + verify(mIOffload).setDataWarningAndLimit(eq(RMNET0), eq(warning), eq(limit)); + when(mIOffload.setDataWarningAndLimit(anyString(), anyLong(), anyLong())).thenReturn(false); + assertFalse(mOffloadHw.setDataWarningAndLimit(RMNET0, warning, limit)); } @Test public void testSetUpstreamParameters() throws Exception { - startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0); + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0); final String v4addr = "192.168.10.1"; final String v4gateway = "192.168.10.255"; final ArrayList v6gws = new ArrayList<>(0); v6gws.add("2001:db8::1"); - mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws); - verify(mIOffloadControl).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway), - eq(v6gws), any()); + when(mIOffload.setUpstreamParameters(anyString(), anyString(), anyString(), any())) + .thenReturn(true); + assertTrue(mOffloadHw.setUpstreamParameters(RMNET0, v4addr, v4gateway, v6gws)); + verify(mIOffload).setUpstreamParameters(eq(RMNET0), eq(v4addr), eq(v4gateway), eq(v6gws)); final ArgumentCaptor> mArrayListCaptor = ArgumentCaptor.forClass(ArrayList.class); - mOffloadHw.setUpstreamParameters(null, null, null, null); - verify(mIOffloadControl).setUpstreamParameters(eq(""), eq(""), eq(""), - mArrayListCaptor.capture(), any()); + when(mIOffload.setUpstreamParameters(anyString(), anyString(), anyString(), any())) + .thenReturn(false); + assertFalse(mOffloadHw.setUpstreamParameters(null, null, null, null)); + verify(mIOffload).setUpstreamParameters(eq(""), eq(""), eq(""), mArrayListCaptor.capture()); assertEquals(mArrayListCaptor.getValue().size(), 0); } @Test - public void testUpdateDownstreamPrefix() throws Exception { - startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0); + public void testUpdateDownstream() throws Exception { + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0); final String ifName = "wlan1"; final String prefix = "192.168.43.0/24"; - mOffloadHw.addDownstreamPrefix(ifName, prefix); - verify(mIOffloadControl).addDownstream(eq(ifName), eq(prefix), any()); - - mOffloadHw.removeDownstreamPrefix(ifName, prefix); - verify(mIOffloadControl).removeDownstream(eq(ifName), eq(prefix), any()); - } - - @Test - public void testTetheringOffloadCallback() throws Exception { - startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STARTED); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStarted(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_ERROR); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStoppedError(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_UNSUPPORTED); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStoppedUnsupported(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_SUPPORT_AVAILABLE); - mTestLooper.dispatchAll(); - verify(mControlCallback).onSupportAvailable(); - - mTetheringOffloadCallback.onEvent(OffloadCallbackEvent.OFFLOAD_STOPPED_LIMIT_REACHED); - mTestLooper.dispatchAll(); - verify(mControlCallback).onStoppedLimitReached(); - - final NatTimeoutUpdate tcpParams = buildNatTimeoutUpdate(NetworkProtocol.TCP); - mTetheringOffloadCallback.updateTimeout(tcpParams); - mTestLooper.dispatchAll(); - verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_TCP), - eq(tcpParams.src.addr), - eq(uint16(tcpParams.src.port)), - eq(tcpParams.dst.addr), - eq(uint16(tcpParams.dst.port))); - - final NatTimeoutUpdate udpParams = buildNatTimeoutUpdate(NetworkProtocol.UDP); - mTetheringOffloadCallback.updateTimeout(udpParams); - mTestLooper.dispatchAll(); - verify(mControlCallback).onNatTimeoutUpdate(eq(OsConstants.IPPROTO_UDP), - eq(udpParams.src.addr), - eq(uint16(udpParams.src.port)), - eq(udpParams.dst.addr), - eq(uint16(udpParams.dst.port))); - reset(mControlCallback); - - startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_1); - - // Verify the interface will process the events that comes from V1.1 HAL. - mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_STARTED); - mTestLooper.dispatchAll(); - final InOrder inOrder = inOrder(mControlCallback); - inOrder.verify(mControlCallback).onStarted(); - inOrder.verifyNoMoreInteractions(); - - mTetheringOffloadCallback.onEvent_1_1(OffloadCallbackEvent.OFFLOAD_WARNING_REACHED); - mTestLooper.dispatchAll(); - inOrder.verify(mControlCallback).onWarningReached(); - inOrder.verifyNoMoreInteractions(); + when(mIOffload.addDownstream(anyString(), anyString())).thenReturn(true); + assertTrue(mOffloadHw.addDownstream(ifName, prefix)); + verify(mIOffload).addDownstream(eq(ifName), eq(prefix)); + when(mIOffload.addDownstream(anyString(), anyString())).thenReturn(false); + assertFalse(mOffloadHw.addDownstream(ifName, prefix)); + when(mIOffload.removeDownstream(anyString(), anyString())).thenReturn(true); + assertTrue(mOffloadHw.removeDownstream(ifName, prefix)); + verify(mIOffload).removeDownstream(eq(ifName), eq(prefix)); + when(mIOffload.removeDownstream(anyString(), anyString())).thenReturn(false); + assertFalse(mOffloadHw.removeDownstream(ifName, prefix)); } @Test public void testSendIpv4NfGenMsg() throws Exception { - startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_1_0); + startOffloadHardwareInterface(OFFLOAD_HAL_VERSION_HIDL_1_0); FileDescriptor writeSocket = new FileDescriptor(); FileDescriptor readSocket = new FileDescriptor(); try { @@ -308,14 +268,4 @@ public final class OffloadHardwareInterfaceTest { assertEquals(0 /* error */, buffer.getShort()); // res_id assertEquals(expectedLen, buffer.position()); } - - private NatTimeoutUpdate buildNatTimeoutUpdate(final int proto) { - final NatTimeoutUpdate params = new NatTimeoutUpdate(); - params.proto = proto; - params.src.addr = "192.168.43.200"; - params.src.port = 100; - params.dst.addr = "172.50.46.169"; - params.dst.port = 150; - return params; - } } diff --git a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java index 79590b73bf..adb1590c7b 100644 --- a/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java +++ b/Tethering/tests/unit/src/com/android/networkstack/tethering/TetheringTest.java @@ -68,7 +68,7 @@ import static com.android.modules.utils.build.SdkLevel.isAtLeastS; import static com.android.modules.utils.build.SdkLevel.isAtLeastT; import static com.android.net.module.util.Inet4AddressUtils.inet4AddressToIntHTH; import static com.android.net.module.util.Inet4AddressUtils.intToInet4AddressHTH; -import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_1_0; +import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_HIDL_1_0; import static com.android.networkstack.tethering.OffloadHardwareInterface.OFFLOAD_HAL_VERSION_NONE; import static com.android.networkstack.tethering.TestConnectivityManager.BROADCAST_FIRST; import static com.android.networkstack.tethering.TestConnectivityManager.CALLBACKS_FIRST; @@ -649,8 +649,7 @@ public class TetheringTest { mInterfaceConfiguration.flags = new String[0]; when(mRouterAdvertisementDaemon.start()) .thenReturn(true); - initOffloadConfiguration(true /* offloadConfig */, OFFLOAD_HAL_VERSION_1_0, - 0 /* defaultDisabled */); + initOffloadConfiguration(OFFLOAD_HAL_VERSION_HIDL_1_0, 0 /* defaultDisabled */); when(mOffloadHardwareInterface.getForwardedStats(any())).thenReturn(mForwardedStats); mServiceContext = new TestContext(mContext); @@ -2345,25 +2344,15 @@ public class TetheringTest { mLooper.dispatchAll(); callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - // 1. Offload fail if no OffloadConfig. - initOffloadConfiguration(false /* offloadConfig */, OFFLOAD_HAL_VERSION_1_0, - 0 /* defaultDisabled */); + // 1. Offload fail if no IOffloadHal. + initOffloadConfiguration(OFFLOAD_HAL_VERSION_NONE, 0 /* defaultDisabled */); runUsbTethering(upstreamState); callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED); runStopUSBTethering(); callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); reset(mUsbManager, mIPv6TetheringCoordinator); - // 2. Offload fail if no OffloadControl. - initOffloadConfiguration(true /* offloadConfig */, OFFLOAD_HAL_VERSION_NONE, - 0 /* defaultDisabled */); - runUsbTethering(upstreamState); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED); - runStopUSBTethering(); - callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_STOPPED); - reset(mUsbManager, mIPv6TetheringCoordinator); - // 3. Offload fail if disabled by settings. - initOffloadConfiguration(true /* offloadConfig */, OFFLOAD_HAL_VERSION_1_0, - 1 /* defaultDisabled */); + // 2. Offload fail if disabled by settings. + initOffloadConfiguration(OFFLOAD_HAL_VERSION_HIDL_1_0, 1 /* defaultDisabled */); runUsbTethering(upstreamState); callback.expectOffloadStatusChanged(TETHER_HARDWARE_OFFLOAD_FAILED); runStopUSBTethering(); @@ -2378,11 +2367,10 @@ public class TetheringTest { verify(mUsbManager).setCurrentFunctions(UsbManager.FUNCTION_NONE); } - private void initOffloadConfiguration(final boolean offloadConfig, - @OffloadHardwareInterface.OffloadHalVersion final int offloadControlVersion, + private void initOffloadConfiguration( + @OffloadHardwareInterface.OffloadHalVersion final int offloadHalVersion, final int defaultDisabled) { - when(mOffloadHardwareInterface.initOffloadConfig()).thenReturn(offloadConfig); - when(mOffloadHardwareInterface.initOffloadControl(any())).thenReturn(offloadControlVersion); + when(mOffloadHardwareInterface.initOffload(any())).thenReturn(offloadHalVersion); when(mOffloadHardwareInterface.getDefaultTetherOffloadDisabled()).thenReturn( defaultDisabled); }