Skip to content
Snippets Groups Projects
Commit 85acbddf authored by Mark Chien's avatar Mark Chien Committed by Gerrit Code Review
Browse files

Merge "[Tether06] Migrate tether offload controller into module"

parents f11a5da0 6aa38890
No related branches found
No related tags found
No related merge requests found
......@@ -27,6 +27,7 @@ java_defaults {
"androidx.annotation_annotation",
"netd_aidl_interface-java",
"networkstack-aidl-interfaces-java",
"android.hardware.tetheroffload.control-V1.0-java",
"tethering-client",
],
manifest: "AndroidManifestBase.xml",
......@@ -38,11 +39,39 @@ android_library {
defaults: ["TetheringAndroidLibraryDefaults"],
}
cc_library_shared {
name: "libtetheroffloadjni",
srcs: [
"jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
],
shared_libs: [
"libnativehelper",
"libcutils",
"android.hardware.tetheroffload.config@1.0",
],
static_libs: [
"liblog",
"libbase",
"libhidlbase",
"libutils",
],
cflags: [
"-Wall",
"-Werror",
"-Wno-unused-parameter",
"-Wthread-safety",
],
}
// Common defaults for compiling the actual APK.
java_defaults {
name: "TetheringAppDefaults",
platform_apis: true,
privileged: true,
jni_libs: [
"libtetheroffloadjni",
],
resource_dirs: [
"res",
],
......@@ -71,6 +100,8 @@ filegroup {
name: "tethering-servicescore-srcs",
srcs: [
"src/com/android/server/connectivity/tethering/EntitlementManager.java",
"src/com/android/server/connectivity/tethering/OffloadController.java",
"src/com/android/server/connectivity/tethering/OffloadHardwareInterface.java",
"src/com/android/server/connectivity/tethering/TetheringConfiguration.java",
"src/com/android/server/connectivity/tethering/UpstreamNetworkMonitor.java",
],
......@@ -88,3 +119,11 @@ filegroup {
"src/android/net/util/PrefixUtils.java",
],
}
// This group would be removed when tethering migration is done.
filegroup {
name: "tethering-jni-srcs",
srcs: [
"jni/com_android_server_connectivity_tethering_OffloadHardwareInterface.cpp",
],
}
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <errno.h>
#include <error.h>
#include <hidl/HidlSupport.h>
#include <jni.h>
#include <nativehelper/JNIHelp.h>
#include <linux/netfilter/nfnetlink.h>
#include <linux/netlink.h>
#include <sys/socket.h>
#include <android-base/unique_fd.h>
#include <android/hardware/tetheroffload/config/1.0/IOffloadConfig.h>
#define LOG_TAG "OffloadHardwareInterface"
#include <utils/Log.h>
namespace android {
using hardware::hidl_handle;
using hardware::hidl_string;
using hardware::tetheroffload::config::V1_0::IOffloadConfig;
namespace {
inline const sockaddr * asSockaddr(const sockaddr_nl *nladdr) {
return reinterpret_cast<const sockaddr *>(nladdr);
}
int conntrackSocket(unsigned groups) {
base::unique_fd s(socket(AF_NETLINK, SOCK_DGRAM, NETLINK_NETFILTER));
if (s.get() < 0) return -errno;
const struct sockaddr_nl bind_addr = {
.nl_family = AF_NETLINK,
.nl_pad = 0,
.nl_pid = 0,
.nl_groups = groups,
};
if (bind(s.get(), asSockaddr(&bind_addr), sizeof(bind_addr)) != 0) {
return -errno;
}
const struct sockaddr_nl kernel_addr = {
.nl_family = AF_NETLINK,
.nl_pad = 0,
.nl_pid = 0,
.nl_groups = groups,
};
if (connect(s.get(), asSockaddr(&kernel_addr), sizeof(kernel_addr)) != 0) {
return -errno;
}
return s.release();
}
// Return a hidl_handle that owns the file descriptor owned by fd, and will
// auto-close it (otherwise there would be double-close problems).
//
// Rely upon the compiler to eliminate the constexprs used for clarity.
hidl_handle handleFromFileDescriptor(base::unique_fd fd) {
hidl_handle h;
static constexpr int kNumFds = 1;
static constexpr int kNumInts = 0;
native_handle_t *nh = native_handle_create(kNumFds, kNumInts);
nh->data[0] = fd.release();
static constexpr bool kTakeOwnership = true;
h.setTo(nh, kTakeOwnership);
return h;
}
} // namespace
static jboolean android_server_connectivity_tethering_OffloadHardwareInterface_configOffload(
JNIEnv* /* env */) {
sp<IOffloadConfig> configInterface = IOffloadConfig::getService();
if (configInterface.get() == nullptr) {
ALOGD("Could not find IOffloadConfig service.");
return false;
}
// Per the IConfigOffload definition:
//
// fd1 A file descriptor bound to the following netlink groups
// (NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY).
//
// fd2 A file descriptor bound to the following netlink groups
// (NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY).
base::unique_fd
fd1(conntrackSocket(NF_NETLINK_CONNTRACK_NEW | NF_NETLINK_CONNTRACK_DESTROY)),
fd2(conntrackSocket(NF_NETLINK_CONNTRACK_UPDATE | NF_NETLINK_CONNTRACK_DESTROY));
if (fd1.get() < 0 || fd2.get() < 0) {
ALOGE("Unable to create conntrack handles: %d/%s", errno, strerror(errno));
return false;
}
hidl_handle h1(handleFromFileDescriptor(std::move(fd1))),
h2(handleFromFileDescriptor(std::move(fd2)));
bool rval(false);
hidl_string msg;
const auto status = configInterface->setHandles(h1, h2,
[&rval, &msg](bool success, const hidl_string& errMsg) {
rval = success;
msg = errMsg;
});
if (!status.isOk() || !rval) {
ALOGE("IOffloadConfig::setHandles() error: '%s' / '%s'",
status.description().c_str(), msg.c_str());
// If status is somehow not ok, make sure rval captures this too.
rval = false;
}
return rval;
}
/*
* JNI registration.
*/
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "configOffload", "()Z",
(void*) android_server_connectivity_tethering_OffloadHardwareInterface_configOffload },
};
int register_android_server_connectivity_tethering_OffloadHardwareInterface(JNIEnv* env) {
return jniRegisterNativeMethods(env,
"com/android/server/connectivity/tethering/OffloadHardwareInterface",
gMethods, NELEM(gMethods));
}
}; // namespace android
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.server.connectivity.tethering;
import static com.android.internal.util.BitUtils.uint16;
import android.hardware.tetheroffload.control.V1_0.IOffloadControl;
import android.hardware.tetheroffload.control.V1_0.ITetheringOffloadCallback;
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.net.util.SharedLog;
import android.os.Handler;
import android.os.RemoteException;
import android.system.OsConstants;
import java.util.ArrayList;
/**
* Capture tethering dependencies, for injection.
*
* @hide
*/
public class OffloadHardwareInterface {
private static final String TAG = OffloadHardwareInterface.class.getSimpleName();
private static final String YIELDS = " -> ";
// Change this value to control whether tether offload is enabled or
// disabled by default in the absence of an explicit Settings value.
// See accompanying unittest to distinguish 0 from non-0 values.
private static final int DEFAULT_TETHER_OFFLOAD_DISABLED = 0;
private static final String NO_INTERFACE_NAME = "";
private static final String NO_IPV4_ADDRESS = "";
private static final String NO_IPV4_GATEWAY = "";
private static native boolean configOffload();
private final Handler mHandler;
private final SharedLog mLog;
private IOffloadControl mOffloadControl;
private TetheringOffloadCallback mTetheringOffloadCallback;
private ControlCallback mControlCallback;
/** The callback to notify status of offload management process. */
public static class ControlCallback {
/** Offload started. */
public void onStarted() {}
/**
* Offload stopped because an error has occurred in lower layer.
*/
public void onStoppedError() {}
/**
* Offload stopped because the device has moved to a bearer on which hardware offload is
* not supported. Subsequent calls to setUpstreamParameters and add/removeDownstream will
* likely fail and cannot be presumed to be saved inside of the hardware management process.
* Upon receiving #onSupportAvailable(), the caller should reprogram the hardware to begin
* offload again.
*/
public void onStoppedUnsupported() {}
/** Indicate that offload is able to proivde support for this time. */
public void onSupportAvailable() {}
/** Offload stopped because of usage limit reached. */
public void onStoppedLimitReached() {}
/** Indicate to update NAT timeout. */
public void onNatTimeoutUpdate(int proto,
String srcAddr, int srcPort,
String dstAddr, int dstPort) {}
}
/** The object which records Tx/Rx forwarded bytes. */
public static class ForwardedStats {
public long rxBytes;
public long txBytes;
public ForwardedStats() {
rxBytes = 0;
txBytes = 0;
}
/** Add Tx/Rx bytes. */
public void add(ForwardedStats other) {
rxBytes += other.rxBytes;
txBytes += other.txBytes;
}
/** Returns the string representation of this object. */
public String toString() {
return String.format("rx:%s tx:%s", rxBytes, txBytes);
}
}
public OffloadHardwareInterface(Handler h, SharedLog log) {
mHandler = h;
mLog = log.forSubComponent(TAG);
}
/** Get default value indicating whether offload is supported. */
public int getDefaultTetherOffloadDisabled() {
return DEFAULT_TETHER_OFFLOAD_DISABLED;
}
/** Configure offload management process. */
public boolean initOffloadConfig() {
return configOffload();
}
/** Initialize the tethering offload HAL. */
public boolean initOffloadControl(ControlCallback controlCb) {
mControlCallback = controlCb;
if (mOffloadControl == null) {
try {
mOffloadControl = IOffloadControl.getService();
} catch (RemoteException e) {
mLog.e("tethering offload control not supported: " + e);
return false;
}
if (mOffloadControl == null) {
mLog.e("tethering IOffloadControl.getService() returned null");
return false;
}
}
final String logmsg = String.format("initOffloadControl(%s)",
(controlCb == null) ? "null"
: "0x" + Integer.toHexString(System.identityHashCode(controlCb)));
mTetheringOffloadCallback = new TetheringOffloadCallback(mHandler, mControlCallback, mLog);
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 false;
}
record(logmsg, results);
return results.mSuccess;
}
/** 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);
}
}
mOffloadControl = null;
mTetheringOffloadCallback = null;
mControlCallback = null;
mLog.log("stopOffloadControl()");
}
/** 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;
}
mLog.log(logmsg + YIELDS + stats);
return stats;
}
/** Set local prefixes to offload management process. */
public boolean setLocalPrefixes(ArrayList<String> 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;
}
/** 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;
}
/** Set upstream parameters to offload management process. */
public boolean setUpstreamParameters(
String iface, String v4addr, String v4gateway, ArrayList<String> v6gws) {
iface = (iface != null) ? iface : NO_INTERFACE_NAME;
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;
}
/** 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;
}
/** 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;
TetheringOffloadCallback(Handler h, ControlCallback cb, SharedLog sharedLog) {
handler = h;
controlCb = cb;
log = sharedLog;
}
@Override
public void onEvent(int event) {
handler.post(() -> {
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;
default:
log.e("Unsupported OffloadCallbackEvent: " + 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;
}
}
}
}
......@@ -25,6 +25,7 @@ android_test {
static_libs: [
"androidx.test.rules",
"frameworks-base-testutils",
"net-tests-utils",
"mockito-target-extended-minus-junit4",
"TetheringApiCurrentLib",
"testables",
......@@ -46,6 +47,7 @@ filegroup {
name: "tethering-tests-src",
srcs: [
"src/com/android/server/connectivity/tethering/EntitlementManagerTest.java",
"src/com/android/server/connectivity/tethering/OffloadControllerTest.java",
"src/com/android/server/connectivity/tethering/TetheringConfigurationTest.java",
"src/com/android/server/connectivity/tethering/UpstreamNetworkMonitorTest.java",
"src/android/net/dhcp/DhcpServingParamsParcelExtTest.java",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment